コード例 #1
0
ファイル: __init__.py プロジェクト: hholzgra/ocitysmap
def render(renderer, ctx):
    svg_path = os.path.abspath(
        os.path.join(os.path.dirname(__file__), '..', '..', '..', '..',
                     'images', 'compass-rose.svg'))

    if not os.path.exists(svg_path):
        logo_path = os.path.join(sys.exec_prefix, 'share', 'images',
                                 'ocitysmap', 'compass-rose.svg')

    if not os.path.exists(svg_path):
        LOG.warning("No compass rose image found")
        return

    h = convert_pt_to_dots(0.05 * renderer.paper_height_pt, renderer.dpi)

    if type(renderer).__name__ == "MultiPageRenderer":
        x = 0
        y = 0
    else:
        x = convert_pt_to_dots(renderer._map_coords[0], renderer.dpi)
        y = convert_pt_to_dots(renderer._map_coords[1], renderer.dpi)

    ctx.save()
    ctx.translate(x + h / 2, y + h / 2)
    rose_grp, rose_width = Renderer._get_svg(ctx, svg_path, h)
    ctx.set_source(rose_grp)
    ctx.paint_with_alpha(0.75)
    ctx.stroke()
    ctx.restore()
コード例 #2
0
    def __init__(self,
                 stylesheet,
                 bounding_box,
                 _width,
                 _height,
                 dpi=72.0,
                 extend_bbox_to_ratio=True):
        """Initialize the map canvas for rendering.

        Args:
            stylesheet (Stylesheet): map stylesheet.
            bounding_box (coords.BoundingBox): geographic bounding box.
            graphical_ratio (float): ratio of the map area (width/height).
            dpi (float): map resolution (default: 72dpi)
            extend_bbox_to_ratio (boolean): allow MapCanvas to extend
            the bounding box to make it match the ratio of the
            provided rendering area. Needed by SinglePageRenderer.
        """

        self._proj = mapnik.Projection(_MAPNIK_PROJECTION)
        self._dpi = dpi

        # This is where the magic of the map canvas happens. Given an original
        # bounding box and a graphical ratio for the output, the bounding box
        # is adjusted (extended) to fill the destination zone. See
        # _fix_bbox_ratio for more details on how this is done.
        orig_envelope = self._project_envelope(bounding_box)
        graphical_ratio = _width / _height

        if extend_bbox_to_ratio:
            off_x, off_y, width, height = self._fix_bbox_ratio(
                orig_envelope.minx, orig_envelope.miny, orig_envelope.width(),
                orig_envelope.height(), graphical_ratio)

            envelope = mapnik.Box2d(off_x, off_y, off_x + width,
                                    off_y + height)
            self._geo_bbox = self._inverse_envelope(envelope)
            LOG.debug('Corrected bounding box from %s to %s, ratio: %.2f.' %
                      (bounding_box, self._geo_bbox, graphical_ratio))
        else:
            envelope = orig_envelope
            self._geo_bbox = bounding_box

        g_width = int(convert_pt_to_dots(_width, dpi))
        g_height = int(convert_pt_to_dots(_height, dpi))

        # Create the Mapnik map with the corrected width and height and zoom to
        # the corrected bounding box ('envelope' in the Mapnik jargon)
        self._map = mapnik.Map(g_width, g_height, _MAPNIK_PROJECTION)
        mapnik.load_map(self._map, stylesheet.path)
        self._map.zoom_to_box(envelope)

        # Added shapes to render
        self._shapes = []

        LOG.debug('MapCanvas rendering map on %dx%dpx.' % (g_width, g_height))
コード例 #3
0
ファイル: draw_utils.py プロジェクト: hholzgra/ocitysmap
def render_page_number(ctx, page_number,
                       usable_area_width_pt, usable_area_height_pt, margin_pt,
                       transparent_background = True):
    """
    Render page number
    """
    ctx.save()
    x_offset = 0
    if page_number % 2:
        x_offset += commons.convert_pt_to_dots(usable_area_width_pt)\
                  - commons.convert_pt_to_dots(margin_pt)
    y_offset = commons.convert_pt_to_dots(usable_area_height_pt)\
             - commons.convert_pt_to_dots(margin_pt)
    ctx.translate(x_offset, y_offset)

    if transparent_background:
        ctx.set_source_rgba(1, 1, 1, 0.6)
    else:
        ctx.set_source_rgba(0.8, 0.8, 0.8, 0.6)
    ctx.rectangle(0, 0, commons.convert_pt_to_dots(margin_pt),
                  commons.convert_pt_to_dots(margin_pt))
    ctx.fill()

    ctx.set_source_rgba(0, 0, 0, 1)
    x_offset = commons.convert_pt_to_dots(margin_pt)/2
    y_offset = commons.convert_pt_to_dots(margin_pt)/2
    ctx.translate(x_offset, y_offset)
    draw_simpletext_center(ctx, unicode(page_number), 0, 0)
    ctx.restore()
コード例 #4
0
ファイル: draw_utils.py プロジェクト: maposmatic/ocitysmap
def render_page_number(ctx,
                       page_number,
                       usable_area_width_pt,
                       usable_area_height_pt,
                       margin_pt,
                       transparent_background=True):
    """
    Render page number
    """
    ctx.save()
    x_offset = 0
    if page_number % 2:
        x_offset += commons.convert_pt_to_dots(usable_area_width_pt)\
                  - commons.convert_pt_to_dots(margin_pt)
    y_offset = commons.convert_pt_to_dots(usable_area_height_pt)\
             - commons.convert_pt_to_dots(margin_pt)
    ctx.translate(x_offset, y_offset)

    if transparent_background:
        ctx.set_source_rgba(1, 1, 1, 0.6)
    else:
        ctx.set_source_rgba(0.8, 0.8, 0.8, 0.6)
    ctx.rectangle(0, 0, commons.convert_pt_to_dots(margin_pt),
                  commons.convert_pt_to_dots(margin_pt))
    ctx.fill()

    ctx.set_source_rgba(0, 0, 0, 1)
    x_offset = commons.convert_pt_to_dots(margin_pt) / 2
    y_offset = commons.convert_pt_to_dots(margin_pt) / 2
    ctx.translate(x_offset, y_offset)
    draw_simpletext_center(ctx, unicode(page_number), 0, 0)
    ctx.restore()
