Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add snapping endpoint #267

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b28ce34
docs: add python file header
merydian May 27, 2024
28dbfcb
feat: add basic class
merydian May 27, 2024
ba20020
feat: ad parameters
merydian May 27, 2024
f4cd97c
feat: add algorithm to qtoolbox
merydian May 27, 2024
51951e1
fix: remove unidentified processing parameter
merydian May 27, 2024
b4c40e9
feat: add display name
merydian May 27, 2024
477eca5
feat: add algo name
merydian May 27, 2024
d155336
feat: add help file
merydian May 27, 2024
5995649
fear: add processAlgorithm methhod
merydian May 27, 2024
465e67a
feat: add snap utilities file
merydian May 27, 2024
2de0366
docs: add snap utilities header
merydian May 27, 2024
ac2a32f
feat: add functionality to processAlgorithm
merydian May 27, 2024
c21ebe8
feat: add function to get features from response
merydian May 27, 2024
7d19ed4
feat: add filling of attribute table
merydian May 27, 2024
d6342c5
fix: remove logging
merydian May 27, 2024
029a5c5
feat: add german help file
merydian May 27, 2024
3b2cc21
docs: add german help
merydian May 27, 2024
933639d
docs: add english help
merydian May 27, 2024
5034f29
docs: fix typo
merydian May 27, 2024
18dae3e
docs: add type hint
merydian May 27, 2024
24b47c3
style: run ruff
merydian May 27, 2024
54f2983
docs: add changelog entry
merydian May 27, 2024
47c3cb6
fix: add id to postJson to get actual error message
merydian Jun 7, 2024
cb500b2
feat: set default value for radius parameter
merydian Jun 7, 2024
c0f1066
fix: only show authorization error, if one arises
merydian Jun 7, 2024
8ccde92
style: rename snap layer proc
merydian Jun 7, 2024
7b4ef95
style: rename snap to snaplayeralgo in provider
merydian Jun 7, 2024
a23028b
fix: typo
merydian Jun 7, 2024
c082fea
Merge branch 'main' into 262-add-snapping-endpoint
merydian Jun 7, 2024
ac47319
feat: add snap from point proc
merydian Jun 7, 2024
9ff72d5
fix: error when no name in response location
merydian Jun 7, 2024
3e195f7
fix: error with no snap returned for particular point
merydian Jun 7, 2024
4f67956
style: run ruff
merydian Jun 7, 2024
f081195
style: run ruff
merydian Jun 10, 2024
90b1535
Merge branch '262-add-snapping-endpoint' of https://github.com/GIScie…
merydian Jun 11, 2024
458a668
feat: remove advanced parameters from snap procs
merydian Jul 6, 2024
ae04b9e
Revert "feat: remove advanced parameters from snap procs"
merydian Jul 6, 2024
3ab237e
feat: actually remove advanced parameters from snap procs
merydian Jul 6, 2024
2d180c6
style: run ruff
merydian Jul 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ RELEASING:
14. Create new release in GitHub with tag version and release title of `vX.X.X`
-->

