[gnome-maps/wip/mlundblad/transit-routing: 26/27] WIP: Add print layout for transit



commit 51e45e8b4713b7d0931ece14ded48c849f44365f
Author: Marcus Lundblad <ml update uu se>
Date:   Tue Sep 20 23:27:09 2016 +0200

    WIP: Add print layout for transit

 src/org.gnome.Maps.src.gresource.xml |    1 +
 src/transitPrintLayout.js            |  234 ++++++++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+), 0 deletions(-)
---
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index fa1b45d..89ff771 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -82,6 +82,7 @@
     <file>transitMoreRow.js</file>
     <file>transitOptions.js</file>
     <file>transitPlan.js</file>
+    <file>transitPrintLayout.js</file>
     <file>transitRouteLabel.js</file>
     <file>transitStopRow.js</file>
     <file>transitWalkMarker.js</file>
diff --git a/src/transitPrintLayout.js b/src/transitPrintLayout.js
new file mode 100644
index 0000000..b80b0a4
--- /dev/null
+++ b/src/transitPrintLayout.js
@@ -0,0 +1,234 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2016 Marcus Lundblad.
+ *
+ * GNOME Maps is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * GNOME Maps 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 General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with GNOME Maps; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Marcus Lundblad <ml update uu se>
+ */
+
+const Lang = imports.lang;
+
+const Cairo = imports.cairo;
+const Champlain = imports.gi.Champlain;
+const Clutter = imports.gi.Clutter;
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+
+const MapSource = imports.mapSource;
+const PrintLayout = imports.printLayout;
+const TransitArrivalMarker = imports.transitArrivalMarker;
+const TransitBoardMarker = imports.transitBoardMarker;
+const TransitLegRow = imports.transitLegRow;
+const TransitWalkMarker = imports.transitWalkMarker;
+
+/* stroke color for walking paths */
+const _STROKE_COLOR = new Clutter.Color({ red: 0,
+                                          blue: 0,
+                                          green: 0,
+                                          alpha: 255 });
+const _STROKE_WIDTH = 5.0;
+
+/* All following constants are ratios of surface size to page size */
+const _Header = {
+    SCALE_X: 0.9,
+    SCALE_Y: 0.03,
+    SCALE_MARGIN: 0.01
+};
+const _MapView = {
+    SCALE_X: 1.0,
+    SCALE_Y: 0.4,
+    SCALE_MARGIN: 0.04,
+    ZOOM_LEVEL: 18
+};
+const _Instruction = {
+    SCALE_X: 0.57,
+    SCALE_Y: 0.05,
+    SCALE_MARGIN: 0.01
+};
+
+const TransitPrintLayout = new Lang.Class({
+    Name: 'TransitPrintLayout',
+    Extends: PrintLayout.PrintLayout,
+
+    _init: function(params) {
+        this._itinerary = params.itinerary;
+        delete params.itinerary;
+
+        params.totalSurfaces = this._getNumberOfSurfaces();
+
+        this.parent(params);
+    },
+
+    _getNumberOfSurfaces: function() {
+        /* always one fixed surface for the title label */
+        let numSurfaces = 1;
+
+        this._itinerary.legs.forEach((function (leg) {
+            numSurfaces++;
+            /* add a surface for a walking instruction map when needed */
+            if (!leg.transit)
+                numSurfaces++;
+        }));
+
+        return numSurfaces;
+    },
+
+    _drawMapView: function(width, height, zoomLevel, leg, nextLeg) {
+        let pageNum = this.numPages - 1;
+        let x = this._cursorX;
+        let y = this._cursorY;
+        let mapSource = MapSource.createPrintSource();
+        let markerLayer = new Champlain.MarkerLayer();
+        let view = new Champlain.View({ width: width,
+                                        height: height,
+                                        zoom_level: zoomLevel });
+        view.set_map_source(mapSource);
+        /* we want to add the path layer before the marker layer, so that
+         * boarding marker are drawn about the walk dash lines */
+        this._addRouteLayer(view, leg);
+        view.add_layer(markerLayer);
+
+        markerLayer.add_marker(this._createStartMarker(leg));
+        if (nextLeg)
+            markerLayer.add_marker(this._createBoardMarker(nextLeg));
+        else
+            markerLayer.add_marker(this._createArrivalMarker(leg));
+
+        /* in some cases, we seem to get get zero distance walking instructions
+         * within station complexes, don't try to show a bounding box for low
+         * distances, instead center on the spot */
+        if (leg.distance < 10)
+            view.center_on(leg.fromCoordinate[0], leg.fromCoordinate[1]);
+        else
+            view.ensure_visible(leg.bbox, false);
+        if (view.state !== Champlain.State.DONE) {
+            let notifyId = view.connect('notify::state', (function() {
+                if (view.state === Champlain.State.DONE) {
+                    view.disconnect(notifyId);
+                    let surface = view.to_surface(true);
+                    if (surface)
+                        this._addSurface(surface, x, y, pageNum);
+                }
+            }).bind(this));
+        } else {
+            let surface = view.to_surface(true);
+            if (surface)
+                this._addSurface(surface, x, y, pageNum);
+        }
+    },
+
+    _createStartMarker: function(leg) {
+        return new TransitWalkMarker.TransitWalkMarker({ leg: leg });
+    },
+
+    _createBoardMarker: function(leg) {
+        return new TransitBoardMarker.TransitBoardMarker({ leg: leg });
+    },
+
+    _createArrivalMarker: function(leg) {
+        return new TransitArrivalMarker.TransitArrivalMarker({ leg: leg });
+    },
+
+    _addRouteLayer: function(view, leg) {
+        let routeLayer = new Champlain.PathLayer({ stroke_width: _STROKE_WIDTH,
+                                                   stroke_color: _STROKE_COLOR });
+        routeLayer.set_dash([5, 5]);
+        view.add_layer(routeLayer);
+        leg.polyline.forEach(routeLayer.add_node.bind(routeLayer));
+    },
+
+    _drawInstruction: function(width, height, leg, start) {
+        let pageNum = this.numPages - 1;
+        let x = this._cursorX;
+        let y = this._cursorY;
+
+        let legRow = new TransitLegRow.TransitLegRow({
+            visible: true,
+            leg: leg,
+            start: start
+        });
+
+        //legRow.width_request = width;
+        //legRow.height_request = height;
+        let [minHeight, natHeight] = legRow.get_preferred_height();
+        let [minWidth, natWidth] = legRow.get_preferred_width();
+        let actualWidth = Math.max(width, minWidth);
+        let actualHeight = Math.max(height, minHeight);
+        let surface = new Cairo.ImageSurface(Cairo.Format.ARGB32,
+                                             actualWidth, actualHeight);
+        let cr = new Cairo.Context(surface);
+
+        legRow.size_allocate(new Gdk.Rectangle({ x: 0, y: 0,
+                                                 width: actualWidth,
+                                                 height: actualHeight }));
+
+        /* Paint the background of the row to be transparent */
+        legRow.connect('draw', (function(widget, cr) {
+            cr.setSourceRGBA(0.0, 0.0, 0.0, 0.0);
+            cr.setOperator(Cairo.Operator.SOURCE);
+            cr.paint();
+            cr.setOperator(Cairo.Operator.OVER);
+        }).bind(this));
+
+        legRow.draw(cr);
+
+        surface.writeToPNG('/tmp/test.png');
+
+        this._addSurface(surface, x, y, pageNum);
+    },
+
+    render: function() {
+        let headerWidth = _Header.SCALE_X * this._pageWidth;
+        let headerHeight = _Header.SCALE_Y * this._pageHeight;
+        let headerMargin = _Header.SCALE_MARGIN * this._pageHeight;
+
+        let mapViewWidth = _MapView.SCALE_X * this._pageWidth;
+        let mapViewHeight = _MapView.SCALE_Y * this._pageHeight;
+        let mapViewMargin = _MapView.SCALE_MARGIN * this._pageHeight;
+        let mapViewZoomLevel = _MapView.ZOOM_LEVEL;
+
+        let instructionWidth = _Instruction.SCALE_X * this._pageWidth;
+        let instructionHeight = _Instruction.SCALE_Y * this._pageHeight;
+        let instructionMargin = _Instruction.SCALE_MARGIN * this._pageHeight;
+
+        let dy = headerHeight + headerMargin;
+
+        this._createNewPage();
+        this._adjustPage(dy);
+        this._drawHeader(headerWidth, headerHeight);
+        this._cursorY += dy;
+
+        for (let i = 0; i < this._itinerary.legs.length; i++) {
+            let leg = this._itinerary.legs[i];
+
+            dy = instructionHeight + instructionMargin;
+            this._adjustPage(dy);
+            this._drawInstruction(instructionWidth, instructionHeight, leg,
+                                  i === 0);
+            this._cursorY += dy;
+
+            if (!leg.transit) {
+                let nextLeg = i < this._itinerary.legs.length - 1 ?
+                              this._itinerary.legs[i + 1] : null;
+                dy = mapViewHeight + mapViewMargin;
+                this._adjustPage(dy);
+                this._drawMapView(mapViewWidth, mapViewHeight, mapViewZoomLevel,
+                                  leg, nextLeg);
+                this._cursorY += dy;
+            }
+        }
+    }
+});


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]