zero2geospatial3d-sdk 0.1.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 (89) hide show
  1. zero2geospatial3d/__init__.py +105 -0
  2. zero2geospatial3d/acoustics.py +284 -0
  3. zero2geospatial3d/classify.py +379 -0
  4. zero2geospatial3d/cli.py +367 -0
  5. zero2geospatial3d/comfort.py +687 -0
  6. zero2geospatial3d/data/epw_stations.json.gz +0 -0
  7. zero2geospatial3d/daylight.py +337 -0
  8. zero2geospatial3d/dispersion.py +249 -0
  9. zero2geospatial3d/epw.py +162 -0
  10. zero2geospatial3d/geo.py +138 -0
  11. zero2geospatial3d/grid.py +119 -0
  12. zero2geospatial3d/microclimate.py +388 -0
  13. zero2geospatial3d/morphology.py +283 -0
  14. zero2geospatial3d/nsga2.py +315 -0
  15. zero2geospatial3d/osm.py +450 -0
  16. zero2geospatial3d/osm_tags.py +180 -0
  17. zero2geospatial3d/py.typed +0 -0
  18. zero2geospatial3d/results.py +231 -0
  19. zero2geospatial3d/road_sources.py +141 -0
  20. zero2geospatial3d/scene.py +555 -0
  21. zero2geospatial3d/server.py +332 -0
  22. zero2geospatial3d/shading.py +443 -0
  23. zero2geospatial3d/solar.py +207 -0
  24. zero2geospatial3d/terrain.py +610 -0
  25. zero2geospatial3d/urban_form.py +326 -0
  26. zero2geospatial3d/walkability.py +380 -0
  27. zero2geospatial3d/weather.py +373 -0
  28. zero2geospatial3d/web/index.html +50 -0
  29. zero2geospatial3d/web/src/analysis/climate.js +278 -0
  30. zero2geospatial3d/web/src/analysis/compliance.js +345 -0
  31. zero2geospatial3d/web/src/analysis/gpu.js +431 -0
  32. zero2geospatial3d/web/src/analysis/optimize.js +208 -0
  33. zero2geospatial3d/web/src/analysis/runners.js +1644 -0
  34. zero2geospatial3d/web/src/analysis/terrain_results.js +145 -0
  35. zero2geospatial3d/web/src/app.js +630 -0
  36. zero2geospatial3d/web/src/core/acoustics.js +341 -0
  37. zero2geospatial3d/web/src/core/classify.js +158 -0
  38. zero2geospatial3d/web/src/core/comfort.js +686 -0
  39. zero2geospatial3d/web/src/core/daylight_rule.js +199 -0
  40. zero2geospatial3d/web/src/core/dispersion.js +264 -0
  41. zero2geospatial3d/web/src/core/microclimate.js +372 -0
  42. zero2geospatial3d/web/src/core/morphology.js +153 -0
  43. zero2geospatial3d/web/src/core/nsga2.js +181 -0
  44. zero2geospatial3d/web/src/core/party_walls.js +60 -0
  45. zero2geospatial3d/web/src/core/road_sources.js +127 -0
  46. zero2geospatial3d/web/src/core/solar.js +112 -0
  47. zero2geospatial3d/web/src/core/terrain_analysis.js +267 -0
  48. zero2geospatial3d/web/src/core/terrain_math.js +279 -0
  49. zero2geospatial3d/web/src/core/urban_form.js +405 -0
  50. zero2geospatial3d/web/src/core/walkability.js +316 -0
  51. zero2geospatial3d/web/src/core/weather.js +225 -0
  52. zero2geospatial3d/web/src/core/wind.js +131 -0
  53. zero2geospatial3d/web/src/export/check_pdf.js +74 -0
  54. zero2geospatial3d/web/src/export/exporters.js +556 -0
  55. zero2geospatial3d/web/src/export/pdf.js +149 -0
  56. zero2geospatial3d/web/src/export/snapshot.js +234 -0
  57. zero2geospatial3d/web/src/export/snapshot_scene.js +150 -0
  58. zero2geospatial3d/web/src/main.js +75 -0
  59. zero2geospatial3d/web/src/scene/objects.js +645 -0
  60. zero2geospatial3d/web/src/scene/overlay.js +145 -0
  61. zero2geospatial3d/web/src/scene/terrain.js +546 -0
  62. zero2geospatial3d/web/src/scene/viewer.js +275 -0
  63. zero2geospatial3d/web/src/scene/wind_streamlines.js +336 -0
  64. zero2geospatial3d/web/src/style.css +225 -0
  65. zero2geospatial3d/web/src/ui/building_report.js +404 -0
  66. zero2geospatial3d/web/src/ui/charts.js +441 -0
  67. zero2geospatial3d/web/src/ui/colorize.js +101 -0
  68. zero2geospatial3d/web/src/ui/compare.js +362 -0
  69. zero2geospatial3d/web/src/ui/dom.js +104 -0
  70. zero2geospatial3d/web/src/ui/panel_analyze.js +742 -0
  71. zero2geospatial3d/web/src/ui/panel_compare.js +294 -0
  72. zero2geospatial3d/web/src/ui/panel_layers.js +172 -0
  73. zero2geospatial3d/web/src/ui/panel_terrain.js +277 -0
  74. zero2geospatial3d/web/src/ui/qgis_link.js +127 -0
  75. zero2geospatial3d/web/src/ui/sunclock.js +45 -0
  76. zero2geospatial3d/web/src/ui/tools.js +197 -0
  77. zero2geospatial3d/web/src/ui/viewtools.js +173 -0
  78. zero2geospatial3d/web/vendor/three/addons/controls/OrbitControls.js +1417 -0
  79. zero2geospatial3d/web/vendor/three/addons/exporters/GLTFExporter.js +3309 -0
  80. zero2geospatial3d/web/vendor/three/addons/utils/TextureUtils.js +98 -0
  81. zero2geospatial3d/web/vendor/three/three.module.js +53044 -0
  82. zero2geospatial3d/wind.py +178 -0
  83. zero2geospatial3d_sdk-0.1.0.dist-info/METADATA +219 -0
  84. zero2geospatial3d_sdk-0.1.0.dist-info/RECORD +89 -0
  85. zero2geospatial3d_sdk-0.1.0.dist-info/WHEEL +5 -0
  86. zero2geospatial3d_sdk-0.1.0.dist-info/entry_points.txt +2 -0
  87. zero2geospatial3d_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
  88. zero2geospatial3d_sdk-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +52 -0
  89. zero2geospatial3d_sdk-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,178 @@
