用到的库:triangle库,用于构建约束三角网
整个拉起白膜过程为:
1、获取shp底面数点数据(这里需要注意geopandas读取面数据首尾点为一样的,所以构建三角网以及构建约束边的时候需要去除这个重复点)
2、底面数据Z值加上建筑物高度为顶面点数据
3、对底面点数据约束构网,底面边数据即为约束边,同理顶面数据构网结果与底面一致,故加上相应值即可
4、对侧面进行构网,侧面为多个矩形组成,矩形可以划分为两个三角形
5、输出文件
整个过程可以根据需求从属性表中添加其他数据,比如建筑物层数,建筑物高度,建筑物底面高度等,这些高度都可以作为建筑物高度或者建筑物底面的高度
import geopandas as gpd
import numpy as np
from shapely.geometry import Polygon
import triangle # 用于三角化
# 读取shp文件
shapefile_path = r'E:\data\building.shp'
gdf = gpd.read_file(shapefile_path)
buildingHeight = 18
# 存储OBJ文件的内容
obj_content = []
# 生成OBJ文件头部
obj_content.append("# Generated OBJ file\n")
# 用于记录顶点索引的计数器
vertex_counter = 1
def polygon_to_triangles(polygon):
# 将Shapely的多边形转换为triangle库能处理的格式
coords = np.array(polygon.exterior.coords[:-1])
# 定义三角化的边界约束
edges = []
for i in range(len(coords) - 1):
edges.append([i, i + 1])
# 闭合边界
edges.append([len(coords) - 1, 0])
# 将边界约束转化为 numpy 数组
edges = np.array(edges)
# 定义三角化的边界约束
constraints = {'segments': edges}
# 三角化
triangulation = triangle.triangulate({'vertices': coords, 'segments': constraints['segments']}, '-p')
return triangulation
for idx, row in gdf.iterrows():
geom = row.geometry
if isinstance(geom, Polygon):
triangulation = polygon_to_triangles(geom)
coords = np.array(geom.exterior.coords[:-1])
height = row['MEAN']
# 添加底面顶点
for coord in coords:
obj_content.append(f"v {coord[0]} {coord[1]} {height}\n")
# 添加顶部顶点
for coord in coords:
obj_content.append(f"v {coord[0]} {coord[1]} {height + buildingHeight}\n")
num_vertices = len(coords)
num_triangles = len(triangulation['triangles'])
# 添加底面三角形
for triangle_indices in triangulation['triangles']:
obj_content.append("f " + " ".join([str(i + vertex_counter) for i in triangle_indices]) + "\n")
# 添加顶部三角形
offset = num_vertices
for triangle_indices in triangulation['triangles']:
obj_content.append("f " + " ".join([str(i + vertex_counter + offset) for i in triangle_indices]) + "\n")
# 添加侧面三角形
for triangle_indices in triangulation['triangles']:
base_triangle = [vertex_counter + i for i in triangle_indices]
top_triangle = [vertex_counter + i + offset for i in triangle_indices]
obj_content.append(f"f {base_triangle[0]} {base_triangle[1]} {top_triangle[1]}\n")
obj_content.append(f"f {base_triangle[0]} {top_triangle[1]} {top_triangle[0]}\n")
obj_content.append(f"f {base_triangle[1]} {base_triangle[2]} {top_triangle[2]}\n")
obj_content.append(f"f {base_triangle[1]} {top_triangle[2]} {top_triangle[1]}\n")
obj_content.append(f"f {base_triangle[2]} {base_triangle[0]} {top_triangle[0]}\n")
obj_content.append(f"f {base_triangle[2]} {top_triangle[0]} {top_triangle[2]}\n")
vertex_counter += num_vertices * 2
# 保存OBJ文件
with open('buildings.obj', 'w') as f:
f.writelines(obj_content)