Пример #1
0
def api_papersize(request):
    """API handler to get the compatible paper sizes for the provided layout
    and bounding box."""

    if request.method != 'POST':
        return HttpResponseBadRequest("ERROR: Bad request")

    f = forms.MapPaperSizeForm(request.POST)
    if not f.is_valid():
        return HttpResponseBadRequest("ERROR: Invalid arguments")

    renderer = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)
    osmid = f.cleaned_data.get('osmid')
    layout = f.cleaned_data.get('layout')
    stylesheet = renderer.get_stylesheet_by_name(
        f.cleaned_data.get('stylesheet'))

    # Determine geographic area
    if osmid is not None:
        try:
            bbox_wkt, area_wkt = renderer.get_geographic_info(osmid)
        except ValueError:
            LOG.exception("Error determining compatible paper sizes")
            raise
        except LookupError, e:
            return HttpResponseBadRequest(str(e))
        bbox = ocitysmap.coords.BoundingBox.parse_wkt(bbox_wkt)
Пример #2
0
    def __init__(self, *args, **kwargs):
        super(MapRenderingJobForm, self).__init__(*args, **kwargs)

        self._ocitysmap = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)

        layout_renderers = self._ocitysmap.get_all_renderers()
        stylesheets = self._ocitysmap.get_all_style_configurations()

        self.fields['layout'].choices = []
        for r in layout_renderers:
            if r.name == 'plain':
                description = _(u"Full-page layout without street index")
            elif r.name == 'single_page_index_side':
                description = _(
                    u"Full-page layout with the street index on the side")
            elif r.name == 'single_page_index_bottom':
                description = _(
                    u"Full-page layout with the street index at the bottom")
            elif r.name == 'multi_page':
                description = _(u"Multi-page layout")
            else:
                description = mark_safe(
                    _(u"The %(layout_name)s layout") % {'layout_name': r.name})
            self.fields['layout'].choices.append((r.name, description))

        self.fields['layout'].initial = layout_renderers[0].name

        self.fields['stylesheet'].choices = []
        for s in stylesheets:
            if s.name == "Default":
                description = _("The default OpenStreetMap.org style")
            elif s.name == "MapQuestEu":
                description = _("The european MapQuest style")
            elif s.name == "MapQuestUs":
                description = _("The US MapQuest style")
            elif s.name == "MapQuestUk":
                description = _("The UK MapQuest style")
            elif s.name == "Printable":
                description = _(
                    "A MapOSMatic-specific stylesheet suitable for printing")
            else:
                description = mark_safe(
                    _("The <i>%(stylesheet_name)s</i> stylesheet") %
                    {'stylesheet_name': s.name})
            self.fields['stylesheet'].choices.append((s.name, description))

        self.fields['stylesheet'].initial = stylesheets[0].name

        def _build_papersize_description(p):
            if p[0] == "Best fit":
                return mark_safe(_("Best fit <em class=\"papersize\"></em>"))
            else:
                return mark_safe("%s <em class=\"papersize\">"
                                 "(%.1f &times; %.1f cm²)</em>" %
                                 (p[0], p[1] / 10., p[2] / 10.))

        self.fields['papersize'].choices = [
            (p[0], _build_papersize_description(p))
            for p in self._ocitysmap.get_all_paper_sizes()
        ]
Пример #3
0
    def run(self):
        """Renders the given job, encapsulating all processing errors and
        exceptions.

        This does not affect the job entry in the database in any way. It's the
        responsibility of the caller to do maintain the job status in the
        database.

        Returns one of the RESULT_ constants.
        """

        l.info("Rendering job #%d '%s'..." % (self.job.id, self.job.maptitle))

        try:
            renderer = ocitysmap.OCitySMap(OCITYSMAP_CFG_PATH)
            config = ocitysmap.RenderingConfiguration()
            config.title = self.job.maptitle
            config.osmid = self.job.administrative_osmid

            if config.osmid:
                bbox_wkt, area_wkt \
                    = renderer.get_geographic_info(config.osmid)
                config.bounding_box = ocitysmap.coords.BoundingBox.parse_wkt(
                    bbox_wkt)
            else:
                config.bounding_box = ocitysmap.coords.BoundingBox(
                        self.job.lat_upper_left,
                        self.job.lon_upper_left,
                        self.job.lat_bottom_right,
                        self.job.lon_bottom_right)

            config.language = self.job.map_language
            config.stylesheet = renderer.get_stylesheet_by_name(
                self.job.stylesheet)
            config.overlays = []
	    if self.job.overlay:
                for overlay in self.job.overlay.split(","):
                    config.overlays.append(renderer.get_overlay_by_name(overlay))
            config.paper_width_mm = self.job.paper_width_mm
            config.paper_height_mm = self.job.paper_height_mm
        except KeyboardInterrupt:
            self.result = RESULT_KEYBOARD_INTERRUPT
            l.info("Rendering of job #%d interrupted!" % self.job.id)
            return self.result
        except Exception, e:
            self.result = RESULT_PREPARATION_EXCEPTION
            l.exception("Rendering of job #%d failed (exception occurred during"
                        " data preparation)!" % self.job.id)
            errfile = os.path.join(RENDERING_RESULT_PATH, self.job.files_prefix() + "-errors.txt")
            fp = open(errfile, "w")
            traceback.print_exc(file=fp)
            fp.close()
            self._email_exception(e)
            return self.result
