ocitysmap/render.py

244 wiersze
9.9 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8; mode: Python -*-
# ocitysmap, city map and street index generator from OpenStreetMap data
# Copyright (C) 2009 David Decotigny
# Copyright (C) 2009 Frédéric Lehobey
# Copyright (C) 2009 David Mentré
# Copyright (C) 2009 Maxime Petazzoni
# Copyright (C) 2009 Thomas Petazzoni
# Copyright (C) 2009 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/>.
__version__ = '0.22'
import logging
import optparse
import os
import sys
import ocitysmap
import ocitysmap.layoutlib.renderers
from coords import BoundingBox
def main():
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
# Paper sizes, sorted in increasing widths
KNOWN_PAPER_SIZE_NAMES = \
map(lambda p: p[0],
sorted(ocitysmap.layoutlib.PAPER_SIZES,
key=lambda p: p[1]))
# Known renderer names
KNOWN_RENDERERS_NAMES = \
map(lambda r: "%s (%s)" % (r.name, r.description),
ocitysmap.layoutlib.renderers.get_renderers())
# Known paper orientations
KNOWN_PAPER_ORIENTATIONS = ['portrait', 'landscape']
usage = '%prog [options] [-b <lat1,long1 lat2,long2>|--osmid <osmid>]'
parser = optparse.OptionParser(usage=usage,
version='%%prog %s' % __version__)
parser.add_option('-C', '--config', dest='config_file', metavar='FILE',
help='specify the location of the config file.')
parser.add_option('-p', '--prefix', dest='output_prefix', metavar='PREFIX',
help='set a prefix to the generated file names. '
'Defaults to "citymap".',
default='citymap')
parser.add_option('-f', '--format', dest='output_formats', metavar='FMT',
help='specify the output formats. Supported file '
'formats: svg, svgz, pdf, ps, ps.gz, png, and csv. '
'Defaults to PDF. May be specified multiple times.',
action='append')
parser.add_option('-t', '--title', dest='output_title', metavar='TITLE',
help='specify the title displayed in the output files.',
default="My Map")
parser.add_option('--osmid', dest='osmid', metavar='OSMID',
help='OSM ID representing the polygon of the city '
'to render.', type="int"),
parser.add_option('-b', '--bounding-box', dest='bbox', nargs=2,
metavar='LAT1,LON1 LAT2,LON2',
help='bounding box (EPSG: 4326).')
parser.add_option('-L', '--language', dest='language',
metavar='LANGUAGE_CODE',
help='language to use when generating the index '
'(default=fr_FR.UTF-8). The map language is '
'driven by the system\' locale setting.',
default='fr_FR.UTF-8')
parser.add_option('-s', '--stylesheet', dest='stylesheet',
metavar='NAME',
help='specify which stylesheet to use. Defaults to the '
'first specified in the configuration file.')
parser.add_option('-l', '--layout', dest='layout',
metavar='NAME',
default=KNOWN_RENDERERS_NAMES[0].split()[0],
help=('specify which layout to use. Available layouts '
'are: %s. Defaults to %s.' %
(', '.join(KNOWN_RENDERERS_NAMES),
KNOWN_RENDERERS_NAMES[0].split()[0])))
parser.add_option('--paper-format', metavar='FMT',
help='set the output paper format. Either "default", '
'or one of %s.' % ', '.join(KNOWN_PAPER_SIZE_NAMES),
default='default')
parser.add_option('--orientation', metavar='ORIENTATION',
help='set the output paper orientation. Either '
'"portrait" or "landscape". Defaults to portrait.',
default='portrait')
(options, args) = parser.parse_args()
if len(args):
parser.print_help()
return 1
# Make sure either -b or -c is given
optcnt = 0
for var in options.bbox, options.osmid:
if var:
optcnt += 1
if optcnt == 0:
parser.error("One of --bounding-box "
"or --osmid is mandatory")
if optcnt > 1:
parser.error("Options --bounding-box "
"or --osmid are exclusive")
# Parse config file and instanciate main object
mapper = ocitysmap.OCitySMap(
[options.config_file or os.path.join(os.environ["HOME"], '.ocitysmap.conf')])
# Parse bounding box arguments when given
bbox = None
if options.bbox:
try:
bbox = BoundingBox.parse_latlon_strtuple(options.bbox)
except ValueError:
parser.error('Invalid bounding box!')
# Check that latitude and langitude are different
lat1, lon1 = bbox.get_top_left()
lat2, lon2 = bbox.get_bottom_right()
if lat1 == lat2:
parser.error('Same latitude in bounding box corners')
if lon1 == lon2:
parser.error('Same longitude in bounding box corners')
# Parse OSM id when given
if options.osmid:
try:
bbox = BoundingBox.parse_wkt(
mapper.get_geographic_info(options.osmid)[0])
except LookupError:
parser.error('No such OSM id: %d' % options.osmid)
# Parse stylesheet (defaults to 1st one)
if options.stylesheet is None:
stylesheet = mapper.get_all_style_configurations()[0]
else:
try:
stylesheet = mapper.get_stylesheet_by_name(options.stylesheet)
except LookupError, ex:
parser.error("%s. Available stylesheets: %s."
% (ex, ', '.join(map(lambda s: s.name,
mapper.STYLESHEET_REGISTRY))))
# Parse rendering layout
if options.layout is None:
cls_renderer = ocitysmap.layoutlib.renderers.get_renderers()[0]
else:
try:
cls_renderer = ocitysmap.layoutlib.renderers.get_renderer_class_by_name(options.layout)
except LookupError, ex:
parser.error("%s\nAvailable layouts: %s."
% (ex, ', '.join(map(lambda lo: "%s (%s)"
% (lo.name, lo.description),
ocitysmap.layoutlib.renderers.get_renderers()))))
# Output file formats
if not options.output_formats:
options.output_formats = ['pdf']
options.output_formats = set(options.output_formats)
# Reject output formats that are not supported by the renderer
compatible_output_formats = cls_renderer.get_compatible_output_formats()
for format in options.output_formats:
if format not in compatible_output_formats:
parser.error("Output format %s not supported by layout %s" %
(format, cls_renderer.name))
# Parse paper size
if (options.paper_format != 'default') \
and options.paper_format not in KNOWN_PAPER_SIZE_NAMES:
parser.error("Invalid paper format. Allowed formats = default, %s"
% ', '.join(KNOWN_PAPER_SIZE_NAMES))
# Determine actual paper size
compat_papers = cls_renderer.get_compatible_paper_sizes(bbox)
if not compat_papers:
parser.error("No paper size compatible with this rendering.")
paper_descr = None
if options.paper_format == 'default':
for p in compat_papers:
if p[5]:
paper_descr = p
break
else:
# Make sure the requested paper size is in list
for p in compat_papers:
if p[0] == options.paper_format:
paper_descr = p
break
if not paper_descr:
parser.error("Requested paper format not compatible with rendering. Compatible paper formats are: %s."
% ', '.join(map(lambda p: "%s (%.1fx%.1fcm²)"
% (p[0], p[1]/10., p[2]/10.),
compat_papers)))
assert paper_descr[3] or paper_descr[4] # Portrait or Landscape accepted
# Validate requested orientation
if options.orientation not in KNOWN_PAPER_ORIENTATIONS:
parser.error("Invalid paper orientation. Allowed orientations: %s"
% KNOWN_PAPER_ORIENTATIONS)
if (options.orientation == 'portrait' and not paper_descr[3]) or \
(options.orientation == 'landscape' and not paper_descr[4]):
parser.error("Requested paper orientation %s not compatible with this rendering at this paper size." % options.orientation)
# Prepare the rendering config
rc = ocitysmap.RenderingConfiguration()
rc.title = options.output_title
rc.osmid = options.osmid or None # Force to None if absent
rc.bounding_box = bbox
rc.language = options.language
rc.stylesheet = stylesheet
if options.orientation == 'portrait':
rc.paper_width_mm = paper_descr[1]
rc.paper_height_mm = paper_descr[2]
else:
rc.paper_width_mm = paper_descr[2]
rc.paper_height_mm = paper_descr[1]
# Go !...
mapper.render(rc, cls_renderer.name, options.output_formats,
options.output_prefix)
return 0
if __name__ == '__main__':
sys.exit(main())