-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcitygml_to_mesh.py
164 lines (139 loc) · 4.67 KB
/
citygml_to_mesh.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
"""
CityGML to mesh conversion.
Author:
Taewook Kang ([email protected])
Date:
2025-02-10, 0.1, Initial version. Support only building geometry's boundary.
Reference:
https://trimesh.org/
"""
import json, argparse, os, logging, random, re
import numpy as np, trimesh
from tqdm import tqdm
from lxml import etree
from citygml_parser3 import *
from xsdata.formats.dataclass.parsers import XmlParser
from xsdata.formats.dataclass.parsers.config import ParserConfig
from pathlib import Path
# logging.basicConfig(level=logging.DEBUG)
def parse_bound_lods(value, obj_surface):
lod_objs = value.__dict__.items()
for lod_key, lod_value in lod_objs:
if lod_value == None:
continue
if 'lod' not in lod_key:
continue
obj_lod = {
'LoD': lod_key,
'surface': []
}
multi_surface = lod_value.multi_surface
surface_members = multi_surface.surface_member
for sf in surface_members:
polygon = sf.polygon
exterior = polygon.exterior
linear_ring = exterior.linear_ring
pos_list = linear_ring.pos_list
pos_list_vector = np.array(pos_list.value).reshape(-1, 3)
obj_lod['surface'].append(pos_list_vector)
obj_surface['surface'].append(obj_lod)
def get_key_object(model, key_query):
dict_items = model.__dict__.items()
for k, v in dict_items:
if k == key_query:
return v
return None
def parse_citygml(input_file):
config = ParserConfig(load_dtd=True, process_xinclude=True, fail_on_unknown_properties = False, fail_on_unknown_attributes = False, fail_on_converter_warnings = True)
parser = XmlParser(config)
model = parser.parse(input_file)
city_objects = model.city_object_member
obj_model = {
'bound': None,
'solid': []
}
for city_object in tqdm(city_objects):
building = get_key_object(city_object, 'building')
if building == None:
print('No building object.')
continue
obj_bound = {
'id': 'boundary',
'surface': []
}
for bound in building.boundary:
bound_items = bound.__dict__.items()
for key, value in bound_items:
if value == None:
continue
if '_surface' not in key:
continue
obj_surface = {
'id': key,
'surface': []
}
parse_bound_lods(value, obj_surface)
obj_bound['surface'].append(obj_surface)
obj_model['bound'] = obj_bound
building_items = building.__dict__.items()
for bldg_key, bldg_value in building_items:
if bldg_value == None:
continue
if '_solid' not in bldg_key:
continue
obj_solid = {
'id': bldg_key,
'surface': []
}
lod_obj = bldg_value
solid = lod_obj.solid
exterior = solid.exterior
shell = exterior.shell
for sf in shell.surface_member:
polygon = sf.polygon
exterior = polygon.exterior
linear_ring = exterior.linear_ring
pos_list = linear_ring.pos_list
pos_list_vector = np.array(pos_list.value).reshape(-1, 3)
obj_solid['surface'].append(pos_list_vector)
obj_model['solid'].append(obj_solid)
# make mesh list from obj_model
mesh_list = []
for surface in obj_model['bound']['surface']:
for lod in tqdm(surface['surface']):
for pos_list_vector in lod['surface']:
vertices = pos_list_vector
faces = [[i for i in range(len(vertices))]]
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
mesh_list.append(mesh)
for solid in tqdm(obj_model['solid']):
for pos_list_vector in solid['surface']:
vertices = pos_list_vector
faces = [[i for i in range(len(vertices))]]
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
mesh_list.append(mesh)
return mesh_list
def convert_mesh(input_gml_fname, output_mesh_fname):
mesh = None
try:
mesh_list = parse_citygml(input_gml_fname)
mesh = trimesh.util.concatenate(mesh_list)
mesh.export(output_mesh_fname) # STL, binary PLY, ASCII OFF, OBJ, GLTF/GLB 2.0, COLLADA, etc.
except Exception as e:
print(f'Error: {e}')
return mesh
return mesh
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='CityGML example to convert to mesh.')
parser.add_argument('--input', type=str, default='./sample/ManhattanSmall.gml', help='Input CityGML file')
parser.add_argument('--output', type=str, default='./mesh/ManhattanSmall.glb', help='Output mesh file. STL, binary PLY, ASCII OFF, OBJ, GLTF/GLB 2.0, COLLADA')
parser.add_argument('--show', type=int, default=0, help='Show mesh file. 0=No, 1=Yes')
args = parser.parse_args()
try:
mesh = convert_mesh(args.input, args.output)
if args.show == 1 and mesh != None:
mesh.show()
print("CityGML file converted to mesh.")
except Exception as e:
print("CityGML file conversion failed.")
print(e)