SHA256
5
5
Fork
You've already forked Nik5
2
Mapnik to image export, C++ port of https://github.com/Zverik/Nik4
  • Go Template 57%
  • C 29%
  • C++ 12%
  • CMake 1.6%
  • Shell 0.4%
Find a file
Michael Reichert 98783c9974 Release 2.0
2026年05月19日 16:06:36 +02:00
cmake Remove traces of GD usage 2026年05月19日 16:05:32 +02:00
img Add missing image demo-zoom-levels.png 2026年04月23日 16:03:37 +02:00
src Remove traces of GD usage 2026年05月19日 16:05:32 +02:00
tests Remove traces of GD usage 2026年05月19日 16:05:32 +02:00
.gitignore Remove XML files which are built using a makefile 2026年01月09日 17:22:14 +01:00
CHANGELOG.md Release 2.0 2026年05月19日 16:06:36 +02:00
CMakeLists.txt Replace libgd by libvips due to size restrictions of libgd 2026年05月13日 13:52:16 +02:00
LICENSE.txt
README.md update changelog, remove mentions of GD from readme 2026年05月18日 15:20:56 +02:00

Nik5

This is a mapnik-to-image exporting script. It requires the Mapnik library.

Nik5 is a C++ port of Nik4 by Ilya Zverev. Nik4 used Mapnik's Python bindings. Those do not have any releases and their build process breaks from time to time. Therefore, Nik4 was ported to C++ and renamed to Nik5 because Mapnik is written in C++.

Changes between Nik4 and Nik5

  • more tests
  • Nik5 can render tiles with a buffer in order to avoid that labels are cut at tile boundaries.
  • The syntax of the command line options --bbox, --size-px and --size was changed from space to comma as separator. If you wrote --size-px 800 600 in the past, you now have to use --size-px 800,600.
  • Using Libvips library instead of the montage program provided by ImageMagick to merge tiles. Tiles are stored in memory and not saved on the disk as temporary files.
  • Use a XML parser (Expat) instead of regular expressions to replace variables in map styles.
  • some preparations to make the code useable in other projects (e.g. for batch rendering)

Building

Currently, this program is written for and used on Linux only. It might work on Mac. It will likely not work on Windows. If you need it to run on those platforms, pull requests are appreciated. The maintainer does not own a Mac or Windows computer, therefore he cannot support these platforms.

Requirements:

  • C++17
  • CMake (for building)
  • sed (for testing)
  • Bash (for testing)
  • Mapnik
  • Boost Program Options (for command line options)
  • Expat (for modifying map styles written in XML, inserting variables, enabling disabled layers)
  • Cairo (optional, required for SVG and PDF output, a dependency of Mapnik packages)
  • libvips (optional, required for merging tiles)

Debian/Ubuntu: apt install libvips-dev libmapnik-dev libboost-program-options-dev libexpat1-dev build-essential cmake

Arch Linux: pacman -S base-devel cmake mapnik libvips boost expat

Nik5 is built using CMake:

mkdir build
cd build
cmake ..
make

If you want to build Nik5 without Cairo and Libvips support, you have to call cmake with following options:

cmake -DWITH_CAIRO=OFF -DWITH_VIPS=OFF ..

Nik4/Nik5 readme

The following was taken from the original readme of Nik4 and slightly modified.

Why is it better

Nik5 takes great care to preserve values you feed it. If you say you need a ×ばつ600 image, it won't take a pixel less or more. It won't shrink a bounding box or distort lines when specifying so called "scale factor". When you need a 300 dpi image, you tell it --ppi 300 and can be sure you will get what you intended.

For example, this is a sample rendering of an area in Tallinn on zoom 17, by Nik5, Nik2img and as seen on the default layer on osm.org:

nik4 - osm.org - nik2img

Also it can use real-world units, that is, millimeters (and prefers to). Specify dimensions for printing, choose bounding box and ppi scale — and the result won't disappoint. Options are intuitive and plenty, and you will be amazed how much tasks became simpler with Nik5.

How to use it

Again, run nik5 -h to see the list of all available options. Here are some examples.

Watch a mapping party area

First, if you haven't already, install PostgreSQL+PostGIS and Mapnik, and use osm2pgsql to populate the database with a planet extract. For instructions see here or here. Get bounds by visiting osm.org: click "Export" and "Choose another region". Then:

nik5 -b -0.009,51.47,0.013,51.484 -z 17 openstreetmap-carto/osm.xml party-before.png

Here osm.xml is the compiled Mapnik style. Then you can update you database and generate snapshots of an area as it is being mapped. Alternatively, you can specify an area with its center and desired image size in pixels:

nik5 -c 0,51.477 --size-px 800,600 -z 17 openstreetmap-carto/osm.xml party-before.png

Even simpler, instead of --center and --zoom options, just grab an URL of a place:

nik5 --url https://www.openstreetmap.org/#map=16/51.47752/0.00193 osm.xml screenshot.png

Make a georeferenced raster image

Some people prefer planning routes with OziExplorer or similar programs. Or want to take a big raster map with them on the road. For that a very big image is needed. Usually they turn to downloading and stitching hundreds of tiles, but with Nik5 you can make Mapnik produce a better looking map, faster and without bothering tile server administrators.

Since you are not bound to any tile provider, you should employ TileMill for customizing your map style: for example, remove forest on low zooms, add contrast to road lines, render more villages, highlight useful POI and cycling routes.