コード例 #5
0
ファイル: __init__.py プロジェクト: hholzgra/ocitysmap
def render(renderer, ctx):
    if renderer.rc.qrcode_text:
        qrcode_text = renderer.rc.qrcode_text
    else:
        qrcode_text = renderer.rc.origin_url

    if not qrcode_text:
        return

    x = convert_pt_to_dots(renderer._map_coords[0], renderer.dpi)
    y = convert_pt_to_dots(renderer._map_coords[1], renderer.dpi)
    w = convert_pt_to_dots(renderer._map_coords[2], renderer.dpi)
    h = convert_pt_to_dots(renderer._map_coords[3], renderer.dpi)
    W = convert_pt_to_dots(renderer.paper_width_pt)

    size = convert_pt_to_dots(
        max(renderer.paper_width_pt, renderer.paper_height_pt),
        renderer.dpi) / 12

    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )

    qr.add_data(qrcode_text)
    qr.make(fit=True)

    img = qr.make_image(image_factory=qrcode.image.svg.SvgPathFillImage,
                        fill_color='lightblue')
    svgstr = BytesIO()
    img.save(svgstr)

    svg_val = svgstr.getvalue()

    rsvg = Rsvg.Handle()
    svg = rsvg.new_from_data(svg_val)
    svgstr.close()

    ctx.save()
    ctx.translate(x + w - size, y + h - size)
    ctx.move_to(0, 0)
    factor = size / svg.props.height
    ctx.scale(factor, factor)
    svg.render_cairo(ctx)
    ctx.restore()
コード例 #6
0
    def render(self, dpi = UTILS.PT_PER_INCH):
        self.ctx.save()

        # Create a PangoCairo context for drawing to Cairo
        pc = pangocairo.CairoContext(self.ctx)

        header_fd = pango.FontDescription("Georgia Bold 12")
        label_column_fd  = pango.FontDescription("DejaVu 8")

        header_layout, header_fascent, header_fheight, header_em = \
            self._create_layout_with_font(pc, header_fd)
        label_layout, label_fascent, label_fheight, label_em = \
            self._create_layout_with_font(pc, label_column_fd)
        column_layout, _, _, _ = \
            self._create_layout_with_font(pc, label_column_fd)

        # By OCitysmap's convention, the default resolution is 72 dpi,
        # which maps to the default pangocairo resolution (96 dpi
        # according to pangocairo docs). If we want to render with
        # another resolution (different from 72), we have to scale the
        # pangocairo resolution accordingly:
        pangocairo.context_set_resolution(column_layout.get_context(),
                                          96.*dpi/UTILS.PT_PER_INCH)
        pangocairo.context_set_resolution(label_layout.get_context(),
                                          96.*dpi/UTILS.PT_PER_INCH)
        pangocairo.context_set_resolution(header_layout.get_context(),
                                          96.*dpi/UTILS.PT_PER_INCH)

        margin = label_em

        # find largest label and location
        max_label_drawing_width = 0.0
        max_location_drawing_width = 0.0
        for category in self.index_categories:
            for street in category.items:
                w = street.label_drawing_width(label_layout)
                if w > max_label_drawing_width:
                    max_label_drawing_width = w

                w = street.location_drawing_width(label_layout)
                if w > max_location_drawing_width:
                    max_location_drawing_width = w

        # No street to render, bail out
        if max_label_drawing_width == 0.0:
            return

        # Find best number of columns
        max_drawing_width = \
            max_label_drawing_width + max_location_drawing_width + 2 * margin
        max_drawing_height = self.rendering_area_h - PAGE_NUMBER_MARGIN_PT

        columns_count = int(math.ceil(self.rendering_area_w / max_drawing_width))
        # following test should not be needed. No time to prove it. ;-)
        if columns_count == 0:
            columns_count = 1

        # We have now have several columns
        column_width = self.rendering_area_w / columns_count

        column_layout.set_width(int(UTILS.convert_pt_to_dots(
                    (column_width - margin) * pango.SCALE, dpi)))
        label_layout.set_width(int(UTILS.convert_pt_to_dots(
                    (column_width - margin - max_location_drawing_width
                     - 2 * label_em)
                    * pango.SCALE, dpi)))
        header_layout.set_width(int(UTILS.convert_pt_to_dots(
                    (column_width - margin) * pango.SCALE, dpi)))

        if not self._i18n.isrtl():
            orig_offset_x = offset_x = margin/2.
            orig_delta_x  = delta_x  = column_width
        else:
            orig_offset_x = offset_x = \
                self.rendering_area_w - column_width + margin/2.
            orig_delta_x  = delta_x  = - column_width

        actual_n_cols = 0
        offset_y = margin/2.

        # page number of first page
        self._draw_page_number()

        for category in self.index_categories:
            if ( offset_y + header_fheight + label_fheight
                 + margin/2. > max_drawing_height ):
                offset_y       = margin/2.
                offset_x      += delta_x
                actual_n_cols += 1

                if actual_n_cols == columns_count:
                    self._new_page()
                    actual_n_cols = 0
                    offset_y = margin / 2.
                    offset_x = orig_offset_x
                    delta_x  = orig_delta_x

            category.draw(self._i18n.isrtl(), self.ctx, pc, header_layout,
                          UTILS.convert_pt_to_dots(header_fascent, dpi),
                          UTILS.convert_pt_to_dots(header_fheight, dpi),
                          UTILS.convert_pt_to_dots(self.rendering_area_x
                                                   + offset_x, dpi),
                          UTILS.convert_pt_to_dots(self.rendering_area_y
                                                   + offset_y
                                                   + header_fascent, dpi))

            offset_y += header_fheight

            for street in category.items:
                label_height = street.label_drawing_height(label_layout)
                if ( offset_y + label_height + margin/2.
                     > max_drawing_height ):
                    offset_y       = margin/2.
                    offset_x      += delta_x
                    actual_n_cols += 1

                    if actual_n_cols == columns_count:
                        self._new_page()
                        actual_n_cols = 0
                        offset_y = margin / 2.
                        offset_x = orig_offset_x
                        delta_x  = orig_delta_x

                street.draw(self._i18n.isrtl(), self.ctx, pc, column_layout,
                            UTILS.convert_pt_to_dots(label_fascent, dpi),
                            UTILS.convert_pt_to_dots(label_fheight, dpi),
                            UTILS.convert_pt_to_dots(self.rendering_area_x
                                                     + offset_x, dpi),
                            UTILS.convert_pt_to_dots(self.rendering_area_y
                                                     + offset_y
                                                     + label_fascent, dpi),
                            label_layout,
                            UTILS.convert_pt_to_dots(label_height, dpi),
                            UTILS.convert_pt_to_dots(max_location_drawing_width,
                                                     dpi))

                offset_y += label_height


        self.ctx.restore()
