Major refactoring of the main rendering process

The RenderingSession system has disappeared in favor of a much simpler
ctor()/render() API for the Renderers. As a consequence, it's the job
of each renderer to create the grid, the map_canvas and the index
renderer (if any). But this is needed because the renderer is in
charge of the page layout, which impacts the way the map_canvas is
rendered and which depends on the index. Potentially every renderer
need to manage the chicken-and-egg problem in their own way. Of
course, now this ctor takes a street_index as parameter, which allows
it (by way of an index renderer) to update the global layout depending
on the size of the index.

For now only the PlainRenderer has been updated. It users an object
placeholder to replace the RenderingSession object. This object
placeholder allows one to define a set of attributes and to access
them through the standard dotted notation. This object has a meaning
only internally to the PlainRenderer::render(). We don't define a
global structure like RenderingSession in order not to pollute the
code with global names. And such an object placeholder is only a
private placeholder for render() and the function it calls, to avoid
having to write 6235 arguments to pass the helper functions it calls.
stable
David Decotigny 2010-09-02 23:43:24 +02:00
rodzic 230e2ccda2
commit 81d0afa312
2 zmienionych plików z 214 dodań i 186 usunięć

Wyświetl plik

@ -381,39 +381,32 @@ class OCitySMap:
# Create a temporary directory for all our shape files
tmpdir = tempfile.mkdtemp(prefix='ocitysmap')
l.debug('Rendering in temporary directory %s' % tmpdir)
# Prepare the main renderer
renderer_cls = renderers.get_renderer_class_by_name(renderer_name)
renderer = renderer_cls(config, tmpdir)
renderer.create_map_canvas()
shade_wkt = self._get_shade_wkt(
renderer.canvas.get_actual_bounding_box(),
config.polygon_wkt)
renderer.add_shade(shade_wkt)
renderer.canvas.render()
street_index_renderer = index.StreetIndexRenderer(
self._i18n,
street_index.categories)
try:
l.debug('Rendering in temporary directory %s' % tmpdir)
# Prepare the generic renderer
renderer_cls = renderers.get_renderer_class_by_name(renderer_name)
renderer = renderer_cls(config, tmpdir, street_index)
# Update the street_index to reflect the grid's actual position
if renderer.grid:
street_index.apply_grid(renderer.grid)
# Perform the actual rendering to the Cairo devices
for output_format in output_formats:
output_filename = '%s.%s' % (file_prefix, output_format)
self._render_one(renderer, street_index_renderer,
output_filename, output_format)
self._render_one(renderer, output_format, output_filename)
# Also dump the CSV street index
street_index.write_to_csv(config.title, '%s.csv' % file_prefix)
finally:
self._cleanup_tempdir(tmpdir)
def _render_one(self, renderer, street_index_renderer, filename,
output_format):
def _render_one(self, renderer, output_format, output_filename):
l.info('Rendering to %s format...' % output_format.upper())
factory = None
dpi = renderers.RenderingSession.PT_PER_INCH
dpi = renderers.UTILS.PT_PER_INCH
if output_format == 'png':
try:
@ -422,17 +415,17 @@ class OCitySMap:
dpi = OCitySMap.DEFAULT_RENDERING_PNG_DPI
factory = lambda w,h: cairo.ImageSurface(cairo.FORMAT_ARGB32,
int(renderers.RenderingSession.pt_to_dots_with_dpi(w, dpi)),
int(renderers.RenderingSession.pt_to_dots_with_dpi(h, dpi)))
int(renderers.UTILS.convert_pt_to_dots_with_dpi(w, dpi)),
int(renderers.UTILS.convert_pt_to_dots_with_dpi(h, dpi)))
elif output_format == 'svg':
factory = lambda w,h: cairo.SVGSurface(filename, w, h)
factory = lambda w,h: cairo.SVGSurface(output_filename, w, h)
elif output_format == 'svgz':
factory = lambda w,h: cairo.SVGSurface(
gzip.GzipFile(filename, 'wb'), w, h)
gzip.GzipFile(output_filename, 'wb'), w, h)
elif output_format == 'pdf':
factory = lambda w,h: cairo.PDFSurface(filename, w, h)
factory = lambda w,h: cairo.PDFSurface(output_filename, w, h)
elif output_format == 'ps':
factory = lambda w,h: cairo.PSSurface(filename, w, h)
factory = lambda w,h: cairo.PSSurface(output_filename, w, h)
elif output_format == 'csv':
# We don't render maps into CSV.
return
@ -442,13 +435,11 @@ class OCitySMap:
'Unsupported output format: %s!' % output_format.upper()
surface = factory(renderer.paper_width_pt, renderer.paper_height_pt)
rs = renderer.create_rendering_session(surface, street_index_renderer,
dpi)
renderer.render(rs)
renderer.render(surface, dpi)
l.debug('Writing %s...' % filename)
l.debug('Writing %s...' % output_filename)
if output_format == 'png':
surface.write_to_png(filename)
surface.write_to_png(output_filename)
surface.finish()

