Giter Club home page Giter Club logo

node-tileserver's Introduction

node-tileserver

node-tileserver is a lightweight tileserver using NodeJS. It can serve bitmap and vector tiles and is designed as a fast and easy-to-install tileserver for rendering OpenStreetMap data. It works perfectly with an osm2pgsql database and Leaflet and KothicJS on the client side.

See the OpenStreetMap Wiki or the Github repository for more information. http://www.matheisen.org/de/tileserver.rss

Features

  • Serves tiles bitmap tiles usable in Leaflet or OpenLayers
  • Serves vector tiles rendered on clientside by KothicJS
  • Uses KothicJS both as bitmap renderer on serverside and canvas renderer on clientside
  • Filesystem caching mechanisms
  • Map styling with MapCSS
  • Support for tiles in multiple rendering styles
  • Designed to use a osm2pgsql hstore database containing OpenStreetMap data
  • Refresh tiles manually by GET requests
  • Rerender expired tiles automatically in the background
  • High performance that profits from the non-blocking I/O design of NodeJS
  • Easy to install on several operating systems, distributions and environments due to less dependencies
  • Renders bitmap tiles in "retina" quality (Supersampling)

Authors

Installation

First install all the dependencies. Depending on your operating system and environment, you may use another command like apt-get.

$ yum update
$ yum install gzip zlib zlib-devel postgresql-server postgresql-libs postgresql postgresql-common postgresql-devel postgis unzip librsvg2 gnome-python2-rsvg pygobject2 pygobject2-devel librsvg2 librsvg2-devel cairo cairo-devel cairomm-devel libjpeg-turbo-devel pango pango-devel pangomm pangomm-devel giflib-devel npm nodejs git python

For system-specific installation of Cairo view the node-canvas Wiki.

Then move to your favorite directory and clone this repository:

$ git clone https://github.com/rurseekatze/node-tileserver.git
$ cd node-tileserver

Not that this is the current developing stage, which may contain bugs and can lead to crashes. Therefore it is recommended to download a stable version.

After that you can install all necessary NodeJS modules with npm:

$ npm install

Now you need osm2pgsql:

$ git clone https://github.com/openstreetmap/osm2pgsql.git
$ cd osm2pgsql
$ ./autogen.sh
$ CFLAGS="-O2 -march=native -fomit-frame-pointer" CXXFLAGS="-O2 -march=native -fomit-frame-pointer" ./configure
$ make
$ cd ..

Set up the PostgreSQL database with all necessary extensions such as hstore:

$ su postgres
$ createuser railmap
$ createdb -E UTF8 -O railmap railmap
$ createlang plpgsql railmap
$ psql -d railmap -f /usr/share/pgsql/contrib/postgis-64.sql
$ psql -d railmap -f /usr/share/pgsql/contrib/postgis-1.5/spatial_ref_sys.sql
$ psql -d railmap -f /usr/share/pgsql/contrib/hstore.sql
$ psql -d railmap -f osm2pgsql/900913.sql

If you are using PostgreSQL version < 9.3 you also need to add a function (from https://gist.github.com/kenaniah/1315484):

$ echo "CREATE OR REPLACE FUNCTION public.hstore2json (
  hs public.hstore
)
RETURNS text AS
$body$
DECLARE
  rv text;
  r record;
BEGIN
  rv:='';
  for r in (select key, val from each(hs) as h(key, val)) loop
    if rv<>'' then
      rv:=rv||',';
    end if;
    rv:=rv || '"'  || r.key || '":';

    --Perform escaping
    r.val := REPLACE(r.val, E'\\', E'\\\\');
    r.val := REPLACE(r.val, '"', E'\\"');
    r.val := REPLACE(r.val, E'\n', E'\\n');
    r.val := REPLACE(r.val, E'\r', E'\\r');

    rv:=rv || CASE WHEN r.val IS NULL THEN 'null' ELSE '"'  || r.val || '"' END;
  end loop;
  return '{'||rv||'}';
END;
$body$
LANGUAGE 'plpgsql'
IMMUTABLE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100;" | psql -d railmap

$ echo "ALTER FUNCTION hstore2json(hs public.hstore) OWNER TO apache;"  | psql -d railmap

For higher performance you should create some indexes:

$ echo "CREATE INDEX railmap_point_tags ON railmap_point USING GIN (tags);" | psql -d railmap
$ echo "CREATE INDEX railmap_line_tags ON railmap_line USING GIN (tags);" | psql -d railmap
$ echo "CREATE INDEX railmap_polygon_tags ON railmap_polygon USING GIN (tags);" | psql -d railmap

Now you can load some data into your database:

$ osm2pgsql --create --database railmap --username railmap --prefix railmap --slim --style railmap.style --hstore --cache 512 railways.osm

Have a look at an example toolchain for an example of using osm2pgsql with filtered data.

