先合并obj再转换osgb

This commit is contained in:
weixin_46229132 2025-02-09 21:05:33 +08:00
parent ad6f4fb1ae
commit 954e87121f
4 changed files with 310 additions and 306 deletions

View File

@ -18,6 +18,7 @@ from utils.grid_divider import GridDivider
from utils.logger import setup_logger from utils.logger import setup_logger
from utils.visualizer import FilterVisualizer from utils.visualizer import FilterVisualizer
from post_pro.merge_tif import MergeTif from post_pro.merge_tif import MergeTif
from post_pro.merge_obj import MergeObj
from post_pro.obj_post_pro import ObjPostProcessor from post_pro.obj_post_pro import ObjPostProcessor
from post_pro.merge_laz import MergePly from post_pro.merge_laz import MergePly
@ -255,10 +256,16 @@ class ImagePreprocessor:
merger.merge_grid_laz(grid_points) merger.merge_grid_laz(grid_points)
def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""合并所有网格的OBJ模型""" """合并所有网格的OBJ模型并转换为OSGB格式"""
self.logger.info("开始合并OBJ模型") self.logger.info("开始合并OBJ模型")
merger = MergeObj(self.config.output_dir)
center_coords = merger.merge_grid_obj(grid_points)
# 转换为OSGB格式
self.logger.info("开始转换为OSGB格式")
processor = ObjPostProcessor(self.config.output_dir) processor = ObjPostProcessor(self.config.output_dir)
processor.process_obj_files() if not processor.convert_to_osgb(center_coords):
self.logger.error("OSGB转换失败")
def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""后处理:合并或复制处理结果""" """后处理:合并或复制处理结果"""

View File