Wyświetl plik

@ -28,31 +28,36 @@ import logging
import mapnik
import math
import os
import re
import pango
import pangocairo
import grid
import map_canvas
from map_canvas import MapCanvas
from grid import Grid
import shapes
l = logging.getLogger('ocitysmap')
class RenderingSession:
class UTILS:
PT_PER_INCH = 72.0
def __init__(self, surface, street_index_renderer,
dpi=PT_PER_INCH):
self.surface = surface
self.street_index_renderer = street_index_renderer
self.dpi = dpi
def pt_to_dots(self, pt):
return RenderingSession.pt_to_dots_with_dpi(pt, self.dpi)
@staticmethod
def convert_pt_to_dots(pt, dpi = PT_PER_INCH):
return float(pt * dpi) / UTILS.PT_PER_INCH
@staticmethod
def pt_to_dots_with_dpi(pt, dpi):
return float(pt * dpi) / RenderingSession.PT_PER_INCH
def convert_mm_to_pt(mm):
return ((mm/10.0) / 2.54) * 72
class _StructPlaceholder:
pass
@staticmethod
def create_struct(**kv):
s = UTILS._StructPlaceholder()
for k,v in kv.iteritems():
setattr(s, k, v)
return s
class Renderer:
@ -60,7 +65,6 @@ class Renderer:
The job of an OCitySMap layout renderer is to lay out the resulting map and
render it from a given rendering configuration.
"""
# Portrait paper sizes in milimeters
PAPER_SIZES = [('A5', 148, 210),
('A4', 210, 297),
@ -94,40 +98,20 @@ class Renderer:
# on the rendered map of a kilometer
DEFAULT_KM_IN_MM = 100
def __init__(self, rc, tmpdir):
self.rc = rc
self.tmpdir = tmpdir
self.canvas = None
self.grid = None
def __init__(self, rc, tmpdir, street_index):
# Note: street_index may be None
self.rc = rc
self.tmpdir = tmpdir
self.grid = None # The implementation is in charge of it
self.street_index = street_index
self.paper_width_pt = \
Renderer.convert_mm_to_pt(self.rc.paper_width_mm)
UTILS.convert_mm_to_pt(self.rc.paper_width_mm)
self.paper_height_pt = \
Renderer.convert_mm_to_pt(self.rc.paper_height_mm)
UTILS.convert_mm_to_pt(self.rc.paper_height_mm)
@staticmethod
def convert_mm_to_pt(mm):
return ((mm/10.0) / 2.54) * 72
def _create_map_canvas(self, graphical_ratio):
"""Creates a map canvas of the given graphical ratio and lays out the
grid on top of it with the canvas's corrected goegraphical bounding
box."""
self.canvas = map_canvas.MapCanvas(self.rc.stylesheet,
self.rc.bounding_box,
graphical_ratio)
# Add the grid
self.grid = grid.Grid(self.canvas.get_actual_bounding_box(),
self.rc.rtl)
grid_shape = self.grid.generate_shape_file(
os.path.join(self.tmpdir, 'grid.shp'))
self.canvas.add_shape_file(grid_shape,
self.rc.stylesheet.grid_line_color,
self.rc.stylesheet.grid_line_alpha,
self.rc.stylesheet.grid_line_width)
def _draw_centered_text(self, ctx, text, x, y):
def _draw_centered_text(ctx, text, x, y):
ctx.save()
xb, yb, tw, th, xa, ya = ctx.text_extents(text)
ctx.move_to(x - tw/2.0 - xb, y - yb/2.0)
@ -135,7 +119,8 @@ class Renderer:
ctx.stroke()
ctx.restore()
def _adjust_font_size(self, layout, fd, constraint_x, constraint_y):
@staticmethod
def _adjust_font_size(layout, fd, constraint_x, constraint_y):
while (layout.get_size()[0] / pango.SCALE < constraint_x and
layout.get_size()[1] / pango.SCALE < constraint_y):
fd.set_size(int(fd.get_size()*1.2))
@ -143,7 +128,8 @@ class Renderer:
fd.set_size(int(fd.get_size()/1.2))
layout.set_font_description(fd)
def _get_osm_logo(self, ctx, rs, height):
@staticmethod
def _get_osm_logo(ctx, height):
logo_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', 'images', 'osm-logo.png'))
try:
@ -164,84 +150,112 @@ class Renderer:
ctx.restore()
return ctx.pop_group(), png.get_width()*factor
def _draw_labels(self, ctx, map_area_width_dots, map_area_height_dots,
@staticmethod
def _draw_labels(ctx, map_grid,
map_area_width_dots, map_area_height_dots,
grid_legend_margin_dots):
ctx.save()
step_horiz = map_area_width_dots / self.grid.horiz_count
last_horiz_portion = math.modf(self.grid.horiz_count)[0]
step_horiz = map_area_width_dots / map_grid.horiz_count
last_horiz_portion = math.modf(map_grid.horiz_count)[0]
step_vert = map_area_height_dots / self.grid.vert_count
last_vert_portion = math.modf(self.grid.vert_count)[0]
step_vert = map_area_height_dots / map_grid.vert_count
last_vert_portion = math.modf(map_grid.vert_count)[0]
ctx.set_font_size(min(0.75 * grid_legend_margin_dots,
0.5 * step_horiz))
for i, label in enumerate(self.grid.horizontal_labels):
for i, label in enumerate(map_grid.horizontal_labels):
x = i * step_horiz
if i < len(self.grid.horizontal_labels) - 1:
if i < len(map_grid.horizontal_labels) - 1:
x += step_horiz/2.0
elif last_horiz_portion >= 0.3:
x += step_horiz * last_horiz_portion/2.0
else:
continue
self._draw_centered_text(ctx, label,
x, grid_legend_margin_dots/2.0)
self._draw_centered_text(ctx, label,
x, map_area_height_dots -
grid_legend_margin_dots/2.0)
Renderer._draw_centered_text(ctx, label,
x, grid_legend_margin_dots/2.0)
Renderer._draw_centered_text(ctx, label,
x, map_area_height_dots -
grid_legend_margin_dots/2.0)
for i, label in enumerate(self.grid.vertical_labels):
for i, label in enumerate(map_grid.vertical_labels):
y = i * step_vert
if i < len(self.grid.vertical_labels) - 1:
if i < len(map_grid.vertical_labels) - 1:
y += step_vert/2.0
elif last_vert_portion >= 0.3:
y += step_vert * last_vert_portion/2.0
else:
continue
self._draw_centered_text(ctx, label,
grid_legend_margin_dots/2.0, y)
self._draw_centered_text(ctx, label,
map_area_width_dots -
grid_legend_margin_dots/2.0, y)
Renderer._draw_centered_text(ctx, label,
grid_legend_margin_dots/2.0, y)
Renderer._draw_centered_text(ctx, label,
map_area_width_dots -
grid_legend_margin_dots/2.0, y)
ctx.restore()
def add_shade(self, shade_wkt):
# Add the grey shade
shade_shape = shapes.PolyShapeFile(
self.canvas.get_actual_bounding_box(),
def _create_map_canvas(self, graphical_ratio,
draw_contour_shade = True):
# Prepare the map canvas
canvas = MapCanvas(self.rc.stylesheet,
self.rc.bounding_box,
graphical_ratio)
if draw_contour_shade:
# Determine the shade WKT
regexp_polygon = re.compile('^POLYGON\(\(([^)]*)\)\)$')
matches = regexp_polygon.match(self.rc.polygon_wkt)
if not matches:
l.error('Administrative boundary looks invalid!')
return None
inside = matches.groups()[0]
bounding_box \
= canvas.get_actual_bounding_box().create_expanded(0.05, 0.05)
shade_wkt = "MULTIPOLYGON(((%s)),((%s)))" % \
(bounding_box.as_wkt(with_polygon_statement = False), inside)
# Prepare the shade SHP
shade_shape = shapes.PolyShapeFile(
canvas.get_actual_bounding_box(),
os.path.join(self.tmpdir, 'shade.shp'),
'shade')
shade_shape.add_shade_from_wkt(shade_wkt)
self.canvas.add_shape_file(shade_shape, self.rc.stylesheet.shade_color,
self.rc.stylesheet.shade_alpha)
shade_shape.add_shade_from_wkt(shade_wkt)
# The next three methods are to be overloaded by the actual renderer.
# Add the shade SHP to the map
canvas.add_shape_file(shade_shape,
self.rc.stylesheet.shade_color,
self.rc.stylesheet.shade_alpha)
def create_map_canvas(self):
"""Creates the Mapnik map canvas to the appropriate graphical ratio so
it can be scaled and fitted into the zone on the page dedicated to the
map."""
raise NotImplementedError
return canvas
def create_rendering_session(self, surface, street_index_renderer,
dpi=RenderingSession.PT_PER_INCH):
"""
"""
return RenderingSession(surface, street_index_renderer, dpi)
def _create_grid(self, canvas):
# Prepare the grid SHP
map_grid = Grid(canvas.get_actual_bounding_box(), self.rc.rtl)
grid_shape = map_grid.generate_shape_file(
os.path.join(self.tmpdir, 'grid.shp'))
def render(self, rs):
# Add the grid SHP to the map
canvas.add_shape_file(grid_shape,
self.rc.stylesheet.grid_line_color,
self.rc.stylesheet.grid_line_alpha,
self.rc.stylesheet.grid_line_width)
return map_grid
# The next two methods are to be overloaded by the actual renderer.
def render(self, cairo_surface, dpi):
"""Renders the map, the index and all other visual map features on the
given Cairo surface.
Args:
rs (RenderingSession): the rendering session (containing the
surface, street index renderer, etc.)
cairo_surface (Cairo.Surface): the destination Cairo device
dpi (int): dots per inch of the device
"""
raise NotImplementedError
@ -275,8 +289,8 @@ class PlainRenderer(Renderer):
name = 'plain'
description = 'A basic, full-page layout for the map.'
def __init__(self, rc, tmpdir):
Renderer.__init__(self, rc, tmpdir)
def __init__(self, rc, tmpdir, street_index = None):
Renderer.__init__(self, rc, tmpdir, street_index)
self.grid_legend_margin_pt = \
min(Renderer.GRID_LEGEND_MARGIN_RATIO * self.paper_width_pt,
@ -291,28 +305,17 @@ class PlainRenderer(Renderer):
self.title_margin_pt +
self.copyright_margin_pt))
def create_rendering_session(self, surface, street_index_renderer,
dpi=RenderingSession.PT_PER_INCH):
rs = Renderer.create_rendering_session(self, surface,
street_index_renderer,
dpi)
rs.paper_width_dots = rs.pt_to_dots(self.paper_width_pt)
rs.paper_height_dots = rs.pt_to_dots(self.paper_height_pt)
# Prepare the map, shade and grid
self._map_canvas = self._create_map_canvas(
float(self.map_area_width_pt) /
float(self.map_area_height_pt))
self.grid = self._create_grid(self._map_canvas)
rs.safe_margin_dots = rs.pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT)
rs.grid_legend_margin_dots = rs.pt_to_dots(self.grid_legend_margin_pt)
rs.title_margin_dots = rs.pt_to_dots(self.title_margin_pt)
rs.copyright_margin_dots = rs.pt_to_dots(self.copyright_margin_pt)
# Prepare canvas
self._map_canvas.render()
rs.map_area_width_dots = rs.pt_to_dots(self.map_area_width_pt)
rs.map_area_height_dots = rs.pt_to_dots(self.map_area_height_pt)
return rs
def create_map_canvas(self):
self._create_map_canvas(float(self.map_area_width_pt) /
float(self.map_area_height_pt))
def _draw_copyright_notice(self, ctx, rs, notice=None):
def _draw_copyright_notice(self, ctx, page_layout, notice=None):
today = datetime.date.today()
notice = notice or \
_(u'Copyright © %(year)d MapOSMatic/OCitySMap developers. '
@ -323,12 +326,14 @@ class PlainRenderer(Renderer):
u'You can contribute to improve this map. '
u'See http://wiki.openstreetmap.org')
notice = notice % {'year': today.year, 'date': today.strftime("%d %B %Y")}
notice = notice % {'year': today.year,
'date': today.strftime("%d %B %Y")}
ctx.save()
ctx.translate(0, rs.map_area_height_dots + 0.005 * rs.paper_height_dots)
ctx.translate(0, (page_layout.map_area_height_dots
+ 0.005 * page_layout.paper_height_dots))
width = rs.paper_width_dots - 2*rs.safe_margin_dots
width = page_layout.paper_width_dots - 2*page_layout.safe_margin_dots
pc = pangocairo.CairoContext(ctx)
fd = pango.FontDescription('DejaVu')
@ -336,35 +341,41 @@ class PlainRenderer(Renderer):
layout = pc.create_layout()
layout.set_font_description(fd)
layout.set_text(notice)
self._adjust_font_size(layout, fd, width, rs.copyright_margin_dots)
self._adjust_font_size(layout, fd, width,
page_layout.copyright_margin_dots)
pc.show_layout(layout)
ctx.restore()
def _draw_title(self, ctx, rs, font_face):
def _draw_title(self, ctx, page_layout, font_face):
# Title background
ctx.save()
ctx.set_source_rgb(0.8, 0.9, 0.96)
ctx.rectangle(0, 0, rs.paper_width_dots - 2*rs.safe_margin_dots,
rs.title_margin_dots)
ctx.rectangle(0, 0,
page_layout.paper_width_dots
- 2*page_layout.safe_margin_dots,
page_layout.title_margin_dots)
ctx.fill()
ctx.restore()
# Retrieve and paint the OSM logo
ctx.save()
grp, logo_width = self._get_osm_logo(ctx, rs, 0.8*rs.title_margin_dots)
ctx.translate(rs.paper_width_dots -
2.0 * rs.safe_margin_dots - \
logo_width - 0.1 * rs.title_margin_dots,
0.1 * rs.title_margin_dots)
grp, logo_width = self._get_osm_logo(
ctx, 0.8*page_layout.title_margin_dots)
ctx.translate(page_layout.paper_width_dots -
2.0 * page_layout.safe_margin_dots -
logo_width - 0.1 * page_layout.title_margin_dots,
0.1 * page_layout.title_margin_dots)
ctx.set_source(grp)
ctx.paint_with_alpha(0.5)
ctx.restore()
pc = pangocairo.CairoContext(ctx)
layout = pc.create_layout()
layout.set_width(int((rs.paper_width_dots -
2.0 * rs.safe_margin_dots -
0.1 * rs.title_margin_dots -
layout.set_width(int((page_layout.paper_width_dots -
2.0 * page_layout.safe_margin_dots -
0.1 * page_layout.title_margin_dots -
logo_width) * pango.SCALE))
if not self.rc.rtl: layout.set_alignment(pango.ALIGN_LEFT)
else: layout.set_alignment(pango.ALIGN_RIGHT)
@ -373,61 +384,87 @@ class PlainRenderer(Renderer):
layout.set_font_description(fd)
layout.set_text(self.rc.title)
self._adjust_font_size(layout, fd, layout.get_width(),
0.8 * rs.title_margin_dots)
0.8 * page_layout.title_margin_dots)
ctx.save()
ctx.rectangle(0, 0, rs.paper_width_dots - 2.0 * rs.safe_margin_dots,
rs.title_margin_dots)
ctx.rectangle(0, 0,
page_layout.paper_width_dots
- 2.0 * page_layout.safe_margin_dots,
page_layout.title_margin_dots)
ctx.stroke()
ctx.translate(0.1 * rs.title_margin_dots,
(rs.title_margin_dots -
ctx.translate(0.1 * page_layout.title_margin_dots,
(page_layout.title_margin_dots -
(layout.get_size()[1] / pango.SCALE)) / 2.0)
pc.show_layout(layout)
ctx.restore()
def render(self, rs):
def render(self, cairo_surface, dpi):
"""
Args:
rs (RenderingSession): ...
...
"""
l.info('PlainRenderer rendering on %dx%dmm paper at %d dpi.' %
(self.rc.paper_width_mm, self.rc.paper_height_mm, rs.dpi))
(self.rc.paper_width_mm, self.rc.paper_height_mm, dpi))
ctx = cairo.Context(rs.surface)
rendered_map = self.canvas.get_rendered_map()
# First determine some useful drawing parameters
page_layout = UTILS.create_struct(
paper_width_dots \
= UTILS.convert_pt_to_dots(self.paper_width_pt, dpi),
paper_height_dots \
= UTILS.convert_pt_to_dots(self.paper_height_pt, dpi),
safe_margin_dots \
= UTILS.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT, dpi),
grid_legend_margin_dots \
= UTILS.convert_pt_to_dots(self.grid_legend_margin_pt, dpi),
title_margin_dots \
= UTILS.convert_pt_to_dots(self.title_margin_pt, dpi),
copyright_margin_dots \
= UTILS.convert_pt_to_dots(self.copyright_margin_pt, dpi),
map_area_width_dots \
= UTILS.convert_pt_to_dots(self.map_area_width_pt, dpi),
map_area_height_dots \
= UTILS.convert_pt_to_dots(self.map_area_height_pt, dpi)
)
ctx = cairo.Context(cairo_surface)
rendered_map = self._map_canvas.get_rendered_map()
# Print margin
ctx.translate(rs.safe_margin_dots, rs.safe_margin_dots)
ctx.translate(page_layout.safe_margin_dots,
page_layout.safe_margin_dots)
self._draw_title(ctx, rs, 'Georgia Bold')
self._draw_title(ctx, page_layout, 'Georgia Bold')
# Skip title height
ctx.translate(0, rs.title_margin_dots)
ctx.translate(0, page_layout.title_margin_dots)
# Draw the map, scaled to fit the designated area
ctx.save()
ctx.scale(rs.map_area_width_dots / rendered_map.width,
rs.map_area_height_dots / rendered_map.height)
ctx.scale(page_layout.map_area_width_dots
/ rendered_map.width,
page_layout.map_area_height_dots
/ rendered_map.height)
mapnik.render(rendered_map, ctx)
ctx.restore()
# Draw a rectangle around the map
ctx.rectangle(0, 0, rs.map_area_width_dots, rs.map_area_height_dots)
ctx.rectangle(0, 0, page_layout.map_area_width_dots,
page_layout.map_area_height_dots)
ctx.stroke()
# Place the vertical and horizontal square labels
self._draw_labels(ctx,
rs.map_area_width_dots,
rs.map_area_height_dots,
rs.grid_legend_margin_dots)
self._draw_labels(ctx, self.grid,
page_layout.map_area_width_dots,
page_layout.map_area_height_dots,
page_layout.grid_legend_margin_dots)
self._draw_copyright_notice(ctx, rs)
self._draw_copyright_notice(ctx, page_layout)
# TODO: map scale
# TODO: compass rose
rs.surface.flush()
cairo_surface.flush()
@staticmethod
def get_compatible_paper_sizes(bounding_box, zoom_level,
@ -446,7 +483,7 @@ class PlainRenderer(Renderer):
l.debug('Map represents %dx%dm, needs at least %.1fx%.1fcm '
'on paper.' % (geo_width_m, geo_height_m,
paper_width_mm/10, paper_height_mm/10))
paper_width_mm/10., paper_height_mm/10.))
# Test both portrait and landscape orientations when checking for paper
# sizes.
@ -490,7 +527,6 @@ def get_paper_sizes():
if __name__ == '__main__':
import coords
import cairo
# Hack to fake gettext
try:
@ -519,6 +555,8 @@ if __name__ == '__main__':
self.grid_line_alpha = 0.9
self.grid_line_width = 2
self.zoom_level = 16
self.shade_color = 'black'
self.shade_alpha = 0.7
class RenderingConfigurationMock:
def __init__(self):
@ -528,17 +566,16 @@ if __name__ == '__main__':
self.paper_height_mm = papers[0][2]
self.rtl = False
self.title = 'Au Kremlin-Bycêtre'
self.polygon_wkt = bbox.as_wkt()
config = RenderingConfigurationMock()
plain = renderer_cls(config, '/tmp')
plain = renderer_cls(config, '/tmp', None)
surface = cairo.PDFSurface('/tmp/plain.pdf',
Renderer.convert_mm_to_pt(config.paper_width_mm),
Renderer.convert_mm_to_pt(config.paper_height_mm))
UTILS.convert_mm_to_pt(config.paper_width_mm),
UTILS.convert_mm_to_pt(config.paper_height_mm))
plain.create_map_canvas()
plain.canvas.render()
plain.render(plain.create_rendering_session(surface, None))
plain.render(surface, UTILS.PT_PER_INCH)
surface.finish()
print "Generated /tmp/plain.pdf"