voxcity 0.3.3__tar.gz → 0.3.4__tar.gz
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.
- {voxcity-0.3.3 → voxcity-0.3.4}/PKG-INFO +5 -5
- {voxcity-0.3.3 → voxcity-0.3.4}/README.md +4 -4
- {voxcity-0.3.3 → voxcity-0.3.4}/pyproject.toml +1 -1
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/eubucco.py +9 -17
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/gee.py +4 -3
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/mbfp.py +7 -7
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/oemj.py +22 -22
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/omt.py +10 -10
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/osm.py +23 -21
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/overture.py +7 -15
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/file/envimet.py +4 -4
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/file/geojson.py +25 -39
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/geo/draw.py +41 -45
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/geo/grid.py +9 -143
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/geo/utils.py +79 -66
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/sim/solar.py +5 -5
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/sim/view.py +5 -5
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/utils/weather.py +7 -7
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity.egg-info/PKG-INFO +5 -5
- {voxcity-0.3.3 → voxcity-0.3.4}/AUTHORS.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/CONTRIBUTING.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/HISTORY.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/LICENSE +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/MANIFEST.in +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/Makefile +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/archive/README.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/authors.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/conf.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/index.rst +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/docs/make.bat +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/setup.cfg +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/__init__.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/__init__.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/download/utils.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/file/__init_.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/file/magicavoxel.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/file/obj.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/geo/__init_.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/sim/__init_.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/sim/utils.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/utils/__init_.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/utils/lc.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/utils/visualization.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity/voxcity.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity.egg-info/SOURCES.txt +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity.egg-info/dependency_links.txt +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity.egg-info/requires.txt +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/src/voxcity.egg-info/top_level.txt +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/tests/__init__.py +0 -0
- {voxcity-0.3.3 → voxcity-0.3.4}/tests/voxelcity.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: voxcity
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: voxcity is an easy and one-stop tool to output 3d city models for microclimate simulation by integrating multiple geospatial open-data
|
|
5
5
|
Author-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
|
|
6
6
|
Maintainer-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
|
|
@@ -164,10 +164,10 @@ Define the target area by directly specifying the coordinates of the rectangle v
|
|
|
164
164
|
|
|
165
165
|
```python
|
|
166
166
|
rectangle_vertices = [
|
|
167
|
-
(
|
|
168
|
-
(
|
|
169
|
-
(
|
|
170
|
-
(
|
|
167
|
+
(-122.33587348582083, 47.59830044521263), # Southwest corner (longitude, latitude)
|
|
168
|
+
(-122.33587348582083, 47.60279755390168), # Northwest corner (longitude, latitude)
|
|
169
|
+
(-122.32922451417917, 47.60279755390168), # Northeast corner (longitude, latitude)
|
|
170
|
+
(-122.32922451417917, 47.59830044521263) # Southeast corner (longitude, latitude)
|
|
171
171
|
]
|
|
172
172
|
```
|
|
173
173
|
|
|
@@ -108,10 +108,10 @@ Define the target area by directly specifying the coordinates of the rectangle v
|
|
|
108
108
|
|
|
109
109
|
```python
|
|
110
110
|
rectangle_vertices = [
|
|
111
|
-
(
|
|
112
|
-
(
|
|
113
|
-
(
|
|
114
|
-
(
|
|
111
|
+
(-122.33587348582083, 47.59830044521263), # Southwest corner (longitude, latitude)
|
|
112
|
+
(-122.33587348582083, 47.60279755390168), # Northwest corner (longitude, latitude)
|
|
113
|
+
(-122.32922451417917, 47.60279755390168), # Northeast corner (longitude, latitude)
|
|
114
|
+
(-122.32922451417917, 47.59830044521263) # Southeast corner (longitude, latitude)
|
|
115
115
|
]
|
|
116
116
|
```
|
|
117
117
|
|
|
@@ -68,16 +68,11 @@ def filter_and_convert_gdf_to_geojson_eubucco(gpkg_file, layer_name, rectangle_v
|
|
|
68
68
|
Parameters:
|
|
69
69
|
- gpkg_file (str): Path to the GeoPackage file.
|
|
70
70
|
- layer_name (str): Name of the layer within the GeoPackage to process.
|
|
71
|
-
- rectangle_vertices (list of tuples): List of (
|
|
71
|
+
- rectangle_vertices (list of tuples): List of (longitude, latitude) tuples defining the rectangle.
|
|
72
72
|
- output_geojson (str): Path to the output GeoJSON file.
|
|
73
73
|
"""
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
rectangle_polygon = Polygon(rectangle_vertices_lonlat)
|
|
77
|
-
|
|
78
|
-
# Helper function to swap coordinate order
|
|
79
|
-
def swap_coordinates_gdf(x, y, z=None):
|
|
80
|
-
return y, x
|
|
74
|
+
# Create polygon from rectangle vertices (already in lon,lat format)
|
|
75
|
+
rectangle_polygon = Polygon(rectangle_vertices)
|
|
81
76
|
|
|
82
77
|
# Get Shapely version for compatibility checks
|
|
83
78
|
shapely_version = shapely.__version__
|
|
@@ -165,13 +160,10 @@ def filter_and_convert_gdf_to_geojson_eubucco(gpkg_file, layer_name, rectangle_v
|
|
|
165
160
|
else:
|
|
166
161
|
shapely_transformed_poly = poly
|
|
167
162
|
|
|
168
|
-
#
|
|
169
|
-
swapped_poly = transform(swap_coordinates_gdf, shapely_transformed_poly)
|
|
170
|
-
|
|
171
|
-
# Extract polygon coordinates
|
|
163
|
+
# Extract polygon coordinates (already in lon,lat format)
|
|
172
164
|
coords = []
|
|
173
|
-
coords.append(list(
|
|
174
|
-
for interior in
|
|
165
|
+
coords.append(list(shapely_transformed_poly.exterior.coords)) # Exterior ring
|
|
166
|
+
for interior in shapely_transformed_poly.interiors: # Interior rings (holes)
|
|
175
167
|
coords.append(list(interior.coords))
|
|
176
168
|
|
|
177
169
|
# Create GeoJSON geometry
|
|
@@ -259,7 +251,7 @@ def save_geojson_from_eubucco(rectangle_vertices, country_links, output_dir, fil
|
|
|
259
251
|
Downloads, extracts, filters, and converts GeoPackage data to GeoJSON based on the rectangle vertices.
|
|
260
252
|
|
|
261
253
|
Parameters:
|
|
262
|
-
- rectangle_vertices (list of tuples): List of (
|
|
254
|
+
- rectangle_vertices (list of tuples): List of (longitude, latitude) tuples defining the rectangle.
|
|
263
255
|
- country_links (dict): Dictionary mapping country names to their respective GeoPackage URLs.
|
|
264
256
|
- output_dir (str): Directory to save output files
|
|
265
257
|
- file_name (str): Name for output GeoJSON file
|
|
@@ -268,7 +260,7 @@ def save_geojson_from_eubucco(rectangle_vertices, country_links, output_dir, fil
|
|
|
268
260
|
- None: Writes the output to a GeoJSON file.
|
|
269
261
|
"""
|
|
270
262
|
# Determine country based on first vertex
|
|
271
|
-
country_name = get_country_name(rectangle_vertices[0][0], rectangle_vertices[0][1])
|
|
263
|
+
country_name = get_country_name(rectangle_vertices[0][0], rectangle_vertices[0][1]) # Swap order for get_country_name
|
|
272
264
|
if country_name in country_links:
|
|
273
265
|
url = country_links[country_name]
|
|
274
266
|
else:
|
|
@@ -304,7 +296,7 @@ def load_geojson_from_eubucco(rectangle_vertices, output_dir):
|
|
|
304
296
|
Downloads EUBUCCO data and loads it as GeoJSON.
|
|
305
297
|
|
|
306
298
|
Parameters:
|
|
307
|
-
- rectangle_vertices (list of tuples): List of (
|
|
299
|
+
- rectangle_vertices (list of tuples): List of (longitude, latitude) tuples defining the area
|
|
308
300
|
- output_dir (str): Directory to save intermediate files
|
|
309
301
|
|
|
310
302
|
Returns:
|
|
@@ -11,7 +11,7 @@ import ee
|
|
|
11
11
|
import geemap
|
|
12
12
|
|
|
13
13
|
# Local imports
|
|
14
|
-
from ..geo.utils import convert_format_lat_lon
|
|
14
|
+
# from ..geo.utils import convert_format_lat_lon
|
|
15
15
|
|
|
16
16
|
def initialize_earth_engine():
|
|
17
17
|
"""Initialize the Earth Engine API."""
|
|
@@ -21,12 +21,13 @@ def get_roi(input_coords):
|
|
|
21
21
|
"""Create an Earth Engine region of interest polygon from coordinates.
|
|
22
22
|
|
|
23
23
|
Args:
|
|
24
|
-
input_coords: List of coordinate pairs defining the polygon vertices
|
|
24
|
+
input_coords: List of coordinate pairs defining the polygon vertices in (lon, lat) format
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
27
|
ee.Geometry.Polygon: Earth Engine polygon geometry
|
|
28
28
|
"""
|
|
29
|
-
coords =
|
|
29
|
+
coords = input_coords.copy()
|
|
30
|
+
coords.append(input_coords[0])
|
|
30
31
|
return ee.Geometry.Polygon(coords)
|
|
31
32
|
|
|
32
33
|
def get_center_point(roi):
|
|
@@ -40,13 +40,13 @@ def get_geojson_links(output_dir):
|
|
|
40
40
|
df_links = pd.read_csv(filepath, dtype=data_types)
|
|
41
41
|
return df_links
|
|
42
42
|
|
|
43
|
-
def find_row_for_location(df,
|
|
44
|
-
"""Find the dataset row containing building data for a given lat
|
|
43
|
+
def find_row_for_location(df, lon, lat):
|
|
44
|
+
"""Find the dataset row containing building data for a given lon/lat coordinate.
|
|
45
45
|
|
|
46
46
|
Args:
|
|
47
47
|
df: DataFrame containing dataset links
|
|
48
|
-
lat: Latitude coordinate to search for
|
|
49
48
|
lon: Longitude coordinate to search for
|
|
49
|
+
lat: Latitude coordinate to search for
|
|
50
50
|
|
|
51
51
|
Returns:
|
|
52
52
|
pandas.Series: Matching row from DataFrame, or None if no match found
|
|
@@ -57,7 +57,7 @@ def find_row_for_location(df, lat, lon):
|
|
|
57
57
|
continue
|
|
58
58
|
|
|
59
59
|
try:
|
|
60
|
-
# Convert lat
|
|
60
|
+
# Convert lon/lat to tile coordinates at the quadkey's zoom level
|
|
61
61
|
loc_tile_x, loc_tile_y = tile_from_lat_lon(lat, lon, len(quadkey))
|
|
62
62
|
qk_tile_x, qk_tile_y, _ = quadkey_to_tile(quadkey)
|
|
63
63
|
|
|
@@ -73,7 +73,7 @@ def get_mbfp_geojson(output_dir, rectangle_vertices):
|
|
|
73
73
|
|
|
74
74
|
Args:
|
|
75
75
|
output_dir: Directory to save downloaded files
|
|
76
|
-
rectangle_vertices: List of (
|
|
76
|
+
rectangle_vertices: List of (lon, lat) tuples defining the rectangle corners
|
|
77
77
|
|
|
78
78
|
Returns:
|
|
79
79
|
dict: GeoJSON data containing building footprints
|
|
@@ -84,8 +84,8 @@ def get_mbfp_geojson(output_dir, rectangle_vertices):
|
|
|
84
84
|
# Find and download files for each vertex of the rectangle
|
|
85
85
|
filenames = []
|
|
86
86
|
for vertex in rectangle_vertices:
|
|
87
|
-
|
|
88
|
-
row = find_row_for_location(df_links,
|
|
87
|
+
lon, lat = vertex
|
|
88
|
+
row = find_row_for_location(df_links, lon, lat)
|
|
89
89
|
if row is not None:
|
|
90
90
|
# Construct filename and download if not already downloaded
|
|
91
91
|
location = row["Location"]
|
|
@@ -15,12 +15,12 @@ import numpy as np
|
|
|
15
15
|
from osgeo import gdal, osr
|
|
16
16
|
import pyproj
|
|
17
17
|
|
|
18
|
-
def deg2num(
|
|
19
|
-
"""Convert latitude
|
|
18
|
+
def deg2num(lon_deg, lat_deg, zoom):
|
|
19
|
+
"""Convert longitude/latitude coordinates to tile coordinates.
|
|
20
20
|
|
|
21
21
|
Args:
|
|
22
|
-
lat_deg (float): Latitude in degrees
|
|
23
22
|
lon_deg (float): Longitude in degrees
|
|
23
|
+
lat_deg (float): Latitude in degrees
|
|
24
24
|
zoom (int): Zoom level
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
@@ -33,7 +33,7 @@ def deg2num(lat_deg, lon_deg, zoom):
|
|
|
33
33
|
return (xtile, ytile)
|
|
34
34
|
|
|
35
35
|
def num2deg(xtile, ytile, zoom):
|
|
36
|
-
"""Convert tile coordinates to latitude
|
|
36
|
+
"""Convert tile coordinates to longitude/latitude coordinates.
|
|
37
37
|
|
|
38
38
|
Args:
|
|
39
39
|
xtile (float): X tile coordinate
|
|
@@ -41,19 +41,19 @@ def num2deg(xtile, ytile, zoom):
|
|
|
41
41
|
zoom (int): Zoom level
|
|
42
42
|
|
|
43
43
|
Returns:
|
|
44
|
-
tuple: (
|
|
44
|
+
tuple: (longitude, latitude) in degrees
|
|
45
45
|
"""
|
|
46
46
|
n = 2.0 ** zoom
|
|
47
47
|
lon_deg = xtile / n * 360.0 - 180.0
|
|
48
48
|
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
|
|
49
49
|
lat_deg = math.degrees(lat_rad)
|
|
50
|
-
return (
|
|
50
|
+
return (lon_deg, lat_deg)
|
|
51
51
|
|
|
52
52
|
def download_tiles(polygon, zoom):
|
|
53
53
|
"""Download satellite imagery tiles covering a polygon region.
|
|
54
54
|
|
|
55
55
|
Args:
|
|
56
|
-
polygon (list): List of (
|
|
56
|
+
polygon (list): List of (lon, lat) coordinates defining the region
|
|
57
57
|
zoom (int): Zoom level for tile detail
|
|
58
58
|
|
|
59
59
|
Returns:
|
|
@@ -62,14 +62,14 @@ def download_tiles(polygon, zoom):
|
|
|
62
62
|
print(f"Downloading tiles")
|
|
63
63
|
|
|
64
64
|
# Find bounding box of polygon
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
min_lon = min(p[0] for p in polygon)
|
|
66
|
+
max_lon = max(p[0] for p in polygon)
|
|
67
|
+
min_lat = min(p[1] for p in polygon)
|
|
68
|
+
max_lat = max(p[1] for p in polygon)
|
|
69
69
|
|
|
70
70
|
# Convert to tile coordinates
|
|
71
|
-
min_x, max_y = map(math.floor, deg2num(
|
|
72
|
-
max_x, min_y = map(math.ceil, deg2num(
|
|
71
|
+
min_x, max_y = map(math.floor, deg2num(min_lon, max_lat, zoom))
|
|
72
|
+
max_x, min_y = map(math.ceil, deg2num(max_lon, min_lat, zoom))
|
|
73
73
|
|
|
74
74
|
# Download tiles within bounds
|
|
75
75
|
tiles = {}
|
|
@@ -108,7 +108,7 @@ def crop_image(image, polygon, bounds, zoom):
|
|
|
108
108
|
|
|
109
109
|
Args:
|
|
110
110
|
image (Image): PIL Image to crop
|
|
111
|
-
polygon (list): List of (
|
|
111
|
+
polygon (list): List of (lon, lat) coordinates
|
|
112
112
|
bounds (tuple): (min_x, min_y, max_x, max_y) tile bounds
|
|
113
113
|
zoom (int): Zoom level
|
|
114
114
|
|
|
@@ -120,8 +120,8 @@ def crop_image(image, polygon, bounds, zoom):
|
|
|
120
120
|
|
|
121
121
|
# Convert polygon coordinates to pixel coordinates
|
|
122
122
|
polygon_pixels = []
|
|
123
|
-
for
|
|
124
|
-
x, y = deg2num(
|
|
123
|
+
for lon, lat in polygon:
|
|
124
|
+
x, y = deg2num(lon, lat, zoom)
|
|
125
125
|
px = (x - min_x) * 256
|
|
126
126
|
py = (y - min_y) * 256
|
|
127
127
|
polygon_pixels.append((px, py))
|
|
@@ -143,7 +143,7 @@ def save_as_geotiff(image, polygon, zoom, bbox, bounds, output_path):
|
|
|
143
143
|
|
|
144
144
|
Args:
|
|
145
145
|
image (Image): PIL Image to save
|
|
146
|
-
polygon (list): List of (
|
|
146
|
+
polygon (list): List of (lon, lat) coordinates
|
|
147
147
|
zoom (int): Zoom level
|
|
148
148
|
bbox (tuple): Bounding box of cropped image
|
|
149
149
|
bounds (tuple): (min_x, min_y, max_x, max_y) tile bounds
|
|
@@ -152,8 +152,8 @@ def save_as_geotiff(image, polygon, zoom, bbox, bounds, output_path):
|
|
|
152
152
|
min_x, min_y, max_x, max_y = bounds
|
|
153
153
|
|
|
154
154
|
# Calculate georeferencing coordinates
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
lon_upper_left, lat_upper_left = num2deg(min_x + bbox[0]/256, min_y + bbox[1]/256, zoom)
|
|
156
|
+
lon_lower_right, lat_lower_right = num2deg(min_x + bbox[2]/256, min_y + bbox[3]/256, zoom)
|
|
157
157
|
|
|
158
158
|
# Create transformation from WGS84 to Web Mercator
|
|
159
159
|
wgs84 = pyproj.CRS('EPSG:4326')
|
|
@@ -161,8 +161,8 @@ def save_as_geotiff(image, polygon, zoom, bbox, bounds, output_path):
|
|
|
161
161
|
transformer = pyproj.Transformer.from_crs(wgs84, web_mercator, always_xy=True)
|
|
162
162
|
|
|
163
163
|
# Transform coordinates to Web Mercator
|
|
164
|
-
upper_left_x, upper_left_y = transformer.transform(
|
|
165
|
-
lower_right_x, lower_right_y = transformer.transform(
|
|
164
|
+
upper_left_x, upper_left_y = transformer.transform(lon_upper_left, lat_upper_left)
|
|
165
|
+
lower_right_x, lower_right_y = transformer.transform(lon_lower_right, lat_lower_right)
|
|
166
166
|
|
|
167
167
|
# Calculate pixel size
|
|
168
168
|
pixel_size_x = (lower_right_x - upper_left_x) / image.width
|
|
@@ -190,7 +190,7 @@ def save_oemj_as_geotiff(polygon, filepath, zoom=16):
|
|
|
190
190
|
"""Download and save OpenEarthMap Japan imagery as GeoTIFF.
|
|
191
191
|
|
|
192
192
|
Args:
|
|
193
|
-
polygon (list): List of (
|
|
193
|
+
polygon (list): List of (lon, lat) coordinates defining region
|
|
194
194
|
filepath (str): Output path for GeoTIFF
|
|
195
195
|
zoom (int, optional): Zoom level for detail. Defaults to 16.
|
|
196
196
|
"""
|
|
@@ -20,20 +20,20 @@ def load_geojsons_from_openmaptiles(rectangle_vertices, API_KEY):
|
|
|
20
20
|
"""Download and process building footprint data from OpenMapTiles vector tiles.
|
|
21
21
|
|
|
22
22
|
Args:
|
|
23
|
-
rectangle_vertices: List of (
|
|
23
|
+
rectangle_vertices: List of (lon, lat) coordinates defining the bounding box
|
|
24
24
|
API_KEY: OpenMapTiles API key for authentication
|
|
25
25
|
|
|
26
26
|
Returns:
|
|
27
27
|
list: List of GeoJSON features containing building footprints with standardized properties
|
|
28
28
|
"""
|
|
29
|
-
# Extract
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# Extract longitudes and latitudes from vertices to find bounding box
|
|
30
|
+
lons = [coord[0] for coord in rectangle_vertices]
|
|
31
|
+
lats = [coord[1] for coord in rectangle_vertices]
|
|
32
32
|
|
|
33
|
-
min_lat = min(lats)
|
|
34
|
-
max_lat = max(lats)
|
|
35
33
|
min_lon = min(lons)
|
|
36
34
|
max_lon = max(lons)
|
|
35
|
+
min_lat = min(lats)
|
|
36
|
+
max_lat = max(lats)
|
|
37
37
|
|
|
38
38
|
# Use zoom level 15 which provides good detail for buildings while keeping data size manageable
|
|
39
39
|
zoom = 15
|
|
@@ -183,10 +183,10 @@ def convert_geojson_format(features):
|
|
|
183
183
|
ring_properties['is_inner'] = j > 0
|
|
184
184
|
ring_properties['role'] = 'inner' if j > 0 else 'outer'
|
|
185
185
|
|
|
186
|
-
# Create new geometry
|
|
186
|
+
# Create new geometry keeping coordinate order as (lon,lat)
|
|
187
187
|
new_geometry = {
|
|
188
188
|
'type': 'Polygon',
|
|
189
|
-
'coordinates': [
|
|
189
|
+
'coordinates': [ring]
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
new_feature = {
|
|
@@ -204,10 +204,10 @@ def convert_geojson_format(features):
|
|
|
204
204
|
ring_properties['is_inner'] = i > 0
|
|
205
205
|
ring_properties['role'] = 'inner' if i > 0 else 'outer'
|
|
206
206
|
|
|
207
|
-
# Create new geometry
|
|
207
|
+
# Create new geometry keeping coordinate order as (lon,lat)
|
|
208
208
|
new_geometry = {
|
|
209
209
|
'type': 'Polygon',
|
|
210
|
-
'coordinates': [
|
|
210
|
+
'coordinates': [ring]
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
new_feature = {
|
|
@@ -26,16 +26,16 @@ def load_geojsons_from_openstreetmap(rectangle_vertices):
|
|
|
26
26
|
"""Download and process building footprint data from OpenStreetMap.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
29
|
-
rectangle_vertices: List of (
|
|
29
|
+
rectangle_vertices: List of (lon, lat) coordinates defining the bounding box
|
|
30
30
|
|
|
31
31
|
Returns:
|
|
32
32
|
list: List of GeoJSON features containing building footprints with standardized properties
|
|
33
33
|
"""
|
|
34
34
|
# Create a bounding box from the rectangle vertices
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
min_lon = min(v[0] for v in rectangle_vertices)
|
|
36
|
+
max_lon = max(v[0] for v in rectangle_vertices)
|
|
37
|
+
min_lat = min(v[1] for v in rectangle_vertices)
|
|
38
|
+
max_lat = max(v[1] for v in rectangle_vertices)
|
|
39
39
|
|
|
40
40
|
# Enhanced Overpass API query with recursive member extraction
|
|
41
41
|
overpass_url = "http://overpass-api.de/api/interpreter"
|
|
@@ -73,7 +73,7 @@ def load_geojsons_from_openstreetmap(rectangle_vertices):
|
|
|
73
73
|
Returns:
|
|
74
74
|
list: Processed coordinate pairs with reversed order
|
|
75
75
|
"""
|
|
76
|
-
return [coord
|
|
76
|
+
return [coord for coord in geometry] # Keep original order since already (lon, lat)
|
|
77
77
|
|
|
78
78
|
def get_height_from_properties(properties):
|
|
79
79
|
"""Helper function to extract height from properties.
|
|
@@ -253,7 +253,7 @@ def convert_feature(feature):
|
|
|
253
253
|
for coord in ring:
|
|
254
254
|
# Swap the order if needed (assuming original is [lat, lon])
|
|
255
255
|
lat, lon = coord
|
|
256
|
-
new_ring.append((lat,
|
|
256
|
+
new_ring.append((lon, lat)) # Changed to (lon, lat)
|
|
257
257
|
new_coordinates.append(new_ring)
|
|
258
258
|
|
|
259
259
|
new_feature['geometry']['type'] = 'Polygon'
|
|
@@ -460,9 +460,8 @@ def swap_coordinates(geom_mapping):
|
|
|
460
460
|
if isinstance(coord_list[0], (list, tuple)):
|
|
461
461
|
return [swap_coords(c) for c in coord_list]
|
|
462
462
|
else:
|
|
463
|
-
#
|
|
464
|
-
|
|
465
|
-
return [lat, lon]
|
|
463
|
+
# Keep original order since already (lon, lat)
|
|
464
|
+
return coord_list
|
|
466
465
|
|
|
467
466
|
geom_mapping['coordinates'] = swap_coords(coords)
|
|
468
467
|
return geom_mapping
|
|
@@ -471,7 +470,7 @@ def load_land_cover_geojson_from_osm(rectangle_vertices_ori):
|
|
|
471
470
|
"""Load land cover data from OpenStreetMap within a given rectangular area.
|
|
472
471
|
|
|
473
472
|
Args:
|
|
474
|
-
rectangle_vertices_ori (list): List of (
|
|
473
|
+
rectangle_vertices_ori (list): List of (lon, lat) coordinates defining the rectangle
|
|
475
474
|
|
|
476
475
|
Returns:
|
|
477
476
|
list: List of GeoJSON features with land cover classifications
|
|
@@ -480,8 +479,11 @@ def load_land_cover_geojson_from_osm(rectangle_vertices_ori):
|
|
|
480
479
|
rectangle_vertices = rectangle_vertices_ori.copy()
|
|
481
480
|
rectangle_vertices.append(rectangle_vertices_ori[0])
|
|
482
481
|
|
|
483
|
-
#
|
|
484
|
-
|
|
482
|
+
# Instead of using poly:"lat lon lat lon...", use area coordinates
|
|
483
|
+
min_lat = min(lat for lon, lat in rectangle_vertices)
|
|
484
|
+
max_lat = max(lat for lon, lat in rectangle_vertices)
|
|
485
|
+
min_lon = min(lon for lon, lat in rectangle_vertices)
|
|
486
|
+
max_lon = max(lon for lon, lat in rectangle_vertices)
|
|
485
487
|
|
|
486
488
|
# Initialize dictionary to store OSM keys and their allowed values
|
|
487
489
|
osm_keys_values = defaultdict(list)
|
|
@@ -504,16 +506,16 @@ def load_land_cover_geojson_from_osm(rectangle_vertices_ori):
|
|
|
504
506
|
for key, values in osm_keys_values.items():
|
|
505
507
|
if values:
|
|
506
508
|
if values == ['*']:
|
|
507
|
-
# Query for any value of this key
|
|
508
|
-
query_parts.append(f'way["{key}"](
|
|
509
|
-
query_parts.append(f'relation["{key}"](
|
|
509
|
+
# Query for any value of this key using bounding box
|
|
510
|
+
query_parts.append(f'way["{key}"]({min_lat},{min_lon},{max_lat},{max_lon});')
|
|
511
|
+
query_parts.append(f'relation["{key}"]({min_lat},{min_lon},{max_lat},{max_lon});')
|
|
510
512
|
else:
|
|
511
513
|
# Remove duplicate values
|
|
512
514
|
values = list(set(values))
|
|
513
515
|
# Build regex pattern for specific values
|
|
514
516
|
values_regex = '|'.join(values)
|
|
515
|
-
query_parts.append(f'way["{key}"~"^{values_regex}$"](
|
|
516
|
-
query_parts.append(f'relation["{key}"~"^{values_regex}$"](
|
|
517
|
+
query_parts.append(f'way["{key}"~"^{values_regex}$"]({min_lat},{min_lon},{max_lat},{max_lon});')
|
|
518
|
+
query_parts.append(f'relation["{key}"~"^{values_regex}$"]({min_lat},{min_lon},{max_lat},{max_lon});')
|
|
517
519
|
|
|
518
520
|
# Combine query parts into complete Overpass query
|
|
519
521
|
query_body = "\n ".join(query_parts)
|
|
@@ -541,11 +543,11 @@ def load_land_cover_geojson_from_osm(rectangle_vertices_ori):
|
|
|
541
543
|
geojson_data = json2geojson(data)
|
|
542
544
|
|
|
543
545
|
# Create shapely polygon from rectangle vertices (in lon,lat order)
|
|
544
|
-
rectangle_polygon = Polygon(
|
|
546
|
+
rectangle_polygon = Polygon(rectangle_vertices)
|
|
545
547
|
|
|
546
548
|
# Calculate center point for projection
|
|
547
|
-
center_lat = sum(lat for
|
|
548
|
-
center_lon = sum(lon for
|
|
549
|
+
center_lat = sum(lat for lon, lat in rectangle_vertices) / len(rectangle_vertices)
|
|
550
|
+
center_lon = sum(lon for lon, lat in rectangle_vertices) / len(rectangle_vertices)
|
|
549
551
|
|
|
550
552
|
# Set up coordinate reference systems for projection
|
|
551
553
|
wgs84 = pyproj.CRS('EPSG:4326') # Standard lat/lon
|
|
@@ -64,7 +64,7 @@ def is_valid_value(value):
|
|
|
64
64
|
|
|
65
65
|
def convert_gdf_to_geojson(gdf):
|
|
66
66
|
"""
|
|
67
|
-
Convert GeoDataFrame to GeoJSON format with coordinates in (
|
|
67
|
+
Convert GeoDataFrame to GeoJSON format with coordinates in (lon, lat) order.
|
|
68
68
|
Extracts all columns as properties except for 'geometry' and 'bbox'.
|
|
69
69
|
Sets height and min_height to 0 if not present and handles arrays.
|
|
70
70
|
|
|
@@ -81,14 +81,6 @@ def convert_gdf_to_geojson(gdf):
|
|
|
81
81
|
# Convert Shapely geometry to GeoJSON format
|
|
82
82
|
geom = mapping(row['geometry'])
|
|
83
83
|
|
|
84
|
-
# Swap coordinate ordering from (lon, lat) to (lat, lon)
|
|
85
|
-
if geom['type'] == 'Polygon':
|
|
86
|
-
new_coordinates = []
|
|
87
|
-
for ring in geom['coordinates']:
|
|
88
|
-
new_ring = [[coord[1], coord[0]] for coord in ring]
|
|
89
|
-
new_coordinates.append(new_ring)
|
|
90
|
-
geom['coordinates'] = new_coordinates
|
|
91
|
-
|
|
92
84
|
# Initialize properties dictionary for this feature
|
|
93
85
|
properties = {}
|
|
94
86
|
|
|
@@ -125,18 +117,18 @@ def convert_gdf_to_geojson(gdf):
|
|
|
125
117
|
|
|
126
118
|
def rectangle_to_bbox(vertices):
|
|
127
119
|
"""
|
|
128
|
-
Convert rectangle vertices in (
|
|
120
|
+
Convert rectangle vertices in (lon, lat) format to a GeoDataFrame bbox
|
|
129
121
|
with Shapely box geometry in (minx, miny, maxx, maxy) format
|
|
130
122
|
|
|
131
123
|
Args:
|
|
132
|
-
vertices (list): List of tuples containing (
|
|
124
|
+
vertices (list): List of tuples containing (lon, lat) coordinates
|
|
133
125
|
|
|
134
126
|
Returns:
|
|
135
127
|
tuple: Bounding box coordinates (min_lon, min_lat, max_lon, max_lat)
|
|
136
128
|
"""
|
|
137
|
-
# Extract
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
# Extract lon, lat values from vertices
|
|
130
|
+
lons = [vertex[0] for vertex in vertices]
|
|
131
|
+
lats = [vertex[1] for vertex in vertices]
|
|
140
132
|
|
|
141
133
|
# Calculate bounding box extents
|
|
142
134
|
min_lat = min(lats)
|
|
@@ -192,7 +184,7 @@ def load_geojsons_from_overture(rectangle_vertices):
|
|
|
192
184
|
Download and process building footprint data from Overture Maps.
|
|
193
185
|
|
|
194
186
|
Args:
|
|
195
|
-
rectangle_vertices (list): List of (
|
|
187
|
+
rectangle_vertices (list): List of (lon, lat) coordinates defining the bounding box
|
|
196
188
|
|
|
197
189
|
Returns:
|
|
198
190
|
list: List of GeoJSON features containing building footprints with standardized properties
|
|
@@ -231,10 +231,10 @@ def create_xml_content(building_height_grid, building_id_grid, land_cover_veg_gr
|
|
|
231
231
|
city_country_name = get_city_country_name_from_rectangle(rectangle_vertices)
|
|
232
232
|
|
|
233
233
|
# Calculate center coordinates of the model area
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
longitudes = [coord[0] for coord in rectangle_vertices] # Changed order from lat to lon
|
|
235
|
+
latitudes = [coord[1] for coord in rectangle_vertices] # Changed order from lat to lon
|
|
236
|
+
center_lon = str(sum(longitudes) / len(longitudes)) # Changed order
|
|
237
|
+
center_lat = str(sum(latitudes) / len(latitudes)) # Changed order
|
|
238
238
|
|
|
239
239
|
timezone_info = get_timezone_info(rectangle_vertices)
|
|
240
240
|
|