zverik / nik4 Goto Github PK
View Code? Open in Web Editor NEWMapnik to image export
License: Do What The F*ck You Want To Public License
Mapnik to image export
License: Do What The F*ck You Want To Public License
Hi @Zverik,
I just wanted to say “Thank you” for a really great program. I can’t live without it in my OSM work anymore :-). Keep up the good work! ❤️
Greetings
Marvin
Hi,
First of all, thanks a lot for this wonderful tool which is, so far, the best tool I know to export maps for print!
In particular, I'm a huge fan of the "print route" feature, to output daily maps of bicycle trips. One issue I have so far is however that it requires quite a lot of manual handling to split a multi-day GPX track and output pages per day, while fitting my printing capacities (only A4 / A3 for instance). This, or exporting a very large picture and tiling manually in an editing software aftewards.
A feature which would be a really nice addition would be to be able to tile along a route with a given paper size. That is tell Nik4 a paper size (typically A4) and a route to fit (--fit route
style for the simplest, could be refined afterwards to not take only the bounds into account but the true shape as well) and a padding. It would output a set of tiles (each of size A4) along the route.
What do you think about this?
Thanks!
The code which calculates the bounding box, scale, scale factor etc. has become quite long and it is likely to break something if you fix/add something different. @Zverik moved it into its own function recently. However, a few unit tests would benefit the project.
I started working on testability today. You can see progress in the branch unittest in my fork of this repository. To make testing easier, I created a new class Nik4Image. I will move more and more code from the main nik4.py
file into it.
Feedback is welcome.
Hello,
thanks for your work on nik4, it is very useful. There seems to be a small problem with the -c
option, though:
$ nik4.py -c 7.5 52.0 -s 5000 --margin 10 --ppi 300 -a 4 mapnik.xml print.png
Traceback (most recent call last):
File "/home/ug/.virtualenvs/maps/bin/nik4.py", line 289, in <module>
scale = scale / math.cos(math.radians(center.y))
NameError: name 'center' is not defined
If I understand correctly, center.y
should be replaced by options.center[1]
, at least this makes the code run through (or is it necessary to apply some transform here?).
210 mm → 750 px → 211.67 mm
wtf
Also, 200×136 turns to be ~ 200×120
Add --url
option for an OSM url. Take center coords and zoom from it, add default size (1280×1024). So basically one could run nik4.py --url <url> style.xml output.png
.
Executing nik4.py --fit route --padding 30 --add-layers route,stops --vars route=/path/to/route.json --ppi 100 -a +4 /path/to/osm.xml -
outputs the following traceback:
Traceback (most recent call last):
File "/path/to/nik4.py", line 677, in <module>
run(options)
File "/path/to/nik4.py", line 520, in run
im.save(outfile, fmt)
Boost.Python.ArgumentError: Python argument types in
Image.save(Image, _io.BufferedRandom, str)
did not match C++ signature:
save(mapnik::image_any, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, mapnik::rgba_palette)
save(mapnik::image_any, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
save(mapnik::image_any, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
While nik4.py --fit route --padding 30 --add-layers route,stops --vars route=/path/to/route.json --ppi 100 -a +4 /path/to/osm.xml /path/to/out.png
works fine.
Is this a problem with Nik4 or perhaps with the way it or some dependency is packaged? I could try installing it via pip on Arch Linux if you think that this may help.
I'm running the latest version 1.7.
Hi,
I don't get how zoom level is computed. I would have expected
python ./nik4.py --dpi 600 -a 4 --fit route --add-layers route ../cyclosm-cartocss-style/mapnik.xml a4.png --vars gpxroute=/tmp/route.gpx
and
python ./nik4.py --dpi 300 -a 2 --fit route --add-layers route ../cyclosm-cartocss-style/mapnik.xml a2.png --vars gpxroute=/tmp/route.gpx
and
python ./nik4.py --factor 2 --dpi 600 -a 4 --fit route --add-layers route ../cyclosm-cartocss-style/mapnik.xml a4.png --vars gpxroute=/tmp/route.gpx
to give the same output as an A2 paper is twice an A4 paper in each dimension.
Changing factor and DPIs gives the same result (that is a wider image but showing the same thing, at the same zoom level, as a --dpi 300 -a 4
image). Switching to A2 paper size however gets a different result, with a different zoom level in use. However, all of the generated images (A4 or A2 with the above commands) do have the same number of pixels.
Why does dpi
does not have any effect on the zoom level in use?
Thanks!
The usage of the tiles option (e.g. --tiles 2) leads to rendering artefacts.
If the aim is to create one large image (e.g. 25000x25000 pixels) the tiles option isn't necessary at all. mapnik has (as far as I know) no size limit. It seems that the problem comes in with the resize function (which seems to be limited [16384?]):
...
# for layer processing we need to create the Map object
m = mapnik.Map(100, 100) # temporary size, will be changed before output
mapnik.load_map_from_string(m, style_xml.encode("utf-8"), False, style_path)
m.srs = proj_target.params()
...
# export image
m.aspect_fix_mode = mapnik.aspect_fix_mode.GROW_BBOX
m.resize(size[0], size[1])
m.zoom_to_box(bbox)
A possible solution to avoid the rendering artefacts (if the aim is to create one large image) is to avoid tiling and resizing.
...
# for layer processing we need to create the Map object
m = mapnik.Map(size[0], size[1])
mapnik.load_map_from_string(m, style_xml.encode("utf-8"), False, style_path)
m.srs = proj_target.params()
...
# export image
m.aspect_fix_mode = mapnik.aspect_fix_mode.GROW_BBOX
m.zoom_to_box(bbox)
...
Make it accept any string, which is a paper format; prefixed with +
for landscape and -
for portrait, default depends on bbox. Would allow for -0
. Leave numbers as a fallback to ISO An, and add other popular formats, like US Letter, Bn (for books), 4A0 and 2A0, business cards, photo papers etc.
The synonim would be --paper
.
In other words, -
for the second positional parameter. Maybe using a temporary file.
Привет, Зверик :)
I may be totally wrong but from my observations current implementation of scale_factor
doesn't seem to work as expected.
Let's take an example invocation:
nik4.py -b 36.6481408595 55.9508668696 36.8974386671 56.127082206 -z 15 osm.xml out.png
This generates an image with dimensions 5809x7350 with unscaled features.
So I'm gonna get use of documented scale_factor
which is implemented in Nik4 as --factor
parameter:
nik4.py -b 36.6481408595 55.9508668696 36.8974386671 56.127082206 -z 15 --factor 2 osm.xml out.png
and I get a double-sized image: 11618x14701
. Yet, labels (and the rest) are very tiny — which is pretty expected as the scale_factor
is not enough. But increasing it up to 6
or more leads to even bigger images which requires --tiles
to be used - which is not an option. This happens also if the target is set to PDF.
After brief code review I saw that scale_factor
variable takes part in calculating map scale and the size of the resulting image. Which made me think that it is sorta misused: the only actual purpose of scale_factor
— is to scale features. So I'd expect my image to be of the same 5809x7350
size but with scaled text and features.
In attempt to figure out an alternative approach of doing things I patched Nik4 by adding an additional parameter --factor2
which is then passed to mapnik.render()
directly. As I expected the image size was preserved (5809x7350
) and the features were scaled. But this was not flawless, as lot of features began to disappear :(
For example, this rule from placenames.mss
:
#placenames-small::hamlet {
[place = 'hamlet'],
[place = 'locality'],
[place = 'neighbourhood'],
[place = 'isolated_dwelling'],
[place = 'farm'] {
[zoom >= 15] {
text-name: "[name]";
text-size: 19;
text-fill: @placenames;
text-face-name: @book-fonts;
text-halo-radius: 1.5;
text-halo-fill: rgba(255,255,255,0.6);
text-wrap-width: 45;
text-min-distance: 10;
}
/* ... */
}
}
is not triggered at all at -z 15
and I had to change it to [zoom >= 12] {
to get those features back.
Well, any ideas?
Че делать-та?
I am trying to render a GeoJSON with Nik4. Can you reproduce the following setup?
Command
nik4.py -p 550 -b 11.79656982421875 55.41810255439062 13.4417724609375 56.07356844648383 -z 11 test.xml map.png
Error message
Segmentation fault (core dumped)
GeoJSON test.geojson
https://gist.github.com/anonymous/567c1138bb1327662a0b
Stylesheet test.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Parameters>
<Parameter name="bounds">-180,-85.05112877980659,180,85.05112877980659</Parameter>
<Parameter name="center">0,0,2</Parameter>
<Parameter name="format">png8</Parameter>
<Parameter name="minzoom">0</Parameter>
<Parameter name="maxzoom">22</Parameter>
<Parameter name="scale">1</Parameter>
<Parameter name="metatile">2</Parameter>
<Parameter name="id"><![CDATA[test1]]></Parameter>
<Parameter name="_updated">1402561991000</Parameter>
<Parameter name="tilejson"><![CDATA[2.0.0]]></Parameter>
<Parameter name="scheme"><![CDATA[xyz]]></Parameter>
</Parameters>
<Style name="test" filter-mode="first">
<Rule>
<MarkersSymbolizer width="6" fill="#ff4455" stroke="#881133" allow-overlap="true" ignore-placement="true" />
</Rule>
</Style>
<Layer name="test"
srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
<StyleName>test</StyleName>
<Datasource>
<Parameter name="file"><![CDATA[/home/uli/Documents/MapBox/project/test1/layers/test.geojson]]></Parameter>
<Parameter name="layer"><![CDATA[OGRGeoJSON]]></Parameter>
<Parameter name="type"><![CDATA[ogr]]></Parameter>
</Datasource>
</Layer>
</Map>
As reported by Wookey in Debian Bug #985764:
The file /usr/share/doc/nik4/README.md.gz contains a link to instructions at
http://switch2osm.org/loading-osm-data/
which no longer exists.
I'm not sure what it should be replaced with.
archive.org still has a few copies, but it should probably be removed.
subj. 4a0 is bigger.
When inputing both ppi and scale_factor, scale_factor is overwritten.
The "zoom-level" used (which is passed to mapnik through the scale_factor) seems to not be changeable at a given dpi and scale (which is not not self-contradicting).
Probably the same that you cannot input both zoom and scale (scale is overwritten).
Is it too specialised (or badly-understood) a problem to be taken into account?
(I'm usually using options: paper, dpi, scale, factor, center)
Smth like --just-tiles
for rendering map in tiles and not stitching them after. With world/ozi map files for each tile.
https://lists.openstreetmap.org/pipermail/talk/2014-December/071574.html
Not sure what I am doing wrong but I always get an error when trying to run Nik4.
For example running this:
/nik4.py -c 0 51.477 --size-px 800 600 -z 17 turkart.xml test.png
Returns this:
Traceback (most recent call last):
File "./nik4.py", line 306, in
style_xml = xml_vars(style_xml, options.vars)
File "./nik4.py", line 154, in xml_vars
for kv in variables:
TypeError: 'NoneType' object is not iterable
https://github.com/Zverik/Nik4/blob/master/nik4.py#L176
It's not * 2
, but / cos(radians(lat))
!
Where to find osm.xml style file? I can only find the uncompiled sources: https://github.com/openstreetmap/mapnik-stylesheets
Since e26570d mapnik does not instanciate layers with "status=off" in the stylefile anymore.
This causes Nik4 to break if you try to use the "--fit" options as described in the example of the README:
nik4.py --fit route --add-layers route,stops --ppi 150 -a 6 osm.xml route.png
I suggest to add a "status=initialize" (or "status=load") to the style and update mapnik accordingly.
Can someone please explain why some chars in the vars-parameters are replaced by nik4 (line 157)? This way Postgresql throws an error, because the single quotes in the given sql-fragment are replaced by ' Are there any side-effects if we remove the single-quote replacement?
<Layer status="off" name="xxx" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs"> <StyleName>xxx</StyleName> <Datasource base="xxx"> <Parameter name="table"><![CDATA[(SELECT id, the_geom FROM xxx WHERE ST_Contains(${sql_poly}, the_geom)) as foo]]></Parameter> <Parameter name="key_field"><![CDATA[id]]></Parameter> <Parameter name="geometry_field"><![CDATA[the_geom]]></Parameter> </Datasource> </Layer>
nik4.py --vars sql_poly="ST_GeomFromText('POLYGON((9.6101188659668 53.324516580827, 9.6933746337891 53.323081133135, 9.6933746337891 53.294875047878, 9.6104621887207 53.297440007518, 9.6101188659668 53.324516580827))',4326)" ...
Please add support for Python 3, running nik4.py
with Python3 currently fails with a SyntaxError:
File "nik4.py", line 379
print 'scale={}'.format(scale)
^
SyntaxError: invalid syntax
It seems ozi map file references only a single tile instead of the whole map.
It's impossible to create images 10k × 20k because of memory limits. But we can create smaller tiles, like 4 images of 5k × 10k, and stitch them together. So check for imagemagick for stitching and add --tiled N
options, where N is a number of tiles: 1 for disabled, 2 for 2×2 etc.
Hi,
When fitting a route, a typical use case I have for padding is to include "as much X kilometers along the route". What about providing an alternative padding option using (OSM) meters as unit instead of paper units?
Thanks!
I like to run a query inside my stylesheet to get a certain feature from my database. It would be nice if I could specify a parameter (e.g. an ID) that will be used inside the function in the Style-sheet.
Hi and thanks very much for taking the time to create this useful script.
In Great Britain, Ordnance Survey maps (used by many hikers etc.) use the British National Grid (EPSG:27700). This system is widely used to specify co-ordinates and so it is useful to be able to use this projection and co-ordinate system in Mapnik.
It would be great if Nik4 was able to generate an OziExplorer file which automatically grabs the projection from the Mapnik XML file so that projections such as this could be supported. This currently seems to be hardcoded here.
Since cairo always writes dimensions in pt (see this issue), they have to be converted in nik4: divide by 1.25.
It seems that the --add-layers option isn't working in Nik4 1.6.
In the xml file I have this:
<Layer name="printmaps_point" status="off" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
<StyleName>printmaps_point</StyleName>
<Datasource>
<Parameter name="type">ogr</Parameter>
<Parameter name="file">${point_file:no_default}</Parameter>
<Parameter name="layer">${point_layer:no_default}</Parameter>
</Datasource>
</Layer>
But the layer isn't activated with this program call:
nik4.py --add-layers printmaps_point --zoom 15 ...
scale=4.70980476192
scale_factor=1.01433296582
size=2173,2173
bbox=Box2d(833675.160253,6787056.39383,843909.566001,6797290.79957)
bbox_wgs84=Box2d(7.48903138446,51.9276621344,7.58096861553,51.9843199662)
layers=coast-poly,waterarea,buildings,highways
PS: Everything works fine if I activate (status="on") the layer direct in the xml file.
Regards Klaus
Edit / additional info: I'm using Mapnik 3.0.9.
Please publish release tags for new versions.
This well help downstream users to be notified of new releases.
So when one of dimensions is 0
, it will be calculated, so the bbox is preserved.
Hello,
we are trying to use Nik4 for exporting map pdfs witih our custom map style. Using fit route together with a specified format (in this example Din A4) we are not getting the correct mearurements:
nik4.py -a 4 --padding 10 --fit route --add-layers route /home/osm/gs_sommer_web/project.xml /var/www/osm/test.pdf --fonts /usr/share/fonts
This resulted in a pdf with the size 374,3 x 264,6,instead of 297 mm x 210 mm (landscape)
Can you help us with this issue? Is there any parameter missing?
Thank you so much for your assistance
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.