From 6c628a433390ad4680333c845643d3895c0c843c Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 09:55:11 +0100 Subject: [PATCH 01/29] feat: add default profiles --- ORStools/proc/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ORStools/proc/__init__.py b/ORStools/proc/__init__.py index 87e79c3f..bedce881 100644 --- a/ORStools/proc/__init__.py +++ b/ORStools/proc/__init__.py @@ -36,6 +36,19 @@ "export": "export", } +PROFILES = [ + "driving-car", + "driving-hgv", + "cycling-regular", + "cycling-mountain", + "cycling-road", + "cycling-electric", + "foot-walking", + "foot-hiking", + "wheelchair", + # "public-transport" +] + DEFAULT_SETTINGS = { "providers": [ { From 27664153db3933f5b0ca4179097597232a36a104 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 09:55:54 +0100 Subject: [PATCH 02/29] feat: add box for profiles to settings and connect it --- ORStools/gui/ORStoolsDialogConfig.py | 37 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 8dfe38dd..71cea03b 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -41,6 +41,9 @@ from ORStools.utils import configmanager, gui from ..proc import ENDPOINTS, DEFAULT_SETTINGS +from ORStools.utils import configmanager +from .ORStoolsDialogConfigUI import Ui_ORStoolsDialogConfigBase +from ..proc import ENDPOINTS, DEFAULT_SETTINGS, PROFILES CONFIG_WIDGET, _ = uic.loadUiType(gui.GuiUtils.get_ui_file_path("ORStoolsDialogConfigUI.ui")) @@ -74,7 +77,7 @@ def accept(self) -> None: collapsible_boxes = self.providers.findChildren(QgsCollapsibleGroupBox) collapsible_boxes = [ - i for i in collapsible_boxes if "_provider_endpoints" not in i.objectName() + i for i in collapsible_boxes if "_provider_endpoints" not in i.objectName() and "_provider_profiles" not in i.objectName() ] for idx, box in enumerate(collapsible_boxes): current_provider = self.temp_config["providers"][idx] @@ -117,6 +120,11 @@ def accept(self) -> None: QtWidgets.QLineEdit, box.title() + "_snapping_endpoint" ).text(), } + provider_box = box.findChild( + QgsCollapsibleGroupBox, f"{box.title()}_provider_profiles" + ) + + current_provider["profiles"] = [profile.text() for profile in provider_box.findChildren(QLineEdit)] configmanager.write_config(self.temp_config) self.close() @@ -148,6 +156,7 @@ def _build_ui(self) -> None: provider_entry["key"], provider_entry["timeout"], provider_entry["endpoints"], + provider_entry["profiles"], new=False, ) @@ -167,7 +176,7 @@ def _add_provider(self) -> None: self, self.tr("New ORS provider"), self.tr("Enter a name for the provider") ) if ok: - self._add_box(provider_name, "http://localhost:8082/ors", "", 60, ENDPOINTS, new=True) + self._add_box(provider_name, "http://localhost:8082/ors", "", 60, ENDPOINTS, PROFILES, new=True) def _remove_provider(self) -> None: """Remove list of providers from list.""" @@ -198,7 +207,7 @@ def _collapse_boxes(self) -> None: box.setCollapsed(True) def _add_box( - self, name: str, url: str, key: str, timeout: int, endpoints: dict, new: bool = False + self, name: str, url: str, key: str, timeout: int, endpoints: dict, profiles: dict, new: bool = False ) -> None: """ Adds a provider box to the QWidget layout and self.temp_config. @@ -269,6 +278,28 @@ def _add_box( row += 1 + # Profile Section + profile_box = QgsCollapsibleGroupBox(provider) + profile_box.setObjectName(name + "_provider_profiles") + profile_box.setTitle(self.tr("Custom profiles")) + profile_layout = QtWidgets.QGridLayout(profile_box) + gridLayout_3.addWidget(profile_box, 7, 0, 1, 4) + + row = 0 + for profile_name in profiles: + profile_label = QtWidgets.QLabel(profile_box) + profile_label.setText(self.tr(profile_name.capitalize())) + profile_layout.addWidget(profile_label, row, 0, 1, 1) + + profile_lineedit = QtWidgets.QLineEdit(profile_box) + profile_lineedit.setText(profile_name) + profile_lineedit.setObjectName(f"{name}_{profile_name}_lineedit") + + profile_layout.addWidget(profile_lineedit, row, 1, 1, 3) + + row += 1 + + # Add reset buttons at the bottom button_layout = QtWidgets.QHBoxLayout() From 77dc892653afb830f146052622d05a06c5d1f6d4 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 09:56:11 +0100 Subject: [PATCH 03/29] feat: ensure backward compatibility --- ORStools/ORStoolsPlugin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py index 2156bf8a..88c72f44 100644 --- a/ORStools/ORStoolsPlugin.py +++ b/ORStools/ORStoolsPlugin.py @@ -33,7 +33,7 @@ import os.path from .gui import ORStoolsDialog -from .proc import provider, ENDPOINTS, DEFAULT_SETTINGS +from .proc import provider, ENDPOINTS, DEFAULT_SETTINGS, PROFILES class ORStools: @@ -101,6 +101,8 @@ def add_default_provider_to_settings(self): # Add here, like the endpoints prov["endpoints"] = ENDPOINTS settings["providers"][i] = prov + prov["profiles"] = PROFILES + settings["providers"][i] = prov if changed: s.setValue("ORStools/config", settings) else: From f994984d4f1cf77b53085f1a01a7d295d93a71ab Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 09:59:40 +0100 Subject: [PATCH 04/29] feat: make reset work with profiles --- ORStools/gui/ORStoolsDialogConfig.py | 29 ++++++++++++++++++++++++++++ ORStools/proc/__init__.py | 1 + 2 files changed, 30 insertions(+) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 71cea03b..fb2954b8 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -36,6 +36,7 @@ QInputDialog, QLineEdit, QDialogButtonBox, + QMessageBox ) from qgis.PyQt.QtGui import QIntValidator @@ -206,6 +207,34 @@ def _collapse_boxes(self) -> None: for box in collapsible_boxes: box.setCollapsed(True) + def _reset_all_providers(self) -> None: + """Reset all providers.""" + + msg_box = QMessageBox() + msg_box.setIcon(QMessageBox.Warning) + msg_box.setWindowTitle("Confirm Reset") + msg_box.setText( + "Are you sure you want to delete all providers? This action cannot be undone." + ) + msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + + result = msg_box.exec() + if result == QMessageBox.Yes: + for box_remove in self.providers.findChildren(QWidget): + if box_remove.objectName() in ["_provider_endpoints", "_provider_profiles"]: + continue + self.verticalLayout.removeWidget(box_remove) + box_remove.setParent(None) + box_remove.deleteLater() + + configmanager.write_config(DEFAULT_SETTINGS) + + self.temp_config = configmanager.read_config() + self._build_ui() + + else: + pass + def _add_box( self, name: str, url: str, key: str, timeout: int, endpoints: dict, profiles: dict, new: bool = False ) -> None: diff --git a/ORStools/proc/__init__.py b/ORStools/proc/__init__.py index bedce881..139cf42c 100644 --- a/ORStools/proc/__init__.py +++ b/ORStools/proc/__init__.py @@ -61,6 +61,7 @@ "name": "openrouteservice", "timeout": 60, "endpoints": ENDPOINTS, + "profiles": PROFILES, } ] } From f06b6078c6cc712e1311cbbeeedec5c3837b8913 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 10:06:43 +0100 Subject: [PATCH 05/29] feat: ensure newly created config will be set up right --- ORStools/gui/ORStoolsDialogConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index fb2954b8..5b0da72a 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -243,7 +243,7 @@ def _add_box( """ if new: self.temp_config["providers"].append( - dict(name=name, base_url=url, key=key, timeout=timeout, endpoints=endpoints) + dict(name=name, base_url=url, key=key, timeout=timeout, endpoints=endpoints, profiles=profiles) ) provider = QgsCollapsibleGroupBox(self.providers) From 359b0085cb99ce0edeec5a0f366accedb2dd3385 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 11:15:08 +0100 Subject: [PATCH 06/29] refactor: rework ui to use QListWidget --- ORStools/gui/ORStoolsDialogConfig.py | 34 ++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 5b0da72a..a4d3af7e 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -26,7 +26,6 @@ * * ***************************************************************************/ """ - from qgis.gui import QgsCollapsibleGroupBox from qgis.PyQt import QtWidgets, uic @@ -36,7 +35,12 @@ QInputDialog, QLineEdit, QDialogButtonBox, - QMessageBox + QMessageBox, + QWidget, + QListWidget, + QVBoxLayout, + QHBoxLayout, + QPushButton, ) from qgis.PyQt.QtGui import QIntValidator @@ -310,8 +314,30 @@ def _add_box( # Profile Section profile_box = QgsCollapsibleGroupBox(provider) profile_box.setObjectName(name + "_provider_profiles") - profile_box.setTitle(self.tr("Custom profiles")) - profile_layout = QtWidgets.QGridLayout(profile_box) + profile_box.setTitle(self.tr("Profiles")) + profile_layout = QHBoxLayout(profile_box) + + self.list_widget = QListWidget(profile_box) + profile_layout.addWidget(self.list_widget) + + button_layout = QVBoxLayout() + add_profile_button = QPushButton(self.tr("+"), profile_box) + remove_profile_button = QPushButton(self.tr("-"), profile_box) + load_profiles_button = QPushButton(self.tr("Load profiles"), profile_box) + + add_profile_button.clicked.connect( + self.add_profile_button_clicked + ) + remove_profile_button.clicked.connect( + self.remove_profile_button_clicked + ) + + button_layout.addWidget(add_profile_button) + button_layout.addWidget(remove_profile_button) + button_layout.addWidget(load_profiles_button) + + profile_layout.addLayout(button_layout) + gridLayout_3.addWidget(profile_box, 7, 0, 1, 4) row = 0 From 29117164215d2b769491fd77606b31215a4d0743 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 11:15:40 +0100 Subject: [PATCH 07/29] feat: add functionality to use add profile button --- ORStools/gui/ORStoolsDialogConfig.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index a4d3af7e..843d792e 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -26,7 +26,7 @@ * * ***************************************************************************/ """ -from qgis.gui import QgsCollapsibleGroupBox +from qgis.gui import QgsCollapsibleGroupBox, QgsNewNameDialog from qgis.PyQt import QtWidgets, uic from qgis.PyQt.QtCore import QMetaObject @@ -373,6 +373,14 @@ def _add_box( gridLayout_3.addLayout(button_layout, 7, 0, 1, 4) self.verticalLayout.addWidget(provider) + provider.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + + def add_profile_button_clicked(self): + dlg = QgsNewNameDialog("Enter profile name", "New Profile") + if dlg.exec_(): + profile_name = dlg.name() + if profile_name: + self.list_widget.addItem(profile_name) def _reset_endpoints(self) -> None: """Resets the endpoints to their original values.""" From e68c24ef8646dd5a729e534e80aa8a8e82d4722e Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 11:15:56 +0100 Subject: [PATCH 08/29] feat: add functionality to use remove profile button --- ORStools/gui/ORStoolsDialogConfig.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 843d792e..ffcb6767 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -390,3 +390,7 @@ def _reset_endpoints(self) -> None: endpoint_name = name.split("_")[1] endpoint_value = ENDPOINTS[endpoint_name] line_edit_remove.setText(endpoint_value) + + def remove_profile_button_clicked(self): + for item in self.list_widget.selectedItems(): + self.list_widget.takeItem(self.list_widget.row(item)) From afa478372a20443c6b060bfd66c518f478c6e2e1 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 13:25:54 +0100 Subject: [PATCH 09/29] refactor: rename profile list widget --- ORStools/gui/ORStoolsDialogConfig.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index ffcb6767..63e9f3f8 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -317,8 +317,8 @@ def _add_box( profile_box.setTitle(self.tr("Profiles")) profile_layout = QHBoxLayout(profile_box) - self.list_widget = QListWidget(profile_box) - profile_layout.addWidget(self.list_widget) + self.list_widget_profiles = QListWidget(profile_box) + profile_layout.addWidget(self.list_widget_profiles) button_layout = QVBoxLayout() add_profile_button = QPushButton(self.tr("+"), profile_box) @@ -380,7 +380,7 @@ def add_profile_button_clicked(self): if dlg.exec_(): profile_name = dlg.name() if profile_name: - self.list_widget.addItem(profile_name) + self.list_widget_profiles.addItem(profile_name) def _reset_endpoints(self) -> None: """Resets the endpoints to their original values.""" @@ -392,5 +392,5 @@ def _reset_endpoints(self) -> None: line_edit_remove.setText(endpoint_value) def remove_profile_button_clicked(self): - for item in self.list_widget.selectedItems(): - self.list_widget.takeItem(self.list_widget.row(item)) + for item in self.list_widget_profiles.selectedItems(): + self.list_widget_profiles.takeItem(self.list_widget_profiles.row(item)) From 1407f173be49fe12176c3b27b575755db9cebc9d Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 13:41:29 +0100 Subject: [PATCH 10/29] fix: modify the right listwidget --- ORStools/gui/ORStoolsDialogConfig.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 63e9f3f8..9055ac2f 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -317,8 +317,8 @@ def _add_box( profile_box.setTitle(self.tr("Profiles")) profile_layout = QHBoxLayout(profile_box) - self.list_widget_profiles = QListWidget(profile_box) - profile_layout.addWidget(self.list_widget_profiles) + list_widget_profiles = QListWidget(profile_box) + profile_layout.addWidget(list_widget_profiles) button_layout = QVBoxLayout() add_profile_button = QPushButton(self.tr("+"), profile_box) @@ -326,10 +326,10 @@ def _add_box( load_profiles_button = QPushButton(self.tr("Load profiles"), profile_box) add_profile_button.clicked.connect( - self.add_profile_button_clicked + lambda: self.add_profile_button_clicked(add_profile_button) ) remove_profile_button.clicked.connect( - self.remove_profile_button_clicked + lambda: self.remove_profile_button_clicked(add_profile_button) ) button_layout.addWidget(add_profile_button) @@ -375,12 +375,18 @@ def _add_box( self.verticalLayout.addWidget(provider) provider.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) - def add_profile_button_clicked(self): + def add_profile_button_clicked(self, button: QPushButton) -> None: dlg = QgsNewNameDialog("Enter profile name", "New Profile") + list_widget = button.parent().findChild(QListWidget) if dlg.exec_(): profile_name = dlg.name() if profile_name: - self.list_widget_profiles.addItem(profile_name) + list_widget.addItem(profile_name) + + def remove_profile_button_clicked(self, button: QPushButton) -> None: + list_widget = button.parent().findChild(QListWidget) + for item in list_widget.selectedItems(): + list_widget.takeItem(list_widget.row(item)) def _reset_endpoints(self) -> None: """Resets the endpoints to their original values.""" From f297e329259013d196454e5ca31ee76044bf026f Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 14:24:11 +0100 Subject: [PATCH 11/29] feat: add load button to get profiles from status endpoint --- ORStools/gui/ORStoolsDialogConfig.py | 40 +++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 9055ac2f..7eb53f6b 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -26,6 +26,11 @@ * * ***************************************************************************/ """ +import json + +from PyQt5.QtCore import QUrl +from PyQt5.QtNetwork import QNetworkRequest +from qgis._core import QgsBlockingNetworkRequest from qgis.gui import QgsCollapsibleGroupBox, QgsNewNameDialog from qgis.PyQt import QtWidgets, uic @@ -329,7 +334,10 @@ def _add_box( lambda: self.add_profile_button_clicked(add_profile_button) ) remove_profile_button.clicked.connect( - lambda: self.remove_profile_button_clicked(add_profile_button) + lambda: self.remove_profile_button_clicked(remove_profile_button) + ) + load_profiles_button.clicked.connect( + lambda: self.load_profiles_button_clicked(load_profiles_button) ) button_layout.addWidget(add_profile_button) @@ -385,8 +393,34 @@ def add_profile_button_clicked(self, button: QPushButton) -> None: def remove_profile_button_clicked(self, button: QPushButton) -> None: list_widget = button.parent().findChild(QListWidget) - for item in list_widget.selectedItems(): - list_widget.takeItem(list_widget.row(item)) + selected = list_widget.selectedItems() + if selected: + for item in selected: + list_widget.takeItem(list_widget.row(item)) + else: + list_widget.takeItem(0) + + + def load_profiles_button_clicked(self, button: QPushButton) -> None: + list_widget = button.parent().findChild(QListWidget) + grand_parent = button.parent().parent() + base_url = None + for child in grand_parent.findChildren(QLineEdit): + if "_base_url_text" in child.objectName(): + base_url = child.text() + + url = f"{base_url}/v2/status" + request = QgsBlockingNetworkRequest() + error_code = request.get(QNetworkRequest(QUrl(url))) + + if error_code == QgsBlockingNetworkRequest.ErrorCode.NoError: + reply = request.reply() + content = json.loads(reply.content().data().decode('utf-8')) + list_widget.addItems( + [i for i in content["profiles"].keys()] + ) + else: + QMessageBox.warning(self, "Unable to load profiles", "There was an error loading the profiles.") def _reset_endpoints(self) -> None: """Resets the endpoints to their original values.""" From 9fb6613019a0d58bb3d4b09eae267cb0fa9d7b5f Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 14:49:53 +0100 Subject: [PATCH 12/29] feat: save state and add reset to defaults button --- ORStools/gui/ORStoolsDialogConfig.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index 7eb53f6b..e0e52630 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -130,15 +130,17 @@ def accept(self) -> None: QtWidgets.QLineEdit, box.title() + "_snapping_endpoint" ).text(), } - provider_box = box.findChild( + profile_box = box.findChild( QgsCollapsibleGroupBox, f"{box.title()}_provider_profiles" ) - current_provider["profiles"] = [profile.text() for profile in provider_box.findChildren(QLineEdit)] + list_widget = profile_box.findChild(QListWidget) + current_provider["profiles"] = [list_widget.item(i).text() for i in range(list_widget.count())] configmanager.write_config(self.temp_config) self.close() + @staticmethod def _adjust_timeout_input(input_line_edit: QLineEdit) -> None: """ @@ -323,12 +325,14 @@ def _add_box( profile_layout = QHBoxLayout(profile_box) list_widget_profiles = QListWidget(profile_box) + list_widget_profiles.addItems(profiles) profile_layout.addWidget(list_widget_profiles) button_layout = QVBoxLayout() add_profile_button = QPushButton(self.tr("+"), profile_box) remove_profile_button = QPushButton(self.tr("-"), profile_box) load_profiles_button = QPushButton(self.tr("Load profiles"), profile_box) + restore_defaults_button = QPushButton(self.tr("Restore defaults"), profile_box) add_profile_button.clicked.connect( lambda: self.add_profile_button_clicked(add_profile_button) @@ -339,10 +343,14 @@ def _add_box( load_profiles_button.clicked.connect( lambda: self.load_profiles_button_clicked(load_profiles_button) ) + restore_defaults_button.clicked.connect( + lambda: self.restore_defaults_button_clicked(restore_defaults_button) + ) button_layout.addWidget(add_profile_button) button_layout.addWidget(remove_profile_button) button_layout.addWidget(load_profiles_button) + button_layout.addWidget(restore_defaults_button) profile_layout.addLayout(button_layout) @@ -422,6 +430,11 @@ def load_profiles_button_clicked(self, button: QPushButton) -> None: else: QMessageBox.warning(self, "Unable to load profiles", "There was an error loading the profiles.") + def restore_defaults_button_clicked(self, button: QPushButton) -> None: + list_widget = button.parent().findChild(QListWidget) + list_widget.clear() + list_widget.addItems(PROFILES) + def _reset_endpoints(self) -> None: """Resets the endpoints to their original values.""" for line_edit_remove in self.providers.findChildren(QLineEdit): From b6dcf9e85395b9d632de8271dcd304e9ec8218e2 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 14:52:28 +0100 Subject: [PATCH 13/29] feat: import profiles from existing source --- ORStools/gui/ORStoolsDialogConfig.py | 3 ++- ORStools/proc/__init__.py | 14 +------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index e0e52630..b8e8d95a 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -53,7 +53,8 @@ from ..proc import ENDPOINTS, DEFAULT_SETTINGS from ORStools.utils import configmanager from .ORStoolsDialogConfigUI import Ui_ORStoolsDialogConfigBase -from ..proc import ENDPOINTS, DEFAULT_SETTINGS, PROFILES +from ..common import PROFILES +from ..proc import ENDPOINTS, DEFAULT_SETTINGS CONFIG_WIDGET, _ = uic.loadUiType(gui.GuiUtils.get_ui_file_path("ORStoolsDialogConfigUI.ui")) diff --git a/ORStools/proc/__init__.py b/ORStools/proc/__init__.py index 139cf42c..fd348d39 100644 --- a/ORStools/proc/__init__.py +++ b/ORStools/proc/__init__.py @@ -26,6 +26,7 @@ * * ***************************************************************************/ """ +from ORStools.common import PROFILES ENDPOINTS = { "directions": "directions", @@ -36,19 +37,6 @@ "export": "export", } -PROFILES = [ - "driving-car", - "driving-hgv", - "cycling-regular", - "cycling-mountain", - "cycling-road", - "cycling-electric", - "foot-walking", - "foot-hiking", - "wheelchair", - # "public-transport" -] - DEFAULT_SETTINGS = { "providers": [ { From 4b9e5849fca3e84c771f2c5ecbc399a85cef38c4 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 16:35:50 +0100 Subject: [PATCH 14/29] feat: add profiles key to settings key --- ORStools/ORStoolsPlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py index 88c72f44..557ad08c 100644 --- a/ORStools/ORStoolsPlugin.py +++ b/ORStools/ORStoolsPlugin.py @@ -90,7 +90,7 @@ def add_default_provider_to_settings(self): s = QgsSettings() settings = s.value("ORStools/config") - settings_keys = ["ENV_VARS", "base_url", "key", "name", "endpoints"] + settings_keys = ["ENV_VARS", "base_url", "key", "name", "endpoints", "profiles"] # Add any new settings here for backwards compatibility if settings: From fa49b916228d93cd115e3e8a3dc9fe596c3d5afb Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 16:46:37 +0100 Subject: [PATCH 15/29] feat: set custom profiles in base processing alg --- ORStools/proc/base_processing_algorithm.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ORStools/proc/base_processing_algorithm.py b/ORStools/proc/base_processing_algorithm.py index 3f5751d4..93d840e0 100644 --- a/ORStools/proc/base_processing_algorithm.py +++ b/ORStools/proc/base_processing_algorithm.py @@ -132,11 +132,13 @@ def profile_parameter(self) -> QgsProcessingParameterEnum: """ Parameter definition for profile, used in all child classes """ + profiles_list = [provider["profiles"] for provider in configmanager.read_config()["providers"]] + profiles = list(set(element for sublist in profiles_list for element in sublist)) return QgsProcessingParameterEnum( self.IN_PROFILE, self.tr("Travel mode", "ORSBaseProcessingAlgorithm"), - PROFILES, - defaultValue=PROFILES[0], + profiles, + defaultValue=profiles[0], ) def output_parameter(self) -> QgsProcessingParameterFeatureSink: From 77e16576c95b3418eea4e245c37ca831851ab8a0 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 16:47:08 +0100 Subject: [PATCH 16/29] style: run ruff --- ORStools/gui/ORStoolsDialogConfig.py | 48 +++++++++++++++------- ORStools/proc/__init__.py | 1 + ORStools/proc/base_processing_algorithm.py | 6 ++- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index b8e8d95a..a079721c 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -26,6 +26,7 @@ * * ***************************************************************************/ """ + import json from PyQt5.QtCore import QUrl @@ -88,7 +89,10 @@ def accept(self) -> None: collapsible_boxes = self.providers.findChildren(QgsCollapsibleGroupBox) collapsible_boxes = [ - i for i in collapsible_boxes if "_provider_endpoints" not in i.objectName() and "_provider_profiles" not in i.objectName() + i + for i in collapsible_boxes + if "_provider_endpoints" not in i.objectName() + and "_provider_profiles" not in i.objectName() ] for idx, box in enumerate(collapsible_boxes): current_provider = self.temp_config["providers"][idx] @@ -131,17 +135,16 @@ def accept(self) -> None: QtWidgets.QLineEdit, box.title() + "_snapping_endpoint" ).text(), } - profile_box = box.findChild( - QgsCollapsibleGroupBox, f"{box.title()}_provider_profiles" - ) + profile_box = box.findChild(QgsCollapsibleGroupBox, f"{box.title()}_provider_profiles") list_widget = profile_box.findChild(QListWidget) - current_provider["profiles"] = [list_widget.item(i).text() for i in range(list_widget.count())] + current_provider["profiles"] = [ + list_widget.item(i).text() for i in range(list_widget.count()) + ] configmanager.write_config(self.temp_config) self.close() - @staticmethod def _adjust_timeout_input(input_line_edit: QLineEdit) -> None: """ @@ -189,7 +192,9 @@ def _add_provider(self) -> None: self, self.tr("New ORS provider"), self.tr("Enter a name for the provider") ) if ok: - self._add_box(provider_name, "http://localhost:8082/ors", "", 60, ENDPOINTS, PROFILES, new=True) + self._add_box( + provider_name, "http://localhost:8082/ors", "", 60, ENDPOINTS, PROFILES, new=True + ) def _remove_provider(self) -> None: """Remove list of providers from list.""" @@ -248,14 +253,28 @@ def _reset_all_providers(self) -> None: pass def _add_box( - self, name: str, url: str, key: str, timeout: int, endpoints: dict, profiles: dict, new: bool = False + self, + name: str, + url: str, + key: str, + timeout: int, + endpoints: dict, + profiles: dict, + new: bool = False, ) -> None: """ Adds a provider box to the QWidget layout and self.temp_config. """ if new: self.temp_config["providers"].append( - dict(name=name, base_url=url, key=key, timeout=timeout, endpoints=endpoints, profiles=profiles) + dict( + name=name, + base_url=url, + key=key, + timeout=timeout, + endpoints=endpoints, + profiles=profiles, + ) ) provider = QgsCollapsibleGroupBox(self.providers) @@ -409,7 +428,6 @@ def remove_profile_button_clicked(self, button: QPushButton) -> None: else: list_widget.takeItem(0) - def load_profiles_button_clicked(self, button: QPushButton) -> None: list_widget = button.parent().findChild(QListWidget) grand_parent = button.parent().parent() @@ -424,12 +442,12 @@ def load_profiles_button_clicked(self, button: QPushButton) -> None: if error_code == QgsBlockingNetworkRequest.ErrorCode.NoError: reply = request.reply() - content = json.loads(reply.content().data().decode('utf-8')) - list_widget.addItems( - [i for i in content["profiles"].keys()] - ) + content = json.loads(reply.content().data().decode("utf-8")) + list_widget.addItems([i for i in content["profiles"].keys()]) else: - QMessageBox.warning(self, "Unable to load profiles", "There was an error loading the profiles.") + QMessageBox.warning( + self, "Unable to load profiles", "There was an error loading the profiles." + ) def restore_defaults_button_clicked(self, button: QPushButton) -> None: list_widget = button.parent().findChild(QListWidget) diff --git a/ORStools/proc/__init__.py b/ORStools/proc/__init__.py index fd348d39..728aa49c 100644 --- a/ORStools/proc/__init__.py +++ b/ORStools/proc/__init__.py @@ -26,6 +26,7 @@ * * ***************************************************************************/ """ + from ORStools.common import PROFILES ENDPOINTS = { diff --git a/ORStools/proc/base_processing_algorithm.py b/ORStools/proc/base_processing_algorithm.py index 93d840e0..02f3cab9 100644 --- a/ORStools/proc/base_processing_algorithm.py +++ b/ORStools/proc/base_processing_algorithm.py @@ -48,7 +48,7 @@ from ORStools import RESOURCE_PREFIX, __help__ from ORStools.utils import configmanager -from ..common import client, PROFILES, AVOID_BORDERS, AVOID_FEATURES, ADVANCED_PARAMETERS +from ..common import client, AVOID_BORDERS, AVOID_FEATURES, ADVANCED_PARAMETERS from ..utils.processing import read_help_file from ..gui.directions_gui import _get_avoid_polygons @@ -132,7 +132,9 @@ def profile_parameter(self) -> QgsProcessingParameterEnum: """ Parameter definition for profile, used in all child classes """ - profiles_list = [provider["profiles"] for provider in configmanager.read_config()["providers"]] + profiles_list = [ + provider["profiles"] for provider in configmanager.read_config()["providers"] + ] profiles = list(set(element for sublist in profiles_list for element in sublist)) return QgsProcessingParameterEnum( self.IN_PROFILE, From e7ecf54c9ca1baf754b33ae31b655a888ac3b8e8 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 6 Mar 2025 16:50:54 +0100 Subject: [PATCH 17/29] refactor: make profiles and providers member of base class --- ORStools/proc/base_processing_algorithm.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ORStools/proc/base_processing_algorithm.py b/ORStools/proc/base_processing_algorithm.py index 02f3cab9..2aab9cd4 100644 --- a/ORStools/proc/base_processing_algorithm.py +++ b/ORStools/proc/base_processing_algorithm.py @@ -74,6 +74,12 @@ def __init__(self) -> None: self.OUT_NAME = "ORSTOOLS_OUTPUT" self.PARAMETERS = None + self.providers = configmanager.read_config()["providers"] + profiles_list = [ + provider["profiles"] for provider in self.providers + ] + self.profiles = list(set(element for sublist in profiles_list for element in sublist)) + def createInstance(self) -> Any: """ Returns instance of any child class @@ -120,7 +126,7 @@ def provider_parameter(self) -> QgsProcessingParameterEnum: """ Parameter definition for provider, used in all child classes """ - providers = [provider["name"] for provider in configmanager.read_config()["providers"]] + providers = [provider["name"] for provider in self.providers] return QgsProcessingParameterEnum( self.IN_PROVIDER, self.tr("Provider", "ORSBaseProcessingAlgorithm"), @@ -132,15 +138,12 @@ def profile_parameter(self) -> QgsProcessingParameterEnum: """ Parameter definition for profile, used in all child classes """ - profiles_list = [ - provider["profiles"] for provider in configmanager.read_config()["providers"] - ] - profiles = list(set(element for sublist in profiles_list for element in sublist)) + return QgsProcessingParameterEnum( self.IN_PROFILE, self.tr("Travel mode", "ORSBaseProcessingAlgorithm"), - profiles, - defaultValue=profiles[0], + self.profiles, + defaultValue=self.profiles[0], ) def output_parameter(self) -> QgsProcessingParameterFeatureSink: From 42e448328281833513bfa8dbb7e6044e4cf96440 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Mon, 19 May 2025 10:41:52 +0200 Subject: [PATCH 18/29] fix: buttons wrongly positioned --- ORStools/gui/ORStoolsDialogConfig.py | 34 ++++++---------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index a079721c..afd23fdc 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -52,10 +52,7 @@ from ORStools.utils import configmanager, gui from ..proc import ENDPOINTS, DEFAULT_SETTINGS -from ORStools.utils import configmanager -from .ORStoolsDialogConfigUI import Ui_ORStoolsDialogConfigBase from ..common import PROFILES -from ..proc import ENDPOINTS, DEFAULT_SETTINGS CONFIG_WIDGET, _ = uic.loadUiType(gui.GuiUtils.get_ui_file_path("ORStoolsDialogConfigUI.ui")) @@ -335,9 +332,13 @@ def _add_box( endpoint_lineedit.setObjectName(f"{name}_{endpoint_name}_endpoint") endpoint_layout.addWidget(endpoint_lineedit, row, 1, 1, 3) - row += 1 + reset_endpoints_button = QtWidgets.QPushButton(self.tr("Reset Endpoints"), provider) + reset_endpoints_button.setObjectName(name + "_reset_endpoints_button") + reset_endpoints_button.clicked.connect(self._reset_endpoints) + endpoint_layout.addWidget(reset_endpoints_button) + # Profile Section profile_box = QgsCollapsibleGroupBox(provider) profile_box.setObjectName(name + "_provider_profiles") @@ -376,22 +377,7 @@ def _add_box( gridLayout_3.addWidget(profile_box, 7, 0, 1, 4) - row = 0 - for profile_name in profiles: - profile_label = QtWidgets.QLabel(profile_box) - profile_label.setText(self.tr(profile_name.capitalize())) - profile_layout.addWidget(profile_label, row, 0, 1, 1) - - profile_lineedit = QtWidgets.QLineEdit(profile_box) - profile_lineedit.setText(profile_name) - profile_lineedit.setObjectName(f"{name}_{profile_name}_lineedit") - - profile_layout.addWidget(profile_lineedit, row, 1, 1, 3) - - row += 1 - - - # Add reset buttons at the bottom + # 6. Reset buttons section button_layout = QtWidgets.QHBoxLayout() reset_url_button = QtWidgets.QPushButton(self.tr("Reset URL"), provider) @@ -401,15 +387,9 @@ def _add_box( ) button_layout.addWidget(reset_url_button) - reset_endpoints_button = QtWidgets.QPushButton(self.tr("Reset Endpoints"), provider) - reset_endpoints_button.setObjectName(name + "_reset_endpoints_button") - reset_endpoints_button.clicked.connect(self._reset_endpoints) - button_layout.addWidget(reset_endpoints_button) - - gridLayout_3.addLayout(button_layout, 7, 0, 1, 4) + gridLayout_3.addLayout(button_layout, 8, 0, 1, 4) # (8, 0–3) self.verticalLayout.addWidget(provider) - provider.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) def add_profile_button_clicked(self, button: QPushButton) -> None: dlg = QgsNewNameDialog("Enter profile name", "New Profile") From 66f8a24e6b8c0cbdbcd08acc5754bc165ce53a63 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Mon, 19 May 2025 10:44:35 +0200 Subject: [PATCH 19/29] refactor: remove unused duplicate method --- ORStools/gui/ORStoolsDialogConfig.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index afd23fdc..b82623a9 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -442,7 +442,3 @@ def _reset_endpoints(self) -> None: endpoint_name = name.split("_")[1] endpoint_value = ENDPOINTS[endpoint_name] line_edit_remove.setText(endpoint_value) - - def remove_profile_button_clicked(self): - for item in self.list_widget_profiles.selectedItems(): - self.list_widget_profiles.takeItem(self.list_widget_profiles.row(item)) From bb84d2878bdc42661a2e331b2b793ecd916ae390 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Mon, 19 May 2025 10:58:18 +0200 Subject: [PATCH 20/29] feat: show warning when trying to query status endpoint of live API --- ORStools/gui/ORStoolsDialogConfig.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index b82623a9..e5b89532 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -417,7 +417,13 @@ def load_profiles_button_clicked(self, button: QPushButton) -> None: base_url = child.text() url = f"{base_url}/v2/status" + + if "api.openrouteservice.org" in url: + QMessageBox.warning(self, "Load profiles not possible", "Load profiles not possible, please use 'Restore Defaults' for the openrouteservice live API", ) + return + request = QgsBlockingNetworkRequest() + print(url) error_code = request.get(QNetworkRequest(QUrl(url))) if error_code == QgsBlockingNetworkRequest.ErrorCode.NoError: From 41cb3fb1852d1eb9d67d23259334b27c117071f5 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Tue, 24 Jun 2025 10:50:13 +0200 Subject: [PATCH 21/29] feat: implement profile refresh functionality for routing travel combo box --- ORStools/gui/ORStoolsDialog.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ORStools/gui/ORStoolsDialog.py b/ORStools/gui/ORStoolsDialog.py index 3d651098..65c7d49a 100644 --- a/ORStools/gui/ORStoolsDialog.py +++ b/ORStools/gui/ORStoolsDialog.py @@ -77,7 +77,6 @@ __help__, ) from ORStools.common import ( - PROFILES, PREFERENCES, ) from ORStools.utils import maptools, configmanager, transform, gui, exceptions @@ -94,6 +93,8 @@ def on_config_click(parent): """ config_dlg = ORStoolsDialogConfigMain(parent=parent) config_dlg.exec() + if type(parent) == ORStoolsDialog: + parent.refresh_profiles() def on_help_click() -> None: @@ -324,7 +325,8 @@ def __init__(self, iface: QgisInterface, parent=None) -> None: os.environ["ORS_REMAINING"] = "None" # Populate combo boxes - self.routing_travel_combo.addItems(PROFILES) + self.provider_combo.currentIndexChanged.connect(self.refresh_profiles) + self.refresh_profiles() self.routing_preference_combo.addItems(PREFERENCES) # Change OK and Cancel button names @@ -408,6 +410,13 @@ def __init__(self, iface: QgisInterface, parent=None) -> None: self.rubber_band = None + def refresh_profiles(self) -> None: + """Refreshes the profiles in the routing travel combo box when a provider is selected.""" + self.routing_travel_combo.clear() + index = self.provider_combo.currentIndex() + provider = configmanager.read_config()["providers"][index] + self.routing_travel_combo.addItems(provider["profiles"]) + def _save_vertices_to_layer(self) -> None: """Saves the vertices list to a temp layer""" items = [ From f4bdd51267b24690265b51fac85d30016babb7d5 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Tue, 24 Jun 2025 11:33:19 +0200 Subject: [PATCH 22/29] fix: actually save settings --- ORStools/ORStoolsPlugin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py index 557ad08c..74832ad6 100644 --- a/ORStools/ORStoolsPlugin.py +++ b/ORStools/ORStoolsPlugin.py @@ -34,6 +34,7 @@ from .gui import ORStoolsDialog from .proc import provider, ENDPOINTS, DEFAULT_SETTINGS, PROFILES +from .utils import configmanager class ORStools: @@ -73,7 +74,10 @@ def __init__(self, iface: QgisInterface) -> None: except TypeError: pass - self.add_default_provider_to_settings() + try: + configmanager.read_config()["providers"] + except KeyError: + self.add_default_provider_to_settings() def initGui(self) -> None: """Create the menu entries and toolbar icons inside the QGIS GUI.""" From be9cd11c4ab865b7eb50ea820bff79c3f86eeb3c Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Tue, 24 Jun 2025 11:34:24 +0200 Subject: [PATCH 23/29] style: run ruff --- ORStools/gui/ORStoolsDialog.py | 2 +- ORStools/gui/ORStoolsDialogConfig.py | 6 +++++- ORStools/proc/base_processing_algorithm.py | 4 +--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ORStools/gui/ORStoolsDialog.py b/ORStools/gui/ORStoolsDialog.py index 65c7d49a..f4462de8 100644 --- a/ORStools/gui/ORStoolsDialog.py +++ b/ORStools/gui/ORStoolsDialog.py @@ -93,7 +93,7 @@ def on_config_click(parent): """ config_dlg = ORStoolsDialogConfigMain(parent=parent) config_dlg.exec() - if type(parent) == ORStoolsDialog: + if type(parent) is ORStoolsDialog: parent.refresh_profiles() diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index e5b89532..f549ac8d 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -419,7 +419,11 @@ def load_profiles_button_clicked(self, button: QPushButton) -> None: url = f"{base_url}/v2/status" if "api.openrouteservice.org" in url: - QMessageBox.warning(self, "Load profiles not possible", "Load profiles not possible, please use 'Restore Defaults' for the openrouteservice live API", ) + QMessageBox.warning( + self, + "Load profiles not possible", + "Load profiles not possible, please use 'Restore Defaults' for the openrouteservice live API", + ) return request = QgsBlockingNetworkRequest() diff --git a/ORStools/proc/base_processing_algorithm.py b/ORStools/proc/base_processing_algorithm.py index 2aab9cd4..5c0a706f 100644 --- a/ORStools/proc/base_processing_algorithm.py +++ b/ORStools/proc/base_processing_algorithm.py @@ -75,9 +75,7 @@ def __init__(self) -> None: self.PARAMETERS = None self.providers = configmanager.read_config()["providers"] - profiles_list = [ - provider["profiles"] for provider in self.providers - ] + profiles_list = [provider["profiles"] for provider in self.providers] self.profiles = list(set(element for sublist in profiles_list for element in sublist)) def createInstance(self) -> Any: From 1e0e3ff0a76d621e1c464e7a39c26257ffdcbace Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Wed, 25 Jun 2025 11:12:51 +0200 Subject: [PATCH 24/29] fix: update profile retrieval to use instance profiles --- ORStools/proc/directions_lines_proc.py | 2 +- ORStools/proc/directions_points_layer_proc.py | 2 +- ORStools/proc/directions_points_layers_proc.py | 2 +- ORStools/proc/export_proc.py | 2 +- ORStools/proc/isochrones_layer_proc.py | 2 +- ORStools/proc/isochrones_point_proc.py | 2 +- ORStools/proc/matrix_proc.py | 2 +- ORStools/proc/snap_layer_proc.py | 2 +- ORStools/proc/snap_point_proc.py | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ORStools/proc/directions_lines_proc.py b/ORStools/proc/directions_lines_proc.py index 4d6436cb..f19bd7d8 100644 --- a/ORStools/proc/directions_lines_proc.py +++ b/ORStools/proc/directions_lines_proc.py @@ -130,7 +130,7 @@ def processAlgorithm( ) -> Dict[str, str]: ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] preference = dict(enumerate(PREFERENCES))[parameters[self.IN_PREFERENCE]] diff --git a/ORStools/proc/directions_points_layer_proc.py b/ORStools/proc/directions_points_layer_proc.py index 41c8d5cf..b76b1137 100644 --- a/ORStools/proc/directions_points_layer_proc.py +++ b/ORStools/proc/directions_points_layer_proc.py @@ -137,7 +137,7 @@ def processAlgorithm( ) -> Dict[str, str]: ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] preference = dict(enumerate(PREFERENCES))[parameters[self.IN_PREFERENCE]] diff --git a/ORStools/proc/directions_points_layers_proc.py b/ORStools/proc/directions_points_layers_proc.py index 024e05ef..4e0c6203 100644 --- a/ORStools/proc/directions_points_layers_proc.py +++ b/ORStools/proc/directions_points_layers_proc.py @@ -151,7 +151,7 @@ def processAlgorithm( ) -> Dict[str, str]: ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] preference = dict(enumerate(PREFERENCES))[parameters[self.IN_PREFERENCE]] diff --git a/ORStools/proc/export_proc.py b/ORStools/proc/export_proc.py index 279d9f3a..4d5208e7 100644 --- a/ORStools/proc/export_proc.py +++ b/ORStools/proc/export_proc.py @@ -80,7 +80,7 @@ def processAlgorithm( ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) # Get profile value - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] target_crs = QgsCoordinateReferenceSystem("EPSG:4326") rect = self.parameterAsExtent(parameters, self.IN_EXPORT, context, crs=target_crs) diff --git a/ORStools/proc/isochrones_layer_proc.py b/ORStools/proc/isochrones_layer_proc.py index 0a4b60e0..abc2bc4d 100644 --- a/ORStools/proc/isochrones_layer_proc.py +++ b/ORStools/proc/isochrones_layer_proc.py @@ -125,7 +125,7 @@ def processAlgorithm( ) -> Dict[str, str]: ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] dimension = dict(enumerate(DIMENSIONS))[parameters[self.IN_METRIC]] location_type = dict(enumerate(LOCATION_TYPES))[parameters[self.LOCATION_TYPE]] diff --git a/ORStools/proc/isochrones_point_proc.py b/ORStools/proc/isochrones_point_proc.py index b6b29c16..12d30de3 100644 --- a/ORStools/proc/isochrones_point_proc.py +++ b/ORStools/proc/isochrones_point_proc.py @@ -111,7 +111,7 @@ def processAlgorithm( ) -> Dict[str, str]: ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] dimension = dict(enumerate(DIMENSIONS))[parameters[self.IN_METRIC]] location_type = dict(enumerate(LOCATION_TYPES))[parameters[self.LOCATION_TYPE]] diff --git a/ORStools/proc/matrix_proc.py b/ORStools/proc/matrix_proc.py index 089347d3..5fda6311 100644 --- a/ORStools/proc/matrix_proc.py +++ b/ORStools/proc/matrix_proc.py @@ -96,7 +96,7 @@ def processAlgorithm( ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) # Get profile value - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] # TODO: enable once core matrix is available # options = self.parseOptions(parameters, context) diff --git a/ORStools/proc/snap_layer_proc.py b/ORStools/proc/snap_layer_proc.py index edd07be5..4e0189f4 100644 --- a/ORStools/proc/snap_layer_proc.py +++ b/ORStools/proc/snap_layer_proc.py @@ -79,7 +79,7 @@ def processAlgorithm( ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) # Get profile value - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] # Get parameter values source = self.parameterAsSource(parameters, self.IN_POINTS, context) diff --git a/ORStools/proc/snap_point_proc.py b/ORStools/proc/snap_point_proc.py index 94fd2db1..a1407860 100644 --- a/ORStools/proc/snap_point_proc.py +++ b/ORStools/proc/snap_point_proc.py @@ -82,7 +82,7 @@ def processAlgorithm( ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback) # Get profile value - profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]] + profile = dict(enumerate(self.profiles))[parameters[self.IN_PROFILE]] # Get parameter values point = self.parameterAsPoint(parameters, self.IN_POINT, context, self.crs_out) From 722b96448acc253423a2657e95ef6c0a4ce060f8 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Wed, 25 Jun 2025 12:09:03 +0200 Subject: [PATCH 25/29] fix: improve error handling for invalid profile parameters --- ORStools/proc/directions_lines_proc.py | 13 +++- ORStools/proc/directions_points_layer_proc.py | 13 +++- .../proc/directions_points_layers_proc.py | 20 ++++-- ORStools/proc/export_proc.py | 12 +++- ORStools/proc/isochrones_layer_proc.py | 20 ++++-- ORStools/proc/isochrones_point_proc.py | 13 +++- ORStools/proc/matrix_proc.py | 72 +++++++++++-------- ORStools/proc/snap_layer_proc.py | 12 +++- ORStools/proc/snap_point_proc.py | 12 +++- 9 files changed, 136 insertions(+), 51 deletions(-) diff --git a/ORStools/proc/directions_lines_proc.py b/ORStools/proc/directions_lines_proc.py index f19bd7d8..f1cd68fe 100644 --- a/ORStools/proc/directions_lines_proc.py +++ b/ORStools/proc/directions_lines_proc.py @@ -50,7 +50,7 @@ ) from qgis.PyQt.QtGui import QIcon -from ORStools.common import directions_core, PROFILES, PREFERENCES, OPTIMIZATION_MODES, EXTRA_INFOS +from ORStools.common import directions_core, PREFERENCES, OPTIMIZATION_MODES, EXTRA_INFOS from ORStools.utils import transform, exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm from ..utils.processing import get_params_optimize @@ -245,7 +245,16 @@ def processAlgorithm( ) ) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"Feature ID {num} caused a {e.__class__.__name__}:\n{str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"Feature ID {num} caused a {e.__class__.__name__}:\n{str(e)}" feedback.reportError(msg) logger.log(msg) continue diff --git a/ORStools/proc/directions_points_layer_proc.py b/ORStools/proc/directions_points_layer_proc.py index b76b1137..2c49b48f 100644 --- a/ORStools/proc/directions_points_layer_proc.py +++ b/ORStools/proc/directions_points_layer_proc.py @@ -49,7 +49,7 @@ QgsProcessingFeedback, ) -from ORStools.common import directions_core, PROFILES, PREFERENCES, OPTIMIZATION_MODES, EXTRA_INFOS +from ORStools.common import directions_core, PREFERENCES, OPTIMIZATION_MODES, EXTRA_INFOS from ORStools.utils import transform, exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm from ..utils.gui import GuiUtils @@ -286,7 +286,16 @@ def sort(f): ) ) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"Feature ID {from_value} caused a {e.__class__.__name__}:\n{str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"Feature ID {from_value} caused a {e.__class__.__name__}:\n{str(e)}" feedback.reportError(msg) logger.log(msg) continue diff --git a/ORStools/proc/directions_points_layers_proc.py b/ORStools/proc/directions_points_layers_proc.py index 4e0c6203..958bc706 100644 --- a/ORStools/proc/directions_points_layers_proc.py +++ b/ORStools/proc/directions_points_layers_proc.py @@ -46,7 +46,7 @@ QgsProcessingFeedback, ) -from ORStools.common import directions_core, PROFILES, PREFERENCES, EXTRA_INFOS +from ORStools.common import directions_core, PREFERENCES, EXTRA_INFOS from ORStools.utils import transform, exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm @@ -246,9 +246,21 @@ def sort_end(f): f"/v2/{endpoint}/{profile}/geojson", {}, post_json=params ) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"Route from {values[0]} to {values[1]} caused a {e.__class__.__name__}:\n{str(e)}" - feedback.reportError(msg) - logger.log(msg) + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + feedback.reportError(msg) + logger.log(msg) + break + else: + msg = f"Route from {values[0]} to {values[1]} caused a {e.__class__.__name__}:\n{str(e)}" + feedback.reportError(msg) + logger.log(msg) continue if extra_info: diff --git a/ORStools/proc/export_proc.py b/ORStools/proc/export_proc.py index 4d5208e7..75d18603 100644 --- a/ORStools/proc/export_proc.py +++ b/ORStools/proc/export_proc.py @@ -49,7 +49,6 @@ from ..utils.wrapper import create_qgs_field -from ORStools.common import PROFILES from ORStools.utils import exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm @@ -150,7 +149,16 @@ def processAlgorithm( sink_point.addFeature(point_feat) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"{e.__class__.__name__}: {str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"{e.__class__.__name__}: {str(e)}" feedback.reportError(msg) logger.log(msg) diff --git a/ORStools/proc/isochrones_layer_proc.py b/ORStools/proc/isochrones_layer_proc.py index abc2bc4d..85a88e11 100644 --- a/ORStools/proc/isochrones_layer_proc.py +++ b/ORStools/proc/isochrones_layer_proc.py @@ -45,7 +45,7 @@ QgsProcessingFeedback, ) -from ORStools.common import isochrones_core, PROFILES, DIMENSIONS, LOCATION_TYPES +from ORStools.common import isochrones_core, DIMENSIONS, LOCATION_TYPES from ORStools.proc.base_processing_algorithm import ORSBaseProcessingAlgorithm from ORStools.utils import transform, exceptions, logger from ORStools.utils.gui import GuiUtils @@ -206,9 +206,21 @@ def processAlgorithm( sink.addFeature(isochrone) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" - feedback.reportError(msg) - logger.log(msg, 2) + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + feedback.reportError(msg) + logger.log(msg, 2) + break + else: + msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" + feedback.reportError(msg) + logger.log(msg, 2) continue feedback.setProgress(int(100.0 / source.featureCount() * num)) diff --git a/ORStools/proc/isochrones_point_proc.py b/ORStools/proc/isochrones_point_proc.py index 12d30de3..ffa36ec7 100644 --- a/ORStools/proc/isochrones_point_proc.py +++ b/ORStools/proc/isochrones_point_proc.py @@ -42,7 +42,7 @@ QgsProcessingFeedback, ) -from ORStools.common import isochrones_core, PROFILES, DIMENSIONS, LOCATION_TYPES +from ORStools.common import isochrones_core, DIMENSIONS, LOCATION_TYPES from ORStools.utils import exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm from ..utils.gui import GuiUtils @@ -164,7 +164,16 @@ def processAlgorithm( sink.addFeature(isochrone) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" feedback.reportError(msg) logger.log(msg, 2) diff --git a/ORStools/proc/matrix_proc.py b/ORStools/proc/matrix_proc.py index 5fda6311..c53cdac2 100644 --- a/ORStools/proc/matrix_proc.py +++ b/ORStools/proc/matrix_proc.py @@ -46,7 +46,6 @@ from qgis.PyQt.QtCore import QMetaType -from ORStools.common import PROFILES from ORStools.utils import transform, exceptions, logger from .base_processing_algorithm import ORSBaseProcessingAlgorithm from ..utils.gui import GuiUtils @@ -182,41 +181,52 @@ def processAlgorithm( endpoint = self.get_endpoint_names_from_provider(parameters[self.IN_PROVIDER])["matrix"] response = ors_client.request(f"/v2/{endpoint}/{profile}", {}, post_json=params) - except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"{e.__class__.__name__}: {str(e)}" - feedback.reportError(msg) - logger.log(msg) + (sink, dest_id) = self.parameterAsSink( + parameters, self.OUT, context, sink_fields, QgsWkbTypes.Type.NoGeometry + ) - (sink, dest_id) = self.parameterAsSink( - parameters, self.OUT, context, sink_fields, QgsWkbTypes.Type.NoGeometry - ) + sources_attributes = [ + feat.attribute(source_field_name) if source_field_name else feat.id() + for feat in sources_features + ] + destinations_attributes = [ + feat.attribute(destination_field_name) if destination_field_name else feat.id() + for feat in destination_features + ] - sources_attributes = [ - feat.attribute(source_field_name) if source_field_name else feat.id() - for feat in sources_features - ] - destinations_attributes = [ - feat.attribute(destination_field_name) if destination_field_name else feat.id() - for feat in destination_features - ] + for s, source in enumerate(sources_attributes): + for d, destination in enumerate(destinations_attributes): + duration = response["durations"][s][d] + distance = response["distances"][s][d] + feat = QgsFeature() + feat.setAttributes( + [ + source, + destination, + duration / 3600 if duration is not None else None, + distance / 1000 if distance is not None else None, + ] + ) - for s, source in enumerate(sources_attributes): - for d, destination in enumerate(destinations_attributes): - duration = response["durations"][s][d] - distance = response["distances"][s][d] - feat = QgsFeature() - feat.setAttributes( - [ - source, - destination, - duration / 3600 if duration is not None else None, - distance / 1000 if distance is not None else None, - ] - ) + sink.addFeature(feat) - sink.addFeature(feat) + return {self.OUT: dest_id} + + except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" + feedback.reportError(msg) + logger.log(msg) - return {self.OUT: dest_id} + return {self.OUT: ""} # TODO working source_type and destination_type differ in both name and type from get_fields in directions_core. # Change to be consistent diff --git a/ORStools/proc/snap_layer_proc.py b/ORStools/proc/snap_layer_proc.py index 4e0189f4..d2ab3a22 100644 --- a/ORStools/proc/snap_layer_proc.py +++ b/ORStools/proc/snap_layer_proc.py @@ -42,7 +42,6 @@ QgsCoordinateReferenceSystem, ) -from ORStools.common import PROFILES from ORStools.utils.gui import GuiUtils from ORStools.utils.processing import get_snapped_point_features from ORStools.proc.base_processing_algorithm import ORSBaseProcessingAlgorithm @@ -131,7 +130,16 @@ def processAlgorithm( sink.addFeature(feat) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"{e.__class__.__name__}: {str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"{e.__class__.__name__}: {str(e)}" feedback.reportError(msg) logger.log(msg) diff --git a/ORStools/proc/snap_point_proc.py b/ORStools/proc/snap_point_proc.py index a1407860..1832c91e 100644 --- a/ORStools/proc/snap_point_proc.py +++ b/ORStools/proc/snap_point_proc.py @@ -41,7 +41,6 @@ QgsCoordinateReferenceSystem, ) -from ORStools.common import PROFILES from ORStools.utils.gui import GuiUtils from ORStools.utils.processing import get_snapped_point_features from ORStools.proc.base_processing_algorithm import ORSBaseProcessingAlgorithm @@ -116,7 +115,16 @@ def processAlgorithm( sink.addFeature(feat) except (exceptions.ApiError, exceptions.InvalidKey, exceptions.GenericServerError) as e: - msg = f"{e.__class__.__name__}: {str(e)}" + if ( + isinstance(e, exceptions.ApiError) + and "Parameter 'profile' has incorrect value" in e.message + ): + provider = self.providers[parameters[self.IN_PROVIDER]]["name"] + msg = self.tr( + f'The selected profile "{profile}" is not available in the chosen provider "{provider}"' + ) + else: + msg = f"Feature ID {params['id']} caused a {e.__class__.__name__}:\n{str(e)}" feedback.reportError(msg) logger.log(msg) From 8999359cc080ec8b76829bcb843e9c2bf7bdc92c Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Wed, 25 Jun 2025 12:50:17 +0200 Subject: [PATCH 26/29] fix: enhance error handling for provider configuration --- ORStools/ORStoolsPlugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py index 74832ad6..cdf61f0d 100644 --- a/ORStools/ORStoolsPlugin.py +++ b/ORStools/ORStoolsPlugin.py @@ -76,7 +76,7 @@ def __init__(self, iface: QgisInterface) -> None: try: configmanager.read_config()["providers"] - except KeyError: + except (TypeError, KeyError): self.add_default_provider_to_settings() def initGui(self) -> None: From 876d69fa4f31729104c015cd0e0dbd8eed59f9cd Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Thu, 26 Jun 2025 10:41:00 +0200 Subject: [PATCH 27/29] test: update assertions to use assertAlmostEqual for precision --- tests/test_gui.py | 18 ++++++++++-------- tests/test_proc.py | 13 +++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/test_gui.py b/tests/test_gui.py index d51a1728..74d08380 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -303,10 +303,13 @@ def test_ORStoolsDialogConfig_endpoints(self): layer = proc.test_directions_points_layer() - self.assertEqual( - "POINT(8.67251100000000008 49.39887900000000087)", - next(layer.getFeatures()).geometry().asPolyline()[0].asWkt(), - ) + # self.assertEqual( + # "POINT(8.67251100000000008 49.39887900000000087)", + # next(layer.getFeatures()).geometry().asPolyline()[0].asWkt(), + # ) + pt = next(layer.getFeatures()).geometry().asPolyline()[0] + self.assertAlmostEqual(pt.x(), 8.67251100000000008, 3) + self.assertAlmostEqual(pt.y(), 49.39887900000000087, 3) def test_ORStoolsDialogConfig_url(self): from ORStools.gui.ORStoolsDialogConfig import ORStoolsDialogConfigMain @@ -354,7 +357,6 @@ def test_ORStoolsDialogConfig_url(self): layer = proc.test_directions_points_layer() - self.assertEqual( - "POINT(8.67251100000000008 49.39887900000000087)", - next(layer.getFeatures()).geometry().asPolyline()[0].asWkt(), - ) + pt = next(layer.getFeatures()).geometry().asPolyline()[0] + self.assertAlmostEqual(pt.x(), 8.67251100000000008, 3) + self.assertAlmostEqual(pt.y(), 49.39887900000000087, 3) diff --git a/tests/test_proc.py b/tests/test_proc.py index c13fc807..35e852f9 100644 --- a/tests/test_proc.py +++ b/tests/test_proc.py @@ -247,9 +247,9 @@ def test_snapping(self): dest_id = snap_point.processAlgorithm(parameters, self.context, self.feedback) processed_layer = QgsProcessingUtils.mapLayerFromString(dest_id["OUTPUT"], self.context) new_feat = next(processed_layer.getFeatures()) - self.assertEqual( - new_feat.geometry().asWkt(), "Point (-106.61225600000000213 34.98548300000000211)" - ) + pt = new_feat.geometry().asPoint() + self.assertAlmostEqual(pt.x(), -106.61225600000000213, places=3) + self.assertAlmostEqual(pt.y(), 34.98548300000000211, places=3) parameters = { "INPUT_PROFILE": 0, @@ -264,9 +264,10 @@ def test_snapping(self): processed_layer = QgsProcessingUtils.mapLayerFromString(dest_id["OUTPUT"], self.context) new_feat = next(processed_layer.getFeatures()) - self.assertEqual( - new_feat.geometry().asWkt(), "Point (8.46554599999999979 49.48699799999999982)" - ) + pt = new_feat.geometry().asPoint() + self.assertAlmostEqual(pt.x(), 8.46554599999999979, places=2) + self.assertAlmostEqual(pt.y(), 49.48699799999999982, places=2) + self.assertEqual(len([i for i in processed_layer.getFeatures()]), 2) # test with "SNAPPED_NAME" being present in layer fields From e316c72a9d8ab3701f443adb18c9eba0f43713f4 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Tue, 27 Jan 2026 16:32:34 +0100 Subject: [PATCH 28/29] refactor: update import statements for qt6 --- ORStools/gui/ORStoolsDialogConfig.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py index f549ac8d..940d6392 100644 --- a/ORStools/gui/ORStoolsDialogConfig.py +++ b/ORStools/gui/ORStoolsDialogConfig.py @@ -29,13 +29,12 @@ import json -from PyQt5.QtCore import QUrl -from PyQt5.QtNetwork import QNetworkRequest -from qgis._core import QgsBlockingNetworkRequest +from qgis.PyQt.QtNetwork import QNetworkRequest +from qgis.core import QgsBlockingNetworkRequest from qgis.gui import QgsCollapsibleGroupBox, QgsNewNameDialog from qgis.PyQt import QtWidgets, uic -from qgis.PyQt.QtCore import QMetaObject +from qgis.PyQt.QtCore import QMetaObject, QUrl from qgis.PyQt.QtWidgets import ( QDialog, QInputDialog, From 33cbc96d6f4825a8d97cfb70832ac459d27ef986 Mon Sep 17 00:00:00 2001 From: Till Frankenbach Date: Wed, 28 Jan 2026 10:42:43 +0100 Subject: [PATCH 29/29] fix: update providers on update of plugin --- ORStools/ORStoolsPlugin.py | 29 ++++++++++++----------------- tests/conftest.py | 10 +++++----- tests/test_gui.py | 2 +- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py index cdf61f0d..299ec0bd 100644 --- a/ORStools/ORStoolsPlugin.py +++ b/ORStools/ORStoolsPlugin.py @@ -52,6 +52,7 @@ def __init__(self, iface: QgisInterface) -> None: """ self.dialog = ORStoolsDialog.ORStoolsDialogMain(iface) self.provider = provider.ORStoolsProvider() + self.settings_keys = ["ENV_VARS", "base_url", "key", "name", "endpoints", "profiles"] # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) @@ -74,10 +75,7 @@ def __init__(self, iface: QgisInterface) -> None: except TypeError: pass - try: - configmanager.read_config()["providers"] - except (TypeError, KeyError): - self.add_default_provider_to_settings() + self.update_settings() def initGui(self) -> None: """Create the menu entries and toolbar icons inside the QGIS GUI.""" @@ -90,24 +88,21 @@ def unload(self) -> None: QgsApplication.processingRegistry().removeProvider(self.provider) self.dialog.unload() - def add_default_provider_to_settings(self): - s = QgsSettings() - settings = s.value("ORStools/config") + def update_settings(self): + settings = configmanager.read_config() - settings_keys = ["ENV_VARS", "base_url", "key", "name", "endpoints", "profiles"] + if settings is not None and settings != {}: + endpoints = settings.get("endpoints", ENDPOINTS) + profiles = settings.get("profiles", PROFILES) - # Add any new settings here for backwards compatibility - if settings: changed = False for i, prov in enumerate(settings["providers"]): - if any([i not in prov for i in settings_keys]): + if any([key not in prov for key in self.settings_keys]): changed = True - # Add here, like the endpoints - prov["endpoints"] = ENDPOINTS - settings["providers"][i] = prov - prov["profiles"] = PROFILES + prov["endpoints"] = endpoints + prov["profiles"] = profiles settings["providers"][i] = prov if changed: - s.setValue("ORStools/config", settings) + configmanager.write_config(settings) else: - s.setValue("ORStools/config", DEFAULT_SETTINGS) + configmanager.write_config(DEFAULT_SETTINGS) diff --git a/tests/conftest.py b/tests/conftest.py index 4c8ead41..1259b371 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,9 @@ import os -from qgis.core import QgsSettings from ORStools.ORStoolsPlugin import ORStools from tests.utils.utilities import get_qgis_app +from ORStools.utils import configmanager def pytest_sessionstart(session): @@ -13,13 +13,13 @@ def pytest_sessionstart(session): """ QGISAPP, CANVAS, IFACE, PARENT = get_qgis_app() - ORStools(IFACE).add_default_provider_to_settings() - s = QgsSettings() - data = s.value("ORStools/config") + ORStools(IFACE) + + data = configmanager.read_config() if not os.environ.get("ORS_API_KEY"): raise ValueError( "No API key found in environment variables. Please set ORS_API_KEY environment variable to run tests." ) data["providers"][0]["key"] = os.environ.get("ORS_API_KEY") - s.setValue("ORStools/config", data) + configmanager.write_config(data) diff --git a/tests/test_gui.py b/tests/test_gui.py index df0ce234..75032196 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -872,4 +872,4 @@ def test_load_layer_exception_handling(self): dialog_main.dlg.load_vertices_from_layer("ok") # Should not crash and list should be empty - self.assertEqual(dialog_main.dlg.routing_fromline_list.count(), 0) \ No newline at end of file + self.assertEqual(dialog_main.dlg.routing_fromline_list.count(), 0)