@ -18,6 +18,7 @@ from utils.grid_divider import GridDivider
from utils.logger import setup_logger from utils.logger import setup_logger
from utils.visualizer import FilterVisualizer from utils.visualizer import FilterVisualizer
from post_pro.merge_tif import MergeTif from post_pro.merge_tif import MergeTif
from post_pro.merge_obj import MergeObj
from post_pro.obj_post_pro import ObjPostProcessor from post_pro.obj_post_pro import ObjPostProcessor
from post_pro.merge_laz import MergePly from post_pro.merge_laz import MergePly
@ -255,10 +256,16 @@ class ImagePreprocessor:
merger.merge_grid_laz(grid_points) merger.merge_grid_laz(grid_points)
def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): def merge_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""合并所有网格的OBJ模型""" """合并所有网格的OBJ模型并转换为OSGB格式"""
self.logger.info("开始合并OBJ模型") self.logger.info("开始合并OBJ模型")
merger = MergeObj(self.config.output_dir)
center_coords = merger.merge_grid_obj(grid_points)
# 转换为OSGB格式
self.logger.info("开始转换为OSGB格式")
processor = ObjPostProcessor(self.config.output_dir) processor = ObjPostProcessor(self.config.output_dir)
processor.process_obj_files() if not processor.convert_to_osgb(center_coords):
self.logger.error("OSGB转换失败")
def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): def post_process(self, successful_grid_points: Dict[tuple, pd.DataFrame], grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]):
"""后处理:合并或复制处理结果""" """后处理:合并或复制处理结果"""

View File

@ -1,18 +1,26 @@
import os import os
import logging import logging
import numpy as np
from typing import Dict
import pandas as pd import pandas as pd
from typing import Dict, List, Tuple
import numpy as np
import shutil import shutil
import time import time
import cv2 import cv2
import subprocess import subprocess
from pyproj import Transformer
class MergeObj: class MergeObj:
def __init__(self, output_dir: str): def __init__(self, output_dir: str):
self.output_dir = output_dir self.output_dir = output_dir
self.logger = logging.getLogger('UAV_Preprocess.MergeObj') self.logger = logging.getLogger('UAV_Preprocess.MergeObj')
# 用于存储所有grid的UTM范围
self.min_east = float('inf')
self.min_north = float('inf')
self.max_east = float('-inf')
self.max_north = float('-inf')
# 初始化UTM到WGS84的转换器
self.transformer = Transformer.from_crs("EPSG:32649", "EPSG:4326", always_xy=True)
def read_obj(self, file_path): def read_obj(self, file_path):
"""读取.obj文件返回顶点、纹理坐标、法线、面的列表和MTL文件名""" """读取.obj文件返回顶点、纹理坐标、法线、面的列表和MTL文件名"""
@ -103,11 +111,7 @@ class MergeObj:
face_str += "/" face_str += "/"
file.write(face_str + "\n") file.write(face_str + "\n")
def translate_vertices(self, vertices, translation): def merge_two_objs(self, obj1_path: str, obj2_path: str, output_path: str, grid_id1: tuple, grid_id2: tuple):
"""平移顶点"""
return [[v[0] + translation[0], v[1] + translation[1], v[2] + translation[2]] for v in vertices]
def merge_two_objs(self, obj1_path: str, obj2_path: str, output_path: str, translation, grid_id1: tuple, grid_id2: tuple):
"""合并两个OBJ文件""" """合并两个OBJ文件"""
try: try:
self.logger.info(f"开始合并OBJ模型:\n输入1: {obj1_path}\n输入2: {obj2_path}") self.logger.info(f"开始合并OBJ模型:\n输入1: {obj1_path}\n输入2: {obj2_path}")
@ -144,17 +148,13 @@ class MergeObj:
for old_name in materials2.keys(): for old_name in materials2.keys():
material_map2[old_name] = f"material_{grid_id2[0]}_{grid_id2[1]}_{old_name}" material_map2[old_name] = f"material_{grid_id2[0]}_{grid_id2[1]}_{old_name}"
# 平移第二个模型的顶点
vertices2_translated = self.translate_vertices(
vertices2, translation)
# 计算偏移量 # 计算偏移量
v_offset = len(vertices1) v_offset = len(vertices1)
vt_offset = len(tex_coords1) vt_offset = len(tex_coords1)
vn_offset = len(normals1) vn_offset = len(normals1)
# 合并顶点、纹理坐标和法线 # 合并顶点、纹理坐标和法线
all_vertices = vertices1 + vertices2_translated all_vertices = vertices1 + vertices2
all_tex_coords = tex_coords1 + tex_coords2 all_tex_coords = tex_coords1 + tex_coords2
all_normals = normals1 + normals2 all_normals = normals1 + normals2
@ -186,6 +186,188 @@ class MergeObj:
self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True) self.logger.error(f"合并OBJ模型时发生错误: {str(e)}", exc_info=True)
raise raise
def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame]) -> Tuple[float, float]:
"""合并所有网格的OBJ模型
Args:
grid_points: 网格点数据字典
Returns:
Tuple[float, float]: (longitude, latitude)中心点经纬度坐标
"""
if len(grid_points) == 1:
grid_id = list(grid_points.keys())[0]
shutil.copytree(os.path.join(self.output_dir,
f"grid_{grid_id[0]}_{grid_id[1]}",
"project",
"odm_texturing"),
os.path.join(self.output_dir, "texturing"))
os.rename(os.path.join(self.output_dir, "texturing", "odm_textured_model_geo.obj"),
os.path.join(self.output_dir, "texturing", "textured_model.obj"))
return
try:
# 创建输出目录
output_model_dir = os.path.join(self.output_dir, "texturing")
os.makedirs(output_model_dir, exist_ok=True)
# 第一次遍历获取所有grid的UTM范围
for grid_id, points in grid_points.items():
base_dir = os.path.join(
self.output_dir,
f"grid_{grid_id[0]}_{grid_id[1]}",
"project"
)
log_file = os.path.join(base_dir, "odm_orthophoto", "odm_orthophoto_log.txt")
east_offset, north_offset = self.read_utm_offset(log_file)
# 更新UTM范围
self.min_east = min(self.min_east, east_offset)
self.min_north = min(self.min_north, north_offset)
self.max_east = max(self.max_east, east_offset)
self.max_north = max(self.max_north, north_offset)
# 获取所有有效的网格文件
grid_files = {}
all_vertices = [] # 用于存储所有顶点坐标
for grid_id, points in grid_points.items():
base_dir = os.path.join(
self.output_dir,
f"grid_{grid_id[0]}_{grid_id[1]}",
"project",
"odm_texturing"
)
obj_path = os.path.join(base_dir, "odm_textured_model_geo.obj")
mtl_path = os.path.join(base_dir, "odm_textured_model_geo.mtl")
if not os.path.exists(obj_path) or not os.path.exists(mtl_path):
self.logger.warning(
f"网格 ({grid_id[0]},{grid_id[1]}) 的文件不存在")
continue
# 读取UTM偏移量
log_file = os.path.join(base_dir, "..", "odm_orthophoto", "odm_orthophoto_log.txt")
utm_offset = self.read_utm_offset(log_file)
# 修改obj文件的顶点坐标
modified_obj = self.modify_obj_coordinates(obj_path, utm_offset)
grid_files[grid_id] = {
'obj': modified_obj,
'mtl': mtl_path.replace('.mtl', '_utm.mtl'),
'dir': base_dir
}
# 读取obj文件的顶点坐标
vertices, _, _, _, _, _ = self.read_obj(modified_obj)
all_vertices.extend(vertices)
if not grid_files:
self.logger.error("没有找到有效的文件")
return
# 收集所有材质和纹理信息
all_materials = {}
for grid_id, files in grid_files.items():
# 复制并重命名纹理文件
texture_map = self.copy_and_rename_texture(
files['dir'],
output_model_dir,
grid_id
)
# 读取并更新MTL内容
materials = self.read_mtl(files['mtl'])
updated_materials = self.update_mtl_content(
materials,
texture_map,
grid_id
)
all_materials.update(updated_materials)
# 写入合并后的MTL文件
final_mtl = os.path.join(output_model_dir, "textured_model.mtl")
with open(final_mtl, 'w') as f:
for mat_name, content in all_materials.items():
f.write(f"newmtl {mat_name}\n")
for line in content:
f.write(f"{line}\n")
f.write("\n")
# 合并OBJ文件
reference_id = list(grid_files.keys())[0]
merged_obj = grid_files[reference_id]['obj']
temp_files = [] # 记录所有中间文件
for grid_id, files in list(grid_files.items())[1:]:
# 生成临时输出文件名
temp_output = os.path.join(
output_model_dir,
f"temp_merged_{int(time.time())}.obj"
)
temp_files.append(temp_output) # 添加到临时文件列表
self.merge_two_objs(
merged_obj, files['obj'], temp_output, reference_id, grid_id)
merged_obj = temp_output
# 最终结果
final_obj = os.path.join(output_model_dir, "textured_model.obj")
try:
if os.path.exists(final_obj):
os.remove(final_obj)
os.rename(merged_obj, final_obj)
except Exception as e:
self.logger.warning(f"重命名最终文件失败: {str(e)}")
shutil.copy2(merged_obj, final_obj)
try:
os.remove(merged_obj)
except:
pass
# 清理所有临时文件
for temp_file in temp_files:
if os.path.exists(temp_file):
try:
os.remove(temp_file)
except Exception as e:
self.logger.warning(
f"删除临时文件失败: {temp_file}, 错误: {str(e)}")
# 计算中心点经纬度
center_lon, center_lat = self.get_center_coordinates(all_vertices)
self.logger.info(f"模型中心点经纬度: ({center_lon}, {center_lat})")
return center_lon, center_lat
except Exception as e:
self.logger.error(f"合并过程中发生错误: {str(e)}", exc_info=True)
raise
def get_center_coordinates(self, vertices: List[List[float]]) -> Tuple[float, float]:
"""计算顶点的中心点UTM坐标并转换为WGS84经纬度。
注意顶点坐标是相对于整体最小UTM坐标的偏移值需要加回最小UTM坐标
Args:
vertices: 顶点列表每个顶点是[x, y, z]格式x和y是相对于最小UTM坐标的偏移
Returns:
Tuple[float, float]: (longitude, latitude)经纬度坐标
"""
# 计算相对坐标的边界框
x_coords = [v[0] for v in vertices]
y_coords = [v[1] for v in vertices]
# 计算中心点相对坐标
center_x_relative = (min(x_coords) + max(x_coords)) / 2
center_y_relative = (min(y_coords) + max(y_coords)) / 2
# 加回最小UTM坐标得到实际的UTM坐标
center_x_utm = center_x_relative + self.min_east
center_y_utm = center_y_relative + self.min_north
# 转换为WGS84经纬度
lon, lat = self.transformer.transform(center_x_utm, center_y_utm)
self.logger.info(f"模型UTM中心点: ({center_x_utm}, {center_y_utm})")
return lon, lat
def read_mtl(self, mtl_path: str) -> dict: def read_mtl(self, mtl_path: str) -> dict:
"""读取MTL文件内容 """读取MTL文件内容
Returns: Returns:
@ -291,153 +473,54 @@ class MergeObj:
return updated_materials return updated_materials
def merge_grid_obj(self, grid_points: Dict[tuple, pd.DataFrame], translations: Dict[tuple, tuple]): def read_utm_offset(self, log_file: str) -> tuple:
"""合并所有网格的OBJ模型""" """读取UTM偏移量"""
if len(grid_points) == 1: try:
grid_id = list(grid_points.keys())[0] east_offset = None
shutil.copytree(os.path.join(self.output_dir, north_offset = None
f"grid_{grid_id[0]}_{grid_id[1]}",
"project", with open(log_file, 'r') as f:
"odm_texturing"), lines = f.readlines()
os.path.join(self.output_dir, "texturing")) for i, line in enumerate(lines):
os.rename(os.path.join(self.output_dir, "texturing", "odm_textured_model_geo.obj"), if 'utm_north_offset' in line and i + 1 < len(lines):
os.path.join(self.output_dir, "texturing", "textured_model.obj")) north_offset = float(lines[i + 1].strip())
self.logger.info(f"开始执行格式转换") elif 'utm_east_offset' in line and i + 1 < len(lines):
docker_command = ( east_offset = float(lines[i + 1].strip())
f"docker run --rm -it "
f"-v {self.output_dir}/texturing:/data " if east_offset is None or north_offset is None:
f"-e LD_LIBRARY_PATH=/opt/osg/build/lib:$LD_LIBRARY_PATH " raise ValueError("未找到UTM偏移量")
f"osg-ubuntu2004 osgconv /data/textured_model.obj /data/textured_model.osgb"
) return east_offset, north_offset
self.logger.info(docker_command) except Exception as e:
subprocess.run( self.logger.error(f"读取UTM偏移量时发生错误: {str(e)}")
docker_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) raise
self.logger.info(f"格式转换完成")
return def modify_obj_coordinates(self, obj_file: str, utm_offset: tuple) -> str:
"""修改obj文件中的顶点坐标使用相对坐标系"""
east_offset, north_offset = utm_offset
output_obj = obj_file.replace('.obj', '_utm.obj')
try: try:
# 创建输出目录 with open(obj_file, 'r') as f_in, open(output_obj, 'w') as f_out:
output_model_dir = os.path.join(self.output_dir, "texturing") for line in f_in:
os.makedirs(output_model_dir, exist_ok=True) if line.startswith('v '):
# 处理顶点坐标行
parts = line.strip().split()
# 使用相对于整体最小UTM坐标的偏移
x = float(parts[1]) + (east_offset - self.min_east)
y = float(parts[2]) + (north_offset - self.min_north)
z = float(parts[3])
f_out.write(f'v {x:.6f} {y:.6f} {z:.6f}\n')
else:
# 其他行直接写入
f_out.write(line)
# 获取所有有效的网格文件 # 复制材质文件
grid_files = {} mtl_file = obj_file.replace('.obj', '.mtl')
for grid_id, points in grid_points.items(): if os.path.exists(mtl_file):
base_dir = os.path.join( shutil.copy2(mtl_file, mtl_file.replace('.mtl', '_utm.mtl'))
self.output_dir,
f"grid_{grid_id[0]}_{grid_id[1]}",
"project",
"odm_texturing"
)
obj_path = os.path.join(base_dir, "odm_textured_model_geo.obj")
mtl_path = os.path.join(base_dir, "odm_textured_model_geo.mtl")
if not os.path.exists(obj_path) or not os.path.exists(mtl_path):
self.logger.warning(
f"网格 ({grid_id[0]},{grid_id[1]}) 的文件不存在")
continue
grid_files[grid_id] = {
'obj': obj_path,
'mtl': mtl_path,
'dir': base_dir
}
if not grid_files:
self.logger.error("没有找到有效的文件")
return
# 收集所有材质和纹理信息
all_materials = {}
for grid_id, files in grid_files.items():
# 复制并重命名纹理文件
texture_map = self.copy_and_rename_texture(
files['dir'],
output_model_dir,
grid_id
)
# 读取并更新MTL内容
materials = self.read_mtl(files['mtl'])
updated_materials = self.update_mtl_content(
materials,
texture_map,
grid_id
)
all_materials.update(updated_materials)
# 写入合并后的MTL文件
final_mtl = os.path.join(output_model_dir, "textured_model.mtl")
with open(final_mtl, 'w') as f:
for mat_name, content in all_materials.items():
f.write(f"newmtl {mat_name}\n")
for line in content:
f.write(f"{line}\n")
f.write("\n")
# 合并OBJ文件
reference_id = list(grid_files.keys())[0]
merged_obj = grid_files[reference_id]['obj']
temp_files = [] # 记录所有中间文件
for grid_id, files in list(grid_files.items())[1:]:
translation = translations[grid_id]
translation = (translation[0], translation[1], 0)
# 生成临时输出文件名
temp_output = os.path.join(
output_model_dir,
f"temp_merged_{int(time.time())}.obj"
)
temp_files.append(temp_output) # 添加到临时文件列表
self.merge_two_objs(
merged_obj, files['obj'], temp_output, translation, reference_id, grid_id)
merged_obj = temp_output
# 最终结果
final_obj = os.path.join(output_model_dir, "textured_model.obj")
try:
if os.path.exists(final_obj):
os.remove(final_obj)
os.rename(merged_obj, final_obj)
except Exception as e:
self.logger.warning(f"重命名最终文件失败: {str(e)}")
shutil.copy2(merged_obj, final_obj)
try:
os.remove(merged_obj)
except:
pass
# 清理所有临时文件
for temp_file in temp_files:
if os.path.exists(temp_file):
try:
os.remove(temp_file)
except Exception as e:
self.logger.warning(
f"删除临时文件失败: {temp_file}, 错误: {str(e)}")
self.logger.info(
f"模型合并完成,输出目录: {output_model_dir}\n"
f"- OBJ文件: textured_model.obj\n"
f"- MTL文件: textured_model.mtl\n"
f"- 纹理文件: {len(os.listdir(output_model_dir)) - 2}"
)
self.logger.info(f"开始执行格式转换")
docker_command = (
f"docker run --rm -it "
f"-v {output_model_dir}:/data "
f"-e LD_LIBRARY_PATH=/opt/osg/build/lib:$LD_LIBRARY_PATH "
f"osg-ubuntu2004 osgconv /data/textured_model.obj /data/textured_model.osgb"
)
self.logger.info(docker_command)
subprocess.run(
docker_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.logger.info(f"格式转换完成")
return output_obj
except Exception as e: except Exception as e:
self.logger.error(f"合并过程中发生错误: {str(e)}", exc_info=True) self.logger.error(f"修改obj坐标时发生错误: {str(e)}")
raise raise

View File

@ -1,140 +1,63 @@
import os import os
import logging import logging
import subprocess import subprocess
import shutil from typing import Tuple
from typing import List, Tuple
import numpy as np
class ObjPostProcessor: class ObjPostProcessor:
def __init__(self, output_dir: str): def __init__(self, output_dir: str):
self.output_dir = output_dir self.output_dir = output_dir
self.logger = logging.getLogger('UAV_Preprocess.ObjPostProcessor') self.logger = logging.getLogger('UAV_Preprocess.ObjPostProcessor')
# 用于存储所有grid的UTM范围
self.min_east = float('inf')
self.min_north = float('inf')
self.max_east = float('-inf')
self.max_north = float('-inf')
def process_obj_files(self): def create_metadata_xml(self, osgb_dir: str, lon: float, lat: float):
"""处理所有grid中的obj文件""" """创建metadata.xml文件包含地理参考信息
Args:
osgb_dir: osgb输出目录
lon: 中心点经度
lat: 中心点纬度
"""
try: try:
# 1. 遍历所有grid文件夹 metadata_content = f'''<?xml version="1.0" encoding="utf-8"?>
grid_dirs = [d for d in os.listdir( <ModelMetadata version="1">
self.output_dir) if d.startswith('grid_')] <!--Spatial Reference System-->
<SRS>EPSG:4326</SRS>
<!--Origin in Spatial Reference System-->
<SRSOrigin>{lon},{lat},0.000000</SRSOrigin>
<Texture>
<ColorSource>Visible</ColorSource>
</Texture>
</ModelMetadata>'''
# 第一次遍历获取所有grid的UTM范围 # metadata.xml 放在根目录
for grid_dir in grid_dirs: metadata_path = os.path.join(osgb_dir, 'metadata.xml')
grid_path = os.path.join(self.output_dir, grid_dir) with open(metadata_path, 'w', encoding='utf-8') as f:
log_file = os.path.join( f.write(metadata_content)
grid_path, 'project', 'odm_orthophoto', 'odm_orthophoto_log.txt')
east_offset, north_offset = self.read_utm_offset(log_file)
# 更新UTM范围 self.logger.info(f"已创建metadata.xml: {metadata_path}")
self.min_east = min(self.min_east, east_offset)
self.min_north = min(self.min_north, north_offset)
self.max_east = max(self.max_east, east_offset)
self.max_north = max(self.max_north, north_offset)
# 创建osgb输出目录 except Exception as e:
self.logger.error(f"创建metadata.xml时发生错误: {str(e)}")
raise
def convert_to_osgb(self, center_coords: Tuple[float, float]):
"""将obj转换为osgb并创建metadata.xml
Args:
center_coords: (longitude, latitude)中心点经纬度坐标
"""
try:
# 获取合并后的obj文件路径
obj_dir = os.path.join(self.output_dir, 'texturing')
obj_file = os.path.join(obj_dir, 'textured_model.obj')
if not os.path.exists(obj_file):
raise Exception(f"未找到obj文件: {obj_file}")
# 创建osgb目录结构
osgb_dir = os.path.join(self.output_dir, 'osgb') osgb_dir = os.path.join(self.output_dir, 'osgb')
os.makedirs(os.path.join(osgb_dir, 'Data'), exist_ok=True) osgb_data_dir = os.path.join(osgb_dir, 'Data', 'textured_model')
os.makedirs(osgb_data_dir, exist_ok=True)
# 第二次遍历处理每个grid # 输出文件路径
for grid_dir in grid_dirs: output_osgb = os.path.join(osgb_data_dir, 'textured_model.osgb')
grid_path = os.path.join(self.output_dir, grid_dir)
self.process_single_grid(grid_path, osgb_dir)
# 创建metadata.xml
self.create_metadata_xml(osgb_dir)
self.logger.info("所有grid处理完成")
return True
except Exception as e:
self.logger.error(f"处理obj文件时发生错误: {str(e)}")
return False
def process_single_grid(self, grid_path: str, osgb_dir: str):
"""处理单个grid的obj文件"""
try:
# 1. 读取UTM偏移量
log_file = os.path.join(
grid_path, 'project', 'odm_orthophoto', 'odm_orthophoto_log.txt')
utm_offset = self.read_utm_offset(log_file)
# 2. 修改obj文件的顶点坐标
obj_file = os.path.join(
grid_path, 'project', 'odm_texturing', 'odm_textured_model_geo.obj')
modified_obj = self.modify_obj_coordinates(obj_file, utm_offset)
# 3. 使用osgconv转换为osgb
grid_name = os.path.basename(grid_path)
self.convert_to_osgb(modified_obj, grid_name, osgb_dir)
except Exception as e:
self.logger.error(f"处理grid {grid_path} 时发生错误: {str(e)}")
raise
def read_utm_offset(self, log_file: str) -> Tuple[float, float]:
"""读取UTM偏移量"""
try:
east_offset = None
north_offset = None
with open(log_file, 'r') as f:
lines = f.readlines()
for i, line in enumerate(lines):
if 'utm_north_offset' in line and i + 1 < len(lines):
north_offset = float(lines[i + 1].strip())
elif 'utm_east_offset' in line and i + 1 < len(lines):
east_offset = float(lines[i + 1].strip())
if east_offset is None or north_offset is None:
raise ValueError("未找到UTM偏移量")
return east_offset, north_offset
except Exception as e:
self.logger.error(f"读取UTM偏移量时发生错误: {str(e)}")
raise
def modify_obj_coordinates(self, obj_file: str, utm_offset: Tuple[float, float]) -> str:
"""修改obj文件中的顶点坐标使用相对坐标系"""
east_offset, north_offset = utm_offset
output_obj = obj_file.replace('.obj', '_utm.obj')
try:
with open(obj_file, 'r') as f_in, open(output_obj, 'w') as f_out:
for line in f_in:
if line.startswith('v '):
# 处理顶点坐标行
parts = line.strip().split()
# 使用相对于整体最小UTM坐标的偏移
x = float(parts[1]) + (east_offset - self.min_east)
y = float(parts[2]) + (north_offset - self.min_north)
z = float(parts[3])
f_out.write(f'v {x:.6f} {y:.6f} {z:.6f}\n')
else:
# 其他行直接写入
f_out.write(line)
# 复制材质文件
mtl_file = obj_file.replace('.obj', '.mtl')
if os.path.exists(mtl_file):
shutil.copy2(mtl_file, mtl_file.replace('.mtl', '_utm.mtl'))
return output_obj
except Exception as e:
self.logger.error(f"修改obj坐标时发生错误: {str(e)}")
raise
def convert_to_osgb(self, obj_file: str, grid_name: str, osgb_dir: str):
"""使用osgconv将obj转换为osgb"""
try:
# 创建tile目录
tile_dir = os.path.join(osgb_dir, 'Data', grid_name)
os.makedirs(tile_dir, exist_ok=True)
output_osgb = os.path.join(tile_dir, f'{grid_name}.osgb')
# 构建osgconv命令 # 构建osgconv命令
cmd = [ cmd = [
@ -148,35 +71,19 @@ class ObjPostProcessor:
] ]
# 执行命令 # 执行命令
self.logger.info(f"执行osgconv命令{cmd}") self.logger.info(f"执行osgconv命令{' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True) result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0: if result.returncode != 0:
raise Exception(f"osgconv执行失败: {result.stderr}") raise Exception(f"osgb格式转换失败: {result.stderr}")
# 创建metadata.xml
lon, lat = center_coords
self.create_metadata_xml(osgb_dir, lon, lat)
self.logger.info(f"转换完成: {output_osgb}") self.logger.info(f"转换完成: {output_osgb}")
return True
except Exception as e: except Exception as e:
self.logger.error(f"转换osgb时发生错误: {str(e)}") self.logger.error(f"转换osgb时发生错误: {str(e)}")
raise return False
def create_metadata_xml(self, osgb_dir: str):
"""创建metadata.xml文件包含UTM偏移信息"""
try:
# 这里需要将UTM坐标转换为WGS84经纬度坐标
# 这里使用示例值,实际应用中需要进行真实的坐标转换
metadata_content = f'''<?xml version="1.0" encoding="utf-8"?>
<ModelMetadata version="1">
<SRS>EPSG:32649</SRS>
<SRSOrigin>{self.min_east:.6f},{self.min_north:.6f},0.000000</SRSOrigin>
<Texture>
<ColorSource>Visible</ColorSource>
</Texture>
</ModelMetadata>'''
with open(os.path.join(osgb_dir, 'metadata.xml'), 'w') as f:
f.write(metadata_content)
except Exception as e:
self.logger.error(f"创建metadata.xml时发生错误: {str(e)}")
raise