2010-08-03 22:10:06 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
# ocitysmap, city map and street index generator from OpenStreetMap data
|
|
|
|
|
# Copyright (C) 2010 David Decotigny
|
|
|
|
|
# Copyright (C) 2010 Frédéric Lehobey
|
|
|
|
|
# Copyright (C) 2010 Pierre Mauduit
|
|
|
|
|
# Copyright (C) 2010 David Mentré
|
|
|
|
|
# Copyright (C) 2010 Maxime Petazzoni
|
|
|
|
|
# Copyright (C) 2010 Thomas Petazzoni
|
|
|
|
|
# Copyright (C) 2010 Gaël Utard
|
|
|
|
|
|
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
# License, or 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 Affero General Public License for more details.
|
|
|
|
|
|
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
"""OCitySMap 2.
|
2010-08-07 09:52:39 +00:00
|
|
|
|
2010-08-08 09:30:54 +00:00
|
|
|
OCitySMap is a Mapnik-based map rendering engine from OpenStreetMap.org data.
|
|
|
|
|
It is architectured around the concept of Renderers, in charge of rendering the
|
|
|
|
|
map and all the visual features that go along with it (scale, grid, legend,
|
|
|
|
|
index, etc.) on the given paper size using a provided Mapnik stylesheet,
|
|
|
|
|
according to their implemented layout.
|
|
|
|
|
|
|
|
|
|
The PlainRenderer for example renders a full-page map with its grid, a title
|
|
|
|
|
header and copyright notice, but without the index.
|
|
|
|
|
|
|
|
|
|
How to use OCitySMap?
|
|
|
|
|
---------------------
|
|
|
|
|
|
|
|
|
|
The API of OCitySMap is very simple. First, you need to instanciate the main
|
|
|
|
|
OCitySMap class with the path to your OCitySMap configuration file (see
|
|
|
|
|
ocitysmap.conf-template):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ocitysmap = ocitysmap2.OCitySMap('/path/to/your/config')
|
|
|
|
|
|
|
|
|
|
The next step is to create a RenderingConfiguration, the object that
|
|
|
|
|
encapsulates all the information to parametize the rendering, including the
|
|
|
|
|
Mapnik stylesheet. You can retrieve the list of supported stylesheets (directly
|
|
|
|
|
as Stylesheet objects) with:
|
|
|
|
|
|
|
|
|
|
styles = ocitysmap.get_all_style_configurations()
|
|
|
|
|
|
|
|
|
|
Fill in your RenderingConfiguration with the map title, the OSM ID or bounding
|
|
|
|
|
box, the chosen map language, the Stylesheet object and the paper size (in
|
|
|
|
|
millimeters) and simply pass it to OCitySMap's render method:
|
|
|
|
|
|
|
|
|
|
ocitysmap.render(rendering_configuration, layout_name,
|
|
|
|
|
output_formats, prefix)
|
|
|
|
|
|
|
|
|
|
The layout name is the renderer's key name. You can get the list of all
|
|
|
|
|
supported renderers with ocitysmap.get_all_renderers(). The output_formats is a
|
|
|
|
|
list of output formats. For now, the following formats are supported:
|
|
|
|
|
|
|
|
|
|
* PNG at 300dpi
|
|
|
|
|
* PDF
|
|
|
|
|
* SVG
|
|
|
|
|
* SVGZ (gzipped-SVG)
|
|
|
|
|
* PS
|
|
|
|
|
|
|
|
|
|
The prefix is the filename prefix for all the rendered files. This is usually a
|
|
|
|
|
path to the destination's directory, eventually followed by some unique, yet
|
|
|
|
|
common prefix for the files rendered for a job.
|
2010-08-03 22:10:06 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__author__ = 'The MapOSMatic developers'
|
|
|
|
|
__version__ = '0.2'
|
|
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
import cairo
|
|
|
|
|
import ConfigParser
|
2010-08-04 21:21:37 +00:00
|
|
|
import gzip
|
2010-08-04 14:34:15 +00:00
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
import psycopg2
|
2010-08-04 22:47:07 +00:00
|
|
|
import re
|
2010-08-04 14:34:15 +00:00
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
import coords
|
|
|
|
|
import i18n
|
2010-08-04 21:21:37 +00:00
|
|
|
import index
|
2010-08-07 09:52:39 +00:00
|
|
|
import index.render
|
2010-08-04 14:34:15 +00:00
|
|
|
import renderers
|
|
|
|
|
|
|
|
|
|
l = logging.getLogger('ocitysmap')
|
|
|
|
|
|
2010-08-03 22:10:06 +00:00
|
|
|
class RenderingConfiguration:
|
|
|
|
|
"""
|
|
|
|
|
The RenderingConfiguration class encapsulate all the information concerning
|
|
|
|
|
a rendering request. This data is used by the layout renderer, in
|
|
|
|
|
conjonction with its rendering mode (defined by its implementation), to
|
|
|
|
|
produce the map.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.title = None # str
|
|
|
|
|
self.osmid = None # None / int (shading + city name)
|
2010-08-04 14:34:15 +00:00
|
|
|
self.bounding_box = None # bbox (from osmid if None)
|
2010-08-03 22:10:06 +00:00
|
|
|
self.language = None # str (locale)
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-03 22:10:06 +00:00
|
|
|
self.stylesheet = None # Obj Stylesheet
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
self.paper_width_mm = None
|
|
|
|
|
self.paper_height_mm = None
|
2010-08-03 22:10:06 +00:00
|
|
|
|
2010-09-02 19:20:39 +00:00
|
|
|
# Setup by Rendering routines from osmid and bounding_box fields:
|
|
|
|
|
self.polygon_wkt = None # str (WKT of interest)
|
|
|
|
|
|
2010-08-03 22:10:06 +00:00
|
|
|
class Stylesheet:
|
2010-08-05 14:58:55 +00:00
|
|
|
"""
|
|
|
|
|
A Stylesheet object defines how the map features will be rendered. It
|
|
|
|
|
contains information pointing to the Mapnik stylesheet and other styling
|
|
|
|
|
parameters.
|
|
|
|
|
"""
|
|
|
|
|
|
2010-08-03 22:10:06 +00:00
|
|
|
def __init__(self):
|
|
|
|
|
self.name = None # str
|
|
|
|
|
self.path = None # str
|
2010-08-05 14:58:55 +00:00
|
|
|
self.description = '' # str
|
|
|
|
|
self.zoom_level = 16
|
2010-08-03 22:10:06 +00:00
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
self.grid_line_color = 'black'
|
2010-08-04 22:47:07 +00:00
|
|
|
self.grid_line_alpha = 0.5
|
2010-08-04 14:34:15 +00:00
|
|
|
self.grid_line_width = 3
|
2010-08-03 22:10:06 +00:00
|
|
|
|
2010-08-04 22:47:07 +00:00
|
|
|
self.shade_color = 'black'
|
|
|
|
|
self.shade_alpha = 0.1
|
|
|
|
|
|
2010-08-05 14:58:55 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def create_from_config_section(parser, section_name):
|
|
|
|
|
"""Creates a Stylesheet object from the OCitySMap configuration.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
parser (ConfigParser.ConfigParser): the configuration parser
|
|
|
|
|
object.
|
|
|
|
|
section_name (string): the stylesheet section name in the
|
|
|
|
|
configuration.
|
|
|
|
|
"""
|
|
|
|
|
s = Stylesheet()
|
|
|
|
|
|
|
|
|
|
def assign_if_present(key, cast_fn=str):
|
|
|
|
|
if parser.has_option(section_name, key):
|
|
|
|
|
setattr(s, key, cast_fn(parser.get(section_name, key)))
|
|
|
|
|
|
|
|
|
|
s.name = parser.get(section_name, 'name')
|
|
|
|
|
s.path = parser.get(section_name, 'path')
|
|
|
|
|
assign_if_present('description')
|
|
|
|
|
assign_if_present('zoom_level', int)
|
|
|
|
|
|
|
|
|
|
assign_if_present('grid_line_color')
|
|
|
|
|
assign_if_present('grid_line_alpha', float)
|
|
|
|
|
assign_if_present('grid_line_width', int)
|
2010-08-04 21:21:37 +00:00
|
|
|
|
2010-08-05 14:58:55 +00:00
|
|
|
assign_if_present('shade_color')
|
|
|
|
|
assign_if_present('shade_alpha', float)
|
|
|
|
|
return s
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def create_all_from_config(parser):
|
|
|
|
|
styles = parser.get('rendering', 'available_stylesheets')
|
|
|
|
|
if not styles:
|
|
|
|
|
raise ValueError, \
|
|
|
|
|
'OCitySMap configuration does not contain any stylesheet!'
|
|
|
|
|
|
|
|
|
|
return [Stylesheet.create_from_config_section(parser, name)
|
|
|
|
|
for name in styles.split(',')]
|
2010-08-03 22:10:06 +00:00
|
|
|
|
|
|
|
|
class OCitySMap:
|
2010-08-07 09:52:39 +00:00
|
|
|
"""
|
2010-08-08 09:30:54 +00:00
|
|
|
This is the main entry point of the OCitySMap map rendering engine. Read
|
|
|
|
|
this module's documentation for more details on its API.
|
2010-08-07 09:52:39 +00:00
|
|
|
"""
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
DEFAULT_REQUEST_TIMEOUT_MIN = 15
|
|
|
|
|
|
|
|
|
|
DEFAULT_ZOOM_LEVEL = 16
|
2010-08-04 17:36:45 +00:00
|
|
|
DEFAULT_RESOLUTION_KM_IN_MM = 150
|
2010-08-06 10:04:20 +00:00
|
|
|
DEFAULT_RENDERING_PNG_DPI = 300
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-05 14:58:55 +00:00
|
|
|
STYLESHEET_REGISTRY = []
|
|
|
|
|
|
2010-08-06 13:36:56 +00:00
|
|
|
def __init__(self, config_files=None,
|
2010-08-04 14:34:15 +00:00
|
|
|
grid_table_prefix=None):
|
2010-08-07 09:52:39 +00:00
|
|
|
"""Instanciate a new configured OCitySMap instance.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
config_file (string or list or None): path, or list of paths to
|
|
|
|
|
the OCitySMap configuration file(s). If None, sensible defaults
|
|
|
|
|
are tried.
|
|
|
|
|
grid_table_prefix (string): a prefix for the grid map areas PostGIS
|
|
|
|
|
table, which is useful when multiple renderings run
|
|
|
|
|
concurrently.
|
|
|
|
|
"""
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-06 13:36:56 +00:00
|
|
|
if config_files is None:
|
|
|
|
|
config_files = ['/etc/ocitysmap.conf', '~/.ocitysmap.conf']
|
|
|
|
|
elif not isinstance(config_files, list):
|
|
|
|
|
config_files = [config_files]
|
|
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
config_files = map(os.path.expanduser, config_files)
|
|
|
|
|
l.info('Reading OCitySMap configuration from %s...' %
|
|
|
|
|
', '.join(config_files))
|
|
|
|
|
|
|
|
|
|
self._parser = ConfigParser.RawConfigParser()
|
|
|
|
|
if not self._parser.read(config_files):
|
|
|
|
|
raise IOError, 'None of the configuration files could be read!'
|
|
|
|
|
|
|
|
|
|
self._locale_path = os.path.join(os.path.dirname(__file__), '..', 'locale')
|
|
|
|
|
self._grid_table_prefix = '%sgrid_squares' % (grid_table_prefix or '')
|
2010-08-05 21:52:40 +00:00
|
|
|
self.__db = None
|
|
|
|
|
|
|
|
|
|
# Read stylesheet configuration
|
|
|
|
|
self.STYLESHEET_REGISTRY = Stylesheet.create_all_from_config(self._parser)
|
2010-08-07 09:52:39 +00:00
|
|
|
l.debug('Found %d Mapnik stylesheets.' % len(self.STYLESHEET_REGISTRY))
|
2010-08-05 21:52:40 +00:00
|
|
|
|
|
|
|
|
def _get_db(self):
|
|
|
|
|
if self.__db:
|
|
|
|
|
return self.__db
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
# Database connection
|
|
|
|
|
datasource = dict(self._parser.items('datasource'))
|
|
|
|
|
l.info('Connecting to database %s on %s as %s...' %
|
|
|
|
|
(datasource['dbname'], datasource['host'], datasource['user']))
|
2010-08-05 21:52:40 +00:00
|
|
|
|
|
|
|
|
db = psycopg2.connect(user=datasource['user'],
|
|
|
|
|
password=datasource['password'],
|
|
|
|
|
host=datasource['host'],
|
|
|
|
|
database=datasource['dbname'])
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
# Force everything to be unicode-encoded, in case we run along Django
|
|
|
|
|
# (which loads the unicode extensions for psycopg2)
|
2010-08-05 21:52:40 +00:00
|
|
|
db.set_client_encoding('utf8')
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
try:
|
2010-08-06 10:04:20 +00:00
|
|
|
timeout = int(self._parser.get('datasource', 'request_timeout'))
|
|
|
|
|
except (ConfigParser.NoOptionError, ValueError):
|
2010-08-04 14:34:15 +00:00
|
|
|
timeout = OCitySMap.DEFAULT_REQUEST_TIMEOUT_MIN
|
2010-08-05 21:52:40 +00:00
|
|
|
self._set_request_timeout(db, timeout)
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-05 21:52:40 +00:00
|
|
|
self.__db = db
|
|
|
|
|
return self.__db
|
|
|
|
|
|
|
|
|
|
_db = property(_get_db)
|
2010-08-05 14:58:55 +00:00
|
|
|
|
2010-08-05 21:52:40 +00:00
|
|
|
def _set_request_timeout(self, db, timeout_minutes=15):
|
2010-08-04 14:34:15 +00:00
|
|
|
"""Sets the PostgreSQL request timeout to avoid long-running queries on
|
|
|
|
|
the database."""
|
2010-08-05 21:52:40 +00:00
|
|
|
cursor = db.cursor()
|
2010-08-04 14:34:15 +00:00
|
|
|
cursor.execute('set session statement_timeout=%d;' %
|
|
|
|
|
(timeout_minutes * 60 * 1000))
|
|
|
|
|
cursor.execute('show statement_timeout;')
|
|
|
|
|
l.debug('Configured statement timeout: %s.' %
|
|
|
|
|
cursor.fetchall()[0][0])
|
|
|
|
|
|
2010-08-07 09:52:39 +00:00
|
|
|
def _cleanup_tempdir(self, tmpdir):
|
|
|
|
|
l.debug('Cleaning up %s...' % tmpdir)
|
|
|
|
|
for root, dirs, files in os.walk(tmpdir, topdown=False):
|
|
|
|
|
for name in files:
|
|
|
|
|
os.remove(os.path.join(root, name))
|
|
|
|
|
for name in dirs:
|
|
|
|
|
os.rmdir(os.path.join(root, name))
|
|
|
|
|
os.rmdir(tmpdir)
|
|
|
|
|
|
2010-08-08 10:07:56 +00:00
|
|
|
def get_geographic_info(self, osmids):
|
2010-09-02 19:20:39 +00:00
|
|
|
"""Return a list of tuples (one tuple for each specified ID in
|
|
|
|
|
osmids) where each tuple contains (osmid, WKT_envelope,
|
|
|
|
|
WKT_buildarea)"""
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-08 10:07:56 +00:00
|
|
|
# Ensure all OSM IDs are integers, bust cast them back to strings
|
|
|
|
|
# afterwards.
|
|
|
|
|
osmids = map(str, map(int, osmids))
|
|
|
|
|
l.debug('Looking up bounding box and contour of OSM IDs %s...'
|
|
|
|
|
% osmids)
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-04 22:47:07 +00:00
|
|
|
cursor = self._db.cursor()
|
2010-08-08 10:07:56 +00:00
|
|
|
cursor.execute("""select osm_id,
|
|
|
|
|
st_astext(st_transform(st_envelope(way), 4002)),
|
|
|
|
|
st_astext(st_transform(st_buildarea(way), 4002))
|
|
|
|
|
from planet_osm_polygon where osm_id in (%s);""" %
|
|
|
|
|
', '.join(osmids))
|
2010-08-07 09:52:39 +00:00
|
|
|
records = cursor.fetchall()
|
|
|
|
|
|
2010-08-04 22:47:07 +00:00
|
|
|
try:
|
2010-08-08 10:07:56 +00:00
|
|
|
return map(lambda x: (x[0], x[1].strip(), x[2].strip()), records)
|
2010-08-04 22:47:07 +00:00
|
|
|
except (KeyError, IndexError, AttributeError):
|
2010-08-08 10:07:56 +00:00
|
|
|
raise AssertionError, 'Invalid database structure!'
|
2010-08-04 22:47:07 +00:00
|
|
|
|
2010-08-07 07:49:12 +00:00
|
|
|
def _get_shade_wkt(self, bounding_box, polygon):
|
2010-08-07 09:52:39 +00:00
|
|
|
"""Creates a shade area for bounding_box with an inner hole for the
|
|
|
|
|
given polygon."""
|
|
|
|
|
regexp_polygon = re.compile('^POLYGON\(\(([^)]*)\)\)$')
|
|
|
|
|
matches = regexp_polygon.match(polygon)
|
2010-08-04 22:47:07 +00:00
|
|
|
if not matches:
|
|
|
|
|
l.error('Administrative boundary looks invalid!')
|
|
|
|
|
return None
|
|
|
|
|
inside = matches.groups()[0]
|
|
|
|
|
|
|
|
|
|
bounding_box = bounding_box.create_expanded(0.05, 0.05)
|
2010-08-07 18:22:02 +00:00
|
|
|
poly = "MULTIPOLYGON(((%s)),((%s)))" % \
|
|
|
|
|
(bounding_box.as_wkt(with_polygon_statement = False), inside)
|
2010-08-04 22:47:07 +00:00
|
|
|
return poly
|
|
|
|
|
|
2010-08-03 22:10:06 +00:00
|
|
|
def get_all_style_configurations(self):
|
|
|
|
|
"""Returns the list of all available stylesheet configurations (list of
|
|
|
|
|
Stylesheet objects)."""
|
2010-08-05 14:58:55 +00:00
|
|
|
return self.STYLESHEET_REGISTRY
|
|
|
|
|
|
|
|
|
|
def get_stylesheet_by_name(self, name):
|
|
|
|
|
"""Returns a stylesheet by its key name."""
|
|
|
|
|
for style in self.STYLESHEET_REGISTRY:
|
|
|
|
|
if style.name == name:
|
|
|
|
|
return style
|
|
|
|
|
raise LookupError, 'The requested stylesheet %s was not found!' % name
|
2010-08-03 22:10:06 +00:00
|
|
|
|
|
|
|
|
def get_all_renderers(self):
|
2010-08-08 09:30:54 +00:00
|
|
|
"""Returns the list of all available layout renderers (list of
|
|
|
|
|
Renderer classes)."""
|
|
|
|
|
return renderers.get_renderers()
|
|
|
|
|
|
|
|
|
|
def get_all_paper_sizes(self):
|
|
|
|
|
return renderers.get_paper_sizes()
|
2010-08-03 22:10:06 +00:00
|
|
|
|
2010-08-05 11:51:32 +00:00
|
|
|
def render(self, config, renderer_name, output_formats, file_prefix):
|
2010-08-03 22:10:06 +00:00
|
|
|
"""Renders a job with the given rendering configuration, using the
|
|
|
|
|
provided renderer, to the given output formats.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
config (RenderingConfiguration): the rendering configuration
|
|
|
|
|
object.
|
2010-08-05 11:51:32 +00:00
|
|
|
renderer_name (string): the layout renderer to use for this rendering.
|
2010-08-03 22:10:06 +00:00
|
|
|
output_formats (list): a list of output formats to render to, from
|
|
|
|
|
the list of supported output formats (pdf, svgz, etc.).
|
|
|
|
|
file_prefix (string): filename prefix for all output files.
|
|
|
|
|
"""
|
|
|
|
|
|
2010-08-05 21:52:40 +00:00
|
|
|
assert config.osmid or config.bounding_box, \
|
2010-08-04 14:34:15 +00:00
|
|
|
'At least an OSM ID or a bounding box must be provided!'
|
|
|
|
|
|
2010-08-04 21:21:37 +00:00
|
|
|
output_formats = map(lambda x: x.lower(), output_formats)
|
|
|
|
|
self._i18n = i18n.install_translation(config.language,
|
|
|
|
|
self._locale_path)
|
2010-08-05 15:47:09 +00:00
|
|
|
config.rtl = self._i18n.isrtl()
|
|
|
|
|
|
2010-08-07 09:52:39 +00:00
|
|
|
l.info('Rendering with renderer %s in language: %s (rtl: %s).' %
|
|
|
|
|
(renderer_name, self._i18n.language_code(), config.rtl))
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-09-02 19:20:39 +00:00
|
|
|
# Determine bounding box and WKT of interest
|
|
|
|
|
if config.osmid:
|
|
|
|
|
try:
|
|
|
|
|
osmid_geo_info = self.get_geographic_info([config.osmid])[0]
|
|
|
|
|
except IndexError:
|
|
|
|
|
raise AssertionError, 'OSM ID not found in the database!'
|
|
|
|
|
|
|
|
|
|
# Define the bbox if not already defined
|
|
|
|
|
if not config.bounding_box:
|
|
|
|
|
config.bounding_box \
|
|
|
|
|
= coords.BoundingBox.parse_wkt(osmid_geo_info[1])
|
|
|
|
|
|
|
|
|
|
# Update the polygon WKT of interest
|
|
|
|
|
config.polygon_wkt = osmid_geo_info[2]
|
|
|
|
|
else:
|
|
|
|
|
# No OSM ID provided => use specified bbox
|
|
|
|
|
config.polygon_wkt = config.bounding_box.as_wkt()
|
2010-08-08 10:07:56 +00:00
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
# Make sure we have a bounding box
|
2010-09-02 19:20:39 +00:00
|
|
|
assert config.bounding_box is not None
|
|
|
|
|
assert config.polygon_wkt is not None
|
|
|
|
|
|
|
|
|
|
# Prepare the index
|
|
|
|
|
street_index = index.indexer.StreetIndex(self._db,
|
|
|
|
|
config.polygon_wkt,
|
|
|
|
|
self._i18n)
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
# Create a temporary directory for all our shape files
|
|
|
|
|
tmpdir = tempfile.mkdtemp(prefix='ocitysmap')
|
|
|
|
|
l.debug('Rendering in temporary directory %s' % tmpdir)
|
|
|
|
|
|
2010-09-02 19:20:39 +00:00
|
|
|
# Prepare the main renderer
|
2010-08-05 14:18:28 +00:00
|
|
|
renderer_cls = renderers.get_renderer_class_by_name(renderer_name)
|
|
|
|
|
renderer = renderer_cls(config, tmpdir)
|
2010-08-05 11:51:32 +00:00
|
|
|
renderer.create_map_canvas()
|
2010-08-04 21:21:37 +00:00
|
|
|
|
2010-09-02 19:20:39 +00:00
|
|
|
shade_wkt = self._get_shade_wkt(
|
|
|
|
|
renderer.canvas.get_actual_bounding_box(),
|
|
|
|
|
config.polygon_wkt)
|
|
|
|
|
renderer.add_shade(shade_wkt)
|
2010-08-04 22:47:07 +00:00
|
|
|
|
2010-08-05 11:51:32 +00:00
|
|
|
renderer.canvas.render()
|
2010-08-07 07:49:12 +00:00
|
|
|
|
2010-09-02 19:20:39 +00:00
|
|
|
street_index_renderer = index.StreetIndexRenderer(
|
|
|
|
|
self._i18n,
|
|
|
|
|
street_index.categories)
|
2010-08-04 21:21:37 +00:00
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
try:
|
2010-08-04 21:21:37 +00:00
|
|
|
for output_format in output_formats:
|
|
|
|
|
output_filename = '%s.%s' % (file_prefix, output_format)
|
2010-08-07 07:49:12 +00:00
|
|
|
self._render_one(renderer, street_index_renderer,
|
|
|
|
|
output_filename, output_format)
|
2010-08-15 14:37:21 +00:00
|
|
|
street_index.write_to_csv(config.title, '%s.csv' % file_prefix)
|
2010-08-04 14:34:15 +00:00
|
|
|
finally:
|
|
|
|
|
self._cleanup_tempdir(tmpdir)
|
|
|
|
|
|
2010-08-07 09:52:39 +00:00
|
|
|
def _render_one(self, renderer, street_index_renderer, filename,
|
|
|
|
|
output_format):
|
2010-08-07 19:37:46 +00:00
|
|
|
l.info('Rendering to %s format...' % output_format.upper())
|
2010-08-04 21:21:37 +00:00
|
|
|
|
|
|
|
|
factory = None
|
2010-08-06 10:04:20 +00:00
|
|
|
dpi = renderers.RenderingSession.PT_PER_INCH
|
2010-08-04 21:21:37 +00:00
|
|
|
|
|
|
|
|
if output_format == 'png':
|
2010-08-06 10:04:20 +00:00
|
|
|
try:
|
|
|
|
|
dpi = int(self._parser.get('rendering', 'png_dpi'))
|
|
|
|
|
except ConfigParser.NoOptionError:
|
|
|
|
|
dpi = OCitySMap.DEFAULT_RENDERING_PNG_DPI
|
|
|
|
|
|
|
|
|
|
factory = lambda w,h: cairo.ImageSurface(cairo.FORMAT_ARGB32,
|
|
|
|
|
int(renderers.RenderingSession.pt_to_dots_with_dpi(w, dpi)),
|
|
|
|
|
int(renderers.RenderingSession.pt_to_dots_with_dpi(h, dpi)))
|
2010-08-04 21:21:37 +00:00
|
|
|
elif output_format == 'svg':
|
|
|
|
|
factory = lambda w,h: cairo.SVGSurface(filename, w, h)
|
|
|
|
|
elif output_format == 'svgz':
|
|
|
|
|
factory = lambda w,h: cairo.SVGSurface(
|
|
|
|
|
gzip.GzipFile(filename, 'wb'), w, h)
|
|
|
|
|
elif output_format == 'pdf':
|
|
|
|
|
factory = lambda w,h: cairo.PDFSurface(filename, w, h)
|
|
|
|
|
elif output_format == 'ps':
|
2010-08-06 06:39:21 +00:00
|
|
|
factory = lambda w,h: cairo.PSSurface(filename, w, h)
|
2010-08-05 21:52:40 +00:00
|
|
|
elif output_format == 'csv':
|
|
|
|
|
# We don't render maps into CSV.
|
|
|
|
|
return
|
|
|
|
|
|
2010-08-04 21:21:37 +00:00
|
|
|
else:
|
|
|
|
|
raise ValueError, \
|
|
|
|
|
'Unsupported output format: %s!' % output_format.upper()
|
|
|
|
|
|
2010-08-05 11:51:32 +00:00
|
|
|
surface = factory(renderer.paper_width_pt, renderer.paper_height_pt)
|
2010-08-07 17:06:56 +00:00
|
|
|
rs = renderer.create_rendering_session(surface, street_index_renderer,
|
|
|
|
|
dpi)
|
2010-08-07 12:12:25 +00:00
|
|
|
renderer.render(rs)
|
2010-08-06 10:04:20 +00:00
|
|
|
|
2010-08-07 19:37:46 +00:00
|
|
|
l.debug('Writing %s...' % filename)
|
2010-08-06 10:04:20 +00:00
|
|
|
if output_format == 'png':
|
|
|
|
|
surface.write_to_png(filename)
|
|
|
|
|
|
2010-08-04 21:21:37 +00:00
|
|
|
surface.finish()
|
|
|
|
|
|
2010-08-04 14:34:15 +00:00
|
|
|
if __name__ == '__main__':
|
2010-08-05 14:58:55 +00:00
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
|
|
2010-08-07 18:28:40 +00:00
|
|
|
o = OCitySMap([os.path.join(os.path.dirname(__file__), '..',
|
|
|
|
|
'ocitysmap.conf.mine')])
|
2010-08-04 14:34:15 +00:00
|
|
|
|
|
|
|
|
c = RenderingConfiguration()
|
2010-08-07 18:45:20 +00:00
|
|
|
c.title = 'Chevreuse, Yvelines, Île-de-France, France, Europe, Monde'
|
2010-08-05 11:51:32 +00:00
|
|
|
c.osmid = -943886 # -7444 (Paris)
|
2010-08-07 18:28:40 +00:00
|
|
|
c.language = 'fr_FR.UTF-8'
|
2010-08-07 17:06:56 +00:00
|
|
|
c.paper_width_mm = 297
|
|
|
|
|
c.paper_height_mm = 420
|
2010-08-05 14:58:55 +00:00
|
|
|
c.stylesheet = o.get_stylesheet_by_name('Default')
|
2010-08-04 14:34:15 +00:00
|
|
|
|
2010-08-07 07:49:12 +00:00
|
|
|
o.render(c, 'plain', ['pdf'], '/tmp/mymap')
|