[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Maposmatic-dev] [PATCH] ocitysmap: get rid of oc itysmap v1
From: |
Thomas Petazzoni |
Subject: |
[Maposmatic-dev] [PATCH] ocitysmap: get rid of oc itysmap v1 |
Date: |
Sat, 31 Mar 2012 00:37:23 +0200 |
OcitySMap2 has been in development for a while now, and has a much,
much larger feature set than the original ocitysmap. Let's keep only
ocitysmap2 in the Git repository. Those nostalgics of the old version
can always dig into the Git history.
Signed-off-by: Thomas Petazzoni <address@hidden>
---
ocitysmap-render | 158 -------
ocitysmap.conf-template | 40 --
ocitysmap/.gitignore | 1 -
ocitysmap/__init__.py | 32 --
ocitysmap/coords.py | 98 -----
ocitysmap/draw_utils.py | 244 -----------
ocitysmap/grid.py | 108 -----
ocitysmap/i18n.py | 817 -----------------------------------
ocitysmap/map_canvas.py | 398 -----------------
ocitysmap/street_index.py | 1050 ---------------------------------------------
ocitysmap/utils.py | 34 --
11 files changed, 0 insertions(+), 2980 deletions(-)
delete mode 100755 ocitysmap-render
delete mode 100644 ocitysmap.conf-template
delete mode 100644 ocitysmap/.gitignore
delete mode 100644 ocitysmap/__init__.py
delete mode 100644 ocitysmap/coords.py
delete mode 100644 ocitysmap/draw_utils.py
delete mode 100644 ocitysmap/grid.py
delete mode 100644 ocitysmap/i18n.py
delete mode 100644 ocitysmap/map_canvas.py
delete mode 100644 ocitysmap/street_index.py
delete mode 100644 ocitysmap/utils.py
diff --git a/ocitysmap-render b/ocitysmap-render
deleted file mode 100755
index 881e0e9..0000000
--- a/ocitysmap-render
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/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.1'
-
-import logging
-import optparse
-import sys, os
-
-from ocitysmap.street_index import OCitySMap, BaseOCitySMapError
-from ocitysmap.coords import BoundingBox
-
-
-def main():
- logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
-
- usage = '%prog [options] [-c <cityname>|-b <lat1,long1
lat2,long2>|--polygon-osmid <osmid>]'
- parser = optparse.OptionParser(usage=usage,
- version='%%prog %s' % __version__)
- parser.add_option('-p', '--prefix', dest='output_prefix', metavar='PREFIX',
- help='Specify the prefix of generated files. '
- 'Defaults to "citymap"',
- default='citymap')
- parser.add_option('-f', '--format', dest='output_format', metavar='FMT',
- help='Specify the output formats. Supported file '
- 'formats: svg, svgz, pdf, ps, png, and csv for '
- 'the index, xml for the map. Defaults to '
- 'SVGZ. 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=None)
- parser.add_option('-c', '--city', dest='city_name', metavar='CITY_NAME',
- help='Specify the name of te city to map',
- default=None)
- parser.add_option('-C', '--config', dest='config_file', metavar='FILE',
- help='Specify the location of the config file')
- parser.add_option('--no-frame', dest='no_frame', action='store_true',
- default=False,
- help="Don't insert the map and index inside a frame")
- parser.add_option('-z', '--zoom-factor',
- metavar='[0-18]', help='Zoom factor for the'
- 'rendering (default=16)', type='int', default =16)
- parser.add_option('-b', '--bounding-box', dest='bbox', nargs=2,
- metavar='LAT1,LON1 LAT2,LON2', help='Bounding box')
- parser.add_option('', '--polygon-osmid', dest='osmid', metavar='OSMID',
- help='OSM id representing the polygon of the city to
render'),
- parser.add_option('-l', '--language', dest='language',
- metavar='LANGUAGE_CODE',
- help='Language to use when generating the index'
- ' (default=fr_FR.UTF-8)',
- default='fr_FR.UTF-8')
-
- (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.city_name, options.bbox, options.osmid:
- if var:
- optcnt += 1
-
- if optcnt == 0:
- parser.error("One of --city or --bounding-box or --osmid is mandatory")
-
- if optcnt > 1:
- parser.error("--city or --bounding-box or --osmid are exclusive")
-
- # Determine title
- if options.no_frame:
- title = None
- elif options.output_title is not None:
- title = options.output_title
- elif options.city_name is not None:
- title = options.city_name
- else:
- title = "City's Map"
-
- # Parse zoom factor
- try:
- options.zoom_factor = int(options.zoom_factor)
- except ValueError:
- parser.error("Invalid zoom factor: %s" % options.zoom_factor)
- if options.zoom_factor < 0 or options.zoom_factor > 18:
- parser.error("Invalid zoom factor: %s" % options.zoom_factor)
-
- if not options.output_format:
- options.output_format = ['svgz']
- options.output_format = set(options.output_format)
-
- # Parse bounding box arguments
- boundingbox = None
- if options.bbox:
- try:
- boundingbox = BoundingBox.parse(options.bbox)
- except ValueError:
- sys.stderr.write('ERROR: Invalid city bounding box!\n')
- return 1
-
- if options.city_name:
- city_name = unicode(options.city_name.decode('utf-8'))
- else:
- city_name = None
-
- osmid = None
- if options.osmid:
- try:
- osmid = int(options.osmid)
- except ValueError:
- sys.stderr.write('ERROR: Invalid polygon OSM id!\n')
- return 1
-
- try:
- prefix = 'ocitysmap_render_%d' % os.getpid()
- renderer = OCitySMap(options.config_file, prefix, city_name,
- boundingbox, osmid, options.language)
- except BaseOCitySMapError, e:
- sys.stderr.write('ERROR: %s\n' % e)
- return 1
- except KeyboardInterrupt:
- sys.stderr.write(' Aborting.\n')
-
- _map = renderer.render_map_into_files(title,
- options.output_prefix,
- options.output_format,
- "zoom:%d" % options.zoom_factor)
-
- renderer.render_index(title, options.output_prefix,
- options.output_format,
- _map.width, _map.height)
-
- return 0
-
-if __name__ == '__main__':
- sys.exit(main())
diff --git a/ocitysmap.conf-template b/ocitysmap.conf-template
deleted file mode 100644
index 4673852..0000000
--- a/ocitysmap.conf-template
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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/>.
-
-# PostGIS datasource.
-[datasource]
-host: localhost
-user: maposmatic
-password: ereiamjh
-dbname: maposmatic
-# SQL request timeout, in minutes. Defaults to 15min.
-# request_timeout: 10
-
-[rendering]
-# List of available stylesheets, each needs to be described by an eponymous
-# configuration section in this file.
-available_stylesheets: stylesheet_osm
-
-# The default Mapnik stylesheet.
-[stylesheet_osm]
-name: Default
-description: The default OSM style
-path: /path/to/mapnik/osm.xml
diff --git a/ocitysmap/.gitignore b/ocitysmap/.gitignore
deleted file mode 100644
index 0d20b64..0000000
--- a/ocitysmap/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.pyc
diff --git a/ocitysmap/__init__.py b/ocitysmap/__init__.py
deleted file mode 100644
index fce3b38..0000000
--- a/ocitysmap/__init__.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# 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/>.
-
-"""OCitySMap.
-
-Provide documentation here.
-"""
-
-__author__ = 'The Hackfest2009 team'
-__version__ = '0.1'
-
-raise DeprecationWarning, 'OCitysmap v%s will soon disappear!' % __version__
diff --git a/ocitysmap/coords.py b/ocitysmap/coords.py
deleted file mode 100644
index 9d10f48..0000000
--- a/ocitysmap/coords.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# 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/>.
-
-import math
-
-
-EARTH_RADIUS = 6370986 # meters
-
-
-class BoundingBox:
- def __init__(self, lat1, long1, lat2, long2):
- (self._lat1, self._long1) = float(lat1), float(long1)
- (self._lat2, self._long2) = float(lat2), float(long2)
-
- # make sure lat1/long1 is the upper left, and the others the btm right
- if (self._lat1 < self._lat2):
- self._lat1, self._lat2 = self._lat2, self._lat1
- if (self._long1 > self._long2):
- self._long1, self._long2 = self._long2, self._long1
-
- @staticmethod
- def parse_wkt(wkt):
- coords = [p.split(' ') for p in wkt[9:].split(',')]
- return BoundingBox(coords[1][1], coords[1][0],
- coords[3][1], coords[3][0])
-
- @staticmethod
- def parse(points):
- (lat1, long1) = points[0].split(',')
- (lat2, long2) = points[1].split(',')
- return BoundingBox(lat1, long1, lat2, long2)
-
- def get_top_left(self):
- return (self._lat1, self._long1)
-
- def get_bottom_right(self):
- return (self._lat2, self._long2)
-
- def ptstr(self, point):
- return '%.4f,%.4f' % (point[0], point[1])
-
- def __str__(self):
- return '(%s %s)' % (self.ptstr(self.get_top_left()),
- self.ptstr(self.get_bottom_right()))
-
- def spheric_sizes(self):
- """Metric distances at the bounding box top latitude.
- Returns the tuple (metric_size_lat, metric_size_long)
- """
- delta_lat = abs(self._lat1 - self._lat2)
- delta_long = abs(self._long1 - self._long2)
- radius_lat = EARTH_RADIUS * math.cos(math.radians(self._lat1))
- return (EARTH_RADIUS * math.radians(delta_lat),
- radius_lat * math.radians(delta_long))
-
- def create_expanded(self, dlat, dlong):
- """Return a new bbox of the same size + dlat/dlong added
- on the top-left sides"""
- return BoundingBox(self._lat1 + dlat, self._long1 - dlong,
- self._lat2, self._long2)
-
- def get_pixel_size_for_zoom_factor(self, zoom = 17):
- """Return the size in pixels (tuple width,height) needed to
- render the bounding box at the given zoom factor"""
- delta_long = abs(self._long1 - self._long2)
- # 2^zoom tiles (1 tile = 256 pix) for the whole earth
- pix_x = delta_long * (2 ** (zoom + 8)) / 360
-
- # http://en.wikipedia.org/wiki/Mercator_projection
- def yplan(lat):
- return math.log(math.tan(math.pi/4. + math.radians(lat)/2.))
-
- # OSM maps are drawn between -85 deg and + 85, the whole amplitude
- # is 256*2^(zoom)
- pix_y = (yplan(self._lat1) - yplan(self._lat2)) \
- * (2 ** (zoom + 7)) / yplan(85)
-
- return (int(math.ceil(pix_x)), int(math.ceil(pix_y)))
diff --git a/ocitysmap/draw_utils.py b/ocitysmap/draw_utils.py
deleted file mode 100644
index 095c311..0000000
--- a/ocitysmap/draw_utils.py
+++ /dev/null
@@ -1,244 +0,0 @@
-# -*- 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/>.
-
-
-import cairo, logging, pango, pangocairo
-import datetime
-
-l = logging.getLogger('ocitysmap')
-
-
-def enclose_in_frame(renderer, insurf_w, insurf_h,
- title,
- outsurf, outsurf_w, outsurf_h, out_margin):
- """
- Fill the given surface with the contents of another one and a
- frame around it
- @param renderer (function : cairo_context -> None) Function
- drawing inside the frame
- @param insurf_w/h (int) width/height of the inside rendering
- @param title (string) title to write on the frame
- @param outsurf (cairo surface) surface to draw the whole thing into
- @param outsurf_w/h (int) width/height of the resulting framed image
- @param out_margin (int) size of the margin around the inner image
- """
-
- ctx = cairo.Context(outsurf)
-
- ctx.save()
-
- # Reset background
- ctx.set_source_rgb (1, 1, 1)
- ctx.set_operator (cairo.OPERATOR_OVER)
- ctx.paint()
-
- # Default pen color
- ctx.set_source_rgb (0, 0, 0)
-
- # Draw the surface with a margin around it
- ctx.save()
- ctx.translate(out_margin, out_margin)
- indest_w = outsurf_w - 2.*out_margin
- indest_h = outsurf_h - 2.*out_margin
- ctx.scale(indest_w / insurf_w, indest_h / insurf_h)
- renderer(ctx)
- ctx.restore()
-
- # Draw the title
- ctx.save()
-
- pc = pangocairo.CairoContext(ctx)
- layout = pc.create_layout()
- fd = pango.FontDescription("DejaVu")
-
- # Do a first test with a font size of out_margin
- fd.set_size(out_margin * pango.SCALE)
- layout.set_font_description(fd)
- layout.set_text(title)
- width = layout.get_size()[0] / pango.SCALE
- height = layout.get_size()[1] / pango.SCALE
-
- # Compute the ratio to be applied on the font size to make the
- # text fit in the available space
- if height > out_margin:
- hratio = float(out_margin) / height
- else:
- hratio = 1.
-
- max_width = indest_w * .8
- if width > max_width:
- wratio = max_width / width
- else:
- wratio = 1.
-
- ratio = min(wratio, hratio)
-
- # Render the text at the appropriate size and location
- fd.set_size(int(out_margin * ratio * pango.SCALE))
- layout.set_font_description(fd)
- width = layout.get_size()[0] / pango.SCALE
- f = layout.get_context().load_font(fd)
- fm = f.get_metrics()
- ascent = fm.get_ascent() / pango.SCALE
- ctx.move_to(out_margin * 2., out_margin * (.5 + .35 * ratio) - ascent)
- pc.show_layout(layout)
-
- ctx.restore()
-
- # Draw the rounded rectangle
- ctx.save()
- ctx.set_line_width(max(out_margin/9., 2.))
- ctx.move_to (out_margin * 2 + width + out_margin/2., out_margin / 2.)
- ctx.line_to (outsurf_w - out_margin, out_margin / 2.)
- ctx.rel_curve_to(0,0, out_margin/2., 0, out_margin/2., out_margin/2.)
- ctx.rel_line_to (0, outsurf_h - 2*out_margin)
- ctx.rel_curve_to(0,0, 0, out_margin/2., -out_margin/2., out_margin/2.)
- ctx.rel_line_to(-(outsurf_w - 2*out_margin), 0)
- ctx.rel_curve_to(0,0, -out_margin/2.,0, -out_margin/2., -out_margin/2.)
- ctx.rel_line_to(0, -(outsurf_h - 2*out_margin))
- ctx.rel_curve_to(0,0, 0,-out_margin/2., out_margin/2.,-out_margin/2.)
- ctx.line_to(out_margin*1.5, out_margin/2.)
- ctx.stroke()
- ctx.restore()
-
- ctx.restore()
- return outsurf
-
-
-def add_logo(ctx, paperwidth, paperheight, logo_path, copyright_notice=None):
- copyright_notice = copyright_notice or \
- (u'© %(year)d MapOSMatic/ocitysmap authors. '
- u'Map data © %(year)d OpenStreetMap.org '
- u'and contributors (CC-BY-SA)' % {'year': datetime.date.today().year})
-
- # Open logo file
- png = None
- if logo_path:
- try:
- f = open(logo_path, 'rb')
- png = cairo.ImageSurface.create_from_png(f)
- l.debug('Using copyright logo: %s' % logo_path)
- f.close()
- except Exception, ex:
- l.warning('Cannot open logo file: %s' % ex)
- except:
- l.warning('Cannot open logo file.')
-
- # Create a virtual buffer containing the png and the copyright notice
- ctx.push_group()
- ctx.move_to(0,0)
- xlat1, ylat1 = ctx.get_current_point()
-
- # Draw the png in the buffer
- if png:
- ctx.set_source_surface(png)
- ctx.paint()
- ctx.rel_move_to(png.get_width(), 0)
- else:
- ctx.rel_move_to(0, font_size*1.5)
-
- # Write the notice in the buffer
- ctx.set_source_rgb(0, 0, 0)
-
- # Set up a layout
- pc = pangocairo.CairoContext(ctx)
- fd = pango.FontDescription("DejaVu")
- fd.set_size(14 * pango.SCALE)
- layout = pc.create_layout()
- layout.set_font_description(fd)
-
- # Render copyright notice
- layout.set_text(copyright_notice)
- textwidth = layout.get_size()[0] / pango.SCALE
- pc.show_layout(layout)
-
- ctx.move_to(png.get_width(), png.get_height() * 0.33)
-
- # Render date of rendering
- today = datetime.date.today()
- gendatetext = _("This map has been rendered on %s and may be incomplete or
inaccurate.") % today.strftime("%d %b %Y")
-
- layout.set_text(gendatetext)
- textwidth = max(textwidth, layout.get_size()[0] / pango.SCALE)
- pc.show_layout(layout)
-
- # Render contribution text
- ctx.move_to(png.get_width(), png.get_height() * 0.66)
- contribute_text = _("You can contribute to improve this map. See
http://wiki.openstreetmap.org")
-
- layout.set_text(contribute_text)
- textwidth = max(textwidth, layout.get_size()[0] / pango.SCALE)
- pc.show_layout(layout)
-
- # Determine the size of the virtual buffer
- if png:
- vbufheight = png.get_height()
- else:
- vbufheight = font_size * 2.5
-
- vbufwidth = png.get_width() + textwidth
-
- grp = ctx.pop_group()
- # Virtual buffer done.
-
- # Display the buffer inside the surface, taking its size into account
- ctx.translate(paperwidth - vbufwidth - 10,
- paperheight - vbufheight - 10)
- ctx.set_source(grp)
-
- # Make it transparent
- ctx.paint_with_alpha(.5)
-
-
-if __name__ == "__main__":
- inner_W, inner_H, margin = 1024, 768, 50
-
- inside_area = cairo.ImageSurface(cairo.FORMAT_RGB24, inner_W, inner_H)
- ctx = cairo.Context(inside_area)
-
- # Fill the inside with something
- ctx.set_source_rgb (1, .1, .1)
- ctx.set_operator (cairo.OPERATOR_OVER)
- ctx.paint()
-
- # Add the logo and save the result as inside.png
- add_logo(ctx, inner_W, inner_H, "../Openstreetmap_logo.png")
- f = open("inside.png", 'wb')
- inside_area.write_to_png(f)
- f.close()
-
- # Add a frame and save the result as outside.png
- def my_render(x):
- x.set_source_surface(inside_area)
- x.paint()
-
- outside_area = cairo.ImageSurface(cairo.FORMAT_RGB24,
- inner_W+2*margin,
- inner_H+2*margin)
- enclose_in_frame(my_render, inner_W, inner_H, "badidonc",
- outside_area, inner_W+2*margin,
- inner_H+2*margin, margin)
-
- f = open("outside.png", 'wb')
- outside_area.write_to_png(f)
- f.close()
diff --git a/ocitysmap/grid.py b/ocitysmap/grid.py
deleted file mode 100644
index 633de1e..0000000
--- a/ocitysmap/grid.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# 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/>.
-
-
-import math, logging
-import map_canvas, utils, coords
-
-l = logging.getLogger('ocitysmap')
-
-class GridDescriptor:
- def __init__(self, bbox, db):
- self.bbox = bbox
- height, width = bbox.spheric_sizes()
-
- # Compute number of squares, assumming a size of 500 meters
- # per square
- self.width_square_count = width / 500
- self.height_square_count = height / 500
-
- # Compute the size in angles of the squares
- self.width_square_angle = (abs(bbox.get_top_left()[1] -
- bbox.get_bottom_right()[1]) /
- self.width_square_count)
- self.height_square_angle = (abs(bbox.get_top_left()[0] -
- bbox.get_bottom_right()[0]) /
- self.height_square_count)
-
- # Compute the lists of longitudes and latitudes of the
- # horizontal and vertical lines delimiting the square
- self.vertical_lines = [bbox.get_top_left()[1] +
- x * self.width_square_angle
- for x in xrange(0,
int(math.floor(self.width_square_count )) + 1)]
- self.horizontal_lines = [bbox.get_top_left()[0] -
- x * self.height_square_angle
- for x in xrange(0,
int(math.floor(self.height_square_count)) + 1)]
-
- # Compute the lists of labels
- self.vertical_labels = [utils.gen_vertical_square_label(x)
- for x in xrange(0,
int(math.ceil(self.width_square_count)))]
- self.horizontal_labels = [utils.gen_horizontal_square_label(x)
- for x in xrange(0,
int(math.ceil(self.height_square_count)))]
- l.debug("vertical lines: %s" % self.vertical_lines)
- l.debug("horizontal lines: %s" % self.horizontal_lines)
- l.debug("vertical labels: %s" % self.vertical_labels)
- l.debug("horizontal labels: %s" % self.horizontal_labels)
-
- def generate_shape_file(self, filename, bbox):
- g = map_canvas.GridFile(bbox, filename)
- for v in self.vertical_lines:
- g.add_vert_line(v)
- for h in self.horizontal_lines:
- g.add_horiz_line(h)
- g.flush()
- return g
-
- def generate_scale_shape_file(self, filename, base_lat):
- """
- Returns a tuple (gridfile, lat, long) of the scale widget, or
- None when not enough room for scale
- """
- if len(self.horizontal_lines) < 2 or len(self.vertical_lines) < 2:
- return None
-
- height_lat = (self.horizontal_lines[-2] - self.horizontal_lines[-1])/30
-
- # Make sure there is enough room between the last horiz line
- # and the bottom:
- if base_lat + (self.horizontal_lines[-1] - base_lat) / 3. \
- + 2*height_lat < self.horizontal_lines[-1]:
- line_lat = base_lat + (self.horizontal_lines[-1] - base_lat) / 3.
- else:
- # Nope...
- line_lat = self.horizontal_lines[-1] \
- + (self.horizontal_lines[-2] - self.horizontal_lines[-1]) \
- / 5.
-
- bbox = coords.BoundingBox(line_lat + height_lat,
- self.vertical_lines[0]-1.e-5,
- line_lat - height_lat,
- self.vertical_lines[1]+1.e-5)
-
- g = map_canvas.GridFile(bbox, filename) # bbox, filename)
- g.add_horiz_line(line_lat)
- g.add_vert_line(self.vertical_lines[0])
- g.add_vert_line(self.vertical_lines[1])
- g.flush()
- return (g, line_lat + height_lat,
- (self.vertical_lines[0] + self.vertical_lines[1]) / 2)
diff --git a/ocitysmap/i18n.py b/ocitysmap/i18n.py
deleted file mode 100644
index efafb4b..0000000
--- a/ocitysmap/i18n.py
+++ /dev/null
@@ -1,817 +0,0 @@
-# -*- 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/>.
-
-import re
-import gettext
-
-def _install_language(language, locale_path):
- t = gettext.translation(domain='ocitysmap',
- localedir=locale_path,
- languages=[language],
- fallback=True)
- t.install(unicode=True)
-
-class i18n:
- """Functions needed to be implemented for a new language.
- See i18n_fr_FR_UTF8 below for an example. """
- def language_code(self):
- pass
-
- def user_readable_street(self, name):
- pass
-
- def first_letter_equal(self, a, b):
- pass
-
- def isrtl(self):
- return False
-
-class i18n_template_code_CODE(i18n):
- def __init__(self, language, locale_path):
- """Install the _() function for the chosen locale other
- object initialisation"""
- self.language = str(language) # FIXME: why do we have unicode here?
- _install_language(language, locale_path)
-
- def language_code(self):
- """returns the language code of the specific language
- supported, e.g. fr_FR.UTF-8"""
- return self.language
-
- def user_readable_street(self, name):
- """ transforms a street name into a suitable form for
- the map index, e.g. Paris (Rue de) for French"""
- return name
-
- def first_letter_equal(self, a, b):
- """returns True if the letters a and b are equal in the map index,
- e.g. É and E are equals in French map index"""
- return a == b
-
- def isrtl(self):
- return False
-
-
-class i18n_fr_generic(i18n):
- APPELLATIONS = [ u"Accès", u"Allée", u"Allées", u"Autoroute", u"Avenue",
u"Barrage",
- u"Boulevard", u"Carrefour", u"Chaussée", u"Chemin",
- u"Cheminement", u"Cale", u"Cales", u"Cavée", u"Cité",
- u"Clos", u"Coin", u"Côte", u"Cour", u"Cours", u"Descente",
- u"Degré", u"Escalier",
- u"Escaliers", u"Esplanade", u"Funiculaire",
- u"Giratoire", u"Hameau", u"Impasse", u"Jardin",
- u"Jardins", u"Liaison", u"Lotissement", u"Mail",
u"Montée", u"Môle",
- u"Parc", u"Passage", u"Passerelle", u"Passerelles",
- u"Place", u"Placette", u"Pont", u"Promenade",
- u"Petite Avenue", u"Petite Rue", u"Quai",
- u"Rampe", u"Rang", u"Résidence", u"Rond-Point",
- u"Route forestière", u"Route", u"Rue", u"Ruelle",
- u"Square", u"Sente", u"Sentier", u"Sentiers",
u"Terre-Plein",
- u"Télécabine", u"Traboule", u"Traverse", u"Tunnel",
- u"Venelle", u"Villa", u"Virage"
- ]
- DETERMINANTS = [ u" des", u" du", u" de la", u" de l'",
- u" de", u" d'", u" aux", u""
- ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_it_generic(i18n):
- APPELLATIONS = [ u"Via", u"Viale", u"Piazza", u"Scali", u"Strada",
u"Largo",
- u"Corso", u"Viale", u"Calle", u"Sottoportico",
- u"Sottoportego", u"Vicolo", u"Piazzetta" ]
- DETERMINANTS = [ u" delle", u" dell'", u" dei", u" degli",
- u" della", u" del", u" di", u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_es_generic(i18n):
- APPELLATIONS = [ u"Avenida", u"Avinguda", u"Calle", u"Callejón",
- u"Calzada", u"Camino", u"Camí", u"Carrer", u"Carretera",
- u"Glorieta", u"Parque", u"Pasaje", u"Pasarela", u"Paseo", u"Plaza",
- u"Plaça", u"Privada", u"Puente", u"Ronda", u"Salida", u"Travesia" ]
- DETERMINANTS = [ u" de", u" de la", u" del", u" de las",
- u" dels", u" de los", u" d'", u" de l'", u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
- N_ACCENT = re.compile(ur"[ñ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- s = self.N_ACCENT.sub("n", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_ca_generic(i18n):
-
- APPELLATIONS = [ # Catalan
- u"Autopista", u"Autovia", u"Avinguda",
- u"Baixada", u"Barranc", u"Barri", u"Barriada",
- u"Biblioteca", u"Carrer", u"Carreró", u"Carretera",
- u"Cantonada", u"Església", u"Estació", u"Hospital",
- u"Monestir", u"Monument", u"Museu", u"Passatge",
- u"Passeig", u"Plaça", u"Planta", u"Polígon",
- u"Pujada", u"Rambla", u"Ronda", u"Travessera",
- u"Travessia", u"Urbanització", u"Via",
- u"Avenida", u"Calle", u"Camino", u"Plaza",
-
- # Spanish (being distinct from Catalan)
- u"Acceso", u"Acequia", u"Alameda", u"Alquería",
- u"Andador", u"Angosta", u"Apartamentos", u"Apeadero",
- u"Arboleda", u"Arrabal", u"Arroyo", u"Autovía",
- u"Avenida", u"Bajada", u"Balneario", u"Banda",
- u"Barranco", u"Barranquil", u"Barrio", u"Bloque",
- u"Brazal", u"Bulevar", u"Calle", u"Calleja",
- u"Callejón", u"Callejuela", u"Callizo", u"Calzada",
- u"Camino", u"Camping", u"Cantera", u"Cantina",
- u"Cantón", u"Carrera", u"Carrero", u"Carreterín",
- u"Carretil", u"Carril", u"Caserío", u"Chalet",
- u"Cinturón", u"Circunvalación", u"Cobertizo",
- u"Colonia", u"Complejo", u"Conjunto", u"Convento",
- u"Cooperativa", u"Corral", u"Corralillo", u"Corredor",
- u"Cortijo", u"Costanilla", u"Costera", u"Cuadra",
- u"Cuesta", u"Dehesa", u"Demarcación", u"Diagonal",
- u"Diseminado", u"Edificio", u"Empresa", u"Entrada",
- u"Escalera", u"Escalinata", u"Espalda", u"Estación",
- u"Estrada", u"Explanada", u"Extramuros", u"Extrarradio",
- u"Fábrica", u"Galería", u"Glorieta", u"Gran Vía",
- u"Granja", u"Hipódromo", u"Jardín", u"Ladera",
- u"Llanura", u"Malecón", u"Mercado", u"Mirador",
- u"Monasterio", u"Muelle", u"Núcleo", u"Palacio",
- u"Pantano", u"Paraje", u"Parque", u"Particular",
- u"Partida", u"Pasadizo", u"Pasaje", u"Paseo",
- u"Paseo marítimo", u"Pasillo", u"Plaza", u"Plazoleta",
- u"Plazuela", u"Poblado", u"Polígono", u"Polígono
industrial",
- u"Portal", u"Pórtico", u"Portillo", u"Prazuela",
- u"Prolongación", u"Pueblo", u"Puente", u"Puerta",
- u"Puerto", u"Punto kilométrico", u"Rampla",
- u"Residencial", u"Ribera", u"Rincón", u"Rinconada",
- u"Sanatorio", u"Santuario", u"Sector", u"Sendera",
- u"Sendero", u"Subida", u"Torrente", u"Tránsito",
- u"Transversal", u"Trasera", u"Travesía", u"Urbanización",
- u"Vecindario", u"Vereda", u"Viaducto", u"Viviendas",
-
- # French (being distinct from Catalan and Spanish)
- u"Accès", u"Allée", u"Allées", u"Autoroute", u"Avenue",
u"Barrage",
- u"Boulevard", u"Carrefour", u"Chaussée", u"Chemin",
- u"Cheminement", u"Cale", u"Cales", u"Cavée", u"Cité",
- u"Clos", u"Coin", u"Côte", u"Cour", u"Cours", u"Descente",
- u"Degré", u"Escalier",
- u"Escaliers", u"Esplanade", u"Funiculaire",
- u"Giratoire", u"Hameau", u"Impasse", u"Jardin",
- u"Jardins", u"Liaison", u"Mail", u"Montée", u"Môle",
- u"Parc", u"Passage", u"Passerelle", u"Passerelles",
- u"Place", u"Placette", u"Pont", u"Promenade",
- u"Petite Avenue", u"Petite Rue", u"Quai",
- u"Rampe", u"Rang", u"Résidence", u"Rond-Point",
- u"Route forestière", u"Route", u"Rue", u"Ruelle",
- u"Square", u"Sente", u"Sentier", u"Sentiers",
u"Terre-Plein",
- u"Télécabine", u"Traboule", u"Traverse", u"Tunnel",
- u"Venelle", u"Villa", u"Virage"
- ]
-
- DETERMINANTS = [ # Catalan
- u" de", u" de la", u" del", u" dels", u" d'",
- u" de l'", u" de sa", u" de son", u" de s'",
- u" de ses", u" d'en", u" de na", u" de n'",
-
- # Spanish (being distinct from Catalan)
- u" de las", u" de los",
-
- # French (being distinct from Catalan and Spanish)
- u" du",
- u""]
-
-
- DETERMINANTS = [ u" de", u" de la", u" del", u" de las",
- u" dels", u" de los", u" d'", u" de l'", u"de sa", u"de
son", u"de s'",
- u"de ses", u"d'en", u"de na", u"de n'", u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
- N_ACCENT = re.compile(ur"[ñ]", re.IGNORECASE | re.UNICODE)
- C_ACCENT = re.compile(ur"[ç]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- s = self.N_ACCENT.sub("n", s)
- s = self.C_ACCENT.sub("c", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_pt_br_generic(i18n):
- APPELLATIONS = [ u"Aeroporto", u"Alameda", u"Área", u"Avenida",
- u"Campo", u"Chácara", u"Colônia",
- u"Condomínio", u"Conjunto", u"Distrito", u"Esplanada",
u"Estação",
- u"Estrada", u"Favela", u"Fazenda",
- u"Feira", u"Jardim", u"Ladeira", u"Lago",
- u"Lagoa", u"Largo", u"Loteamento", u"Morro", u"Núcleo",
- u"Parque", u"Passarela", u"Pátio", u"Praça", u"Quadra",
- u"Recanto", u"Residencial", u"Rua",
- u"Setor", u"Sítio", u"Travessa", u"Trecho", u"Trevo",
- u"Vale", u"Vereda", u"Via", u"Viaduto", u"Viela",
- u"Vila" ]
- DETERMINANTS = [ u" do", u" da", u" dos", u" das", u"" ]
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_ar_generic(i18n):
- APPELLATIONS = [ u"شارع", u"طريق", u"زقاق", u"نهج", u"جادة",
- u"ممر", u"حارة",
- u"كوبري", u"كوبرى", u"جسر", u"مطلع", u"منزل",
- u"مفرق", u"ملف", u"تقاطع",
- u"ساحل",
- u"ميدان", u"ساحة", u"دوار" ]
-
- DETERMINANTS = [ u" ال", u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- A_ACCENT = re.compile(ur"[اإآ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.A_ACCENT.sub("أ", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
- def isrtl(self):
- return True
-
-class i18n_ru_generic(i18n):
- APPELLATIONS = [ u"ул", u"бул", u"пер", u"пр", u"улица", u"бульвар",
u"проезд",
- u"проспект", u"площадь", u"сквер", u"парк" ]
- # only "ул." and "пер." are recommended shortenings, however other words
can
- # occur shortened.
- #
- # http://bit.ly/6ASISp (OSM wiki)
- #
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)\.?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS)), re.IGNORECASE
- | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- # usually, there are no accents in russian names, only "ё" sometimes,
but
- # not as first letter
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_nl_generic(i18n):
- #
- # Dutch streets are often named after people and include a title.
- # The title will be captured as part of the <prefix>
- #
- APPELLATIONS = [ u"St.", u"Sint", u"Ptr.", u"Pater",
- u"Prof.", u"Professor", u"Past.", u"Pastoor",
- u"Pr.", u"Prins", u"Prinses", u"Gen.", u"Generaal",
- u"Mgr.", u"Monseigneur", u"Mr.", u"Meester",
- u"Burg.", u"Burgermeester", u"Dr.", u"Dokter",
- u"Ir.", u"Ingenieur", u"Ds.", u"Dominee", u"Deken",
- u"Drs.",
- # counting words before street name,
- # e.g. "1e Walstraat" => "Walstraat (1e)"
- u"\d+e",
- u"" ]
- #
- # Surnames in Dutch streets named after people tend to have the middle name
- # listed after the rest of the surname,
- # e.g. "Prins van Oranjestraat" => "Oranjestraat (Prins van)"
- # Likewise, articles are captured as part of the prefix,
- # e.g. "Den Urling" => "Urling (Den)"
- #
- DETERMINANTS = [ u"\s?van der", u"\s?van den", u"\s?van de", u"\s?van",
- u"\s?Den", u"\s?D'n", u"\s?D'", u"\s?De", u"\s?'T",
u"\s?Het",
- u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)),
- re.IGNORECASE | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- #
- # Make sure name actually contains something,
- # the PREFIX_REGEXP.match fails on zero-length strings
- #
- if len(name) == 0:
- return name
-
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- matches = self.PREFIX_REGEXP.match(name)
- #
- # If no prefix was captured, that's okay. Don't substitute
- # the name however, "<name> ()" looks silly
- #
- if matches == None:
- return name
-
- if matches.group('prefix'):
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_hr_HR(i18n):
- # for _upper_unaccent_string
- C_ACCENT = re.compile(ur"[ćč]", re.IGNORECASE | re.UNICODE)
- D_ACCENT = re.compile(ur"đ|dž", re.IGNORECASE | re.UNICODE)
- N_ACCENT = re.compile(ur"nj", re.IGNORECASE | re.UNICODE)
- L_ACCENT = re.compile(ur"lj", re.IGNORECASE | re.UNICODE)
- S_ACCENT = re.compile(ur"š", re.IGNORECASE | re.UNICODE)
- Z_ACCENT = re.compile(ur"ž", re.IGNORECASE | re.UNICODE)
-
- def _upper_unaccent_string(self, s):
- s = self.C_ACCENT.sub("c", s)
- s = self.D_ACCENT.sub("d", s)
- s = self.N_ACCENT.sub("n", s)
- s = self.L_ACCENT.sub("l", s)
- s = self.S_ACCENT.sub("s", s)
- s = self.Z_ACCENT.sub("z", s)
- return s.upper()
-
- def __init__(self, language, locale_path):
- """Install the _() function for the chosen locale other
- object initialisation"""
- self.language = str(language) # FIXME: why do we have unicode here?
- _install_language(language, locale_path)
-
- def language_code(self):
- """returns the language code of the specific language
- supported, e.g. fr_FR.UTF-8"""
- return self.language
-
- def user_readable_street(self, name):
- """ transforms a street name into a suitable form for
- the map index, e.g. Paris (Rue de) for French"""
- return name
-
- ## FIXME: only first letter does not work for Croatian digraphs (dž, lj,
nj)
- def first_letter_equal(self, a, b):
- """returns True if the letters a and b are equal in the map index,
- e.g. É and E are equals in French map index"""
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_pl_generic(i18n):
-
- APPELLATIONS = [ u"Dr.", u"Doktora", u"Ks.", u"Księdza",
- u"Generała", u"Gen.",
- u"Aleja", u"Plac", u"Pl.",
- u"Rondo", u"rondo", u"Profesora",
- u"Prof.",
- u"" ]
-
- DETERMINANTS = [ u"\s?im.", u"\s?imienia", u"\s?pw.",
- u"" ]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)),
- re.IGNORECASE | re.UNICODE)
-
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- #
- # Make sure name actually contains something,
- # the PREFIX_REGEXP.match fails on zero-length strings
- #
- if len(name) == 0:
- return name
-
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- matches = self.PREFIX_REGEXP.match(name)
- #
- # If no prefix was captured, that's okay. Don't substitute
- # the name however, "<name> ()" looks silly
- #
- if matches == None:
- return name
-
- if matches.group('prefix'):
- name = self.PREFIX_REGEXP.sub(r"\g<name>, \g<prefix>", name)
- return name
-
- def first_letter_equal(self, a, b):
- return a == b
-
-
-class i18n_de_generic(i18n):
- #
- # German streets are often named after people and include a title.
- # The title will be captured as part of the <prefix>
- # Covering airport names and "New"/"Old" as prefixes as well
- #
- APPELLATIONS = [ u"Alte", u"Alter", u"Doktor", u"Dr.",
- u"Flughafen", u"Flugplatz", u"Gen.,", u"General",
- u"Neue", u"Neuer", u"Platz",
- u"Prinz", u"Prinzessin", u"Prof.",
- u"Professor" ]
- #
- # Surnames in german streets named after people tend to have the middle
name
- # listed after the rest of the surname,
- # e.g. "Platz der deutschen Einheit" => "deutschen Einheit (Platz der)"
- # Likewise, articles are captured as part of the prefix,
- # e.g. "An der Märchenwiese" => "Märchenwiese (An der)"
- #
- DETERMINANTS = [ u"\s?An den", u"\s?An der", u"\s?Am",
- u"\s?Auf den" , u"\s?Auf der"
- u" an", u" des", u" der", u" von", u" vor"]
-
- SPACE_REDUCE = re.compile(r"\s+")
- PREFIX_REGEXP = re.compile(r"^(?P<prefix>(%s)(%s)?)\s?\b(?P<name>.+)" %
- ("|".join(APPELLATIONS),
- "|".join(DETERMINANTS)), re.IGNORECASE
- | re.UNICODE)
-
- # for IndexPageGenerator._upper_unaccent_string
- E_ACCENT = re.compile(ur"[éèêëẽ]", re.IGNORECASE | re.UNICODE)
- I_ACCENT = re.compile(ur"[íìîïĩ]", re.IGNORECASE | re.UNICODE)
- A_ACCENT = re.compile(ur"[áàâäã]", re.IGNORECASE | re.UNICODE)
- O_ACCENT = re.compile(ur"[óòôöõ]", re.IGNORECASE | re.UNICODE)
- U_ACCENT = re.compile(ur"[úùûüũ]", re.IGNORECASE | re.UNICODE)
-
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def _upper_unaccent_string(self, s):
- s = self.E_ACCENT.sub("e", s)
- s = self.I_ACCENT.sub("i", s)
- s = self.A_ACCENT.sub("a", s)
- s = self.O_ACCENT.sub("o", s)
- s = self.U_ACCENT.sub("u", s)
- return s.upper()
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- #
- # Make sure name actually contains something,
- # the PREFIX_REGEXP.match fails on zero-length strings
- #
- if len(name) == 0:
- return name
-
- name = name.strip()
- name = self.SPACE_REDUCE.sub(" ", name)
- name = self.PREFIX_REGEXP.sub(r"\g<name> (\g<prefix>)", name)
- return name
-
- def first_letter_equal(self, a, b):
- return self._upper_unaccent_string(a) == self._upper_unaccent_string(b)
-
-class i18n_generic(i18n):
- def __init__(self, language, locale_path):
- self.language = str(language)
- _install_language(language, locale_path)
-
- def language_code(self):
- return self.language
-
- def user_readable_street(self, name):
- return name
-
- def first_letter_equal(self, a, b):
- return a == b
-
-# When not listed in the following map, default language class will be
-# i18n_generic
-language_class_map = {
- 'fr_BE.UTF-8': i18n_fr_generic,
- 'fr_FR.UTF-8': i18n_fr_generic,
- 'fr_CA.UTF-8': i18n_fr_generic,
- 'fr_CH.UTF-8': i18n_fr_generic,
- 'fr_LU.UTF-8': i18n_fr_generic,
- 'en_AG': i18n_generic,
- 'en_AU.UTF-8': i18n_generic,
- 'en_BW.UTF-8': i18n_generic,
- 'en_CA.UTF-8': i18n_generic,
- 'en_DK.UTF-8': i18n_generic,
- 'en_GB.UTF-8': i18n_generic,
- 'en_HK.UTF-8': i18n_generic,
- 'en_IE.UTF-8': i18n_generic,
- 'en_IN': i18n_generic,
- 'en_NG': i18n_generic,
- 'en_NZ.UTF-8': i18n_generic,
- 'en_PH.UTF-8': i18n_generic,
- 'en_SG.UTF-8': i18n_generic,
- 'en_US.UTF-8': i18n_generic,
- 'en_ZA.UTF-8': i18n_generic,
- 'en_ZW.UTF-8': i18n_generic,
- 'nb_NO.UTF-8': i18n_generic,
- 'nl_BE.UTF-8': i18n_nl_generic,
- 'nl_NL.UTF-8': i18n_nl_generic,
- 'it_IT.UTF-8': i18n_it_generic,
- 'it_CH.UTF-8': i18n_it_generic,
- 'de_AT.UTF-8': i18n_de_generic,
- 'de_BE.UTF-8': i18n_de_generic,
- 'de_DE.UTF-8': i18n_de_generic,
- 'de_LU.UTF-8': i18n_de_generic,
- 'de_CH.UTF-8': i18n_de_generic,
- 'es_ES.UTF-8': i18n_es_generic,
- 'es_AR.UTF-8': i18n_es_generic,
- 'es_BO.UTF-8': i18n_es_generic,
- 'es_CL.UTF-8': i18n_es_generic,
- 'es_CR.UTF-8': i18n_es_generic,
- 'es_DO.UTF-8': i18n_es_generic,
- 'es_EC.UTF-8': i18n_es_generic,
- 'es_SV.UTF-8': i18n_es_generic,
- 'es_GT.UTF-8': i18n_es_generic,
- 'es_HN.UTF-8': i18n_es_generic,
- 'es_MX.UTF-8': i18n_es_generic,
- 'es_NI.UTF-8': i18n_es_generic,
- 'es_PA.UTF-8': i18n_es_generic,
- 'es_PY.UTF-8': i18n_es_generic,
- 'es_PE.UTF-8': i18n_es_generic,
- 'es_PR.UTF-8': i18n_es_generic,
- 'es_US.UTF-8': i18n_es_generic,
- 'es_UY.UTF-8': i18n_es_generic,
- 'es_VE.UTF-8': i18n_es_generic,
- 'ca_ES.UTF-8': i18n_ca_generic,
- 'ca_AD.UTF-8': i18n_ca_generic,
- 'ca_FR.UTF-8': i18n_ca_generic,
- 'pt_BR.UTF-8': i18n_pt_br_generic,
- 'da_DK.UTF-8': i18n_generic,
- 'ar_AE.UTF-8': i18n_ar_generic,
- 'ar_BH.UTF-8': i18n_ar_generic,
- 'ar_DZ.UTF-8': i18n_ar_generic,
- 'ar_EG.UTF-8': i18n_ar_generic,
- 'ar_IN': i18n_ar_generic,
- 'ar_IQ.UTF-8': i18n_ar_generic,
- 'ar_JO.UTF-8': i18n_ar_generic,
- 'ar_KW.UTF-8': i18n_ar_generic,
- 'ar_LB.UTF-8': i18n_ar_generic,
- 'ar_LY.UTF-8': i18n_ar_generic,
- 'ar_MA.UTF-8': i18n_ar_generic,
- 'ar_OM.UTF-8': i18n_ar_generic,
- 'ar_QA.UTF-8': i18n_ar_generic,
- 'ar_SA.UTF-8': i18n_ar_generic,
- 'ar_SD.UTF-8': i18n_ar_generic,
- 'ar_SY.UTF-8': i18n_ar_generic,
- 'ar_TN.UTF-8': i18n_ar_generic,
- 'ar_YE.UTF-8': i18n_ar_generic,
- 'hr_HR.UTF-8': i18n_hr_HR,
- 'ru_RU.UTF-8': i18n_ru_generic,
- 'pl_PL.UTF-8': i18n_pl_generic,
-}
-
-def install_translation(locale_name, locale_path):
- """Return a new i18n class instance, depending on the specified
- locale name (eg. "fr_FR.UTF-8"). See output of "locale -a" for a
- list of system-supported locale names. When none matching, default
- class is i18n_generic"""
- language_class = language_class_map.get(locale_name, i18n_generic)
- return language_class(locale_name, locale_path)
diff --git a/ocitysmap/map_canvas.py b/ocitysmap/map_canvas.py
deleted file mode 100644
index d606ab8..0000000
--- a/ocitysmap/map_canvas.py
+++ /dev/null
@@ -1,398 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-
-# 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/>.
-
-
-import os, mapnik, logging, locale, gzip
-
-# The ogr module is now known as osgeo.ogr in recent versions of the
-# module, but we want to keep compatibility with older versions
-try:
- from osgeo import ogr
-except ImportError:
- import ogr
-
-from coords import BoundingBox
-import draw_utils
-
-try:
- import cairo
-except ImportError:
- cairo = None
-
-l = logging.getLogger('ocitysmap')
-
-class GLOBALS:
- MAIN_PROJECTION = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0
+x_0=0.0 +y_0=0 +k=1.0 +units=m address@hidden +no_defs +over"
-
-
-class GridFile:
- """
- Class to generate a shape_file containing vertical/horizontal lines.
- Call flush() to commit the final rendering to disk. Afterwards,
- any attempt to add grid lines will fail (exception).
- The coordinates are not related to any projection
- """
- def __init__(self, envelope, out_filename, layer_name = "Grid"):
- """
- @param envelope (BoundingBox) envelope of the grid lines
- @param out_filename (string) path to the output shape file we generate
- @param layer_name (string) layer name in the shape file
- """
- self._envelope = envelope
- self._filepath = out_filename
- driver = ogr.GetDriverByName('ESRI Shapefile')
- if os.path.exists(out_filename):
- # Delete the detination file first
- driver.DeleteDataSource(out_filename)
- self._ds = driver.CreateDataSource(out_filename)
- self._layer = self._ds.CreateLayer(layer_name,
- geom_type=ogr.wkbLineString)
-
- def add_horiz_line(self, y):
- """
- Add a new latitude line at the given latitude
- """
- line = ogr.Geometry(type = ogr.wkbLineString)
- line.AddPoint_2D(self._envelope.get_top_left()[1], y)
- line.AddPoint_2D(self._envelope.get_bottom_right()[1], y)
- f = ogr.Feature(feature_def = self._layer.GetLayerDefn())
- f.SetGeometryDirectly(line)
- self._layer.CreateFeature(f)
- f.Destroy()
-
- def add_vert_line(self, x):
- """
- Add a new longitude line at the given longitude
- """
- line = ogr.Geometry(type = ogr.wkbLineString)
- line.AddPoint_2D(x, self._envelope.get_top_left()[0])
- line.AddPoint_2D(x, self._envelope.get_bottom_right()[0])
- f = ogr.Feature(feature_def = self._layer.GetLayerDefn())
- f.SetGeometryDirectly(line)
- self._layer.CreateFeature(f)
- f.Destroy()
-
- def flush(self):
- """
- Commit the file to disk and prevent any further addition of
- new longitude/latitude lines
- """
- self._ds.Destroy()
- self._ds = None
-
- def get_filepath(self):
- """
- Return the path to the destination shape file
- """
- return self._filepath
-
- def __str__(self):
- return "GridFile(%s)" % self._filepath
-
-
-def create_shapefile_polygon_from_wkt(out_fname, wkt):
- driver = ogr.GetDriverByName('ESRI Shapefile')
- if os.path.exists(out_fname):
- driver.DeleteDataSource(out_fname)
- ds = driver.CreateDataSource(out_fname)
- layer = ds.CreateLayer('poly', geom_type=ogr.wkbPolygon)
-
- prev_locale = locale.getlocale(locale.LC_ALL)
- locale.setlocale(locale.LC_ALL, "C")
- try:
- poly = ogr.CreateGeometryFromWkt(wkt)
- finally:
- locale.setlocale(locale.LC_ALL, prev_locale)
-
- f = ogr.Feature(feature_def = layer.GetLayerDefn())
- f.SetGeometryDirectly(poly)
- layer.CreateFeature(f)
- f.Destroy()
- ds.Destroy()
-
-
-def _project_envelope(proj, envelope):
- """
- Returns a new envelop, projected along the given projection object.
- @param proj mapnik.Projection object
- @param envelope mapnik.Envelope object
- """
- c0 = proj.forward(mapnik.Coord(envelope.minx, envelope.miny))
- c1 = proj.forward(mapnik.Coord(envelope.maxx, envelope.maxy))
- return mapnik.Envelope(c0.x, c0.y, c1.x, c1.y)
-
-
-class MapCanvas:
- """
- OSM in the background of a canvas used to draw grids and text.
- """
- def __init__(self, mapfile_path, geographic_bbox, graph_bbox = None):
- """
- @param mapfile_path (string) path the the osm.xml map file
- @param geographic_bbox (BoundingBox) bounding box to render, in
- latlong (4326) coordinates
- @param graph_bbox (int) graphical width/height of the
- rendered area for raster output (None = auto)
- """
- self._projname = GLOBALS.MAIN_PROJECTION
- self._proj = mapnik.Projection(self._projname)
- if graph_bbox is None:
- graph_bbox = geographic_bbox.get_pixel_size_for_zoom_factor()
- elif str(graph_bbox).startswith('zoom:'):
- graph_bbox =
geographic_bbox.get_pixel_size_for_zoom_factor(int(graph_bbox[5:]))
- self._envelope = mapnik.Envelope(geographic_bbox.get_top_left()[1],
- geographic_bbox.get_top_left()[0],
-
geographic_bbox.get_bottom_right()[1],
-
geographic_bbox.get_bottom_right()[0])
- # Determine the size of a meter in pixels (float)
- ymeters, xmeters = geographic_bbox.spheric_sizes()
- xpixels = graph_bbox[0] / xmeters
- ypixels = graph_bbox[1] / ymeters
- self.one_meter_in_pixels = min(xpixels, ypixels)
- l.debug('Geo size: %sx%s, pixels=%sx%s, 1m=%s|%s' \
- % (xmeters, ymeters,
- graph_bbox[0], graph_bbox[1],
- xpixels, ypixels))
-
- self._map = mapnik.Map(graph_bbox[0],
- graph_bbox[1],
- self._projname)
- mapnik.load_map(self._map, mapfile_path)
-
- # Keep geographic bounding box, ignoring one dimension of the
- # specified grwidth/grheight constraints
- self._map.aspect_fix_mode = mapnik.aspect_fix_mode.SHRINK_CANVAS
-
- # The data to render in the resulting scene
- self._labels = mapnik.PointDatasource()
- self._labelstyles = set() # Set of label styles, used to
- # define the sets of layers
- self._shapes = [] # Data from the shape files
- self._dirty = True # Rendering needed because data have
- # been added
-
- def add_label(self, x, y, str_label,
- str_color = "red", alpha = 0.5,
- font_size = 11,
- font_family = "DejaVu Sans Book"):
- """
- Add a label on top of the map.
- @param x,y Coordinates of the label, in latlong coordinates (4326)
- @param str_label (string) Text to display
- @param str_color (string) Color definition (html)
- @param alpha (float 0..1) Color opacity
- @param font_size (int) Font size of the label
- @param font_family (string) Name of the font
- """
- pt = self._proj.forward(mapnik.Coord(x, y))
- labelstyle = (str_color, alpha, font_size, font_family)
- self._labels.add_point(pt.x, pt.y,
- 'style_%x' % hash(labelstyle),
- str_label)
- self._labelstyles.add(labelstyle)
- self._dirty = True
-
- def _render_label_style(self, str_color, alpha, font_size, font_family):
- labelstyle = (str_color, alpha, font_size, font_family)
- H = hash(labelstyle)
-
- s = mapnik.Style()
- r = mapnik.Rule()
- c = mapnik.Color(str_color)
- c.a = int(255 * alpha)
- symb = mapnik.TextSymbolizer('style_%x' % H,
- font_family,
- int(font_size),
- c)
- symb.allow_overlap = True
- symb.set_label_placement = mapnik.label_placement.POINT_PLACEMENT
- r.symbols.append(symb)
- s.rules.append(r)
-
- lyr = mapnik.Layer('Labels_%x' % H, self._projname)
- lyr.datasource = self._labels
- self._map.append_style('labels_%x' % H, s)
- lyr.styles.append('labels_%x' % H)
- self._map.layers.append(lyr)
-
- def add_shapefile(self, path_shpfile, str_color = 'grey', alpha = 0.5,
- line_width = 1.):
- """
- Add a shape file to display on top of the map
- @param path_shpfile (string) path to the shape file to render
- @param str_color (string) Color definition (html)
- @param alpha (float 0..1) Color opacity
- @param line_width (float) line width in raster pixels
- """
- col = mapnik.Color(str_color)
- col.a = int(255 * alpha)
- self._shapes.append(['SHPFILE', (path_shpfile, col, line_width)])
- self._dirty = True
-
- def _render_shp(self, path_shpfile, obj_color, line_width):
- shpid = os.path.basename(path_shpfile)
- s,r = mapnik.Style(), mapnik.Rule()
- r.symbols.append(mapnik.PolygonSymbolizer(obj_color))
- r.symbols.append(mapnik.LineSymbolizer(obj_color, line_width))
- s.rules.append(r)
- self._map.append_style('style_' + shpid, s)
- lyr = mapnik.Layer(shpid)
- lyr.datasource = mapnik.Shapefile(file=path_shpfile)
- lyr.styles.append("style_" + shpid)
- self._map.layers.append(lyr)
-
- def render_map(self):
- """
- Render map in memory. Automatically called by save_map(), only
- when needed.
- @return the mapnik map object
- """
- for lyrtype, lyrparms in self._shapes:
- self._render_shp(*lyrparms)
-
- for labelstyle in self._labelstyles:
- self._render_label_style(*labelstyle)
-
- l.debug("rendering to bbox %s as %sx%s..."
- % (self._envelope, self._map.height, self._map.width))
- bbox = _project_envelope(self._proj, self._envelope)
- self._map.zoom_to_box(bbox)
- l.debug("rendered to bbox %s as %sx%s." \
- % (bbox, self._map.height, self._map.width))
- self._dirty = False
- return self._map
-
- def save_map(self, output_filename,
- title = "City's map",
- file_type = None,
- copyright_logo_png = None,
- force = False):
- """
- Save the map as an image. By default, the format is inferred
- from the filename (its extension). It can be forced with the
- 'file_type' parameter.
- @param output_filename (string) file to generate
- @param file_type (string) None, or 'xml', 'png', 'ps',
- 'pdf', 'svg' or 'svgz'
- @param force (bool) fore render_map() to be called, even if it
- does not appear to have changed since last render_map()
- """
- if self._dirty or force:
- self.render_map()
-
- if file_type is None:
- file_type = output_filename.split('.')[-1]
-
- file_type = file_type.lower()
- cairo_factory = None
-
- if file_type == 'xml':
- mapnik.save_map(self._map, output_filename)
- return
-
- elif file_type == 'csv':
- l.debug('not rendering map as csv (not supported)')
- return
-
- elif file_type in ('png', 'png24'): # 24-bits, the default
- if (title is None) or (cairo is None):
- mapnik.render_to_file(self._map, output_filename, 'png')
- return
- else:
- cairo_factory = \
- lambda w,h: cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
-
- elif file_type == 'svg' and cairo is not None:
- cairo_factory = lambda w,h: cairo.SVGSurface(output_filename, w, h)
-
- elif file_type == 'svgz' and cairo is not None:
- def cairo_factory(w,h):
- gz = gzip.GzipFile(output_filename, 'wb')
- return cairo.SVGSurface(gz, w, h)
-
- elif file_type == 'pdf' and cairo is not None:
- cairo_factory = lambda w,h: cairo.PDFSurface(output_filename, w, h)
-
- elif file_type == 'ps' and cairo is not None:
- cairo_factory = lambda w,h: cairo.PSSurface(output_filename, w, h)
-
- else:
- raise ValueError('Unsupported output format: %s' % file_type)
-
- # Cairo reendering
- if title is not None:
- frame_width = int(max(self._map.height / 20., 30))
-
- surface = cairo_factory(self._map.width + frame_width*2,
- self._map.height + frame_width*2)
-
- def my_render(ctx):
- mapnik.render(self._map,
- ctx)
- draw_utils.add_logo(ctx, self._map.width,
- self._map.height,
- copyright_logo_png)
-
- draw_utils.enclose_in_frame(my_render,
- self._map.width, self._map.height,
- title,
- surface,
- self._map.width + frame_width*2,
- self._map.height + frame_width*2, frame_width)
- else:
- surface = cairo_factory(self._map.width, self._map.height)
- ctx = cairo.Context(surface)
- mapnik.render(self._map, ctx)
-
- surface.flush()
-
- # png rendering with cairo...
- if file_type in ('png', 'png24'):
- surface.write_to_png(output_filename)
-
- surface.finish()
-
-
-if __name__ == "__main__":
- # A few tests
-
- # Create the grid shape file
- g = GridFile("mygrid.shp")
- g.add_horiz_line(44.48)
- g.add_vert_line(-1.08)
- g.flush()
-
- # Declare a map with a grid and some text
- sanguinet = MapCanvas("/home/decot/downloads/svn/mapnik-osm/osm.xml",
- BoundingBox(44.4883, -1.0901,
- 44.4778, -1.0637))
- sanguinet.add_label(-1.075, 44.483, "Toto")
- sanguinet.add_label(-1.075, 44.479, "Titi", '#ff00ff', 30)
- sanguinet.add_shapefile(g.get_filepath())
-
- # Save the rendered map into different file formats
- for fname in ('sanguinet.xml', 'sanguinet.png',
- 'sanguinet.svg', 'sanguinet.pdf',
- 'sanguinet.ps', 'sanguinet.jpg'):
- sanguinet.save_map(fname)
diff --git a/ocitysmap/street_index.py b/ocitysmap/street_index.py
deleted file mode 100644
index 9de5322..0000000
--- a/ocitysmap/street_index.py
+++ /dev/null
@@ -1,1050 +0,0 @@
-# -*- 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/>.
-
-import cairo
-import ConfigParser
-import csv
-import datetime
-import gzip
-import locale
-import logging
-import math
-import os
-import pango
-import pangocairo
-import psycopg2
-import re
-import sys
-import tempfile
-import traceback
-
-import grid
-import i18n
-import map_canvas
-import utils
-
-from coords import BoundingBox
-from draw_utils import enclose_in_frame
-
-import psycopg2.extensions
-# compatibility with django: see http://code.djangoproject.com/ticket/5996
-psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
-# SQL string escaping routine
-sql_escape_unicode = lambda s: psycopg2.extensions.adapt(s.encode('utf-8'))
-
-LOG = logging.getLogger('maposmatic')
-STATEMENT_TIMEOUT_MINUTES = 18
-
-class BaseOCitySMapError(Exception):
- """Base class for exceptions thrown by OCitySMap."""
-
-class UnsufficientDataError(BaseOCitySMapError):
- """Not enough data in the OSM database to proceed."""
-
-def _user_readable_label(squares):
- """Creates a label usable in the street index adjacent to the map
- from a square list."""
-
- def couple_compare(x,y):
- a = y[0] - x[0]
- if a:
- return a
- return y[1] - x[1]
-
- def distance(a,b):
- return (b[0]-a[0])**2 + (b[1]-a[1])**2
-
- minx = min([x[0] for x in squares])
- maxx = max([x[0] for x in squares])
- miny = min([x[1] for x in squares])
- maxy = max([x[1] for x in squares])
- if len(squares) == 1:
- label = (utils.gen_vertical_square_label(squares[0][0]) +
- utils.gen_horizontal_square_label(squares[0][1]))
- elif minx == maxx:
- label = ('%s%s-%s' % (utils.gen_vertical_square_label(minx),
- utils.gen_horizontal_square_label(miny),
- utils.gen_horizontal_square_label(maxy)))
- elif miny == maxy:
- label = ('%s-%s%s' % (utils.gen_vertical_square_label(minx),
- utils.gen_vertical_square_label(maxx),
- utils.gen_horizontal_square_label(miny)))
- elif (maxx - minx + 1) * (maxy - miny + 1) == len(squares):
- label = ('%s-%s%s-%s' % (utils.gen_vertical_square_label(minx),
- utils.gen_vertical_square_label(maxx),
- utils.gen_horizontal_square_label(miny),
- utils.gen_horizontal_square_label(maxy)))
- else:
- squares_x_first = sorted(squares, couple_compare)
- squares_y_first = sorted(squares, lambda x,y: couple_compare(y,x))
- if (distance(squares_x_first[0], squares_x_first[-1]) >
- distance(squares_y_first[0], squares_y_first[-1])):
- first = squares_x_first[0]
- last = squares_x_first[-1]
- else:
- first = squares_y_first[0]
- last = squares_y_first[-1]
-
- label = '%s%s...%s%s' % (utils.gen_vertical_square_label(first[0]),
- utils.gen_horizontal_square_label(first[1]),
- utils.gen_vertical_square_label(last[0]),
- utils.gen_horizontal_square_label(last[1]))
- return label
-
-class IndexPageGenerator:
- def __init__(self, streets, i18n):
- self.streets = streets
- self.i18n = i18n
-
- def _street_label_width(self, street, layout):
- (firstletter, label, location) = street
- layout.set_text(label)
- width = layout.get_size()[0] / pango.SCALE
- layout.set_text(location)
- width += layout.get_size()[0] / pango.SCALE
- return width
-
- def _get_font_parameters(self, cr, pc, fontsize):
- layout = pc.create_layout()
- fd = pango.FontDescription("DejaVu")
- fd.set_size(int(fontsize * 1.2 * pango.SCALE))
- f = layout.get_context().load_font(fd)
- heading_fm = f.get_metrics()
-
- fd.set_size(fontsize * pango.SCALE)
- layout.set_font_description(fd)
- f = layout.get_context().load_font(fd)
- fm = f.get_metrics()
-
- em = fm.get_approximate_char_width() / pango.SCALE
- widths = map(lambda x: self._street_label_width(x, layout),
self.streets)
- maxwidth = max(widths)
- colwidth = maxwidth + 3 * em
-
- return {
- 'colwidth' : colwidth,
- 'heading_fascent': heading_fm.get_ascent() / pango.SCALE,
- 'heading_fheight' : (heading_fm.get_ascent() +
heading_fm.get_descent()) / pango.SCALE,
- 'fascent': fm.get_ascent() / pango.SCALE,
- 'fheight' : (fm.get_ascent() + fm.get_descent()) / pango.SCALE,
- 'em' : em,
- }
-
- def _fits_in_page(self, cr, pc, paperwidth, paperheight, fontsize):
- fp = self._get_font_parameters(cr, pc, fontsize)
-
- prevletter = u''
- heading_letter_count = 0
- for street in self.streets:
- if not self.i18n.first_letter_equal(street[0], prevletter):
- heading_letter_count += 1
- prevletter = street[0]
-
- colheight = len(self.streets) * fp['fheight'] + heading_letter_count *
fp['heading_fheight']
-
- paperncols = math.floor(paperwidth / fp['colwidth'])
- if paperncols == 0:
- return False
- # Add a small space before/after each column
- colheight += paperncols * fp['fheight']
- colheight /= paperncols
- return colheight < paperheight
-
- def _compute_font_size(self, cr, pc, paperwidth, paperheight):
- minfontsize = 6
- maxfontsize = 128
-
- if not self._fits_in_page(cr, pc, paperwidth, paperheight,
minfontsize):
- print "Index does not fit even with font size %d" % minfontsize
- sys.exit(1)
-
- while maxfontsize - minfontsize != 1:
- meanfontsize = int((maxfontsize + minfontsize) / 2)
- if self._fits_in_page(cr, pc, paperwidth, paperheight,
meanfontsize):
- minfontsize = meanfontsize
- else:
- maxfontsize = meanfontsize
-
- return minfontsize
-
- def render(self, cr, paperwidth, paperheight):
- pc = pangocairo.CairoContext(cr)
-
- cr.set_source_rgb(1, 1, 1)
- cr.paint()
- cr.set_source_rgb(0.0, 0.0, 0.0)
-
- fontsize = self._compute_font_size(cr, pc, paperwidth, paperheight)
-
- fp = self._get_font_parameters(cr, pc, fontsize)
- heading_fheight = fp['heading_fheight']
- heading_fascent = fp['heading_fascent']
- fheight = fp['fheight']
- fascent = fp['fascent']
- colwidth = fp['colwidth']
- em = fp['em']
-
- remaining = paperwidth % colwidth
- colwidth += (remaining / int(paperwidth / colwidth))
-
- y = 0
-
- if self.i18n.isrtl():
- x = paperwidth - colwidth + em
- else:
- x = em
-
- prevletter = u''
- for street in self.streets:
- # Letter label
- firstletter = street[0]
- if not self.i18n.first_letter_equal(firstletter, prevletter):
- # Make sure we have no orphelin heading letter label at the
- # end of a column
- if y + heading_fheight + fheight > paperheight:
- y = 0
-
- if self.i18n.isrtl():
- x -= colwidth
- else:
- x += colwidth
-
- # Reserve height for the heading letter label
- y += heading_fheight
-
- cr.set_source_rgb(0.9, 0.9, 0.9)
- cr.rectangle(x, y - heading_fascent, colwidth - em,
heading_fheight)
- cr.fill()
-
- cr.set_source_rgb(0, 0, 0)
-
- # Draw the heading letter label
- layout = pc.create_layout()
- fd = pango.FontDescription("DejaVu")
- fd.set_size(int(fontsize * 1.2 * pango.SCALE))
- layout.set_font_description(fd)
- layout.set_text(firstletter)
- w = layout.get_size()[0] / pango.SCALE
- indent = (colwidth - 2 * em - w) / 2
- cr.move_to(x + indent, y - heading_fascent)
- pc.show_layout(layout)
- prevletter = firstletter
-
- # Reserve height for the street
- y += fheight
- layout = pc.create_layout()
- fd = pango.FontDescription("DejaVu")
- fd.set_size(int(fontsize * pango.SCALE))
- layout.set_font_description(fd)
-
- # Compute length of the dashed line between the street name and
- # the squares label
- layout.set_text(street[1])
- street_name_width = layout.get_size()[0] / pango.SCALE
- layout.set_text(street[2])
- squares_label_width = layout.get_size()[0] / pango.SCALE
- line_width = colwidth - street_name_width - squares_label_width -
2 * em
-
- if self.i18n.isrtl():
- # Draw squares label
- cr.move_to(x, y - fascent)
- layout.set_text(street[2])
- pc.show_layout(layout)
- # Draw dashed line
- strokewidth = max(fontsize / 12, 1)
- cr.set_line_width(strokewidth)
- cr.set_dash([ strokewidth, strokewidth * 2 ])
- cr.move_to(x + squares_label_width + em / 2, y - 0.1 * em)
- cr.rel_line_to(line_width, 0)
- cr.stroke()
- # Draw street label
- cr.move_to(x + colwidth - em - street_name_width, y - fascent)
- layout.set_text(street[1])
- pc.show_layout(layout)
- else:
- # Draw street name
- cr.move_to(x, y - fascent)
- layout.set_text(street[1])
- pc.show_layout(layout)
- # Draw dashed line
- strokewidth = max(fontsize / 12, 1)
- cr.set_line_width(strokewidth)
- cr.set_dash([ strokewidth, strokewidth * 2 ])
- cr.move_to(x + street_name_width + em / 2, y - 0.1 * em)
- cr.rel_line_to(line_width, 0)
- cr.stroke()
- # Draw squares label
- cr.move_to(x + colwidth - em - squares_label_width, y -
fascent)
- layout.set_text(street[2])
- pc.show_layout(layout)
-
- if y + fheight > paperheight:
- y = 0
-
- if self.i18n.isrtl():
- x -= colwidth
- else:
- x += colwidth
-
-class OCitySMap:
- def __init__(self, config_file=None, map_areas_prefix=None,
- city_name=None, boundingbox=None, osmid=None, language=None):
- """Creates a new OCitySMap renderer instance for the given city.
-
- Args:
- config_file (string): location of the config file
- map_areas_prefix (string): map_areas table name prefix
- city_name (string): The name of the city we're created the map of.
- boundingbox (BoundingBox): An optional BoundingBox object defining
- the city's bounding box. If not given, OCitySMap will try to
- guess the bounding box from the OSM data. An
UnsufficientDataError
- exception will be raised in the bounding box can't be guessed.
- osmid (integer): The OSM id of the polygon of the city to render
- """
-
- optcnt = 0
- for var in city_name, boundingbox, osmid:
- if var:
- optcnt += 1
-
- assert optcnt == 1
-
- (self.city_name, self.boundingbox, self.osmid) = (city_name,
boundingbox, osmid)
-
- if self.city_name:
- LOG.info('OCitySMap renderer for %s.' % self.city_name)
- elif self.boundingbox:
- LOG.info('OCitySMap renderer for bounding box %s.' %
self.boundingbox)
- else:
- LOG.info('OCitySMap renderer for OSMID %s.' % self.osmid)
-
-
- if config_file:
- config_files = [config_file]
- else:
- config_files = ['/etc/ocitysmap.conf',
- os.getenv('HOME') + '/.ocitysmap.conf']
-
- LOG.info('Reading configuration from %s...' % ', '.join(config_files))
- self.parser = ConfigParser.RawConfigParser()
- if not self.parser.read(config_files):
- raise IOError, 'Failed to load the config file'
-
- locale_path = self.parser.get('ocitysmap', 'locale_path')
- self.i18n = i18n.install_translation(language, locale_path)
- LOG.info('Rendering language: ' + self.i18n.language_code())
-
- # Create the map_areas table name string using the provided prefix
- map_areas_prefix = map_areas_prefix or ''
- self._map_areas_table_name = '%smap_areas' % map_areas_prefix
-
- self.SELECTED_AMENITIES = [
- (_(u"Places of worship"), "place_of_worship", _(u"Place of
worship")),
- (_(u"Education"), "kindergarten", _(u"Kindergarten")),
- (_(u"Education"), "school", _(u"School")),
- (_(u"Education"), "college", _(u"College")),
- (_(u"Education"), "university", _(u"University")),
- (_(u"Education"), "library", _(u"Library")),
- (_(u"Public buildings"), "townhall", _(u"Town hall")),
- (_(u"Public buildings"), "post_office", _(u"Post office")),
- (_(u"Public buildings"), "public_building", _(u"Public building")),
- (_(u"Public buildings"), "police", _(u"Police"))]
-
- datasource = dict(self.parser.items('datasource'))
-
- LOG.info('Connecting to database %s at %s (user: %s)...' %
- (datasource['dbname'], datasource['host'],
datasource['user']))
- db = psycopg2.connect(user=datasource['user'],
- password=datasource['password'],
- host=datasource['host'],
- database=datasource['dbname'])
-
- # Force everything to be unicode-encoded, in case we run along
- # django (which loads the unicode extensions for psycopg2)
- db.set_client_encoding('utf8')
-
- # Set session timeout parameter (18mn)
- cursor = db.cursor()
- cursor.execute("show statement_timeout;")
- LOG.debug("Initial statement timeout: %s" % cursor.fetchall()[0][0])
- cursor.execute("set session statement_timeout=%d;"
- % (STATEMENT_TIMEOUT_MINUTES*60*1000))
- cursor.execute("show statement_timeout;")
- LOG.debug("Configured statement timeout: %s" % cursor.fetchall()[0][0])
- del cursor
-
- if self.city_name:
- self.boundingbox = self.find_bounding_box_by_name(db,
self.city_name)
- elif self.osmid:
- self.boundingbox = self.find_bounding_box_by_osmid(db, self.osmid)
-
- self.griddesc = grid.GridDescriptor(self.boundingbox, db)
-
- try:
- self._gen_map_areas(db)
-
- self.contour = None
- self.polygon = None
-
- if self.city_name:
- self.contour, self.polygon = \
- self.get_city_contour_by_name(db, self.city_name)
- elif self.osmid:
- self.contour, self.polygon = \
- self.get_city_contour_by_osmid(db, self.osmid)
-
- self.streets = self.get_streets(db, self.polygon)
- self.amenities = self.get_amenities(db, self.polygon)
- finally:
- LOG.debug('Restoring database state...')
- db.rollback()
- self._del_map_areas(db)
-
- LOG.info('City bounding box is %s.' % str(self.boundingbox))
-
-
- def find_bounding_box_by_name(self, db, name):
- """Find the bounding box of a city from its name.
-
- Args:
- db: connection to the database
- name (string): The city name.
- Returns a BoundingBox object describing the bounding box around
- the given city.
- """
-
- LOG.info("Looking for rendering bounding box around %s..." % name)
-
- cursor = db.cursor()
- cursor.execute("""select osm_id,
st_astext(st_transform(st_envelope(way), 4002))
- from planet_osm_line
- where boundary='administrative' and
- admin_level='8' and
- name=%s;""" % \
- sql_escape_unicode(name))
- records = cursor.fetchall()
- if not records:
- raise UnsufficientDataError, "Wrong city name (%s) or missing
administrative boundary in database!" % repr(name)
-
- osm_id, wkt = records[0]
- LOG.info("Found matching OSMID %s with bounding box %s." % (osm_id,
wkt))
- return BoundingBox.parse_wkt(wkt)
-
- def find_bounding_box_by_osmid(self, db, osmid):
- """Find the bounding box of a city from its OSM id.
-
- Args:
- db: connection to the database
- osmid (integer): The city OSM id.
- Returns a BoundingBox object describing the bounding box around
- the given city.
- """
-
- LOG.info('Looking for rendering bounding box around OSMID %s...' %
osmid)
-
- cursor = db.cursor()
- cursor.execute("""select st_astext(st_transform(st_envelope(way),
4002))
- from planet_osm_polygon
- where osm_id=%d;""" % \
- osmid)
- records = cursor.fetchall()
- if not records:
- raise UnsufficientDataError, "OSMID %s not found in the database!"
% repr(osmid)
-
- LOG.info("Found bounding box: %s" % records[0][0])
- return BoundingBox.parse_wkt(records[0][0])
-
- _regexp_contour = re.compile('^POLYGON\(\(([^)]*)\)\)$')
- _regexp_coords = re.compile('^(\S*)\s+(\S*)$')
-
- def parse_city_contour(self, data):
- try:
- contour = data[0][0].strip()
- polygon = data[0][1].strip()
- except (KeyError,IndexError,AttributeError):
- LOG.error("Invalid DB row structure")
- return None
-
- # Got nothing usable
- if not contour or not polygon:
- return None
-
- # Parse the answer, in order to add a margin around the area
- prev_locale = locale.getlocale(locale.LC_ALL)
- locale.setlocale(locale.LC_ALL, "C")
- try:
- matches = self._regexp_contour.match(contour)
- if not matches:
- LOG.error("Area not conformant")
- return None
- bbox_coords = matches.groups()[0].split(',')
-
- # Determine bbox envelope
- if len(bbox_coords) != 5:
- LOG.error("Coords look atypical: %s", bbox_coords)
- return None
-
- # Find the four corners
- xmin, xmax, ymin, ymax = (None,)*4
- for point in bbox_coords:
- matches = self._regexp_coords.match(point)
- y,x = map(float,matches.groups())
- if (xmax is None) or xmax < x: xmax = x
- if (xmin is None) or xmin > x: xmin = x
- if (ymax is None) or ymax < y: ymax = y
- if (ymin is None) or ymin > y: ymin = y
-
- # Add one degree around the area
- xmin -= 1. ; xmax += 1.
- ymin -= 1. ; ymax += 1.
-
- matches = self._regexp_contour.match(polygon)
- if not matches:
- LOG.error("Administrative boundary looks invalid")
- return None
- inside = matches.groups()[0]
-
- l = "MULTIPOLYGON(((%f %f, %f %f, %f %f, %f %f, %f %f)),((%s)))" \
- % (ymin, xmin, ymin, xmax, ymax, xmax, ymax, xmin, ymin, xmin,
- inside)
- return (l, polygon)
- except:
- # Regexp error: area is not a "simple" polygon
- LOG.exception("Unexpected exception")
- return None
- finally:
- locale.setlocale(locale.LC_ALL, prev_locale)
-
- def get_city_contour_by_name(self, db, city):
- assert city is not None
- LOG.info('Looking for contour around %s...' % repr(city))
- cursor = db.cursor()
- cursor.execute("""select st_astext(st_transform(st_envelope(way),
4002))
- as contour,
- st_astext(st_transform(st_buildarea(way),
4002))
- as polygon
- from planet_osm_line
- where boundary='administrative'
- and admin_level='8' and name=%s;""" % \
- sql_escape_unicode(city))
- data = cursor.fetchall()
- LOG.debug('Got contour.')
- return self.parse_city_contour(data)
-
- def get_city_contour_by_osmid(self, db, osmid):
- LOG.info('Looking for contour around OSMID %s...' % repr(osmid))
- cursor = db.cursor()
- cursor.execute("""select st_astext(st_transform(st_envelope(way),
4002))
- as contour,
- st_astext(st_transform(st_buildarea(way),
4002))
- as polygon
- from planet_osm_polygon
- where osm_id=%d;""" % \
- osmid)
- data = cursor.fetchall()
- LOG.debug('Got contour.')
- return self.parse_city_contour(data)
-
- # This function creates a map_areas table that contains the list
- # of the squares used to divide the global city map. Each entry of
- # this table represent one of these square, with x, y being the
- # square identifiers, and geom being its geographical
- # geometry. This temporary table allows to conveniently perform a
- # joint with the planet_osm_line or planet_osm_polygon tables, so
- # that getting the list of squares for a given set of streets can
- # be performed in a single query
- def _gen_map_areas(self, db):
- cursor = db.cursor()
- LOG.info("Generating map grid in table %s..."
- % self._map_areas_table_name)
-
- # Make sure our map_areas table does not exist already. We don't call
- # _del_map_areas here because we want this to be part of the local
- # DB transaction.
- LOG.debug('drop %s...' % self._map_areas_table_name)
- cursor.execute("drop table if exists %s" % self._map_areas_table_name)
-
- LOG.debug('create table %s...' % self._map_areas_table_name)
- cursor.execute("create table %s (x integer, y integer)"
- % self._map_areas_table_name)
- LOG.debug('addgeometrycolumn...')
- cursor.execute("select addgeometrycolumn('%s', 'geom', 4002,
'POLYGON', 2)" % self._map_areas_table_name)
- for i in xrange(0, int(math.ceil(self.griddesc.width_square_count))):
- for j in xrange(0,
int(math.ceil(self.griddesc.height_square_count))):
- LOG.debug('add square %d,%d...' % (i,j))
- lon1 = (self.boundingbox.get_top_left()[1] +
- i * self.griddesc.width_square_angle)
- lon2 = (self.boundingbox.get_top_left()[1] +
- (i + 1) * self.griddesc.width_square_angle)
- lat1 = (self.boundingbox.get_top_left()[0] -
- j * self.griddesc.height_square_angle)
- lat2 = (self.boundingbox.get_top_left()[0] -
- (j + 1) * self.griddesc.height_square_angle)
- poly = ("POLYGON((%f %f, %f %f, %f %f, %f %f, %f %f))" %
- (lon1, lat1, lon1, lat2, lon2,
- lat2, lon2, lat1, lon1, lat1))
- cursor.execute("""insert into %s (x, y, geom)
- values (%d, %d, st_geomfromtext('%s',
4002))""" %
- (self._map_areas_table_name, i, j, poly))
-
- LOG.debug('Commit gen_map_areas...')
- db.commit()
- LOG.debug('Done with gen_map_areas for %s.'
- % self._map_areas_table_name)
-
- def _del_map_areas(self, db):
- """Destroys the map_areas table used by this OCitySMap instance
- (parametrized by its prefix)."""
-
- cursor = db.cursor()
- LOG.debug('drop %s...' % self._map_areas_table_name)
- cursor.execute("drop table if exists %s" % self._map_areas_table_name)
- db.commit()
-
- # Given a list of street and their corresponding squares, do some
- # cleanup and pass it through the internationalization layer to
- # get proper sorting, filtering of common prefixes, etc. Returns a
- # updated street list.
- def humanize_street_list(self, sl):
- # We transform the string representing the squares list into a
- # Python list
- sl = [( street[0],
- [ map(int, x.split(',')) for x in street[1].split(';')[:-1] ] )
- for street in sl]
-
- # Street prefixes are postfixed, a human readable label is
- # built to represent the list of squares, and the list is
- # alphabetically-sorted.
- prev_locale = locale.getlocale(locale.LC_COLLATE)
- locale.setlocale(locale.LC_COLLATE, self.i18n.language_code())
-
- def _humanize_street_label(street):
- return (self.i18n.user_readable_street(street[0]),
- _user_readable_label(street[1]))
-
- try:
- sl = sorted(map(_humanize_street_label, sl),
- lambda x, y: locale.strcoll(x[0].lower(),
y[0].lower()))
- finally:
- locale.setlocale(locale.LC_COLLATE, prev_locale)
-
- # Add the first letter of the street name as category
- sl = [(street[0][0], street[0], street[1]) for street in sl]
-
- return sl
-
- def get_streets(self, db, contour=None):
- """Get the list of streets in the administrative area if city is
- defined or in the bounding box otherwise, and for each
- street, the list of squares that it intersects.
-
- Returns a list of the form [(street_name, 'A-B1'),
- (street2_name, 'B3')]
- """
-
- cursor = db.cursor()
- LOG.info("Getting streets...")
-
- # The inner select query creates the list of (street, square)
- # for all the squares in the temporary map_areas table. The
- # left_join + the test on cities_area is used to filter out
- # the streets outside the city administrative boundaries. The
- # outer select builds an easy to parse list of the squares for
- # each street.
- #
- # A typical result entry is:
- # [ "Rue du Moulin", "0,1;1,2;1,3" ]
- #
- # REMARKS:
- #
- # * The cities_area view is created once for all at
- # installation. It associates the name of a city with the
- # area covering it. As of today, only parts of the french
- # cities have these administrative boundaries available in
- # OSM. When available, this boundary is used to filter out
- # the streets that are not inside the selected city but
- # still in the bounding box rendered on the map. So these
- # streets will be shown but not listed in the street index.
- #
- # * The textcat_all() aggregate must also be installed in the
- # database
- #
- # See ocitysmap-init.sql for details
-
- intersect = 'true'
- if contour:
- intersect = """st_intersects(way, st_transform(
- GeomFromText('%s', 4002), 900913))""" % contour
-
- cursor.execute("""select name, textcat_all(x || ',' || y || ';')
- from (select distinct name, x, y
- from planet_osm_line
- join %s
- on st_intersects(way, st_transform(geom,
900913))
- where trim(name) != '' and highway is not null
- and %s)
- as foo
- group by name
- order by name;""" % \
- (self._map_areas_table_name,
- intersect))
-
- sl = cursor.fetchall()
- LOG.debug("Got streets (%d)." % len(sl))
- return self.humanize_street_list(sl)
-
- # Given a list of amenities and their corresponding squares, do some
- # cleanup and pass it through the internationalization layer to
- # get proper sorting, filtering of common prefixes, etc. Returns a
- # updated amenity list.
- def humanize_amenity_list(self, am):
- # We transform the string representing the squares list into a
- # Python list
- am = [( amenity[0], amenity[1],
- [ map(int, x.split(',')) for x in amenity[2].split(';')[:-1] ]
)
- for amenity in am]
-
- # Street prefixes are postfixed and a human readable label is
- # built to represent the list of squares.
- def _humanize_amenity_label(amenity):
- return (amenity[0], amenity[1],
- _user_readable_label(amenity[2]))
-
- am = map(_humanize_amenity_label, am)
-
- return am
-
- def get_amenities(self, db, contour=None):
- """Get the list of amenities in the administrative area if city is
- defined or in the bounding box otherwise, and for each
- amenity, the list of squares that it intersects.
-
- Returns a list of the form [(category, name, 'A-B1'),
- (category, name, 'B3')]
- """
-
- cursor = db.cursor()
-
- # The inner select query creates the list of (amenity, square)
- # for all the squares in the temporary map_areas table. The
- # left_join + the test on cities_area is used to filter out
- # the amenities outside the city administrative boundaries. The
- # outer select builds an easy to parse list of the squares for
- # each amenity.
- #
- # A typical result entry is:
- # [ "Places of worship", "Basilique Sainte Germaine", "0,1;1,2;1,3" ]
- #
- # REMARKS:
- #
- # * The cities_area view is created once for all at
- # installation. It associates the name of a city with the
- # area covering it. As of today, only parts of the french
- # cities have these administrative boundaries available in
- # OSM. When available, this boundary is used to filter out
- # the streets that are not inside the selected city but
- # still in the bounding box rendered on the map. So these
- # streets will be shown but not listed in the street index.
- #
- # * The textcat_all() aggregate must also be installed in the
- # database
- #
- # See ocitysmap-init.sql for details
- intersect = 'true'
- if contour:
- intersect = """st_intersects(way, st_transform(
- GeomFromText('%s', 4002), 900913))""" % contour
-
- al = []
- for cat, amenity, human in self.SELECTED_AMENITIES:
- LOG.info("Getting amenities: %s..." % amenity)
- cursor.execute("""select %(category)s, name, textcat_all(x || ','
|| y || ';')
- from (select distinct amenity, name, x, y, osm_id
- from planet_osm_point join %(tmp_tblname)s
- on st_intersects(way, st_transform(geom,
900913))
- where amenity = %(amenity)s and
%(intersect)s
- union
- select distinct amenity, name, x, y, osm_id
- from planet_osm_polygon join
%(tmp_tblname)s
- on st_intersects(way, st_transform(geom,
900913))
- where amenity = %(amenity)s and
%(intersect)s)
- as foo
- group by amenity, osm_id, name
- order by amenity, name
- """ % dict(category=sql_escape_unicode(cat),
-
tmp_tblname=self._map_areas_table_name,
- amenity=sql_escape_unicode(amenity),
- intersect=intersect))
- sub_al = [(c,a or human,h) for c,a,h in cursor.fetchall()]
- sub_al = self.humanize_amenity_list(sub_al)
- LOG.debug("Got amenities: %s (%d)." % (amenity, len(sub_al)))
- al.extend(sub_al)
- return al
-
- def _render_one_prefix(self, title, output_prefix, file_type,
- paperwidth, paperheight):
- file_type = file_type.lower()
- frame_width = int(max(paperheight / 20., 30))
-
- output_filename = "%s_index.%s" % (output_prefix, file_type)
-
- generator = IndexPageGenerator(self.streets + self.amenities,
self.i18n)
-
- if file_type == 'xml':
- LOG.debug('not rendering index as xml (not supported)')
- return
-
- elif file_type == 'csv':
- try:
- writer = csv.writer(open(output_filename, 'w'))
- except Exception,ex:
- LOG.warning('error while opening destination file %s: %s'
- % (output_filename, ex))
- else:
- # Try to treat indifferently unicode and str in CSV rows
- def csv_writerow(row):
- _r = []
- for e in row:
- if type(e) is unicode:
- _r.append(e.encode('UTF-8'))
- else:
- _r.append(e)
- return writer.writerow(_r)
-
- copyright_notice = (u'© %(year)d MapOSMatic/ocitysmap authors.
'
- u'Map data © %(year)d OpenStreetMap.org '
- u'and contributors (CC-BY-SA)' %
- {'year': datetime.date.today().year})
- if title is not None:
- csv_writerow(['# (UTF-8)', title, copyright_notice])
- else:
- csv_writerow(['# (UTF-8)', '', copyright_notice])
-
- for entry in self.streets + self.amenities:
- csv_writerow(entry)
- return
-
- if file_type in ('png', 'png24'):
- cairo_factory = \
- lambda w,h: cairo.ImageSurface(cairo.FORMAT_RGB24, w, h)
-
- elif file_type == 'svg':
- cairo_factory = lambda w,h: cairo.SVGSurface(output_filename, w, h)
-
- elif file_type == 'svgz':
- def cairo_factory(w,h):
- gz = gzip.GzipFile(output_filename, 'wb')
- return cairo.SVGSurface(gz, w, h)
-
- elif file_type == 'pdf':
- cairo_factory = lambda w,h: cairo.PDFSurface(output_filename, w, h)
-
- elif file_type == 'ps':
- cairo_factory = lambda w,h: cairo.PSSurface(output_filename, w, h)
-
- else:
- raise ValueError('Unsupported output format: %s' % file_type)
-
- if title is not None:
- surface = cairo_factory(paperwidth + frame_width*2,
- paperheight + frame_width*2)
- enclose_in_frame(lambda ctx: generator.render(ctx, paperwidth,
- paperheight),
- paperwidth, paperheight,
- title, surface,
- paperwidth + frame_width*2,
- paperheight + frame_width*2, frame_width)
- else:
- surface = cairo_factory(paperwidth, paperheight)
- ctx = cairo.Context(surface)
- generator.render(ctx, paperwidth, paperheight)
-
- surface.flush()
-
- if file_type in ('png', 'png24'):
- surface.write_to_png(output_filename)
-
- surface.finish()
-
- def render_index(self, title, output_prefix, output_format,
- paperwidth, paperheight):
-
- if not self.streets:
- LOG.warning('No street to write to index')
- return
-
- for fmt in output_format:
- LOG.debug('saving %s.%s...' % (output_prefix, fmt))
- try:
- self._render_one_prefix(title, output_prefix, fmt,
- paperwidth, paperheight)
- except:
- print >>sys.stderr, \
- "Error while rendering %s:" % (fmt)
- traceback.print_exc()
- # Not fatal !
-
-
- def render_map_into_files(self, title,
- out_prefix, out_formats,
- zoom_factor):
- """
- Render the current boundingbox into the destination files.
- @param title (string/None) title of the map, or None: no frame
- @param out_prefix (string) prefix to use for generated files
- @param out_formats (iterable of strings) format of image files
- to generate
- @param zoom_factor None, a tuple (pixels_x, pixel_y) or
- 'zoom:X' with X an integer [1..18]
- returns the MApnik map object used to render the map
- """
- # Create a temporary dir for the shapefiles and call _render_into_files
-
- osm_map_file = self.parser.get('mapnik', 'map')
- if not os.path.exists(osm_map_file):
- raise IOError, 'Invalid path to the osm.xml file (%s)' %
osm_map_file
-
- tmpdir = tempfile.mkdtemp(prefix='ocitysmap')
- LOG.debug('rendering tmp dir: %s' % tmpdir)
- try:
- return self._render_map_into_files(tmpdir, osm_map_file,
- title,
- out_prefix, out_formats,
- zoom_factor)
- finally:
- 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)
-
- def _render_map_into_files(self, tmpdir,
- osm_map_file, title, out_prefix, out_formats,
- zoom_factor):
- # Does the real job of rendering the map
- GRID_COLOR = '#8BB381'
- LOG.debug('rendering from %s to %s.%s...' % (osm_map_file,
- out_prefix,
- out_formats))
- bbox =
self.boundingbox.create_expanded(self.griddesc.height_square_angle*.7,
-
self.griddesc.width_square_angle*.7)
- LOG.debug('bbox is: %s' % bbox)
- city = map_canvas.MapCanvas(osm_map_file, bbox, zoom_factor)
- LOG.debug('adding labels...')
-
- # Add the greyed-out area
- if self.contour is not None:
- path_contour = os.path.join(tmpdir, 'contour.shp')
- map_canvas.create_shapefile_polygon_from_wkt(path_contour,
- self.contour)
- city.add_shapefile(path_contour, str_color = 'grey', alpha = .1)
-
- # Determine font size, depending on the zoom factor
- half_km_in_pixels = city.one_meter_in_pixels * 500.
- LOG.debug('500m = %f pixels' % half_km_in_pixels)
- if half_km_in_pixels < 10:
- font_size = 6
- line_width = 1
- elif half_km_in_pixels < 25:
- font_size = 10
- line_width = 1
- elif half_km_in_pixels < 50:
- font_size = 20
- line_width = 2
- elif half_km_in_pixels < 100:
- font_size = 40
- line_width = 3
- elif half_km_in_pixels < 150:
- font_size = 65
- line_width = 4
- elif half_km_in_pixels < 200:
- font_size = 80
- line_width = 5
- elif half_km_in_pixels < 400:
- font_size = 120
- line_width = 6
- else:
- font_size = 200
- line_width = 7
-
- # Add the grid
- g = self.griddesc.generate_shape_file(os.path.join(tmpdir,
- 'grid.shp'), bbox)
- city.add_shapefile(g.get_filepath(), GRID_COLOR, .6, line_width)
-
- # Add the labels
- for idx, label in enumerate(self.griddesc.vertical_labels):
- x = self.griddesc.vertical_lines[idx] \
- + self.griddesc.width_square_angle/2.
- y = self.griddesc.horizontal_lines[0] \
- + self.griddesc.height_square_angle*.35
- city.add_label(x, y, label,
- str_color = GRID_COLOR,
- alpha = .9,
- font_size = font_size,
- font_family = "DejaVu Sans Bold")
- for idx, label in enumerate(self.griddesc.horizontal_labels):
- x = self.griddesc.vertical_lines[0] \
- - self.griddesc.width_square_angle*.35
- y = self.griddesc.horizontal_lines[idx] \
- - self.griddesc.height_square_angle/2.
- city.add_label(x, y, label,
- str_color = GRID_COLOR,
- alpha = .9,
- font_size = font_size,
- font_family = "DejaVu Sans Bold")
-
- # Add the scale
- T = self.griddesc.generate_scale_shape_file(os.path.join(tmpdir,
- 'scale.shp'),
- bbox.get_bottom_right()[0])
- if T is not None:
- s, lat, lg = T
- city.add_shapefile(s.get_filepath(), 'black', .9, 1)
- city.add_label(lg, lat, "500m", font_size = 16, str_color =
'black')
-
- # Determine parameters
- try:
- copyright_logo = self.parser.get('ocitysmap', 'copyright_logo')
- except Exception:
- copyright_logo = None
-
- # Rendering...
- LOG.info("Rendering generated map to %s outputs..."
- % ', '.join(out_formats))
- _map = city.render_map()
- for fmt in out_formats:
- LOG.debug('saving %s.%s...' % (out_prefix, fmt))
- try:
- city.save_map("%s.%s" % (out_prefix, fmt),
- title,
- file_type = fmt,
- copyright_logo_png = copyright_logo)
- except:
- print >>sys.stderr, \
- "Error while rendering %s:" % (fmt)
- traceback.print_exc()
- # Not fatal !
-
- return _map
diff --git a/ocitysmap/utils.py b/ocitysmap/utils.py
deleted file mode 100644
index 637e4bc..0000000
--- a/ocitysmap/utils.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# 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/>.
-
-
-def gen_vertical_square_label(x):
- label = ''
- while x != -1:
- label = chr(ord('A') + x % 26) + label
- x /= 26
- x -= 1
- return label
-
-def gen_horizontal_square_label(x):
- return str(x + 1)
--
1.7.4.1
- [Maposmatic-dev] [PATCH] ocitysmap: get rid of oc itysmap v1,
Thomas Petazzoni <=