kopia lustrzana https://github.com/hholzgra/ocitysmap
Rearranged files and sources for index rendering
Signed-off-by: David Decotigny <d2@maposmatic.org>stable
rodzic
54d5c4073f
commit
5885e3a630
|
|
@ -405,7 +405,9 @@ class OCitySMap:
|
|||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
o = OCitySMap(['/home/sam/src/python/maposmatic/ocitysmap/ocitysmap.conf.mine'])
|
||||
## o = OCitySMap(['/home/sam/src/python/maposmatic/ocitysmap/ocitysmap.conf.mine'])
|
||||
o = OCitySMap([os.path.join(os.path.dirname(__file__)), '..',
|
||||
'ocitysmap.conf.mine'])
|
||||
|
||||
c = RenderingConfiguration()
|
||||
c.title = 'Chevreuse'
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
import cairo
|
||||
import pango
|
||||
|
||||
def _draw_text_left(ctx, pc, layout, fascent, fheight,
|
||||
def draw_text_left(ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y, text):
|
||||
"""Draws the given text left aligned into the provided Cairo context
|
||||
through the Pango layout.
|
||||
|
|
@ -42,7 +42,7 @@ def _draw_text_left(ctx, pc, layout, fascent, fheight,
|
|||
pc.show_layout(layout)
|
||||
return baseline_x + width, baseline_y
|
||||
|
||||
def _draw_text_right(ctx, pc, layout, fascent, fheight,
|
||||
def draw_text_right(ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y, text):
|
||||
"""Draws the given text right aligned into the provided Cairo context
|
||||
through the Pango layout.
|
||||
|
|
@ -59,7 +59,7 @@ def _draw_text_right(ctx, pc, layout, fascent, fheight,
|
|||
pc.show_layout(layout)
|
||||
return baseline_x + layout.get_width() / pango.SCALE - width, baseline_y
|
||||
|
||||
def _draw_dotted_line(ctx, line_width, baseline_x, baseline_y, length):
|
||||
def draw_dotted_line(ctx, line_width, baseline_x, baseline_y, length):
|
||||
ctx.set_line_width(line_width)
|
||||
ctx.set_dash([line_width, line_width*2])
|
||||
ctx.move_to(baseline_x, baseline_y)
|
||||
|
|
|
|||
|
|
@ -28,195 +28,13 @@ import math
|
|||
import pango
|
||||
import pangocairo
|
||||
|
||||
import coords
|
||||
import draw_utils
|
||||
import grid
|
||||
from ocitysmap2 import coords, draw_utils, grid
|
||||
from indexer import StreetIndex
|
||||
from render import StreetIndexRenderer
|
||||
from commons import IndexCategory, IndexItem
|
||||
|
||||
l = logging.getLogger('ocitysmap')
|
||||
|
||||
class IndexEmptyError(Exception):
|
||||
pass
|
||||
|
||||
class IndexDoesNotFitError(Exception):
|
||||
pass
|
||||
|
||||
class IndexCategory:
|
||||
name = None
|
||||
items = None
|
||||
|
||||
def __init__(self, name, items):
|
||||
self.name, self.items = name, items
|
||||
|
||||
def __str__(self):
|
||||
return '<%s (%s)>' % (self.name, map(str, self.items))
|
||||
|
||||
def draw(self, rtl, ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y):
|
||||
"""Draw this category header.
|
||||
|
||||
Args:
|
||||
...
|
||||
"""
|
||||
|
||||
ctx.save()
|
||||
ctx.set_source_rgb(0.9, 0.9, 0.9)
|
||||
ctx.rectangle(baseline_x, baseline_y - fascent,
|
||||
layout.get_width() / pango.SCALE, fheight)
|
||||
ctx.fill()
|
||||
|
||||
ctx.set_source_rgb(0.0, 0.0, 0.0)
|
||||
ctx.move_to(baseline_x,
|
||||
baseline_y - fascent)
|
||||
layout.set_alignment(pango.ALIGN_CENTER)
|
||||
layout.set_text(self.name)
|
||||
pc.show_layout(layout)
|
||||
ctx.restore()
|
||||
|
||||
def get_all_item_labels(self):
|
||||
return [x.label for x in self.items]
|
||||
|
||||
def get_all_item_squares(self):
|
||||
return [x.squares for x in self.items]
|
||||
|
||||
class IndexItem:
|
||||
__slots__ = ['label', 'squares']
|
||||
label = None
|
||||
squares = None
|
||||
|
||||
def __init__(self, label, squares):
|
||||
self.label, self.squares = label, squares
|
||||
|
||||
def __str__(self):
|
||||
return '%s...%s' % (self.label, self.squares)
|
||||
|
||||
def draw(self, rtl, ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y):
|
||||
"""Draw this index item to the provided Cairo context. It prints the
|
||||
label, the squares definition and the dotted line, with respect to the
|
||||
RTL setting.
|
||||
|
||||
Args:
|
||||
rtl (boolean): right-to-left localization.
|
||||
ctx (cairo.Context): the Cairo context to draw to.
|
||||
pc (pangocairo.PangoCairo): the PangoCairo context for text
|
||||
drawing.
|
||||
layout (pango.Layout): the Pango layout to use for text
|
||||
rendering, pre-configured with the appropriate font.
|
||||
fascent (int): font ascent.
|
||||
fheight (int): font height.
|
||||
baseline_x (int): X axis coordinate of the baseline.
|
||||
baseline_y (int): Y axis coordinate of the baseline.
|
||||
"""
|
||||
|
||||
width = layout.get_width() / pango.SCALE
|
||||
|
||||
ctx.save()
|
||||
if not rtl:
|
||||
line_start, _ = _draw_text_left(ctx, pc, layout,
|
||||
fascent, fheight, baseline_x, baseline_y, self.label)
|
||||
line_end, _ = _draw_text_right(ctx, pc, layout,
|
||||
fascent, fheight, baseline_x, baseline_y,
|
||||
self.squares)
|
||||
else:
|
||||
line_start, _ = _draw_text_left(ctx, pc, layout,
|
||||
fascent, fheight, baseline_x, baseline_y,
|
||||
self.squares)
|
||||
line_end, _ = _draw_text_right(ctx, pc, layout,
|
||||
fascent, fheight, baseline_x, baseline_y,
|
||||
self.label)
|
||||
|
||||
_draw_dotted_line(ctx, max(fheight/12, 1),
|
||||
line_start + fheight/4, baseline_y,
|
||||
line_end - line_start - fheight/2)
|
||||
ctx.restore()
|
||||
|
||||
|
||||
class StreetIndex:
|
||||
def __init__(self, db, osmid, bounding_box, i18n, grid, polygon):
|
||||
self._db = db
|
||||
self._osmid = osmid
|
||||
self._bounding_box = bounding_box
|
||||
self._i18n = i18n
|
||||
self._grid = grid
|
||||
self._polygon = polygon
|
||||
|
||||
def _humanize_street_label(self, street):
|
||||
return (self._i18n.user_readable_street(street[0]),
|
||||
self._user_readable_label(street[1]))
|
||||
|
||||
def _humanize_street_list(self, sl):
|
||||
"""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.
|
||||
|
||||
Args:
|
||||
sl (list): list of streets, each in the form [(name, squares)].
|
||||
|
||||
Returns the humanized street list.
|
||||
"""
|
||||
|
||||
# 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())
|
||||
try:
|
||||
sl = sorted(map(self._humanize_street_label, sl),
|
||||
lambda x, y: locale.strcoll(x[0].lower(), y[0].lower()))
|
||||
finally:
|
||||
locale.setlocale(locale.LC_COLLATE, prev_locale)
|
||||
|
||||
result = []
|
||||
first_letter = None
|
||||
current_category = None
|
||||
for street in sl:
|
||||
if not self._i18n.first_letter_equal(street[0][0], first_letter):
|
||||
current_category = IndexCategory(street[0])
|
||||
result.append(current_category)
|
||||
current_category.items.append(IndexItem(street[0], street[1]))
|
||||
|
||||
return result
|
||||
|
||||
def get_streets(self):
|
||||
"""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 = self._db.cursor()
|
||||
l.info("Getting streets...")
|
||||
|
||||
intersect = 'true'
|
||||
if self._polygon:
|
||||
intersect = """st_intersects(way, st_transform(
|
||||
GeomFromText('%s', 4002), 900913))""" % self._polygon
|
||||
|
||||
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()
|
||||
l.debug("Got streets (%d)." % len(sl))
|
||||
return self.humanize_street_list(sl)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import random
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ocitysmap, city map and street index generator from OpenStreetMap data
|
||||
# Copyright (C) 2010 David Decotigny
|
||||
# Copyright (C) 2010 Frédéric Lehobey
|
||||
# Copyright (C) 2010 Pierre Mauduit
|
||||
# Copyright (C) 2010 David Mentré
|
||||
# Copyright (C) 2010 Maxime Petazzoni
|
||||
# Copyright (C) 2010 Thomas Petazzoni
|
||||
# Copyright (C) 2010 Gaël Utard
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pango
|
||||
|
||||
from ocitysmap2 import draw_utils
|
||||
|
||||
|
||||
class IndexEmptyError(Exception):
|
||||
pass
|
||||
|
||||
class IndexDoesNotFitError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class IndexCategory:
|
||||
name = None
|
||||
items = None
|
||||
|
||||
def __init__(self, name, items):
|
||||
self.name, self.items = name, items
|
||||
|
||||
def __str__(self):
|
||||
return '<%s (%s)>' % (self.name, map(str, self.items))
|
||||
|
||||
def draw(self, rtl, ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y):
|
||||
"""Draw this category header.
|
||||
|
||||
Args:
|
||||
...
|
||||
"""
|
||||
|
||||
ctx.save()
|
||||
ctx.set_source_rgb(0.9, 0.9, 0.9)
|
||||
ctx.rectangle(baseline_x, baseline_y - fascent,
|
||||
layout.get_width() / pango.SCALE, fheight)
|
||||
ctx.fill()
|
||||
|
||||
ctx.set_source_rgb(0.0, 0.0, 0.0)
|
||||
ctx.move_to(baseline_x,
|
||||
baseline_y - fascent)
|
||||
layout.set_alignment(pango.ALIGN_CENTER)
|
||||
layout.set_text(self.name)
|
||||
pc.show_layout(layout)
|
||||
ctx.restore()
|
||||
|
||||
def get_all_item_labels(self):
|
||||
return [x.label for x in self.items]
|
||||
|
||||
def get_all_item_squares(self):
|
||||
return [x.squares for x in self.items]
|
||||
|
||||
class IndexItem:
|
||||
__slots__ = ['label', 'squares']
|
||||
label = None
|
||||
squares = None
|
||||
|
||||
def __init__(self, label, squares):
|
||||
self.label, self.squares = label, squares
|
||||
|
||||
def __str__(self):
|
||||
return '%s...%s' % (self.label, self.squares)
|
||||
|
||||
def draw(self, rtl, ctx, pc, layout, fascent, fheight,
|
||||
baseline_x, baseline_y):
|
||||
"""Draw this index item to the provided Cairo context. It prints the
|
||||
label, the squares definition and the dotted line, with respect to the
|
||||
RTL setting.
|
||||
|
||||
Args:
|
||||
rtl (boolean): right-to-left localization.
|
||||
ctx (cairo.Context): the Cairo context to draw to.
|
||||
pc (pangocairo.PangoCairo): the PangoCairo context for text
|
||||
drawing.
|
||||
layout (pango.Layout): the Pango layout to use for text
|
||||
rendering, pre-configured with the appropriate font.
|
||||
fascent (int): font ascent.
|
||||
fheight (int): font height.
|
||||
baseline_x (int): X axis coordinate of the baseline.
|
||||
baseline_y (int): Y axis coordinate of the baseline.
|
||||
"""
|
||||
|
||||
ctx.save()
|
||||
if not rtl:
|
||||
line_start, _ = draw_utils.draw_text_left(ctx, pc, layout,
|
||||
fascent, fheight,
|
||||
baseline_x, baseline_y,
|
||||
self.label)
|
||||
line_end, _ = draw_utils.draw_text_right(ctx, pc, layout,
|
||||
fascent, fheight,
|
||||
baseline_x, baseline_y,
|
||||
self.squares)
|
||||
else:
|
||||
line_start, _ = draw_utils.draw_text_left(ctx, pc, layout,
|
||||
fascent, fheight,
|
||||
baseline_x, baseline_y,
|
||||
self.squares)
|
||||
line_end, _ = draw_utils.draw_text_right(ctx, pc, layout,
|
||||
fascent, fheight,
|
||||
baseline_x, baseline_y,
|
||||
self.label)
|
||||
|
||||
draw_utils.draw_dotted_line(ctx, max(fheight/12, 1),
|
||||
line_start + fheight/4, baseline_y,
|
||||
line_end - line_start - fheight/2)
|
||||
ctx.restore()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import cairo
|
||||
import pangocairo
|
||||
|
||||
surface = cairo.PDFSurface('/tmp/idx_commons.pdf', 1000, 1000)
|
||||
|
||||
ctx = cairo.Context(surface)
|
||||
pc = pangocairo.CairoContext(ctx)
|
||||
|
||||
font_desc = pango.FontDescription('DejaVu')
|
||||
font_desc.set_size(12 * pango.SCALE)
|
||||
|
||||
layout = pc.create_layout()
|
||||
layout.set_font_description(font_desc)
|
||||
layout.set_width(200 * pango.SCALE)
|
||||
|
||||
font = layout.get_context().load_font(font_desc)
|
||||
font_metric = font.get_metrics()
|
||||
|
||||
fascent = font_metric.get_ascent() / pango.SCALE
|
||||
fheight = ((font_metric.get_ascent() + font_metric.get_descent())
|
||||
/ pango.SCALE)
|
||||
|
||||
first_item = IndexItem('First Item', 'A1')
|
||||
second_item = IndexItem('Second Item', 'A2')
|
||||
category = IndexCategory('Hello world !', [first_item, second_item])
|
||||
|
||||
category.draw(False, ctx, pc, layout, fascent, fheight,
|
||||
72, 80)
|
||||
first_item.draw(False, ctx, pc, layout, fascent, fheight,
|
||||
72, 100)
|
||||
second_item.draw(False, ctx, pc, layout, fascent, fheight,
|
||||
72, 120)
|
||||
|
||||
surface.finish()
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ocitysmap, city map and street index generator from OpenStreetMap data
|
||||
# Copyright (C) 2010 David Decotigny
|
||||
# Copyright (C) 2010 Frédéric Lehobey
|
||||
# Copyright (C) 2010 Pierre Mauduit
|
||||
# Copyright (C) 2010 David Mentré
|
||||
# Copyright (C) 2010 Maxime Petazzoni
|
||||
# Copyright (C) 2010 Thomas Petazzoni
|
||||
# Copyright (C) 2010 Gaël Utard
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
class StreetIndex:
|
||||
def __init__(self, db, osmid, bounding_box, i18n, grid, polygon):
|
||||
self._db = db
|
||||
self._osmid = osmid
|
||||
self._bounding_box = bounding_box
|
||||
self._i18n = i18n
|
||||
self._grid = grid
|
||||
self._polygon = polygon
|
||||
|
||||
def _humanize_street_label(self, street):
|
||||
return (self._i18n.user_readable_street(street[0]),
|
||||
self._user_readable_label(street[1]))
|
||||
|
||||
def _humanize_street_list(self, sl):
|
||||
"""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.
|
||||
|
||||
Args:
|
||||
sl (list): list of streets, each in the form [(name, squares)].
|
||||
|
||||
Returns the humanized street list.
|
||||
"""
|
||||
|
||||
# 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())
|
||||
try:
|
||||
sl = sorted(map(self._humanize_street_label, sl),
|
||||
lambda x, y: locale.strcoll(x[0].lower(), y[0].lower()))
|
||||
finally:
|
||||
locale.setlocale(locale.LC_COLLATE, prev_locale)
|
||||
|
||||
result = []
|
||||
first_letter = None
|
||||
current_category = None
|
||||
for street in sl:
|
||||
if not self._i18n.first_letter_equal(street[0][0], first_letter):
|
||||
current_category = IndexCategory(street[0])
|
||||
result.append(current_category)
|
||||
current_category.items.append(IndexItem(street[0], street[1]))
|
||||
|
||||
return result
|
||||
|
||||
def get_streets(self):
|
||||
"""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 = self._db.cursor()
|
||||
l.info("Getting streets...")
|
||||
|
||||
intersect = 'true'
|
||||
if self._polygon:
|
||||
intersect = """st_intersects(way, st_transform(
|
||||
GeomFromText('%s', 4002), 900913))""" % self._polygon
|
||||
|
||||
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()
|
||||
l.debug("Got streets (%d)." % len(sl))
|
||||
return self.humanize_street_list(sl)
|
||||
|
|
@ -28,6 +28,8 @@ import math
|
|||
import pango
|
||||
import pangocairo
|
||||
|
||||
from ocitysmap2.index import commons
|
||||
|
||||
l = logging.getLogger('ocitysmap')
|
||||
|
||||
class StreetIndexRenderer:
|
||||
|
|
@ -71,7 +73,7 @@ class StreetIndexRenderer:
|
|||
raise ValueError, 'Incompatible freedom direction and alignment!'
|
||||
|
||||
if not self._data:
|
||||
raise IndexEmptyError
|
||||
raise commons.IndexEmptyError
|
||||
|
||||
ctx = cairo.Context(surface)
|
||||
ctx.move_to(x, y)
|
||||
|
|
@ -80,7 +82,8 @@ class StreetIndexRenderer:
|
|||
pc = pangocairo.CairoContext(ctx)
|
||||
|
||||
n_cols, min_dimension = self._compute_columns_split(pc,
|
||||
w, h, 12, 16, freedom_direction)
|
||||
w, h, 12, 16,
|
||||
freedom_direction)
|
||||
|
||||
self._label_fd.set_size(12 * pango.SCALE)
|
||||
self._header_fd.set_size(16 * pango.SCALE)
|
||||
|
|
@ -236,7 +239,7 @@ class StreetIndexRenderer:
|
|||
dedicated to the index on the Cairo surface.
|
||||
|
||||
If the columns split does not fit on the index zone,
|
||||
IndexDoesNotFitError is raised.
|
||||
commons.IndexDoesNotFitError is raised.
|
||||
|
||||
Args:
|
||||
pc (pangocairo.CairoContext): the PangoCairo context.
|
||||
|
|
@ -266,7 +269,7 @@ class StreetIndexRenderer:
|
|||
|
||||
if (n_cols <= 0 or n_cols * tall_width > zone_width_dots or
|
||||
min_required_height > zone_height_dots):
|
||||
raise IndexDoesNotFitError
|
||||
raise commons.IndexDoesNotFitError
|
||||
|
||||
return int(n_cols), min_required_height
|
||||
elif freedom_direction == 'width':
|
||||
|
|
@ -276,7 +279,7 @@ class StreetIndexRenderer:
|
|||
|
||||
if (min_required_width > zone_width_dots or
|
||||
tall_height + extra > n_cols * zone_height_dots):
|
||||
raise IndexDoesNotFitError
|
||||
raise commons.IndexDoesNotFitError
|
||||
|
||||
return int(n_cols), min_required_width
|
||||
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue