h3-landcover
April 21, 2023 ยท View on GitHub
Make low resolution landcover vector tiles from high resolution raster landcover data with h3 downsampling
Demo
https://wipfli.github.io/h3-landcover/
Design
The following design descriptions for the individual steps were given to ChatGPT which then produced the python scripts. I had to tweek some of the python scripts by hand afterwards, but the majority of the code content was written automatically by ChatGPT.
Download
Download raster landcover data from https://lcviewer.vito.be/download. The data is split into 20 by 20 degree tiles. Use 2019 version, 100 m resolution.
Attribution landcover data: https://lcviewer.vito.be/about
The download URLs look like:
https://s3-eu-west-1.amazonaws.com/vito.landcover.global/v3.0.1/2019/E000N60/E000N60_PROBAV_LC100_global_v3.0.1_2019-nrt_Bare-CoverFraction-layer_EPSG-4326.tif
template:
https://s3-eu-west-1.amazonaws.com/vito.landcover.global/v3.0.1/2019/{longitude}{latitude}/{longitude}{latitude}_PROBAV_LC100_global_v3.0.1_2019-nrt_{kind}-CoverFraction-layer_EPSG-4326.tif
latitudes: S40, S20, N00, N20, N40, N60, N80 longitudes: W180, W160, W140, W120, W100, W080, W060, W040, W020, E000, E020, E040, E060, E080, E100, E120, E140, E160 kinds: Snow, Bare, Grass, Crops, Tree, BuiltUp
Download all files into a sources folder.
Write a python script which loops through the filenames and downloads them to the sources folder with system calls using the cli tool axel which has parallel downloads with the -a -n 10 option.
The total landcover raster data is 26 GB in 564 files.
Raster to H3 Classification
For every image in the sources folder:
- Open the image
- Loop through the pixels
- The pixel value is landcover in percent, the pixel size is 100 m by 100 m
- Call
ReadAsArrayonly once per image - Make sure to ignore nodata pixels
- Print fractional progress after every 10000th pixel
- Multiply pixel area by pixel value divided by 100 to the the covered area of the pixel
- Hold a hash map H3 index to covered area
- Add the area to the H3 cell in the hash map in which the pixel is located, use H3 resolution 9
- Save the hashmap as a json file. Save it at
classified/{original_filename}.jsonand create the folder if needed. - Use the osgeo gdal library
- Use
h3.api.numpy_intotherwise it is too slow
Write a python script for this.
Second promt: Using the above script, write a version which runs on a 12 core CPU. The images are independent and can be treated in parallel.
Combine Grass, Bare, Snow
The kinds Grass, Bare, Snow are plotted on top of eachother. In mountains, we first have Grass landcover, then with higher altitude, Bare landcover, and above this we have Snow. To not have any gaps between Snow and Bare landcover, lets combine the following layers:
- Grass-Bare-Snow
- Bare-Snow
- Snow
The Snow layer we have already. Bare-Snow, and Grass-Bare-Snow have to be generated.
The folder classified contains files with template filename {longitude}{latitude}_PROBAV_LC100_global_v3.0.1_2019-nrt_{kind}-CoverFraction-layer_EPSG-4326.json.
With
latitudes: S40, S20, N00, N20, N40, N60, N80 longitudes: W180, W160, W140, W120, W100, W080, W060, W040, W020, E000, E020, E040, E060, E080, E100, E120, E140, E160
The file content has the form
{
"1453143554": 4636.3,
"1453459834": 634.0
}
i.e. dictionary with key H3 cell id, and value covered area of cell in m2.
Loop through all latitude, longitude combinations. If the file exists, loop through the kinds [Bare, Snow] and [Grass, Bare, Snow]. Create a new dictionnary, loop through the cells of the input dictionary and add them to the cells of the new output dictionarry.
Store the resulting H3 map in a file called classified/{longitude}{latitude}_PROBAV_LC100_global_v3.0.1_2019-nrt_{new_kind}-CoverFraction-layer_EPSG-4326.json where new_kind is Bare-Snow or Grass-Bare-Snow.
Size of classified folder: 271 GB
H3 to GeoJSON
The folder classified contains files with json H3 maps. The file contents are of the form
{
"1453143554": 4636.3,
"1453459834": 634.0
}
i.e. H3 index as key and covered area in m2 as value. The files are in H3 resolution 9. For every file, do the following:
- Read the original file contents to
input_resolution_mapvariable. - Loop through the following target resolutions 9, 8, 7, 6, 5, 4
- Downsample if the target resolution is not 9:
- Create an empty
target_resolution_map. - Loop through the entries of the
input_resolution_map - For every input H3 index at resolution 9, find the parent H3 index at the target resolution
- Add the
input_resolution_mapvalue to thetarget_resolution_mapat the parent H3 index
- Create an empty
- If the target resolution is 9, use the the input_resolution_map as the target_resolution map
- Thresholding: For every cell in the
target_resolution_map, decide if more than 50 percent of the cell area is covered. If a cell is covered, add its H3 id to a list calledactivated. - Convert to GeoJSON: Convert the
activatedH3 cell list to a GeoJSON multipolygon. - Store the multipolygon at the following template filename
geojson/{original_filename}-resolution-{target_resolution}.geojson.
Size geojson folder: 49 GB
Arctic and Antarctic Polygons
The Arctic and Antarctic are outside of the coverage region of the landcover data. Create polygons for these regions separately which show that they are Snow.
Steps:
- Create a geojson polygons with the bounds South 86 deg to 59 deg, E 180 deg to W 180 deg
- For the resolutions 9, 8, 7, 6, 5, 4, save this polygon in the following location
geojson/ANTARCTIC_PROBAV_LC100_global_v3.0.1_2019-nrt_Snow-CoverFraction-layer_EPSG-4326.json-resolution-{resolution}.geojson - Create a geojson polygons with the bounds North 77 deg to 86 deg, E 180 deg to W 180 deg
- For the resolutions 9, 8, 7, 6, 5, 4, save this polygon in the following location
geojson/ARCTIC_PROBAV_LC100_global_v3.0.1_2019-nrt_Snow-CoverFraction-layer_EPSG-4326.json-resolution-{resolution}.geojson
Write a python script for this without using a GeoJSON library. Just write json directly.
GeoPackage Zip Files
To consume the geometries in Planetiler, we need to transform the GeoJSON files to GeoPackage files. Files of the same kind should be bundled in a zip file.
Steps are:
- Create the
zipsfolder if needed - Loop through the kinds Snow, Bare-Snow, Grass-Bare-Snow, Crops, Tree, BuiltUp
- Loop through the resolutions 9, 8, 7, 6, 5, 4
- For all files in the
geojsonfolder which match the pattern*_PROBAV_LC100_global_v3.0.1_2019-nrt_{kind}-CoverFraction-layer_EPSG-4326.json-resolution-{resolution}.geojsondo:- Convert the file from GeoJSON to GPKG
- Add the file to a zip folder
- Save the Zip folder at
zips/{kind}-resolution-{resolution}.zip
Write a python script to do this. You can assume that the ogr2ogr utility is installed.
Size zips folder: 13 GB
Planetiler
Run planetiler with
cd planetiler
./regenerate-swissmap.sh
Download
https://pub-726b01260c98468a9387cc0dfcb7386b.r2.dev/h3-landcover.pmtiles (282 MB)