[gnome-maps/wip/mlundblad/transit-routing: 9/13] mapView: Implement showing transit routes
- From: Marcus Lundblad <mlundblad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/mlundblad/transit-routing: 9/13] mapView: Implement showing transit routes
- Date: Mon, 13 Feb 2017 20:55:18 +0000 (UTC)
commit 5af5f4eb51e708af8891e5dcc7a7314ebbbe81c3
Author: Marcus Lundblad <ml update uu se>
Date: Tue Mar 29 23:10:22 2016 +0200
mapView: Implement showing transit routes
Also added functionallity to allow showing dynamically created
route layer segments, optionally using a dashed style.
This is used by transit routes to show walking and transit legs
of journeys.
https://bugzilla.gnome.org/show_bug.cgi?id=755808
src/mapView.js | 195 +++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 179 insertions(+), 16 deletions(-)
---
diff --git a/src/mapView.js b/src/mapView.js
index 30d078e..420bb77 100644
--- a/src/mapView.js
+++ b/src/mapView.js
@@ -30,6 +30,7 @@ const Mainloop = imports.mainloop;
const Application = imports.application;
const ContactPlace = imports.contactPlace;
+const Color = imports.color;
const Geoclue = imports.geoclue;
const GeoJSONShapeLayer = imports.geoJSONShapeLayer;
const KmlShapeLayer = imports.kmlShapeLayer;
@@ -40,8 +41,12 @@ const MapSource = imports.mapSource;
const MapWalker = imports.mapWalker;
const Place = imports.place;
const PlaceMarker = imports.placeMarker;
+const RouteQuery = imports.routeQuery;
const ShapeLayer = imports.shapeLayer;
const StoredRoute = imports.storedRoute;
+const TransitArrivalMarker = imports.transitArrivalMarker;
+const TransitBoardMarker = imports.transitBoardMarker;
+const TransitWalkMarker = imports.transitWalkMarker;
const TurnPointMarker = imports.turnPointMarker;
const UserLocationMarker = imports.userLocationMarker;
const Utils = imports.utils;
@@ -63,6 +68,24 @@ const MIN_LATITUDE = -85.05112;
const MAX_LONGITUDE = 180;
const MIN_LONGITUDE = -180;
+/* threashhold for route color luminance when we consider it more or less
+ * as white, and draw an outline on the path */
+const OUTLINE_LUMINANCE_THREASHHOLD = 0.9;
+
+// color used for turn-by-turn-based routes (non-transit)
+const TURN_BY_TURN_ROUTE_COLOR = '0000FF';
+
+// line width for route lines
+const ROUTE_LINE_WIDTH = 5;
+
+/* length of filled parts of dashed lines used for walking legs of transit
+ * itineraries
+ */
+const DASHED_ROUTE_LINE_FILLED_LENGTH = 5;
+
+// length of gaps of dashed lines used for walking legs of transit itineraries
+const DASHED_ROUTE_LINE_GAP_LENGTH = 5;
+
const MapView = new Lang.Class({
Name: 'MapView',
Extends: GtkChamplain.Embed,
@@ -84,13 +107,16 @@ const MapView = new Lang.Class({
},
get routeVisible() {
- return this._routeLayer.visible || this._instructionMarkerLayer.visible;
+ return this._routeVisible || this._instructionMarkerLayer.visible;
},
set routeVisible(value) {
let isValid = Application.routeQuery.isValid();
- this._routeLayer.visible = value && isValid;
+ this._routeVisible = value && isValid;
+ this._routeLayers.forEach((function(routeLayer) {
+ routeLayer.visible = value && isValid;
+ }).bind(this));
this._instructionMarkerLayer.visible = value && isValid;
this.notify('routeVisible');
},
@@ -156,22 +182,44 @@ const MapView = new Lang.Class({
return view;
},
- _initLayers: function() {
- let strokeColor = new Clutter.Color({ red: 0,
- blue: 255,
- green: 0,
- alpha: 100 });
+ /* create and store a route layer, pass true to get a dashed line */
+ _createRouteLayer: function(dashed, lineColor, width) {
+ let red = Color.parseColor(lineColor, 0);
+ let green = Color.parseColor(lineColor, 1);
+ let blue = Color.parseColor(lineColor, 2);
+ // Clutter uses a 0-255 range for color components
+ let strokeColor = new Clutter.Color({ red: red * 255,
+ blue: blue * 255,
+ green: green * 255,
+ alpha: 255 });
+ let routeLayer = new Champlain.PathLayer({ stroke_width: width,
+ stroke_color: strokeColor });
+ if (dashed)
+ routeLayer.set_dash([DASHED_ROUTE_LINE_FILLED_LENGTH,
+ DASHED_ROUTE_LINE_GAP_LENGTH]);
+
+ this._routeLayers.push(routeLayer);
+ this.view.add_layer(routeLayer);
+
+ return routeLayer;
+ },
+
+ _clearRouteLayers: function() {
+ this._routeLayers.forEach((function(routeLayer) {
+ routeLayer.remove_all();
+ routeLayer.visible = false;
+ this.view.remove_layer(routeLayer);
+ }).bind(this));
+ this._routeLayers = [];
+ },
+
+ _initLayers: function() {
let mode = Champlain.SelectionMode.SINGLE;
this._userLocationLayer = new Champlain.MarkerLayer({ selection_mode: mode });
this.view.add_layer(this._userLocationLayer);
- this._routeLayer = new Champlain.PathLayer({ stroke_width: 5.0,
- stroke_color: strokeColor });
- this.view.add_layer(this._routeLayer);
-
-
this._placeLayer = new Champlain.MarkerLayer({ selection_mode: mode });
this.view.add_layer(this._placeLayer);
@@ -184,15 +232,35 @@ const MapView = new Lang.Class({
ShapeLayer.SUPPORTED_TYPES.push(GeoJSONShapeLayer.GeoJSONShapeLayer);
ShapeLayer.SUPPORTED_TYPES.push(KmlShapeLayer.KmlShapeLayer);
ShapeLayer.SUPPORTED_TYPES.push(GpxShapeLayer.GpxShapeLayer);
+
+ this._routeLayers = [];
+ },
+
+ _ensureInstructionLayerAboveRouteLayers: function() {
+ this.view.remove_layer(this._instructionMarkerLayer);
+ this.view.add_layer(this._instructionMarkerLayer);
},
_connectRouteSignals: function() {
- let route = Application.routeService.route;
+ let route = Application.routingDelegator.graphHopper.route;
+ let transitPlan = Application.routingDelegator.openTripPlanner.plan;
let query = Application.routeQuery;
route.connect('update', this.showRoute.bind(this, route));
route.connect('reset', (function() {
- this._routeLayer.remove_all();
+ this._clearRouteLayers();
+ this._instructionMarkerLayer.remove_all();
+ }).bind(this));
+ transitPlan.connect('update', this._showTransitPlan.bind(this, transitPlan));
+ transitPlan.connect('reset', (function() {
+ this._clearRouteLayers();
+ this._instructionMarkerLayer.remove_all();
+ }).bind(this));
+ transitPlan.connect('itinerary-selected', (function(obj, itinerary) {
+ this._showTransitItinerary(itinerary);
+ }).bind(this));
+ transitPlan.connect('itinerary-deselected', (function() {
+ this._clearRouteLayers();
this._instructionMarkerLayer.remove_all();
}).bind(this));
@@ -396,6 +464,17 @@ const MapView = new Lang.Class({
this._turnPointMarker.goTo();
},
+ showTransitStop: function(transitStop, transitLeg) {
+ if (this._turnPointMarker)
+ this._turnPointMarker.destroy();
+
+ this._turnPointMarker = new TurnPointMarker.TurnPointMarker({ transitStop: transitStop,
+ transitLeg: transitLeg,
+ mapView: this });
+ this._instructionMarkerLayer.add_marker(this._turnPointMarker);
+ this._turnPointMarker.goTo();
+ },
+
showContact: function(contact) {
let places = contact.get_places();
if (places.length === 0)
@@ -458,12 +537,17 @@ const MapView = new Lang.Class({
},
showRoute: function(route) {
- this._routeLayer.remove_all();
+ let routeLayer;
+
+ this._clearRouteLayers();
this._placeLayer.remove_all();
+ routeLayer = this._createRouteLayer(false, TURN_BY_TURN_ROUTE_COLOR,
+ ROUTE_LINE_WIDTH);
+ route.path.forEach(routeLayer.add_node.bind(routeLayer));
this.routeVisible = true;
- route.path.forEach(this._routeLayer.add_node.bind(this._routeLayer));
+ this._ensureInstructionLayerAboveRouteLayers();
this._showDestinationTurnpoints();
this.gotoBBox(route.bbox);
@@ -487,6 +571,85 @@ const MapView = new Lang.Class({
}, this);
},
+ _showTransitItinerary: function(itinerary) {
+ this.gotoBBox(itinerary.bbox);
+ this._clearRouteLayers();
+ this._placeLayer.remove_all();
+ this._instructionMarkerLayer.remove_all();
+
+ itinerary.legs.forEach((function (leg, index) {
+ let dashed = !leg.transit;
+ let color = leg.color;
+ let outlineColor = leg.textColor;
+ let hasOutline = Color.relativeLuminance(color) >
+ OUTLINE_LUMINANCE_THREASHHOLD;
+ let routeLayer;
+ let outlineRouteLayer;
+
+ /* draw an outline by drawing a background path layer if needed
+ * TODO: maybe we should add support for outlined path layers in
+ * libchamplain */
+ if (hasOutline)
+ outlineRouteLayer = this._createRouteLayer(dashed, outlineColor,
+ ROUTE_LINE_WIDTH + 2);
+ routeLayer = this._createRouteLayer(dashed, color, ROUTE_LINE_WIDTH);
+
+ /* if this is a walking leg and not at the start, "stitch" it
+ * together with the end point of the previous leg, as the walk
+ * route might not reach all the way */
+ if (index > 0 && !leg.transit) {
+ let previousLeg = itinerary.legs[index - 1];
+ let lastPoint = previousLeg.polyline.last();
+
+ routeLayer.add_node(lastPoint);
+ }
+
+ if (hasOutline)
+ leg.polyline.forEach(outlineRouteLayer.add_node.bind(outlineRouteLayer));
+ leg.polyline.forEach(routeLayer.add_node.bind(routeLayer));
+
+ /* like above, "stitch" the route segment with the next one if it's
+ * a walking leg, and not the last one */
+ if (index < itinerary.legs.length - 1 && !leg.transit) {
+ let nextLeg = itinerary.legs[index + 1];
+ let firstPoint = nextLeg.polyline[0];
+
+ routeLayer.add_node(firstPoint);
+ }
+ }).bind(this));
+
+ this._ensureInstructionLayerAboveRouteLayers();
+
+ itinerary.legs.forEach((function (leg, index) {
+ let previousLeg = index === 0 ? null : itinerary.legs[index - 1];
+
+ /* add start marker */
+ let start;
+ if (!leg.transit) {
+ start = new TransitWalkMarker.TransitWalkMarker({ leg: leg,
+ previousLeg: previousLeg,
+ mapView: this });
+ } else {
+ start = new TransitBoardMarker.TransitBoardMarker({ leg: leg,
+ mapView: this });
+ }
+
+ this._instructionMarkerLayer.add_marker(start);
+ }).bind(this));
+
+ /* add arrival marker */
+ let lastLeg = itinerary.legs.last();
+ let arrival = new TransitArrivalMarker.TransitArrivalMarker({ leg: lastLeg,
+ mapView: this });
+ this._instructionMarkerLayer.add_marker(arrival);
+
+ this.routeVisible = true;
+ },
+
+ _showTransitPlan: function(plan) {
+ this.gotoBBox(plan.bbox);
+ },
+
_onViewMoved: function() {
this.emit('view-moved');
if (this._storeId !== 0)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]