コード例 #7
0
    def render(self, cairo_surface, dpi, osm_date):
        """Renders the map, the index and all other visual map features on the
        given Cairo surface.

        Args:
            cairo_surface (Cairo.Surface): the destination Cairo device.
            dpi (int): dots per inch of the device.
        """
        LOG.info('SinglePageRenderer rendering -%s- on %dx%dmm paper at %d dpi.' %
                 (self.rc.output_format, self.rc.paper_width_mm, self.rc.paper_height_mm, dpi))

        # First determine some useful drawing parameters
        safe_margin_dots \
            = commons.convert_pt_to_dots(Renderer.PRINT_SAFE_MARGIN_PT, dpi)

        usable_area_width_dots \
            = commons.convert_pt_to_dots(self._usable_area_width_pt, dpi)

        usable_area_height_dots \
            = commons.convert_pt_to_dots(self._usable_area_height_pt, dpi)

        title_margin_dots \
            = commons.convert_pt_to_dots(self._title_margin_pt, dpi)

        copyright_margin_dots \
            = commons.convert_pt_to_dots(self._copyright_margin_pt, dpi)

        map_coords_dots = list(map(lambda l: commons.convert_pt_to_dots(l, dpi),
                              self._map_coords))

        ctx = cairo.Context(cairo_surface)

        # Set a white background
        ctx.save()
        ctx.set_source_rgb(1, 1, 1)
        ctx.rectangle(0, 0, commons.convert_pt_to_dots(self.paper_width_pt, dpi),
                      commons.convert_pt_to_dots(self.paper_height_pt, dpi))
        ctx.fill()
        ctx.restore()

        ##
        ## Draw the map, scaled to fit the designated area
        ##
        ctx.save()

        # prevent map background from filling the full canvas
        ctx.rectangle(map_coords_dots[0], map_coords_dots[1], map_coords_dots[2], map_coords_dots[3])
        ctx.clip()

        # Prepare to draw the map at the right location
        ctx.translate(map_coords_dots[0], map_coords_dots[1])

        # Draw the rescaled Map
        ctx.save()
        scale_factor = int(dpi / 72)
        rendered_map = self._map_canvas.get_rendered_map()
        LOG.debug('Map:')
        LOG.debug('Mapnik scale: 1/%f' % rendered_map.scale_denominator())
        LOG.debug('Actual scale: 1/%f' % self._map_canvas.get_actual_scale())

        # exclude layers based on configuration setting "exclude_layers"
        for layer in rendered_map.layers:
            if layer.name in self.rc.stylesheet.exclude_layers:
                LOG.debug("Excluding layer: %s" % layer.name)
                layer.status = False

        # now perform the actual drawing
        mapnik.render(rendered_map, ctx, scale_factor, 0, 0)
        ctx.restore()

        # Draw the rescaled Overlays
        for overlay_canvas in self._overlay_canvases:
            ctx.save()
            rendered_overlay = overlay_canvas.get_rendered_map()
            LOG.debug('Overlay:') # TODO: overlay name
            mapnik.render(rendered_overlay, ctx, scale_factor, 0, 0)
            ctx.restore()

        # Place the vertical and horizontal square labels
        if self.grid and self.index_position:
            self._draw_labels(ctx, self.grid,
                              map_coords_dots[2],
                              map_coords_dots[3],
                              commons.convert_pt_to_dots(self._grid_legend_margin_pt,
                                                         dpi))
        ctx.restore()

        # Draw a rectangle frame around the map
        ctx.save()
        ctx.set_line_width(1)
        ctx.rectangle(map_coords_dots[0], map_coords_dots[1], map_coords_dots[2], map_coords_dots[3])
        ctx.stroke()
        ctx.restore()

        ##
        ## Draw the title
        ##
        if self.rc.title:
            ctx.save()
            ctx.translate(safe_margin_dots, safe_margin_dots)
            self._draw_title(ctx, usable_area_width_dots,
                             title_margin_dots, 'Droid Sans Bold')
            ctx.restore()

        # make sure that plugins do not render outside the actual map area
        ctx.save()
        ctx.rectangle(map_coords_dots[0], map_coords_dots[1], map_coords_dots[2], map_coords_dots[3])
        ctx.clip()

        # apply effect plugin overlays
        for plugin_name, effect in self._overlay_effects.items():
            try:
                effect.render(self, ctx)
            except Exception as e:
                # TODO better logging
                LOG.warning("Error while rendering overlay: %s\n%s" % (plugin_name, e))
        ctx.restore()

        ##
        ## Draw the index, when applicable
        ##

        # Update the street_index to reflect the grid's actual position
        if self.grid and self.street_index and self.index_position is not None:
            self.street_index.apply_grid(self.grid)

            # Dump the CSV street index
            self.street_index.write_to_csv(self.rc.title, '%s.csv' % self.file_prefix)

        if self._index_renderer and self._index_area:
            ctx.save()

            # NEVER use ctx.scale() here because otherwise pango will
            # choose different dont metrics which may be incompatible
            # with what has been computed by __init__(), which may
            # require more columns than expected !  Instead, we have
            # to trick pangocairo into believing it is rendering to a
            # device with the same default resolution, but with a
            # cairo resolution matching the 'dpi' specified
            # resolution. See
            # index::render::StreetIndexRenederer::render() and
            # comments within.

            self._index_renderer.render(ctx, self._index_area, dpi)

            ctx.restore()

            # Also draw a rectangle frame around the index
            ctx.save()
            ctx.set_line_width(1)
            ctx.rectangle(commons.convert_pt_to_dots(self._index_area.x, dpi),
                          commons.convert_pt_to_dots(self._index_area.y, dpi),
                          commons.convert_pt_to_dots(self._index_area.w, dpi),
                          commons.convert_pt_to_dots(self._index_area.h, dpi))
            ctx.stroke()
            ctx.restore()

        ##
        ## Draw the copyright notice
        ##
        ctx.save()

        # Move to the right position
        ctx.translate(safe_margin_dots,
                      ( safe_margin_dots + title_margin_dots
                        + usable_area_height_dots
                        + copyright_margin_dots/4. ) )

        # Draw the copyright notice
        self._draw_copyright_notice(ctx, usable_area_width_dots,
                                    copyright_margin_dots,
                                    osm_date=osm_date)
        ctx.restore()

        # render index on 2nd page if requested, and output format supports it
        if self.index_position == 'extra_page' and self._has_multipage_format() and self._index_renderer is not None:
            cairo_surface.show_page()

            # We use a fake vector device to determine the actual
            # rendering characteristics
            fake_surface = cairo.PDFSurface(None,
                                            self.paper_width_pt,
                                            self.paper_height_pt)

            usable_area_width_pt = (self.paper_width_pt -
                                          2 * Renderer.PRINT_SAFE_MARGIN_PT)
            usable_area_height_pt = (self.paper_height_pt -
                                           2 * Renderer.PRINT_SAFE_MARGIN_PT)

            index_area = self._index_renderer.precompute_occupation_area(
                fake_surface,
                Renderer.PRINT_SAFE_MARGIN_PT,
                ( self.paper_height_pt
                  - Renderer.PRINT_SAFE_MARGIN_PT
                  - usable_area_height_pt
                ),
                usable_area_width_pt,
                usable_area_height_pt,
                'width', 'left')

            ctx.save()
            self._index_renderer.render(ctx, index_area, dpi)
            ctx.restore()

            cairo_surface.show_page()
        else:
            cairo_surface.flush()
