Skip to content

Commit 8687fe3

Browse files
merydiankoebi
andauthored
feat: add processing algo for export endpoint (#269)
Co-authored-by: Jakob Schnell <[email protected]>
1 parent 78064b1 commit 8687fe3

File tree

9 files changed

+257
-18
lines changed

9 files changed

+257
-18
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ RELEASING:
4141
-->
4242

4343
## Unreleased
44+
### Added
45+
- Processing algorithms for the Network Export endpoint ([#210](https://github.com/GIScience/orstools-qgis-plugin/issues/210))
4446

4547
### Changed
4648
- Use QgsSettings instead of config.yml file to avoid deletion of providers on update ([#108](https://github.com/GIScience/orstools-qgis-plugin/issues/108))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Export the base graph for different modes of transport.
2+
3+
You need to have a valid API key ('Web' menu ► 'ORS Tools' ► 'Configuration') or sign up at <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a>.
4+
Current <a href="https://openrouteservice.org/restrictions/">restriction limits</a> for the openrouteservice API apply.
5+
6+
<i>Travel Mode</i>: determines the profile used.
7+
8+
<i>Input Extent</i>: Choose an extent, the content of which will be exported.
9+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Das Basisgraph für verschiedene Verkehrsmittel exportieren.
2+
3+
Ein gültiger API-Schlüssel ist erforderlich ('Web'-Menü ► 'ORS Tools' ► 'Konfiguration') oder eine Anmeldung unter <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a>. Aktuelle <a href="https://openrouteservice.org/restrictions/">Beschränkungslimits</a> für die openrouteservice API gelten.
4+
5+
<i>Verkehrsmittel</i>: bestimmt das genutzte Reise-Profil
6+
7+
<i>Input-Extent</i>: Es ist ein Bereich auszuwählen, dessen Inhalt exportiert wird.

ORStools/i18n/orstools_de.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
<context>
55
<name>@default</name>
66
<message>
7-
<location filename="../gui/ORStoolsDialog.py" line="122"/>
7+
<location filename="../gui/ORStoolsDialog.py" line="129"/>
88
<source>About {}</source>
99
<translation>Über {}</translation>
1010
</message>
1111
<message>
12-
<location filename="../gui/ORStoolsDialog.py" line="105"/>
12+
<location filename="../gui/ORStoolsDialog.py" line="111"/>
1313
<source>&lt;b&gt;ORS Tools&lt;/b&gt; provides access to &lt;a href=&quot;https://openrouteservice.org&quot; style=&quot;color: {0}&quot;&gt;openrouteservice&lt;/a&gt; routing functionalities.&lt;br&gt;&lt;br&gt;&lt;center&gt;&lt;a href=&quot;https://heigit.org/de/willkommen&quot;&gt;&lt;img src=&quot;:/plugins/ORStools/img/logo_heigit_300.png&quot;/&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;/center&gt;Author: HeiGIT gGmbH&lt;br&gt;Email: &lt;a href=&quot;mailto:Openrouteservice &lt;{1}&gt;&quot;&gt;{1}&lt;/a&gt;&lt;br&gt;Web: &lt;a href=&quot;{2}&quot;&gt;{2}&lt;/a&gt;&lt;br&gt;Repo: &lt;a href=&quot;https://github.com/GIScience/orstools-qgis-plugin&quot;&gt;github.com/GIScience/orstools-qgis-plugin&lt;/a&gt;&lt;br&gt;Version: {3}</source>
1414
<translation>&lt;b&gt;ORS Tools&lt;/b&gt; bietet Zugriff auf &lt;a href=&quot;https://openrouteservice.org&quot; style=&quot;color: {0}&quot;&gt;openrouteservice&lt;/a&gt; Berechnungen.&lt;br&gt;&lt;br&gt;&lt;center&gt;&lt;a href=&quot;https://heigit.org/de/willkommen&quot;&gt;&lt;img src=&quot;:/plugins/ORStools/img/logo_heigit_300.png&quot;/&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;&lt;/center&gt;Author: HeiGIT gGmbH&lt;br&gt;Email: &lt;a href=&quot;mailto:Openrouteservice &lt;{1}&gt;&quot;&gt;{1}&lt;/a&gt;&lt;br&gt;Web: &lt;a href=&quot;{2}&quot;&gt;{2}&lt;/a&gt;&lt;br&gt;Repo: &lt;a href=&quot;https://github.com/GIScience/orstools-qgis-plugin&quot;&gt;github.com/GIScience/orstools-qgis-plugin&lt;/a&gt;&lt;br&gt;Version: {3}</translation>
1515
</message>
@@ -127,7 +127,7 @@
127127
<translation>Wegpunktoptimierung (sonstige Konfiguration wird nicht berücksichtigt)</translation>
128128
</message>
129129
<message>
130-
<location filename="../proc/directions_points_layer_proc.py" line="298"/>
130+
<location filename="../proc/directions_points_layer_proc.py" line="297"/>
131131
<source>Directions from 1 Point-Layer</source>
132132
<translation>Routenberechnung aus einem Punkt-Layer</translation>
133133
</message>
@@ -229,6 +229,19 @@ Duplikate entfernen oder Wegpunktoptimierung abwählen.</translation>
229229
<translation>Csv Spalte (benötigt Csv Faktor und csv in Extra Info)</translation>
230230
</message>
231231
</context>
232+
<context>
233+
<name>ORSExportAlgo</name>
234+
<message>
235+
<location filename="../proc/export_proc.py" line="67"/>
236+
<source>Input Extent</source>
237+
<translation>Ausdehnung</translation>
238+
</message>
239+
<message>
240+
<location filename="../proc/export_proc.py" line="179"/>
241+
<source>Export Network from Map</source>
242+
<translation>Netzwerk von Karte exportieren</translation>
243+
</message>
244+
</context>
232245
<context>
233246
<name>ORSIsochronesLayerAlgo</name>
234247
<message>
@@ -331,12 +344,12 @@ Duplikate entfernen oder Wegpunktoptimierung abwählen.</translation>
331344
<context>
332345
<name>ORStoolsDialog</name>
333346
<message>
334-
<location filename="../gui/ORStoolsDialog.py" line="473"/>
347+
<location filename="../gui/ORStoolsDialog.py" line="481"/>
335348
<source>Apply</source>
336349
<translation>Anwenden</translation>
337350
</message>
338351
<message>
339-
<location filename="../gui/ORStoolsDialog.py" line="474"/>
352+
<location filename="../gui/ORStoolsDialog.py" line="482"/>
340353
<source>Close</source>
341354
<translation>Schließen</translation>
342355
</message>
@@ -443,7 +456,7 @@ p, li { white-space: pre-wrap; }
443456
<translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
444457
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
445458
p, li { white-space: pre-wrap; }
446-
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
459+
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
447460
&lt;p style=&quot; padding: 10px; -qt-block-indent:0; text-indent:0px ; background-color:#e7f2fa; color: #999999&quot;&gt;&lt;img stype=&quot;margin: 10px&quot; src=&quot;:/plugins/ORStools/img/icon_about.png&quot; width=16 height=16 /&gt; Sämtliche Einstellungen werden überschrieben&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
448461
</message>
449462
<message>
@@ -696,27 +709,27 @@ p, li { white-space: pre-wrap; }
696709
<context>
697710
<name>ORStoolsDialogMain</name>
698711
<message>
699-
<location filename="../gui/ORStoolsDialog.py" line="178"/>
712+
<location filename="../gui/ORStoolsDialog.py" line="185"/>
700713
<source>Help</source>
701714
<translation>Hilfe</translation>
702715
</message>
703716
<message>
704-
<location filename="../gui/ORStoolsDialog.py" line="170"/>
717+
<location filename="../gui/ORStoolsDialog.py" line="177"/>
705718
<source>Provider Settings</source>
706719
<translation>Dienst-Einstellungen</translation>
707720
</message>
708721
<message>
709-
<location filename="../gui/ORStoolsDialog.py" line="176"/>
722+
<location filename="../gui/ORStoolsDialog.py" line="183"/>
710723
<source>About</source>
711724
<translation>Über</translation>
712725
</message>
713726
<message>
714-
<location filename="../gui/ORStoolsDialog.py" line="329"/>
727+
<location filename="../gui/ORStoolsDialog.py" line="336"/>
715728
<source>Duplicates</source>
716729
<translation>Duplikate</translation>
717730
</message>
718731
<message>
719-
<location filename="../gui/ORStoolsDialog.py" line="329"/>
732+
<location filename="../gui/ORStoolsDialog.py" line="336"/>
720733
<source>
721734
There are duplicate points in the input layer. Traveling Salesman Optimization does not allow this.
722735
Either remove the duplicates or deselect Traveling Salesman.
@@ -725,7 +738,7 @@ p, li { white-space: pre-wrap; }
725738
Duplikate entfernen oder Wegpunktoptimierung abwählen.</translation>
726739
</message>
727740
<message>
728-
<location filename="../gui/ORStoolsDialog.py" line="338"/>
741+
<location filename="../gui/ORStoolsDialog.py" line="346"/>
729742
<source>The request has been aborted!</source>
730743
<translation>Die Anfrage wurde abgebrochen!</translation>
731744
</message>

ORStools/i18n/translate.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ SOURCES = ../common/directions_core.py \
99
../proc/isochrones_layer_proc.py \
1010
../proc/isochrones_point_proc.py \
1111
../proc/matrix_proc.py \
12+
../proc/export_proc.py \
1213
../gui/ORStoolsDialog.py \
1314
../gui/ORStoolsDialogConfig.py
1415

ORStools/proc/base_processing_algorithm.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,19 @@ def initAlgorithm(self, configuration: Dict) -> None:
227227
Combines default and algorithm parameters and adds them in order to the
228228
algorithm dialog window.
229229
"""
230-
parameters = (
231-
[self.provider_parameter(), self.profile_parameter()]
232-
+ self.PARAMETERS
233-
+ self.option_parameters()
234-
+ [self.output_parameter()]
235-
)
230+
if self.ALGO_NAME not in ["export_network_from_map"]:
231+
parameters = (
232+
[self.provider_parameter(), self.profile_parameter()]
233+
+ self.PARAMETERS
234+
+ self.option_parameters()
235+
+ [self.output_parameter()]
236+
)
237+
else:
238+
parameters = (
239+
[self.provider_parameter(), self.profile_parameter()]
240+
+ self.PARAMETERS
241+
+ [self.output_parameter()]
242+
)
236243
for param in parameters:
237244
if param.name() in ADVANCED_PARAMETERS:
238245
if self.GROUP == "Matrix":

ORStools/proc/export_proc.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
/***************************************************************************
4+
ORStools
5+
A QGIS plugin
6+
QGIS client to query openrouteservice
7+
-------------------
8+
begin : 2017-02-01
9+
git sha : $Format:%H$
10+
copyright : (C) 2021 by HeiGIT gGmbH
11+
12+
***************************************************************************/
13+
14+
This plugin provides access to openrouteservice API functionalities
15+
(https://openrouteservice.org), developed and
16+
maintained by the openrouteservice team of HeiGIT gGmbH, Germany. By using
17+
this plugin you agree to the ORS terms of service
18+
(https://openrouteservice.org/terms-of-service/).
19+
20+
/***************************************************************************
21+
* *
22+
* This program is free software; you can redistribute it and/or modify *
23+
* it under the terms of the GNU General Public License as published by *
24+
* the Free Software Foundation; either version 2 of the License, or *
25+
* (at your option) any later version. *
26+
* *
27+
***************************************************************************/
28+
"""
29+
30+
from typing import Dict
31+
32+
from qgis.core import (
33+
QgsWkbTypes,
34+
QgsFeature,
35+
QgsField,
36+
QgsFields,
37+
QgsCoordinateReferenceSystem,
38+
QgsProcessingParameterExtent,
39+
QgsProcessingParameterFeatureSink,
40+
QgsProcessingContext,
41+
QgsProcessingFeedback,
42+
QgsPointXY,
43+
QgsGeometry,
44+
)
45+
46+
from qgis.PyQt.QtCore import QVariant
47+
48+
49+
from ORStools.common import PROFILES
50+
from ORStools.utils import exceptions, logger
51+
from .base_processing_algorithm import ORSBaseProcessingAlgorithm
52+
53+
54+
# noinspection PyPep8Naming
55+
class ORSExportAlgo(ORSBaseProcessingAlgorithm):
56+
def __init__(self):
57+
super().__init__()
58+
self.ALGO_NAME: str = "export_network_from_map"
59+
self.GROUP: str = "Export"
60+
self.IN_EXPORT: str = "INPUT_EXPORT"
61+
self.OUT_POINT = "OUTPUT_POINT"
62+
self.PARAMETERS: list = [
63+
QgsProcessingParameterExtent(
64+
name=self.IN_EXPORT,
65+
description=self.tr("Input Extent"),
66+
),
67+
QgsProcessingParameterFeatureSink(
68+
name=self.OUT_POINT,
69+
description="Node Export",
70+
),
71+
]
72+
73+
def processAlgorithm(
74+
self, parameters: dict, context: QgsProcessingContext, feedback: QgsProcessingFeedback
75+
) -> Dict[str, str]:
76+
ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
77+
78+
# Get profile value
79+
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
80+
81+
target_crs = QgsCoordinateReferenceSystem("EPSG:4326")
82+
rect = self.parameterAsExtent(parameters, self.IN_EXPORT, context, crs=target_crs)
83+
84+
extent = [[rect.xMinimum(), rect.yMinimum()], [rect.xMaximum(), rect.yMaximum()]]
85+
86+
params = {
87+
"bbox": extent,
88+
"id": "export_request",
89+
}
90+
91+
(sink_line, dest_id_line) = self.parameterAsSink(
92+
parameters,
93+
self.OUT,
94+
context,
95+
self.get_fields_line(),
96+
QgsWkbTypes.Type.LineString,
97+
QgsCoordinateReferenceSystem.fromEpsgId(4326),
98+
)
99+
100+
(sink_point, dest_id_point) = self.parameterAsSink(
101+
parameters,
102+
self.OUT_POINT,
103+
context,
104+
self.get_fields_point(),
105+
QgsWkbTypes.Type.Point,
106+
QgsCoordinateReferenceSystem.fromEpsgId(4326),
107+
)
108+
109+
# Make request and catch ApiError
110+
try:
111+
response = ors_client.request("/v2/export/" + profile, {}, post_json=params)
112+
nodes_dict = {item["nodeId"]: item["location"] for item in response["nodes"]}
113+
edges = response["edges"]
114+
for edge in edges:
115+
from_id = edge["fromId"]
116+
to_id = edge["toId"]
117+
weight = edge["weight"]
118+
119+
to_coords = nodes_dict[to_id]
120+
from_coords = nodes_dict[from_id]
121+
122+
geometry = QgsGeometry.fromPolylineXY(
123+
[
124+
QgsPointXY(from_coords[0], from_coords[1]),
125+
QgsPointXY(to_coords[0], to_coords[1]),
126+
]
127+
)
128+
129+
feat = QgsFeature()
130+
feat.setGeometry(geometry)
131+
feat.setAttributes([from_id, to_id, weight])
132+
sink_line.addFeature(feat)
133+
134+
unique_coordinates = {
135+
tuple(item["location"]): item["nodeId"] for item in response["nodes"]
136+
}
137+
points = [(coords, node_id) for coords, node_id in unique_coordinates.items()]
138+
for item in points:
139+
point = QgsPointXY(item[0][0], item[0][1])
140+
point_geometry = QgsGeometry.fromPointXY(point)
141+
142+
point_feat = QgsFeature()
143+
point_feat.setGeometry(point_geometry)
144+
point_feat.setAttributes([item[1]])
145+
sink_point.addFeature(point_feat)
146+
147+
except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e:
148+
msg = f"{e.__class__.__name__}: {str(e)}"
149+
feedback.reportError(msg)
150+
logger.log(msg)
151+
152+
return {self.OUT: dest_id_line, self.OUT_POINT: dest_id_point}
153+
154+
@staticmethod
155+
def get_fields_line():
156+
fields = QgsFields()
157+
fields.append(QgsField("FROM_ID", QVariant.Double))
158+
fields.append(QgsField("TO_ID", QVariant.Double))
159+
fields.append(QgsField("WEIGHT", QVariant.Double))
160+
161+
return fields
162+
163+
@staticmethod
164+
def get_fields_point():
165+
fields = QgsFields()
166+
fields.append(QgsField("ID", QVariant.Int))
167+
168+
return fields
169+
170+
def displayName(self) -> str:
171+
"""
172+
Algorithm name shown in QGIS toolbox
173+
:return:
174+
"""
175+
return self.tr("Export Network from Map")

ORStools/proc/provider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from .directions_lines_proc import ORSDirectionsLinesAlgo
3636
from .directions_points_layer_proc import ORSDirectionsPointsLayerAlgo
3737
from .directions_points_layers_proc import ORSDirectionsPointsLayersAlgo
38+
from .export_proc import ORSExportAlgo
3839
from .isochrones_layer_proc import ORSIsochronesLayerAlgo
3940
from .isochrones_point_proc import ORSIsochronesPointAlgo
4041
from .matrix_proc import ORSMatrixAlgo
@@ -63,6 +64,7 @@ def loadAlgorithms(self) -> None:
6364
self.addAlgorithm(ORSIsochronesLayerAlgo())
6465
self.addAlgorithm(ORSIsochronesPointAlgo())
6566
self.addAlgorithm(ORSMatrixAlgo())
67+
self.addAlgorithm(ORSExportAlgo())
6668

6769
@staticmethod
6870
def icon() -> QIcon:

0 commit comments

Comments
 (0)