[gnome-maps/wip/mlundblad/transit-service-discovery: 12/15] WIP: Add transit router
- From: Marcus Lundblad <mlundblad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-maps/wip/mlundblad/transit-service-discovery: 12/15] WIP: Add transit router
- Date: Tue, 3 Sep 2019 20:40:27 +0000 (UTC)
commit d6232389229ed173f1137380604169b35db37885
Author: Marcus Lundblad <ml update uu se>
Date: Tue Aug 20 22:07:58 2019 +0200
WIP: Add transit router
Dispatches routing requests to matching
transit provider.
src/mapView.js | 2 +-
src/org.gnome.Maps.src.gresource.xml | 1 +
src/printOperation.js | 2 +-
src/routingDelegator.js | 16 ++-
src/sidebar.js | 16 +--
src/transitRouter.js | 199 ++++++++++++++++++++++++++++++++++
src/transitplugins/openTripPlanner.js | 20 +---
7 files changed, 219 insertions(+), 37 deletions(-)
---
diff --git a/src/mapView.js b/src/mapView.js
index 3610272..e206cfd 100644
--- a/src/mapView.js
+++ b/src/mapView.js
@@ -262,7 +262,7 @@ var MapView = GObject.registerClass({
_connectRouteSignals() {
let route = Application.routingDelegator.graphHopper.route;
- let transitPlan = Application.routingDelegator.openTripPlanner.plan;
+ let transitPlan = Application.routingDelegator.transitRouter.plan;
let query = Application.routeQuery;
route.connect('update', () => {
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index 0ca0fc2..94e5099 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -90,6 +90,7 @@
<file>transitOptionsPanel.js</file>
<file>transitPlan.js</file>
<file>transitPrintLayout.js</file>
+ <file>transitRouter.js</file>
<file>transitRouteLabel.js</file>
<file>transitStopRow.js</file>
<file>transitWalkMarker.js</file>
diff --git a/src/printOperation.js b/src/printOperation.js
index 89462a5..b3307c0 100644
--- a/src/printOperation.js
+++ b/src/printOperation.js
@@ -57,7 +57,7 @@ var PrintOperation = class PrintOperation {
_beginPrint(operation, context, data) {
let route = Application.routingDelegator.graphHopper.route;
let selectedTransitItinerary =
- Application.routingDelegator.openTripPlanner.plan.selectedItinerary;
+ Application.routingDelegator.transitRouter.plan.selectedItinerary;
let width = context.get_width();
let height = context.get_height();
diff --git a/src/routingDelegator.js b/src/routingDelegator.js
index 57e956f..37728c7 100644
--- a/src/routingDelegator.js
+++ b/src/routingDelegator.js
@@ -20,7 +20,7 @@
*/
const GraphHopper = imports.graphHopper;
-const OpenTripPlanner = imports.transitplugins.openTripPlanner;
+const TransitRouter = imports.transitRouter;
const RouteQuery = imports.routeQuery;
const _FALLBACK_TRANSPORTATION = RouteQuery.Transportation.PEDESTRIAN;
@@ -32,16 +32,14 @@ var RoutingDelegator = class RoutingDelegator {
this._transitRouting = false;
this._graphHopper = new GraphHopper.GraphHopper({ query: this._query });
- this._openTripPlanner =
- new OpenTripPlanner.OpenTripPlanner({ query: this._query,
- graphHopper: this._graphHopper });
+ this._transitRouter = new TransitRouter.TransitRouter({ query: this._query });
this._query.connect('notify::points', this._onQueryChanged.bind(this));
/* if the query is set to transit mode when it's not available, revert
* to a fallback mode
*/
if (this._query.transportation === RouteQuery.Transportation.TRANSIT &&
- !this._openTripPlanner.enabled) {
+ !this._transitRouter.enabled) {
this._query.transportation = _FALLBACK_TRANSPORTATION;
}
}
@@ -50,8 +48,8 @@ var RoutingDelegator = class RoutingDelegator {
return this._graphHopper;
}
- get openTripPlanner() {
- return this._openTripPlanner;
+ get transitRouter() {
+ return this._transitRouter;
}
set useTransit(useTransit) {
@@ -60,7 +58,7 @@ var RoutingDelegator = class RoutingDelegator {
reset() {
if (this._transitRouting)
- this._openTripPlanner.plan.reset();
+ this._transitRouter.plan.reset();
else
this._graphHopper.route.reset();
}
@@ -68,7 +66,7 @@ var RoutingDelegator = class RoutingDelegator {
_onQueryChanged() {
if (this._query.isValid()) {
if (this._transitRouting) {
- this._openTripPlanner.fetchFirstResults();
+ this._transitRouter.fetchFirstResults();
} else {
this._graphHopper.fetchRoute(this._query.filledPoints,
this._query.transportation);
diff --git a/src/sidebar.js b/src/sidebar.js
index 1b546a2..1ab1acf 100644
--- a/src/sidebar.js
+++ b/src/sidebar.js
@@ -95,13 +95,13 @@ var Sidebar = GObject.registerClass({
this._query.addPoint(1);
this._switchRoutingMode(Application.routeQuery.transportation);
/* Enable/disable transit mode switch based on the presence of
- * OpenTripPlanner.
+ * public transit providers.
* For some reason, setting visible to false in the UI file and
* dynamically setting visible false here doesn't work, maybe because
* it's part of a radio group? As a workaround, just remove the button
* instead.
*/
- if (!Application.routingDelegator.openTripPlanner.enabled)
+ if (!Application.routingDelegator.transitRouter.enabled)
this._modeTransitToggle.destroy();
}
@@ -154,7 +154,7 @@ var Sidebar = GObject.registerClass({
Application.routingDelegator.useTransit = false;
this._linkButtonStack.visible_child_name = 'graphHopper';
this._transitRevealer.reveal_child = false;
- Application.routingDelegator.openTripPlanner.plan.deselectItinerary();
+ Application.routingDelegator.transitRouter.plan.deselectItinerary();
}
this._clearInstructions();
}
@@ -214,7 +214,7 @@ var Sidebar = GObject.registerClass({
_initInstructionList() {
let route = Application.routingDelegator.graphHopper.route;
- let transitPlan = Application.routingDelegator.openTripPlanner.plan;
+ let transitPlan = Application.routingDelegator.transitRouter.plan;
route.connect('reset', () => {
this._clearInstructions();
@@ -341,7 +341,7 @@ var Sidebar = GObject.registerClass({
}
_showTransitOverview() {
- let plan = Application.routingDelegator.openTripPlanner.plan;
+ let plan = Application.routingDelegator.transitRouter.plan;
this._transitListStack.visible_child_name = 'overview';
this._transitHeader.visible_child_name = 'options';
@@ -354,7 +354,7 @@ var Sidebar = GObject.registerClass({
}
_populateTransitItineraryOverview() {
- let plan = Application.routingDelegator.openTripPlanner.plan;
+ let plan = Application.routingDelegator.transitRouter.plan;
plan.itineraries.forEach((itinerary) => {
let row =
@@ -371,7 +371,7 @@ var Sidebar = GObject.registerClass({
}
_onItineraryActivated(itinerary) {
- let plan = Application.routingDelegator.openTripPlanner.plan;
+ let plan = Application.routingDelegator.transitRouter.plan;
this._populateTransitItinerary(itinerary);
this._showTransitItineraryView();
@@ -380,7 +380,7 @@ var Sidebar = GObject.registerClass({
_onMoreActivated(row) {
row.startLoading();
- Application.routingDelegator.openTripPlanner.fetchMoreResults();
+ Application.routingDelegator.transitRouter.fetchMoreResults();
}
_onItineraryOverviewRowActivated(listBox, row) {
diff --git a/src/transitRouter.js b/src/transitRouter.js
new file mode 100644
index 0000000..d38f462
--- /dev/null
+++ b/src/transitRouter.js
@@ -0,0 +1,199 @@
+/* -*- Mode: JS2; indent-tabs-mode: nil; js2-basic-offset: 4 -*- */
+/* vim: set et ts=4 sw=4: */
+/*
+ * Copyright (c) 2019 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 Champlain = imports.gi.Champlain;
+
+const Service = imports.service;
+const TransitPlan = imports.transitPlan;
+const Utils = imports.utils;
+
+var TransitRouter = class TransitRoute {
+ constructor(params) {
+ this._plan = new TransitPlan.Plan();
+ this._query = params.query;
+ this._providers = Service.getService().transitProviders;
+ this._providerCache = [];
+ this._language = Utils.getLanguage();
+ }
+
+ get enabled() {
+ return this._providers !== undefined;
+ }
+
+ get plan() {
+ return this._plan;
+ }
+
+ fetchFirstResults() {
+ let bestProvider = this._getBestProviderForQuery();
+
+ if (bestProvider) {
+ let provider = bestProvider[0];
+
+ this._currPluginInstance = bestProvider[1];
+ this._plan.attribution = this._getAttributionForProvider(provider);
+ if (provider.attributionUrl)
+ this._plan.attributionUrl = provider.attributionUrl;
+ this._currPluginInstance.fetchFirstResults();
+ } else {
+ this._plan.reset();
+ this._query.reset();
+ this._plan.noProvider();
+ }
+ }
+
+ fetchMoreResults() {
+ if (this._currPluginInstance)
+ this._currPluginInstance.fetchMoreResults();
+ else
+ throw new Error('No previous provider');
+ }
+
+ _getAttributionForProvider(provider) {
+ Utils.debug('getAttribution: ' + JSON.stringify(provider, '', 2));
+ if (provider['attribution:' + this._language])
+ return provider['attribution:' + this._language];
+ else if (provider.attribution)
+ return provider.attribution;
+ else
+ return null;
+ }
+
+ _getBestProviderForQuery() {
+ let startLocation = this._query.filledPoints[0].place.location;
+ let endLocation =
+ this._query.filledPoints[this._query.points.length - 1].place.location;
+ let startCountry =
+ Utils.getCountryCodeForCoordinates(startLocation.latitude,
+ startLocation.longitude);
+ let endCountry =
+ Utils.getCountryCodeForCoordinates(endLocation.latitude,
+ endLocation.longitude);
+
+ Utils.debug('country of start and end: ' + startCountry + ', ' + endCountry);
+
+ let matchingProviders = [];
+
+ this._providers.forEach((p) => {
+ let provider = p.provider;
+ let areas = provider.areas;
+
+ Utils.debug('checking provider ' + provider.name);
+
+ if (!areas) {
+ Utils.debug('No coverage info for provider ' + provider.name);
+ return;
+ }
+
+ areas.forEach((area) => {
+ /* if the area has a specified priority, override the
+ * overall area priority, this allows sub-areas of of
+ * coverage for a provider to have higher or lowe priorities
+ * than other providers (e.g. one "native" to that area
+ */
+ if (area.priority)
+ provider.priority = area.priority;
+
+ let countries = area.countries;
+
+ if (countries) {
+ Utils.debug('Number of countries covered: ' + countries.length);
+
+ if (countries.includes(startCountry) &&
+ countries.includes(endCountry)) {
+ Utils.debug('Provider matches on country');
+ matchingProviders.push(provider);
+ return;
+ }
+ }
+
+ let bbox = area.bbox;
+
+ if (bbox) {
+ if (bbox.length !== 4) {
+ Utils.debug('malformed bounding box for provider ' + provider.name);
+ return;
+ }
+
+ let [x1, y1, x2, y2] = bbox;
+ let cbbox = new Champlain.BoundingBox({ bottom: x1,
+ left: y1,
+ top: x2,
+ right: y2 });
+
+ if (cbbox.covers(startLocation.latitude,
+ startLocation.longitude) &&
+ cbbox.covers(endLocation.latitude,
+ endLocation.longitude)) {
+ Utils.debug('Provider matches on bbox: ' + bbox);
+ matchingProviders.push(provider);
+ return;
+ }
+ }
+ });
+ });
+
+ Utils.debug('Number of matching providers: ' + matchingProviders.length);
+
+ if (matchingProviders.length === 0)
+ return null;
+
+ matchingProviders.sort(this._sortProviders);
+
+ for (let i = 0; i < matchingProviders.length; i++) {
+ let provider = matchingProviders[i];
+ let plugin = provider.plugin;
+
+ if (this._providerCache[plugin])
+ return [provider, this._providerCache[plugin]];
+
+ let module =
+ plugin[0].toLowerCase() + plugin.substring(1, plugin.length);
+
+ try {
+ let params = provider.params;
+ let instance =
+ params ? new imports.transitplugins[module][plugin](params) :
+ new imports.transitplugins[module][plugin]();
+
+ this._providerCache[plugin] = instance;
+
+ return [provider, instance];
+ } catch (e) {
+ Utils.debug('Failed to load plugin: ' + plugin + ": " + e);
+ }
+ }
+
+ Utils.debug('No suitable provider found');
+ return null;
+ }
+
+ _sortProviders(p1, p2) {
+ if (p1.priority && p2.priority)
+ return p1.priority - p2.priority;
+ else if (p1.priority)
+ return -1;
+ else if (p2.priority)
+ return 1;
+ else
+ return 0;
+ }
+};
diff --git a/src/transitplugins/openTripPlanner.js b/src/transitplugins/openTripPlanner.js
index d42e71b..b10b644 100644
--- a/src/transitplugins/openTripPlanner.js
+++ b/src/transitplugins/openTripPlanner.js
@@ -130,7 +130,8 @@ var OpenTripPlanner = class OpenTripPlanner {
this._routersUpdatedTimestamp = 0;
this._plan = Application.routingDelegator.transitRouter.plan;
this._query = Application.routeQuery;
- this._baseUrl = this._getBaseUrl();
+ this._baseUrl = params.baseUrl;
+ Utils.debug('baseUrl: ' + this._baseUrl);
this._walkingRoutes = [];
this._extendPrevious = false;
}
@@ -153,23 +154,6 @@ var OpenTripPlanner = class OpenTripPlanner {
this._fetchRoute();
}
- _getBaseUrl() {
- let debugUrl = GLib.getenv('OTP_BASE_URL');
-
- if (debugUrl) {
- return debugUrl;
- } else {
- let otp = Service.getService().openTripPlanner
-
- if (otp && otp.baseUrl) {
- return otp.baseUrl;
- } else {
- Utils.debug('No OpenTripPlanner URL defined in service file');
- return null;
- }
- }
- }
-
_getRouterUrl(router) {
if (!router || router.length === 0)
router = 'default';
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]