コード例 #8
0
ファイル: multi_page_renderer.py プロジェクト: Nimn/ocitysmap
    def render(self, dpi=UTILS.PT_PER_INCH):
        self.ctx.save()

        # Create a PangoCairo context for drawing to Cairo
        pc = pangocairo.CairoContext(self.ctx)

        header_fd = pango.FontDescription("Georgia Bold 12")
        label_column_fd = pango.FontDescription("DejaVu 8")

        header_layout, header_fascent, header_fheight, header_em = \
            self._create_layout_with_font(pc, header_fd)
        label_layout, label_fascent, label_fheight, label_em = \
            self._create_layout_with_font(pc, label_column_fd)
        column_layout, _, _, _ = \
            self._create_layout_with_font(pc, label_column_fd)

        # By OCitysmap's convention, the default resolution is 72 dpi,
        # which maps to the default pangocairo resolution (96 dpi
        # according to pangocairo docs). If we want to render with
        # another resolution (different from 72), we have to scale the
        # pangocairo resolution accordingly:
        pangocairo.context_set_resolution(column_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)
        pangocairo.context_set_resolution(label_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)
        pangocairo.context_set_resolution(header_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)

        margin = label_em

        # find largest label and location
        max_label_drawing_width = 0.0
        max_location_drawing_width = 0.0
        for category in self.index_categories:
            for street in category.items:
                w = street.label_drawing_width(label_layout)
                if w > max_label_drawing_width:
                    max_label_drawing_width = w

                w = street.location_drawing_width(label_layout)
                if w > max_location_drawing_width:
                    max_location_drawing_width = w

        # No street to render, bail out
        if max_label_drawing_width == 0.0:
            return

        # Find best number of columns
        max_drawing_width = \
            max_label_drawing_width + max_location_drawing_width + 2 * margin
        max_drawing_height = self.rendering_area_h - PAGE_NUMBER_MARGIN_PT

        columns_count = int(
            math.ceil(self.rendering_area_w / max_drawing_width))
        # following test should not be needed. No time to prove it. ;-)
        if columns_count == 0:
            columns_count = 1

        # We have now have several columns
        column_width = self.rendering_area_w / columns_count

        column_layout.set_width(
            int(
                UTILS.convert_pt_to_dots((column_width - margin) * pango.SCALE,
                                         dpi)))
        label_layout.set_width(
            int(
                UTILS.convert_pt_to_dots(
                    (column_width - margin - max_location_drawing_width -
                     2 * label_em) * pango.SCALE, dpi)))
        header_layout.set_width(
            int(
                UTILS.convert_pt_to_dots((column_width - margin) * pango.SCALE,
                                         dpi)))

        if not self._i18n.isrtl():
            orig_offset_x = offset_x = margin / 2.
            orig_delta_x = delta_x = column_width
        else:
            orig_offset_x = offset_x = \
                self.rendering_area_w - column_width + margin/2.
            orig_delta_x = delta_x = -column_width

        actual_n_cols = 0
        offset_y = margin / 2.

        # page number of first page
        self._draw_page_number()

        for category in self.index_categories:
            if (offset_y + header_fheight + label_fheight + margin / 2. >
                    max_drawing_height):
                offset_y = margin / 2.
                offset_x += delta_x
                actual_n_cols += 1

                if actual_n_cols == columns_count:
                    self._new_page()
                    actual_n_cols = 0
                    offset_y = margin / 2.
                    offset_x = orig_offset_x
                    delta_x = orig_delta_x

            category.draw(
                self._i18n.isrtl(), self.ctx, pc, header_layout,
                UTILS.convert_pt_to_dots(header_fascent, dpi),
                UTILS.convert_pt_to_dots(header_fheight, dpi),
                UTILS.convert_pt_to_dots(self.rendering_area_x + offset_x,
                                         dpi),
                UTILS.convert_pt_to_dots(
                    self.rendering_area_y + offset_y + header_fascent, dpi))

            offset_y += header_fheight

            for street in category.items:
                label_height = street.label_drawing_height(label_layout)
                if (offset_y + label_height + margin / 2. >
                        max_drawing_height):
                    offset_y = margin / 2.
                    offset_x += delta_x
                    actual_n_cols += 1

                    if actual_n_cols == columns_count:
                        self._new_page()
                        actual_n_cols = 0
                        offset_y = margin / 2.
                        offset_x = orig_offset_x
                        delta_x = orig_delta_x

                street.draw(
                    self._i18n.isrtl(), self.ctx, pc, column_layout,
                    UTILS.convert_pt_to_dots(label_fascent, dpi),
                    UTILS.convert_pt_to_dots(label_fheight, dpi),
                    UTILS.convert_pt_to_dots(self.rendering_area_x + offset_x,
                                             dpi),
                    UTILS.convert_pt_to_dots(
                        self.rendering_area_y + offset_y + label_fascent, dpi),
                    label_layout, UTILS.convert_pt_to_dots(label_height, dpi),
                    UTILS.convert_pt_to_dots(max_location_drawing_width, dpi))

                offset_y += label_height

        self.ctx.restore()
