voxcity 0.6.26__py3-none-any.whl → 0.7.0__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 (75) hide show
  1. voxcity/__init__.py +14 -8
  2. voxcity/downloader/__init__.py +2 -1
  3. voxcity/downloader/gba.py +210 -0
  4. voxcity/downloader/gee.py +5 -1
  5. voxcity/downloader/mbfp.py +1 -1
  6. voxcity/downloader/oemj.py +80 -8
  7. voxcity/downloader/utils.py +73 -73
  8. voxcity/errors.py +30 -0
  9. voxcity/exporter/__init__.py +13 -5
  10. voxcity/exporter/cityles.py +633 -538
  11. voxcity/exporter/envimet.py +728 -708
  12. voxcity/exporter/magicavoxel.py +334 -297
  13. voxcity/exporter/netcdf.py +238 -211
  14. voxcity/exporter/obj.py +1481 -1406
  15. voxcity/generator/__init__.py +44 -0
  16. voxcity/generator/api.py +675 -0
  17. voxcity/generator/grids.py +379 -0
  18. voxcity/generator/io.py +94 -0
  19. voxcity/generator/pipeline.py +282 -0
  20. voxcity/generator/voxelizer.py +380 -0
  21. voxcity/geoprocessor/__init__.py +75 -6
  22. voxcity/geoprocessor/conversion.py +153 -0
  23. voxcity/geoprocessor/draw.py +62 -12
  24. voxcity/geoprocessor/heights.py +199 -0
  25. voxcity/geoprocessor/io.py +101 -0
  26. voxcity/geoprocessor/merge_utils.py +91 -0
  27. voxcity/geoprocessor/mesh.py +806 -790
  28. voxcity/geoprocessor/network.py +708 -679
  29. voxcity/geoprocessor/overlap.py +84 -0
  30. voxcity/geoprocessor/raster/__init__.py +82 -0
  31. voxcity/geoprocessor/raster/buildings.py +428 -0
  32. voxcity/geoprocessor/raster/canopy.py +258 -0
  33. voxcity/geoprocessor/raster/core.py +150 -0
  34. voxcity/geoprocessor/raster/export.py +93 -0
  35. voxcity/geoprocessor/raster/landcover.py +156 -0
  36. voxcity/geoprocessor/raster/raster.py +110 -0
  37. voxcity/geoprocessor/selection.py +85 -0
  38. voxcity/geoprocessor/utils.py +18 -14
  39. voxcity/models.py +113 -0
  40. voxcity/simulator/common/__init__.py +22 -0
  41. voxcity/simulator/common/geometry.py +98 -0
  42. voxcity/simulator/common/raytracing.py +450 -0
  43. voxcity/simulator/solar/__init__.py +43 -0
  44. voxcity/simulator/solar/integration.py +336 -0
  45. voxcity/simulator/solar/kernels.py +62 -0
  46. voxcity/simulator/solar/radiation.py +648 -0
  47. voxcity/simulator/solar/temporal.py +434 -0
  48. voxcity/simulator/view.py +36 -2286
  49. voxcity/simulator/visibility/__init__.py +29 -0
  50. voxcity/simulator/visibility/landmark.py +392 -0
  51. voxcity/simulator/visibility/view.py +508 -0
  52. voxcity/utils/logging.py +61 -0
  53. voxcity/utils/orientation.py +51 -0
  54. voxcity/utils/weather/__init__.py +26 -0
  55. voxcity/utils/weather/epw.py +146 -0
  56. voxcity/utils/weather/files.py +36 -0
  57. voxcity/utils/weather/onebuilding.py +486 -0
  58. voxcity/visualizer/__init__.py +24 -0
  59. voxcity/visualizer/builder.py +43 -0
  60. voxcity/visualizer/grids.py +141 -0
  61. voxcity/visualizer/maps.py +187 -0
  62. voxcity/visualizer/palette.py +228 -0
  63. voxcity/visualizer/renderer.py +928 -0
  64. {voxcity-0.6.26.dist-info → voxcity-0.7.0.dist-info}/METADATA +107 -34
  65. voxcity-0.7.0.dist-info/RECORD +77 -0
  66. voxcity/generator.py +0 -1302
  67. voxcity/geoprocessor/grid.py +0 -1739
  68. voxcity/geoprocessor/polygon.py +0 -1344
  69. voxcity/simulator/solar.py +0 -2339
  70. voxcity/utils/visualization.py +0 -2849
  71. voxcity/utils/weather.py +0 -1038
  72. voxcity-0.6.26.dist-info/RECORD +0 -38
  73. {voxcity-0.6.26.dist-info → voxcity-0.7.0.dist-info}/WHEEL +0 -0
  74. {voxcity-0.6.26.dist-info → voxcity-0.7.0.dist-info}/licenses/AUTHORS.rst +0 -0
  75. {voxcity-0.6.26.dist-info → voxcity-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,211 +1,238 @@
1
- """
2
- NetCDF export utilities for VoxCity.
3
-
4
- This module provides functions to convert a 3D voxel grid produced by
5
- `voxcity.generator.get_voxcity` into a NetCDF file for portable storage
6
- and downstream analysis.
7
-
8
- The voxel values follow VoxCity conventions (see generator.create_3d_voxel):
9
- - -3: built structures (buildings)
10
- - -2: vegetation canopy
11
- - -1: subsurface/underground
12
- - >= 1: ground-surface land cover code (offset by +1 from source classes)
13
-
14
- Notes
15
- -----
16
- - This writer prefers xarray for NetCDF export. If xarray is not installed,
17
- a clear error is raised with installation hints.
18
- - Coordinates are stored as index-based distances in meters from the grid
19
- origin along the y, x, and z axes. Geographic metadata such as the
20
- `rectangle_vertices` and `meshsize_m` are stored as global attributes to
21
- avoid making assumptions about map projection or geodesic conversions.
22
- """
23
-
24
- from __future__ import annotations
25
-
26
- from pathlib import Path
27
- from typing import Any, Dict, Mapping, MutableMapping, Optional, Sequence, Tuple
28
- import json
29
-
30
- import numpy as np
31
-
32
- try: # xarray is the preferred backend for NetCDF writing
33
- import xarray as xr # type: ignore
34
- XR_AVAILABLE = True
35
- except Exception: # pragma: no cover - optional dependency
36
- XR_AVAILABLE = False
37
-
38
- __all__ = [
39
- "voxel_to_xarray_dataset",
40
- "save_voxel_netcdf",
41
- ]
42
-
43
-
44
- def _ensure_parent_dir(path: Path) -> None:
45
- path.parent.mkdir(parents=True, exist_ok=True)
46
-
47
-
48
- def voxel_to_xarray_dataset(
49
- voxcity_grid: np.ndarray,
50
- voxel_size_m: float,
51
- rectangle_vertices: Optional[Sequence[Tuple[float, float]]] = None,
52
- extra_attrs: Optional[Mapping[str, Any]] = None,
53
- ) -> "xr.Dataset":
54
- """Create an xarray Dataset from a VoxCity voxel grid.
55
-
56
- Parameters
57
- ----------
58
- voxcity_grid
59
- 3D numpy array with shape (rows, cols, levels) as returned by
60
- `get_voxcity` (first element of the returned tuple).
61
- voxel_size_m
62
- Voxel size (mesh size) in meters.
63
- rectangle_vertices
64
- Optional polygon vertices defining the area of interest in
65
- longitude/latitude pairs, typically the same list passed to
66
- `get_voxcity`.
67
- extra_attrs
68
- Optional mapping of additional global attributes to store in the
69
- dataset.
70
-
71
- Returns
72
- -------
73
- xr.Dataset
74
- Dataset containing one DataArray named "voxels" with dims (y, x, z)
75
- and coordinate variables in meters from origin.
76
- """
77
- if not XR_AVAILABLE: # pragma: no cover - optional dependency
78
- raise ImportError(
79
- "xarray is required to export NetCDF. Install with: \n"
80
- " pip install xarray netCDF4\n"
81
- "or: \n"
82
- " pip install xarray h5netcdf"
83
- )
84
-
85
- if voxcity_grid.ndim != 3:
86
- raise ValueError(
87
- f"voxcity_grid must be 3D (rows, cols, levels); got shape={voxcity_grid.shape}"
88
- )
89
-
90
- rows, cols, levels = voxcity_grid.shape
91
-
92
- # Coordinate vectors in meters relative to the grid origin
93
- # y increases with row index, x increases with column index
94
- y_m = np.arange(rows, dtype=float) * float(voxel_size_m)
95
- x_m = np.arange(cols, dtype=float) * float(voxel_size_m)
96
- z_m = np.arange(levels, dtype=float) * float(voxel_size_m)
97
-
98
- ds_attrs: MutableMapping[str, Any] = {
99
- "title": "VoxCity voxel grid",
100
- "institution": "VoxCity",
101
- "source": "voxcity.generator.create_3d_voxel",
102
- "Conventions": "CF-1.10 (partial)",
103
- # NetCDF attributes must be basic types; serialize complex structures as strings
104
- "vox_value_meanings": [
105
- "-3: building",
106
- "-2: vegetation_canopy",
107
- "-1: subsurface",
108
- ">=1: surface_land_cover_code (offset +1)",
109
- ],
110
- "meshsize_m": float(voxel_size_m),
111
- # Store vertices as JSON string for portability
112
- "rectangle_vertices_lonlat_json": (
113
- json.dumps([[float(v[0]), float(v[1])] for v in rectangle_vertices])
114
- if rectangle_vertices is not None else ""
115
- ),
116
- "vertical_reference": "z=0 corresponds to min(DEM) as used in voxel construction",
117
- }
118
- if extra_attrs:
119
- ds_attrs.update(dict(extra_attrs))
120
-
121
- da = xr.DataArray(
122
- voxcity_grid,
123
- dims=("y", "x", "z"),
124
- coords={
125
- "y": ("y", y_m, {"units": "m", "long_name": "row_distance_from_origin"}),
126
- "x": ("x", x_m, {"units": "m", "long_name": "col_distance_from_origin"}),
127
- "z": ("z", z_m, {"units": "m", "positive": "up", "long_name": "height_above_vertical_origin"}),
128
- },
129
- name="voxels",
130
- attrs={
131
- "units": "category",
132
- "description": "VoxCity voxel values; see global attribute 'vox_value_meanings'",
133
- },
134
- )
135
-
136
- ds = xr.Dataset({"voxels": da}, attrs=ds_attrs)
137
- return ds
138
-
139
-
140
- def save_voxel_netcdf(
141
- voxcity_grid: np.ndarray,
142
- output_path: str | Path,
143
- voxel_size_m: float,
144
- rectangle_vertices: Optional[Sequence[Tuple[float, float]]] = None,
145
- extra_attrs: Optional[Mapping[str, Any]] = None,
146
- engine: Optional[str] = None,
147
- ) -> str:
148
- """Save a VoxCity voxel grid to a NetCDF file.
149
-
150
- Parameters
151
- ----------
152
- voxcity_grid
153
- 3D numpy array (rows, cols, levels) of voxel values.
154
- output_path
155
- Path to the NetCDF file to be written. Parent directories will be
156
- created as needed.
157
- voxel_size_m
158
- Voxel size in meters.
159
- rectangle_vertices
160
- Optional list of (lon, lat) pairs defining the area of interest.
161
- Stored as dataset metadata only.
162
- extra_attrs
163
- Optional additional global attributes to embed in the dataset.
164
- engine
165
- Optional xarray engine, e.g., "netcdf4" or "h5netcdf". If not provided,
166
- xarray will choose a default; on failure we retry alternate engines.
167
-
168
- Returns
169
- -------
170
- str
171
- The string path to the written NetCDF file.
172
- """
173
- if not XR_AVAILABLE: # pragma: no cover - optional dependency
174
- raise ImportError(
175
- "xarray is required to export NetCDF. Install with: \n"
176
- " pip install xarray netCDF4\n"
177
- "or: \n"
178
- " pip install xarray h5netcdf"
179
- )
180
-
181
- path = Path(output_path)
182
- _ensure_parent_dir(path)
183
-
184
- ds = voxel_to_xarray_dataset(
185
- voxcity_grid=voxcity_grid,
186
- voxel_size_m=voxel_size_m,
187
- rectangle_vertices=rectangle_vertices,
188
- extra_attrs=extra_attrs,
189
- )
190
-
191
- # Attempt to save with the requested or default engine; on failure, try a fallback
192
- tried_engines = []
193
- try:
194
- ds.to_netcdf(path, engine=engine) # type: ignore[call-arg]
195
- except Exception as e_first: # pragma: no cover - I/O backend dependent
196
- tried_engines.append(engine or "default")
197
- for fallback in ("netcdf4", "h5netcdf"):
198
- try:
199
- ds.to_netcdf(path, engine=fallback) # type: ignore[call-arg]
200
- break
201
- except Exception:
202
- tried_engines.append(fallback)
203
- else:
204
- raise RuntimeError(
205
- f"Failed to write NetCDF using engines: {tried_engines}. "
206
- f"Original error: {e_first}"
207
- )
208
-
209
- return str(path)
210
-
211
-
1
+ """
2
+ NetCDF export utilities for VoxCity.
3
+
4
+ This module provides functions to convert a 3D voxel grid produced by
5
+ `voxcity.generator.get_voxcity` into a NetCDF file for portable storage
6
+ and downstream analysis.
7
+
8
+ The voxel values follow VoxCity conventions (see generator.create_3d_voxel):
9
+ - -3: built structures (buildings)
10
+ - -2: vegetation canopy
11
+ - -1: subsurface/underground
12
+ - >= 1: ground-surface land cover code (offset by +1 from source classes)
13
+
14
+ Notes
15
+ -----
16
+ - This writer prefers xarray for NetCDF export. If xarray is not installed,
17
+ a clear error is raised with installation hints.
18
+ - Coordinates are stored as index-based distances in meters from the grid
19
+ origin along the y, x, and z axes. Geographic metadata such as the
20
+ `rectangle_vertices` and `meshsize_m` are stored as global attributes to
21
+ avoid making assumptions about map projection or geodesic conversions.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from pathlib import Path
27
+ from typing import Any, Dict, Mapping, MutableMapping, Optional, Sequence, Tuple
28
+ import json
29
+
30
+ import numpy as np
31
+
32
+ try: # xarray is the preferred backend for NetCDF writing
33
+ import xarray as xr # type: ignore
34
+ XR_AVAILABLE = True
35
+ except Exception: # pragma: no cover - optional dependency
36
+ XR_AVAILABLE = False
37
+
38
+ __all__ = [
39
+ "voxel_to_xarray_dataset",
40
+ "save_voxel_netcdf",
41
+ "NetCDFExporter",
42
+ ]
43
+
44
+
45
+ def _ensure_parent_dir(path: Path) -> None:
46
+ path.parent.mkdir(parents=True, exist_ok=True)
47
+
48
+
49
+ def voxel_to_xarray_dataset(
50
+ voxcity_grid: np.ndarray,
51
+ voxel_size_m: float,
52
+ rectangle_vertices: Optional[Sequence[Tuple[float, float]]] = None,
53
+ extra_attrs: Optional[Mapping[str, Any]] = None,
54
+ ) -> "xr.Dataset":
55
+ """Create an xarray Dataset from a VoxCity voxel grid.
56
+
57
+ Parameters
58
+ ----------
59
+ voxcity_grid
60
+ 3D numpy array with shape (rows, cols, levels) as returned by
61
+ `get_voxcity` (first element of the returned tuple).
62
+ voxel_size_m
63
+ Voxel size (mesh size) in meters.
64
+ rectangle_vertices
65
+ Optional polygon vertices defining the area of interest in
66
+ longitude/latitude pairs, typically the same list passed to
67
+ `get_voxcity`.
68
+ extra_attrs
69
+ Optional mapping of additional global attributes to store in the
70
+ dataset.
71
+
72
+ Returns
73
+ -------
74
+ xr.Dataset
75
+ Dataset containing one DataArray named "voxels" with dims (y, x, z)
76
+ and coordinate variables in meters from origin.
77
+ """
78
+ if not XR_AVAILABLE: # pragma: no cover - optional dependency
79
+ raise ImportError(
80
+ "xarray is required to export NetCDF. Install with: \n"
81
+ " pip install xarray netCDF4\n"
82
+ "or: \n"
83
+ " pip install xarray h5netcdf"
84
+ )
85
+
86
+ if voxcity_grid.ndim != 3:
87
+ raise ValueError(
88
+ f"voxcity_grid must be 3D (rows, cols, levels); got shape={voxcity_grid.shape}"
89
+ )
90
+
91
+ rows, cols, levels = voxcity_grid.shape
92
+
93
+ # Coordinate vectors in meters relative to the grid origin
94
+ # y increases with row index, x increases with column index
95
+ y_m = np.arange(rows, dtype=float) * float(voxel_size_m)
96
+ x_m = np.arange(cols, dtype=float) * float(voxel_size_m)
97
+ z_m = np.arange(levels, dtype=float) * float(voxel_size_m)
98
+
99
+ ds_attrs: MutableMapping[str, Any] = {
100
+ "title": "VoxCity voxel grid",
101
+ "institution": "VoxCity",
102
+ "source": "voxcity.generator.create_3d_voxel",
103
+ "Conventions": "CF-1.10 (partial)",
104
+ # NetCDF attributes must be basic types; serialize complex structures as strings
105
+ "vox_value_meanings": [
106
+ "-3: building",
107
+ "-2: vegetation_canopy",
108
+ "-1: subsurface",
109
+ ">=1: surface_land_cover_code (offset +1)",
110
+ ],
111
+ "meshsize_m": float(voxel_size_m),
112
+ # Store vertices as JSON string for portability
113
+ "rectangle_vertices_lonlat_json": (
114
+ json.dumps([[float(v[0]), float(v[1])] for v in rectangle_vertices])
115
+ if rectangle_vertices is not None else ""
116
+ ),
117
+ "vertical_reference": "z=0 corresponds to min(DEM) as used in voxel construction",
118
+ }
119
+ if extra_attrs:
120
+ ds_attrs.update(dict(extra_attrs))
121
+
122
+ da = xr.DataArray(
123
+ voxcity_grid,
124
+ dims=("y", "x", "z"),
125
+ coords={
126
+ "y": ("y", y_m, {"units": "m", "long_name": "row_distance_from_origin"}),
127
+ "x": ("x", x_m, {"units": "m", "long_name": "col_distance_from_origin"}),
128
+ "z": ("z", z_m, {"units": "m", "positive": "up", "long_name": "height_above_vertical_origin"}),
129
+ },
130
+ name="voxels",
131
+ attrs={
132
+ "units": "category",
133
+ "description": "VoxCity voxel values; see global attribute 'vox_value_meanings'",
134
+ },
135
+ )
136
+
137
+ ds = xr.Dataset({"voxels": da}, attrs=ds_attrs)
138
+ return ds
139
+
140
+
141
+ def save_voxel_netcdf(
142
+ voxcity_grid: np.ndarray,
143
+ output_path: str | Path,
144
+ voxel_size_m: float,
145
+ rectangle_vertices: Optional[Sequence[Tuple[float, float]]] = None,
146
+ extra_attrs: Optional[Mapping[str, Any]] = None,
147
+ engine: Optional[str] = None,
148
+ ) -> str:
149
+ """Save a VoxCity voxel grid to a NetCDF file.
150
+
151
+ Parameters
152
+ ----------
153
+ voxcity_grid
154
+ 3D numpy array (rows, cols, levels) of voxel values.
155
+ output_path
156
+ Path to the NetCDF file to be written. Parent directories will be
157
+ created as needed.
158
+ voxel_size_m
159
+ Voxel size in meters.
160
+ rectangle_vertices
161
+ Optional list of (lon, lat) pairs defining the area of interest.
162
+ Stored as dataset metadata only.
163
+ extra_attrs
164
+ Optional additional global attributes to embed in the dataset.
165
+ engine
166
+ Optional xarray engine, e.g., "netcdf4" or "h5netcdf". If not provided,
167
+ xarray will choose a default; on failure we retry alternate engines.
168
+
169
+ Returns
170
+ -------
171
+ str
172
+ The string path to the written NetCDF file.
173
+ """
174
+ if not XR_AVAILABLE: # pragma: no cover - optional dependency
175
+ raise ImportError(
176
+ "xarray is required to export NetCDF. Install with: \n"
177
+ " pip install xarray netCDF4\n"
178
+ "or: \n"
179
+ " pip install xarray h5netcdf"
180
+ )
181
+
182
+ path = Path(output_path)
183
+ _ensure_parent_dir(path)
184
+
185
+ ds = voxel_to_xarray_dataset(
186
+ voxcity_grid=voxcity_grid,
187
+ voxel_size_m=voxel_size_m,
188
+ rectangle_vertices=rectangle_vertices,
189
+ extra_attrs=extra_attrs,
190
+ )
191
+
192
+ # Attempt to save with the requested or default engine; on failure, try a fallback
193
+ tried_engines = []
194
+ try:
195
+ ds.to_netcdf(path, engine=engine) # type: ignore[call-arg]
196
+ except Exception as e_first: # pragma: no cover - I/O backend dependent
197
+ tried_engines.append(engine or "default")
198
+ for fallback in ("netcdf4", "h5netcdf"):
199
+ try:
200
+ ds.to_netcdf(path, engine=fallback) # type: ignore[call-arg]
201
+ break
202
+ except Exception:
203
+ tried_engines.append(fallback)
204
+ else:
205
+ raise RuntimeError(
206
+ f"Failed to write NetCDF using engines: {tried_engines}. "
207
+ f"Original error: {e_first}"
208
+ )
209
+
210
+ return str(path)
211
+
212
+
213
+ class NetCDFExporter:
214
+ """Exporter adapter to write a VoxCity voxel grid to NetCDF."""
215
+
216
+ def export(self, obj, output_directory: str, base_filename: str, **kwargs):
217
+ from ..models import VoxCity
218
+ path = Path(output_directory) / f"{base_filename}.nc"
219
+ if not isinstance(obj, VoxCity):
220
+ raise TypeError("NetCDFExporter expects a VoxCity instance")
221
+ rect = obj.extras.get("rectangle_vertices")
222
+ # Merge default attrs with user-provided extras
223
+ user_extra = kwargs.get("extra_attrs") or {}
224
+ attrs = {
225
+ "land_cover_source": obj.extras.get("land_cover_source", ""),
226
+ "building_source": obj.extras.get("building_source", ""),
227
+ "dem_source": obj.extras.get("dem_source", ""),
228
+ }
229
+ attrs.update(user_extra)
230
+ return save_voxel_netcdf(
231
+ voxcity_grid=obj.voxels.classes,
232
+ output_path=path,
233
+ voxel_size_m=obj.voxels.meta.meshsize,
234
+ rectangle_vertices=rect,
235
+ extra_attrs=attrs,
236
+ engine=kwargs.get("engine"),
237
+ )
238
+