Пример #4
0
def api_papersize(request):
    """API handler to get the compatible paper sizes for the provided layout
    and bounding box."""

    if request.method != 'POST':
        return HttpResponseBadRequest("ERROR: Bad request")

    f = forms.MapPaperSizeForm(request.POST)
    if not f.is_valid():
       return HttpResponseBadRequest("ERROR: Invalid arguments")

    renderer = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)
    osmid = f.cleaned_data.get('osmid')
    layout = f.cleaned_data.get('layout')
    stylesheet = renderer.get_stylesheet_by_name(
        f.cleaned_data.get('stylesheet'))

    # Determine geographic area
    if osmid is not None:
        try:
            bbox_wkt, area_wkt = renderer.get_geographic_info(osmid)
        except ValueError:
            LOG.exception("Error determining compatible paper sizes")
            raise
        bbox = ocitysmap.coords.BoundingBox.parse_wkt(bbox_wkt)
    else:
        lat_upper_left = f.cleaned_data.get("lat_upper_left")
        lon_upper_left = f.cleaned_data.get("lon_upper_left")
        lat_bottom_right = f.cleaned_data.get("lat_bottom_right")
        lon_bottom_right = f.cleaned_data.get("lon_bottom_right")

        # Check we have correct floats
        if (lat_upper_left == None or lon_upper_left == None
            or lat_bottom_right == None or lon_bottom_right == None):
           return HttpResponseBadRequest("ERROR: Invalid arguments")

        bbox = ocitysmap.coords.BoundingBox(
            lat_upper_left, lon_upper_left,
            lat_bottom_right, lon_bottom_right)

    renderer_cls = ocitysmap.renderers.get_renderer_class_by_name(layout)
    paper_sizes = sorted(renderer_cls.get_compatible_paper_sizes(bbox),
                         key = lambda p: p[1])

    return HttpResponse(content=json_encode(paper_sizes),
                        mimetype='text/json')
Пример #5
0
def api_bbox(request, osm_id):
    """API handler that returns the bounding box from an OSM ID polygon."""

    try:
        osm_id = int(osm_id)
    except ValueError:
        return HttpResponseBadRequest("ERROR: Invalid arguments")

    renderer = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)
    try:
        bbox_wkt, area_wkt = renderer.get_geographic_info(osm_id)
        bbox = ocitysmap.coords.BoundingBox.parse_wkt(bbox_wkt)
        return HttpResponse(content=json_encode(bbox.as_json_bounds()),
                            mimetype='text/json')
    except:
        LOG.exception("Error calculating bounding box for OSM ID %d!" % osm_id)

    return HttpResponseBadRequest("ERROR: OSM ID %d not found!" % osm_id)