Now you can begin to write your own rendering styles. node-tileserver processes rendering styles written in MapCSS. Have a look at the following websites for an introduction, examples and specifications:

You need MapCSS converter to compile your MapCSS styles to JavaScript. Go to your styles directory and compile all your MapCSS styles in one run (you have to do this after every change of your stylesheets):

$ for stylefile in *.mapcss ; do python mapcss_converter.py --mapcss "$stylefile" --icons-path . ; done

Note that you have to recompile the stylesheets every time you change the MapCSS files to apply the changes. It is also necessary to restart the tileserver to reload the stylesheets.

You need a proxy that routes incoming requests. It is recommended to use a NodeJS proxy like this, especially if you are running another webserver like Apache parallel to NodeJS. Remember to change the domains in the script and the configuration of your parallel running webservers. The NodeJS proxy listens on port 80 while parallel webservers should listen on 8080.

Now you are almost ready to run the tileserver. You just need to check the configuration.

Configuration

You can set various options to configure your tileserver:

  • tileSize Size of tiles in pixels. Usually it is not necessary to change this value, but you can increase or decrease it for higher rendering performance or faster map generation. Consider that the bitmap tiles are rendered in "retina" quality, so the actual tile size is twice as high (Supersampling). Default: 256

  • prefix The prefix used for osm2pgsql tables. Depends on the parameters you are using in osm2pgsql. Default: railmap

  • database The name of the used database. Depends on the parameters you are using in osm2pgsql. Default: railmap

  • username The username to be used for database requests. Depends on the parameters you are using in osm2pgsql. Default: postgres

  • password The password to be used for database requests. The value can be empty. Depends on the parameters you are using in osm2pgsql. Default: <empty>

  • vtiledir Relative or absolute path to the vector tile directory. Default: ../tiles

  • tiledir Relative or absolute path to the bitmap tile directory. Default: ../bitmap-tiles

  • expiredtilesdir Relative or absolute path to the list of expired tiles. Default: ../../olm/import

  • scriptdir Relative or absolute path to the directory of the required scripts, usually the kothic directory included in this repository. Default: ../js

  • styledir Relative or absolute path to the directory containing (compiled) MapCSS styles. Default: ../styles

  • zoomOffset Zoom offset. Default: 0

  • minZoom Lowest allowed zoomlevel for tiles. Change this value if you do not want to serve lowzoom tiles. Default: 0

  • maxZoom Highest allowed zoomlevel for tiles. Change this value if you do not want to serve highzoom tiles. Default: 20

  • styles List of available rendering styles. Please add the filenames of rendering styles in the styles directory to this list. Note that vector is already in use for serving vector tiles. Default: standard, maxspeed, signals

  • intscalefactor Scale factor. You do not need to change this value. Default: 10000

  • geomcolumn Name of the geometry column used in the database. You will not need to change this value. Default: way

  • pxtolerance Pixel tolerance used for simplifying vector data. You do not need to change this value. Default: 1.8

  • maxPrerender Highest zoomlevel in which tiles are prerendered in initial rendering run. Tiles in higher zoomlevels will be rendered just on request. Change this value to increase or decrease the load for your system. As higher the value, as more tiles have to be rendered. If your selected value is too low, tile requests will be slow, so you should find a value that balances system load and request times. Default: 8

  • maxCached Highest zoomlevel in which tiles are cached. Tiles in higher zoomlevels will be rendered just on request and removed from the filesystem cache instead of rerendering if they are expired. Change this value to increase or decrease the load for your system. As higher the value is, as more tiles have to be rerendered. If your selected value is too low, tile requests will be slow, so you should find a value that balances system load and request times. Default: 16

  • minExpiring Lowest zoomlevel in which tiles are only marked as expired if they are affected by an edit. Tiles in lower zoomlevels will be marked as expired only by a manual expiring (such as in an update script). Change this value to increase or decrease the load for your system. As lower the value is, as more tiles have to be rerendered. If your selected value is too high, too many not-affected tiles will be marked as expired, so you should find a value that balances system load and request times. Default: 10

  • maxsockets Maximum number of concurring http connections. The optimal value depends on your environment (hardware, operating system, system settings, ...), so you should try some values to get the optimal performance. Default: 100

  • tileserverPort Port on which the tileserver is listening. Change this value if you have conflicts with other applications. Default: 9000

  • tileBoundTolerance Extend the bounding box of the requested data by this number of pixels to avoid cutted icons at tile bounds. Default: 20

  • filterconditions For higher performance and smaller tiles it is possible to set some hardcoded SQL filter conditions

Note: For some parameters it is also necessary to change the modify the options in kothic-leaflet.js!

Run the server

Start the tileserver and the proxy in a screen session:

$ screen -R tileserver
$ node tileserver.js
$ [Ctrl][A][D]
$ screen -R proxy
$ node proxy.js
$ [Ctrl][A][D]

