Rearranged files and sources for index rendering

Signed-off-by: David Decotigny <d2@maposmatic.org>
stable
David Decotigny 2010-08-07 12:39:39 +02:00
rodzic 54d5c4073f
commit 5885e3a630
6 zmienionych plików z 294 dodań i 195 usunięć

Wyświetl plik

@ -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'

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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()

Wyświetl plik

@ -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)

Wyświetl plik

@ -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