Installing a Map Tile Server on Linux
These instructions are specific to the Fedora 25 Linux distribution. They can be adapted to other distributions by adjusting package names and file paths as necessary, otherwise the process should be pretty much the same. These instructions install and run the map tile services under your main userid. Here we assume that is "ibmadmin". When complete you will have a "renderd" service running that generates map tile PNG graphics files into the /var/lib/mod_tile directory from where they are served up by your local web server. To use this as the map server in an application you would configure your base map as an XYZ map tile server with the URL http://localhost/osm_tiles/${z}/${x}/${y}.png
The installation process is as follows:
Contents
- 1 Install a collection of prerequisite software packages using your distribution's package manager
- 2 Configure the postgresql database software and create a database called "gis"
- 3 Install and compile the mod_tile software
- 4 Install the mapnik map tile rendering stylesheet
- 5 Configure renderd
- 6 Download the map data for the area you are working in and import it into the "gis" database.
- 7 Test your installation
- 8 Configure your system to start the httpd and renderd services at boot
- 9 Bonus: Create an OpenLayers map web page so that you can play with your map tile server
Install a collection of prerequisite software packages using your distribution's package manager
sudo dnf install postgresql postgis osm2pgsql postgresql-contrib python2-mapnik mapnik-devel httpd-devel mapbox-variant-devel carto redhat-rpm-config automake libtool gcc-c++ libmemcached-devel
If you haven't previously set up a development build environment on your workstation then also do this installation:
sudo dnf groupinstall "Development Tools"
Configure the postgresql database software and create a database called "gis"
Do the initial configuration and start postgresql:
sudo /usr/bin/postgresql-setup --initdb sudo systemctl start postgresql sudo systemctl enable postgresql
Create the "gis" database:
sudo -u postgres -i -bash-4.3$ createuser ibmadmin -bash-4.3$ createdb -E UTF8 -O ibmadmin gis -bash-4.3$ exit
Add the postgis and hstore extensions to the "gis" database
sudo -u postgres psql postgres=# \c gis gis=# CREATE EXTENSION postgis; gis=# CREATE EXTENSION postgis_topology; gis=# CREATE EXTENSION hstore; gis=# ALTER TABLE geometry_columns OWNER TO ibmadmin; gis=# ALTER TABLE spatial_ref_sys OWNER TO ibmadmin; gis=# \q
Tune postgresql for better performance by adding the following settings to /var/lib/pgsql/data/postgresql.conf: (Comment out the existing shared_buffers setting and restart postgresql after making these changes.)
shared_buffers = 4GB work_mem = 100MB maintenance_work_mem = 4096MB fsync = off autovacuum = off random_page_cost = 1.1 effective_io_concurrency = 2
Install and compile the mod_tile software
mkdir ~/src cd ~/src git clone https://github.com/openstreetmap/mod_tile/ cd mod_tile ./autogen.sh ./configure make sudo make install sudo make install-mod_tile sudo ldconfig
Configure your web server to run the mod_tile module by creating the following two files and then restarting the httpd service:
/etc/httpd/conf.modules.d/10-mod_tile.conf
LoadModule tile_module /usr/lib64/httpd/modules/mod_tile.so
/etc/httpd/conf.d/mod_tile.conf
LoadTileConfigFile /usr/local/etc/renderd.conf ModTileRenderdSocketName /var/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 Header set Access-Control-Allow-Origin "*"
Install the mapnik map tile rendering stylesheet
There are a number of different style-sheets publicly available and you can also build your own. We will use the OpenStreetMap default style sheet.
cd ~/src svn co http://svn.openstreetmap.org/applications/rendering/mapnik mapnik-style cd ~/src/mapnik-style sudo ./get-coastlines.sh /usr/local/share
In order for mapnik to find the correct postgis database and the coast line data, you will need to configure the mapnik style-sheet to your local settings. In your style-sheet directory (e.g. ~/src/mapnik-style) there should be a directory inc. There are a number of files you need to adapt in this directory:
cd inc cp fontset-settings.xml.inc.template fontset-settings.xml.inc cp datasource-settings.xml.inc.template datasource-settings.xml.inc cp settings.xml.inc.template settings.xml.inc
Now you need to modify each of these files:
settings.xml.inc
replace
<!ENTITY symbols "%(symbols)s">
with
<!ENTITY symbols "symbols">
replace
<!ENTITY osm2pgsql_projection "&srs%(epsg)s;">
with
<!ENTITY osm2pgsql_projection "&srs900913;">
replace
<!ENTITY dwithin_node_way "&dwithin_%(epsg)s;">
with
<!ENTITY dwithin_node_way "&dwithin_900913;">
replace
<!ENTITY world_boundaries "%(world_boundaries)s">
with
<!ENTITY world_boundaries "/usr/local/share/world_boundaries">
replace
<!ENTITY prefix "%(prefix)s">
with
<!ENTITY prefix "planet_osm">
datasource-settings.xml.inc
In this file you will need to enter your database settings. You are running postgresql on the same machine as the rendering stack, so you can comment out the parameters “password”, “host” and “port” with an HTML-style comment. This will enable mapnik to use the “unix local user” as an authentication method. Change the “dbname” from “%(dbname)s” to “gis”, “estimate_extent” to “false”, and “extent” to “-20037508,-19929239,20037508,19929239” so that the file now reads:
<!-- Settings for your postgres setup. Note: feel free to leave password, host, port, or use blank --> <Parameter name="type">postgis</Parameter> <!-- <Parameter name="password">%(password)s</Parameter> --> <!-- <Parameter name="host">%(host)s</Parameter> --> <!-- <Parameter name="port">%(port)s</Parameter> --> <!-- <Parameter name="user">%(user)s</Parameter> --> <Parameter name="dbname">gis</Parameter> <!-- this should be 'false' if you are manually providing the 'extent' --> <Parameter name="estimate_extent">false</Parameter> <!-- manually provided extent in epsg 900913 for whole globe --> <!-- providing this speeds up Mapnik database queries --> <Parameter name="extent">-20037508,-19929239,20037508,19929239</Parameter>
Configure renderd
Change the the renderd settings by editing the /usr/local/etc/renderd.conf and change the following lines as shown (remember to change the "ibmadmin" username to your user’s name if necessary):
socketname=/var/run/renderd/renderd.sock plugins_dir=/usr/lib64/mapnik/input font_dir=/usr/share/fonts/dejavu XML=/home/ibmadmin/src/mapnik-style/osm.xml HOST=localhost
Create the files required for the mod_tile system to run (remember to change the "ibmadmin" username to your user’s name if necessary):
sudo mkdir /var/run/renderd sudo chown ibmadmin /var/run/renderd sudo mkdir /var/lib/mod_tile sudo chown ibmadmin /var/lib/mod_tile
Create a file named /etc/tmpfiles.d/renderd.conf which contains this single line:
d /var/run/renderd 0755 ibmadmin ibmadmin -
This will ensure that the system recreates the /var/run/renderd directory after a reboot, when temp files are cleaned up.
Download the map data for the area you are working in and import it into the "gis" database.
Your demo system probably doesn't have enough space to hold the map data for the whole world. Fortunately, there are state-by-state or region-by-region subset extracts that can be downloaded and imported into the "gis" database to cover the area covered in your demonstration. You can get these extracts (*.pbf files) from http://download.geofabrik.de and load them up with
osm2pgsql -d gis -C 16000 --slim --number-processes 8 filename
Each time you do this the data in the "gis" table is cleared out and replaced by the new data you are loading. If you need to combine multiple extract files together to get the map coverage you need, you can append multiple files together with the osmosis tool before loading them into the database:
osmosis --rb one.osm.pbf --rb two.osm.pbf --rb three.osm.pbf --merge --merge --wb combined.osm.pbf
Once that is complete you can load the combined.osm.pdf into the database using the osm2pgqsl command shown above.
Note that renderd only generates map tiles that do not already exist in the /var/lib/mod_tile directory structure. If you update the data in your database, those updates will not be visible in your map unless you force renderd to recreate those tiles. I usually just delete all the map tiles every time I make an update, forcing renderd to start over from scratch:
sudo rm -fr /var/lib/mod_tile/* sudo systemctl restart renderd
Test your installation
Make sure that your web server is running.
Start renderd in the foreground in a terminal window so that you can see any debugging messages:
/usr/local/bin/renderd -f -c /usr/local/etc/renderd.conf
There may be some iniparse messages about syntax errors for some of the comment lines in the renderd.conf file. You may safely ignore these. You may also safely ignore messages about a missing "unifont Medium" font.
Point your browser to http://localhost/osm_tiles/0/0/0.png You should see a small map of the world in the middle of your browser window.
Ctrl-C to stop the renderd command running in the terminal window before proceeding to the next step.
Configure your system to start the httpd and renderd services at boot
Create a service definition file for the renderd service, /usr/lib/systemd/system/renderd.service:
[Unit] Description=renderd After=multi-user.target Requires=multi-user.target [Service] Type=simple User=ibmadmin PIDFile=/var/run/renderd/renderd.pid ExecStart=/usr/local/bin/renderd -c /usr/local/etc/renderd.conf [Install] WantedBy=multi-user.target
Then do the following set of commands
sudo systemctl daemon-reload sudo systemctl start renderd sudo systemctl enable renderd sudo systemctl enable httpd
Bonus: Create an OpenLayers map web page so that you can play with your map tile server
The index.html shown below will display a full page map in your browser using your tile server. This page uses the OpenLayers Javascript library to display the map. I place this file in my /var/www/html directory making it the home page for my local web server, at the http://localhost URL
<!doctype html> <html lang="en"> <head> <link rel="stylesheet" href="http://openlayers.org/en/v3.5.0/css/ol.css" type="text/css"> <style> html, body { height: 100%; padding: 0; margin: 0; } .map { height: 100%; width: 100%; } </style> <script src="http://openlayers.org/en/v3.5.0/build/ol.js" type="text/javascript"></script> <title>OpenLayers 3.5 Test Page</title> </head> <body> <div id="map" class="map"></div> <script type="text/javascript"> var map = new ol.Map({ target: 'map', layers: [ new ol.layer.Tile({ title: 'localhost as XYZ', type: 'base', visible: false, source: new ol.source.XYZ({ url: "http://localhost.localdomain/osm_tiles/{z}/{x}/{y}.png", attributions: [ ol.source.OSM.ATTRIBUTION ] }) }), new ol.layer.Tile({ title: 'localhost as OSM', type: 'base', visible: true, source: new ol.source.OSM({ url: "http://localhost.localdomain/osm_tiles/{z}/{x}/{y}.png", }) }), new ol.layer.Tile({ title: 'Mapquest satellite', type: 'base', visible: false, source: new ol.source.MapQuest({layer: 'sat'}) }) ], view: new ol.View({ center: ol.proj.transform([0, 0], 'EPSG:4326', 'EPSG:3857'), zoom: 3 }), controls: [ new ol.control.Zoom(), new ol.control.ZoomSlider(), new ol.control.Rotate(), new ol.control.Attribution(), new ol.control.MousePosition({ projection: 'EPSG:4326' }), new ol.control.ScaleLine() ] }); </script> </body> </html>