Contour relief maps using mapnik
In February 2008 contours were implemented on the cycle map (a Mapnik rendered Slippy Map). A similar approach was then used for OpenPisteMap, which was then copied by the cycle map in turn.
In both cases, a "toolchain" was set up, such that ongoing OSM data improvements are re-rendered along with the contour graphics at regular intervals. Rendering happens for the entire set of tile images (at several zoom levels), which makes it quite an intensive process in terms of CPU/memory and storage. This page describes the toolchain, and various other considerations.
Toolchain
In summary
- Download SRTM data from NASA
- Prepare for gdal_contour
- Run gdal_contour to create contour lines
- Store the contours in either shapefiles or postgis
- Add layer definitions for mapnik
Downloading data from NASA
We're interested in srtm v2 SRTM3 from [1]. Version 1 of the srtm data had contours over the oceans - version 2 is clipped to land regions. SRTM3 covers the whole world between 60S and 60N - SRTM1 is higher resolution for the US but we're sticking with SRTM3 even in North America.
The data comes in zip files covering 1degree x 1degree sections of the planet at 1201x1201 pixels of spot-height data. So download each .hgt.zip file you are interested in. At the time of writing we had 248 zip files.
wget http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Eurasia/N54W001.hgt.zip etc...
The openstreetmap-carto approach
This is approach adds contours to the standard OpenStreetMap mapnik-rendered view using CartoCSS, as defined in the openstreetmap-carto repository. The commands assume that you are using the Docker-based setup as described in the openstreetmap-carto DOCKER.md instructions. It assumes a Debian/Ubuntu Linux machine for preparing the contour files.
Prerequisites
The Python 'elevation' library is used to download SRTM data, GDAL/OGR is used to create contours and shp2pgsql (provided by postgis) is used to load them into the database.
sudo apt install python-pip gdal-bin postgis sudo pip install elevation
Generate contours
The following steps download a DEM (30 m pixels) from the Shuttle Radar Topography Mission (SRTM) project and calculate contours.
# Download data eio clip -o srtm_30m.tif --bounds -61.52 12.55 -61.01 13.50 # Calculate 10 m contours gdal_contour -i 10 -a height srtm_30m.tif contour.shp
Load contour data to database
Ensure that the database is running and that you can connect to it. You can test connection with:
psql -h <hostname> -U postgres -d gis -c 'SELECT 1;'
The data are loaded via shp2pgsql by running the following commands within the shapefile directory:
cd srtm_30m_contours_10m shp2pgsql -p -I -g way -s 4326:3857 contour.shp contour | psql -h localhost -p 5432 -U postgres -d gis shp2pgsql -a -g way -s 4326:3857 contour.shp contour | psql -h localhost -p 5432 -U postgres -d gis
This creates a table called contours in the public schema in the Web Mercator projection.
Update the CSS files
Create a file in the openstreetmap-carto directory named contours.mss with the following content:
@contour: lighten(brown, 30);
@contours-text: lighten(brown, 30);
@contours-line-width: 0.5;
@contours-line-smooth: 0.9; // A value from 0 to 1
@contours-medium-multiplier: 1.5;
@contours-major-multiplier: 2.0;
#contours10[zoom>=16] {
line-color: lighten(@contour, 10);
line-smooth: @contours-line-smooth;
line-width: @contours-line-width;
line-opacity: 0.4;
}
#contours50[zoom>=14] {
line-color: lighten(@contour, 10);
line-smooth: @contours-line-smooth;
line-width: @contours-line-width * @contours-medium-multiplier;
line-opacity: 0.5;
}
#contours200[zoom>=12] {
line-color: @contour;
line-smooth: @contours-line-smooth;
line-width: @contours-line-width * @contours-major-multiplier;
line-opacity: 0.5;
}
#contours-text50 {
text-name: "[height]";
text-face-name: @book-fonts;
text-placement: line;
text-fill: @contours-text;
[zoom >= 16][zoom < 20] {
text-spacing: 1000;
text-size: 10;
}
}
#contours-text200 {
text-name: "[height]";
text-face-name: @book-fonts;
text-placement: line;
text-fill: @contours-text;
text-halo-fill: @standard-halo-fill;
text-halo-radius: @standard-halo-radius;
[zoom >= 12][zoom < 14] {
text-spacing: 6000;
text-size: 8;
}
[zoom >= 14][zoom < 20] {
text-spacing: 1000;
text-size: 10;
}
}
Update the Stylesheet section of project.mml by inserting the following:
- contours.mss
Update the Layer section of the project.mml file by inserting the following, just before the landcover-line section:
- id: contours10
geometry: multilinestring
<<: *extents
Datasource:
<<: *osm2pgsql
table: |-
(SELECT
way
FROM contour
WHERE height::int % 50 != 0 AND height::int % 200 != 0
) AS contours10
properties:
minzoom: 16
- id: contours50
geometry: multilinestring
<<: *extents
Datasource:
<<: *osm2pgsql
table: |-
(SELECT
way
FROM contour
WHERE height::int % 50 = 0 AND height::int % 200 != 0
) AS contours50
properties:
minzoom: 14
- id: contours200
geometry: multilinestring
<<: *extents
Datasource:
<<: *osm2pgsql
table: |-
(SELECT
way
FROM contour
WHERE height::int % 200 = 0
) AS contours200
properties:
minzoom: 12
- id: contours-text50
geometry: linestring
<<: *extents
Datasource:
<<: *osm2pgsql
table: |-
(SELECT
way, height
FROM contour
WHERE height::int % 50 = 0 AND height::int % 200 != 0
) AS contours_text50
properties:
minzoom: 16
- id: contours-text200
geometry: linestring
<<: *extents
Datasource:
<<: *osm2pgsql
table: |-
(SELECT
way, height
FROM contour
WHERE height::int % 200 = 0
) AS contours_text200
properties:
minzoom: 12
Start kosmtik to view
As per the openstreetmap-carto DOCKER.md instructions, kosmtik can be started as follows:
docker-compose up -d kosmtik
Kosmtik should be visible by browsing to http://localhost:6789, allowing you to inspect the contours.
The shapefiles approach
There are two approaches in use to render contours onto the map. The cycle map orignially stored the contours as shapefiles, as described here, but has since moved to storing the contours in the postgis database alongside the main osm data.
Preparing for gdal_contour (unnecessary with recent gdal)
Old versions of gdal can't read the .hgt files directly. In this case, you can convert the .hgt files into .tiff files using the srtm_generate_hdr.sh script.
Use gdal_contour to generate shapefiles
For the cycle map, we want to render the 10m, 50m and 100m contours separately, so for every .zip file we'll generate 3 shapefiles. The gdal_contour command is along the lines of:
gdal_contour -i 10 -snodata 32767 -a height N54W001.tif N54W001c10.shp
-i sets the contour interval. -snodata is needed to override the void detection, otherwise it thinks the voids are actually 32km high spikes (so you get very dense circular contours around the voids). -a height means that the height of each line will end up in the shapefile, otherwise it'll just be a collection of lines with no actual data.
The previous two steps can easily be scripted, as per process.sh (Note: srtm_generate_hdr.sh is not needed with newer Gdal, the script should be adapted):
#!/bin/bash cd /home/osm/shape-resources/srtm/ for X in *.hgt.zip do yes | /home/osm/andy/srtm/srtm_generate_hdr.sh $X done rm *.hgt #remove unzipped .hgt files, not needed rm *.shp #remove old shapefiles so that gdal_contour doesn't barf rm *.shx rm *.dbf for X in /home/osm/shape-resources/srtm/*.tif do echo $X gdal_contour -i 10 -snodata 32767 -a height $X ${X%%.tif}c10.shp gdal_contour -i 50 -snodata 32767 -a height $X ${X%%.tif}c50.shp gdal_contour -i 100 -snodata 32767 -a height $X ${X%%.tif}c100.shp done
Index the shapefiles
The shapefiles can be given a spatial index, which significantly speeds up the rendering (since mapnik can check the index and ignore shapefiles that are outwith the bounds of the particular tile it's rendering. If you've got mapnik installed, you'll have the shapeindex utily, and you'll be able to
index.sh:
#!/bin/bash for X in /home/osm/shape-resources/srtm/*.shp do shapeindex ${X%%.shp} done
Generate the mapnik layers
You'll need a layer definition for every shapefile. Given that we're going to have different styles for each set of contours (10, 50, 100m) we need to correlate them. So the two styles (for lines and text) are suffixed by the set number. The y iterator simply makes sure the layers have unique names.
generatexml.py:
#!/usr/bin/python import glob f = open('../osm-cycle/contour-layers-c100.include', 'w') y = 0 for X in glob.glob('/home/osm/shape-resources/srtm/*c100.shp'): f.write("<Layer name=\"srtm100"+`y`+"\" status=\"on\" srs=\"+proj=latlong +datum=WGS84\">\n") f.write(" <StyleName>contours100</StyleName>\n") f.write(" <StyleName>contours-text100</StyleName>\n") f.write(" <Datasource>\n") f.write(" <Parameter name=\"type\"><![CDATA[shape]]></Parameter>\n") f.write(' <Parameter name="file"><![CDATA[' + X + "]]></Parameter>\n") f.write(" </Datasource>\n") f.write("</Layer>\n") y = y + 1 f.close f = open('../osm-cycle/contour-layers-c50.include', 'w') y = 0 for X in glob.glob('/home/osm/shape-resources/srtm/*c50.shp'): f.write("<Layer name=\"srtm50"+`y`+"\" status=\"on\" srs=\"+proj=latlong +datum=WGS84\">\n") f.write(" <StyleName>contours50</StyleName>\n") f.write(" <StyleName>contours-text50</StyleName>\n") f.write(" <Datasource>\n") f.write(" <Parameter name=\"type\"><![CDATA[shape]]></Parameter>\n") f.write(' <Parameter name="file"><![CDATA[' + X + "]]></Parameter>\n") f.write(" </Datasource>\n") f.write("</Layer>\n") y = y + 1 f.close f = open('../osm-cycle/contour-layers-c10.include', 'w') y = 0 for X in glob.glob('/home/osm/shape-resources/srtm/*c10.shp'): f.write("<Layer name=\"srtm10"+`y`+"\" status=\"on\" srs=\"+proj=latlong +datum=WGS84\">\n") f.write(" <StyleName>contours10</StyleName>\n") f.write(" <StyleName>contours-text10</StyleName>\n") f.write(" <Datasource>\n") f.write(" <Parameter name=\"type\"><![CDATA[shape]]></Parameter>\n") f.write(' <Parameter name="file"><![CDATA[' + X + "]]></Parameter>\n") f.write(" </Datasource>\n") f.write("</Layer>\n") y = y + 1 f.close
Take the three files and put their content into the osm.xml style file at whichever point you want the contours to show up - usually just after the coastline shapefiles and add styles (necessary).
Styles
<Style name="contours10"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.5" /> </Rule> </Style> <Style name="contours50"> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.6" /> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#747b90" stroke-width="0.6" /> </Rule> </Style> <Style name="contours100"> <Rule> <MaxScaleDenominator>409483</MaxScaleDenominator> <MinScaleDenominator>204741</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.7" /> </Rule> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer stroke="#747b90" stroke-width="0.7" /> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#855d62" stroke-width="0.7" /> </Rule> </Style> <Style name="contours-text10"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer size="8" fill="#9c9" fontset-name="fontset-0" halo-radius="1" wrap-width="14">[ID]</TextSymbolizer> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#747b90" halo-radius="1" placement="line">[height]</TextSymbolizer> </Rule> </Style> <Style name="contours-text50"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer size="8" fill="#9c9" fontset-name="fontset-0" halo-radius="1" wrap-width="14">[ID]</TextSymbolizer> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#747b90" halo-radius="1" placement="line">[height]</TextSymbolizer> </Rule> </Style> <Style name="contours-text100"> <Rule> <MaxScaleDenominator>102370</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#747b90" halo-radius="1" placement="line" >[height]</TextSymbolizer> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#855d62" halo-radius="1" placement="line" >[height]</TextSymbolizer> </Rule> </Style>
See also: Tutorial on thematicmapping.org which goes via shapefile
The PostGIS approach
The alternative approach, developed by OpenPisteMap, is to store the contours data in PostGIS. This only requires one set of layers to be set up for the whole data set, rather than one set per shapefile.
Needs CHECKING!
Importing ASTER_GDEMv2 data
This tutorial deals with ASTER Global Digital Elevation Model V002 data from NASA Reverb|ECHO.
Put the .zip archives containing the DEM data in a single directory, make sure you have the GDAL utilities installed (gdal-bin in Debian/Ubuntu) and of course, prepared a postgresql-postgis databse. (if you have no databse yet, you might want to look at the following mapbox-tutorials: OSM-Bright Ubuntu quickstart )
The author does not take any responsibility for the following script. Use on your own risk!
Run the "import_ASTGTM2.sh" script:
#!/bin/bash ## ======================================================================================== ## ## file: import_ASTGTM2.sh Author: ikcalB ## -------------------------- ## ## import *ASTER_GDEM v2* into postgis databse ## (utilize "man <command>" if you need help) ## ## /- ASTGTM2_N##E###_dem.tif ## data-STRUCTURE: ASTGTM2_N##E###.zip -- ASTGTM2_N##E###_num.tif ## \- README.pdf ## ## (example for Northern hemisphere, *FURTHER information in README.pdf* ## to the east of greenwich) ## ======================================================================================== ## PREP_TABLE="1" # initialize table? TABLE="contours" # table name for insertion NODATA_VAL="-9999" # ASTER_GDEM v2 nodata-vale #+ (according to README.pdf inside each .zip-archive) INTERVAL="10" # METERS (Resolution for contour-lines) COLUMN="height" # column name for height-information GEOMETRY="geometry" # column name for geometry-information OPTIONS=(-q -d osm -U postgres) # postgresql options: quiet, db-name, username for FILE in *.zip do # unzip unzip "$FILE" -x "*_num.tif" "README.pdf" # sanitize filename for further use FILE="${FILE%%.zip}" # import contours gdal_contour -i $INTERVAL -snodata $NODATA_VAL -a $COLUMN "${FILE}_dem.tif" "${FILE}.shp" # prepare database (executed only once) (( PREP_TABLE )) && shp2pgsql -p -I -g $GEOMETRY "${FILE}.shp" $TABLE | psql "${OPTIONS[@]}" PREP_TABLE=0 # append data to table shp2pgsql -a -g $GEOMETRY "${FILE}.shp" $TABLE | psql "${OPTIONS[@]}" # clean up rm "${FILE}_dem.tif" # extracted ASTER_GDEM file rm "${FILE}.shp" # computed shape file rm "${FILE}.dbf" "${FILE}.shx" "${FILE}.prj" # shapefile information done echo echo "DONE" exit 0
This will import all of the contours data for 10m interval contours into the PostGIS-databse osm authenticating as postgres inserting into table contours. (Customization is eased by using variables)
- P.S.: README.pdf found in ASTGTM2-files declares "-9999" value for void-data.
Importing the SRTM3 data (deprecated)
Put the zip files from NASA in a single directory, make sure you have the GDAL utilities installed (gdal-bin in Debian/Ubuntu) and srtm_generate_hdr.sh in your path (srtm_generate_hdr.sh is not needed with newer Gdal; adapted script at #Importing ASTER_GDEMv2 data).
Then run this import script:
#!/bin/bash PREP_TABLE="1" for X in *.hgt.zip; do yes | ./srtm_generate_hdr.sh $X rm -f "${X%%.zip}" # Import 10m contours rm -f "${X%%.hgt.zip}.shp" "${X%%.hgt.zip}.shx" "${X%%.hgt.zip}.dbf" gdal_contour -i 10 -snodata 32767 -a height "${X%%.hgt.zip}.tif" "${X%%.hgt.zip}.shp" [ "$PREP_TABLE" ] && shp2pgsql -p -I -g way "${X%%.hgt.zip}" contours | psql -q gis shp2pgsql -a -g way "${X%%.hgt.zip}" contours | psql -q gis rm -f "${X%%.hgt.zip}.shp" "${X%%.hgt.zip}.shx" "${X%%.hgt.zip}.dbf" rm -f "${X%%.hgt.zip}.bil" rm -f "${X%%.hgt.zip}.hdr" rm -f "${X%%.hgt.zip}.prj" rm -f "${X%%.hgt.zip}.tif" unset PREP_TABLE done
This will import all of the contours data for 10m interval contours into a PostGIS table called contours.
- PS: Maybe I'm wrong, but to take "no values" (gap) into account, the SRTM nodata is -32767, so I replaced "gdal_contour -i 10 -snodata 32767" by "gdal_contour -i 10 -snodata -32767"
- Response: "32767" is correct. NASA says the "no data" value is -32768, but the files appear to actually be using 32767.
Setting up Mapnik
You need to add layers for the contour intervals you are interested in:
<Layer name="srtm_10" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours10</StyleName> <StyleName>contours-text10</StyleName> <Datasource> <Parameter name="type">postgis</Parameter> <Parameter name="host"></Parameter> <Parameter name="port"></Parameter> <Parameter name="user"></Parameter> <Parameter name="password"></Parameter> <Parameter name="dbname">gis</Parameter> <Parameter name="estimate_extent">false</Parameter> <Parameter name="table">(SELECT way,height FROM contours WHERE height::integer % 10 = 0 AND height::integer % 50 != 0 AND height::integer % 100 != 0) AS "contours-10"</Parameter> <Parameter name="extent">-180,-89.99,180,89.99</Parameter> </Datasource> </Layer> <Layer name="srtm_50" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours50</StyleName> <StyleName>contours-text50</StyleName> <Datasource> <Parameter name="type">postgis</Parameter> <Parameter name="host"></Parameter> <Parameter name="port"></Parameter> <Parameter name="user"></Parameter> <Parameter name="password"></Parameter> <Parameter name="dbname">gis</Parameter> <Parameter name="estimate_extent">false</Parameter> <Parameter name="table">(SELECT way,height FROM contours WHERE height::integer % 50 = 0 AND height::integer % 100 != 0) AS "contours-50"</Parameter> <Parameter name="extent">-180,-89.99,180,89.99</Parameter> </Datasource> </Layer> <Layer name="srtm_100" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours100</StyleName> <StyleName>contours-text100</StyleName> <Datasource> <Parameter name="type">postgis</Parameter> <Parameter name="host"></Parameter> <Parameter name="port"></Parameter> <Parameter name="user"></Parameter> <Parameter name="password"></Parameter> <Parameter name="dbname">gis</Parameter> <Parameter name="estimate_extent">false</Parameter> <Parameter name="table">(SELECT way,height FROM contours WHERE height::integer % 100 = 0) AS "contours-100"</Parameter> <Parameter name="extent">-180,-89.99,180,89.99</Parameter> </Datasource> </Layer>
And you need some styles:
<Style name="contours10"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#9cb197</CssParameter> <CssParameter name="stroke-width">0.5</CssParameter> </LineSymbolizer> </Rule> </Style> <Style name="contours50"> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#9cb197</CssParameter> <CssParameter name="stroke-width">0.6</CssParameter> </LineSymbolizer> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#747b90</CssParameter> <CssParameter name="stroke-width">0.6</CssParameter> </LineSymbolizer> </Rule> </Style> <Style name="contours100"> <Rule> <MaxScaleDenominator>409483</MaxScaleDenominator> <MinScaleDenominator>204741</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#9cb197</CssParameter> <CssParameter name="stroke-width">0.7</CssParameter> </LineSymbolizer> </Rule> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#747b90</CssParameter> <CssParameter name="stroke-width">0.7</CssParameter> </LineSymbolizer> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer> <CssParameter name="stroke">#855d62</CssParameter> <CssParameter name="stroke-width">0.7</CssParameter> </LineSymbolizer> </Rule> </Style> <Style name="contours-text50"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer name="height" face_name="DejaVu Sans Book" size="8" fill="#747b90" halo_radius="1" placement="line" /> </Rule> </Style> <Style name="contours-text100"> <Rule> <MaxScaleDenominator>102370</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <TextSymbolizer name="height" face_name="DejaVu Sans Book" size="8" fill="#747b90" halo_radius="1" placement="line" /> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer name="height" face_name="DejaVu Sans Book" size="8" fill="#855d62" halo_radius="1" placement="line" /> </Rule> </Style>
For mapnik version 2.1 the xml changed a little bit, so I added this in osm.xml
<Layer name="srtm_10" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours10</StyleName> <StyleName>contours-text10</StyleName> <Datasource> &datasource-settings; <Parameter name="table">(SELECT way,height,height as name FROM contours WHERE height::integer % 10 = 0 AND height::integer % 50 != 0 AND height::integer % 100 != 0) AS "contours-10"</Parameter> </Datasource> </Layer> <Layer name="srtm_50" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours50</StyleName> <StyleName>contours-text50</StyleName> <Datasource> &datasource-settings; <Parameter name="table">(SELECT way,height,height as name FROM contours WHERE height::integer % 50 = 0 AND height::integer % 100 != 0) AS "contours-50"</Parameter> </Datasource> </Layer> <Layer name="srtm_100" status="on" srs="+proj=latlong +datum=WGS84"> <StyleName>contours100</StyleName> <StyleName>contours-text100</StyleName> <Datasource> &datasource-settings; <Parameter name="table">(SELECT way,height,height as name FROM contours WHERE height::integer % 100 = 0) AS "contours-100"</Parameter> </Datasource> </Layer> <Style name="contours10"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.5" /> </Rule> </Style> <Style name="contours50"> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.6" /> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#747b90" stroke-width="0.6" /> </Rule> </Style> <Style name="contours100"> <Rule> <MaxScaleDenominator>409483</MaxScaleDenominator> <MinScaleDenominator>204741</MinScaleDenominator> <LineSymbolizer stroke="#9cb197" stroke-width="0.7" /> </Rule> <Rule> <MaxScaleDenominator>204741</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <LineSymbolizer stroke="#747b90" stroke-width="0.7" /> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <LineSymbolizer stroke="#855d62" stroke-width="0.7" /> </Rule> </Style> <Style name="contours-text50"> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer size="8" fill="#9c9" fontset-name="bold-fonts" halo-radius="1" wrap-width="14">[name]</TextSymbolizer> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#747b90" halo-radius="1" placement="line">[height]</TextSymbolizer> </Rule> </Style> <Style name="contours-text100"> <Rule> <MaxScaleDenominator>102370</MaxScaleDenominator> <MinScaleDenominator>51185</MinScaleDenominator> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#747b90" halo-radius="1" placement="line" >[height]</TextSymbolizer> </Rule> <Rule> <MaxScaleDenominator>51185</MaxScaleDenominator> <MinScaleDenominator>1599</MinScaleDenominator> <TextSymbolizer face-name="DejaVu Sans Book" size="8" fill="#855d62" halo-radius="1" placement="line" >[height]</TextSymbolizer> </Rule> </Style>
Other considerations
Colours, styles etc
If you are using the shapefiles approach, one thing to consider when you are coming up with your styles is that the 100m contours will be rendered 3 times over - the 100m contours are in the 10, 50 and 100m shapefiles. So it's a good idea to make them slightly wider than the 50s, which are slightly wider than the 10s. It doesn't matter if they are all the same colour, but when they are different colours they will go muddy due to anti-aliasing. The PostGIS approach avoids this problem since the select statements in the layers will exclude overlapping contours (i.e. the 10m layer won't contain contours at 50m and 100m heights and the 50m layer won't contain contours at 100m heights).
Void filling
Although the voids aren't rendered, they still exist as gaps in the contours. Although single-pixel gaps and other small voids could be filled by extrapolation, there are larger areas that need other sources of data.
Other sources
Given the voids, and the lattitude limits on the data, future work will include using the lower-resolution SRTM30 dataset to fill in the gaps.
Tools
See Srtm2Osm to convert srtm data to osm files.
Related
- HikingBikingMaps shows how to render images with hill shading in addition to contour lines
- OSM_on_Paper/Vector_Topo_Maperitive shows how to generate inkscape SVG files with vector contour lines
Storage requirements
Here are some numbers from the mailinglist on disk space usage for those interested. I had a go at importing 10 metre contour lines for the whole of Eurasia into PostGIS - latitudes of 0 - 46 degrees North required about 110 gig of disk space for the Postgres table and amounted to around 105 million contour lines. (At this point before I ran out of space) - the SRTM3 data set extends up to 60 degrees North).
Between 0 and 46 degrees north across Eurasia amounts to 3244 1x1 degree tiles. So this averages you around 35MB of disk space to import a 1x1 degree tile into PostGIS (obviously dependent on the terrain the tile covers), giving rough estimated numbers of:
Location | est. storage req. | number of tiles |
---|---|---|
Africa | 111 GB | (3250 tiles) |
Australia | 36 GB | (1060 tiles) |
Eurasia | 202 GB | (5902 tiles) |
Islands | 5 GB | (141 tiles) |
N America | 82 GB | (2412 tiles) |
S America | 62 GB | (1807 tiles) |
WHOLE WORLD | 498 GB | (14572 tiles) |
So with half a terabyte of disk you can import the whole lot... There is also the higher resolution SRTM1 data set covering North America - I'm not clear on how using those data would affect these numbers - probably substantially, since the number of vertices in each contour line would go up due to the higher resolution of the DEM