voxcity 0.7.0__py3-none-any.whl → 1.0.13__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.
Files changed (81) hide show
  1. voxcity/__init__.py +14 -14
  2. voxcity/downloader/ocean.py +559 -0
  3. voxcity/exporter/__init__.py +12 -12
  4. voxcity/exporter/cityles.py +633 -633
  5. voxcity/exporter/envimet.py +733 -728
  6. voxcity/exporter/magicavoxel.py +333 -333
  7. voxcity/exporter/netcdf.py +238 -238
  8. voxcity/exporter/obj.py +1480 -1480
  9. voxcity/generator/__init__.py +47 -44
  10. voxcity/generator/api.py +727 -675
  11. voxcity/generator/grids.py +394 -379
  12. voxcity/generator/io.py +94 -94
  13. voxcity/generator/pipeline.py +582 -282
  14. voxcity/generator/update.py +429 -0
  15. voxcity/generator/voxelizer.py +18 -6
  16. voxcity/geoprocessor/__init__.py +75 -75
  17. voxcity/geoprocessor/draw.py +1494 -1219
  18. voxcity/geoprocessor/merge_utils.py +91 -91
  19. voxcity/geoprocessor/mesh.py +806 -806
  20. voxcity/geoprocessor/network.py +708 -708
  21. voxcity/geoprocessor/raster/__init__.py +2 -0
  22. voxcity/geoprocessor/raster/buildings.py +435 -428
  23. voxcity/geoprocessor/raster/core.py +31 -0
  24. voxcity/geoprocessor/raster/export.py +93 -93
  25. voxcity/geoprocessor/raster/landcover.py +178 -51
  26. voxcity/geoprocessor/raster/raster.py +1 -1
  27. voxcity/geoprocessor/utils.py +824 -824
  28. voxcity/models.py +115 -113
  29. voxcity/simulator/solar/__init__.py +66 -43
  30. voxcity/simulator/solar/integration.py +336 -336
  31. voxcity/simulator/solar/sky.py +668 -0
  32. voxcity/simulator/solar/temporal.py +792 -434
  33. voxcity/simulator_gpu/__init__.py +115 -0
  34. voxcity/simulator_gpu/common/__init__.py +9 -0
  35. voxcity/simulator_gpu/common/geometry.py +11 -0
  36. voxcity/simulator_gpu/core.py +322 -0
  37. voxcity/simulator_gpu/domain.py +262 -0
  38. voxcity/simulator_gpu/environment.yml +11 -0
  39. voxcity/simulator_gpu/init_taichi.py +154 -0
  40. voxcity/simulator_gpu/integration.py +15 -0
  41. voxcity/simulator_gpu/kernels.py +56 -0
  42. voxcity/simulator_gpu/radiation.py +28 -0
  43. voxcity/simulator_gpu/raytracing.py +623 -0
  44. voxcity/simulator_gpu/sky.py +9 -0
  45. voxcity/simulator_gpu/solar/__init__.py +178 -0
  46. voxcity/simulator_gpu/solar/core.py +66 -0
  47. voxcity/simulator_gpu/solar/csf.py +1249 -0
  48. voxcity/simulator_gpu/solar/domain.py +561 -0
  49. voxcity/simulator_gpu/solar/epw.py +421 -0
  50. voxcity/simulator_gpu/solar/integration.py +2953 -0
  51. voxcity/simulator_gpu/solar/radiation.py +3019 -0
  52. voxcity/simulator_gpu/solar/raytracing.py +686 -0
  53. voxcity/simulator_gpu/solar/reflection.py +533 -0
  54. voxcity/simulator_gpu/solar/sky.py +907 -0
  55. voxcity/simulator_gpu/solar/solar.py +337 -0
  56. voxcity/simulator_gpu/solar/svf.py +446 -0
  57. voxcity/simulator_gpu/solar/volumetric.py +1151 -0
  58. voxcity/simulator_gpu/solar/voxcity.py +2953 -0
  59. voxcity/simulator_gpu/temporal.py +13 -0
  60. voxcity/simulator_gpu/utils.py +25 -0
  61. voxcity/simulator_gpu/view.py +32 -0
  62. voxcity/simulator_gpu/visibility/__init__.py +109 -0
  63. voxcity/simulator_gpu/visibility/geometry.py +278 -0
  64. voxcity/simulator_gpu/visibility/integration.py +808 -0
  65. voxcity/simulator_gpu/visibility/landmark.py +753 -0
  66. voxcity/simulator_gpu/visibility/view.py +944 -0
  67. voxcity/utils/__init__.py +11 -0
  68. voxcity/utils/classes.py +194 -0
  69. voxcity/utils/lc.py +80 -39
  70. voxcity/utils/shape.py +230 -0
  71. voxcity/visualizer/__init__.py +24 -24
  72. voxcity/visualizer/builder.py +43 -43
  73. voxcity/visualizer/grids.py +141 -141
  74. voxcity/visualizer/maps.py +187 -187
  75. voxcity/visualizer/renderer.py +1146 -928
  76. {voxcity-0.7.0.dist-info → voxcity-1.0.13.dist-info}/METADATA +56 -52
  77. voxcity-1.0.13.dist-info/RECORD +116 -0
  78. voxcity-0.7.0.dist-info/RECORD +0 -77
  79. {voxcity-0.7.0.dist-info → voxcity-1.0.13.dist-info}/WHEEL +0 -0
  80. {voxcity-0.7.0.dist-info → voxcity-1.0.13.dist-info}/licenses/AUTHORS.rst +0 -0
  81. {voxcity-0.7.0.dist-info → voxcity-1.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -1,43 +1,43 @@
1
- from __future__ import annotations
2
-
3
- import numpy as np
4
- import trimesh
5
-
6
- from ..models import MeshModel, MeshCollection, VoxelGrid
7
- from ..geoprocessor.mesh import create_city_meshes
8
- from .palette import get_voxel_color_map
9
-
10
-
11
- class MeshBuilder:
12
- """Build mesh collections from voxel grids for rendering/export."""
13
-
14
- @staticmethod
15
- def from_voxel_grid(voxel_grid: VoxelGrid, meshsize: float, voxel_color_map: "str|dict" = "default",
16
- include_classes=None, exclude_classes=None) -> MeshCollection:
17
- if isinstance(voxel_color_map, dict):
18
- vox_dict = voxel_color_map
19
- else:
20
- vox_dict = get_voxel_color_map(voxel_color_map)
21
-
22
- meshes = create_city_meshes(
23
- voxel_grid.classes,
24
- vox_dict,
25
- meshsize=meshsize,
26
- include_classes=include_classes,
27
- exclude_classes=exclude_classes,
28
- )
29
-
30
- collection = MeshCollection()
31
- for key, m in meshes.items():
32
- if m is None:
33
- continue
34
- colors = getattr(m.visual, 'face_colors', None)
35
- collection.add(str(key), MeshModel(
36
- vertices=m.vertices.copy(),
37
- faces=m.faces.copy(),
38
- colors=colors.copy() if colors is not None else None,
39
- name=str(key)
40
- ))
41
- return collection
42
-
43
-
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ import trimesh
5
+
6
+ from ..models import MeshModel, MeshCollection, VoxelGrid
7
+ from ..geoprocessor.mesh import create_city_meshes
8
+ from .palette import get_voxel_color_map
9
+
10
+
11
+ class MeshBuilder:
12
+ """Build mesh collections from voxel grids for rendering/export."""
13
+
14
+ @staticmethod
15
+ def from_voxel_grid(voxel_grid: VoxelGrid, meshsize: float, voxel_color_map: "str|dict" = "default",
16
+ include_classes=None, exclude_classes=None) -> MeshCollection:
17
+ if isinstance(voxel_color_map, dict):
18
+ vox_dict = voxel_color_map
19
+ else:
20
+ vox_dict = get_voxel_color_map(voxel_color_map)
21
+
22
+ meshes = create_city_meshes(
23
+ voxel_grid.classes,
24
+ vox_dict,
25
+ meshsize=meshsize,
26
+ include_classes=include_classes,
27
+ exclude_classes=exclude_classes,
28
+ )
29
+
30
+ collection = MeshCollection()
31
+ for key, m in meshes.items():
32
+ if m is None:
33
+ continue
34
+ colors = getattr(m.visual, 'face_colors', None)
35
+ collection.add(str(key), MeshModel(
36
+ vertices=m.vertices.copy(),
37
+ faces=m.faces.copy(),
38
+ colors=colors.copy() if colors is not None else None,
39
+ name=str(key)
40
+ ))
41
+ return collection
42
+
43
+
@@ -1,141 +1,141 @@
1
- from __future__ import annotations
2
-
3
- import numpy as np
4
- import matplotlib.pyplot as plt
5
- import matplotlib.colors as mcolors
6
- from matplotlib.colors import ListedColormap, BoundaryNorm
7
- import contextily as ctx
8
-
9
- from ..geoprocessor.raster import grid_to_geodataframe
10
- from ..utils.lc import get_land_cover_classes
11
-
12
-
13
- def visualize_landcover_grid_on_basemap(landcover_grid, rectangle_vertices, meshsize, source='Standard', alpha=0.6, figsize=(12, 8), basemap='CartoDB light', show_edge=False, edge_color='black', edge_width=0.5):
14
- land_cover_classes = get_land_cover_classes(source)
15
- gdf = grid_to_geodataframe(landcover_grid, rectangle_vertices, meshsize)
16
- colors = [(r/255, g/255, b/255) for (r,g,b) in land_cover_classes.keys()]
17
- cmap = ListedColormap(colors)
18
- bounds = np.arange(len(colors) + 1)
19
- norm = BoundaryNorm(bounds, cmap.N)
20
- gdf_web = gdf.to_crs(epsg=3857)
21
- fig, ax = plt.subplots(figsize=figsize)
22
- gdf_web.plot(column='value', ax=ax, alpha=alpha, cmap=cmap, norm=norm, legend=True,
23
- legend_kwds={'label': 'Land Cover Class', 'ticks': bounds[:-1] + 0.5, 'boundaries': bounds,
24
- 'format': lambda x, p: list(land_cover_classes.values())[int(x)]},
25
- edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
26
- basemaps = {
27
- 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
28
- 'CartoDB light': ctx.providers.CartoDB.Positron,
29
- 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
30
- 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
31
- 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
32
- }
33
- ctx.add_basemap(ax, source=basemaps[basemap])
34
- ax.set_axis_off()
35
- plt.tight_layout(); plt.show()
36
-
37
-
38
- def visualize_numerical_grid_on_basemap(grid, rectangle_vertices, meshsize, value_name="value", cmap='viridis', vmin=None, vmax=None,
39
- alpha=0.6, figsize=(12, 8), basemap='CartoDB light', show_edge=False, edge_color='black', edge_width=0.5):
40
- gdf = grid_to_geodataframe(grid, rectangle_vertices, meshsize)
41
- gdf_web = gdf.to_crs(epsg=3857)
42
- fig, ax = plt.subplots(figsize=figsize)
43
- gdf_web.plot(column='value', ax=ax, alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, legend=True,
44
- legend_kwds={'label': value_name}, edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
45
- basemaps = {
46
- 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
47
- 'CartoDB light': ctx.providers.CartoDB.Positron,
48
- 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
49
- 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
50
- 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
51
- }
52
- ctx.add_basemap(ax, source=basemaps[basemap])
53
- ax.set_axis_off()
54
- plt.tight_layout(); plt.show()
55
-
56
-
57
- def visualize_numerical_gdf_on_basemap(gdf, value_name="value", cmap='viridis', vmin=None, vmax=None,
58
- alpha=0.6, figsize=(12, 8), basemap='CartoDB light',
59
- show_edge=False, edge_color='black', edge_width=0.5, input_crs=None):
60
- if gdf.crs is None:
61
- if input_crs is not None:
62
- gdf = gdf.set_crs(input_crs, allow_override=True)
63
- else:
64
- try:
65
- minx, miny, maxx, maxy = gdf.total_bounds
66
- looks_like_lonlat = (-180.0 <= minx <= 180.0 and -180.0 <= maxx <= 180.0 and -90.0 <= miny <= 90.0 and -90.0 <= maxy <= 90.0)
67
- except Exception:
68
- looks_like_lonlat = False
69
- if looks_like_lonlat:
70
- gdf = gdf.set_crs("EPSG:4326", allow_override=True)
71
- else:
72
- raise ValueError("Input GeoDataFrame has no CRS. Provide 'input_crs' or set gdf.crs.")
73
-
74
- gdf_web = gdf.to_crs(epsg=3857) if str(gdf.crs) != 'EPSG:3857' else gdf
75
- fig, ax = plt.subplots(figsize=figsize)
76
- gdf_web.plot(column=value_name, ax=ax, alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, legend=True,
77
- legend_kwds={'label': value_name}, edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
78
- basemaps = {
79
- 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
80
- 'CartoDB light': ctx.providers.CartoDB.Positron,
81
- 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
82
- 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
83
- 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
84
- }
85
- ctx.add_basemap(ax, source=basemaps[basemap])
86
- ax.set_axis_off()
87
- plt.tight_layout(); plt.show()
88
-
89
-
90
- def visualize_point_gdf_on_basemap(point_gdf, value_name='value', **kwargs):
91
- import contextily as ctx
92
- defaults = {
93
- 'figsize': (12, 8),
94
- 'colormap': 'viridis',
95
- 'markersize': 20,
96
- 'alpha': 0.7,
97
- 'vmin': None,
98
- 'vmax': None,
99
- 'title': None,
100
- 'basemap_style': ctx.providers.CartoDB.Positron,
101
- 'zoom': 15
102
- }
103
- settings = {**defaults, **kwargs}
104
- fig, ax = plt.subplots(figsize=settings['figsize'])
105
- point_gdf_web = point_gdf.to_crs(epsg=3857)
106
- point_gdf_web.plot(column=value_name, ax=ax, cmap=settings['colormap'], markersize=settings['markersize'], alpha=settings['alpha'], vmin=settings['vmin'], vmax=settings['vmax'], legend=True, legend_kwds={'label': value_name})
107
- ctx.add_basemap(ax, source=settings['basemap_style'], zoom=settings['zoom'])
108
- if settings['title']:
109
- plt.title(settings['title'])
110
- ax.set_axis_off(); plt.tight_layout(); plt.show()
111
-
112
-
113
- def visualize_land_cover_grid(grid, mesh_size, color_map, land_cover_classes):
114
- all_classes = list(land_cover_classes.values())
115
- unique_classes = list(dict.fromkeys(all_classes))
116
- colors = [color_map[cls] for cls in unique_classes]
117
- cmap = mcolors.ListedColormap(colors)
118
- bounds = np.arange(len(unique_classes) + 1)
119
- norm = mcolors.BoundaryNorm(bounds, cmap.N)
120
- class_to_num = {cls: i for i, cls in enumerate(unique_classes)}
121
- numeric_grid = np.vectorize(class_to_num.get)(grid)
122
- plt.figure(figsize=(10, 10))
123
- im = plt.imshow(numeric_grid, cmap=cmap, norm=norm, interpolation='nearest')
124
- cbar = plt.colorbar(im, ticks=bounds[:-1] + 0.5)
125
- cbar.set_ticklabels(unique_classes)
126
- plt.title(f'Land Use/Land Cover Grid (Mesh Size: {mesh_size}m)')
127
- plt.xlabel('Grid Cells (X)')
128
- plt.ylabel('Grid Cells (Y)')
129
- plt.show()
130
-
131
-
132
- def visualize_numerical_grid(grid, mesh_size, title, cmap='viridis', label='Value', vmin=None, vmax=None):
133
- plt.figure(figsize=(10, 10))
134
- plt.imshow(grid, cmap=cmap, vmin=vmin, vmax=vmax)
135
- plt.colorbar(label=label)
136
- plt.title(f'{title} (Mesh Size: {mesh_size}m)')
137
- plt.xlabel('Grid Cells (X)')
138
- plt.ylabel('Grid Cells (Y)')
139
- plt.show()
140
-
141
-
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ import matplotlib.pyplot as plt
5
+ import matplotlib.colors as mcolors
6
+ from matplotlib.colors import ListedColormap, BoundaryNorm
7
+ import contextily as ctx
8
+
9
+ from ..geoprocessor.raster import grid_to_geodataframe
10
+ from ..utils.lc import get_land_cover_classes
11
+
12
+
13
+ def visualize_landcover_grid_on_basemap(landcover_grid, rectangle_vertices, meshsize, source='Standard', alpha=0.6, figsize=(12, 8), basemap='CartoDB light', show_edge=False, edge_color='black', edge_width=0.5):
14
+ land_cover_classes = get_land_cover_classes(source)
15
+ gdf = grid_to_geodataframe(landcover_grid, rectangle_vertices, meshsize)
16
+ colors = [(r/255, g/255, b/255) for (r,g,b) in land_cover_classes.keys()]
17
+ cmap = ListedColormap(colors)
18
+ bounds = np.arange(len(colors) + 1)
19
+ norm = BoundaryNorm(bounds, cmap.N)
20
+ gdf_web = gdf.to_crs(epsg=3857)
21
+ fig, ax = plt.subplots(figsize=figsize)
22
+ gdf_web.plot(column='value', ax=ax, alpha=alpha, cmap=cmap, norm=norm, legend=True,
23
+ legend_kwds={'label': 'Land Cover Class', 'ticks': bounds[:-1] + 0.5, 'boundaries': bounds,
24
+ 'format': lambda x, p: list(land_cover_classes.values())[int(x)]},
25
+ edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
26
+ basemaps = {
27
+ 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
28
+ 'CartoDB light': ctx.providers.CartoDB.Positron,
29
+ 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
30
+ 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
31
+ 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
32
+ }
33
+ ctx.add_basemap(ax, source=basemaps[basemap])
34
+ ax.set_axis_off()
35
+ plt.tight_layout(); plt.show()
36
+
37
+
38
+ def visualize_numerical_grid_on_basemap(grid, rectangle_vertices, meshsize, value_name="value", cmap='viridis', vmin=None, vmax=None,
39
+ alpha=0.6, figsize=(12, 8), basemap='CartoDB light', show_edge=False, edge_color='black', edge_width=0.5):
40
+ gdf = grid_to_geodataframe(grid, rectangle_vertices, meshsize)
41
+ gdf_web = gdf.to_crs(epsg=3857)
42
+ fig, ax = plt.subplots(figsize=figsize)
43
+ gdf_web.plot(column='value', ax=ax, alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, legend=True,
44
+ legend_kwds={'label': value_name}, edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
45
+ basemaps = {
46
+ 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
47
+ 'CartoDB light': ctx.providers.CartoDB.Positron,
48
+ 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
49
+ 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
50
+ 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
51
+ }
52
+ ctx.add_basemap(ax, source=basemaps[basemap])
53
+ ax.set_axis_off()
54
+ plt.tight_layout(); plt.show()
55
+
56
+
57
+ def visualize_numerical_gdf_on_basemap(gdf, value_name="value", cmap='viridis', vmin=None, vmax=None,
58
+ alpha=0.6, figsize=(12, 8), basemap='CartoDB light',
59
+ show_edge=False, edge_color='black', edge_width=0.5, input_crs=None):
60
+ if gdf.crs is None:
61
+ if input_crs is not None:
62
+ gdf = gdf.set_crs(input_crs, allow_override=True)
63
+ else:
64
+ try:
65
+ minx, miny, maxx, maxy = gdf.total_bounds
66
+ looks_like_lonlat = (-180.0 <= minx <= 180.0 and -180.0 <= maxx <= 180.0 and -90.0 <= miny <= 90.0 and -90.0 <= maxy <= 90.0)
67
+ except Exception:
68
+ looks_like_lonlat = False
69
+ if looks_like_lonlat:
70
+ gdf = gdf.set_crs("EPSG:4326", allow_override=True)
71
+ else:
72
+ raise ValueError("Input GeoDataFrame has no CRS. Provide 'input_crs' or set gdf.crs.")
73
+
74
+ gdf_web = gdf.to_crs(epsg=3857) if str(gdf.crs) != 'EPSG:3857' else gdf
75
+ fig, ax = plt.subplots(figsize=figsize)
76
+ gdf_web.plot(column=value_name, ax=ax, alpha=alpha, cmap=cmap, vmin=vmin, vmax=vmax, legend=True,
77
+ legend_kwds={'label': value_name}, edgecolor=edge_color if show_edge else 'none', linewidth=edge_width if show_edge else 0)
78
+ basemaps = {
79
+ 'CartoDB dark': ctx.providers.CartoDB.DarkMatter,
80
+ 'CartoDB light': ctx.providers.CartoDB.Positron,
81
+ 'CartoDB voyager': ctx.providers.CartoDB.Voyager,
82
+ 'CartoDB light no labels': ctx.providers.CartoDB.PositronNoLabels,
83
+ 'CartoDB dark no labels': ctx.providers.CartoDB.DarkMatterNoLabels,
84
+ }
85
+ ctx.add_basemap(ax, source=basemaps[basemap])
86
+ ax.set_axis_off()
87
+ plt.tight_layout(); plt.show()
88
+
89
+
90
+ def visualize_point_gdf_on_basemap(point_gdf, value_name='value', **kwargs):
91
+ import contextily as ctx
92
+ defaults = {
93
+ 'figsize': (12, 8),
94
+ 'colormap': 'viridis',
95
+ 'markersize': 20,
96
+ 'alpha': 0.7,
97
+ 'vmin': None,
98
+ 'vmax': None,
99
+ 'title': None,
100
+ 'basemap_style': ctx.providers.CartoDB.Positron,
101
+ 'zoom': 15
102
+ }
103
+ settings = {**defaults, **kwargs}
104
+ fig, ax = plt.subplots(figsize=settings['figsize'])
105
+ point_gdf_web = point_gdf.to_crs(epsg=3857)
106
+ point_gdf_web.plot(column=value_name, ax=ax, cmap=settings['colormap'], markersize=settings['markersize'], alpha=settings['alpha'], vmin=settings['vmin'], vmax=settings['vmax'], legend=True, legend_kwds={'label': value_name})
107
+ ctx.add_basemap(ax, source=settings['basemap_style'], zoom=settings['zoom'])
108
+ if settings['title']:
109
+ plt.title(settings['title'])
110
+ ax.set_axis_off(); plt.tight_layout(); plt.show()
111
+
112
+
113
+ def visualize_land_cover_grid(grid, mesh_size, color_map, land_cover_classes):
114
+ all_classes = list(land_cover_classes.values())
115
+ unique_classes = list(dict.fromkeys(all_classes))
116
+ colors = [color_map[cls] for cls in unique_classes]
117
+ cmap = mcolors.ListedColormap(colors)
118
+ bounds = np.arange(len(unique_classes) + 1)
119
+ norm = mcolors.BoundaryNorm(bounds, cmap.N)
120
+ class_to_num = {cls: i for i, cls in enumerate(unique_classes)}
121
+ numeric_grid = np.vectorize(class_to_num.get)(grid)
122
+ plt.figure(figsize=(10, 10))
123
+ im = plt.imshow(numeric_grid, cmap=cmap, norm=norm, interpolation='nearest')
124
+ cbar = plt.colorbar(im, ticks=bounds[:-1] + 0.5)
125
+ cbar.set_ticklabels(unique_classes)
126
+ plt.title(f'Land Use/Land Cover Grid (Mesh Size: {mesh_size}m)')
127
+ plt.xlabel('Grid Cells (X)')
128
+ plt.ylabel('Grid Cells (Y)')
129
+ plt.show()
130
+
131
+
132
+ def visualize_numerical_grid(grid, mesh_size, title, cmap='viridis', label='Value', vmin=None, vmax=None):
133
+ plt.figure(figsize=(10, 10))
134
+ plt.imshow(grid, cmap=cmap, vmin=vmin, vmax=vmax)
135
+ plt.colorbar(label=label)
136
+ plt.title(f'{title} (Mesh Size: {mesh_size}m)')
137
+ plt.xlabel('Grid Cells (X)')
138
+ plt.ylabel('Grid Cells (Y)')
139
+ plt.show()
140
+
141
+