1
+ """Pedestrian-level wind screening on a building-height grid.
2
+
3
+ NOT CFD: a fast geometric estimate meant to find sheltered and exposed
4
+ places, to be confirmed with a flow simulation or wind-tunnel test where it
5
+ matters. For each open cell the ratio to the open-terrain pedestrian wind is::
6
+
7
+ ratio = (1 - wake deficit) x channelling x corner factor
8
+
9
+ * wake deficit: strongest upwind wake ``A exp(-x / (k h))`` for an obstacle of
10
+ height ``h`` at distance ``x``;
11
+ * channelling: open fetch along the wind with buildings close on both sides;
12
+ * corner factor: cells beside a building much taller than its surroundings.
13
+
14
+ The open-terrain pedestrian wind is the 10 m reference speed scaled by a log
15
+ profile over open terrain: ``ln(1.5/z0) / ln(10/z0)`` with ``z0 = 0.03 m``.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import math
21
+ from typing import Callable, Optional
22
+
23
+ import numpy as np
24
+
25
+ __all__ = ["OPEN_PEDESTRIAN_FACTOR", "lawson_class", "wind_ratio"]
26
+
27
+ OPEN_PEDESTRIAN_FACTOR = math.log(1.5 / 0.03) / math.log(10 / 0.03)
28
+
29
+
30
+ def _jsround(x: float) -> int:
31
+ return int(math.floor(x + 0.5))
32
+
33
+
34
+ def _shift(a: np.ndarray, dc: int, dr: int, fill: float = 0.0) -> np.ndarray:
35
+ """``out[r, c] = a[r + dr, c + dc]`` where in bounds, else ``fill``."""
36
+ rows, cols = a.shape
37
+ out = np.full(a.shape, fill, dtype=float)
38
+ r0, r1 = max(0, -dr), min(rows, rows - dr)
39
+ c0, c1 = max(0, -dc), min(cols, cols - dc)
40
+ if r0 < r1 and c0 < c1:
41
+ out[r0:r1, c0:c1] = a[r0 + dr : r1 + dr, c0 + dc : c1 + dc]
42
+ return out
43
+
44
+
45
+ def wind_ratio(
46
+ heights: np.ndarray,
47
+ cell: float,
48
+ wind_from_deg: float,
49
+ porosity: Optional[np.ndarray] = None,
50
+ wake_amplitude: float = 0.75,
51
+ wake_length: float = 3.5,
52
+ on_progress: Optional[Callable[[float], None]] = None,
53
+ ) -> np.ndarray:
54
+ """Ratio of pedestrian wind to open-terrain wind per cell.
55
+
56
+ Args:
57
+ heights: ``(rows, cols)`` obstacle height above ground (0 = open).
58
+ cell: cell size in metres.
59
+ wind_from_deg: meteorological direction the wind comes from.
60
+ porosity: optional optical porosity per cell 0..1 (trees ~0.5).
61
+ wake_amplitude: maximum wake deficit.
62
+ wake_length: wake recovery length in obstacle heights.
63
+
64
+ Returns:
65
+ ``(rows, cols)`` ratio in ``0.05..1.6``, NaN inside solid obstacles.
66
+ """
67
+ h = np.asarray(heights, dtype=float)
68
+ rows, cols = h.shape
69
+ poro = None if porosity is None else np.nan_to_num(np.asarray(porosity, dtype=float))
70
+ frm = math.radians(wind_from_deg)
71
+ dc = math.sin(frm)
72
+ dr = -math.cos(frm)
73
+ pc = -dr
74
+ pr = dc
75
+ h_max = float(h.max()) if h.size else 0.0
76
+ pos = h[h > 0]
77
+ h_mean = float(pos.mean()) if pos.size else 0.0
78
+ has_tall = bool(np.any((h > 2 * h_mean) & (h > 20)))
79
+ max_steps = math.ceil(min(600, max(60, wake_length * 5 * h_max)) / cell)
80
+ side_steps = math.ceil(40 / cell)
81
+ cgrid = np.arange(cols)[None, :].astype(float)
82
+ rgrid = np.arange(rows)[:, None].astype(float)
83
+
84
+ # 1. Wake deficit from upwind obstacles.
85
+ deficit = np.zeros(h.shape)
86
+ fetch = np.full(h.shape, max_steps * cell)
87
+ stopped = np.zeros(h.shape, dtype=bool)
88
+ limit = wake_length * 5 * h_max
89
+ for s in range(1, max_steps + 1):
90
+ cc = cgrid + dc * s
91
+ rr = rgrid + dr * s
92
+ inside = (cc >= 0) & (rr >= 0) & (cc <= cols - 1) & (rr <= rows - 1)
93
+ active = inside & ~stopped
94
+ if not active.any():
95
+ if not inside.any():
96
+ break
97
+ continue
98
+ oc, orr = _jsround(dc * s), _jsround(dr * s)
99
+ hs = _shift(h, oc, orr)
100
+ hit = active & (hs > 0)
101
+ if not hit.any():
102
+ continue
103
+ x = s * cell
104
+ fetch = np.where(hit & (x < fetch), x, fetch)
105
+ p = _shift(poro, oc, orr) if poro is not None else 0.0
106
+ with np.errstate(divide="ignore", invalid="ignore"):
107
+ d = wake_amplitude * (1 - p) * np.exp(-x / (wake_length * np.where(hs > 0, hs, 1)))
108
+ deficit = np.where(hit & (d > deficit), d, deficit)
109
+ if x > limit:
110
+ stopped |= hit
111
+ if on_progress is not None:
112
+ on_progress(0.8 * s / max_steps)
113
+
114
+ # 2. Channelling: buildings close on both sides, open along the wind.
115
+ left = np.zeros(h.shape)
116
+ right = np.zeros(h.shape)
117
+ hl = np.zeros(h.shape)
118
+ hr = np.zeros(h.shape)
119
+ for s in range(1, side_steps + 1):
120
+ a = _shift(h, _jsround(pc * s), _jsround(pr * s))
121
+ m = (left == 0) & (a > 0)
122
+ left = np.where(m, s * cell, left)
123
+ hl = np.where(m, a, hl)
124
+ b = _shift(h, _jsround(-pc * s), _jsround(-pr * s))
125
+ m = (right == 0) & (b > 0)
126
+ right = np.where(m, s * cell, right)
127
+ hr = np.where(m, b, hr)
128
+ channel = np.ones(h.shape)
129
+ both = (left > 0) & (right > 0)
130
+ if both.any():
131
+ width = np.where(both, left + right, 1.0)
132
+ h_side = np.minimum(hl, hr)
133
+ along = np.minimum(1, fetch / (3 * width))
134
+ channel = np.where(both, 1 + 0.25 * along * np.minimum(1, h_side / width), 1.0)
135
+
136
+ # 3. Corner / downwash: a much taller building close by.
137
+ corner = np.ones(h.shape)
138
+ if has_tall:
139
+ rad = math.ceil(30 / cell)
140
+ best = np.zeros(h.shape)
141
+ for dy in range(-rad, rad + 1, 2):
142
+ for dx in range(-rad, rad + 1, 2):
143
+ a = _shift(h, dx, dy)
144
+ dist = math.hypot(dx, dy) * cell
145
+ tall = (a > 2 * h_mean) & (a > 20)
146
+ with np.errstate(divide="ignore", invalid="ignore"):
147
+ near = tall & (dist < 0.6 * a)
148
+ v = (1 - dist / (0.6 * np.where(tall, a, 1))) * np.minimum(
149
+ 1, a / (4 * h_mean if h_mean else 1)
150
+ )
151
+ best = np.where(near, np.maximum(best, v), best)
152
+ corner = 1 + 0.35 * best
153
+
154
+ out = np.clip((1 - deficit) * channel * corner, 0.05, 1.6)
155
+ solid = (h > 0) & ~(poro > 0 if poro is not None else np.zeros(h.shape, dtype=bool))
156
+ out[solid] = np.nan
157
+ if on_progress is not None:
158
+ on_progress(1.0)
159
+ return out
160
+
161
+
162
+ def lawson_class(speed: float) -> Optional[dict[str, str]]:
163
+ """Indicative LDDC (Lawson) pedestrian comfort class for a wind speed (m/s).
164
+
165
+ Thresholds: sitting < 4, standing < 6, strolling < 8, business walking < 10.
166
+ Lawson applies them to the speed exceeded 5 % of the time.
167
+ """
168
+ if speed is None or not math.isfinite(speed):
169
+ return None
170
+ if speed < 4:
171
+ return {"key": "sitting", "label": "Sitting", "color": "#2c7bb6"}
172
+ if speed < 6:
173
+ return {"key": "standing", "label": "Standing", "color": "#abd9e9"}
174
+ if speed < 8:
175
+ return {"key": "strolling", "label": "Strolling", "color": "#ffffbf"}
176
+ if speed < 10:
177
+ return {"key": "walking", "label": "Business walking", "color": "#fdae61"}
178
+ return {"key": "uncomfortable", "label": "Uncomfortable", "color": "#d7191c"}
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: zero2geospatial3d-sdk
3
+ Version: 0.1.0
4
+ Summary: Headless 3D urban and terrain analysis in Python: sun hours, shadows, sky view factor, solar PV, UTCI/PET comfort, noise, dispersion, flooding and cut/fill.
5
+ Author-email: Yusuf Eminoğlu <yusufeminoglu@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://geophilo.com/
8
+ Project-URL: Repository, https://github.com/YusufEminoglu/zero2geospatial3d_sdk
9
+ Project-URL: Issues, https://github.com/YusufEminoglu/zero2geospatial3d_sdk/issues
10
+ Project-URL: Documentation, https://github.com/YusufEminoglu/zero2geospatial3d_sdk#readme
11
+ Project-URL: Changelog, https://github.com/YusufEminoglu/zero2geospatial3d_sdk/blob/main/CHANGELOG.md
12
+ Project-URL: QGIS plugin, https://github.com/YusufEminoglu/zero2geospatial3d
13
+ Keywords: gis,3d,urban-planning,urban-climate,solar,sun-hours,shadow,sky-view-factor,photovoltaic,daylight,bre-209,utci,pet,thermal-comfort,microclimate,wind-comfort,noise,air-quality,viewshed,terrain,dem,flood,cut-and-fill,epw,openstreetmap,walkability
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Operating System :: OS Independent
22
+ Classifier: Topic :: Scientific/Engineering :: GIS
23
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
24
+ Classifier: Topic :: Scientific/Engineering :: Physics
25
+ Classifier: Intended Audience :: Science/Research
26
+ Classifier: Intended Audience :: Developers
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.9
29
+ Description-Content-Type: text/markdown
30
+ License-File: LICENSE
31
+ License-File: THIRD_PARTY_NOTICES.md
32
+ Requires-Dist: numpy>=1.20.0
33
+ Provides-Extra: geo
34
+ Requires-Dist: shapely>=2.0.0; extra == "geo"
35
+ Requires-Dist: rasterio>=1.3.0; extra == "geo"
36
+ Requires-Dist: pyproj>=3.3.0; extra == "geo"
37
+ Requires-Dist: matplotlib>=3.5.0; extra == "geo"
38
+ Provides-Extra: dev
39
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
40
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
41
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
42
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
43
+ Dynamic: license-file
44
+
45
+ <div align="center">
46
+
47
+ # zero2geospatial3d-sdk
48
+
49
+ [![PyPI version](https://img.shields.io/pypi/v/zero2geospatial3d-sdk.svg?color=10b981)](https://pypi.org/project/zero2geospatial3d-sdk/)
50
+ [![Python version support](https://img.shields.io/pypi/pyversions/zero2geospatial3d-sdk.svg?color=3b82f6)](https://pypi.org/project/zero2geospatial3d-sdk/)
51
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/YusufEminoglu/zero2geospatial3d_sdk/blob/main/LICENSE)
52
+ [![Code style: Ruff](https://img.shields.io/badge/code%20style-ruff-D7FF64.svg)](https://docs.astral.sh/ruff/)
53
+ [![Test Coverage](https://img.shields.io/badge/coverage-92%25-brightgreen.svg)](#development-and-testing)
54
+
55
+ **Headless 3D terrain and building analysis in Python: sun hours, shadows, sky view factor, solar PV, UTCI/PET comfort, wind, noise and air-quality screening, daylight rules, terrain sections, flooding and cut/fill.**
56
+
57
+ [PyPI Package](https://pypi.org/project/zero2geospatial3d-sdk/) • [GitHub](https://github.com/YusufEminoglu/zero2geospatial3d_sdk) • [Issue Tracker](https://github.com/YusufEminoglu/zero2geospatial3d_sdk/issues) • [QGIS plugin](https://github.com/YusufEminoglu/zero2geospatial3d)
58
+
59
+ </div>
60
+
61
+ `zero2geospatial3d-sdk` is the scriptable, QGIS-free engine behind the
62
+ **02 Geo3D Analyzer** QGIS plugin (`zero2geospatial3d`). The plugin runs its
63
+ analyses on the GPU in a browser; this package ports the same mathematics to
64
+ NumPy so they run in notebooks, batch jobs, servers and CI pipelines. Scene
65
+ packages are interchangeable: a scene exported by the plugin loads in Python,
66
+ and a scene built in Python opens in the bundled browser viewer.
67
+
68
+ ## Installation
69
+
70
+ ```bash
71
+ # Core: NumPy only
72
+ pip install zero2geospatial3d-sdk
73
+
74
+ # Optional geospatial extras (GeoTIFF output with rasterio, pyproj, shapely, matplotlib)
75
+ pip install "zero2geospatial3d-sdk[geo]"
76
+ ```
77
+
78
+ Python 3.9 to 3.13. The import name is `zero2geospatial3d`.
79
+
80
+ ## Quickstart (runs offline)
81
+
82
+ ```python
83
+ import numpy as np
84
+ from zero2geospatial3d import Building, Scene, shading, comfort, terrain
85
+ from zero2geospatial3d.results import aggregate_by_id
86
+
87
+ # A 200 m x 200 m level site in Izmir (UTC+3) with a 2 m grid and two buildings.
88
+ scene = Scene.flat(200, 200, cell=2.0, lat=38.42, lon=27.14, utc_offset=3.0)
89
+ for cx, h in ((-30, 24.0), (30, 12.0)):
90
+ ring = [[cx - 15, -10], [cx + 15, -10], [cx + 15, 10], [cx - 15, 10], [cx - 15, -10]]
91
+ scene.buildings.append(Building([[ring]], height=h))
92
+
93
+ surface = scene.surface() # terrain + buildings as a Grid
94
+ _, ids = scene.building_rasters() # building id per cell (0 = ground)
95
+
96
+ # BRE 209: sun hours on 21 March
97
+ hours = shading.sun_hours(surface, scene.lat, scene.lon, scene.utc_offset, month=3, day=21)
98
+ open_ground = hours[ids == 0]
99
+ print(f"open ground with >= 2 h of sun: {100 * np.mean(open_ground >= 2):.0f} %")
100
+
101
+ # Sky view factor and per-building roof statistics
102
+ svf = shading.sky_view_factor(surface, n_azimuths=16)
103
+ print(aggregate_by_id(ids, svf, cell_area=4.0)[1]["mean"])
104
+
105
+ # Thermal comfort for one set of conditions
106
+ print(comfort.utci(32.0, 55.0, 1.5, 40.0), comfort.pet(32.0, 55.0, 1.5, 40.0))
107
+
108
+ # Terrain: slope, flooding and earthworks on the DEM
109
+ slope, aspect = terrain.slope_aspect(scene.terrain)
110
+ pad = [[-20, -20], [20, -20], [20, 20], [-20, 20], [-20, -20]]
111
+ print(terrain.cut_fill(scene.terrain, pad, design_level=0.5).fill)
112
+ ```
113
+
114
+ ## Command line
115
+
116
+ ```bash
117
+ zero2geospatial3d sun --lat 38.42 --lon 27.14 --date 2026-06-21 --tz 3
118
+ zero2geospatial3d comfort --ta 32 --tr 55 --v 1.5 --rh 40
119
+ zero2geospatial3d stations --lat 41.01 --lon 28.97 -n 3 # nearest TMYx stations
120
+ zero2geospatial3d epw --lat 41.01 --lon 28.97 --out istanbul.epw # download the nearest one
121
+
122
+ zero2geospatial3d demo --out demo_scene # synthetic scene package
123
+ zero2geospatial3d osm --lat 38.4237 --lon 27.1428 --half-size 250 --out izmir_scene
124
+ zero2geospatial3d analyze demo_scene sun-hours --date 2026-03-21 --out sun.asc
125
+ zero2geospatial3d analyze demo_scene svf --azimuths 36 --out svf.npy
126
+ zero2geospatial3d analyze demo_scene irradiation --epw istanbul.epw --out kwh.tif # .tif needs [geo]
127
+ zero2geospatial3d analyze demo_scene flood --level 12 --out depth.asc
128
+ zero2geospatial3d view demo_scene --results results/ # bundled 3D viewer
129
+ ```
130
+
131
+ `analyze` supports `sun-hours`, `shadow`, `svf`, `viewshed`, `irradiation`,
132
+ `slope`, `aspect`, `hillshade` and `flood`, and prints summary statistics as
133
+ JSON. `view` serves the plugin's three.js viewer on `127.0.0.1` (your data
134
+ stays on your computer); results sent from the viewer are saved to `--results`.
135
+
136
+ ## How the 3D analyses work headlessly
137
+
138
+ The plugin renders depth maps of the full 3D scene on the GPU. The SDK works on
139
+ a 2.5-D **surface**: the DEM plus the heights of buildings (and optionally tree
140
+ crowns) rasterised onto the same grid. Sun visibility is tested by marching
141
+ rays across this surface with bilinear sampling, the sky view factor is
142
+ `1 - mean(sin^2(horizon))` over azimuth sectors, and irradiation combines
143
+ direct sun (from EPW or clear-sky sun-direction bins), isotropic diffuse sky
144
+ and ground reflection, exactly as the plugin's `solarRadiation` runner does.
145
+ Overhangs and facade receivers are not represented in 2.5-D.
146
+
147
+ ## Public API
148
+
149
+ | Module | Main contents |
150
+ |---|---|
151
+ | `grid` | `Grid` (values, cell, min_x, max_y; `sample`, `coords`, `index_of`), `make_grid` |
152
+ | `scene` | `Scene` (`flat`, `from_osm`, `surface`, `building_rasters`, `tree_raster`, `export`), `Building`, `Tree`, `Road`, `load_scene`, `rasterize_polygons` |
153
+ | `solar` | `solar_position`, `sunrise_sunset`, `sun_path`, `sky_hemisphere`, `sun_vector`, `day_of_year` |
154
+ | `shading` | `shadow_mask`, `sun_hours`, `shadow_study`, `horizon_angles`, `sky_view_factor`, `viewshed`, `directional_irradiation`, `solar_irradiation`, `pv_yield` |
155
+ | `comfort` | `utci`, `pet`, `mean_radiant_temperature`, `sky_temperature`, `projected_area_factor`, `utci_category`, `pet_category` |
156
+ | `weather` | `Weather`, `parse_epw`, `read_epw`, `monthly_summary`, `wind_rose`, `clear_sky_year`, `annual_sun_bins`, `conditions_at`, `typical_doy` |
157
+ | `epw` | `nearest_stations`, `load_index`, `download_station`, `extract_epw`, `read_header` |
158
+ | `microclimate` | `surface_temperature`, `canopy_transmittance`, `canopy_cooling_grid`, `diurnal_point_profile`, `SURFACE_MATERIALS` |
159
+ | `wind` | `wind_ratio`, `lawson_class`, `OPEN_PEDESTRIAN_FACTOR` |
160
+ | `morphology` | `distance_to_built`, `macdonald`, `classify_lcz`, `block_indicators` |
161
+ | `dispersion` | `ventilation_efficiency`, `pollutant_dispersion`, `ventilation_class`, `air_quality_class` |
162
+ | `acoustics` | `noise_propagation`, `building_facade_noise`, `maekawa_diffraction`, `noise_class` |
163
+ | `road_sources` | `sample_road_lines`, `street_centrelines`, `road_weight` |
164
+ | `daylight` | `prepare_rule`, `check_rule`, `vsc_long`, `allowed_angle`, `reach25`, `wall_samples` (BRE 209) |
165
+ | `urban_form` | `green_view_index`, `solar_envelope`, `gvi_class` |
166
+ | `walkability` | `pedestrian_walkability_index`, `find_cool_route`, subscores |
167
+ | `terrain` | `slope_aspect`, `hillshade`, `contours`, `profile`, `flood`, `cut_fill`, `balanced_level`, `curvature`, `tpi`, `ruggedness`, `aspect_classes`, `flow_accumulation`, `wetness`, `crop_grid` |
168
+ | `classify` | `equal_breaks`, `quantile_breaks`, `jenks_breaks`, `ramp_stops`, `ramp_color`, `RAMPS` |
169
+ | `nsga2` | `Problem`, `nsga2`, `dominates`, `non_dominated_sort`, `crowding_distance` |
170
+ | `osm`, `osm_tags` | `fetch` (Overpass, timeouts, cache), `payload_to_features`, `building_height`, `road_traffic` |
171
+ | `geo` | `lonlat_to_local`, `local_to_lonlat`, `bbox_around`, `utm_epsg_for`, `grid_shape` |
172
+ | `results` | `aggregate_by_id`, `grid_stats`, `write_ascii_grid`, `write_grid_npy`, `write_geotiff`, `save_result` |
173
+ | `server` | `serve_scene`, `ViewerServer`, `viewer_root` |
174
+
175
+ All grids are NumPy arrays of shape `(rows, cols)` with row 0 at the north
176
+ edge; coordinates are local metres (x east, y north). Angles from `solar` are
177
+ radians with azimuth clockwise from north.
178
+
179
+ ## Validation
180
+
181
+ The test suite reproduces the numeric assertions of the plugin's JavaScript
182
+ tests (`tests/js/*.test.mjs`): the NREL SPA reference sun position, UTCI and
183
+ PET reference values, EPW parsing and wind roses, surface energy balance and
184
+ canopy cooling, Maekawa diffraction and traffic-noise scaling, ventilation and
185
+ dispersion, BRE 209 VSC closed forms, solar envelopes and green view, terrain
186
+ curvature, TPI, TRI, flow accumulation and wetness, flood and cut/fill
187
+ volumes, Jenks breaks, and the seeded NSGA-II run. The shading engines are
188
+ checked against closed forms (canyon sky view factor, wall shadow length).
189
+
190
+ ## Development and testing
191
+
192
+ ```bash
193
+ git clone https://github.com/YusufEminoglu/zero2geospatial3d_sdk.git
194
+ cd zero2geospatial3d_sdk
195
+ pip install -e ".[dev]"
196
+ python -m pytest --cov=zero2geospatial3d
197
+ python -m ruff check .
198
+ python -m mypy src
199
+ ```
200
+
201
+ ## Academic citation
202
+
203
+ ```bibtex
204
+ @software{eminoglu_zero2geospatial3d_sdk,
205
+ author = {Eminoğlu, Yusuf},
206
+ title = {zero2geospatial3d-sdk: headless 3D terrain and building analysis in Python},
207
+ year = {2026},
208
+ version = {0.1.0},
209
+ url = {https://github.com/YusufEminoglu/zero2geospatial3d_sdk}
210
+ }
211
+ ```
212
+
213
+ See also `CITATION.cff`.
214
+
215
+ ## License
216
+
217
+ MIT, see [LICENSE](LICENSE). The bundled viewer includes three.js (MIT) and the
218
+ UTCI polynomial follows pythermalcomfort (MIT); the station index credits
219
+ Climate.OneBuilding.Org. Details in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
@@ -0,0 +1,89 @@
1
+ zero2geospatial3d/__init__.py,sha256=YFW1RAgD5EJ_wo6aHprv5SUblJsIItjyX9eeKeFe564,2505
2
+ zero2geospatial3d/acoustics.py,sha256=XHPpTiZcAXtPlZqbmjQy-scIqOBOca1sNBrmCq6UJzI,10373
3
+ zero2geospatial3d/classify.py,sha256=V2nbscwyYi1I56pulkbU7iw8YmPswwInAiyArfzzdBQ,9363
4
+ zero2geospatial3d/cli.py,sha256=nwS_ojBkakB01VbLYihXfBAlfwDau2UsOqss_WEyAB4,13539
5
+ zero2geospatial3d/comfort.py,sha256=_SbZJQ8-MU0c23YcPDwUR46oNiWNYIeXe-4nDm8UE84,30617
6
+ zero2geospatial3d/daylight.py,sha256=jweavZ9hlK-dnLNsbi8Ja19yoE4mG_fBo0tTwzQ0Sc4,11317
7
+ zero2geospatial3d/dispersion.py,sha256=VILCa6sgFxRLgrNDRlZ2EPJiRz2J-1sEB0X5LHxAoCo,9418
8
+ zero2geospatial3d/epw.py,sha256=jA6W9Nuz__ywhNbjZJQWvCATpiPtpXK7VcrKdfoIxCk,5733
9
+ zero2geospatial3d/geo.py,sha256=digJEC69_zCCI3p62FFa48l28s69iROCcvZGCVB4E4E,5056
10
+ zero2geospatial3d/grid.py,sha256=1zHx-BHZGP77wWOYdZJD_SIcSe-Y9bvZfmXgy4lGsGw,4243
11
+ zero2geospatial3d/microclimate.py,sha256=JjmqCru-qD4dS7zRdO2fvTCTwuLWu_ChdUWouXMNc-Y,12934
12
+ zero2geospatial3d/morphology.py,sha256=SpwtYTMDdpr-bAnvI4c31anyzIGefAoPE_TdcGeyJo8,9508
13
+ zero2geospatial3d/nsga2.py,sha256=bHulq9b6SZebDKRd7yyr5QjtxNJkG2Cm9QwedKbIoqk,9944
14
+ zero2geospatial3d/osm.py,sha256=R4ZNjr56o-OMvGUpsyM2KM4me1SFoJIUqaglrBGHv_8,16438
15
+ zero2geospatial3d/osm_tags.py,sha256=iXXs4OEfmMZglKGYHEHuCQZqzCFNOemg96deXU1JHKA,5550
16
+ zero2geospatial3d/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ zero2geospatial3d/results.py,sha256=kQSHnWsUD875OgUqiiUeIJiTNbCIOxQW0aVtwGm8btc,7937
18
+ zero2geospatial3d/road_sources.py,sha256=ac6hbJY_mp00YOoLLHj0VsQ1XRIhSmx5W4MVjlvqqqM,4882
19
+ zero2geospatial3d/scene.py,sha256=gi3DQWsB-7hLqdFAlrFRw_B-UAvYAsf7A3GRhltGGHQ,20886
20
+ zero2geospatial3d/server.py,sha256=T0Js4rm9Gv20__Po0TNQnJkIkaPHxOogOcJDvbugBV0,11238
21
+ zero2geospatial3d/shading.py,sha256=3LXfWbCKw7PVZsC3XngfTUMHZpzeyLqAkk-7Q-oLOys,14593
22
+ zero2geospatial3d/solar.py,sha256=vDm_sZlA0kqckKAus_9tccXocJ0eG65qNl9dV-nNqJY,6320
23
+ zero2geospatial3d/terrain.py,sha256=tZ1n3Ib7gqq40c7qCM8U5C8ZKH84lma2r66DLthWWUM,21329
24
+ zero2geospatial3d/urban_form.py,sha256=ns0cm0AG5jWE3WDyWAaOdu6X5UTDZO9Xi9NhLk2ObKM,12293
25
+ zero2geospatial3d/walkability.py,sha256=heDEDkRglgCd3mCGotLz-_W3xdoKAdbkUSj_dYu_Daw,11748
26
+ zero2geospatial3d/weather.py,sha256=EbhWnXV4cVZIQRI93Pi5KeUhois45CDonH9m6FSx84c,12683
27
+ zero2geospatial3d/wind.py,sha256=DVEIN7CKxF5HUxEH3TEmelC9PqkNiR--2NTgXpO8t9I,6889
28
+ zero2geospatial3d/data/epw_stations.json.gz,sha256=h1ljOWOzrlZcMb0taaZt1h-OH4Xq1afKt8yn8UuAu8o,540433
29
+ zero2geospatial3d/web/index.html,sha256=NydaoutKzm1MOJdBpKtgZVkCosyf2aGjfpzv14GcHi0,1834
30
+ zero2geospatial3d/web/src/app.js,sha256=LSgo83kCAmp72c-sfDE5kCx_v_reQ1cR4GOofeeOESw,26017
31
+ zero2geospatial3d/web/src/main.js,sha256=twfmMkJI56AlHcXSPCNxwOauOXKJPMEngT-DzrX5_Co,2974
32
+ zero2geospatial3d/web/src/style.css,sha256=FeUWvw7V3ZzaVZSF6nI3YCgojtvrRvHlT7j_U_UvXBY,14120
33
+ zero2geospatial3d/web/src/analysis/climate.js,sha256=Lno4DxcPG7D2YJ6jvpw6pGB_e0BcCaU_pZovVLGmzJQ,14665
34
+ zero2geospatial3d/web/src/analysis/compliance.js,sha256=Mw8coDHmeMLYQKUtMmSQps7i2Se5EI0tMygf20m-XIM,18475
35
+ zero2geospatial3d/web/src/analysis/gpu.js,sha256=9AG3yKpjyn629_E8yvBqgIJH5pXtYhWrmxLNRxRhfrw,17974
36
+ zero2geospatial3d/web/src/analysis/optimize.js,sha256=hWivrmMyUE0juM6Seu_T9_9QLXiZdYYTuFePvwW6sBc,11257
37
+ zero2geospatial3d/web/src/analysis/runners.js,sha256=4EQeQ1IOwb8Upr3GUHQP8sJvzKnSTwXztDZnywsL_Js,77887
38
+ zero2geospatial3d/web/src/analysis/terrain_results.js,sha256=etzVC2sAa2KlK1y3tCH_O67DLyClJdL5d2knkrTxIBc,8538
39
+ zero2geospatial3d/web/src/core/acoustics.js,sha256=5v8K5EmDYkFRywnE3sy6JixyMhBFTGWX36cpJkncx4Y,12540
40
+ zero2geospatial3d/web/src/core/classify.js,sha256=qV-SP9cs66qwDWLc_A_JfMDGjT3dCD05XR4SuYQ8Cyw,6756
41
+ zero2geospatial3d/web/src/core/comfort.js,sha256=0Cn0FOFk9VpNbm-ysd5s-BJouW_KmIvHtuD9OEUpWq4,28729
42
+ zero2geospatial3d/web/src/core/daylight_rule.js,sha256=W8tpAogMNLB_MVJB1m-wGmpAH6sfUwE2SSgwlX-Vi6Q,8842
43
+ zero2geospatial3d/web/src/core/dispersion.js,sha256=DHnQe4Hw0UuItzRcCp2Wxw3kmP0ILteZSMMXTmD6ODo,10701
44
+ zero2geospatial3d/web/src/core/microclimate.js,sha256=8zZOiLa7jykt2MqWWsG92UMxBhMd-goO9X3Ni9Uu5vE,14029
45
+ zero2geospatial3d/web/src/core/morphology.js,sha256=4Cf4F4CJItk1MdRXPtXgnEguNhNGdR1Vr8Hi6HOaO9M,7338
46
+ zero2geospatial3d/web/src/core/nsga2.js,sha256=w4pF99TllZGbwlopBcy4ju9ZELGd-qkYK-c7NqV1qMg,7631
47
+ zero2geospatial3d/web/src/core/party_walls.js,sha256=rHOroRKYpedK4JwlUltN-0ucdtNQJdz3T7vn-x716TQ,2425
48
+ zero2geospatial3d/web/src/core/road_sources.js,sha256=ZRtVlt1AAPMXqD6EDWWky-8viWRawlsMPMU4EF29Zec,5143
49
+ zero2geospatial3d/web/src/core/solar.js,sha256=PmKGdGOmZbzBdNfxJIst_CKEpSPM72oKySpBRkKySjs,4651
50
+ zero2geospatial3d/web/src/core/terrain_analysis.js,sha256=wlXlmE-4_hbvwPBae2sBuaFqMRptxdN6NTXP4cu3kO8,10457
51
+ zero2geospatial3d/web/src/core/terrain_math.js,sha256=YUV34Y-6AfXShGigWm3pAWqYgdaKR9V4ZyMTTIZbpdM,12199
52
+ zero2geospatial3d/web/src/core/urban_form.js,sha256=bCMfVP80BYfJab7eVi6VU6_-Pd7ijMOW4zle9pIJGcI,15337
53
+ zero2geospatial3d/web/src/core/walkability.js,sha256=DQjTGZ5fBDVvsLTgjZy81yTRwpB6sEgwbjITGfwCr5I,10847
54
+ zero2geospatial3d/web/src/core/weather.js,sha256=YvVgMjPyUsC-g_w04VrbpdJpyd4UYMThcS3jyugWyzA,9288
55
+ zero2geospatial3d/web/src/core/wind.js,sha256=XkNI4Xfz2WQBlozmHzYOKYuVKcv4CsGmRHsjbzzooAU,6152
56
+ zero2geospatial3d/web/src/export/check_pdf.js,sha256=0H8kOZW5ZdAcQUIS6jMGMCJTTvIDiToJ2MjMGhutzvc,4164
57
+ zero2geospatial3d/web/src/export/exporters.js,sha256=R6RK3xGA_PVaTyDmKXpxRGZajVMcSg12NCsl3Bruq2Y,25061
58
+ zero2geospatial3d/web/src/export/pdf.js,sha256=YYBygJoL_y-laQEZ4kpjiscFNiawil7cvrvMNe6s_wo,7031
59
+ zero2geospatial3d/web/src/export/snapshot.js,sha256=HC8EerG73fIARmf9CPUkR97Zv7LJHxZNYW7WkgyPJTA,10892
60
+ zero2geospatial3d/web/src/export/snapshot_scene.js,sha256=VlVf73Zzdf-O-v4pBGZHzyL9TAPfQdOwx3Un9Zgghs4,6404
61
+ zero2geospatial3d/web/src/scene/objects.js,sha256=6UvXlcc5Y9tRHklLoapNpx1t56klt1YYhHAXcYc6qzI,25327
62
+ zero2geospatial3d/web/src/scene/overlay.js,sha256=jvjxmWZ1qRyKSq0ZjwHRAhRnVwmXxgQf42-4TAitiR0,6146
63
+ zero2geospatial3d/web/src/scene/terrain.js,sha256=1qIkBYckauC9_w7fOOM7f9MRNL09rtpsgJJcK7ysKlU,22603
64
+ zero2geospatial3d/web/src/scene/viewer.js,sha256=E83Pj9_AKpe-QTy0Yex__686B8ZmcdsStC8CqhjEntE,10660
65
+ zero2geospatial3d/web/src/scene/wind_streamlines.js,sha256=XV8m23PC5s5_FiSEOFiBJG4gapwZ8oFKBuDJ-_Z-f_s,11384
66
+ zero2geospatial3d/web/src/ui/building_report.js,sha256=GMFKT0dp4jchT4X-etDXrDmb70PbHaxm5SyYRJv1ljE,23312
67
+ zero2geospatial3d/web/src/ui/charts.js,sha256=IvsrHqMLGpOtZLn9K1pWU8mQ1q4AZcPZB7_687Mf6A4,16352
68
+ zero2geospatial3d/web/src/ui/colorize.js,sha256=Y08Uj_W49bmW3vg8Z6XgRcwqMKhbWWz0W4qLC8k_Km0,4461
69
+ zero2geospatial3d/web/src/ui/compare.js,sha256=GQoLi8CBSrKdSShNS2xrv6Wk4pVrM1G_FvbDqrFRXiw,14938
70
+ zero2geospatial3d/web/src/ui/dom.js,sha256=PdyDBuChOijcm_DvrBmCXTfkBDpdN9ldObwbaSlxUkI,4421
71
+ zero2geospatial3d/web/src/ui/panel_analyze.js,sha256=QVYxqikxBPR4Tb20GEhGl35hpEVpmiSy8zP5oamhE5w,44704
72
+ zero2geospatial3d/web/src/ui/panel_compare.js,sha256=hyx1XuWETsIdah5vGkLGwLeUdPK4SC913KK3l9Na81A,20448
73
+ zero2geospatial3d/web/src/ui/panel_layers.js,sha256=8-ByL7j6xsyZfGsw9laSA1BbvbCXWh3lTpQEZhauN9s,9789
74
+ zero2geospatial3d/web/src/ui/panel_terrain.js,sha256=8dg9kuE0Qi8HGRsB4h1lkTMpeiERTMSo-vb7dhdI_EI,15730
75
+ zero2geospatial3d/web/src/ui/qgis_link.js,sha256=U5_1W91BagR3uBmeT-ofUxDFI-a3L-RejktqJl6OXvk,4710
76
+ zero2geospatial3d/web/src/ui/sunclock.js,sha256=MOnVWl6q-s0oiGu8YW9d87ULkcMABYy-_un_RtnDiN4,2545
77
+ zero2geospatial3d/web/src/ui/tools.js,sha256=goEBMRivRarv_tBbEBPNh7amuhDy4YZW6pOFacDOyMc,7363
78
+ zero2geospatial3d/web/src/ui/viewtools.js,sha256=3qSkx9frtZXbbHrBruu4_rHGbgX6l3QtKs4jFJO_0H0,10021
79
+ zero2geospatial3d/web/vendor/three/three.module.js,sha256=dt6oFRvJNSrvNSi0Ji4kmyYE9iVDgoMo25eNBg1hpJU,1272972
80
+ zero2geospatial3d/web/vendor/three/addons/controls/OrbitControls.js,sha256=WkSp6GoqD7EZM-7Wm8LNM8dqSWhUwa7W7Xdu-ofXsGQ,29868
81
+ zero2geospatial3d/web/vendor/three/addons/exporters/GLTFExporter.js,sha256=-Kbrh3GeaMkl_fRFAUterUnPd5rhTmnGzByx3U6uUU0,77714
82
+ zero2geospatial3d/web/vendor/three/addons/utils/TextureUtils.js,sha256=G4Sn_eh0XdYU9itsqBCLWSFEJ4wHiRoMvozfjhV5bvo,2367
83
+ zero2geospatial3d_sdk-0.1.0.dist-info/licenses/LICENSE,sha256=u-7Z2aEYRgr9uxEEH_y9DhG08eIi_iv185vJJXtaYNY,1071
84
+ zero2geospatial3d_sdk-0.1.0.dist-info/licenses/THIRD_PARTY_NOTICES.md,sha256=WVJyTeYI4u_xKDiOUKF7kTKi43g_gTE-65WZewPOXeo,2425
85
+ zero2geospatial3d_sdk-0.1.0.dist-info/METADATA,sha256=TUgSFOciPku0lpzrfllzP-7_WITuEvv-95F_JZUP6pw,11834
86
+ zero2geospatial3d_sdk-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
87
+ zero2geospatial3d_sdk-0.1.0.dist-info/entry_points.txt,sha256=IxArwtBbdLux9-EYvDxIYjWd3Esu4dXERwINHB5ZI-M,65
88
+ zero2geospatial3d_sdk-0.1.0.dist-info/top_level.txt,sha256=BwxUXWt4iJcHnzHim0XclWcFnCtazDo34yn4s4RHTCg,18
89
+ zero2geospatial3d_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ zero2geospatial3d = zero2geospatial3d.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yusuf Eminoglu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,52 @@
1
+ # Third-party notices
2
+
3
+ zero2geospatial3d-sdk (a Python port of the 02 Geo3D Analyzer QGIS plugin) ships or
4
+ derives from the following works. Paths are relative to `src/zero2geospatial3d/`.
5
+
6
+ ## Three.js r160
7
+
8
+ Source: <https://github.com/mrdoob/three.js>
9
+
10
+ The MIT License
11
+
12
+ Copyright © 2010-2023 Three.js Authors
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+
32
+ Files: `web/vendor/three/three.module.js` and the add-ons under `web/vendor/three/addons/`
33
+ (bundled browser viewer).
34
+
35
+ ## pythermalcomfort (UTCI polynomial)
36
+
37
+ Source: <https://github.com/CenterForTheBuiltEnvironment/pythermalcomfort>, `models/utci.py`.
38
+ The UTCI regression polynomial and the saturation vapour pressure function in
39
+ `web/src/core/comfort.js` and its Python port `comfort.py` are transcribed from it. The polynomial itself is the
40
+ operational approximation of Bröde et al. (2012), Int J Biometeorol 56:481-494,
41
+ doi:10.1007/s00484-011-0454-1.
42
+
43
+ MIT License, Copyright (c) 2019 Federico Tartarini. The full MIT text is the
44
+ same as the one above, with that copyright line.
45
+
46
+ ## climate.onebuilding.org station index
47
+
48
+ `data/epw_stations.json.gz` lists station names, coordinates, time zones,
49
+ elevations and download paths taken from the TMYx location indexes published
50
+ at <https://climate.onebuilding.org/> (built with the plugin's `tools/build_epw_index.py`).
51
+ Weather files are downloaded from that site at the user's request; they are
52
+ not bundled. Credit: Climate.OneBuilding.Org.
@@ -0,0 +1 @@
1
+ zero2geospatial3d