Some OSM Ids such as 365634 have several entries in the polygon/line
tables. The original maposmatic code crashed because it assumed only
one entry was in the DB, and the ocitysmap code only considered the
1st entry returned by postgis.
This patch allows to work on the union of all the areas returned by
the polygon and line tables.
It adds another dependency: shapely. This allows to call libgeos
functions from within python. This also calls for some code
refactoring (=> TODO): the coords et al. API should be replaced by the
plain shapelib API
(http://gispython.org/shapely/docs/1.2/manual.html).
Split the renderers.py file info:
- renderers.py: the layour registry
- abstract_renderer: the ABC for a renderer
- singe_page_renderer: 3 classes to render into a single page
Implemented ocitysmap2-render command-line frontend. Tested it on
Chevreuse (OSM ID -943886) with many possible cmdline option
combinations. Default layout is now map with index at bottom.
New maplib (map creation routines), indexlib (index creation routines)
and layoutlib (page layout & rendering) structure. Besides moving
files around, a new layoutlib.commons module has been create to host
the mm to/from pt conversion routines.
When the small start to be small, avoid rounding errors (such as font
em = 0) by using floats as much as possible. And also try to signal
when the number of cols we draw is < expected, without raising an
exception.
This has not been so easy because:
- we need some kind of scaling because by default 1 pt in PDF = 1 px
in PNG... And we would prefer to have 1 pt in PDF = 300/72. px
(assuming PNG is 300dpi)
- we cannot call ctx.scale() before rendering the index because pango
takes the transformation matrix into account when it chooses the
font metrics. So with ctx.scale(), we could have the actual font
metrics different from those computed in
precompute_index_occupation() { which is called on a PDF surface by
SinglePageRenderer::render() }.
- we cannot render to a 72dpi vector surface (eg. PDF) and then
project it with a scaling factor onto a 300 DPI PNG surface,
because the result is ugly (pixelized)
- we cannot push_group()/post_group().set_matrix(xx=factor,
yy=factor=)/set_source() for the same reasons (pixelized)
So the solution we adopt is to trick pango into believing it is
rendering without any scale factor, which for us corresponds to a
72dpi resolution, and which for it corresponds to a 96 dpi cairo
resolution (see sources): this should always generate the same font
metrics as precompute_index_occupation() did. Then we tell it that the
cairo resolution, instead of being 96dpi as it assumes, is
96*desired_resolution/72. This is what this patch does.
One more note: we don't use an ImageSurface cairo surface for PNG
output because, for some other reason, it leads to different font
metrics. So, for PNG, we do use a PDF cairo surface !...
Fixed a trivial function name bug in __init__.py and a few index style
issues with the margins around the index. Added comments and reorder
the drawing operations to make sure the rectangle are printed last.
As a result, all 3 page layout renderer work in vector format... but
NOT in raster because the layout preparation is entirely based on
vector rendering engines even if the actual rendering engine is
raster... and both seem to handle font parameters differently.
This patch defines a generic SinglePageRenderer renderer which is able
to render a single page without an index, with an index on the side
(on the right for non RTL languages, on the left for RTL) or with an
index at the bottom of the page. This generic renderer is subclassed
to define the SinglePageRendererNoIndex ("plain") class, the
SinglePageRendererIndexOnSide ("single_page_index_side") class,
SinglePageRendererIndexBottom ("single_page_index_bottom").
IMPORTANT: Only SinglePageRendererNoIndex has been tested and leads to
the exact same result as before. Chances are that the index rendering
doesn't work for raster cairo devices, because of the lack of DPI
support in the index rendering API.
The RenderingConfiguration has a new "i18n" field, setup by render()
and which replaces rtl: corresponds to the language used in the
rendering.
Added docstrings.
Note: the struct placeholder mechanism implemented in previous patch
has been dropped, in favor of a much cleaner API with only 2
parameters passed (w_dots, h_dots).
The RenderingSession system has disappeared in favor of a much simpler
ctor()/render() API for the Renderers. As a consequence, it's the job
of each renderer to create the grid, the map_canvas and the index
renderer (if any). But this is needed because the renderer is in
charge of the page layout, which impacts the way the map_canvas is
rendered and which depends on the index. Potentially every renderer
need to manage the chicken-and-egg problem in their own way. Of
course, now this ctor takes a street_index as parameter, which allows
it (by way of an index renderer) to update the global layout depending
on the size of the index.
For now only the PlainRenderer has been updated. It users an object
placeholder to replace the RenderingSession object. This object
placeholder allows one to define a set of attributes and to access
them through the standard dotted notation. This object has a meaning
only internally to the PlainRenderer::render(). We don't define a
global structure like RenderingSession in order not to pollute the
code with global names. And such an object placeholder is only a
private placeholder for render() and the function it calls, to avoid
having to write 6235 arguments to pass the helper functions it calls.
This patch makes the ctor args minimal for the StreetIndex indexer
objects, and also drops the ref to the DB after the object has been
created. Also cleans up the rendering process a bit (WIP, still).
With this patch, the street index should now have its labels A1-B2
correct for the location of streets. The street indexer has a new
method apply_grid() that calls item.update_location_str(grid) so that
all the items have their location_str field correct. The items are
updated in-place: beware if multiple threads are manipulating a
StreetIndex object...
With this patch, it is now possible to specify different header/label
styles when contructing StreetIndexRenederer objects. This will allow
the renderer to use the first header/label font from this list that
allows to render the index into the specified area. For now the list
of possible header/label fonts is hard-coded as the default parameter
of the constructor.
As for the grid-neutral street index generation, with this patch we
compute the extension of each amenity with ST_LongestLine. This allows
to store geographic coordinates of the amenity in the index, hence
independent on any grid that might be used to locate them.
Signed-off-by: David Decotigny <david@decotigny.fr>
To map the street paths onto a Grid, we proceed in 2 passes since the
grid is not known before the streets are determined (size of the
rendered bbox depends on the number of streets => grid position over
the city's rendering is not fixed). This patch allows to optimize the
number of GIS queries: we only need to perform them in pass 1.
First pass gets the positions of the most distant points from the
street. This is what this patch implements. The second pass will then
map these points onto the grid.
This patch also changes the IndexItem structure to include the 2 most
distant endpoints of the item.
We only deal with the streets for now. The code for amenities is dummy
at that point; we'll work on this later.
Signed-off-by: David Decotigny <d2@maposmatic.org>
The new get_geographic_info method in OCitySMap allows one to query the
PostGIS database for the commonly-needed geographic information about a
set of OSM IDs.
This is used by OCitySMap itself, of course, for map rendering when it
needs to find the bounding box and contour of the requested OSM ID. This
will also be used by the Nominatim search gateway in MapOSMatic in the
near future.
Signed-off-by: Maxime Petazzoni <maxime.petazzoni@bulix.org>
The compatible paper size calculation in the PlainRenderer was not
properly using the returned result of BoundingBox.spheric_size(): the
width and height were switched, resulting in wrong chosen paper
orientations and sizes.
Signed-off-by: Maxime Petazzoni <maxime.petazzoni@bulix.org>
We split the index rendering phase into 2 steps:
- first pre-position the index and precompute its size
- this will allow the main page renderer to prepare the page layout,
place the grid and computhe the exact "square locations" in the index
- render the actual index, with the actual square positions (eg. A1,
B2, etc.)
Signed-off-by: David Decotigny <d2@maposmatic.org>