Jump back to the session to see log output or to restart the processes:

$ screen -r tileserver
$ screen -r proxy

Start the initial rendering:

$ node init-rendering.js

Usage

Bitmap tiles

The URL to load the bitmap tiles with Leaflet or Openlayers:

http://tiles.YOURDOMAIN.org/STYLENAME/z/x/y.png

Leaflet example:

...
map = L.map('mapFrame');
railmap = new L.TileLayer('http://{s}.tiles.YOURDOMAIN.org/standard/{z}/{x}/{y}.png',
{
    minZoom: 2,
    maxZoom: 19,
    tileSize: 256
}).addTo(map);
...

If you have more than one rendering style, you can change between them by changing the source url:

...
railmap._url = 'http://{s}.tiles.YOURDOMAIN.org/'+style+'/{z}/{x}/{y}.png';
railmap.redraw();
...

Vector tiles

URL to access vector tiles for using in Leaflet and KothicJS:

http://tiles.YOURDOMAIN.org/vector/z/x/y.json

Leaflet example:

Include all javascript files from kothic/src and kothic/dist and your compiled MapCSS styles into your website.

...
map = L.map('mapFrame');
railmap = new L.TileLayer.Kothic('http://{s}.tiles.YOURDOMAIN.org/vector/{z}/{x}/{y}.json',
{
    minZoom: 2,
    maxZoom: 19
});

MapCSS.onImagesLoad = function()
{
    map.addLayer(railmap);

    map.on('zoomend', function(e)
    {
        railmap.redraw();
    });

    setStyle("standard");
};

var styles = ["standard", "signals", "maxspeeds"]
for (var i=0; i<styles.length; i++)
    MapCSS.preloadSpriteImage(styles[i], "styles/"+styles[i]+".png");

If you have more than one rendering style, you can change between them:

...
for (var i=0; i<MapCSS.availableStyles.length; i++)
    if (MapCSS.availableStyles[i] != style)
        railmap.disableStyle(MapCSS.availableStyles[i]);

railmap.enableStyle(style);
railmap.redraw();
...

There is also the possibility to force the rerendering of a single tile. Just add /dirty at the end of a tile URL. Example:

http://tiles.YOURDOMAIN.org/STYLENAME/z/x/y.png/dirty

or

http://tiles.YOURDOMAIN.org/vector/z/x/y.png/dirty

Update database and tiles

Use osm2pgsql to update your database. To rerender all expired tiles, you need a file that contains a list of expired tiles. Such a command could look like this:

$ osm2pgsql --database railmap --username railmap --append --prefix railmap --slim --style railmap.style --hstore --cache 512 --expire-tiles 0-15 --expire-output expired_tiles changes.osc

Note that the value of the parameter --expire-tiles should have the format minZoom-(maxCached minus 2).

Also have a look at an example toolchain on how to update a database containing filtered data.

Run

$ node expire-tiles.js path/to/expired_tiles

to load the list of expired tiles and to mark all these tiles as expired. They will be rerendered on their next request or deleted from cache if they are highzoom tiles.

Note: It is not efficient to go through this list for all zoom levels, so by default only tiles zoom>=maxPrerender and zoom<=maxCached are marked as expired. For other zoomlevels you can mark all affected tiles by executing

$ cd /path/to/your/vector/tiles
$ find <zoom> -exec touch -t 197001010000 {} \;

You can also execute these commands to expire all tiles after a change of your stylesheet.

MapCSS extension

node-tileserver extends the used KothicJS renderer with a new MapCSS rule. Set

-x-ignore-layer: true;

to ignore the layer=* tag.

This can be useful for some purposes when you want to override the built-in layering of KothicJS.

Example: You want to render maxspeeds by colouring each line dependent on the maxspeed=* tag. If lines are overlapping, ones with higher values should be drawn on the top of lines with lower values. Without -x-ignore-layer, it may happen that a line with a higher value of maxspeed is hidden by a line with a lower values of maxspeed, because the lowspeed way is a bridge and has a higher value of layer=*.

References

  • [OpenRailwayMap] (http://www.openrailwaymap.org/) - a map of the global railway network based on OpenStreetMap. Provides a client-rendered canvas and a standard bitmap tile version of the map.

Contribute

Want to contribute to node-tileserver? Patches for new features, bug fixes, documentation, examples and others are welcome. Take also a look at the issues.

You can honor this project also by a donation with Flattr or Paypal. This project is operated by the developers in their spare time and has no commercial goals. By making a donation you can show that you appreciate the voluntary work of the developers and can motivate them to continue the project in the future.

License

Copyright (C) 2014 Alexander Matheisen

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

The file mapcss_converter.py and the files in the mapcss_parser and kothic directories are published under other licenses. See the header of each file for more information.

node-tileserver's People

Contributors

pbabik avatar philhug avatar rurseekatze avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.