diff --git a/poor/config.py b/poor/config.py
index 9a8c808..26890f7 100644
--- a/poor/config.py
+++ b/poor/config.py
@@ -111,6 +111,12 @@ def _migrate(self, values):
# Impose new default providers introduced in 0.25.
for option in ("geocoder", "guide", "router"):
values[option] = DEFAULTS[option]
+ if version < (0,30):
+ # libosmscout and Valhalla routers merged in 0.30.
+ # https://github.com/otsaloma/poor-maps/pull/41
+ routers = values.setdefault("routers", {})
+ routers.pop("osmscout", None)
+ routers.pop("osmscout_valhalla", None)
return values
def read(self, path=None):
diff --git a/routers/osmscout.json b/routers/osmscout.json
index 2c0f29a..53fccd1 100644
--- a/routers/osmscout.json
+++ b/routers/osmscout.json
@@ -2,7 +2,7 @@
"attribution": "Data © OpenStreetMap contributors",
"_description": "Routing based on OpenStreetMap data",
"_modes": "car, bicycle, foot",
- "name": "OSM Scout libosmscout",
- "requires": ["harbour-osmscout-server"],
+ "name": "OSM Scout",
+ "requires": ["harbour-osmscout-server", "harbour-osmscout-server-module-route"],
"_source": "OSM Scout offline server"
}
diff --git a/routers/osmscout.py b/routers/osmscout.py
index dc9bb61..324d292 100644
--- a/routers/osmscout.py
+++ b/routers/osmscout.py
@@ -16,18 +16,59 @@
# along with this program. If not, see .
"""
-Routing using OSM Scout Server.
+Routing using OSM Scout Server's Valhalla and libosmscout routers.
https://github.com/rinigus/osmscout-server
+https://github.com/valhalla/valhalla-docs/blob/master/api-reference.md
"""
import copy
+import json
import poor
import urllib.parse
-CONF_DEFAULTS = {"type": "car"}
+CONF_DEFAULTS = {"type": "auto"}
-ICONS = {
+ICONS = { 0: "flag",
+ 1: "depart",
+ 2: "depart-right",
+ 3: "depart-left",
+ 4: "arrive",
+ 5: "arrive-right",
+ 6: "arrive-left",
+ 7: "continue",
+ 8: "continue",
+ 9: "turn-slight-right",
+ 10: "turn-right",
+ 11: "turn-sharp-right",
+ 12: "uturn",
+ 13: "uturn",
+ 14: "turn-sharp-left",
+ 15: "turn-left",
+ 16: "turn-slight-left",
+ 17: "continue",
+ 18: "off-ramp-slight-right",
+ 19: "off-ramp-slight-left",
+ 20: "off-ramp-slight-right",
+ 21: "off-ramp-slight-left",
+ 22: "fork-straight",
+ 23: "fork-slight-right",
+ 24: "fork-slight-left",
+ 25: "merge-slight-left",
+ 26: "roundabout",
+ 27: "off-ramp-slight-right",
+ 28: "flag",
+ 29: "flag",
+ 30: "flag",
+ 31: "flag",
+ 32: "flag",
+ 33: "flag",
+ 34: "flag",
+ 35: "flag",
+ 36: "flag",
+}
+
+ICONS_OSMSCOUT = {
"destination": "arrive",
"flag": "flag",
"merge": "merge-slight-left",
@@ -46,36 +87,62 @@
"turn-slight-right": "turn-slight-right",
}
-URL = "http://localhost:8553/v1/route?type={type}"
+URL = "http://localhost:8553/v2/route?json={input}"
cache = {}
-def render_endpoint(i, point):
- """Return URL component for given endpoint."""
+def prepare_endpoint(point):
+ """Return `point` as a dictionary ready to be passed on to the router."""
if isinstance(point, (list, tuple)):
- return ("&p[{i:d}][lng]={x:.6f}&p[{i:d}][lat]={y:.6f}"
- .format(i=i, x=point[0], y=point[1]))
- point = urllib.parse.quote_plus(point)
- return "&p[{i:d}][search]={point}".format(i=i, point=point)
+ return dict(lat=point[1], lon=point[0])
+ geocoder = poor.Geocoder("osmscout")
+ results = geocoder.geocode(point, dict(limit=1))
+ return prepare_endpoint((results[0]["x"], results[0]["y"]))
def route(fm, to, params):
"""Find route and return its properties as a dictionary."""
- type = poor.conf.routers.osmscout.type
+ fm, to = map(prepare_endpoint, (fm, to))
+ lang = poor.util.get_default_language("en")
+ input = dict(locations=[fm, to],
+ costing=poor.conf.routers.osmscout.type,
+ directions_options=dict(language=lang))
+
+ input = urllib.parse.quote(json.dumps(input))
url = URL.format(**locals())
- for i, point in enumerate((fm, to)):
- url += render_endpoint(i, point)
with poor.util.silent(KeyError):
return copy.deepcopy(cache[url])
result = poor.http.get_json(url)
+ if result.get("API version", "") == "libosmscout V1":
+ return parse_result_libosmscout(url, result)
+ return parse_result_valhalla(url, result)
+
+def parse_result_libosmscout(url, result):
+ """Parse and return route from libosmscout engine."""
x, y = result["lng"], result["lat"]
maneuvers = [dict(
x=float(maneuver["lng"]),
y=float(maneuver["lat"]),
- icon=ICONS.get(maneuver.get("type", "flag"), "flag"),
+ icon=ICONS_OSMSCOUT.get(maneuver.get("type", "flag"), "flag"),
narrative=maneuver["instruction"],
duration=float(maneuver["time"]),
length=float(maneuver["length"]),
) for maneuver in result["maneuvers"]]
- route = dict(x=x, y=y, maneuvers=maneuvers)
+ route = dict(x=x, y=y, maneuvers=maneuvers, engine="libosmscout")
+ if route and route["x"]:
+ cache[url] = copy.deepcopy(route)
+ return route
+
+def parse_result_valhalla(url, result):
+ """Parse and return route from Valhalla engine."""
+ legs = result["trip"]["legs"][0]
+ x, y = poor.util.decode_epl(legs["shape"], precision=6)
+ maneuvers = [dict(
+ x=float(x[maneuver["begin_shape_index"]]),
+ y=float(y[maneuver["begin_shape_index"]]),
+ icon=ICONS.get(maneuver["type"], "flag"),
+ narrative=maneuver["instruction"],
+ duration=float(maneuver["time"]),
+ ) for maneuver in legs["maneuvers"]]
+ route = dict(x=x, y=y, maneuvers=maneuvers, engine="Valhalla")
if route and route["x"]:
cache[url] = copy.deepcopy(route)
return route
diff --git a/routers/osmscout_results.qml b/routers/osmscout_results.qml
index 566c305..6d09396 100644
--- a/routers/osmscout_results.qml
+++ b/routers/osmscout_results.qml
@@ -26,7 +26,6 @@ Page {
property bool loading: true
BusyModal {
id: busy
- description: qsTranslate("", "For long routes, this could take up to a minute.")
running: page.loading
}
onStatusChanged: {
@@ -50,7 +49,11 @@ Page {
"x": route.x,
"y": route.y,
"mode": "car",
- "attribution": qsTranslate("", "Routing courtesy of %1.").arg("OSM Scout Server")
+ // TRANSLATORS: %1 refers to the routing engine, %2 to the service.
+ "attribution": (qsTranslate("", "Routing by %1, courtesy of %2")
+ .arg(route.engine)
+ .arg("OSM Scout Server"))
+
});
map.hidePoiBubbles();
map.fitViewToRoute();
diff --git a/routers/osmscout_settings.qml b/routers/osmscout_settings.qml
index 25412a0..e254270 100644
--- a/routers/osmscout_settings.qml
+++ b/routers/osmscout_settings.qml
@@ -28,7 +28,7 @@ Column {
MenuItem { text: qsTranslate("", "Bicycle") }
MenuItem { text: qsTranslate("", "Foot") }
}
- property var keys: ["car", "bicycle", "foot"]
+ property var keys: ["auto", "bicycle", "pedestrian"]
Component.onCompleted: {
var key = app.conf.get("routers.osmscout.type");
typeComboBox.currentIndex = typeComboBox.keys.indexOf(key);
diff --git a/routers/osmscout_valhalla.json b/routers/osmscout_valhalla.json
deleted file mode 100644
index c09ffa4..0000000
--- a/routers/osmscout_valhalla.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "attribution": "Data © OpenStreetMap contributors",
- "_description": "Routing based on OpenStreetMap data",
- "_modes": "car, bicycle, foot",
- "name": "OSM Scout Valhalla",
- "requires": ["harbour-osmscout-server", "harbour-osmscout-server-module-route"],
- "_source": "OSM Scout offline server"
-}
diff --git a/routers/osmscout_valhalla.py b/routers/osmscout_valhalla.py
deleted file mode 100644
index 789f478..0000000
--- a/routers/osmscout_valhalla.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2015 Osmo Salomaa
-#
-# This program 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 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 this program. If not, see .
-
-"""
-Routing using OSM Scout Server's Valhalla router.
-
-https://github.com/rinigus/osmscout-server
-https://github.com/valhalla/valhalla-docs/blob/master/api-reference.md
-"""
-
-import copy
-import json
-import poor
-import urllib.parse
-
-CONF_DEFAULTS = {"type": "auto"}
-
-ICONS = { 0: "flag",
- 1: "depart",
- 2: "depart-right",
- 3: "depart-left",
- 4: "arrive",
- 5: "arrive-right",
- 6: "arrive-left",
- 7: "continue",
- 8: "continue",
- 9: "turn-slight-right",
- 10: "turn-right",
- 11: "turn-sharp-right",
- 12: "uturn",
- 13: "uturn",
- 14: "turn-sharp-left",
- 15: "turn-left",
- 16: "turn-slight-left",
- 17: "continue",
- 18: "off-ramp-slight-right",
- 19: "off-ramp-slight-left",
- 20: "off-ramp-slight-right",
- 21: "off-ramp-slight-left",
- 22: "fork-straight",
- 23: "fork-slight-right",
- 24: "fork-slight-left",
- 25: "merge-slight-left",
- 26: "roundabout",
- 27: "off-ramp-slight-right",
- 28: "flag",
- 29: "flag",
- 30: "flag",
- 31: "flag",
- 32: "flag",
- 33: "flag",
- 34: "flag",
- 35: "flag",
- 36: "flag",
-}
-
-URL = "http://localhost:8553/v2/route?json={input}"
-cache = {}
-
-def prepare_endpoint(point):
- """Return `point` as a dictionary ready to be passed on to the router."""
- if isinstance(point, (list, tuple)):
- return dict(lat=point[1], lon=point[0])
- geocoder = poor.Geocoder("osmscout")
- results = geocoder.geocode(point, dict(limit=1))
- return prepare_endpoint((results[0]["x"], results[0]["y"]))
-
-def route(fm, to, params):
- """Find route and return its properties as a dictionary."""
- fm, to = map(prepare_endpoint, (fm, to))
- type = poor.conf.routers.osmscout_valhalla.type
- input = dict(locations=[fm, to], costing=type,
- directions_options={"language": poor.util.get_default_language("en")})
- input = urllib.parse.quote(json.dumps(input))
- url = URL.format(**locals())
- with poor.util.silent(KeyError):
- return copy.deepcopy(cache[url])
- result = poor.http.get_json(url)
- legs = result["trip"]["legs"][0]
- x, y = poor.util.decode_epl(legs["shape"], precision=6)
- maneuvers = [dict(
- x=float(x[maneuver["begin_shape_index"]]),
- y=float(y[maneuver["begin_shape_index"]]),
- icon=ICONS.get(maneuver["type"], "flag"),
- narrative=maneuver["instruction"],
- duration=float(maneuver["time"]),
- ) for maneuver in legs["maneuvers"]]
- route = dict(x=x, y=y, maneuvers=maneuvers)
- if route and route["x"]:
- cache[url] = copy.deepcopy(route)
- return route
diff --git a/routers/osmscout_valhalla_results.qml b/routers/osmscout_valhalla_results.qml
deleted file mode 100644
index ef8459a..0000000
--- a/routers/osmscout_valhalla_results.qml
+++ /dev/null
@@ -1,68 +0,0 @@
-/* -*- coding: utf-8-unix -*-
- *
- * Copyright (C) 2014 Osmo Salomaa
- *
- * This program 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 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program. If not, see .
- */
-
-import QtQuick 2.0
-import Sailfish.Silica 1.0
-import "../qml"
-
-Page {
- id: page
- allowedOrientations: app.defaultAllowedOrientations
- property bool loading: true
- BusyModal {
- id: busy
- running: page.loading
- }
- onStatusChanged: {
- if (page.status === PageStatus.Activating) {
- busy.text = qsTranslate("", "Searching");
- } else if (page.status === PageStatus.Active) {
- page.findRoute();
- }
- }
- function findRoute() {
- // Load routing results from the Python backend.
- var routePage = app.pageStack.previousPage();
- var args = [routePage.from, routePage.to];
- py.call("poor.app.router.route", args, function(route) {
- if (route && route.error && route.message) {
- busy.error = route.message;
- page.loading = false;
- } else if (route && route.x && route.x.length > 0) {
- app.hideMenu();
- map.addRoute({
- "x": route.x,
- "y": route.y,
- "mode": "car",
- // TRANSLATORS: %1 refers to the routing engine, %2 to the service.
- "attribution": (qsTranslate("", "Routing by %1, courtesy of %2")
- .arg("Valhalla")
- .arg("OSM Scout Server"))
-
- });
- map.hidePoiBubbles();
- map.fitViewToRoute();
- map.addManeuvers(route.maneuvers);
- app.pageStack.navigateBack(PageStackAction.Immediate);
- } else {
- busy.error = qsTranslate("", "No results");
- page.loading = false;
- }
- });
- }
-}
diff --git a/routers/osmscout_valhalla_settings.qml b/routers/osmscout_valhalla_settings.qml
deleted file mode 100644
index 9214f96..0000000
--- a/routers/osmscout_valhalla_settings.qml
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- coding: utf-8-unix -*-
- *
- * Copyright (C) 2014 Osmo Salomaa
- *
- * This program 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 3 of the License, or
- * (at your option) any later version.
- *
- * This program 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 this program. If not, see .
- */
-
-import QtQuick 2.0
-import Sailfish.Silica 1.0
-
-Column {
- ComboBox {
- id: typeComboBox
- label: qsTranslate("", "Type")
- menu: ContextMenu {
- MenuItem { text: qsTranslate("", "Car") }
- MenuItem { text: qsTranslate("", "Bicycle") }
- MenuItem { text: qsTranslate("", "Foot") }
- }
- property var keys: ["auto", "bicycle", "pedestrian"]
- Component.onCompleted: {
- var key = app.conf.get("routers.osmscout_valhalla.type");
- typeComboBox.currentIndex = typeComboBox.keys.indexOf(key);
- }
- onCurrentIndexChanged: {
- var option = "routers.osmscout_valhalla.type";
- app.conf.set(option, typeComboBox.keys[typeComboBox.currentIndex]);
- }
- }
-}