voxcity 0.3.25__py3-none-any.whl → 0.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of voxcity might be problematic. Click here for more details.

@@ -45,37 +45,516 @@ from ..geoprocessor.mesh import (
45
45
  )
46
46
  from .material import get_material_dict
47
47
 
48
- def get_default_voxel_color_map():
49
- return {
50
- -99: [0, 0, 0], # void,
51
- -30: [255, 0, 102], # (Pink) 'Landmark',
52
- -17: [238, 242, 234], # (light gray) 'plaster',
53
- -16: [56, 78, 84], # (Dark blue) 'glass',
54
- -15: [147, 140, 114], # (Light brown) 'stone',
55
- -14: [139, 149, 159], # (Gray) 'metal',
56
- -13: [186, 187, 181], # (Gray) 'concrete',
57
- -12: [248, 166, 2], # (Orange) 'wood',
58
- -11: [81, 59, 56], # (Dark red) 'brick',
59
- -3: [180, 187, 216], # Building
60
- -2: [78, 99, 63], # Tree
61
- -1: [188, 143, 143], # Underground
62
- 1: [239, 228, 176], # 'Bareland (ground surface)',
63
- 2: [123, 130, 59], # 'Rangeland (ground surface)',
64
- 3: [97, 140, 86], # 'Shrub (ground surface)',
65
- 4: [112, 120, 56], # 'Agriculture land (ground surface)',
66
- 5: [116, 150, 66], # 'Tree (ground surface)',
67
- 6: [187, 204, 40], # 'Moss and lichen (ground surface)',
68
- 7: [77, 118, 99], # 'Wet land (ground surface)',
69
- 8: [22, 61, 51], # 'Mangrove (ground surface)',
70
- 9: [44, 66, 133], # 'Water (ground surface)',
71
- 10: [205, 215, 224], # 'Snow and ice (ground surface)',
72
- 11: [108, 119, 129], # 'Developed space (ground surface)',
73
- 12: [59, 62, 87], # 'Road (ground surface)',
74
- 13: [150, 166, 190], # 'Building (ground surface)'
75
- 14: [239, 228, 176], # 'No Data (ground surface)'
76
- }
48
+ # def get_voxel_color_map():
49
+ # return {
50
+ # -99: [0, 0, 0], # void,
51
+ # -30: [255, 0, 102], # (Pink) 'Landmark',
52
+ # -17: [238, 242, 234], # (light gray) 'plaster',
53
+ # -16: [56, 78, 84], # (Dark blue) 'glass',
54
+ # -15: [147, 140, 114], # (Light brown) 'stone',
55
+ # -14: [139, 149, 159], # (Gray) 'metal',
56
+ # -13: [186, 187, 181], # (Gray) 'concrete',
57
+ # -12: [248, 166, 2], # (Orange) 'wood',
58
+ # -11: [81, 59, 56], # (Dark red) 'brick',
59
+ # -3: [180, 187, 216], # Building
60
+ # -2: [78, 99, 63], # Tree
61
+ # -1: [188, 143, 143], # Underground
62
+ # 1: [239, 228, 176], # 'Bareland (ground surface)',
63
+ # 2: [123, 130, 59], # 'Rangeland (ground surface)',
64
+ # 3: [97, 140, 86], # 'Shrub (ground surface)',
65
+ # 4: [112, 120, 56], # 'Agriculture land (ground surface)',
66
+ # 5: [116, 150, 66], # 'Tree (ground surface)',
67
+ # 6: [187, 204, 40], # 'Moss and lichen (ground surface)',
68
+ # 7: [77, 118, 99], # 'Wet land (ground surface)',
69
+ # 8: [22, 61, 51], # 'Mangrove (ground surface)',
70
+ # 9: [44, 66, 133], # 'Water (ground surface)',
71
+ # 10: [205, 215, 224], # 'Snow and ice (ground surface)',
72
+ # 11: [108, 119, 129], # 'Developed space (ground surface)',
73
+ # 12: [59, 62, 87], # 'Road (ground surface)',
74
+ # 13: [150, 166, 190], # 'Building (ground surface)'
75
+ # 14: [239, 228, 176], # 'No Data (ground surface)'
76
+ # }
77
+ def get_voxel_color_map(color_scheme='default'):
78
+ """
79
+ Returns a color map for voxel visualization based on the specified color scheme.
80
+
81
+ Parameters:
82
+ -----------
83
+ color_scheme : str, optional
84
+ The name of the color scheme to use. Options are:
85
+ - 'default': The original color scheme
86
+ - 'high_contrast': High contrast colors for better visibility
87
+ - 'monochrome': Shades of blue
88
+ - 'pastel': Softer, more muted colors
89
+ - 'dark_mode': Darker colors overall
90
+ - 'grayscale': Black and white gradient
91
+ - 'autumn': Warm reds, oranges, and browns
92
+ - 'cool': Cool blues, purples, and cyans
93
+ - 'earth_tones': Natural earth colors
94
+ - 'vibrant': Very bright, saturated colors
95
+
96
+ # NEW:
97
+ - 'cyberpunk': Neon-like purples, pinks, and blues
98
+ - 'tropical': Vibrant greens, oranges, pinks (island vibes)
99
+ - 'vintage': Muted, sepia-like tones
100
+ - 'neon_dreams': Super-bright, nightclub neon palette
101
+
102
+ Returns:
103
+ --------
104
+ dict
105
+ A dictionary mapping voxel IDs to RGB color values
106
+ """
107
+ # ----------------------
108
+ # DO NOT MODIFY DEFAULT
109
+ # ----------------------
110
+ if color_scheme == 'default':
111
+ return {
112
+ -99: [0, 0, 0], # void,
113
+ -30: [255, 0, 102], # (Pink) 'Landmark',
114
+ -17: [238, 242, 234], # (light gray) 'plaster',
115
+ -16: [56, 78, 84], # (Dark blue) 'glass',
116
+ -15: [147, 140, 114], # (Light brown) 'stone',
117
+ -14: [139, 149, 159], # (Gray) 'metal',
118
+ -13: [186, 187, 181], # (Gray) 'concrete',
119
+ -12: [248, 166, 2], # (Orange) 'wood',
120
+ -11: [81, 59, 56], # (Dark red) 'brick',
121
+ -3: [180, 187, 216], # Building
122
+ -2: [78, 99, 63], # Tree
123
+ -1: [188, 143, 143], # Underground
124
+ 1: [239, 228, 176], # 'Bareland (ground surface)',
125
+ 2: [123, 130, 59], # 'Rangeland (ground surface)',
126
+ 3: [97, 140, 86], # 'Shrub (ground surface)',
127
+ 4: [112, 120, 56], # 'Agriculture land (ground surface)',
128
+ 5: [116, 150, 66], # 'Tree (ground surface)',
129
+ 6: [187, 204, 40], # 'Moss and lichen (ground surface)',
130
+ 7: [77, 118, 99], # 'Wet land (ground surface)',
131
+ 8: [22, 61, 51], # 'Mangrove (ground surface)',
132
+ 9: [44, 66, 133], # 'Water (ground surface)',
133
+ 10: [205, 215, 224], # 'Snow and ice (ground surface)',
134
+ 11: [108, 119, 129], # 'Developed space (ground surface)',
135
+ 12: [59, 62, 87], # 'Road (ground surface)',
136
+ 13: [150, 166, 190], # 'Building (ground surface)'
137
+ 14: [239, 228, 176], # 'No Data (ground surface)'
138
+ }
139
+
140
+ elif color_scheme == 'high_contrast':
141
+ return {
142
+ -99: [0, 0, 0], # void
143
+ -30: [255, 0, 255], # (Bright Magenta) 'Landmark'
144
+ -17: [255, 255, 255], # (Pure White) 'plaster'
145
+ -16: [0, 0, 255], # (Bright Blue) 'glass'
146
+ -15: [153, 76, 0], # (Dark Brown) 'stone'
147
+ -14: [192, 192, 192], # (Silver) 'metal'
148
+ -13: [128, 128, 128], # (Gray) 'concrete'
149
+ -12: [255, 128, 0], # (Bright Orange) 'wood'
150
+ -11: [153, 0, 0], # (Dark Red) 'brick'
151
+ -3: [0, 255, 255], # (Cyan) Building
152
+ -2: [0, 153, 0], # (Green) Tree
153
+ -1: [204, 0, 102], # (Dark Pink) Underground
154
+ 1: [255, 255, 153], # (Light Yellow) 'Bareland'
155
+ 2: [102, 153, 0], # (Olive Green) 'Rangeland'
156
+ 3: [0, 204, 0], # (Bright Green) 'Shrub'
157
+ 4: [153, 204, 0], # (Yellowish Green) 'Agriculture land'
158
+ 5: [0, 102, 0], # (Dark Green) 'Tree'
159
+ 6: [204, 255, 51], # (Lime Green) 'Moss and lichen'
160
+ 7: [0, 153, 153], # (Teal) 'Wet land'
161
+ 8: [0, 51, 0], # (Very Dark Green) 'Mangrove'
162
+ 9: [0, 102, 204], # (Bright Blue) 'Water'
163
+ 10: [255, 255, 255], # (White) 'Snow and ice'
164
+ 11: [76, 76, 76], # (Dark Gray) 'Developed space'
165
+ 12: [0, 0, 0], # (Black) 'Road'
166
+ 13: [102, 102, 255], # (Light Purple) 'Building'
167
+ 14: [255, 204, 153], # (Light Orange) 'No Data'
168
+ }
169
+
170
+ elif color_scheme == 'monochrome':
171
+ return {
172
+ -99: [0, 0, 0], # void
173
+ -30: [28, 28, 99], # 'Landmark'
174
+ -17: [242, 242, 242], # 'plaster'
175
+ -16: [51, 51, 153], # 'glass'
176
+ -15: [102, 102, 204], # 'stone'
177
+ -14: [153, 153, 204], # 'metal'
178
+ -13: [204, 204, 230], # 'concrete'
179
+ -12: [76, 76, 178], # 'wood'
180
+ -11: [25, 25, 127], # 'brick'
181
+ -3: [179, 179, 230], # Building
182
+ -2: [51, 51, 153], # Tree
183
+ -1: [102, 102, 178], # Underground
184
+ 1: [230, 230, 255], # 'Bareland'
185
+ 2: [128, 128, 204], # 'Rangeland'
186
+ 3: [102, 102, 204], # 'Shrub'
187
+ 4: [153, 153, 230], # 'Agriculture land'
188
+ 5: [76, 76, 178], # 'Tree'
189
+ 6: [204, 204, 255], # 'Moss and lichen'
190
+ 7: [76, 76, 178], # 'Wet land'
191
+ 8: [25, 25, 127], # 'Mangrove'
192
+ 9: [51, 51, 204], # 'Water'
193
+ 10: [242, 242, 255], # 'Snow and ice'
194
+ 11: [128, 128, 178], # 'Developed space'
195
+ 12: [51, 51, 127], # 'Road'
196
+ 13: [153, 153, 204], # 'Building'
197
+ 14: [230, 230, 255], # 'No Data'
198
+ }
199
+
200
+ elif color_scheme == 'pastel':
201
+ return {
202
+ -99: [0, 0, 0], # void
203
+ -30: [255, 179, 217], # (Pastel Pink) 'Landmark'
204
+ -17: [245, 245, 245], # (Off White) 'plaster'
205
+ -16: [173, 196, 230], # (Pastel Blue) 'glass'
206
+ -15: [222, 213, 196], # (Pastel Brown) 'stone'
207
+ -14: [211, 219, 226], # (Pastel Gray) 'metal'
208
+ -13: [226, 226, 226], # (Light Gray) 'concrete'
209
+ -12: [255, 223, 179], # (Pastel Orange) 'wood'
210
+ -11: [204, 168, 166], # (Pastel Red) 'brick'
211
+ -3: [214, 217, 235], # (Pastel Purple) Building
212
+ -2: [190, 207, 180], # (Pastel Green) Tree
213
+ -1: [235, 204, 204], # (Pastel Pink) Underground
214
+ 1: [250, 244, 227], # (Cream) 'Bareland'
215
+ 2: [213, 217, 182], # (Pastel Olive) 'Rangeland'
216
+ 3: [200, 226, 195], # (Pastel Green) 'Shrub'
217
+ 4: [209, 214, 188], # (Pastel Yellow-Green) 'Agriculture land'
218
+ 5: [195, 220, 168], # (Light Pastel Green) 'Tree'
219
+ 6: [237, 241, 196], # (Pastel Yellow) 'Moss and lichen'
220
+ 7: [180, 210, 205], # (Pastel Teal) 'Wet land'
221
+ 8: [176, 196, 190], # (Darker Pastel Teal) 'Mangrove'
222
+ 9: [188, 206, 235], # (Pastel Blue) 'Water'
223
+ 10: [242, 245, 250], # (Light Blue-White) 'Snow and ice'
224
+ 11: [209, 213, 219], # (Pastel Gray) 'Developed space'
225
+ 12: [189, 190, 204], # (Pastel Blue-Gray) 'Road'
226
+ 13: [215, 221, 232], # (Very Light Pastel Blue) 'Building'
227
+ 14: [250, 244, 227], # (Cream) 'No Data'
228
+ }
229
+
230
+ elif color_scheme == 'dark_mode':
231
+ return {
232
+ -99: [0, 0, 0], # void
233
+ -30: [153, 51, 102], # (Dark Pink) 'Landmark'
234
+ -17: [76, 76, 76], # (Dark Gray) 'plaster'
235
+ -16: [33, 46, 51], # (Very Dark Blue) 'glass'
236
+ -15: [89, 84, 66], # (Very Dark Brown) 'stone'
237
+ -14: [83, 89, 94], # (Dark Gray) 'metal'
238
+ -13: [61, 61, 61], # (Dark Gray) 'concrete'
239
+ -12: [153, 102, 0], # (Dark Orange) 'wood'
240
+ -11: [51, 35, 33], # (Very Dark Red) 'brick'
241
+ -3: [78, 82, 99], # (Dark Purple) Building
242
+ -2: [46, 58, 37], # (Dark Green) Tree
243
+ -1: [99, 68, 68], # (Dark Pink) Underground
244
+ 1: [102, 97, 75], # (Dark Yellow) 'Bareland'
245
+ 2: [61, 66, 31], # (Dark Olive) 'Rangeland'
246
+ 3: [46, 77, 46], # (Dark Green) 'Shrub'
247
+ 4: [56, 61, 28], # (Dark Yellow-Green) 'Agriculture land'
248
+ 5: [54, 77, 31], # (Dark Green) 'Tree'
249
+ 6: [89, 97, 20], # (Dark Yellow) 'Moss and lichen'
250
+ 7: [38, 59, 49], # (Dark Teal) 'Wet land'
251
+ 8: [16, 31, 26], # (Very Dark Green) 'Mangrove'
252
+ 9: [22, 33, 66], # (Dark Blue) 'Water'
253
+ 10: [82, 87, 92], # (Dark Blue-Gray) 'Snow and ice'
254
+ 11: [46, 51, 56], # (Dark Gray) 'Developed space'
255
+ 12: [25, 31, 43], # (Very Dark Blue) 'Road'
256
+ 13: [56, 64, 82], # (Dark Blue-Gray) 'Building'
257
+ 14: [102, 97, 75], # (Dark Yellow) 'No Data'
258
+ }
259
+
260
+ elif color_scheme == 'grayscale':
261
+ return {
262
+ -99: [0, 0, 0], # void (black)
263
+ -30: [200, 200, 200], # 'Landmark'
264
+ -17: [240, 240, 240], # 'plaster'
265
+ -16: [60, 60, 60], # 'glass'
266
+ -15: [130, 130, 130], # 'stone'
267
+ -14: [150, 150, 150], # 'metal'
268
+ -13: [180, 180, 180], # 'concrete'
269
+ -12: [170, 170, 170], # 'wood'
270
+ -11: [70, 70, 70], # 'brick'
271
+ -3: [190, 190, 190], # Building
272
+ -2: [90, 90, 90], # Tree
273
+ -1: [160, 160, 160], # Underground
274
+ 1: [230, 230, 230], # 'Bareland'
275
+ 2: [120, 120, 120], # 'Rangeland'
276
+ 3: [110, 110, 110], # 'Shrub'
277
+ 4: [115, 115, 115], # 'Agriculture land'
278
+ 5: [100, 100, 100], # 'Tree'
279
+ 6: [210, 210, 210], # 'Moss and lichen'
280
+ 7: [95, 95, 95], # 'Wet land'
281
+ 8: [40, 40, 40], # 'Mangrove'
282
+ 9: [50, 50, 50], # 'Water'
283
+ 10: [220, 220, 220], # 'Snow and ice'
284
+ 11: [140, 140, 140], # 'Developed space'
285
+ 12: [30, 30, 30], # 'Road'
286
+ 13: [170, 170, 170], # 'Building'
287
+ 14: [230, 230, 230], # 'No Data'
288
+ }
289
+
290
+ elif color_scheme == 'autumn':
291
+ return {
292
+ -99: [0, 0, 0], # void
293
+ -30: [227, 66, 52], # (Red) 'Landmark'
294
+ -17: [250, 240, 230], # (Antique White) 'plaster'
295
+ -16: [94, 33, 41], # (Dark Red) 'glass'
296
+ -15: [160, 120, 90], # (Medium Brown) 'stone'
297
+ -14: [176, 141, 87], # (Bronze) 'metal'
298
+ -13: [205, 186, 150], # (Tan) 'concrete'
299
+ -12: [204, 85, 0], # (Dark Orange) 'wood'
300
+ -11: [128, 55, 36], # (Rust) 'brick'
301
+ -3: [222, 184, 135], # (Tan) Building
302
+ -2: [107, 68, 35], # (Brown) Tree
303
+ -1: [165, 105, 79], # (Copper) Underground
304
+ 1: [255, 235, 205], # (Blanched Almond) 'Bareland'
305
+ 2: [133, 99, 99], # (Brown) 'Rangeland'
306
+ 3: [139, 69, 19], # (Saddle Brown) 'Shrub'
307
+ 4: [160, 82, 45], # (Sienna) 'Agriculture land'
308
+ 5: [101, 67, 33], # (Dark Brown) 'Tree'
309
+ 6: [255, 228, 196], # (Bisque) 'Moss and lichen'
310
+ 7: [138, 51, 36], # (Rust) 'Wet land'
311
+ 8: [85, 45, 23], # (Deep Brown) 'Mangrove'
312
+ 9: [175, 118, 70], # (Light Brown) 'Water'
313
+ 10: [255, 250, 240], # (Floral White) 'Snow and ice'
314
+ 11: [188, 143, 143], # (Rosy Brown) 'Developed space'
315
+ 12: [69, 41, 33], # (Very Dark Brown) 'Road'
316
+ 13: [210, 180, 140], # (Tan) 'Building'
317
+ 14: [255, 235, 205], # (Blanched Almond) 'No Data'
318
+ }
319
+
320
+ elif color_scheme == 'cool':
321
+ return {
322
+ -99: [0, 0, 0], # void
323
+ -30: [180, 82, 205], # (Purple) 'Landmark'
324
+ -17: [240, 248, 255], # (Alice Blue) 'plaster'
325
+ -16: [70, 130, 180], # (Steel Blue) 'glass'
326
+ -15: [100, 149, 237], # (Cornflower Blue) 'stone'
327
+ -14: [176, 196, 222], # (Light Steel Blue) 'metal'
328
+ -13: [240, 255, 255], # (Azure) 'concrete'
329
+ -12: [65, 105, 225], # (Royal Blue) 'wood'
330
+ -11: [95, 158, 160], # (Cadet Blue) 'brick'
331
+ -3: [135, 206, 235], # (Sky Blue) Building
332
+ -2: [0, 128, 128], # (Teal) Tree
333
+ -1: [127, 255, 212], # (Aquamarine) Underground
334
+ 1: [220, 240, 250], # (Light Blue) 'Bareland'
335
+ 2: [72, 209, 204], # (Medium Turquoise) 'Rangeland'
336
+ 3: [0, 191, 255], # (Deep Sky Blue) 'Shrub'
337
+ 4: [100, 149, 237], # (Cornflower Blue) 'Agriculture land'
338
+ 5: [0, 128, 128], # (Teal) 'Tree'
339
+ 6: [175, 238, 238], # (Pale Turquoise) 'Moss and lichen'
340
+ 7: [32, 178, 170], # (Light Sea Green) 'Wet land'
341
+ 8: [25, 25, 112], # (Midnight Blue) 'Mangrove'
342
+ 9: [30, 144, 255], # (Dodger Blue) 'Water'
343
+ 10: [240, 255, 255], # (Azure) 'Snow and ice'
344
+ 11: [119, 136, 153], # (Light Slate Gray) 'Developed space'
345
+ 12: [25, 25, 112], # (Midnight Blue) 'Road'
346
+ 13: [173, 216, 230], # (Light Blue) 'Building'
347
+ 14: [220, 240, 250], # (Light Blue) 'No Data'
348
+ }
349
+
350
+ elif color_scheme == 'earth_tones':
351
+ return {
352
+ -99: [0, 0, 0], # void
353
+ -30: [210, 105, 30], # (Chocolate) 'Landmark'
354
+ -17: [245, 245, 220], # (Beige) 'plaster'
355
+ -16: [139, 137, 137], # (Gray) 'glass'
356
+ -15: [160, 120, 90], # (Medium Brown) 'stone'
357
+ -14: [169, 169, 169], # (Dark Gray) 'metal'
358
+ -13: [190, 190, 180], # (Light Gray-Tan) 'concrete'
359
+ -12: [160, 82, 45], # (Sienna) 'wood'
360
+ -11: [139, 69, 19], # (Saddle Brown) 'brick'
361
+ -3: [210, 180, 140], # (Tan) Building
362
+ -2: [85, 107, 47], # (Dark Olive Green) Tree
363
+ -1: [133, 94, 66], # (Beaver) Underground
364
+ 1: [222, 184, 135], # (Burlywood) 'Bareland'
365
+ 2: [107, 142, 35], # (Olive Drab) 'Rangeland'
366
+ 3: [85, 107, 47], # (Dark Olive Green) 'Shrub'
367
+ 4: [128, 128, 0], # (Olive) 'Agriculture land'
368
+ 5: [34, 139, 34], # (Forest Green) 'Tree'
369
+ 6: [189, 183, 107], # (Dark Khaki) 'Moss and lichen'
370
+ 7: [143, 188, 143], # (Dark Sea Green) 'Wet land'
371
+ 8: [46, 139, 87], # (Sea Green) 'Mangrove'
372
+ 9: [95, 158, 160], # (Cadet Blue) 'Water'
373
+ 10: [238, 232, 205], # (Light Tan) 'Snow and ice'
374
+ 11: [169, 169, 169], # (Dark Gray) 'Developed space'
375
+ 12: [90, 90, 90], # (Dark Gray) 'Road'
376
+ 13: [188, 170, 152], # (Tan) 'Building'
377
+ 14: [222, 184, 135], # (Burlywood) 'No Data'
378
+ }
379
+
380
+ elif color_scheme == 'vibrant':
381
+ return {
382
+ -99: [0, 0, 0], # void
383
+ -30: [255, 0, 255], # (Magenta) 'Landmark'
384
+ -17: [255, 255, 255], # (White) 'plaster'
385
+ -16: [0, 191, 255], # (Deep Sky Blue) 'glass'
386
+ -15: [255, 215, 0], # (Gold) 'stone'
387
+ -14: [0, 250, 154], # (Medium Spring Green) 'metal'
388
+ -13: [211, 211, 211], # (Light Gray) 'concrete'
389
+ -12: [255, 69, 0], # (Orange Red) 'wood'
390
+ -11: [178, 34, 34], # (Firebrick) 'brick'
391
+ -3: [123, 104, 238], # (Medium Slate Blue) Building
392
+ -2: [50, 205, 50], # (Lime Green) Tree
393
+ -1: [255, 20, 147], # (Deep Pink) Underground
394
+ 1: [255, 255, 0], # (Yellow) 'Bareland'
395
+ 2: [0, 255, 0], # (Lime) 'Rangeland'
396
+ 3: [0, 128, 0], # (Green) 'Shrub'
397
+ 4: [154, 205, 50], # (Yellow Green) 'Agriculture land'
398
+ 5: [34, 139, 34], # (Forest Green) 'Tree'
399
+ 6: [127, 255, 0], # (Chartreuse) 'Moss and lichen'
400
+ 7: [64, 224, 208], # (Turquoise) 'Wet land'
401
+ 8: [0, 100, 0], # (Dark Green) 'Mangrove'
402
+ 9: [0, 0, 255], # (Blue) 'Water'
403
+ 10: [240, 248, 255], # (Alice Blue) 'Snow and ice'
404
+ 11: [128, 128, 128], # (Gray) 'Developed space'
405
+ 12: [47, 79, 79], # (Dark Slate Gray) 'Road'
406
+ 13: [135, 206, 250], # (Light Sky Blue) 'Building'
407
+ 14: [255, 255, 224], # (Light Yellow) 'No Data'
408
+ }
409
+
410
+ # ------------------------------------------------
411
+ # NEWLY ADDED STYLISH COLOR SCHEMES BELOW:
412
+ # ------------------------------------------------
413
+ elif color_scheme == 'cyberpunk':
414
+ """
415
+ Vibrant neon purples, pinks, and blues with deep blacks.
416
+ Think futuristic city vibes and bright neon signs.
417
+ """
418
+ return {
419
+ -99: [0, 0, 0], # void (keep it pitch black)
420
+ -30: [255, 0, 255], # (Neon Magenta) 'Landmark'
421
+ -17: [255, 255, 255], # (Bright White) 'plaster'
422
+ -16: [0, 255, 255], # (Neon Cyan) 'glass'
423
+ -15: [128, 0, 128], # (Purple) 'stone'
424
+ -14: [50, 50, 50], # (Dark Gray) 'metal'
425
+ -13: [102, 0, 102], # (Dark Magenta) 'concrete'
426
+ -12: [255, 20, 147], # (Deep Pink) 'wood'
427
+ -11: [153, 0, 76], # (Deep Purple-Red) 'brick'
428
+ -3: [124, 0, 255], # (Strong Neon Purple) Building
429
+ -2: [0, 255, 153], # (Neon Greenish Cyan) Tree
430
+ -1: [255, 0, 102], # (Hot Pink) Underground
431
+ 1: [255, 255, 153], # (Pale Yellow) 'Bareland'
432
+ 2: [0, 204, 204], # (Teal) 'Rangeland'
433
+ 3: [153, 51, 255], # (Light Purple) 'Shrub'
434
+ 4: [0, 153, 255], # (Bright Neon Blue) 'Agriculture land'
435
+ 5: [0, 255, 153], # (Neon Greenish Cyan) 'Tree'
436
+ 6: [204, 0, 255], # (Vivid Violet) 'Moss and lichen'
437
+ 7: [0, 255, 255], # (Neon Cyan) 'Wet land'
438
+ 8: [0, 102, 102], # (Dark Teal) 'Mangrove'
439
+ 9: [51, 0, 102], # (Deep Indigo) 'Water'
440
+ 10: [255, 255, 255], # (White) 'Snow and ice'
441
+ 11: [102, 102, 102], # (Gray) 'Developed space'
442
+ 12: [0, 0, 0], # (Black) 'Road'
443
+ 13: [204, 51, 255], # (Bright Magenta) 'Building'
444
+ 14: [255, 255, 153], # (Pale Yellow) 'No Data'
445
+ }
446
+
447
+ elif color_scheme == 'tropical':
448
+ """
449
+ Bold, bright 'tropical vacation' color palette.
450
+ Lots of greens, oranges, pinks, reminiscent of island florals.
451
+ """
452
+ return {
453
+ -99: [0, 0, 0], # void
454
+ -30: [255, 99, 164], # (Bright Tropical Pink) 'Landmark'
455
+ -17: [255, 248, 220], # (Cornsilk) 'plaster'
456
+ -16: [0, 150, 136], # (Teal) 'glass'
457
+ -15: [255, 140, 0], # (Dark Orange) 'stone'
458
+ -14: [255, 215, 180], # (Light Peach) 'metal'
459
+ -13: [210, 210, 210], # (Light Gray) 'concrete'
460
+ -12: [255, 165, 0], # (Orange) 'wood'
461
+ -11: [205, 92, 92], # (Indian Red) 'brick'
462
+ -3: [255, 193, 37], # (Tropical Yellow) Building
463
+ -2: [34, 139, 34], # (Forest Green) Tree
464
+ -1: [255, 160, 122], # (Light Salmon) Underground
465
+ 1: [240, 230, 140], # (Khaki) 'Bareland'
466
+ 2: [60, 179, 113], # (Medium Sea Green) 'Rangeland'
467
+ 3: [46, 139, 87], # (Sea Green) 'Shrub'
468
+ 4: [255, 127, 80], # (Coral) 'Agriculture land'
469
+ 5: [50, 205, 50], # (Lime Green) 'Tree'
470
+ 6: [255, 239, 213], # (Papaya Whip) 'Moss and lichen'
471
+ 7: [255, 99, 71], # (Tomato) 'Wet land'
472
+ 8: [47, 79, 79], # (Dark Slate Gray) 'Mangrove'
473
+ 9: [0, 128, 128], # (Teal) 'Water'
474
+ 10: [224, 255, 255], # (Light Cyan) 'Snow and ice'
475
+ 11: [218, 112, 214], # (Orchid) 'Developed space'
476
+ 12: [85, 107, 47], # (Dark Olive Green) 'Road'
477
+ 13: [253, 245, 230], # (Old Lace) 'Building'
478
+ 14: [240, 230, 140], # (Khaki) 'No Data'
479
+ }
480
+
481
+ elif color_scheme == 'vintage':
482
+ """
483
+ A muted, old-photo or sepia-inspired palette
484
+ for a nostalgic or antique look.
485
+ """
486
+ return {
487
+ -99: [0, 0, 0], # void
488
+ -30: [133, 94, 66], # (Beaver/Brownish) 'Landmark'
489
+ -17: [250, 240, 230], # (Antique White) 'plaster'
490
+ -16: [169, 157, 143], # (Muted Brown-Gray) 'glass'
491
+ -15: [181, 166, 127], # (Khaki Tan) 'stone'
492
+ -14: [120, 106, 93], # (Faded Gray-Brown) 'metal'
493
+ -13: [190, 172, 145], # (Light Brown) 'concrete'
494
+ -12: [146, 109, 83], # (Leather Brown) 'wood'
495
+ -11: [125, 80, 70], # (Dusty Brick) 'brick'
496
+ -3: [201, 174, 146], # (Tanned Beige) Building
497
+ -2: [112, 98, 76], # (Faded Olive-Brown) Tree
498
+ -1: [172, 140, 114], # (Light Saddle Brown) Underground
499
+ 1: [222, 202, 166], # (Light Tan) 'Bareland'
500
+ 2: [131, 114, 83], # (Brownish) 'Rangeland'
501
+ 3: [105, 96, 74], # (Dark Olive Brown) 'Shrub'
502
+ 4: [162, 141, 118], # (Beige Brown) 'Agriculture land'
503
+ 5: [95, 85, 65], # (Muted Dark Brown) 'Tree'
504
+ 6: [212, 200, 180], # (Off-White Tan) 'Moss and lichen'
505
+ 7: [140, 108, 94], # (Dusky Mauve-Brown) 'Wet land'
506
+ 8: [85, 73, 60], # (Dark Taupe) 'Mangrove'
507
+ 9: [166, 152, 121], # (Pale Brown) 'Water'
508
+ 10: [250, 245, 235], # (Light Antique White) 'Snow and ice'
509
+ 11: [120, 106, 93], # (Faded Gray-Brown) 'Developed space'
510
+ 12: [77, 66, 55], # (Dark Taupe) 'Road'
511
+ 13: [203, 188, 162], # (Light Warm Gray) 'Building'
512
+ 14: [222, 202, 166], # (Light Tan) 'No Data'
513
+ }
514
+
515
+ elif color_scheme == 'neon_dreams':
516
+ """
517
+ A super-bright, high-energy neon palette.
518
+ Perfect if you want a 'nightclub in 2080' vibe.
519
+ """
520
+ return {
521
+ -99: [0, 0, 0], # void
522
+ -30: [255, 0, 255], # (Magenta) 'Landmark'
523
+ -17: [255, 255, 255], # (White) 'plaster'
524
+ -16: [0, 255, 255], # (Cyan) 'glass'
525
+ -15: [255, 255, 0], # (Yellow) 'stone'
526
+ -14: [0, 255, 0], # (Lime) 'metal'
527
+ -13: [128, 128, 128], # (Gray) 'concrete'
528
+ -12: [255, 165, 0], # (Neon Orange) 'wood'
529
+ -11: [255, 20, 147], # (Deep Pink) 'brick'
530
+ -3: [75, 0, 130], # (Indigo) Building
531
+ -2: [102, 255, 0], # (Bright Lime Green) Tree
532
+ -1: [255, 51, 153], # (Neon Pink) Underground
533
+ 1: [255, 153, 0], # (Bright Orange) 'Bareland'
534
+ 2: [153, 204, 0], # (Vivid Yellow-Green) 'Rangeland'
535
+ 3: [102, 205, 170], # (Aquamarine-ish) 'Shrub'
536
+ 4: [0, 250, 154], # (Medium Spring Green) 'Agriculture land'
537
+ 5: [173, 255, 47], # (Green-Yellow) 'Tree'
538
+ 6: [127, 255, 0], # (Chartreuse) 'Moss and lichen'
539
+ 7: [64, 224, 208], # (Turquoise) 'Wet land'
540
+ 8: [0, 128, 128], # (Teal) 'Mangrove'
541
+ 9: [0, 0, 255], # (Blue) 'Water'
542
+ 10: [224, 255, 255], # (Light Cyan) 'Snow and ice'
543
+ 11: [192, 192, 192], # (Silver) 'Developed space'
544
+ 12: [25, 25, 25], # (Near Black) 'Road'
545
+ 13: [75, 0, 130], # (Indigo) 'Building'
546
+ 14: [255, 153, 0], # (Bright Orange) 'No Data'
547
+ }
548
+
549
+ else:
550
+ # If an unknown color scheme is specified, return the default
551
+ print(f"Unknown color scheme '{color_scheme}'. Using default instead.")
552
+ return get_voxel_color_map('default')
553
+
554
+ def visualize_3d_voxel(voxel_grid, voxel_color_map = 'default', voxel_size=2.0, save_path=None):
555
+
556
+ color_map = get_voxel_color_map(voxel_color_map)
77
557
 
