Prequesites
I have vector data in a Shapefile N47E007_CONTOURS.shp
produced with
wget -O N47E007.zip http://e4ftl01.cr.usgs.gov/SRTM/SRTMGL1.003/2000.02.11/N47E007.SRTMGL1.hgt.zip
unzip N47E007.zip
# Convert from WGS 84 to Web Mercator and from HGT to GeoTIFF
gdalwarp -s_srs EPSG:4326 N47E007.hgt \
-t_srs EPSG:3785 N47E007_WebMercator.tif \
-co "COMPRESS=LZW" -of GTiff -r bilinear
# Compute contour vector data
gdal_contour -a elev -i 20 N47E007_WebMercator.tif N47E007_CONTOURS.shp
(Contour lines based on one patch (N47E007
) of NASA SRTM elevation data.)
Process
From this, I generate TMS/WMTS tiles to be used by leaflet, by first rasterizing the vector data from the Shapefile with gdal_rasterize
and then tiling it with gdal2tiles.py
. I repeat both steps for every zoomlevel, so I can rasterize to a larger raster for higher zoomlevels, so that lines will have the same on-screen thickness on each one:
#!/bin/sh
for z in $(seq 1 15)
do
gdal_rasterize \
--config GDAL_CACHEMAX 1024 \
-ts $(calc "2851 << (${z} - 12)") \
$(calc "4220 << (${z} - 12)") \
-ot byte -of GTiff -co COMPRESS=DEFLATE -co PREDICTOR=2 -co ZLEVEL=9 \
-ot Byte -burn 0 -a_nodata 255 \
-l N47E007_CONTOURS N47E007_CONTOURS.shp N47E007_CONTOURS_rasterized_z${z}.tif
gdal2tiles.py \
-r near -z${z} -a 255,255,255 \
-e N47E007_CONTOURS_rasterized_z${z}.tif \
N47E007_contours_tiled
done
Adapting resolution to zoomelevel
(See also my answer to Problem with contour lines thickness in bigger zoom levels.)
calc "2851 << (${z} - 12)"
and calc "4220 << (${z} - 12)"
compute the size of the image to rasterize to (N47E007_CONTOURS_rasterized_z${z}.tif
) depending on the zoomelevel $z
.
<<
is the binary left-shift operator, so this translates to 2851⋅2 z-12 and 4220⋅2 z-12. This means that
- for zoomlevel 12, the intermediate raster image
N47E007_CONTOURS_rasterized_z12.tif
will be 2851×4220 pixels, - for zoom level 13 (
N47E007_CONTOURS_rasterized_z13.tif
) it will be
twice that in each direction (5702×8440) and - for zoom level 11 (
N47E007_CONTOURS_rasterized_z11.tif
) half that
of zoomlevel 12, i.e. 1425×2110.
Imprecise parametrization
The numbers 2851
and 4220
(the size for zoomelevel 12) were guessed or measured on screen or otherwise derived in an imprecise manner.
Problem
The resulting tiles show some artefacts that aren't visible in the intermediate (untiled) rasterized images:
-
double-width segments of lines,
e.g.13/4270/5331.png
vs. corresponding excerpt ofN47E007_CONTOURS_rasterized_z13.tif
-
gaps in lines,
e.g. at tile edges between14/8536/10665.png
14/8537/10665.png
14/8536/10664.png
14/8537/10664.png
corresponding excerpt from
N47E007_CONTOURS_rasterized_z14.tif
for comparison:
If I skip -r near
(so rasterization uses the default resampling mode 'average
') I instead get some washed-out pixels which make the lines look unsharp.
My theory
The coordinate reference systems already match (due to the gdalwarp
step before calculating the contours), so no general transformation/re-projection should be required. But because the size to rasterize to only approximates the on-screen size of the geographic area covered by the N47E007
data patch, some rescaling must still happen in gdal2tiles.py
, to produce correctly placed and sized seamless tiles.
With -r near
this causes some kind of macroscopic Moiré effect, because the pixel grids of the tiles and of the intermediate raster only almost match. (Additionally, gdal2tiles.py
might not look accross tile boarders when looking for the nearest pixel in the source image, causing the gaps.) With average
(the default for -r
), the same causes effect the washed-out look because values from more than one source pixel get 'mixed' to get the value for a given tile pixel.
Thus, if I knew the correct (zoom-level-dependant) on-screen-size of the geographic area of the data patch and would use that for rasterization, the artifacts should go away.
Questions
- Is my theory correct?
- If so, how can I determine the precise
-ts
parameters forgdal_rasterize
(or, alternatively,-tr
parameters) so that no rescaling (or a reschaling without effect) will be done bygdal2tiles.py
?
Best Answer
It seems so, as I get flawless results with the method described below.
While
gdal2tiles.py
is meant to be used as a command, we can load it as a python module and use the very same functions it uses itself for the size computations. Thus, I've made me this little python script:I then use the WGS 84 coordinates from
gdalinfo N47E007.hgt
(exctracting them with the regular expressions/^\s*Upper Right\s*\(\s*(?<east>\d+\.?\d*),\s*(?<north>\d+\.?\d*)\)
and/^\s*Lower Left\s*\(\s*(?<west>\d+\.?\d*),\s*(?<south>\d+\.?\d*)\)/
) to calllat_lon_to_pixels
from the python script above for the north-east corner and the south-west corner and compute the differences, which I then use as argument for the-ts
option ofgdal_rasterize
. (This only works because this HGT file has WGS 84 as coordinate reference system and WGS 84 and WebMercator are axis-aligned to each other.)