Пример #6
0
def main():
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

    # Known renderer names
    KNOWN_RENDERERS_NAMES = \
        list(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="")
    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=en_US.UTF-8). The map language is '
                           'driven by the system\' locale setting.',
                      default='en_US.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('--overlay', dest='overlays',
                      metavar='NAME',
                      help='comma separated list of overlay stylesheets to use. '
                      'Defaults to none')
    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", '
                           '"Best fit", one of the paper size names '
                           'defined in the configuration file, '
                           'or a custom size in millimeters like e.g. 100x100',
                      default='default')
    parser.add_option('--orientation', metavar='ORIENTATION',
                      help='set the output paper orientation. Either '
                            '"portrait" or "landscape". Defaults to portrait.',
                      default='portrait')
    parser.add_option('--poi-file', metavar='FILE',
                      help='provide a file containing POI information to '
                           'create an index instead of auto-generating it.')
    parser.add_option('--gpx-file', metavar='FILE',
                      help='a GPX track to be put on top of the rendered map.')
    parser.add_option('--umap-file', metavar='FILE',
                      help='a Umap export file to be put on top of the rendered map.')
    parser.add_option('--import-file', metavar='FILE', action='append',
                      help='import file, any of GPX, Umap, GeoJson or POI file, can be used multiple times')

    (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 as ex:
            parser.error("%s. Available stylesheets: %s."
                 % (ex, ', '.join(map(lambda s: s.name,
                      mapper.STYLESHEET_REGISTRY))))

    # Parse overlay stylesheet (defaults to none)
    overlays = []
    if options.overlays is not None:
        for overlay_name in options.overlays.split(","): 
            try:
                overlays.append(mapper.get_overlay_by_name(overlay_name))
            except LookupError as ex:
                parser.error("%s. Available overlay stylesheets: %s."
                     % (ex, ', '.join(map(lambda s: s.name,
                          mapper.OVERLAY_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 as 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))

    # check paper-format option if given
    paper_width = None
    paper_height = None
    if options.paper_format and options.paper_format != 'default':
        matches = re.search('^(\d+)[x\*](\d+)$', options.paper_format)
        if bool(matches):
            paper_width  = int(matches.group(1))
            paper_height = int(matches.group(2))
        else:
            paper_format_names = mapper.get_all_paper_size_names()
            for format_name in paper_format_names:
                name1 = format_name.lower().replace(" ","")
                name2 = options.paper_format.lower().replace(" ","")
                if name1 == name2:
                    options.paper_format = format_name
                    break
            if not options.paper_format in paper_format_names:
                parser.error("Requested paper format %s not found. Compatible paper formats are:\n\t%s."
                             % ( options.paper_format,
                                 ', '.join(paper_format_names)))

    # Determine actual paper size

    if paper_width and paper_height:
        min_width, min_height = cls_renderer.get_minimal_paper_size(bbox)
        if paper_width < min_width or paper_height < min_height:
            parser.error("Given paper size %dmm x %dmm is too small, minimal required size is: %dmm x %dmm" %
                         (paper_width, paper_height, min_width, min_height))
    else:
        compat_papers = cls_renderer.get_compatible_paper_sizes(bbox, mapper)
        if not compat_papers:
            parser.error("No paper size compatible with this rendering.")

        paper_descr = None
        if options.paper_format == 'default':
            for paper in compat_papers:
                if paper['default']:
                    paper_descr = p
                    break
        else:
            # Make sure the requested paper size is in list
            for paper in compat_papers:
                if paper['name'] == options.paper_format:
                    paper_descr = paper
                    break
        if not paper_descr:
            parser.error("Requested paper format not compatible with rendering. Compatible paper formats are:\n\t%s."
                         % ',\n\t'.join(map(lambda p: "%s (%.1fx%.1fcm²)"
                                            % (p['name'], p['width']/10., p['height']/10.),
                                            compat_papers)))
        assert paper_descr['portrait_ok'] or paper_descr['landscape_ok']

        # 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['portrait_ok']) or \
           (options.orientation == 'landscape' and not paper_descr['landscape_ok']):
            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
    rc.overlays     = overlays
    if (options.poi_file):
        rc.poi_file     = os.path.realpath(options.poi_file)
    if (options.gpx_file):
        rc.gpx_file     = os.path.realpath(options.gpx_file)
    if (options.umap_file):
        rc.umap_file    = os.path.realpath(options.umap_file)
    rc.import_files = []
    if options.import_file:
        for import_file in options.import_file:
            import_file = os.path.realpath(import_file)
            file_type = ocitysmap.guess_filetype(import_file)
            rc.import_files.append((file_type, import_file))
    if paper_width and paper_height:
        rc.paper_width_mm  = paper_width
        rc.paper_height_mm = paper_height
    elif options.orientation == 'portrait':
        rc.paper_width_mm  = paper_descr['width']
        rc.paper_height_mm = paper_descr['height']
    else:
        rc.paper_width_mm  = paper_descr['height']
        rc.paper_height_mm = paper_descr['width']

    # Go !...
    mapper.render(rc, cls_renderer.name, options.output_formats,
                  options.output_prefix)

    return 0
Пример #7
0
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=en_US.UTF-8). The map language is '
                      'driven by the system\' locale setting.',
                      default='en_US.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('--overlay',
                      dest='overlay',
                      metavar='NAME',
                      help='specify which overlay stylesheet to use. '
                      'Defaults to none')
    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))))