78
- def visualize_3d_voxel(voxel_grid, color_map = get_default_voxel_color_map(), voxel_size=2.0, save_path=None):
79
558
  print("\tVisualizing 3D voxel data")
80
559
  # Create a figure and a 3D axis
81
560
  fig = plt.figure(figsize=(12, 10))
@@ -133,7 +612,10 @@ def visualize_3d_voxel(voxel_grid, color_map = get_default_voxel_color_map(), vo
133
612
  plt.show()
134
613
 
135
614
 
136
- def visualize_3d_voxel_plotly(voxel_grid, color_map = get_default_voxel_color_map(), voxel_size=2.0):
615
+ def visualize_3d_voxel_plotly(voxel_grid, voxel_color_map = 'default', voxel_size=2.0):
616
+
617
+ color_map = get_voxel_color_map(voxel_color_map)
618
+
137
619
  print("Preparing visualization...")
138
620
 
139
621
  print("Processing voxels...")
@@ -1030,21 +1512,10 @@ def visualize_voxcity_multi_view(voxel_array, meshsize, **kwargs):
1030
1512
  pv.global_theme.background = 'white'
1031
1513
  pv.global_theme.window_size = [1024, 768]
1032
1514
  pv.global_theme.jupyter_backend = 'static'
1033
-
1034
- # view_kwargs = {
1035
- # "view_point_height": 1.5, # To set height of view point in meters. Default: 1.5 m.
1036
- # "dem_grid": dem_grid,
1037
- # "colormap": 'viridis', # Choose a colormap. Default: 'viridis'.
1038
- # "obj_export": True, # Set "True" if you want to export the result in an OBJ file.
1039
- # "output_directory": f'output/{key}/obj', # To set directory path for output files. Default: False.
1040
- # "output_file_name": 'gvi', # To set file name excluding extension. Default: 'view_index'.
1041
- # "num_colors": 10, # Number of discrete colors
1042
- # "alpha": 1.0, # Set transparency (0.0 to 1.0)
1043
- # "vmin": 0.0, # Minimum value for colormap normalization
1044
- # "vmax": 1.0 # Maximum value for colormap normalization
1045
- # }
1515
+
1046
1516
  # Parse kwargs
1047
- vox_dict = kwargs.get("vox_dict", get_default_voxel_color_map())
1517
+ voxel_color_map = kwargs.get("voxel_color_map", 'default')
1518
+ vox_dict = get_voxel_color_map(voxel_color_map)
1048
1519
  output_directory = kwargs.get("output_directory", 'output')
1049
1520
  base_filename = kwargs.get("output_file_name", None)
1050
1521
  sim_grid = kwargs.get("sim_grid", None)
@@ -1143,7 +1614,8 @@ def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize,
1143
1614
  pv.global_theme.jupyter_backend = 'static'
1144
1615
 
1145
1616
  # Parse general kwargs
1146
- vox_dict = kwargs.get("vox_dict", get_default_voxel_color_map())
1617
+ voxel_color_map = kwargs.get("voxel_color_map", 'default')
1618
+ vox_dict = get_voxel_color_map(voxel_color_map)
1147
1619
  output_directory = kwargs.get("output_directory", 'output')
1148
1620
  base_filename = kwargs.get("output_file_name", None)
1149
1621
  dem_grid_ori = kwargs.get("dem_grid", None)
@@ -1219,125 +1691,252 @@ def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize,
1219
1691
 
1220
1692
  return meshes
1221
1693
 
1222
- # def create_interactive_scene(meshes):
1223
- # scene = trimesh.Scene()
1224
- # scene.ambient_light = np.array([0.1, 0.1, 0.1, 1.0])
1225
- # scene.directional_light = np.array([0.1, 0.1, 0.1, 1.0])
1226
-
1227
- # for class_id, mesh in meshes.items():
1228
-
1229
- # # If this is our sim_surface, do NOT override the per-face colors.
1230
- # if class_id == "sim_surface":
1231
- # # Just add the mesh as-is, retaining mesh.visual.face_colors
1232
- # scene.add_geometry(mesh, node_name=f"class_{class_id}")
1233
- # else:
1234
- # # Existing code for single-color classes
1235
- # material = trimesh.visual.material.PBRMaterial(
1236
- # baseColorFactor=mesh.visual.face_colors[0],
1237
- # metallicFactor=0.2,
1238
- # roughnessFactor=0.8,
1239
- # emissiveFactor=np.array([0.1, 0.1, 0.1]),
1240
- # alphaMode='OPAQUE'
1241
- # )
1242
- # mesh.visual = trimesh.visual.TextureVisuals(
1243
- # material=material,
1244
- # uv=None
1245
- # )
1246
- # scene.add_geometry(mesh, node_name=f"class_{class_id}")
1247
-
1248
- # # (Optional) add checkboxes if in Jupyter:
1249
- # try:
1250
- # import ipywidgets as widgets
1251
- # from IPython.display import display
1252
-
1253
- # def update_visibility(cid, visible):
1254
- # scene.graph.nodes[f"class_{cid}"].visible = visible
1255
-
1256
- # checkboxes = []
1257
- # for cid in meshes.keys():
1258
- # checkbox = widgets.Checkbox(value=True, description=f'Class {cid}')
1259
- # checkbox.observe(
1260
- # lambda change, _cid=cid: update_visibility(_cid, change['new']),
1261
- # names='value'
1262
- # )
1263
- # checkboxes.append(checkbox)
1264
- # display(widgets.VBox(checkboxes))
1265
- # except ImportError:
1266
- # pass # Not running in Jupyter
1267
-
1268
- # return scene
1269
-
1270
- # def visualize_voxcity_interactive(voxel_array, **kwargs):
1271
- # """
1272
- # Process voxel city data:
1273
- # - create voxel meshes,
1274
- # - optionally create a sim_grid surface mesh,
1275
- # - optionally export,
1276
- # - return a trimesh Scene for visualization.
1277
-
1278
- # Optional arguments via **kwargs:
1279
- # --------------------------------
1280
- # base_filename : str, default "city_model"
1281
- # Base name for exported files (OBJ, STL).
1282
- # sim_grid : 2D np.ndarray or None, default None
1283
- # Simulation array for creating a 2D surface mesh.
1284
- # dem_grid : 2D np.ndarray or None, default None
1285
- # DEM array for the surface mesh. Must match sim_grid shape.
1286
- # meshsize : float, default 1.0
1287
- # Real-world size (in meters) per voxel/cell in x,y,z.
1288
- # z_offset : float, default 1.5
1289
- # Offset added to dem_grid when placing sim_grid surface.
1290
- # cmap_name : str, default 'viridis'
1291
- # Matplotlib colormap name for sim_grid.
1292
- # vmin : float or None, default 0
1293
- # Minimum value for color mapping. If None, auto from data.
1294
- # vmax : float or None, default 1
1295
- # Maximum value for color mapping. If None, auto from data.
1296
- # """
1297
-
1298
- # # 1. Parse **kwargs
1299
- # vox_dict = kwargs.get("vox_dict", get_default_voxel_color_map())
1300
- # base_filename = kwargs.get("base_filename", None)
1301
- # sim_grid = kwargs.get("sim_grid", None)
1302
- # dem_grid = kwargs.get("dem_grid", None)
1303
- # meshsize = kwargs.get("meshsize", 1.0) / 5
1304
- # z_offset = kwargs.get("z_offset", 1.5)
1305
- # cmap_name = kwargs.get("cmap_name", "viridis")
1306
- # vmin = kwargs.get("vmin", 0)
1307
- # vmax = kwargs.get("vmax", 1)
1308
-
1309
- # # 2. Create voxel-based meshes (same logic as before)
1310
- # print("Creating voxel meshes...")
1311
- # meshes = create_city_meshes(voxel_array, vox_dict, meshsize=meshsize)
1312
-
1313
- # # 3. Optionally create the sim_grid surface mesh
1314
- # if sim_grid is not None and dem_grid is not None:
1315
- # print("Creating sim_grid surface mesh...")
1316
- # sim_mesh = create_sim_surface_mesh(
1317
- # sim_grid,
1318
- # dem_grid,
1319
- # meshsize=meshsize,
1320
- # z_offset=z_offset,
1321
- # cmap_name=cmap_name,
1322
- # vmin=vmin,
1323
- # vmax=vmax
1324
- # )
1325
- # if sim_mesh is not None:
1326
- # meshes["sim_surface"] = sim_mesh
1327
- # else:
1328
- # print("No valid cells in sim_grid (all NaN?). Skipping surface mesh.")
1329
-
1330
- # # 4. Optionally export
1331
- # if base_filename is not None:
1332
- # print(f"Exporting files to '{base_filename}.*' ...")
1333
- # export_meshes(meshes, base_filename)
1334
- # else:
1335
- # print("Skipping export step.")
1336
-
1337
- # # 5. Create interactive visualization (voxel + optional sim_surface)
1338
- # print("Creating interactive visualization...")
1339
- # scene = create_interactive_scene(meshes)
1340
-
1341
- # scene.show()
1342
-
1343
- # return scene
1694
+ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None, **kwargs):
1695
+ """
1696
+ Create multiple views of the voxel city data with custom simulation meshes replacing specific voxel classes.
1697
+
1698
+ Parameters
1699
+ ----------
1700
+ voxel_array : np.ndarray
1701
+ 3D array of voxel values.
1702
+ meshsize : float
1703
+ Size of each voxel in meters.
1704
+ custom_meshes : dict, optional
1705
+ Dictionary mapping voxel class IDs to custom meshes (e.g., {-3: building_svf_mesh}).
1706
+ These meshes will replace the original voxel meshes for visualization.
1707
+ **kwargs:
1708
+ vox_dict : dict
1709
+ Dictionary mapping voxel class IDs to colors.
1710
+ output_directory : str
1711
+ Directory to save output files.
1712
+ output_file_name : str
1713
+ Base filename for exported meshes.
1714
+ sim_grid : np.ndarray
1715
+ 2D array with simulation values to visualize as a surface.
1716
+ dem_grid : np.ndarray
1717
+ 2D array with elevation values.
1718
+ view_point_height : float
1719
+ Height offset for simulation grid visualization.
1720
+ colormap : str
1721
+ Matplotlib colormap name for simulation results.
1722
+ vmin, vmax : float
1723
+ Min/max values for color mapping.
1724
+ projection_type : str
1725
+ 'perspective' or 'orthogonal'
1726
+ distance_factor : float
1727
+ Adjusts camera distance.
1728
+ colorbar_title : str
1729
+ Title for the colorbar (for simulation results).
1730
+ value_name : str
1731
+ Name of the field in metadata containing values to visualize.
1732
+ nan_color : str or tuple
1733
+ Color to use for NaN values (default: 'gray')
1734
+
1735
+ Returns
1736
+ -------
1737
+ list
1738
+ List of (view_name, image_file_path) tuples for the generated views.
1739
+ """
1740
+ os.system('Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &')
1741
+ os.environ['DISPLAY'] = ':99'
1742
+
1743
+ # Configure PyVista settings
1744
+ pv.set_plot_theme('document')
1745
+ pv.global_theme.background = 'white'
1746
+ pv.global_theme.window_size = [1024, 768]
1747
+ pv.global_theme.jupyter_backend = 'static'
1748
+
1749
+ # Parse kwargs
1750
+ voxel_color_map = kwargs.get("voxel_color_map", 'default')
1751
+ vox_dict = get_voxel_color_map(voxel_color_map)
1752
+ output_directory = kwargs.get("output_directory", 'output')
1753
+ base_filename = kwargs.get("output_file_name", None)
1754
+ sim_grid = kwargs.get("sim_grid", None)
1755
+ dem_grid_ori = kwargs.get("dem_grid", None)
1756
+ if dem_grid_ori is not None:
1757
+ dem_grid = dem_grid_ori - np.min(dem_grid_ori)
1758
+ z_offset = kwargs.get("view_point_height", 1.5)
1759
+ cmap_name = kwargs.get("colormap", "viridis")
1760
+ vmin = kwargs.get("vmin", None)
1761
+ vmax = kwargs.get("vmax", None)
1762
+ projection_type = kwargs.get("projection_type", "perspective")
1763
+ distance_factor = kwargs.get("distance_factor", 1.0)
1764
+ colorbar_title = kwargs.get("colorbar_title", "")
1765
+ value_name = kwargs.get("value_name", None)
1766
+ nan_color = kwargs.get("nan_color", "gray")
1767
+
1768
+ if value_name is None:
1769
+ print("Set value_name")
1770
+
1771
+ # Create meshes from voxel data
1772
+ print("Creating voxel meshes...")
1773
+ meshes = create_city_meshes(voxel_array, vox_dict, meshsize=meshsize)
1774
+
1775
+ # Replace specific voxel class meshes with custom simulation meshes
1776
+ if custom_meshes is not None:
1777
+ for class_id, custom_mesh in custom_meshes.items():
1778
+ # Apply coloring to custom meshes if they have metadata values
1779
+ if hasattr(custom_mesh, 'metadata') and value_name in custom_mesh.metadata:
1780
+ # Create a colored copy of the mesh for visualization
1781
+ import matplotlib.cm as cm
1782
+ import matplotlib.colors as mcolors
1783
+
1784
+ # Get values from metadata
1785
+ values = custom_mesh.metadata[value_name]
1786
+
1787
+ # Set vmin/vmax if not provided
1788
+ local_vmin = vmin if vmin is not None else np.nanmin(values[~np.isnan(values)])
1789
+ local_vmax = vmax if vmax is not None else np.nanmax(values[~np.isnan(values)])
1790
+
1791
+ # Create colors
1792
+ cmap = cm.get_cmap(cmap_name)
1793
+ norm = mcolors.Normalize(vmin=local_vmin, vmax=local_vmax)
1794
+
1795
+ # Handle NaN values with custom color
1796
+ face_colors = np.zeros((len(values), 4))
1797
+
1798
+ # Convert string color to RGBA if needed
1799
+ if isinstance(nan_color, str):
1800
+ import matplotlib.colors as mcolors
1801
+ nan_rgba = np.array(mcolors.to_rgba(nan_color))
1802
+ else:
1803
+ # Assume it's already a tuple/list of RGBA values
1804
+ nan_rgba = np.array(nan_color)
1805
+
1806
+ # Apply colors: NaN values get nan_color, others get colormap colors
1807
+ nan_mask = np.isnan(values)
1808
+ face_colors[~nan_mask] = cmap(norm(values[~nan_mask]))
1809
+ face_colors[nan_mask] = nan_rgba
1810
+
1811
+ # Create a copy with colors
1812
+ vis_mesh = custom_mesh.copy()
1813
+ vis_mesh.visual.face_colors = face_colors
1814
+
1815
+ if class_id in meshes:
1816
+ print(f"Replacing voxel class {class_id} with colored custom simulation mesh")
1817
+ meshes[class_id] = vis_mesh
1818
+ else:
1819
+ print(f"Adding colored custom simulation mesh for class {class_id}")
1820
+ meshes[class_id] = vis_mesh
1821
+ else:
1822
+ # No metadata values, use the mesh as is
1823
+ if class_id in meshes:
1824
+ print(f"Replacing voxel class {class_id} with custom simulation mesh")
1825
+ meshes[class_id] = custom_mesh
1826
+ else:
1827
+ print(f"Adding custom simulation mesh for class {class_id}")
1828
+ meshes[class_id] = custom_mesh
1829
+
1830
+ # Create sim_grid surface mesh if provided
1831
+ if sim_grid is not None and dem_grid is not None:
1832
+ print("Creating sim_grid surface mesh...")
1833
+
1834
+ # If vmin/vmax not provided, use actual min/max of the valid sim data
1835
+ if vmin is None:
1836
+ vmin = np.nanmin(sim_grid)
1837
+ if vmax is None:
1838
+ vmax = np.nanmax(sim_grid)
1839
+
1840
+ sim_mesh = create_sim_surface_mesh(
1841
+ sim_grid, dem_grid,
1842
+ meshsize=meshsize,
1843
+ z_offset=z_offset,
1844
+ cmap_name=cmap_name,
1845
+ vmin=vmin,
1846
+ vmax=vmax,
1847
+ nan_color=nan_color # Pass nan_color to the mesh creation
1848
+ )
1849
+ if sim_mesh is not None:
1850
+ meshes["sim_surface"] = sim_mesh
1851
+
1852
+ # Prepare the colormap and create colorbar
1853
+ norm = mcolors.Normalize(vmin=vmin, vmax=vmax)
1854
+ scalar_map = cm.ScalarMappable(norm=norm, cmap=cmap_name)
1855
+
1856
+ # Create a figure and axis for the colorbar but don't display
1857
+ fig, ax = plt.subplots(figsize=(6, 1))
1858
+ cbar = plt.colorbar(scalar_map, cax=ax, orientation='horizontal')
1859
+ if colorbar_title:
1860
+ cbar.set_label(colorbar_title)
1861
+ plt.tight_layout()
1862
+ plt.show()
1863
+
1864
+ # Export if filename provided
1865
+ if base_filename is not None:
1866
+ print(f"Exporting files to '{base_filename}.*' ...")
1867
+ # Create output directory if it doesn't exist
1868
+ os.makedirs(output_directory, exist_ok=True)
1869
+ export_meshes(meshes, output_directory, base_filename)
1870
+
1871
+ # Create and save multiple views
1872
+ print("Creating multiple views...")
1873
+ # Create output directory if it doesn't exist
1874
+ os.makedirs(output_directory, exist_ok=True)
1875
+ image_files = create_multi_view_scene(meshes, output_directory=output_directory,
1876
+ projection_type=projection_type,
1877
+ distance_factor=distance_factor)
1878
+
1879
+ # Display each view separately
1880
+ for view_name, img_file in image_files:
1881
+ plt.figure(figsize=(12, 8))
1882
+ img = plt.imread(img_file)
1883
+ plt.imshow(img)
1884
+ plt.title(view_name.replace('_', ' ').title(), pad=20)
1885
+ plt.axis('off')
1886
+ plt.show()
1887
+ plt.close()
1888
+
1889
+ return image_files
1890
+
1891
+ def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **kwargs):
1892
+ """
1893
+ Visualize building simulation results by replacing building meshes in the original model.
1894
+
1895
+ This is a specialized wrapper around visualize_voxcity_with_sim_meshes that specifically
1896
+ targets building simulation meshes (assuming building class ID is -3).
1897
+
1898
+ Parameters
1899
+ ----------
1900
+ voxel_array : np.ndarray
1901
+ 3D array of voxel values.
1902
+ meshsize : float
1903
+ Size of each voxel in meters.
1904
+ building_sim_mesh : trimesh.Trimesh
1905
+ Simulation result mesh for buildings with values stored in metadata.
1906
+ **kwargs:
1907
+ Same parameters as visualize_voxcity_with_sim_meshes.
1908
+ Additional parameters:
1909
+ value_name : str
1910
+ Name of the field in metadata containing values to visualize (default: 'svf_values')
1911
+ nan_color : str or tuple
1912
+ Color for NaN values (default: 'gray')
1913
+
1914
+ Returns
1915
+ -------
1916
+ list
1917
+ List of (view_name, image_file_path) tuples for the generated views.
1918
+ """
1919
+ # Building class ID is typically -3 in voxcity
1920
+ building_class_id = kwargs.get("building_class_id", -3)
1921
+
1922
+ # Create custom meshes dictionary with the building simulation mesh
1923
+ custom_meshes = {building_class_id: building_sim_mesh}
1924
+
1925
+ # Add colorbar title if not provided
1926
+ if "colorbar_title" not in kwargs:
1927
+ # Try to guess a title based on the mesh name/type
1928
+ if hasattr(building_sim_mesh, 'name') and building_sim_mesh.name:
1929
+ kwargs["colorbar_title"] = building_sim_mesh.name
1930
+ else:
1931
+ # Use value_field name as fallback
1932
+ value_name = kwargs.get("value_name", "svf_values")
1933
+ pretty_name = value_name.replace('_', ' ').title()
1934
+ kwargs["colorbar_title"] = pretty_name
1935
+
1936
+ # Call the more general visualization function
1937
+ return visualize_voxcity_with_sim_meshes(
1938
+ voxel_array,
1939
+ meshsize,
1940
+ custom_meshes=custom_meshes,
1941
+ **kwargs
1942
+ )