voxcity 0.5.14__py3-none-any.whl → 0.5.15__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of voxcity might be problematic. Click here for more details.
- voxcity/downloader/citygml.py +202 -28
- voxcity/downloader/eubucco.py +91 -14
- voxcity/downloader/gee.py +164 -22
- voxcity/downloader/mbfp.py +55 -9
- voxcity/downloader/oemj.py +110 -24
- voxcity/downloader/omt.py +74 -7
- voxcity/downloader/osm.py +109 -23
- voxcity/downloader/overture.py +108 -23
- voxcity/downloader/utils.py +37 -7
- voxcity/exporter/envimet.py +180 -61
- voxcity/exporter/magicavoxel.py +138 -28
- voxcity/exporter/obj.py +159 -36
- voxcity/generator.py +159 -76
- voxcity/geoprocessor/draw.py +180 -27
- voxcity/geoprocessor/grid.py +178 -38
- voxcity/geoprocessor/mesh.py +347 -43
- voxcity/geoprocessor/network.py +196 -63
- voxcity/geoprocessor/polygon.py +365 -88
- voxcity/geoprocessor/utils.py +283 -72
- voxcity/simulator/solar.py +596 -201
- voxcity/simulator/view.py +278 -723
- voxcity/utils/lc.py +183 -0
- voxcity/utils/material.py +99 -32
- voxcity/utils/visualization.py +2578 -1988
- voxcity/utils/weather.py +816 -615
- {voxcity-0.5.14.dist-info → voxcity-0.5.15.dist-info}/METADATA +10 -12
- voxcity-0.5.15.dist-info/RECORD +38 -0
- {voxcity-0.5.14.dist-info → voxcity-0.5.15.dist-info}/WHEEL +1 -1
- voxcity-0.5.14.dist-info/RECORD +0 -38
- {voxcity-0.5.14.dist-info → voxcity-0.5.15.dist-info}/licenses/AUTHORS.rst +0 -0
- {voxcity-0.5.14.dist-info → voxcity-0.5.15.dist-info}/licenses/LICENSE +0 -0
- {voxcity-0.5.14.dist-info → voxcity-0.5.15.dist-info}/top_level.txt +0 -0
voxcity/downloader/gee.py
CHANGED
|
@@ -4,6 +4,19 @@ Module for interacting with Google Earth Engine API and downloading geospatial d
|
|
|
4
4
|
This module provides functionality to initialize Earth Engine, create regions of interest,
|
|
5
5
|
download various types of satellite imagery and terrain data, and save them as GeoTIFF files.
|
|
6
6
|
It supports multiple data sources including DEMs, land cover maps, and building footprints.
|
|
7
|
+
|
|
8
|
+
The module offers the following main functionalities:
|
|
9
|
+
1. Earth Engine initialization and region of interest (ROI) management
|
|
10
|
+
2. Digital Elevation Model (DEM) data access from multiple sources
|
|
11
|
+
3. Land cover data retrieval from ESA WorldCover, Dynamic World, and ESRI
|
|
12
|
+
4. Building footprint and height data extraction
|
|
13
|
+
5. GeoTIFF export with customizable parameters
|
|
14
|
+
|
|
15
|
+
Dependencies:
|
|
16
|
+
- ee: Google Earth Engine Python API
|
|
17
|
+
- geemap: Python package for interactive mapping with Earth Engine
|
|
18
|
+
|
|
19
|
+
Note: Most functions require Earth Engine authentication to be set up beforehand.
|
|
7
20
|
"""
|
|
8
21
|
|
|
9
22
|
# Earth Engine and geospatial imports
|
|
@@ -14,17 +27,29 @@ import geemap
|
|
|
14
27
|
# from ..geo.utils import convert_format_lat_lon
|
|
15
28
|
|
|
16
29
|
def initialize_earth_engine():
|
|
17
|
-
"""Initialize the Earth Engine API.
|
|
30
|
+
"""Initialize the Earth Engine API.
|
|
31
|
+
|
|
32
|
+
This function must be called before using any other Earth Engine functionality.
|
|
33
|
+
It assumes that Earth Engine authentication has been set up properly.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ee.EEException: If authentication fails or Earth Engine is unavailable
|
|
37
|
+
"""
|
|
18
38
|
ee.Initialize()
|
|
19
39
|
|
|
20
40
|
def get_roi(input_coords):
|
|
21
41
|
"""Create an Earth Engine region of interest polygon from coordinates.
|
|
22
42
|
|
|
23
43
|
Args:
|
|
24
|
-
input_coords: List of coordinate pairs defining the polygon vertices in (lon, lat) format
|
|
44
|
+
input_coords: List of coordinate pairs defining the polygon vertices in (lon, lat) format.
|
|
45
|
+
The coordinates should form a valid polygon (non-self-intersecting).
|
|
25
46
|
|
|
26
47
|
Returns:
|
|
27
|
-
ee.Geometry.Polygon: Earth Engine polygon geometry
|
|
48
|
+
ee.Geometry.Polygon: Earth Engine polygon geometry representing the ROI
|
|
49
|
+
|
|
50
|
+
Note:
|
|
51
|
+
The function automatically closes the polygon by connecting the last vertex
|
|
52
|
+
to the first vertex if they are not the same.
|
|
28
53
|
"""
|
|
29
54
|
coords = input_coords.copy()
|
|
30
55
|
coords.append(input_coords[0])
|
|
@@ -34,10 +59,14 @@ def get_center_point(roi):
|
|
|
34
59
|
"""Get the centroid coordinates of a region of interest.
|
|
35
60
|
|
|
36
61
|
Args:
|
|
37
|
-
roi: Earth Engine geometry
|
|
62
|
+
roi: Earth Engine geometry object representing the region of interest
|
|
38
63
|
|
|
39
64
|
Returns:
|
|
40
|
-
tuple: (longitude, latitude) of centroid
|
|
65
|
+
tuple: (longitude, latitude) coordinates of the centroid
|
|
66
|
+
|
|
67
|
+
Note:
|
|
68
|
+
The centroid is calculated using Earth Engine's geometric centroid algorithm,
|
|
69
|
+
which may not always fall within the geometry for complex shapes.
|
|
41
70
|
"""
|
|
42
71
|
center_point = roi.centroid()
|
|
43
72
|
center_coords = center_point.coordinates().getInfo()
|
|
@@ -47,11 +76,15 @@ def get_ee_image_collection(collection_name, roi):
|
|
|
47
76
|
"""Get the first image from an Earth Engine ImageCollection filtered by region.
|
|
48
77
|
|
|
49
78
|
Args:
|
|
50
|
-
collection_name: Name of the Earth Engine ImageCollection
|
|
79
|
+
collection_name: Name of the Earth Engine ImageCollection (e.g., 'LANDSAT/LC08/C02/T1_TOA')
|
|
51
80
|
roi: Earth Engine geometry to filter by
|
|
52
81
|
|
|
53
82
|
Returns:
|
|
54
|
-
ee.Image: First image from collection clipped to ROI
|
|
83
|
+
ee.Image: First image from collection clipped to ROI, with any masked pixels unmasked
|
|
84
|
+
|
|
85
|
+
Note:
|
|
86
|
+
The function sorts images by time (earliest first) and unmasks any masked pixels
|
|
87
|
+
in the final image. This is useful for ensuring complete coverage of the ROI.
|
|
55
88
|
"""
|
|
56
89
|
# Filter collection by bounds and get first image
|
|
57
90
|
collection = ee.ImageCollection(collection_name).filterBounds(roi)
|
|
@@ -61,11 +94,15 @@ def get_ee_image(collection_name, roi):
|
|
|
61
94
|
"""Get an Earth Engine Image clipped to a region.
|
|
62
95
|
|
|
63
96
|
Args:
|
|
64
|
-
collection_name: Name of the Earth Engine Image
|
|
97
|
+
collection_name: Name of the Earth Engine Image asset
|
|
65
98
|
roi: Earth Engine geometry to clip to
|
|
66
99
|
|
|
67
100
|
Returns:
|
|
68
101
|
ee.Image: Image clipped to ROI
|
|
102
|
+
|
|
103
|
+
Note:
|
|
104
|
+
Unlike get_ee_image_collection(), this function works with single image assets
|
|
105
|
+
rather than image collections. It's useful for static datasets like DEMs.
|
|
69
106
|
"""
|
|
70
107
|
collection = ee.Image(collection_name)
|
|
71
108
|
return collection.clip(roi)
|
|
@@ -73,13 +110,22 @@ def get_ee_image(collection_name, roi):
|
|
|
73
110
|
def save_geotiff(image, filename, resolution=1, scale=None, region=None, crs=None):
|
|
74
111
|
"""Save an Earth Engine image as a GeoTIFF file.
|
|
75
112
|
|
|
113
|
+
This function provides flexible options for exporting Earth Engine images to GeoTIFF format.
|
|
114
|
+
It handles different export scenarios based on the provided parameters.
|
|
115
|
+
|
|
76
116
|
Args:
|
|
77
117
|
image: Earth Engine image to save
|
|
78
|
-
filename: Output filename
|
|
79
|
-
resolution: Output resolution in degrees (default: 1)
|
|
80
|
-
scale: Output scale in meters
|
|
81
|
-
region: Region to export
|
|
82
|
-
crs: Coordinate reference system
|
|
118
|
+
filename: Output filename for the GeoTIFF
|
|
119
|
+
resolution: Output resolution in degrees (default: 1), used when scale is not provided
|
|
120
|
+
scale: Output scale in meters (overrides resolution if provided)
|
|
121
|
+
region: Region to export (required if scale is provided)
|
|
122
|
+
crs: Coordinate reference system (e.g., 'EPSG:4326')
|
|
123
|
+
|
|
124
|
+
Note:
|
|
125
|
+
- If scale and region are provided, uses ee_export_image()
|
|
126
|
+
- Otherwise, uses ee_to_geotiff() with resolution parameter
|
|
127
|
+
- The function automatically converts output to Cloud Optimized GeoTIFF (COG)
|
|
128
|
+
format when using ee_to_geotiff()
|
|
83
129
|
"""
|
|
84
130
|
# Handle different export scenarios based on provided parameters
|
|
85
131
|
if scale and region:
|
|
@@ -96,13 +142,30 @@ def save_geotiff(image, filename, resolution=1, scale=None, region=None, crs=Non
|
|
|
96
142
|
def get_dem_image(roi_buffered, source):
|
|
97
143
|
"""Get a digital elevation model (DEM) image for a region.
|
|
98
144
|
|
|
145
|
+
This function provides access to various global and regional Digital Elevation Model (DEM)
|
|
146
|
+
datasets through Earth Engine. Each source has different coverage areas and resolutions.
|
|
147
|
+
|
|
99
148
|
Args:
|
|
100
|
-
roi_buffered: Earth Engine geometry with buffer
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
roi_buffered: Earth Engine geometry with buffer - should be larger than the actual
|
|
150
|
+
area of interest to ensure smooth interpolation at edges
|
|
151
|
+
source: DEM source, one of:
|
|
152
|
+
- 'NASA': SRTM 30m global DEM
|
|
153
|
+
- 'COPERNICUS': Copernicus 30m global DEM
|
|
154
|
+
- 'DeltaDTM': Deltares global DTM
|
|
155
|
+
- 'FABDEM': Forest And Buildings removed MERIT DEM
|
|
156
|
+
- 'England 1m DTM': UK Environment Agency 1m terrain model
|
|
157
|
+
- 'DEM France 5m': IGN RGE ALTI 5m France coverage
|
|
158
|
+
- 'DEM France 1m': IGN RGE ALTI 1m France coverage
|
|
159
|
+
- 'AUSTRALIA 5M DEM': Geoscience Australia 5m DEM
|
|
160
|
+
- 'USGS 3DEP 1m': USGS 3D Elevation Program 1m DEM
|
|
103
161
|
|
|
104
162
|
Returns:
|
|
105
163
|
ee.Image: DEM image clipped to region
|
|
164
|
+
|
|
165
|
+
Note:
|
|
166
|
+
Some sources may have limited coverage or require special access permissions.
|
|
167
|
+
The function will raise an error if the selected source is not available for
|
|
168
|
+
the specified region.
|
|
106
169
|
"""
|
|
107
170
|
# Handle different DEM sources
|
|
108
171
|
if source == 'NASA':
|
|
@@ -154,9 +217,30 @@ def get_dem_image(roi_buffered, source):
|
|
|
154
217
|
def save_geotiff_esa_land_cover(roi, geotiff_path):
|
|
155
218
|
"""Save ESA WorldCover land cover data as a colored GeoTIFF.
|
|
156
219
|
|
|
220
|
+
Downloads and exports the ESA WorldCover 10m resolution global land cover map.
|
|
221
|
+
The output is a colored GeoTIFF where each land cover class is represented by
|
|
222
|
+
a unique color as defined in the color_map.
|
|
223
|
+
|
|
157
224
|
Args:
|
|
158
225
|
roi: Earth Engine geometry defining region of interest
|
|
159
226
|
geotiff_path: Output path for GeoTIFF file
|
|
227
|
+
|
|
228
|
+
Land cover classes and their corresponding colors:
|
|
229
|
+
- Trees (10): Dark green
|
|
230
|
+
- Shrubland (20): Orange
|
|
231
|
+
- Grassland (30): Yellow
|
|
232
|
+
- Cropland (40): Purple
|
|
233
|
+
- Built-up (50): Red
|
|
234
|
+
- Barren/sparse vegetation (60): Gray
|
|
235
|
+
- Snow and ice (70): White
|
|
236
|
+
- Open water (80): Blue
|
|
237
|
+
- Herbaceous wetland (90): Teal
|
|
238
|
+
- Mangroves (95): Light green
|
|
239
|
+
- Moss and lichen (100): Beige
|
|
240
|
+
|
|
241
|
+
Note:
|
|
242
|
+
The output GeoTIFF is exported at 10m resolution, which is the native
|
|
243
|
+
resolution of the ESA WorldCover dataset.
|
|
160
244
|
"""
|
|
161
245
|
# Initialize Earth Engine
|
|
162
246
|
ee.Initialize()
|
|
@@ -197,10 +281,30 @@ def save_geotiff_esa_land_cover(roi, geotiff_path):
|
|
|
197
281
|
def save_geotiff_dynamic_world_v1(roi, geotiff_path, date=None):
|
|
198
282
|
"""Save Dynamic World land cover data as a colored GeoTIFF.
|
|
199
283
|
|
|
284
|
+
Downloads and exports Google's Dynamic World near real-time land cover classification.
|
|
285
|
+
The data is available globally at 10m resolution from 2015 onwards, updated every 2-5 days.
|
|
286
|
+
|
|
200
287
|
Args:
|
|
201
288
|
roi: Earth Engine geometry defining region of interest
|
|
202
289
|
geotiff_path: Output path for GeoTIFF file
|
|
203
|
-
date: Optional date string to get data for specific time
|
|
290
|
+
date: Optional date string (YYYY-MM-DD) to get data for specific time.
|
|
291
|
+
If None, uses the most recent available image.
|
|
292
|
+
|
|
293
|
+
Land cover classes and their colors:
|
|
294
|
+
- water: Blue (#419bdf)
|
|
295
|
+
- trees: Dark green (#397d49)
|
|
296
|
+
- grass: Light green (#88b053)
|
|
297
|
+
- flooded_vegetation: Purple (#7a87c6)
|
|
298
|
+
- crops: Orange (#e49635)
|
|
299
|
+
- shrub_and_scrub: Tan (#dfc35a)
|
|
300
|
+
- built: Red (#c4281b)
|
|
301
|
+
- bare: Gray (#a59b8f)
|
|
302
|
+
- snow_and_ice: Light purple (#b39fe1)
|
|
303
|
+
|
|
304
|
+
Note:
|
|
305
|
+
If a specific date is provided but no image is available, the function
|
|
306
|
+
will use the closest available date and print a message indicating the
|
|
307
|
+
actual date used.
|
|
204
308
|
"""
|
|
205
309
|
# Initialize Earth Engine
|
|
206
310
|
ee.Initialize()
|
|
@@ -287,10 +391,29 @@ def save_geotiff_dynamic_world_v1(roi, geotiff_path, date=None):
|
|
|
287
391
|
def save_geotiff_esri_landcover(roi, geotiff_path, year=None):
|
|
288
392
|
"""Save ESRI Land Cover data as a colored GeoTIFF.
|
|
289
393
|
|
|
394
|
+
Downloads and exports ESRI's 10m resolution global land cover classification.
|
|
395
|
+
This dataset is updated annually and provides consistent global coverage.
|
|
396
|
+
|
|
290
397
|
Args:
|
|
291
398
|
roi: Earth Engine geometry defining region of interest
|
|
292
399
|
geotiff_path: Output path for GeoTIFF file
|
|
293
|
-
year: Optional year to get data for specific time
|
|
400
|
+
year: Optional year (YYYY) to get data for specific time.
|
|
401
|
+
If None, uses the most recent available year.
|
|
402
|
+
|
|
403
|
+
Land cover classes and colors:
|
|
404
|
+
- Water (#1A5BAB): Water bodies
|
|
405
|
+
- Trees (#358221): Tree cover
|
|
406
|
+
- Flooded Vegetation (#87D19E): Vegetation in water-logged areas
|
|
407
|
+
- Crops (#FFDB5C): Agricultural areas
|
|
408
|
+
- Built Area (#ED022A): Urban and built-up areas
|
|
409
|
+
- Bare Ground (#EDE9E4): Exposed soil and rock
|
|
410
|
+
- Snow/Ice (#F2FAFF): Permanent snow and ice
|
|
411
|
+
- Clouds (#C8C8C8): Cloud cover
|
|
412
|
+
- Rangeland (#C6AD8D): Natural vegetation
|
|
413
|
+
|
|
414
|
+
Note:
|
|
415
|
+
The function will print the year of the data actually used, which may
|
|
416
|
+
differ from the requested year if data is not available for that time.
|
|
294
417
|
"""
|
|
295
418
|
# Initialize Earth Engine
|
|
296
419
|
ee.Initialize()
|
|
@@ -371,9 +494,18 @@ def save_geotiff_esri_landcover(roi, geotiff_path, year=None):
|
|
|
371
494
|
def save_geotiff_open_buildings_temporal(aoi, geotiff_path):
|
|
372
495
|
"""Save Open Buildings temporal data as a GeoTIFF.
|
|
373
496
|
|
|
497
|
+
Downloads and exports building height data from Google's Open Buildings dataset.
|
|
498
|
+
This dataset provides building footprints and heights derived from satellite imagery.
|
|
499
|
+
|
|
374
500
|
Args:
|
|
375
501
|
aoi: Earth Engine geometry defining area of interest
|
|
376
502
|
geotiff_path: Output path for GeoTIFF file
|
|
503
|
+
|
|
504
|
+
Note:
|
|
505
|
+
- The output GeoTIFF contains building heights in meters
|
|
506
|
+
- The dataset is updated periodically and may not cover all regions
|
|
507
|
+
- Resolution is fixed at 4 meters per pixel
|
|
508
|
+
- Areas without buildings will have no-data values
|
|
377
509
|
"""
|
|
378
510
|
# Initialize Earth Engine
|
|
379
511
|
ee.Initialize()
|
|
@@ -402,14 +534,24 @@ def save_geotiff_open_buildings_temporal(aoi, geotiff_path):
|
|
|
402
534
|
def save_geotiff_dsm_minus_dtm(roi, geotiff_path, meshsize, source):
|
|
403
535
|
"""Get the height difference between DSM and DTM from terrain data.
|
|
404
536
|
|
|
537
|
+
Calculates the difference between Digital Surface Model (DSM) and Digital Terrain
|
|
538
|
+
Model (DTM) to estimate heights of buildings, vegetation, and other above-ground
|
|
539
|
+
features.
|
|
540
|
+
|
|
405
541
|
Args:
|
|
406
542
|
roi: Earth Engine geometry defining area of interest
|
|
407
543
|
geotiff_path: Output path for GeoTIFF file
|
|
408
|
-
meshsize: Size of each grid cell in meters
|
|
409
|
-
source: Source of terrain data
|
|
544
|
+
meshsize: Size of each grid cell in meters - determines output resolution
|
|
545
|
+
source: Source of terrain data, one of:
|
|
546
|
+
- 'England 1m DSM - DTM': UK Environment Agency 1m resolution
|
|
547
|
+
- 'Netherlands 0.5m DSM - DTM': AHN4 0.5m resolution
|
|
410
548
|
|
|
411
|
-
|
|
412
|
-
|
|
549
|
+
Note:
|
|
550
|
+
- A 100m buffer is automatically added around the ROI to ensure smooth
|
|
551
|
+
interpolation at edges
|
|
552
|
+
- The output represents height above ground level in meters
|
|
553
|
+
- Negative values may indicate data artifacts or actual below-ground features
|
|
554
|
+
- The function requires both DSM and DTM data to be available for the region
|
|
413
555
|
"""
|
|
414
556
|
# Initialize Earth Engine
|
|
415
557
|
ee.Initialize()
|
voxcity/downloader/mbfp.py
CHANGED
|
@@ -4,6 +4,16 @@ Module for downloading and processing Microsoft Building Footprints data.
|
|
|
4
4
|
This module provides functionality to download building footprint data from Microsoft's
|
|
5
5
|
open dataset, which contains building polygons extracted from satellite imagery using
|
|
6
6
|
AI. It handles downloading quadkey-based data files and converting them to GeoJSON format.
|
|
7
|
+
|
|
8
|
+
The data is organized using quadkeys, which are hierarchical spatial indexing strings
|
|
9
|
+
that identify tiles on the map at different zoom levels. Each quadkey corresponds to
|
|
10
|
+
a specific geographic area and zoom level.
|
|
11
|
+
|
|
12
|
+
Key Features:
|
|
13
|
+
- Downloads building footprint data from Microsoft's global buildings dataset
|
|
14
|
+
- Handles quadkey-based spatial queries
|
|
15
|
+
- Converts compressed data files to GeoJSON format
|
|
16
|
+
- Supports rectangular region queries using vertex coordinates
|
|
7
17
|
"""
|
|
8
18
|
|
|
9
19
|
import pandas as pd
|
|
@@ -15,11 +25,22 @@ from ..geoprocessor.polygon import load_gdf_from_multiple_gz, swap_coordinates
|
|
|
15
25
|
def get_geojson_links(output_dir):
|
|
16
26
|
"""Download and load the dataset links CSV file containing building footprint URLs.
|
|
17
27
|
|
|
28
|
+
This function downloads a master CSV file from Microsoft's server that contains
|
|
29
|
+
links to all available building footprint datasets. The CSV includes metadata
|
|
30
|
+
such as location names, quadkeys, URLs, and file sizes for each dataset tile.
|
|
31
|
+
|
|
18
32
|
Args:
|
|
19
|
-
output_dir: Directory
|
|
33
|
+
output_dir (str): Directory path where the CSV file will be saved
|
|
20
34
|
|
|
21
35
|
Returns:
|
|
22
|
-
pandas.DataFrame: DataFrame containing dataset links
|
|
36
|
+
pandas.DataFrame: DataFrame containing dataset links with columns:
|
|
37
|
+
- Location: String identifier for the geographic region
|
|
38
|
+
- QuadKey: String representing the tile's quadkey
|
|
39
|
+
- Url: Direct download link for the GeoJSON data
|
|
40
|
+
- Size: File size information
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
The CSV file is cached locally in the output directory for future use.
|
|
23
44
|
"""
|
|
24
45
|
# URL for the master CSV file containing links to all building footprint data
|
|
25
46
|
url = "https://minedbuildings.z5.web.core.windows.net/global-buildings/dataset-links.csv"
|
|
@@ -43,13 +64,23 @@ def get_geojson_links(output_dir):
|
|
|
43
64
|
def find_row_for_location(df, lon, lat):
|
|
44
65
|
"""Find the dataset row containing building data for a given lon/lat coordinate.
|
|
45
66
|
|
|
67
|
+
This function searches through the dataset links DataFrame to find the appropriate
|
|
68
|
+
tile containing the specified geographic coordinates. It converts the input
|
|
69
|
+
coordinates to tile coordinates at the same zoom level as each quadkey and
|
|
70
|
+
checks for a match.
|
|
71
|
+
|
|
46
72
|
Args:
|
|
47
|
-
df: DataFrame containing dataset links
|
|
48
|
-
lon: Longitude coordinate to search for
|
|
49
|
-
lat: Latitude coordinate to search for
|
|
73
|
+
df (pandas.DataFrame): DataFrame containing dataset links from get_geojson_links()
|
|
74
|
+
lon (float): Longitude coordinate to search for (-180 to 180)
|
|
75
|
+
lat (float): Latitude coordinate to search for (-90 to 90)
|
|
50
76
|
|
|
51
77
|
Returns:
|
|
52
|
-
pandas.Series: Matching row from DataFrame
|
|
78
|
+
pandas.Series: Matching row from DataFrame containing the quadkey and download URL,
|
|
79
|
+
or None if no matching tile is found
|
|
80
|
+
|
|
81
|
+
Note:
|
|
82
|
+
The function handles invalid quadkeys gracefully by skipping them and
|
|
83
|
+
continues searching through all available tiles.
|
|
53
84
|
"""
|
|
54
85
|
for index, row in df.iterrows():
|
|
55
86
|
quadkey = str(row['QuadKey'])
|
|
@@ -71,12 +102,27 @@ def find_row_for_location(df, lon, lat):
|
|
|
71
102
|
def get_mbfp_gdf(output_dir, rectangle_vertices):
|
|
72
103
|
"""Download and process building footprint data for a rectangular region.
|
|
73
104
|
|
|
105
|
+
This function takes a list of coordinates defining a rectangular region and:
|
|
106
|
+
1. Downloads the necessary building footprint data files covering the region
|
|
107
|
+
2. Loads and combines the GeoJSON data from all relevant files
|
|
108
|
+
3. Processes the data to ensure consistent coordinate ordering
|
|
109
|
+
4. Assigns unique sequential IDs to each building
|
|
110
|
+
|
|
74
111
|
Args:
|
|
75
|
-
output_dir: Directory
|
|
76
|
-
rectangle_vertices: List of (lon, lat) tuples defining the rectangle corners
|
|
112
|
+
output_dir (str): Directory path where downloaded files will be saved
|
|
113
|
+
rectangle_vertices (list): List of (lon, lat) tuples defining the rectangle corners.
|
|
114
|
+
The coordinates should define a bounding box of the
|
|
115
|
+
area of interest.
|
|
77
116
|
|
|
78
117
|
Returns:
|
|
79
|
-
|
|
118
|
+
geopandas.GeoDataFrame: GeoDataFrame containing building footprints with columns:
|
|
119
|
+
- geometry: Building polygon geometries
|
|
120
|
+
- id: Sequential unique identifier for each building
|
|
121
|
+
|
|
122
|
+
Note:
|
|
123
|
+
- Files are downloaded only if not already present in the output directory
|
|
124
|
+
- Coordinates in the input vertices should be in (longitude, latitude) order
|
|
125
|
+
- The function handles cases where some vertices might not have available data
|
|
80
126
|
"""
|
|
81
127
|
print("Downloading geojson files")
|
|
82
128
|
df_links = get_geojson_links(output_dir)
|
voxcity/downloader/oemj.py
CHANGED
|
@@ -5,6 +5,17 @@ This module provides functionality to download, compose, crop and save satellite
|
|
|
5
5
|
from OpenEarthMap Japan as georeferenced GeoTIFF files. It handles coordinate conversions between
|
|
6
6
|
latitude/longitude and tile coordinates, downloads tiles within a polygon region, and saves the
|
|
7
7
|
final image with proper geospatial metadata.
|
|
8
|
+
|
|
9
|
+
Key Features:
|
|
10
|
+
- Convert between geographic (lat/lon) and tile coordinates
|
|
11
|
+
- Download satellite imagery tiles for a specified region
|
|
12
|
+
- Compose multiple tiles into a single image
|
|
13
|
+
- Crop images to a specified polygon boundary
|
|
14
|
+
- Save results as georeferenced GeoTIFF files
|
|
15
|
+
|
|
16
|
+
Example Usage:
|
|
17
|
+
polygon = [(139.7, 35.6), (139.8, 35.6), (139.8, 35.7), (139.7, 35.7)] # Tokyo area
|
|
18
|
+
save_oemj_as_geotiff(polygon, "tokyo_satellite.tiff", zoom=16)
|
|
8
19
|
"""
|
|
9
20
|
|
|
10
21
|
import requests
|
|
@@ -16,15 +27,22 @@ from osgeo import gdal, osr
|
|
|
16
27
|
import pyproj
|
|
17
28
|
|
|
18
29
|
def deg2num(lon_deg, lat_deg, zoom):
|
|
19
|
-
"""Convert longitude/latitude coordinates to tile coordinates.
|
|
30
|
+
"""Convert longitude/latitude coordinates to tile coordinates using Web Mercator projection.
|
|
31
|
+
|
|
32
|
+
The function converts geographic coordinates to tile coordinates using the standard
|
|
33
|
+
Web Mercator tiling scheme (XYZ). The resulting coordinates can be used to identify
|
|
34
|
+
and download specific map tiles.
|
|
20
35
|
|
|
21
36
|
Args:
|
|
22
|
-
lon_deg (float): Longitude in degrees
|
|
23
|
-
lat_deg (float): Latitude in degrees
|
|
24
|
-
zoom (int): Zoom level
|
|
37
|
+
lon_deg (float): Longitude in degrees (-180 to 180)
|
|
38
|
+
lat_deg (float): Latitude in degrees (-90 to 90)
|
|
39
|
+
zoom (int): Zoom level (0-20, where 0 is most zoomed out)
|
|
25
40
|
|
|
26
41
|
Returns:
|
|
27
|
-
tuple: (x, y) tile coordinates
|
|
42
|
+
tuple: (x, y) tile coordinates as floats
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> x, y = deg2num(139.7, 35.6, 16) # Tokyo coordinates
|
|
28
46
|
"""
|
|
29
47
|
lat_rad = math.radians(lat_deg)
|
|
30
48
|
n = 2.0 ** zoom
|
|
@@ -33,15 +51,21 @@ def deg2num(lon_deg, lat_deg, zoom):
|
|
|
33
51
|
return (xtile, ytile)
|
|
34
52
|
|
|
35
53
|
def num2deg(xtile, ytile, zoom):
|
|
36
|
-
"""Convert tile coordinates to longitude/latitude coordinates.
|
|
54
|
+
"""Convert tile coordinates back to longitude/latitude coordinates.
|
|
55
|
+
|
|
56
|
+
This is the inverse operation of deg2num(). It converts tile coordinates
|
|
57
|
+
back to geographic coordinates using the Web Mercator projection.
|
|
37
58
|
|
|
38
59
|
Args:
|
|
39
60
|
xtile (float): X tile coordinate
|
|
40
61
|
ytile (float): Y tile coordinate
|
|
41
|
-
zoom (int): Zoom level
|
|
62
|
+
zoom (int): Zoom level (0-20)
|
|
42
63
|
|
|
43
64
|
Returns:
|
|
44
65
|
tuple: (longitude, latitude) in degrees
|
|
66
|
+
|
|
67
|
+
Example:
|
|
68
|
+
>>> lon, lat = num2deg(29326, 13249, 15) # Sample tile coordinates
|
|
45
69
|
"""
|
|
46
70
|
n = 2.0 ** zoom
|
|
47
71
|
lon_deg = xtile / n * 360.0 - 180.0
|
|
@@ -52,12 +76,24 @@ def num2deg(xtile, ytile, zoom):
|
|
|
52
76
|
def download_tiles(polygon, zoom):
|
|
53
77
|
"""Download satellite imagery tiles covering a polygon region.
|
|
54
78
|
|
|
79
|
+
Downloads all tiles that intersect with the given polygon at the specified zoom level
|
|
80
|
+
from the OpenEarthMap Japan server. The function calculates the minimum bounding box
|
|
81
|
+
that contains the polygon and downloads all tiles within that box.
|
|
82
|
+
|
|
55
83
|
Args:
|
|
56
|
-
polygon (list): List of (lon, lat)
|
|
57
|
-
|
|
84
|
+
polygon (list): List of (lon, lat) tuples defining the region vertices in clockwise
|
|
85
|
+
or counterclockwise order
|
|
86
|
+
zoom (int): Zoom level for tile detail (recommended range: 14-18)
|
|
58
87
|
|
|
59
88
|
Returns:
|
|
60
|
-
tuple: (
|
|
89
|
+
tuple: (
|
|
90
|
+
tiles: dict mapping (x,y) tile coordinates to PIL Image objects,
|
|
91
|
+
bounds: tuple of (min_x, min_y, max_x, max_y) tile coordinates
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
Note:
|
|
95
|
+
Higher zoom levels provide more detail but require downloading more tiles.
|
|
96
|
+
The function will print progress messages during download.
|
|
61
97
|
"""
|
|
62
98
|
print(f"Downloading tiles")
|
|
63
99
|
|
|
@@ -85,14 +121,23 @@ def download_tiles(polygon, zoom):
|
|
|
85
121
|
return tiles, (min(min_x, max_x), min(min_y, max_y), max(min_x, max_x), max(min_y, max_y))
|
|
86
122
|
|
|
87
123
|
def compose_image(tiles, bounds):
|
|
88
|
-
"""Compose downloaded tiles into a single image.
|
|
124
|
+
"""Compose downloaded tiles into a single continuous image.
|
|
125
|
+
|
|
126
|
+
Takes individual map tiles and combines them into a single large image based on
|
|
127
|
+
their relative positions. The tiles are placed according to their x,y coordinates
|
|
128
|
+
within the bounds.
|
|
89
129
|
|
|
90
130
|
Args:
|
|
91
131
|
tiles (dict): Mapping of (x,y) coordinates to tile Image objects
|
|
92
|
-
bounds (tuple): (min_x, min_y, max_x, max_y) tile bounds
|
|
132
|
+
bounds (tuple): (min_x, min_y, max_x, max_y) tile coordinate bounds
|
|
93
133
|
|
|
94
134
|
Returns:
|
|
95
|
-
Image: Composed PIL Image
|
|
135
|
+
Image: Composed PIL Image with dimensions (width x height) where:
|
|
136
|
+
width = (max_x - min_x + 1) * 256
|
|
137
|
+
height = (max_y - min_y + 1) * 256
|
|
138
|
+
|
|
139
|
+
Note:
|
|
140
|
+
Each tile is assumed to be 256x256 pixels, which is standard for web maps.
|
|
96
141
|
"""
|
|
97
142
|
min_x, min_y, max_x, max_y = bounds
|
|
98
143
|
width = abs(max_x - min_x + 1) * 256
|
|
@@ -104,16 +149,26 @@ def compose_image(tiles, bounds):
|
|
|
104
149
|
return result
|
|
105
150
|
|
|
106
151
|
def crop_image(image, polygon, bounds, zoom):
|
|
107
|
-
"""Crop composed image to polygon boundary.
|
|
152
|
+
"""Crop composed image to the exact polygon boundary.
|
|
153
|
+
|
|
154
|
+
Creates a mask from the polygon coordinates and uses it to crop the image,
|
|
155
|
+
removing areas outside the polygon of interest. The polygon coordinates are
|
|
156
|
+
converted from geographic coordinates to pixel coordinates in the image space.
|
|
108
157
|
|
|
109
158
|
Args:
|
|
110
159
|
image (Image): PIL Image to crop
|
|
111
|
-
polygon (list): List of (lon, lat) coordinates
|
|
160
|
+
polygon (list): List of (lon, lat) coordinates defining the boundary
|
|
112
161
|
bounds (tuple): (min_x, min_y, max_x, max_y) tile bounds
|
|
113
|
-
zoom (int): Zoom level
|
|
162
|
+
zoom (int): Zoom level used for coordinate conversion
|
|
114
163
|
|
|
115
164
|
Returns:
|
|
116
|
-
tuple: (
|
|
165
|
+
tuple: (
|
|
166
|
+
cropped Image: PIL Image cropped to polygon boundary,
|
|
167
|
+
bbox: tuple of (left, upper, right, lower) pixel coordinates of bounding box
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
ValueError: If the polygon does not intersect with the downloaded tiles
|
|
117
172
|
"""
|
|
118
173
|
min_x, min_y, max_x, max_y = bounds
|
|
119
174
|
img_width, img_height = image.size
|
|
@@ -139,15 +194,23 @@ def crop_image(image, polygon, bounds, zoom):
|
|
|
139
194
|
return cropped.crop(bbox), bbox
|
|
140
195
|
|
|
141
196
|
def save_as_geotiff(image, polygon, zoom, bbox, bounds, output_path):
|
|
142
|
-
"""Save cropped image as georeferenced GeoTIFF.
|
|
197
|
+
"""Save cropped image as a georeferenced GeoTIFF file.
|
|
198
|
+
|
|
199
|
+
Converts the image to a GeoTIFF format with proper spatial reference information
|
|
200
|
+
using the Web Mercator projection (EPSG:3857). The function handles coordinate
|
|
201
|
+
transformation and sets up the necessary geospatial metadata.
|
|
143
202
|
|
|
144
203
|
Args:
|
|
145
204
|
image (Image): PIL Image to save
|
|
146
205
|
polygon (list): List of (lon, lat) coordinates
|
|
147
|
-
zoom (int): Zoom level
|
|
148
|
-
bbox (tuple): Bounding box of cropped image
|
|
206
|
+
zoom (int): Zoom level used for coordinate calculations
|
|
207
|
+
bbox (tuple): Bounding box of cropped image in pixels (left, upper, right, lower)
|
|
149
208
|
bounds (tuple): (min_x, min_y, max_x, max_y) tile bounds
|
|
150
|
-
output_path (str): Path
|
|
209
|
+
output_path (str): Path where the GeoTIFF will be saved
|
|
210
|
+
|
|
211
|
+
Note:
|
|
212
|
+
The output GeoTIFF will have 3 bands (RGB) and use the Web Mercator
|
|
213
|
+
projection (EPSG:3857) for compatibility with most GIS software.
|
|
151
214
|
"""
|
|
152
215
|
min_x, min_y, max_x, max_y = bounds
|
|
153
216
|
|
|
@@ -187,12 +250,35 @@ def save_as_geotiff(image, polygon, zoom, bbox, bounds, output_path):
|
|
|
187
250
|
dataset = None
|
|
188
251
|
|
|
189
252
|
def save_oemj_as_geotiff(polygon, filepath, zoom=16):
|
|
190
|
-
"""Download and save OpenEarthMap Japan imagery as GeoTIFF.
|
|
253
|
+
"""Download and save OpenEarthMap Japan imagery as a georeferenced GeoTIFF file.
|
|
254
|
+
|
|
255
|
+
This is the main function that orchestrates the entire process of downloading,
|
|
256
|
+
processing, and saving satellite imagery for a specified region.
|
|
191
257
|
|
|
192
258
|
Args:
|
|
193
|
-
polygon (list): List of (lon, lat) coordinates defining region
|
|
194
|
-
|
|
259
|
+
polygon (list): List of (lon, lat) coordinates defining the region to download.
|
|
260
|
+
Must be in clockwise or counterclockwise order.
|
|
261
|
+
filepath (str): Output path for the GeoTIFF file
|
|
195
262
|
zoom (int, optional): Zoom level for detail. Defaults to 16.
|
|
263
|
+
- 14: ~9.5m/pixel
|
|
264
|
+
- 15: ~4.8m/pixel
|
|
265
|
+
- 16: ~2.4m/pixel
|
|
266
|
+
- 17: ~1.2m/pixel
|
|
267
|
+
- 18: ~0.6m/pixel
|
|
268
|
+
|
|
269
|
+
Example:
|
|
270
|
+
>>> polygon = [
|
|
271
|
+
(139.7, 35.6), # Bottom-left
|
|
272
|
+
(139.8, 35.6), # Bottom-right
|
|
273
|
+
(139.8, 35.7), # Top-right
|
|
274
|
+
(139.7, 35.7) # Top-left
|
|
275
|
+
]
|
|
276
|
+
>>> save_oemj_as_geotiff(polygon, "tokyo_area.tiff", zoom=16)
|
|
277
|
+
|
|
278
|
+
Note:
|
|
279
|
+
- Higher zoom levels provide better resolution but require more storage
|
|
280
|
+
- The polygon should be relatively small to avoid memory issues
|
|
281
|
+
- The output GeoTIFF will be in Web Mercator projection (EPSG:3857)
|
|
196
282
|
"""
|
|
197
283
|
try:
|
|
198
284
|
tiles, bounds = download_tiles(polygon, zoom)
|