Пример #8
0
def main():
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')

    # Known renderer names
    KNOWN_RENDERERS_NAMES = \
        list(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="")
    parser.add_option('--osmids', dest='osmids', metavar='OSMIDS',
                      help='OSM IDs representing the polygon of the cities '
                      'to render.', type="string"),
    parser.add_option('-a', '--add-polys', dest='addpolys',
                      metavar='ADD_WKTPOLY',
                      help='add Polygon(s) (WktString).')
    parser.add_option('-r', '--remove-polys', dest='subpolys',
                      metavar='REMOVE_WKTPOLY',
                      help='remove polygon(s) (WktString).')
    parser.add_option('--insert-pages-before-index', dest='insert_pages_before_index',
                      metavar='INSERT_PAGES_BEFORE_INDEX',
                      help='insert x pages before index.')
    parser.add_option('--multipage-default-scale', dest='multipage_default_scale',
                      metavar='MULTIPAGE_DEFAULT_SCALE',
                      help='scaling.')
    parser.add_option('--map-first-page-number', dest='map_first_page_number',
                      metavar='MULTIPAGE_MAP_FIRST_PAGE',
                      help='first page-map-number')
    parser.add_option('-L', '--language', dest='language',
                      metavar='LANGUAGE_CODE',
                      help='language to use when generating the index '
                           '(default=en_US.UTF-8). The map language is '
                           'driven by the system\' locale setting.',
                      default='en_US.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('--overlay', dest='overlays',
                      metavar='NAME',
                      help='comma separated list of overlay stylesheets to use. '
                      'Defaults to none')
    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", '
                           '"Best fit", or one of the paper size names '
                           'defined in the configuration file',
                      default='default')
    parser.add_option('--orientation', metavar='ORIENTATION',
                      help='set the output paper orientation. Either '
                            '"portrait" or "landscape". Defaults to portrait.',
                      default='portrait')
    parser.add_option('--poi-file', metavar='FILE',
                      help='provide a file containing POI information to '
                           'create an index instead of auto-generating it.')
    parser.add_option('--gpx-file', metavar='FILE',
                      help='a GPX track to be put on top of the rendered map.')
    parser.add_option('--umap-file', metavar='FILE',
                      help='a Umap export file to be put on top of the rendered map.')

    (options, args) = parser.parse_args()
    if len(args):
        parser.print_help()
        return 1

    # Make sure either --add-polys or --osmids is given
    optcnt = 0
    for var in options.addpolys, options.osmids:
        if var:
            optcnt += 1

    if optcnt == 0:
        parser.error("--add-polys "
                     "and/or --osmids is mandatory")

    # Parse config file and instanciate main object
    mapper = ocitysmap.OCitySMap(
        [options.config_file or os.path.join(os.environ["HOME"], '.ocitysmap.conf')])

    bbox = None
    osmids = None

    # Parse OSM ids when given
    if options.osmids:
        try:
            osmids = list(map(int, options.osmids.split(",")))
            bbox = BoundingBox.parse_wkt(
                    mapper.get_geographic_info(osmids)[0])
        except LookupError:
            parser.error('No such OSM id: %d' % options.osmids)

    # Parse bounding box arguments when given
    #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 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 as ex:
            parser.error("%s. Available stylesheets: %s."
                 % (ex, ', '.join(map(lambda s: s.name,
                      mapper.STYLESHEET_REGISTRY))))

    # Parse overlay stylesheet (defaults to none)
    overlays = []
    if options.overlays is not None:
        for overlay_name in options.overlays.split(","): 
            try:
                overlays.append(mapper.get_overlay_by_name(overlay_name))
            except LookupError as ex:
                parser.error("%s. Available overlay stylesheets: %s."
                     % (ex, ', '.join(map(lambda s: s.name,
                          mapper.OVERLAY_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 as 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))

    # check paper-format option if given
    if options.paper_format and options.paper_format != 'default':
        paper_format_names = mapper.get_all_paper_size_names()
        if not options.paper_format in paper_format_names:
            parser.error("Requested paper format %s not found. Compatible paper formats are:\n\t%s."
                         % ( options.paper_format,
                             ', '.join(paper_format_names)))

    # Determine actual paper size
    compat_papers = cls_renderer.get_compatible_paper_sizes(bbox, mapper)
    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]: # TODO: why 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:\n\t%s."
             % ',\n\t'.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.osmids       = osmids or None # Force to None if absent
    rc.bounding_box = bbox
    rc.language     = options.language
    rc.stylesheet   = stylesheet
    rc.overlays     = overlays
    rc.poi_file     = options.poi_file
    rc.gpx_file     = options.gpx_file
    rc.umap_file     = options.umap_file
    
    if not options.insert_pages_before_index is None:
        rc.ins_pgs_bef_idx = int(options.insert_pages_before_index) or 0
    
    if not options.multipage_default_scale is None:
        rc.multipg_def_scale = int(options.multipage_default_scale) or Renderer.DEFAULT_MULTIPAGE_SCALE

    if not options.map_first_page_number is None and int(options.map_first_page_number) > 0:
        rc.multipg_frst_map_page = int(options.map_first_page_number)

    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]

    if options.addpolys:
        rc.addpolys = options.addpolys

    if options.subpolys:
        rc.subpolys = options.subpolys

    # Go !...
    mapper.render(rc, cls_renderer.name, options.output_formats,
                  options.output_prefix)

    return 0