nik5 -b 0.0016,51.4785,0.0096,51.4826 -z 18 custom.xml greenwich-18.png --ozi greenwich-18.map

This will render ×ばつ10709 image with a georeferencing file ready to open in OziExplorer. For a .wld file, which can be used in desktop GIS applications or for creating a GeoTIFF file, use --wld option. You can convert png+wld to geotiff with GDAL:

gdal_translate -of GTiff -a_srs epsg:4326 image.png image.tif

Make a BIG raster image

You would likely encounter out of memory error while trying to generate ×ばつ10709 image from the last chapter. Despair not:

nik5 -b -0.87,51.25,0.4,51.75 -z 15 custom.xml stitiched.png --ozi stitched.map --tiles 4

Voilà — now Mapnik has to generate 16 images of a manageable size ×ばつ2678. After that Nik5 will stitch all tiles together.

Get an image for printing

A4 options

Let's say you need a 1:5000 image of a city center for printing on a A4 sheet with margins.

nik5 -s 5000 --ppi 300 -a 4 -c 24.1094,56.9488 --margin 10 ~/osm/krym/carto/osm.xml 4print.png

What you get is a raster image, which when printed on an A4 with 300 dpi resolution, would have 10 mm margins and scale of exactly 50 m in a cm. See the picture above for explanation of margins and other options. Formats can be a0-a9, letter, card and so on. The paper orientation depends on a bbox; to force landscape or portrait orientation prepend the format with + or - characters. Or don't bother and enter numbers by hand: -d 150 100 will export a ×ばつ10 postcard map.

Wait, what's that again, about dimensions?

Dimensions you specify in --size (-d) and --size-px (-x) arguments are not exactly width and height in that order: they will be swapped if a bounding box would fit better. For example, when you export "landscape" bbox and specify -d 200 400, the image would be 40 cm wide and 20 cm tall. To prevent this behaviour, use --norotate option: with it, that image would be 20 cm wide, with the bounding box expanded vertically.

When you don't want your bounding box altered, use 0 for one of dimension values. The first one in that case is considered a long side length, the second is for shorter side. With --norotate option, they are width and height respectively. For example, -x 1024 0 --norotate would make the resulting image 1024 pixels wide regardless of bounding box proportions.

Print a route

On the image above there is a route. Nik5 cannot parse GPX files or draw anything on top of exported images, but it can manage layers in Mapnik style file. And Mapnik (via OGR plugin) can draw a lot of things, including GPX, GeoJSON, CSV, KML. Just add your route to the style like this:

<Style name="route" filter-mode="first">
 <Rule>
 <LineSymbolizer stroke-width="5" stroke="#012d64" stroke-linejoin="round" stroke-linecap="round" />
 </Rule>
</Style>
<Layer name="route" status="off" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
 <StyleName>route</StyleName>
 <Datasource>
 <Parameter name="type">ogr</Parameter>
 <Parameter name="file">/home/user/route.gpx</Parameter>
 <Parameter name="layer">tracks</Parameter>
 </Datasource>
 </Layer>

Note that you can add it in any place: for example, between road and label layers, so the route does not obscure any text. Also note status="off": this layer won't be drawn by default. So if you want to export a clean map for the extent of your route (or any other) layer, use those options:

nik5 --fit route --bbox -0.008,51.4719,0.012,51.4839 --size-px 400,700 osm.xml route_area.png

To enable drawing of the layer, use --add-layers option:

nik5 --fit route --add-layers route,stops --bbox -0.008,51.4719,0.012,51.4839 --ppi 150 -a 6 osm.xml route.png

You can list many layers, separating them with commas. And you can hide some layers: --hide-layers contours,shields. Obviously you can fit several layers at once, as well as specify a bounding box to include on a map. All layer names are case-sensitive, so if something does not appear, check your style file for exact layer names.

Print a different route each time

Nik5 supports variables in XML styles: ${name:default} defines a variable with the given name and its default value (which can be omitted, along with :). To substitute variable definitions with values or defaults, use --vars parameter. For example, let's make stroke width in the last example configurable, and request GPX file name:

 <LineSymbolizer stroke-width="${width:5}" stroke="#012d64" stroke-linejoin="round" stroke-linecap="round" />
 ...
 <Parameter name="file">${route}</Parameter>

Now to make an image of a route, use this command:

nik5 --fit route --ppi 150 -a 6 osm.xml route.png --vars width=8 route=~/routes/day2.gpx

Note that path would likely to be resolved relative to the XML file location. If you omit route variable in this example, you'll get an error message.

Generate a vector drawing from a map

It's as easy as adding an .svg extension to the output file name.

nik5 --fit route -a -5 --factor 4 osm.xml map.svg

Why did I use --factor (it's the same as using --ppi 362.8, which is 90.7 * 4)? Shouldn't vector images be independent of the resolution? Well, the problem is in label kerning:

SVG labels quality

Left image was exported with --factor 1. You can see in "ali", "sis", "Uus" that distance between letters is varying unpredictably, not like the font instructs. That's because Mapnik rounds letter widths to nearest integers, that is, to pixels. By increasing the resolution, you make that granularity finer, so rounding errors are much less prominent. Labels would become slightly longer, that's why they are different in the second image.

You can export a map to PDF and be done with it, but often you'd want to do some postprocessing: move labels away from roads, highlight features, draw additional labels and arrows. For that I recommend processing the SVG file with mapnik-group-text, which would allow for easier label movement.

See also

For generating tiles, see polytiles.py.

Author and license

The script was written by Ilya Zverev and published under WTFPL.