## Unreleased
### Added
- Support for Snap endpoint ([#262](https://github.com/GIScience/orstools-qgis-plugin/issues/262))

## [1.8.3] - 2024-05-29

### Fixed
Expand Down
49 changes: 49 additions & 0 deletions ORStools/common/snap_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
ORStools
A QGIS plugin
QGIS client to query openrouteservice
-------------------
begin : 2017-02-01
git sha : $Format:%H$
copyright : (C) 2021 by HeiGIT gGmbH
email : [email protected]
***************************************************************************/

This plugin provides access to openrouteservice API functionalities
(https://openrouteservice.org), developed and
maintained by the openrouteservice team of HeiGIT gGmbH, Germany. By using
this plugin you agree to the ORS terms of service
(https://openrouteservice.org/terms-of-service/).

/***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""

from qgis.core import QgsFeature, QgsPointXY, QgsGeometry


def get_snapped_point_features(response: dict) -> list:
locations = response["locations"]
feats = []
for location in locations:
feat = QgsFeature()
if location:
coords = location["location"]
if "name" in location.keys():
name = location["name"]
snapped_distance = location["snapped_distance"]
attr = [name, snapped_distance] if "name" in location.keys() else ["", snapped_distance]
feat.setAttributes(attr)
feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(coords[0], coords[1])))

feats.append(feat)

return feats
11 changes: 11 additions & 0 deletions ORStools/help/snap_from_point.help
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
The snapping endpoint can be used to snap points to the edges of the street network for a specific means of transportation.

You need to have a valid API key ('Web' menu > 'Configuration') or sign up at <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a>.

<i>Travel Mode</i>: determines the profile used.

<i>Input layer</i>: select an input point from the canvas.

<i>Radius</i>: Radius in which to search.

Current <a href="https://openrouteservice.org/restrictions/">restriction limits</a> for the openrouteservice API apply.
11 changes: 11 additions & 0 deletions ORStools/help/snap_from_point_de.help
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Der Snap-Algorithmus kann verwendet werden, um Punkte an den Rändern des Straßennetzes für ein bestimmtes Verkehrsmittel zu finden.

Ein gültiger API-Key ('Web'-Menü > 'Dienst-Einstellungen') oder Registrierung unter <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a> wird benötigt.

<i>Verkehrsmittel</i>: bestimmt das genutzte Reise-Profil

<i>Eingabelayer</i>: Eingabepunkt

<i>Radius</i>: Radius in welchem gesucht wird.

Es gelten die <a href="https://openrouteservice.org/restrictions/">Restriktionen</a> der openrouteservice-API.
11 changes: 11 additions & 0 deletions ORStools/help/snap_from_point_layer.help
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
The snapping endpoint can be used to snap points to the edges of the street network for a specific means of transportation.

You need to have a valid API key ('Web' menu > 'Configuration') or sign up at <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a>.

<i>Travel Mode</i>: determines the profile used.

<i>Input layer</i>: only Point layers are allowed.

<i>Radius</i>: Radius in which to search.

Current <a href="https://openrouteservice.org/restrictions/">restriction limits</a> for the openrouteservice API apply.
11 changes: 11 additions & 0 deletions ORStools/help/snap_from_point_layer_de.help
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Der Snap-Algorithmus kann verwendet werden, um Punkte an den Rändern des Straßennetzes für ein bestimmtes Verkehrsmittel zu finden.

Ein gültiger API-Key ('Web'-Menü > 'Dienst-Einstellungen') oder Registrierung unter <a href="https://openrouteservice.org/sign-up/">https://openrouteservice.org/sign-up/</a> wird benötigt.

<i>Verkehrsmittel</i>: bestimmt das genutzte Reise-Profil

<i>Eingabelayer</i>: nur Punkt-Layer zugelassen.

<i>Radius</i>: Radius in welchem gesucht wird.

Es gelten die <a href="https://openrouteservice.org/restrictions/">Restriktionen</a> der openrouteservice-API.
19 changes: 13 additions & 6 deletions ORStools/proc/base_processing_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,12 +227,19 @@ def initAlgorithm(self, configuration: Dict) -> None:
Combines default and algorithm parameters and adds them in order to the
algorithm dialog window.
"""
parameters = (
[self.provider_parameter(), self.profile_parameter()]
+ self.PARAMETERS
+ self.option_parameters()
+ [self.output_parameter()]
)
if self.ALGO_NAME not in ["snap_from_point_layer", "snap_from_point"]:
parameters = (
[self.provider_parameter(), self.profile_parameter()]
+ self.PARAMETERS
+ self.option_parameters()
+ [self.output_parameter()]
)
else:
parameters = (
[self.provider_parameter(), self.profile_parameter()]
+ self.PARAMETERS
+ [self.output_parameter()]
)
for param in parameters:
if param.name() in ADVANCED_PARAMETERS:
if self.GROUP == "Matrix":
Expand Down
4 changes: 4 additions & 0 deletions ORStools/proc/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
from .isochrones_layer_proc import ORSIsochronesLayerAlgo
from .isochrones_point_proc import ORSIsochronesPointAlgo
from .matrix_proc import ORSMatrixAlgo
from .snap_layer_proc import ORSSnapLayerAlgo
from .snap_point_proc import ORSSnapPointAlgo


class ORStoolsProvider(QgsProcessingProvider):
Expand All @@ -63,6 +65,8 @@ def loadAlgorithms(self) -> None:
self.addAlgorithm(ORSIsochronesLayerAlgo())
self.addAlgorithm(ORSIsochronesPointAlgo())
self.addAlgorithm(ORSMatrixAlgo())
self.addAlgorithm(ORSSnapLayerAlgo())
self.addAlgorithm(ORSSnapPointAlgo())

@staticmethod
def icon() -> QIcon:
Expand Down
130 changes: 130 additions & 0 deletions ORStools/proc/snap_layer_proc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
ORStools
A QGIS plugin
QGIS client to query openrouteservice
-------------------
begin : 2017-02-01
git sha : $Format:%H$
copyright : (C) 2021 by HeiGIT gGmbH
email : [email protected]
***************************************************************************/

This plugin provides access to openrouteservice API functionalities
(https://openrouteservice.org), developed and
maintained by the openrouteservice team of HeiGIT gGmbH, Germany. By using
this plugin you agree to the ORS terms of service
(https://openrouteservice.org/terms-of-service/).

/***************************************************************************
* *
* 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""

from typing import Dict

from qgis.PyQt.QtCore import QVariant
from qgis.core import (
QgsProcessingParameterFeatureSource,
QgsProcessing,
QgsProcessingParameterNumber,
QgsProcessingContext,
QgsProcessingFeedback,
QgsWkbTypes,
QgsFields,
QgsCoordinateReferenceSystem,
QgsField,
)

from ORStools.common import PROFILES
from ORStools.common.snap_core import get_snapped_point_features
from ORStools.proc.base_processing_algorithm import ORSBaseProcessingAlgorithm
from ORStools.utils import exceptions, logger, transform


# noinspection PyPep8Naming
class ORSSnapLayerAlgo(ORSBaseProcessingAlgorithm):
def __init__(self) -> None:
super().__init__()
self.ALGO_NAME: str = "snap_from_point_layer"
self.GROUP: str = "Snap"
self.IN_POINTS: str = "IN_POINTS"
self.RADIUS: str = "RADIUS"
self.PARAMETERS: list = [
QgsProcessingParameterFeatureSource(
name=self.IN_POINTS,
description=self.tr("Input Point Layer"),
types=[QgsProcessing.SourceType.TypeVectorPoint],
),
QgsProcessingParameterNumber(
name=self.RADIUS,
description=self.tr("Search Radius [m]"),
defaultValue=300,
),
]

def processAlgorithm(
self, parameters: dict, context: QgsProcessingContext, feedback: QgsProcessingFeedback
) -> Dict[str, str]:
ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)

# Get profile value
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]

# Get parameter values
source = self.parameterAsSource(parameters, self.IN_POINTS, context)
radius = self.parameterAsDouble(parameters, self.RADIUS, context)

sources_features = list(source.getFeatures())

x_former = transform.transformToWGS(source.sourceCrs())
sources_features_x_formed = [
x_former.transform(feat.geometry().asPoint()) for feat in sources_features
]

params = {
"locations": [[point.x(), point.y()] for point in sources_features_x_formed],
"radius": radius,
"id": None,
}

sink_fields = QgsFields()
sink_fields.append(QgsField("NAME", QVariant.String))
sink_fields.append(QgsField("SNAPPED_DISTANCE", QVariant.Double))

(sink, dest_id) = self.parameterAsSink(
parameters,
self.OUT,
context,
sink_fields,
QgsWkbTypes.Type.Point,
QgsCoordinateReferenceSystem.fromEpsgId(4326),
)

# Make request and catch ApiError
try:
response = ors_client.request("/v2/snap/" + profile, {}, post_json=params)
point_features = get_snapped_point_features(response)

for feat in point_features:
sink.addFeature(feat)

except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e:
msg = f"{e.__class__.__name__}: {str(e)}"
feedback.reportError(msg)
logger.log(msg)

return {self.OUT: dest_id}

def displayName(self) -> str:
"""
Algorithm name shown in QGIS toolbox
:return:
"""
return self.tr("Snap from Point Layer")
Loading
Loading