[gnome-maps/wip/mlundblad/transit-tweaks: 1/2] WIP: Add module to apply tweaks on transit itineraries



commit 40cab2ef3c476daadcf19fec61ee74bc82f557a6
Author: Marcus Lundblad <ml update uu se>
Date:   Sat Oct 19 23:16:01 2019 +0200

    WIP: Add module to apply tweaks on transit itineraries

 src/org.gnome.Maps.src.gresource.xml |   1 +
 src/transitTweaks.js                 | 187 +++++++++++++++++++++++++++++++++++
 2 files changed, 188 insertions(+)
---
diff --git a/src/org.gnome.Maps.src.gresource.xml b/src/org.gnome.Maps.src.gresource.xml
index 322722bf..19475973 100644
--- a/src/org.gnome.Maps.src.gresource.xml
+++ b/src/org.gnome.Maps.src.gresource.xml
@@ -94,6 +94,7 @@
     <file>transitRouter.js</file>
     <file>transitRouteLabel.js</file>
     <file>transitStopRow.js</file>
+    <file>transitTweaks.js</file>
     <file>transitWalkMarker.js</file>
     <file>translations.js</file>
     <file>turnPointMarker.js</file>
diff --git a/src/transitTweaks.js b/src/transitTweaks.js
new file mode 100644
index 00000000..28bfe9d1
--- /dev/null
+++ b/src/transitTweaks.js
@@ -0,0 +1,187 @@
+/* -*- 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 GLib = imports.gi.GLib;
+const Soup = imports.gi.Soup;
+
+const Utils = imports.utils;
+
+const BASE_URL = 'https://gis.gnome.org/services/aux';
+
+var TransitTweaks = class {
+    constructor(params) {
+        this._name = params.name;
+        this._session =
+            new Soup.Session({ user_agent: 'gnome-maps/' + pkg.version });
+
+        if (!this._name)
+            throw new Error('Missing tweak name');
+    }
+
+    applyTweaks(itineraries, callback) {
+        /* TODO: for now read tweaks from a file, pointed to by
+         * a TRANSIT_TWEAKS_<NAME> env variable
+         */
+        if (!this._tweaks) {
+            let variable = 'TRANSIT_TWEAKS_' + this._name.toUpperCase();
+            let filename = GLib.getenv(variable);
+
+            if (filename) {
+                this._readTweaksFromFile(filename);
+                this._doApplyTweaks(itineraries, callback);
+            } else {
+                this._fetchTweaksAsync(itineraries, callback);
+            }
+        } else {
+            this._doApplyTweaks(itineraries, callback);
+        }
+    }
+
+    _doApplyTweaks(itineraries, callback) {
+        if (this._tweaks !== {}) {
+            itineraries.forEach((itinerary) =>
+                this._applyTweaksToItinerary(itinerary));
+        }
+
+        callback();
+    }
+
+    _readTweaksFromFile(filename) {
+        let data = Utils.readFile(filename);
+
+        if (!data) {
+            Utils.debug('Failed to read from tweak file');
+            callback();
+        }
+
+        try {
+            this._tweaks = JSON.parse(Utils.getBufferText(data));
+        } catch (e) {
+            Utils.debug('Failed to parse tweaks: ' + e);
+            this._tweaks = {};
+        }
+    }
+
+    _fetchTweaksAsync(itineraries, callback) {
+        let uri = new Soup.URI(BASE_URL + '/' + 'tweaks-' + this._name + '.json');
+        let request = new Soup.Message({ method: 'GET', uri: uri });
+
+        this._session.queue_message(request, (obj, message) => {
+            if (message.status !== Soup.Status.OK) {
+                Utils.debug('Failed to download tweaks');
+                callback();
+            } else {
+                try {
+                    this._tweaks = JSON.parse(message.response_body.data);
+                } catch (e) {
+                    Utils.debug('Failed to parse tweaks: ' + e);
+                    this._tweaks = {};
+                }
+
+                this._doApplyTweaks(itineraries, callback);
+            }
+        });
+    }
+
+    _applyTweaksToItinerary(itinerary) {
+        itinerary.legs.forEach((leg) => {
+            if (leg.transit)
+                this._applyTweaksToLeg(leg);
+        });
+    }
+
+    _applyTweaksToLeg(leg) {
+        let agencyTweaks = this._tweaks.agencies[leg.agencyName];
+
+        if (agencyTweaks) {
+            let routeTypeTweaks = agencyTweaks.routeTypes[leg.routeType];
+
+            Utils.debug('route type tweaks: ' + JSON.stringify(routeTypeTweaks, null, 2));
+
+            if (routeTypeTweaks) {
+                let tweakToApply;
+                let bboxTweaks = routeTypeTweaks.bboxes;
+                let routeTweaks = routeTypeTweaks.routes ?
+                                  routeTypeTweaks.routes[leg.route] : null;
+                let routePatternTweaks = routeTypeTweaks.routePatterns;
+
+                // first check for boundingbox-specific tweaks
+                if (bboxTweaks) {
+                    bboxTweaks.forEach((tweak) => {
+                        let bbox = tweak.bbox;
+                        let cbbox = new Champlain.BoundingBox({ bottom: bbox[0],
+                                                                left: bbox[1],
+                                                                top: bbox[2],
+                                                                right: bbox[3] });
+
+                        if (cbbox.covers(leg.polyline[0].latitude,
+                                         leg.polyline[0].longitude)) {
+                            /* if boundingbox fits, use embedded route or
+                             * route pattern tweaks
+                             */
+                            Utils.debug('matching on bounding box');
+                            routeTweaks = tweak.routes ?
+                                          tweak.routes[leg.route] : null;
+                            routePatternTweaks = tweak.routePatterns;
+                        }
+                    });
+                }
+
+                if (routeTweaks) {
+                    tweakToApply = routeTweaks;
+                } else if (routePatternTweaks) {
+                    routePatternTweaks.forEach((pattern) => {
+                        if (!(pattern.regex instanceof RegExp)) {
+                            pattern.regex = new RegExp(pattern.regex);
+                        }
+
+                        if (leg.route.match(pattern.regex))
+                            tweakToApply = pattern;
+                    });
+                }
+
+                if (!tweakToApply) {
+                    tweakToApply = routeTypeTweaks;
+                }
+
+                Utils.debug('tweak to apply: ' +
+                            JSON.stringify(tweakToApply, null, 2));
+
+                this._applyRouteTweaksToLeg(leg, tweakToApply);
+            }
+        }
+    }
+
+    _applyRouteTweaksToLeg(leg, tweaks) {
+        if (tweaks.route)
+            leg.route = tweaks.route;
+
+        if (tweaks.routeType)
+            leg.routeType = tweaks.routeType;
+
+        if (tweaks.color)
+            leg.color = tweaks.color;
+
+        if (tweaks.textColor)
+            leg.textColor = tweaks.textColor;
+    }
+}


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