Skip to content

Commit

Permalink
fix: alert on duplicate points traveling salesman (#275)
Browse files Browse the repository at this point in the history
Co-authored-by: Jakob Schnell <[email protected]>
  • Loading branch information
merydian and koebi committed Aug 27, 2024
1 parent 1f0e123 commit 0131012
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 44 deletions.
52 changes: 50 additions & 2 deletions ORStools/gui/ORStoolsDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
from qgis.gui import QgsMapCanvasAnnotationItem

from qgis.PyQt.QtCore import QSizeF, QPointF, QCoreApplication
from qgis.PyQt.QtGui import QIcon, QTextDocument
from qgis.PyQt.QtGui import QIcon, QTextDocument, QColor
from qgis.PyQt.QtWidgets import (
QAction,
QDialog,
Expand Down Expand Up @@ -106,6 +106,8 @@ def on_help_click() -> None:
def on_about_click(parent: QWidget) -> None:
"""Slot for click event of About button/menu entry."""

# ruff will add trailing comma to last string line which breaks pylupdate5
# fmt: off
info = QCoreApplication.translate(
"@default",
'<b>ORS Tools</b> provides access to <a href="https://openrouteservice.org"'
Expand All @@ -120,8 +122,9 @@ def on_about_click(parent: QWidget) -> None:
'Web: <a href="{2}">{2}</a><br>'
'Repo: <a href="https://github.com/GIScience/orstools-qgis-plugin">'
"github.com/GIScience/orstools-qgis-plugin</a><br>"
"Version: {3}",
"Version: {3}"
).format(DEFAULT_COLOR, __email__, __web__, __version__)
# fmt: on

QMessageBox.information(
parent, QCoreApplication.translate("@default", "About {}").format(PLUGIN_NAME), info
Expand Down Expand Up @@ -324,6 +327,27 @@ def run_gui_control(self) -> None:
try:
params = directions.get_parameters()
if self.dlg.optimization_group.isChecked():
# check for duplicate points
points = [
self.dlg.routing_fromline_list.item(x).text()
for x in range(self.dlg.routing_fromline_list.count())
]
if len(points) != len(set(points)):
QMessageBox.warning(
self.dlg,
self.tr("Duplicates"),
self.tr(
"""
There are duplicate points in the input layer. Traveling Salesman Optimization does not allow this.
Either remove the duplicates or deselect Traveling Salesman.
"""
),
)
msg = self.tr("The request has been aborted!")
logger.log(msg, 0)
self.dlg.debug_text.setText(msg)
return

if len(params["jobs"]) <= 1: # Start/end locations don't count as job
QMessageBox.critical(
self.dlg,
Expand Down Expand Up @@ -494,6 +518,14 @@ def __init__(self, iface: QgisInterface, parent=None) -> None:
self.routing_fromline_list.model().rowsMoved.connect(self._reindex_list_items)
self.routing_fromline_list.model().rowsRemoved.connect(self._reindex_list_items)

# Connect signals to the color_duplicate_items function
self.routing_fromline_list.model().rowsRemoved.connect(
lambda: self.color_duplicate_items(self.routing_fromline_list)
)
self.routing_fromline_list.model().rowsInserted.connect(
lambda: self.color_duplicate_items(self.routing_fromline_list)
)

self.annotation_canvas = self._iface.mapCanvas()

def _save_vertices_to_layer(self) -> None:
Expand Down Expand Up @@ -637,3 +669,19 @@ def _on_linetool_map_doubleclick(self) -> None:
QApplication.restoreOverrideCursor()
self._iface.mapCanvas().setMapTool(self.last_maptool)
self.show()

def color_duplicate_items(self, list_widget):
item_dict = {}
for index in range(list_widget.count()):
item = list_widget.item(index)
text = item.text()
if text in item_dict:
item_dict[text].append(index)
else:
item_dict[text] = [index]

for indices in item_dict.values():
if len(indices) > 1:
for index in indices:
item = list_widget.item(index)
item.setBackground(QColor("lightsalmon"))
84 changes: 42 additions & 42 deletions ORStools/i18n/orstools_de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
<TS version="2.1" language="de" sourcelanguage="en">
<context>
<name>@default</name>
<message>
<location filename="../gui/ORStoolsDialog.py" line="79"/>
<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>
<translation type="obsolete">&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>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="122"/>
<source>About {}</source>
<translation>Über {}</translation>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="105"/>
<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>
<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>
</message>
</context>
<context>
<name>ORSBaseProcessingAlgorithm</name>
Expand Down Expand Up @@ -79,7 +79,7 @@
<translation>Wegpunktoptimierung (sonstige Konfiguration wird nicht berücksichtigt)</translation>
</message>
<message>
<location filename="../proc/directions_lines_proc.py" line="286"/>
<location filename="../proc/directions_lines_proc.py" line="288"/>
<source>Directions from 1 Polyline-Layer</source>
<translation>Routenberechnung aus einem Polyline-Layer</translation>
</message>
Expand All @@ -104,34 +104,6 @@
<translation>Csv Spalte (benötigt Csv Faktor und csv in Extra Info)</translation>
</message>
</context>
<context>
<name>ORSDirectionsLinesAlgorithm</name>
<message>
<location filename="../proc/directions_lines_proc.py" line="60"/>
<source>Input Line layer</source>
<translation type="obsolete">Eingabelayer (Linien)</translation>
</message>
<message>
<location filename="../proc/directions_lines_proc.py" line="65"/>
<source>Layer ID Field</source>
<translation type="obsolete">ID-Attribut</translation>
</message>
<message>
<location filename="../proc/directions_lines_proc.py" line="72"/>
<source>Travel preference</source>
<translation type="obsolete">Routenpräferenz</translation>
</message>
<message>
<location filename="../proc/directions_lines_proc.py" line="78"/>
<source>Traveling Salesman (omits other configurations)</source>
<translation type="obsolete">Wegpunktoptimierung (sonstige Konfiguration wird nicht berücksichtigt)</translation>
</message>
<message>
<location filename="../proc/directions_lines_proc.py" line="207"/>
<source>Directions from 1 Polyline-Layer</source>
<translation type="obsolete">Routenberechnung aus einem Polyline-Layer</translation>
</message>
</context>
<context>
<name>ORSDirectionsPointsLayerAlgo</name>
<message>
Expand All @@ -155,7 +127,7 @@
<translation>Wegpunktoptimierung (sonstige Konfiguration wird nicht berücksichtigt)</translation>
</message>
<message>
<location filename="../proc/directions_points_layer_proc.py" line="289"/>
<location filename="../proc/directions_points_layer_proc.py" line="298"/>
<source>Directions from 1 Point-Layer</source>
<translation>Routenberechnung aus einem Punkt-Layer</translation>
</message>
Expand All @@ -165,7 +137,7 @@
<translation>ID-Attribut (zum Beispiel für joins)</translation>
</message>
<message>
<location filename="../proc/directions_points_layer_proc.py" line="133"/>
<location filename="../proc/directions_points_layer_proc.py" line="132"/>
<source>Export order of jobs</source>
<translation>Reihenfolge exportieren</translation>
</message>
Expand All @@ -175,15 +147,24 @@
<translation>Extra Info</translation>
</message>
<message>
<location filename="../proc/directions_points_layer_proc.py" line="119"/>
<location filename="../proc/directions_points_layer_proc.py" line="118"/>
<source>Csv Factor (needs Csv Column and csv in Extra Info)</source>
<translation>Csv Faktor (benötigt Csv Spalte und csv in Extra Info)</translation>
</message>
<message>
<location filename="../proc/directions_points_layer_proc.py" line="128"/>
<location filename="../proc/directions_points_layer_proc.py" line="127"/>
<source>Csv Column (needs Csv Factor and csv in Extra Info)</source>
<translation>Csv Spalte (benötigt Csv Faktor und csv in Extra Info)</translation>
</message>
<message>
<location filename="../proc/directions_points_layer_proc.py" line="223"/>
<source>
There are duplicate points in the input layer. Traveling Salesman Optimization does not allow this.
Either remove the duplicates or deselect Traveling Salesman.
</source>
<translation>Das Eingabelayer enthält duplizierte Punkte. Dies ist mit der Wegpunktoptimierung nicht erlaubt.
Duplikate entfernen oder Wegpunktoptimierung abwählen.</translation>
</message>
</context>
<context>
<name>ORSDirectionsPointsLayersAlgo</name>
Expand Down Expand Up @@ -228,7 +209,7 @@
<translation>Zuordnungs-Verfahren</translation>
</message>
<message>
<location filename="../proc/directions_points_layers_proc.py" line="319"/>
<location filename="../proc/directions_points_layers_proc.py" line="326"/>
<source>Directions from 2 Point-Layers</source>
<translation>Routenberechnung aus zwei Punkt-Layern</translation>
</message>
Expand Down Expand Up @@ -350,12 +331,12 @@
<context>
<name>ORStoolsDialog</name>
<message>
<location filename="../gui/ORStoolsDialog.py" line="453"/>
<location filename="../gui/ORStoolsDialog.py" line="473"/>
<source>Apply</source>
<translation>Anwenden</translation>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="454"/>
<location filename="../gui/ORStoolsDialog.py" line="474"/>
<source>Close</source>
<translation>Schließen</translation>
</message>
Expand Down Expand Up @@ -462,7 +443,7 @@ p, li { white-space: pre-wrap; }
<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;
&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;
p, li { white-space: pre-wrap; }
&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;
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
&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>
</message>
<message>
Expand Down Expand Up @@ -729,5 +710,24 @@ p, li { white-space: pre-wrap; }
<source>About</source>
<translation>Über</translation>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="329"/>
<source>Duplicates</source>
<translation>Duplikate</translation>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="329"/>
<source>
There are duplicate points in the input layer. Traveling Salesman Optimization does not allow this.
Either remove the duplicates or deselect Traveling Salesman.
</source>
<translation>Das Eingabelayer enthält duplizierte Punkte. Dies ist mit der Wegpunktoptimierung nicht erlaubt.
Duplikate entfernen oder Wegpunktoptimierung abwählen.</translation>
</message>
<message>
<location filename="../gui/ORStoolsDialog.py" line="338"/>
<source>The request has been aborted!</source>
<translation>Die Anfrage wurde abgebrochen!</translation>
</message>
</context>
</TS>
9 changes: 9 additions & 0 deletions ORStools/proc/directions_points_layer_proc.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@ def sort(f):

try:
if optimization_mode is not None:
# check for duplicate points
if len(points) != len(set(points)):
raise exceptions.DuplicateError(
self.tr("""
There are duplicate points in the input layer. Traveling Salesman Optimization does not allow this.
Either remove the duplicates or deselect Traveling Salesman.
""")
)

params = get_params_optimize(points, profile, optimization_mode)
response = ors_client.request("/optimization", {}, post_json=params)

Expand Down
8 changes: 8 additions & 0 deletions ORStools/utils/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ def __str__(self):
return self.status
else:
return f"{self.status} ({self.message})"


class DuplicateError(Exception):
def __init__(self, message=None):
self.message = message

def __str__(self):
return self.message

0 comments on commit 0131012

Please sign in to comment.