コード例 #9
0
ファイル: renderer.py プロジェクト: Nimn/ocitysmap
    def render(self, ctx, rendering_area, dpi = UTILS.PT_PER_INCH):
        """
        Render the street and amenities index at the given (x,y)
        coordinates into the provided Cairo surface. The index must
        not be larger than the provided surface (use
        precompute_occupation_area() to adjust it).

        Args:
            ctx (cairo.Context): the cairo context to use for the rendering.
            rendering_area (StreetIndexRenderingArea): the result from
                precompute_occupation_area().
            dpi (number): resolution of the target device.
        """

        if not self._index_categories:
            raise commons.IndexEmptyError

        LOG.debug("Rendering the street index within %s at %sdpi..."
                  % (rendering_area, dpi))

        ##
        ## In the following, the algorithm only manipulates values
        ## expressed in 'pt'. Only the drawing-related functions will
        ## translate them to cairo units
        ##

        ctx.save()
        ctx.move_to(UTILS.convert_pt_to_dots(rendering_area.x, dpi),
                    UTILS.convert_pt_to_dots(rendering_area.y, dpi))

        # Create a PangoCairo context for drawing to Cairo
        pc = pangocairo.CairoContext(ctx)

        header_fd = pango.FontDescription(
            rendering_area.rendering_style.header_font_spec)
        label_fd  = pango.FontDescription(
            rendering_area.rendering_style.label_font_spec)

        header_layout, header_fascent, header_fheight, header_em = \
                self._create_layout_with_font(pc, header_fd)
        label_layout, label_fascent, label_fheight, label_em = \
                self._create_layout_with_font(pc, label_fd)

        #print "RENDER", header_layout, header_fascent, header_fheight, header_em
        #print "RENDER", label_layout, label_fascent, label_fheight, label_em

        # By OCitysmap's convention, the default resolution is 72 dpi,
        # which maps to the default pangocairo resolution (96 dpi
        # according to pangocairo docs). If we want to render with
        # another resolution (different from 72), we have to scale the
        # pangocairo resolution accordingly:
        pangocairo.context_set_resolution(label_layout.get_context(),
                                          96.*dpi/UTILS.PT_PER_INCH)
        pangocairo.context_set_resolution(header_layout.get_context(),
                                          96.*dpi/UTILS.PT_PER_INCH)
        # All this is because we want pango to have the exact same
        # behavior as with the default 72dpi resolution. If we instead
        # decided to call cairo::scale, then pango might choose
        # different font metrics which don't fit in the prepared
        # layout anymore...

        margin = label_em
        column_width = int(rendering_area.w / rendering_area.n_cols)

        label_layout.set_width(int(UTILS.convert_pt_to_dots(
                    (column_width - margin) * pango.SCALE, dpi)))
        header_layout.set_width(int(UTILS.convert_pt_to_dots(
                    (column_width - margin) * pango.SCALE, dpi)))

        if not self._i18n.isrtl():
            offset_x = margin/2.
            delta_x  = column_width
        else:
            offset_x = rendering_area.w - column_width + margin/2.
            delta_x  = - column_width

        actual_n_cols = 1
        offset_y = margin/2.
        for category in self._index_categories:
            if ( offset_y + header_fheight + label_fheight
                 + margin/2. > rendering_area.h ):
                offset_y       = margin/2.
                offset_x      += delta_x
                actual_n_cols += 1

            category.draw(self._i18n.isrtl(), ctx, pc, header_layout,
                          UTILS.convert_pt_to_dots(header_fascent, dpi),
                          UTILS.convert_pt_to_dots(header_fheight, dpi),
                          UTILS.convert_pt_to_dots(rendering_area.x
                                                   + offset_x, dpi),
                          UTILS.convert_pt_to_dots(rendering_area.y
                                                   + offset_y
                                                   + header_fascent, dpi))

            offset_y += header_fheight

            for street in category.items:
                if ( offset_y + label_fheight + margin/2.
                     > rendering_area.h ):
                    offset_y       = margin/2.
                    offset_x      += delta_x
                    actual_n_cols += 1

                street.draw(self._i18n.isrtl(), ctx, pc, label_layout,
                            UTILS.convert_pt_to_dots(label_fascent, dpi),
                            UTILS.convert_pt_to_dots(label_fheight, dpi),
                            UTILS.convert_pt_to_dots(rendering_area.x
                                                     + offset_x, dpi),
                            UTILS.convert_pt_to_dots(rendering_area.y
                                                     + offset_y
                                                     + label_fascent, dpi))

                offset_y += label_fheight

        # Restore original context
        ctx.restore()

        # Simple verification...
        if actual_n_cols < rendering_area.n_cols:
            LOG.warning("Rounding/security margin lost some space (%d actual cols vs. allocated %d" % (actual_n_cols, rendering_area.n_cols))
        assert actual_n_cols <= rendering_area.n_cols
