voxcity 0.5.14__py3-none-any.whl → 0.5.16__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/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
- source: DEM source ('NASA', 'COPERNICUS', 'DeltaDTM', 'FABDEM', 'England 1m DTM',
102
- 'DEM France 5m', 'DEM France 1m', 'AUSTRALIA 5M DEM', 'USGS 3DEP 1m')
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 ('England' or 'Netherlands')
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
- Returns:
412
- ee.Image: Image representing DSM minus DTM (building/vegetation heights)
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()
@@ -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 to save the downloaded CSV file
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 and quadkey information
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, or None if no match found
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 to save downloaded files
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
- dict: GeoJSON data containing building footprints
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)
@@ -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) coordinates defining the region
57
- zoom (int): Zoom level for tile detail
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: (tiles dict mapping (x,y) to Image objects, bounds 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: (cropped Image, bounding box)
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 to save GeoTIFF
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
- filepath (str): Output path for GeoTIFF
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)