This text describes how to set up a tile server on a Raspberry Pi 4 with 4 Gigabytes of RAM. It deals with OpenStreetMap.org-based styles as well as with OpenStreetMap.de-based styles and addresses a couple of issues that I encountered while actually setting up the software. Be prepared to run into issues - changes to OSM software are frequent so part of the recipe is very likely alread outdated when you are using it.
A demonstration of the layers available on my Raspberry Pi 4 can be found at https://mapintosh.de/myOsm/ - use the layer selector button at the upper right of the map.
Make sure that the Ubuntu system is up to date, then install the packages required for a tile server. The commands below should install everything required - including recent version of node, npm and yarn. Issuing apt update twice is in order as you add package sources
sudo apt update sudo apt upgrade sudo apt install curl curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list sudo apt update sudo apt install apache2 apache2-dev autoconf build-essential bzip2 cmake \ fonts-noto fonts-noto-hinted g++ gcc gdal-bin git-core libagg-dev \ libboost-all-dev libboost-dev libboost-filesystem-dev libboost-system-dev \ libbz2-dev libcairo-dev libcairomm-1.0-dev libexpat1-dev libfreetype6-dev \ libgdal-dev libgeos-dev libgeos++-dev libgeotiff-epsg libicu-dev \ liblua5.1-dev libpq-dev libmapnik-dev libpq-dev \ libproj-dev libprotobuf-c-dev libtiff5-dev libtool libxml2-dev lua5.1 make \ mapnik-utils munin munin-node nodejs osm2pgsql postgis postgresql-11 \ postgresql-11-postgis-2.5 postgresql-11-postgis-2.5-scripts \ postgresql-contrib protobuf-c-compiler python-mapnik tar ttf-unifont unzip \ wget yarn zlib1g-dev sudo npm install -g carto
Now create a PostGIS database
sudo -u postgres -i createuser renderman createdb -E UTF8 -O renderman gis
Remain logged in as user postgres start psql
and do the following:
\c gis CREATE EXTENSION postgis; CREATE EXTENSION hstore; ALTER TABLE geometry_columns OWNER TO renderman; ALTER TABLE spatial_ref_sys OWNER TO renderman; \q
Now log out user postgres.
Add a user for rendering and make it a sudoer:
sudo adduser renderman sudo usermod -aG sudo renderman
Check if mapnik
has been installed correctly:
python >>> import mapnik >>>
If the import statement results in no complaint, everything is in order and you can quit Python by entering
quit()
To install mod_tile
do the following:
cd ~/src git clone https://github.com/SomeoneElseOSM/mod_tile.git cd mod_tile ./autogen.sh ./configure make -j 4 sudo make install sudo make install-mod_tile sudo ldconfig
Note that out of the box mod_tile will not allow you to have more than 10 different XML configurations, i.e. it will not allow you to serve more than 10 different styles. Ten different styles may sound excessive but I added this section after running into the limit.
WARNING: If you already have an existing config in /usr/local/etc/renderd.conf be sure to make a backup copy of it before installing a modded mod_tile. I learned the hard way that will be relentlessly replaced by the default content. Fortunately I stubbornly sticked to a strict naming scheme (all XML files are named mapnik.xml and the directory name is identical to the URI) so I managed to rapidly restore the contents.
Fixing the issue is easy, just change the limit that is hard-coded in includes/render_config.h
git diff includes/render_config.h diff --git a/includes/render_config.h b/includes/render_config.h index 997348f..57792d3 100644 --- a/includes/render_config.h +++ b/includes/render_config.h @@ -32,7 +32,7 @@ // The XML configuration used if one is not provided #define XMLCONFIG_DEFAULT "default" // Maximum number of configurations that mod tile will allow -#define XMLCONFIGS_MAX 10 +#define XMLCONFIGS_MAX 100 // Mapnik input plugins (will need to adjust for 32 bit libs) #define MAPNIK_PLUGINS "/usr/local/lib64/mapnik/input"
To obtain the standard style use
cd ~/src git clone https://github.com/gravitystorm/openstreetmap-carto.git cd openstreetmap-carto/
Now convert the carto project into something that Mapnik can understand:
carto project.mml > mapnik.xml
Executing this command may take some time.
There are a number of sources for data. We'll use bbbike.org to obtain the data for the city of Bonn.
mkdir ~/src/osm-data cd ~/src/osm-data curl -O https://download.bbbike.org/osm/bbbike/Bonn/Bonn.osm.pbf
To import the data just downloaded use
osm2pgsql -d gis \ -C 1000 \ --create \ -d gis \ -G \ --hstore \ --number-processes 4 \ --slim \ -S ~/src/openstreetmap-carto/openstreetmap-carto.style \ --tag-transform-script ~/src/openstreetmap-carto/openstreetmap-carto.lua \ ~/src/osm-data/Bonn.osm.pbf
Note that -C controls how much memory is allocated and –number-processes sets the number of cores to be used.
For performance create indexes
psql -d gis -f ~/src/openstreetmap-carto/indexes.sql
For rendering you need some shapefiles like low resolution country boundaries and the like (altogether some 750 MB)
cd ~/src/openstreetmap-carto/ ./scripts/get-shapefiles.py
To configure renderd edit /usr/local/etc/renderd.conf
. Some lines may need
changes, namely
XML=/home/jburgess/osm/svn.openstreetmap.org/applications/rendering/mapnik/osm-local.xml
to the actual location of the XML file, with the above that should read
XML=/home/renderman/src/openstreetmap-carto/mapnik.xml
and
URI=/osm_tiles/
if you want a different name.
A word of advice: Please confirm that plugins_dir
points to the directory that actually contains the plugins,
in my case
plugins_dir=/usr/lib/mapnik/3.0/input
The directory is the correct one if it contains a number of files ending in .input
Here's how my config looks renderd.conf looks like:
[renderd] num_threads=4 tile_dir=/var/lib/mod_tile stats_file=/var/run/renderd/renderd.stats [mapnik] plugins_dir=/usr/lib/mapnik/3.0/input/ font_dir=/usr/share/fonts/truetype font_dir_recurse=1 [osm] URI=/osm/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/openstreetmap-carto/mapnik.xml HOST=penpendede.hopto.org TILESIZE=256
To configure apache first create two directories
sudo mkdir /var/lib/mod_tile /run/renderd sudo chown renderman /var/lib/mod_tile /run/renderd
To enable mod_tile
you first need a config file so that Apache can load the
module.
sudo sh -c "echo 'LoadModule tile_module /usr/lib/apache2/modules/mod_tile.so' \ >> /etc/apache2/conf-available/mod_tile.conf"
Once it exists use
sudo a2enconf mod_tile
to enable the module but don't run systemctl reload apache2
as suggested.
Edit /etc/apache2/sites-available/000-default.conf
(or what other config you are using) and between ServerAdmin
and DocumentRoot add
LoadTileConfigFile /usr/local/etc/renderd.conf ModTileRenderdSocketName /run/renderd/renderd.sock # Timeout before giving up for a tile to be rendered ModTileRequestTimeout 0 # Timeout before giving up for a tile to be rendered that is otherwise missing ModTileMissingRequestTimeout 30
when done with this reload apache twice.
sudo service apache2 reload sudo service apache2 reload
Time to check if everything is set up properly. Run renderd
in the foreground using
renderd -f -c /usr/local/etc/renderd.conf
Now point your browser to
http://localhost/osm_tiles/0/0/0.png
(you of course need to use the correcthostname) and if everything works out fine you see the world.
If something goes wrong check the output of renderd
for hints on the reason why.
Apply the following changes to ~/src/mod_tile/debian/renderd.init
(and don't worry, you can always use git
checkout
to revert mishaps)
Firstly change
RUNASUSER=www-data
to
RUNASUSER=renderman
secondly
DAEMON=/usr/bin/$NAME
to
DAEMON=/usr/local/bin/$NAME
thirdly
DAEMON_ARGS=""
to
DAEMON_ARGS="-c /usr/local/etc/renderd.conf"
forthly
PIDSOCKDIR=/var/run/$NAME
to
PIDSOCKDIR=/run/$NAME
and finally
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
to
[ -r /usr/local/etc/$NAME ] && . /usr/local/etc/$NAME
After the block in which variables are initialized add
[ -d "$PIDSOCKDIR" ] || mkdir -p $PIDSOCKDIR && chown $RUNASUSER $PIDSOCKDIR
then copy the file to /etc/init.d/renderd
and make that copy executable:
sudo cp ~/src/mod_tile/debian/renderd.init /etc/init.d/renderd sudo chmod u+x /etc/init.d/renderd
and create /lib/systemd/system/renderd.service
containing
[Unit] Description=renderd After=multi-user.target Requires=multi-user.target [Service] Type=simple User=root PIDFile=/run/renderd/renderd.pid ExecStart=/etc/init.d/renderd start [Install] WantedBy=multi-user.target
make sure that /run/renderd
exists and (including its contents) is owned by renderman
sudo test -d /run/renderd || sudo mkdir /run/renderd sudo chown -R renderman /run/renderd
and finally
sudo systemctl start renderd sudo systemctl enable renderd sudo systemctl restart apache2
Annuntio vobis gaudium magnum: habemus tile server. Let's set up a small testing page for it. First we find out the region covered by our data, then we use Leaflet to show the map.
The following method for obtaining the bounding box of a region works for virtually any region known to OSM.
To obtain the bounding box of Bonn (i.e. the range of longitudes and latitudes that covers all of Bonn) visit http://boundingbox.klokantech.com/ and search for Bonn, Germany (or whatever region you want. Then go to the bottom of the page and in the pull-down menu under Copy & Paste select CSV. This should result in something like 7.022535,50.632691,7.210677,50.774423.
For our purposes 7.022,50.632,7.211,50.775 is sufficient which means that the longitude ranges from 7.022 to 7.211 degrees East and from 50.632 to 50.775 degrees North.
Under /var/www/html/
create the subdirectory my_osm_map
. In it create style.css
containing
* { box-sizing: border-box; } body { bottom: 0; left: 0; margin: 0; padding: 0; position: absolute; right: 0; top: 0; } .title { font-family: sans-serif; font-size: 24px; font-weight: bold; height: 48px; line-height: 48px; margin: 0; text-align: center; width: 100%; } .content { height: calc( 100% - 48px ); width: 100%; }
and index.html
containing
<!DOCTYPE html> <html> <head> <title>OSM Map of Bonn</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js" integrity="sha512-QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg==" crossorigin=""></script> </head> <body> <div class="title">OSM Map of Bonn</div> <div class="content" id="mapid"></div> <script> var mymap = L.map('mapid').fitBounds([ [50.632, 7.022], [50.775, 7.211] ]); L.tileLayer('http://localhost/osm_tiles/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>', id: 'mapbox.streets' }).addTo(mymap); </script> </body> </html>
When done visit [http://localhost/my_osm_map](http://localhost/my_osm_map). You should see a nice little page that (after some delay whilst the tiles are being rendered) displays an OSM style map of Bonn.
At this point nothing more remains to be done - unless you want or need the map off the grid.
The only dependence on remote resources currently is leaflet that is fetched from the online source unpkg.com. To go offline we need a local copy of leaflet. We'll use npm that has already been installed to obtain carto.
Go to /var/www/html/my_osm_map
and issue
bash npm install leaflet cp node_modules/leaflet/dist/leaflet.js . cp node_modules/leaflet/dist/leaflet.css . cp -R node_modules/leaflet/dist/images .
then change
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" integrity="sha512-puBpdR0798OZvTTbP4A8Ix/l+A4dHDD0DGqYW6RQ+9jxkRFclaxxQb/SJAWZfWAkuyeQUytO7+7N4QKrDh+drA==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js" integrity="sha512-QVftwZFqvtRNi0ZyCtsznlKSWOStnDORoefr1enyq5mVL4tmKB3S/EnC3rRJcxCPavG10IcrVGSmPh6Qw5lwrg==" crossorigin=""></script>
to
<link rel="stylesheet" href="leaflet.css" /> <script src="leaflet.js"></script>
and reload the page. That's all there is to it.
BEWARE: ANYTHING BEYOND THIS POINT IS NOT READY FOR USE!!!!!!
Lasciate ogne speranza, voi ch'intrate
I will assume that you have followed the above tutorial but want to have the German style as well.
The individuals behind the German OSM style came up with the very ingenious idea of using a different database layout for their style.
While the tile server is already up and running, more work needs to be done to support German. This includes setting up a fitting database.
Firstly we need KAKASI, a simple Kanji/Kana/Romaji converter.
sudo apt install kakasi
As we want to install the libraries for the C/C++ stored procedures to be used, we need some Debian development stuff
sudo apt install devscripts equivs
That will likely install tons of packages.
As renderman:
cd ~/src/ git clone https://github.com/giggls/mapnik-german-l10n.git cd mapnik-german-l10n
There is an INSTALL.md that explains the installation process, that covers the basics but in my opinion requires an experienced Linux navigator to circumnavigate the cliffs. Now that we have mapnik-german-l10n we need to install it. For this some packages need to be installed:
sudo pip3 install pythainlp sudo apt install postgresql-plpython3-11
Note: You need to install the version of postgresql-plpython3 that matches your postgres version.
Make sure that the postgres version you are using actually is listed in debian/control and debian/control.ini (under Build-Depends, this is not always the case), then issue
sudo mk-build-deps -i debian/control
When this is done build the deb package and install it (please note that the deb package is created in the parent directory):
make deb for file in ../postgresql*osml10n*.deb; do sudo apt install $file; done
Then, just to be safe (actually only needed if the above causes an error):
sudo apt install -f sudo apt update sudo apt upgrade
To start with we'll add a new database named osm. So sudo -u postgres psql
and enter
CREATE DATABASE osm WITH OWNER renderman; \q
Exit, then sudo -u postgres psql -d osm
and enter
CREATE EXTENSION hstore; CREATE EXTENSION postgis; CREATE EXTENSION unaccent; CREATE EXTENSION fuzzystrmatch; CREATE EXTENSION osml10n; CREATE EXTENSION osml10n CASCADE; CREATE EXTENSION osml10n_thai_transcript CASCADE; \q
Make sure that all required Python packages are installed issue:
pip install pyyaml lxml mapnik nik4
Note that nik4 is not actually needed for tile rendering but it is extremely cool: It can be used to render a map of your liking, see https://github.com/Zverik/Nik4
Clone the German style from github:
cd ~/src git clone git://github.com/giggls/openstreetmap-carto-de.git cd openstreetmap-carto-de
Import the data:
osm2pgsql \ -d osm \ -G \ --hstore \ --hstore-match-only \ --number-processes 4 \ -p planet_osm_hstore \ --style hstore-only.style \ ~/src/osm-data/Bonn.osm.pbf
Get the required shapefiles
./scripts/get-shapefiles.py
Pardon me? Oh, well, Oberpfaffenhofen is the location of Deutsches Raumfahrt-Kontrollzentrum, the German space mission control center and the heading as a whole referns to the infamous Apollo 13 accident.
It turns out that the current version of openstreetmap-carto-de is broken but fortunately not beyond repair. You can find a patch at https://mg.guelker.eu/articles/2020/01/01/openstreetmap-printkarten/ but I recommend not using it for two reasons:
manner
I suggest that as renderman you change directory to ~/src/openstreetmap-carto-de
put the patch below into a
file named fix.patch and then issue the following commands:
rm project.mml.d/lakes-z[0-9] project.mml.d/ocean-z[0-6] project.mml.d/rivers-z[0-6] patch -p1 < fix.patch
My version of the that as far as I can tell solves the issue without breaking any rendering is available in the ZIP file openstreetmap-carto-de-fix.zip, the fix contains this:
--- a/project.mml.d/order.txt +++ b/project.mml.d/order.txt @@ -8,28 +8,7 @@ water-lines-casing water-lines-low-zoom springs water-lines -rivers-z0 -rivers-z1 -rivers-z2 -rivers-z3 -rivers-z4 -rivers-z5 -rivers-z6 -lakes-z0 -lakes-z1 -lakes-z2 -lakes-z3 -lakes-z4 -lakes-z5 -lakes-z6 water-areas -ocean-z0 -ocean-z1 -ocean-z2 -ocean-z3 -ocean-z4 -ocean-z5 -ocean-z6 ocean-lz ocean landcover-area-symbols --- a/project.mml 2020-05-21 19:49:19.247102843 +0200 +++ b/project.mml 2020-05-21 19:48:44.677720976 +0200 @@ -152,7 +152,7 @@ file: data/world_boundaries/builtup_area.shp type: shape properties: - minzoom: 8 + minzoom: 0 maxzoom: 9 - id: landcover-line geometry: linestring @@ -174,7 +174,7 @@ file: data/antarctica-icesheet-polygons-3857/icesheet_polygons.shp type: shape properties: - minzoom: 5 + minzoom: 0 - id: water-lines-casing geometry: linestring <<: *extents @@ -259,144 +259,6 @@ ) AS water_lines properties: minzoom: 12 - - id: rivers-z0 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z0.shp" - type: "shape" - properties: - maxzoom: 0 - advanced: {} - - id: rivers-z1 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z1.shp" - type: "shape" - properties: - minzoom: 1 - maxzoom: 1 - advanced: {} - - id: rivers-z2 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z2.shp" - type: "shape" - properties: - minzoom: 2 - maxzoom: 2 - advanced: {} - - id: rivers-z3 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z3.shp" - type: "shape" - properties: - minzoom: 3 - maxzoom: 3 - advanced: {} - - id: rivers-z4 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z4.shp" - type: "shape" - properties: - minzoom: 4 - maxzoom: 4 - advanced: {} - - id: rivers-z5 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z5.shp" - type: "shape" - properties: - minzoom: 5 - maxzoom: 5 - advanced: {} - - id: rivers-z6 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/river-polygons-reduced-3857/river_reduced_z6.shp" - type: "shape" - properties: - minzoom: 6 - maxzoom: 6 - advanced: {} - - id: lakes-z0 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z0.shp" - type: "shape" - properties: - maxzoom: 0 - advanced: {} - - id: lakes-z1 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z1.shp" - type: "shape" - properties: - minzoom: 1 - maxzoom: 1 - advanced: {} - - id: lakes-z2 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z2.shp" - type: "shape" - properties: - minzoom: 2 - maxzoom: 2 - advanced: {} - - id: lakes-z3 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z3.shp" - type: "shape" - properties: - minzoom: 3 - maxzoom: 3 - advanced: {} - - id: lakes-z4 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z4.shp" - type: "shape" - properties: - minzoom: 4 - maxzoom: 4 - advanced: {} - - id: lakes-z5 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z5.shp" - type: "shape" - properties: - minzoom: 5 - maxzoom: 5 - advanced: {} - - id: lakes-z6 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/lakes-polygons-reduced-3857/lakes_reduced_z6.shp" - type: "shape" - properties: - minzoom: 6 - maxzoom: 6 - advanced: {} - id: water-areas geometry: polygon <<: *extents @@ -427,75 +289,6 @@ properties: cache-features: true minzoom: 0 - - id: ocean-z0 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z0.shp" - type: "shape" - properties: - maxzoom: 0 - advanced: {} - - id: ocean-z1 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z1.shp" - type: "shape" - properties: - minzoom: 1 - maxzoom: 1 - advanced: {} - - id: ocean-z2 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z2.shp" - type: "shape" - properties: - minzoom: 2 - maxzoom: 2 - advanced: {} - - id: ocean-z3 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z3.shp" - type: "shape" - properties: - minzoom: 3 - maxzoom: 3 - advanced: {} - - id: ocean-z4 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z4.shp" - type: "shape" - properties: - minzoom: 4 - maxzoom: 4 - advanced: {} - - id: ocean-z5 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z5.shp" - type: "shape" - properties: - minzoom: 5 - maxzoom: 5 - advanced: {} - - id: ocean-z6 - geometry: "polygon" - <<: *extents - Datasource: - file: "data/ocean-polygons-reduced-3857/ocean_reduced_z6.shp" - type: "shape" - properties: - minzoom: 6 - maxzoom: 6 - advanced: {} - id: ocean-lz geometry: polygon <<: *extents @@ -503,7 +296,7 @@ file: data/simplified-water-polygons-split-3857/simplified_water_polygons.shp type: shape properties: - minzoom: 7 + minzoom: 0 maxzoom: 9 - id: ocean geometry: polygon @@ -550,7 +343,7 @@ file: data/antarctica-icesheet-outlines-3857/icesheet_outlines.shp type: shape properties: - minzoom: 5 + minzoom: 0 - id: water-barriers-line geometry: linestring <<: *extents @@ -1356,7 +1149,7 @@ file: data/ne_110m_admin_0_boundary_lines_land/ne_110m_admin_0_boundary_lines_land.shp type: shape properties: - minzoom: 1 + minzoom: 0 maxzoom: 3 - id: admin-low-zoom geometry: linestring
Now that the SQL scripts have been fixed use
psql -d osm -f osm_tag2num.sql psql -d osm -f views_osmde/view-line.sql psql -d osm -f views_osmde/view-point.sql psql -d osm -f views_osmde/view-polygon.sql psql -d osm -f views_osmde/view-roads.sql
and create indexes to speed things up
psql -d osm -f indexes-hstore.sql
In the same manner as you did for the default style enter
cd ~/src/openstreetmap-carto-de/ carto project.mml > mapnik.xml
With both the osm and osm-de style my renderd.conf now looks like this:
[renderd] num_threads=4 tile_dir=/var/lib/mod_tile stats_file=/var/run/renderd/renderd.stats [mapnik] plugins_dir=/usr/lib/mapnik/3.0/input/ font_dir=/usr/share/fonts/truetype font_dir_recurse=1 [osm] URI=/osm/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/openstreetmap-carto/mapnik.xml HOST=penpendede.hopto.org TILESIZE=256 [osm-de] URI=/osm-de/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/openstreetmap-carto-de/mapnik.xml HOST=penpendede.hopto.org TILESIZE=256
With some small changes you can modify the web page presenting the osm style into one presenting both both the osm and
the osm-de style. Simply change index.html
like this:
<!DOCTYPE html> <html> <head> <title>OSM Map of Bonn</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="leaflet.css"> <script src="leaflet.js"></script> </head> <body> <div class="title">OSM Map of Bonn</div> <div class="content" id="mapid"></div> <script> var osm = L.tileLayer('https://penpendede.hopto.org/osm/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }) var osmDe = L.tileLayer('https://penpendede.hopto.org/osm-de/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }) var map = L.map('mapid', { layers: [osm, osmDe] }).fitBounds([ [50.632, 7.022], [50.775, 7.211] ]); var baseMaps = { "osm": osm, "osmDe": osmDe } var overlayMaps = { } L.control.layers(baseMaps, overlayMaps).addTo(map) </script> </body> </html>
Despite its name, the OpenStreetMap databases contains tons on information on railways. https://www.openrailwaymap.org turns this data into overlay maps. If you already have a tile server with the default OSM style up and running it is quite easy to reproduce those maps.
As user renderman issue:
cd ~/src git clone https://github.com/OpenRailwayMap/OpenRailwayMap-CartoCSS.git
The map style accesses the database through a couple of views. In addition, it requires a few custom functions and a precomputed station label ranking. To run the required SQL scripts do the following:
cd ~/src/OpenRailwayMap-CartoCSS psql -d gis -f sql/osm_carto_views.sql psql -d gis -f sql/functions.sql psql -d gis -f sql/get_station_importance.sql
To create the style files use
carto project.mml > standard.xml carto maxspeed.mml > maxspeed.xml carto signals.mml > signals.xml
To add the styles to renderd you need to first add them to /usr/local/etc/renderd.conf
; it should look similar to this:
[renderd] num_threads=4 tile_dir=/var/lib/mod_tile stats_file=/var/run/renderd/renderd.stats [mapnik] plugins_dir=/usr/lib/mapnik/3.0/input/ font_dir=/usr/share/fonts/truetype font_dir_recurse=1 [osm] URI=/osm/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/openstreetmap-carto/mapnik.xml HOST=penpendede.hopto.org TILESIZE=256 [osm-de] URI=/osm-de/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/openstreetmap-carto-de/mapnik.xml HOST=penpendede.hopto.org TILESIZE=256 [train] URI=/train/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/OpenRailwayMap-CartoCSS/standard.xml HOST=penpendede.hopto.org TILESIZE=256 [train-maxspeed] URI=/train-maxspeed/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/OpenRailwayMap-CartoCSS/maxspeed.xml HOST=penpendede.hopto.org TILESIZE=256 [train-signals] URI=/train-signals/ TILEDIR=/var/lib/mod_tile XML=/home/renderman/src/OpenRailwayMap-CartoCSS/signals.xml HOST=penpendede.hopto.org TILESIZE=256
When done restart renderd and apache:
sudo service renderd restart sudo service apache2 restart
While it would be entirely possible to use the new layers as base maps I prefer using them as overlay maps. The new index.html looks like this:
renderman@penpendede:~/src $ more /var/www/html/myOsm/index.html <!DOCTYPE html> <html> <head> <title>OSM Map of Bonn</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="leaflet.css"> <script src="leaflet.js"></script> </head> <body> <div class="title">OSM Map of Bonn</div> <div class="content" id="mapid"></div> <script> var baseMaps = { 'osm': L.tileLayer('https://penpendede.hopto.org/osm/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }), osmDe: L.tileLayer('https://penpendede.hopto.org/osm-de/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }) } var overlayMaps = { 'train': L.tileLayer('https://penpendede.hopto.org/train/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }), 'train-maxspeed': L.tileLayer('https://penpendede.hopto.org/train-maxspeed/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }), 'train-signals': L.tileLayer('https://penpendede.hopto.org/train-signals/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, ' + '<a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' }) } var map = L.map('mapid', { layers: [baseMaps['osmDe']] }).fitBounds([ [50.632, 7.022], [50.775, 7.211] ]) L.control.layers(baseMaps, overlayMaps).addTo(map) </script> </body> </html>
You might have noticed that the shapefiles used are huge compared with everything else that consists a certain OSM style. So if you need a lot of different styles at once you waste dozens of gigs for identical copies.
Consider having a separate directory with all the shape file that are used and simply replace the data subdirectory of openstreetmap-carto, openstreetmap-carto-de, … by a symbolic link to that directory. Do not replace the individual files with a symbolic link as that won't work (at least it did not work when I tried). Some tools seem incapable of following symbolic links to files.
To pre-render the metatiles I use a script (I named it populate_bonn.sh that calls render_list
with the appropriate parameters for Bonn:
cmd=render_list socket=/run/renderd/renderd.sock $cmd -m $map -a -z 0 -Z 0 -x 0 -X 0 -y 0 -Y 0 -f -n 4 -s $socket $cmd -m $map -a -z 1 -Z 1 -x 1 -X 1 -y 0 -Y 0 -f -n 4 -s $socket $cmd -m $map -a -z 2 -Z 2 -x 2 -X 2 -y 1 -Y 1 -f -n 4 -s $socket $cmd -m $map -a -z 3 -Z 3 -x 4 -X 4 -y 2 -Y 2 -f -n 4 -s $socket $cmd -m $map -a -z 4 -Z 4 -x 8 -X 8 -y 5 -Y 5 -f -n 4 -s $socket $cmd -m $map -a -z 5 -Z 5 -x 16 -X 16 -y 10 -Y 10 -f -n 4 -s $socket $cmd -m $map -a -z 6 -Z 6 -x 33 -X 33 -y 21 -Y 21 -f -n 4 -s $socket $cmd -m $map -a -z 7 -Z 7 -x 66 -X 66 -y 42 -Y 43 -f -n 4 -s $socket $cmd -m $map -a -z 8 -Z 8 -x 132 -X 133 -y 85 -Y 86 -f -n 4 -s $socket $cmd -m $map -a -z 9 -Z 9 -x 265 -X 266 -y 171 -Y 172 -f -n 4 -s $socket $cmd -m $map -a -z 10 -Z 10 -x 531 -X 532 -y 343 -Y 344 -f -n 4 -s $socket $cmd -m $map -a -z 11 -Z 11 -x 1063 -X 1065 -y 687 -Y 688 -f -n 4 -s $socket $cmd -m $map -a -z 12 -Z 12 -x 2127 -X 2130 -y 1375 -Y 1377 -f -n 4 -s $socket $cmd -m $map -a -z 13 -Z 13 -x 4255 -X 4260 -y 2750 -Y 2755 -f -n 4 -s $socket $cmd -m $map -a -z 14 -Z 14 -x 8511 -X 8520 -y 5501 -Y 5511 -f -n 4 -s $socket $cmd -m $map -a -z 15 -Z 15 -x 17023 -X 17040 -y 11002 -Y 11022 -f -n 4 -s $socket $cmd -m $map -a -z 16 -Z 16 -x 34046 -X 34080 -y 22005 -Y 22045 -f -n 4 -s $socket $cmd -m $map -a -z 17 -Z 17 -x 68092 -X 68161 -y 44010 -Y 44091 -f -n 4 -s $socket $cmd -m $map -a -z 18 -Z 18 -x 136185 -X 136322 -y 88020 -Y 88183 -f -n 4 -s $socket
To use it for a different region you need to modify the values provided for x, y, X, and Y accordingly.
You may wonder where the value for the variable $map
comes from. Well, I call the script in the following manner:
map=osm ./populate_bonn.sh
This means that the script is called with the variable $map
set to osm
- it will not set the environment variable $map
- the assignment only effects the command at hand.