コード例 #10
0
    def render(self, dpi=UTILS.PT_PER_INCH):
        self.ctx.save()

        # Create a PangoCairo context for drawing to Cairo
        pc = PangoCairo.create_context(self.ctx)

        city_fd = Pango.FontDescription("DejaVu Sans Condensed Bold 18")
        header_fd = Pango.FontDescription("DejaVu Sans Condensed Bold 12")
        label_column_fd = Pango.FontDescription("DejaVu 6")

        city_layout, city_fascent, city_fheight, city_em = \
            self._create_layout_with_font(self.ctx, pc, city_fd)
        header_layout, header_fascent, header_fheight, header_em = \
            self._create_layout_with_font(self.ctx, pc, header_fd)
        label_layout, label_fascent, label_fheight, label_em = \
            self._create_layout_with_font(self.ctx, pc, label_column_fd)
        column_layout, _, _, _ = \
            self._create_layout_with_font(self.ctx, pc, label_column_fd)

        # By OCitysmap's convention, the default resolution is 72 dpi,
        # which maps to the default pangocairo resolution (96 dpi
        # according to pangocairo docs). If we want to render with
        # another resolution (different from 72), we have to scale the
        # pangocairo resolution accordingly:
        PangoCairo.context_set_resolution(city_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)
        PangoCairo.context_set_resolution(column_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)
        PangoCairo.context_set_resolution(label_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)
        PangoCairo.context_set_resolution(header_layout.get_context(),
                                          96. * dpi / UTILS.PT_PER_INCH)

        margin = label_em
        cityBlockHeight = city_fheight * 2

        index_area_w_pt = self.rendering_area_w - 2 * self.print_bleed_pt - self.margin_inside_pt - self.margin_outside_pt
        city_layout.set_width(
            int(UTILS.convert_pt_to_dots((index_area_w_pt) * Pango.SCALE,
                                         dpi)))

        self._draw_page_stroke()
        self._draw_page_content_stroke()

        citiesWithEntries = {
            k: v
            for k, v in self.index_categories.items() if len(v) > 0
        }  # print only cities with entries
        cities = list(citiesWithEntries.keys())
        cities.sort()

        margin_top = self.print_bleed_pt + self.margin_top_bottom_pt
        margin_top_page = margin_top
        offset_y = margin_top_page
        max_drawing_height = 0
        city_index = -1
        LOG.debug(
            "%f print_bleed_pt, %f print_safe_margin_pt, %f margin_top_bottom_pt, %f margin_top"
            % (self.print_bleed_pt, self.print_safe_margin_pt,
               self.margin_top_bottom_pt, margin_top))
        page_full_available_h = self.rendering_area_h - 2 * self.print_bleed_pt - 2 * self.margin_top_bottom_pt

        content_width = self.rendering_area_w - 2 * Renderer.PRINT_BLEED_PT
        content_height = self.rendering_area_h - 2 * Renderer.PRINT_BLEED_PT
        margin_x = self.print_bleed_pt
        margin_y = self.print_bleed_pt

        if Renderer.DEBUG:  # red stroke dash1: show area excluding bleed-difference
            self.ctx.save()
            self.ctx.set_source_rgba(1, 0.4, 0.4, .75)
            self.ctx.set_dash([1.0, 1.0], 1.0 / 2.0)
            self.ctx.rectangle(margin_x, margin_y, content_width,
                               content_height)
            self.ctx.stroke()
            self.ctx.restore()

        content_width -= self.margin_inside_pt + self.margin_outside_pt
        content_height -= 2 * self.margin_top_bottom_pt
        margin_x += (self.margin_inside_pt if
                     (self.index_page_num + self.page_offset) %
                     2 else self.margin_outside_pt)
        margin_y += self.margin_top_bottom_pt

        #LOG.debug(list(filter(lambda x: len(self.index_categories[cities[x]]) > 0, cities)))
        for city in cities:
            city_index = city_index + 1

            margin_top_page += max_drawing_height  # add max drawing height of previous city
            index_area_h_pt = self.rendering_area_h - self.print_bleed_pt - self.margin_top_bottom_pt - margin_top_page
            LOG.debug("============")
            LOG.debug(
                "printing index for city '%s'. available area: %f x %f, margin_top_page: %f"
                % (city, index_area_w_pt, index_area_h_pt, margin_top_page))

            if (margin_top_page > (page_full_available_h * 4 / 5)):
                LOG.debug("NEW PAGE: margin_top_page (%f) > %f" %
                          (margin_top_page, page_full_available_h * 4 / 5))
                self._new_page()
                self._draw_page_stroke()
                self._draw_page_content_stroke()
                margin_top = self.print_bleed_pt + self.margin_top_bottom_pt
                margin_top_page = margin_top
                index_area_h_pt = self.rendering_area_h - self.print_bleed_pt - self.margin_top_bottom_pt - margin_top_page  # full page height available now with this new page.

            city_header_height = 0
            if len(cities) > 1:
                city_header_height = self._draw_page_header(
                    self._i18n.isrtl(),
                    self.ctx,
                    pc,
                    city_layout,
                    UTILS.convert_pt_to_dots(city_fascent, dpi),
                    UTILS.convert_pt_to_dots(cityBlockHeight, dpi),
                    UTILS.convert_pt_to_dots(
                        self.rendering_area_x + self.print_bleed_pt +
                        (self.margin_inside_pt if
                         (self.index_page_num + self.page_offset) %
                         2 else self.margin_outside_pt), dpi),
                    UTILS.convert_pt_to_dots(
                        margin_top_page + header_fascent / 2, dpi
                    ),  # baseline_y, original: self.rendering_area_y + header_fascent
                    margin_top_page,  #margin_top
                    city)
                index_area_h_pt -= city_header_height
                margin_top_page += city_header_height

            # find largest label and location
            max_label_drawing_width = 0.0
            max_location_drawing_width = 0.0

            if False:
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])
                self.index_categories[city].append(
                    self.index_categories[city][0])

            if False:
                # Alle Kategorien, bis auf 1 entfernen
                while len(self.index_categories[city]) > 1:
                    self.index_categories[city].pop(1)

                # Alle Einträge, bis auf 1, dieser Kategorie entferne
                #while len(self.index_categories[city][0].items)>4:
                #    self.index_categories[city][0].items.pop(1)

                # Bei dieser Kategorie weitere Einträge hinzufügen
                for i in range(40):
                    self.index_categories[city][0].items.append(
                        self.index_categories[city][0].items[3])

            # calculate height of categories
            #LOG.debug("number of categories: %d" % len(self.index_categories[city]))
            #LOG.debug("number of entries in first category: %d" % len(self.index_categories[city][0].items))
            sum_height = 0
            for category in self.index_categories[city]:
                sum_height += category.label_drawing_height(header_layout)
                #LOG.debug("adding height %f for category %s" % (category.label_drawing_height(header_layout), category.name))
                for street in category.items:
                    #LOG.debug("label_drawing_height of %s: %f" % (street.label, street.label_drawing_height(label_layout)))
                    sum_height += street.label_drawing_height(label_layout)

                    w = street.label_drawing_width(label_layout)
                    if w > max_label_drawing_width:
                        max_label_drawing_width = w
                        #LOG.debug("new max_label_drawing_width: %f (%s)" % (max_label_drawing_width, street.label))

                    w = street.location_drawing_width(label_layout)
                    if w > max_location_drawing_width:
                        max_location_drawing_width = w
                        #LOG.debug("new max_location_drawing_width: %f (%s)" % (max_location_drawing_width, street.location_str))
            col_max_height = math.ceil(float(sum_height) / 4) + 40
            LOG.debug(
                "sum_height: %f, %f per column (4) => col_max_height: %d" %
                (sum_height, float(sum_height) / 4, col_max_height))

            # No street to render, bail out
            if max_label_drawing_width == 0.0:
                return

            #LOG.debug("max_label_drawing_width: %f" % max_label_drawing_width)
            #LOG.debug("max_location_drawing_width: %f" % max_location_drawing_width)

            # Find best number of columns
            # max_drawing_width = max_label_drawing_width + max_location_drawing_width + 2 * margin
            max_drawing_height = col_max_height
            needed_drawing_height = max_drawing_height
            if (index_area_h_pt < max_drawing_height):
                LOG.debug(
                    "more height neededed (max_drawing_height: %f), than there is available on this page left (index_area_h_pt: %f). Setting max_drawing_height to index_area_h_pt"
                    % (max_drawing_height, index_area_h_pt))
                max_drawing_height = index_area_h_pt

            if Renderer.DEBUG:  # green stroke dash3: show printable page area (after header)
                LOG.debug("Index - printable area (after header): %f x %f" %
                          (index_area_w_pt, max_drawing_height))
                self.ctx.save()
                self.ctx.set_source_rgba(0, 1, 0, .75)
                self.ctx.set_dash([3.0, 3.0], 3.0 / 2.0)
                self.ctx.rectangle(
                    self.print_bleed_pt +
                    (self.margin_inside_pt if
                     (self.index_page_num + self.page_offset) %
                     2 else self.margin_outside_pt), margin_top_page,
                    index_area_w_pt, max_drawing_height)
                self.ctx.stroke()
                self.ctx.restore()

            #LOG.debug("max_drawing_width: %f" % max_drawing_width)

            #columns_count = int(math.ceil(index_area_w_pt / max_drawing_width))
            # following test should not be needed. No time to prove it. ;-)
            #if columns_count == 0:
            #    columns_count = 1

            #LOG.debug("number of columns: %d" % columns_count)

            columns_count = 4  # Gerald: fixed to 4.
            # We have now have several columns
            column_width = index_area_w_pt / columns_count
            #LOG.debug("column_width: %d" % column_width)

            column_layout.set_width(
                int(
                    UTILS.convert_pt_to_dots(
                        (column_width - margin - 5) * Pango.SCALE, dpi)))
            label_layout.set_width(
                int(
                    UTILS.convert_pt_to_dots(
                        (column_width - margin - max_location_drawing_width -
                         2 * label_em) * Pango.SCALE, dpi)))
            header_layout.set_width(
                int(
                    UTILS.convert_pt_to_dots(
                        (column_width - margin) * Pango.SCALE, dpi)))

            if not self._i18n.isrtl():
                orig_offset_x = offset_x = margin / 2.
                orig_delta_x = delta_x = column_width
            else:
                orig_offset_x = offset_x = index_area_w_pt - column_width + margin / 2.
                orig_delta_x = delta_x = -column_width

            actual_n_cols = 0

            # page number of first page
            self._draw_page_number()

            if Renderer.DEBUG:  # light pink stroke dash 4: full column
                # temp: show index-area (inside grayed margin)
                LOG.debug(
                    "pink: %f w -> index_area_w_pt, %f h -> index_area_h_pt" %
                    (index_area_w_pt, index_area_h_pt))
                self.ctx.save()
                self.ctx.set_source_rgba(.85, .25, .85, .75)
                self.ctx.set_dash([4.0, 4.0], 4.0 / 2.0)
                self.ctx.rectangle(
                    self.print_bleed_pt +
                    (self.margin_inside_pt if
                     (self.index_page_num + self.page_offset) %
                     2 else self.margin_outside_pt) +
                    (city_index % 4) * column_width, margin_top_page,
                    column_width, max_drawing_height)
                self.ctx.stroke()
                self.ctx.restore()

            offset_y = margin_top_page  # each city/category starts on the corresponding margin
            for category in self.index_categories[city]:
                if (offset_y + header_fheight + label_fheight + margin / 2. >
                    (max_drawing_height + margin_top_page)):
                    offset_y = margin_top_page
                    offset_x += delta_x
                    actual_n_cols += 1

                    if actual_n_cols == columns_count:
                        self._new_page()
                        self._draw_page_stroke()
                        self._draw_page_content_stroke()
                        actual_n_cols = 0
                        city_header_height = 0  # no city-header on the additional city-pages
                        margin_top_page = margin_top
                        offset_y = margin_top_page
                        offset_x = orig_offset_x
                        delta_x = orig_delta_x
                        max_drawing_height = needed_drawing_height - max_drawing_height + margin_top_page  # OR index_area_h_pt => its a new page, full index_area_h_pt is available now #
                        LOG.debug(
                            "NEW PAGE (before category %s). actual_n_cols == columns_count (%d). needed_drawing_height %d, max_drawing_height %d"
                            % (category.name, columns_count,
                               needed_drawing_height, max_drawing_height))

                        if Renderer.DEBUG:
                            self.ctx.save()
                            self.ctx.set_source_rgba(1, 0, 0, .75)
                            self.ctx.set_dash([8.0, 8.0], 8.0 / 2.0)
                            self.ctx.rectangle(
                                self.rendering_area_x +
                                (self.margin_inside_pt if
                                 (self.index_page_num + self.page_offset) %
                                 2 else self.margin_outside_pt) + offset_x,
                                offset_y, column_width, max_drawing_height)
                            self.ctx.stroke()
                            self.ctx.restore()

                category_height = category.label_drawing_height(header_layout)
                #LOG.debug("category %s, height draw %d | %d | %d | %d" % (category.name, category_height, header_fascent, UTILS.convert_pt_to_dots(header_fascent, dpi), header_fheight))
                category.draw(
                    self._i18n.isrtl(), self.ctx, pc, header_layout,
                    UTILS.convert_pt_to_dots(header_fascent, dpi),
                    UTILS.convert_pt_to_dots(header_fheight, dpi),
                    UTILS.convert_pt_to_dots(
                        self.rendering_area_x +
                        (self.margin_inside_pt if
                         (self.index_page_num + self.page_offset) %
                         2 else self.margin_outside_pt) + offset_x, dpi),
                    UTILS.convert_pt_to_dots(
                        self.rendering_area_y + offset_y + header_fascent,
                        dpi))

                offset_y += category_height

                for street in category.items:
                    label_height = street.label_drawing_height(label_layout)
                    if (offset_y + label_height + margin / 2. >
                        (max_drawing_height + margin_top_page)):
                        offset_y = margin_top_page
                        offset_x += delta_x
                        actual_n_cols += 1

                        if actual_n_cols == columns_count:
                            LOG.debug(
                                "NEW PAGE (before street %s). actual_n_cols %d == columns_count %d"
                                % (street.label, actual_n_cols, columns_count))
                            self._new_page()
                            self._draw_page_stroke()
                            self._draw_page_content_stroke()
                            actual_n_cols = 0
                            city_header_height = 0
                            margin_top_page = margin_top
                            offset_y = margin_top_page
                            offset_x = orig_offset_x
                            delta_x = orig_delta_x
                            max_drawing_height = needed_drawing_height - max_drawing_height + margin_top_page

                            if Renderer.DEBUG:
                                self.ctx.save()
                                self.ctx.set_source_rgba(1, 0, 0, .75)
                                self.ctx.set_dash([8.0, 8.0], 8.0 / 2.0)
                                self.ctx.rectangle(
                                    self.rendering_area_x +
                                    (self.margin_inside_pt if
                                     (self.index_page_num + self.page_offset) %
                                     2 else self.margin_outside_pt) + offset_x,
                                    offset_y, column_width, max_drawing_height)
                                self.ctx.stroke()
                                self.ctx.restore()

                    self.ctx.set_source_rgb(0, 0, 0)

                    if (street.color is None):
                        self.ctx.set_source_rgb(0, 0, 0)
                    else:
                        color = tuple(
                            int(street.color.lstrip('#')[i:i + 2], 16) / 255.
                            for i in (0, 2, 4))

                        # draw colorized rectangle next to street
                        self.ctx.save()
                        self.ctx.set_source_rgb(color[0], color[1], color[2])
                        self.ctx.rectangle(
                            self.rendering_area_x +
                            (self.margin_inside_pt if
                             (self.index_page_num + self.page_offset) %
                             2 else self.margin_outside_pt) + offset_x,
                            self.rendering_area_y + offset_y, 5, label_height)
                        self.ctx.fill()
                        self.ctx.restore()
                        # alternative: colorize street-text:
                        # self.ctx.set_source_rgb(color[0],color[1],color[2])

                    street.draw(
                        self._i18n.isrtl(), self.ctx, pc, column_layout,
                        UTILS.convert_pt_to_dots(label_fascent, dpi),
                        UTILS.convert_pt_to_dots(label_fheight, dpi),
                        UTILS.convert_pt_to_dots(
                            self.rendering_area_x + 5 +
                            (self.margin_inside_pt if
                             (self.index_page_num + self.page_offset) %
                             2 else self.margin_outside_pt) + offset_x, dpi),
                        UTILS.convert_pt_to_dots(
                            self.rendering_area_y + offset_y + label_fascent,
                            dpi), label_layout,
                        UTILS.convert_pt_to_dots(label_height, dpi),
                        UTILS.convert_pt_to_dots(max_location_drawing_width,
                                                 dpi))

                    offset_y += label_height

        self.ctx.restore()