diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml
new file mode 100644
index 00000000..12961605
--- /dev/null
+++ b/.github/workflows/ruff.yml
@@ -0,0 +1,15 @@
+name: Ruff
+on: [ push, pull_request ]
+jobs:
+ ruff:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: chartboost/ruff-action@v1
+ with:
+ src: ORStools
+ args: format --check
+ - uses: chartboost/ruff-action@v1
+ with:
+ src: ORStools
+ args: check
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 10ec17f4..1f25e242 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,8 @@ RELEASING:
### Added
- remove blue lines every time the red X button is clicked ([#120](https://github.com/GIScience/orstools-qgis-plugin/issues/120))
+- Additional parameter for the "smoothing factor" to isochrones processing algorithms ([#172](https://github.com/GIScience/orstools-qgis-plugin/issues/172))
+- Mention omission of configuration options when using traveling salesman
## [1.6.0] - 2023-07-25
@@ -50,7 +52,6 @@ RELEASING:
- translation mechanism ([#183](https://github.com/GIScience/orstools-qgis-plugin/pull/183))
- german translation ([#183](https://github.com/GIScience/orstools-qgis-plugin/pull/183))
-
## [1.5.3] - 2023-03-30
### Fixed
diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py
index e95558de..4b653b41 100644
--- a/ORStools/ORStoolsPlugin.py
+++ b/ORStools/ORStoolsPlugin.py
@@ -37,6 +37,7 @@
class ORStools:
"""QGIS Plugin Implementation."""
+
# noinspection PyTypeChecker,PyArgumentList,PyCallByClass
def __init__(self, iface):
@@ -54,17 +55,14 @@ def __init__(self, iface):
self.plugin_dir = os.path.dirname(__file__)
# initialize locale
- locale = QSettings().value('locale/userLocale')[0:2]
- locale_path = os.path.join(
- self.plugin_dir,
- 'i18n',
- 'orstools_{}.qm'.format(locale))
+ locale = QSettings().value("locale/userLocale")[0:2]
+ locale_path = os.path.join(self.plugin_dir, "i18n", "orstools_{}.qm".format(locale))
if os.path.exists(locale_path):
self.translator = QTranslator()
self.translator.load(locale_path)
- if qVersion() > '4.3.3':
+ if qVersion() > "4.3.3":
QCoreApplication.installTranslator(self.translator)
def initGui(self):
diff --git a/ORStools/__init__.py b/ORStools/__init__.py
index 155acf1c..5e82c330 100644
--- a/ORStools/__init__.py
+++ b/ORStools/__init__.py
@@ -41,28 +41,28 @@ def classFactory(iface): # pylint: disable=invalid-name
"""
from .ORStoolsPlugin import ORStools
+
return ORStools(iface)
# Define plugin wide constants
-PLUGIN_NAME = 'ORS Tools'
-DEFAULT_COLOR = '#a8b1f5'
+PLUGIN_NAME = "ORS Tools"
+DEFAULT_COLOR = "#a8b1f5"
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
RESOURCE_PREFIX = ":plugins/ORStools/img/"
-CONFIG_PATH = os.path.join(BASE_DIR, 'config.yml')
-ENV_VARS = {'ORS_REMAINING': 'X-Ratelimit-Remaining',
- 'ORS_QUOTA': 'X-Ratelimit-Limit'}
+CONFIG_PATH = os.path.join(BASE_DIR, "config.yml")
+ENV_VARS = {"ORS_REMAINING": "X-Ratelimit-Remaining", "ORS_QUOTA": "X-Ratelimit-Limit"}
# Read metadata.txt
METADATA = configparser.ConfigParser()
-METADATA.read(os.path.join(BASE_DIR, 'metadata.txt'), encoding='utf-8')
+METADATA.read(os.path.join(BASE_DIR, "metadata.txt"), encoding="utf-8")
today = datetime.today()
-__version__ = METADATA['general']['version']
-__author__ = METADATA['general']['author']
-__email__ = METADATA['general']['email']
-__web__ = METADATA['general']['homepage']
-__help__ = METADATA['general']['help']
-__date__ = today.strftime('%Y-%m-%d')
-__copyright__ = f'(C) {today.year} by {__author__}'
+__version__ = METADATA["general"]["version"]
+__author__ = METADATA["general"]["author"]
+__email__ = METADATA["general"]["email"]
+__web__ = METADATA["general"]["homepage"]
+__help__ = METADATA["general"]["help"]
+__date__ = today.strftime("%Y-%m-%d")
+__copyright__ = f"(C) {today.year} by {__author__}"
diff --git a/ORStools/common/__init__.py b/ORStools/common/__init__.py
index b714c41f..5dbffe60 100644
--- a/ORStools/common/__init__.py
+++ b/ORStools/common/__init__.py
@@ -28,25 +28,31 @@
"""
PROFILES = [
- 'driving-car',
- 'driving-hgv',
- 'cycling-regular',
- 'cycling-road',
- 'cycling-mountain',
- 'cycling-electric',
- 'foot-walking',
- 'foot-hiking',
- 'wheelchair'
- ]
-
-DIMENSIONS = ['time', 'distance']
-
-PREFERENCES = ['fastest', 'shortest', 'recommended']
-
-OPTIMIZATION_MODES = ['Round Trip', 'Fix Start Point', 'Fix End Point', 'Fix Start and End Point']
-
-AVOID_FEATURES = ['highways', 'tollways', 'ferries', 'fords', 'steps']
-
-AVOID_BORDERS = ['all', 'controlled', 'none']
-
-ADVANCED_PARAMETERS = ["INPUT_AVOID_FEATURES", "INPUT_AVOID_BORDERS", "INPUT_AVOID_COUNTRIES", "INPUT_AVOID_POLYGONS"]
+ "driving-car",
+ "driving-hgv",
+ "cycling-regular",
+ "cycling-road",
+ "cycling-mountain",
+ "cycling-electric",
+ "foot-walking",
+ "foot-hiking",
+ "wheelchair",
+]
+
+DIMENSIONS = ["time", "distance"]
+
+PREFERENCES = ["fastest", "shortest", "recommended"]
+
+OPTIMIZATION_MODES = ["Round Trip", "Fix Start Point", "Fix End Point", "Fix Start and End Point"]
+
+AVOID_FEATURES = ["highways", "tollways", "ferries", "fords", "steps"]
+
+AVOID_BORDERS = ["all", "controlled", "none"]
+
+ADVANCED_PARAMETERS = [
+ "INPUT_AVOID_FEATURES",
+ "INPUT_AVOID_BORDERS",
+ "INPUT_AVOID_COUNTRIES",
+ "INPUT_AVOID_POLYGONS",
+ "INPUT_SMOOTHING",
+]
diff --git a/ORStools/common/client.py b/ORStools/common/client.py
index 8a0b6afb..35b62c99 100644
--- a/ORStools/common/client.py
+++ b/ORStools/common/client.py
@@ -57,33 +57,29 @@ def __init__(self, provider=None):
"""
QObject.__init__(self)
- self.key = provider['key']
- self.base_url = provider['base_url']
- self.ENV_VARS = provider.get('ENV_VARS')
+ self.key = provider["key"]
+ self.base_url = provider["base_url"]
+ self.ENV_VARS = provider.get("ENV_VARS")
# self.session = requests.Session()
- retry_timeout = provider.get('timeout')
+ retry_timeout = provider.get("timeout")
self.nam = networkaccessmanager.NetworkAccessManager(debug=False, timeout=retry_timeout)
self.retry_timeout = timedelta(seconds=retry_timeout)
self.headers = {
- "User-Agent": _USER_AGENT,
- 'Content-type': 'application/json',
- 'Authorization': provider['key']
- }
+ "User-Agent": _USER_AGENT,
+ "Content-type": "application/json",
+ "Authorization": provider["key"],
+ }
# Save some references to retrieve in client instances
self.url = None
self.warnings = None
overQueryLimit = pyqtSignal()
- def request(self,
- url,
- params,
- first_request_time=None,
- retry_counter=0,
- post_json=None):
+
+ def request(self, url, params, first_request_time=None, retry_counter=0, post_json=None):
"""Performs HTTP GET/POST with credentials, returning the body as
JSON.
@@ -124,14 +120,15 @@ def request(self,
# 0.5 * (1.5 ^ i) is an increased sleep time of 1.5x per iteration,
# starting at 0.5s when retry_counter=1. The first retry will occur
# at 1, so subtract that first.
- delay_seconds = 1.5**(retry_counter - 1)
+ delay_seconds = 1.5 ** (retry_counter - 1)
# Jitter this value by 50% and pause.
time.sleep(delay_seconds * (random.random() + 0.5))
- authed_url = self._generate_auth_url(url,
- params,
- )
+ authed_url = self._generate_auth_url(
+ url,
+ params,
+ )
self.url = self.base_url + authed_url
# Default to the client-level self.requests_kwargs, with method-level
@@ -140,25 +137,20 @@ def request(self,
# Determine GET/POST
# requests_method = self.session.get
- requests_method = 'GET'
+ requests_method = "GET"
body = None
if post_json is not None:
# requests_method = self.session.post
# final_requests_kwargs["json"] = post_json
body = post_json
- requests_method = 'POST'
+ requests_method = "POST"
- logger.log(
- f"url: {self.url}\nParameters: {json.dumps(body, indent=2)}",
- 0
- )
+ logger.log(f"url: {self.url}\nParameters: {json.dumps(body, indent=2)}", 0)
try:
- response, content = self.nam.request(self.url,
- method=requests_method,
- body=body,
- headers=self.headers,
- blocking=True)
+ response, content = self.nam.request(
+ self.url, method=requests_method, body=body, headers=self.headers, blocking=True
+ )
except networkaccessmanager.RequestsExceptionTimeout:
raise exceptions.Timeout
@@ -167,7 +159,6 @@ def request(self,
self._check_status()
except exceptions.OverQueryLimit as e:
-
# Let the instances know something happened
# noinspection PyUnresolvedReferences
self.overQueryLimit.emit()
@@ -176,7 +167,9 @@ def request(self,
return self.request(url, params, first_request_time, retry_counter + 1, post_json)
except exceptions.ApiError as e:
- logger.log(f"Feature ID {post_json['id']} caused a {e.__class__.__name__}: {str(e)}", 2)
+ logger.log(
+ f"Feature ID {post_json['id']} caused a {e.__class__.__name__}: {str(e)}", 2
+ )
raise
raise
@@ -184,9 +177,11 @@ def request(self,
# Write env variables if successful
if self.ENV_VARS:
for env_var in self.ENV_VARS:
- configmanager.write_env_var(env_var, response.headers.get(self.ENV_VARS[env_var], 'None'))
+ configmanager.write_env_var(
+ env_var, response.headers.get(self.ENV_VARS[env_var], "None")
+ )
- return json.loads(content.decode('utf-8'))
+ return json.loads(content.decode("utf-8"))
def _check_status(self):
"""
@@ -202,34 +197,28 @@ def _check_status(self):
"""
status_code = self.nam.http_call_result.status_code
- message = self.nam.http_call_result.text if self.nam.http_call_result.text != '' else self.nam.http_call_result.reason
+ message = (
+ self.nam.http_call_result.text
+ if self.nam.http_call_result.text != ""
+ else self.nam.http_call_result.reason
+ )
if not status_code:
- raise Exception(f"{message}. Are your provider settings correct and the provider ready?")
+ raise Exception(
+ f"{message}. Are your provider settings correct and the provider ready?"
+ )
elif status_code == 403:
- raise exceptions.InvalidKey(
- str(status_code),
- message
- )
+ raise exceptions.InvalidKey(str(status_code), message)
elif status_code == 429:
- raise exceptions.OverQueryLimit(
- str(status_code),
- message
- )
+ raise exceptions.OverQueryLimit(str(status_code), message)
# Internal error message for Bad Request
elif 400 <= status_code < 500:
- raise exceptions.ApiError(
- str(status_code),
- message
- )
+ raise exceptions.ApiError(str(status_code), message)
# Other HTTP errors have different formatting
elif status_code != 200:
- raise exceptions.GenericServerError(
- str(status_code),
- message
- )
+ raise exceptions.GenericServerError(str(status_code), message)
def _generate_auth_url(self, path, params):
"""Returns the path and query string portion of the request URL, first
@@ -245,7 +234,7 @@ def _generate_auth_url(self, path, params):
:rtype: string
"""
- if type(params) is dict:
+ if isinstance(params, dict):
params = sorted(dict(**params).items())
# Only auto-add API key when using ORS. If own instance, API key must
diff --git a/ORStools/common/directions_core.py b/ORStools/common/directions_core.py
index 038319a6..e7496ac3 100644
--- a/ORStools/common/directions_core.py
+++ b/ORStools/common/directions_core.py
@@ -28,12 +28,7 @@
"""
from itertools import product
-from qgis.core import (QgsPoint,
- QgsPointXY,
- QgsGeometry,
- QgsFeature,
- QgsFields,
- QgsField)
+from qgis.core import QgsPoint, QgsPointXY, QgsGeometry, QgsFeature, QgsFields, QgsField
from typing import List
from PyQt5.QtCore import QVariant
@@ -55,18 +50,18 @@ def get_request_point_features(route_dict, row_by_row):
:rtype: tuple
"""
- locations_list = list(product(route_dict['start']['geometries'],
- route_dict['end']['geometries']))
- values_list = list(product(route_dict['start']['values'],
- route_dict['end']['values']))
+ locations_list = list(
+ product(route_dict["start"]["geometries"], route_dict["end"]["geometries"])
+ )
+ values_list = list(product(route_dict["start"]["values"], route_dict["end"]["values"]))
# If row-by-row in two-layer mode, then only zip the locations
- if row_by_row == 'Row-by-Row':
- locations_list = list(zip(route_dict['start']['geometries'],
- route_dict['end']['geometries']))
+ if row_by_row == "Row-by-Row":
+ locations_list = list(
+ zip(route_dict["start"]["geometries"], route_dict["end"]["geometries"])
+ )
- values_list = list(zip(route_dict['start']['values'],
- route_dict['end']['values']))
+ values_list = list(zip(route_dict["start"]["values"], route_dict["end"]["values"]))
for properties in zip(locations_list, values_list):
# Skip if first and last location are the same
@@ -79,7 +74,13 @@ def get_request_point_features(route_dict, row_by_row):
yield coordinates, values
-def get_fields(from_type=QVariant.String, to_type=QVariant.String, from_name="FROM_ID", to_name="TO_ID", line=False):
+def get_fields(
+ from_type=QVariant.String,
+ to_type=QVariant.String,
+ from_name="FROM_ID",
+ to_name="TO_ID",
+ line=False,
+):
"""
Builds output fields for directions response layer.
@@ -115,7 +116,9 @@ def get_fields(from_type=QVariant.String, to_type=QVariant.String, from_name="FR
return fields
-def get_output_feature_directions(response, profile, preference, options=None, from_value=None, to_value=None):
+def get_output_feature_directions(
+ response, profile, preference, options=None, from_value=None, to_value=None
+):
"""
Build output feature based on response attributes for directions endpoint.
@@ -140,21 +143,24 @@ def get_output_feature_directions(response, profile, preference, options=None, f
:returns: Output feature with attributes and geometry set.
:rtype: QgsFeature
"""
- response_mini = response['features'][0]
+ response_mini = response["features"][0]
feat = QgsFeature()
- coordinates = response_mini['geometry']['coordinates']
- distance = response_mini['properties']['summary']['distance']
- duration = response_mini['properties']['summary']['duration']
+ coordinates = response_mini["geometry"]["coordinates"]
+ distance = response_mini["properties"]["summary"]["distance"]
+ duration = response_mini["properties"]["summary"]["duration"]
qgis_coords = [QgsPoint(x, y, z) for x, y, z in coordinates]
feat.setGeometry(QgsGeometry.fromPolyline(qgis_coords))
- feat.setAttributes([f"{distance / 1000:.3f}",
- f"{duration / 3600:.3f}",
- profile,
- preference,
- str(options),
- from_value,
- to_value
- ])
+ feat.setAttributes(
+ [
+ f"{distance / 1000:.3f}",
+ f"{duration / 3600:.3f}",
+ profile,
+ preference,
+ str(options),
+ from_value,
+ to_value,
+ ]
+ )
return feat
@@ -176,25 +182,33 @@ def get_output_features_optimization(response, profile, from_value=None):
:rtype: QgsFeature
"""
- response_mini = response['routes'][0]
+ response_mini = response["routes"][0]
feat = QgsFeature()
- polyline = response_mini['geometry']
- distance = response_mini['distance']
- duration = response_mini['cost']
+ polyline = response_mini["geometry"]
+ distance = response_mini["distance"]
+ duration = response_mini["cost"]
qgis_coords = [QgsPointXY(x, y) for x, y in convert.decode_polyline(polyline)]
feat.setGeometry(QgsGeometry.fromPolylineXY(qgis_coords))
- feat.setAttributes([f"{distance / 1000:.3f}",
- f"{duration / 3600:.3f}",
- profile,
- 'fastest',
- 'optimized',
- from_value
- ])
+ feat.setAttributes(
+ [
+ f"{distance / 1000:.3f}",
+ f"{duration / 3600:.3f}",
+ profile,
+ "fastest",
+ "optimized",
+ from_value,
+ ]
+ )
return feat
-def build_default_parameters(preference: str, point_list: List[QgsPointXY] = None, coordinates: list = None, options: dict = None) -> dict:
+def build_default_parameters(
+ preference: str,
+ point_list: List[QgsPointXY] = None,
+ coordinates: list = None,
+ options: dict = None,
+) -> dict:
"""
Build default parameters for directions endpoint. Either uses a list of QgsPointXY to create the coordinates
passed in point_list or an existing coordinate list within the coordinates parameter.
@@ -212,15 +226,19 @@ def build_default_parameters(preference: str, point_list: List[QgsPointXY] = Non
:returns: parameters for directions endpoint
:rtype: dict
"""
- coords = coordinates if coordinates else [[round(point.x(), 6), round(point.y(), 6)] for point in point_list]
+ coords = (
+ coordinates
+ if coordinates
+ else [[round(point.x(), 6), round(point.y(), 6)] for point in point_list]
+ )
params = {
- 'coordinates': coords,
- 'preference': preference,
- 'geometry': 'true',
- 'instructions': 'false',
- 'elevation': True,
- 'id': None,
- "options": options
+ "coordinates": coords,
+ "preference": preference,
+ "geometry": "true",
+ "instructions": "false",
+ "elevation": True,
+ "id": None,
+ "options": options,
}
return params
diff --git a/ORStools/common/isochrones_core.py b/ORStools/common/isochrones_core.py
index f60a82f8..39c90150 100644
--- a/ORStools/common/isochrones_core.py
+++ b/ORStools/common/isochrones_core.py
@@ -27,15 +27,17 @@
***************************************************************************/
"""
-from qgis.core import (QgsPointXY,
- QgsFeature,
- QgsField,
- QgsFields,
- QgsGeometry,
- QgsSymbol,
- QgsSimpleFillSymbolLayer,
- QgsRendererCategory,
- QgsCategorizedSymbolRenderer)
+from qgis.core import (
+ QgsPointXY,
+ QgsFeature,
+ QgsField,
+ QgsFields,
+ QgsGeometry,
+ QgsSymbol,
+ QgsSimpleFillSymbolLayer,
+ QgsRendererCategory,
+ QgsCategorizedSymbolRenderer,
+)
from PyQt5.QtCore import QVariant
from PyQt5.QtGui import QColor
@@ -48,7 +50,6 @@ class Isochrones:
"""convenience class to build isochrones"""
def __init__(self):
-
# Will all be set in self.set_parameters(), bcs Processing Algo has to initialize this class before it
# knows about its own parameters
self.profile = None
@@ -58,7 +59,9 @@ def __init__(self):
self.factor = None
self.field_dimension_name = None
- def set_parameters(self, profile, dimension, factor, id_field_type=QVariant.String, id_field_name='ID'):
+ def set_parameters(
+ self, profile, dimension, factor, id_field_type=QVariant.String, id_field_name="ID"
+ ):
"""
Sets all parameters defined in __init__, because processing algorithm calls this class when it doesn't know
its parameters yet.
@@ -84,7 +87,7 @@ def set_parameters(self, profile, dimension, factor, id_field_type=QVariant.Stri
self.id_field_name = id_field_name
self.factor = factor
- self.field_dimension_name = "AA_MINS" if self.dimension == 'time' else "AA_METERS"
+ self.field_dimension_name = "AA_MINS" if self.dimension == "time" else "AA_METERS"
def get_fields(self):
"""
@@ -119,22 +122,26 @@ def get_features(self, response, id_field_value):
# Sort features based on the isochrone value, so that longest isochrone
# is added first. This will plot the isochrones on top of each other.
- for isochrone in sorted(response['features'], key=lambda x: x['properties']['value'], reverse=True):
+ for isochrone in sorted(
+ response["features"], key=lambda x: x["properties"]["value"], reverse=True
+ ):
feat = QgsFeature()
- coordinates = isochrone['geometry']['coordinates']
- iso_value = isochrone['properties']['value']
- center = isochrone['properties']['center']
- total_pop = isochrone['properties'].get('total_pop')
+ coordinates = isochrone["geometry"]["coordinates"]
+ iso_value = isochrone["properties"]["value"]
+ center = isochrone["properties"]["center"]
+ total_pop = isochrone["properties"].get("total_pop")
qgis_coords = [QgsPointXY(x, y) for x, y in coordinates[0]]
feat.setGeometry(QgsGeometry.fromPolygonXY([qgis_coords]))
- feat.setAttributes([
- id_field_value,
- center[0],
- center[1],
- int(iso_value / self.factor),
- self.profile,
- total_pop
- ])
+ feat.setAttributes(
+ [
+ id_field_value,
+ center[0],
+ center[1],
+ int(iso_value / self.factor),
+ self.profile,
+ total_pop,
+ ]
+ )
yield feat
@@ -159,24 +166,26 @@ def stylePoly(self, layer):
:type layer: QgsMapLayer
"""
- if self.dimension == 'time':
- legend_suffix = ' min'
+ if self.dimension == "time":
+ legend_suffix = " min"
else:
- legend_suffix = ' m'
+ legend_suffix = " m"
field = layer.fields().indexOf(self.field_dimension_name)
unique_values = sorted(layer.uniqueValues(field))
- colors = {0: QColor('#2b83ba'),
- 1: QColor('#64abb0'),
- 2: QColor('#9dd3a7'),
- 3: QColor('#c7e9ad'),
- 4: QColor('#edf8b9'),
- 5: QColor('#ffedaa'),
- 6: QColor('#fec980'),
- 7: QColor('#f99e59'),
- 8: QColor('#e85b3a'),
- 9: QColor('#d7191c')}
+ colors = {
+ 0: QColor("#2b83ba"),
+ 1: QColor("#64abb0"),
+ 2: QColor("#9dd3a7"),
+ 3: QColor("#c7e9ad"),
+ 4: QColor("#edf8b9"),
+ 5: QColor("#ffedaa"),
+ 6: QColor("#fec980"),
+ 7: QColor("#f99e59"),
+ 8: QColor("#e85b3a"),
+ 9: QColor("#d7191c"),
+ }
categories = []
@@ -185,8 +194,9 @@ def stylePoly(self, layer):
symbol = QgsSymbol.defaultSymbol(layer.geometryType())
# configure a symbol layer
- symbol_layer = QgsSimpleFillSymbolLayer(color=colors[cid],
- strokeColor=QColor('#000000'))
+ symbol_layer = QgsSimpleFillSymbolLayer(
+ color=colors[cid], strokeColor=QColor("#000000")
+ )
# replace default symbol layer with the configured one
if symbol_layer is not None:
diff --git a/ORStools/common/networkaccessmanager.py b/ORStools/common/networkaccessmanager.py
index 8ae126ae..c2dd8132 100644
--- a/ORStools/common/networkaccessmanager.py
+++ b/ORStools/common/networkaccessmanager.py
@@ -21,42 +21,45 @@
from builtins import object
import json
-__author__ = 'Alessandro Pasotti'
-__date__ = 'August 2016'
+__author__ = "Alessandro Pasotti"
+__date__ = "August 2016"
import re
import io
-import urllib.request, urllib.error, urllib.parse
+import urllib.parse
from qgis.PyQt.QtCore import QUrl, QEventLoop
+
from qgis.PyQt.QtNetwork import QNetworkRequest, QNetworkReply
-from qgis.core import (
- QgsApplication,
- QgsNetworkAccessManager,
- QgsMessageLog
-)
+from qgis.core import QgsApplication, QgsNetworkAccessManager, QgsMessageLog
# FIXME: ignored
DEFAULT_MAX_REDIRECTS = 4
+
class RequestsException(Exception):
pass
+
class RequestsExceptionTimeout(RequestsException):
pass
+
class RequestsExceptionConnectionError(RequestsException):
pass
+
class RequestsExceptionUserAbort(RequestsException):
pass
+
class Map(dict):
"""
Example:
m = Map({'first_name': 'Eduardo'}, last_name='Pool', age=24, sports=['Soccer'])
"""
+
def __init__(self, *args, **kwargs):
super(Map, self).__init__(*args, **kwargs)
for arg in args:
@@ -89,6 +92,7 @@ def __delitem__(self, key):
class Response(Map):
pass
+
class NetworkAccessManager(object):
"""
This class mimics httplib2 by using QgsNetworkAccessManager for all
@@ -142,7 +146,14 @@ class NetworkAccessManager(object):
'exception' - the exception returned during execution
"""
- def __init__(self, authid=None, disable_ssl_certificate_validation=False, exception_class=None, debug=True, timeout=60):
+ def __init__(
+ self,
+ authid=None,
+ disable_ssl_certificate_validation=False,
+ exception_class=None,
+ debug=True,
+ timeout=60,
+ ):
self.disable_ssl_certificate_validation = disable_ssl_certificate_validation
self.authid = authid
self.reply = None
@@ -150,17 +161,19 @@ def __init__(self, authid=None, disable_ssl_certificate_validation=False, except
self.exception_class = exception_class
self.on_abort = False
self.blocking_mode = False
- self.http_call_result = Response({
- 'status': 0,
- 'status_code': 0,
- 'status_message': '',
- 'content' : '',
- 'ok': False,
- 'headers': {},
- 'reason': '',
- 'exception': None,
- })
- self.timeout=timeout
+ self.http_call_result = Response(
+ {
+ "status": 0,
+ "status_code": 0,
+ "status_message": "",
+ "content": "",
+ "ok": False,
+ "headers": {},
+ "reason": "",
+ "exception": None,
+ }
+ )
+ self.timeout = timeout
def msg_log(self, msg):
if self.debug:
@@ -177,7 +190,7 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):
Make a network request by calling QgsNetworkAccessManager.
redirections argument is ignored and is here only for httplib2 compatibility.
"""
- self.msg_log(f'http_call request: {url}')
+ self.msg_log(f"http_call request: {url}")
self.blocking_mode = blocking
req = QNetworkRequest()
@@ -192,7 +205,7 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):
# encoding processing".
# See: https://bugs.webkit.org/show_bug.cgi?id=63696#c1
try:
- del headers['Accept-Encoding']
+ del headers["Accept-Encoding"]
except KeyError:
pass
for k, v in list(headers.items()):
@@ -204,8 +217,8 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):
self.auth_manager().updateNetworkRequest(req, self.authid)
if self.reply is not None and self.reply.isRunning():
self.reply.close()
- if method.lower() == 'delete':
- func = getattr(QgsNetworkAccessManager.instance(), 'deleteResource')
+ if method.lower() == "delete":
+ func = getattr(QgsNetworkAccessManager.instance(), "deleteResource")
else:
func = getattr(QgsNetworkAccessManager.instance(), method.lower())
# Calling the server ...
@@ -215,21 +228,21 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):
headers = {str(h): str(req.rawHeader(h)) for h in req.rawHeaderList()}
for k, v in list(headers.items()):
self.msg_log("%s: %s" % (k, v))
- if method.lower() in ['post', 'put']:
+ if method.lower() in ["post", "put"]:
if isinstance(body, io.IOBase):
body = body.read()
if isinstance(body, str):
body = body.encode()
if isinstance(body, dict):
- body = str(json.dumps(body)).encode(encoding='utf-8')
+ body = str(json.dumps(body)).encode(encoding="utf-8")
self.reply = func(req, body)
else:
self.reply = func(req)
if self.authid:
self.msg_log(f"Update reply w/ authid: {self.authid}")
self.auth_manager().updateNetworkReply(self.reply, self.authid)
-
- QgsNetworkAccessManager.instance().setTimeout(self.timeout*1000)
+
+ QgsNetworkAccessManager.instance().setTimeout(self.timeout * 1000)
# necessary to trap local timeout managed by QgsNetworkAccessManager
# calling QgsNetworkAccessManager::abortRequest
@@ -268,7 +281,7 @@ def request(self, url, method="GET", body=None, headers=None, blocking=True):
def downloadProgress(self, bytesReceived, bytesTotal):
"""Keep track of the download progress"""
- #self.msg_log("downloadProgress %s of %s ..." % (bytesReceived, bytesTotal))
+ # self.msg_log("downloadProgress %s of %s ..." % (bytesReceived, bytesTotal))
pass
# noinspection PyUnusedLocal
@@ -286,14 +299,18 @@ def replyFinished(self):
self.http_call_result.status = httpStatus
self.http_call_result.status_message = httpStatusMessage
for k, v in self.reply.rawHeaderPairs():
- self.http_call_result.headers[str(k.data(), encoding='utf-8')] = str(v.data(), encoding='utf-8')
- self.http_call_result.headers[str(k.data(), encoding='utf-8').lower()] = str(v.data(), encoding='utf-8')
+ self.http_call_result.headers[str(k.data(), encoding="utf-8")] = str(
+ v.data(), encoding="utf-8"
+ )
+ self.http_call_result.headers[str(k.data(), encoding="utf-8").lower()] = str(
+ v.data(), encoding="utf-8"
+ )
if err != QNetworkReply.NoError:
# handle error
# check if errorString is empty, if so, then set err string as
# reply dump
- if re.match('(.)*server replied: $', self.reply.errorString()):
+ if re.match("(.)*server replied: $", self.reply.errorString()):
errString = self.reply.errorString() + self.http_call_result.content
else:
errString = self.reply.errorString()
@@ -305,7 +322,7 @@ def replyFinished(self):
msg = f"Network error: {errString}"
self.http_call_result.reason = msg
- self.http_call_result.text = str(self.reply.readAll().data(), encoding='utf-8')
+ self.http_call_result.text = str(self.reply.readAll().data(), encoding="utf-8")
self.http_call_result.ok = False
self.msg_log(msg)
# set return exception
@@ -351,14 +368,18 @@ def replyFinished(self):
ba = self.reply.readAll()
self.http_call_result.content = bytes(ba)
- self.http_call_result.text = str(ba.data(), encoding='utf-8')
+ self.http_call_result.text = str(ba.data(), encoding="utf-8")
self.http_call_result.ok = True
# Let's log the whole response for debugging purposes:
- self.msg_log("Got response %s %s from %s" % \
- (self.http_call_result.status_code,
- self.http_call_result.status_message,
- self.reply.url().toString()))
+ self.msg_log(
+ "Got response %s %s from %s"
+ % (
+ self.http_call_result.status_code,
+ self.http_call_result.status_message,
+ self.reply.url().toString(),
+ )
+ )
for k, v in list(self.http_call_result.headers.items()):
self.msg_log("%s: %s" % (k, v))
if len(self.http_call_result.content) < 1024:
diff --git a/ORStools/gui/ORStoolsDialog.py b/ORStools/gui/ORStoolsDialog.py
index a3ae4623..3ca36468 100644
--- a/ORStools/gui/ORStoolsDialog.py
+++ b/ORStools/gui/ORStoolsDialog.py
@@ -31,31 +31,32 @@
import os
import processing
import webbrowser
-from qgis.core import (QgsProject,
- QgsVectorLayer,
- QgsTextAnnotation,
- QgsMapLayerProxyModel)
+from qgis.core import QgsProject, QgsVectorLayer, QgsTextAnnotation, QgsMapLayerProxyModel
from qgis.gui import QgsMapCanvasAnnotationItem
from PyQt5.QtCore import QSizeF, QPointF, QCoreApplication
from PyQt5.QtGui import QIcon, QTextDocument
-from PyQt5.QtWidgets import (QAction,
- QDialog,
- QApplication,
- QMenu,
- QMessageBox,
- QDialogButtonBox)
-
-from ORStools import RESOURCE_PREFIX, PLUGIN_NAME, DEFAULT_COLOR, __version__, __email__, __web__, __help__
-from ORStools.common import (client,
- directions_core,
- PROFILES,
- PREFERENCES, )
+from PyQt5.QtWidgets import QAction, QDialog, QApplication, QMenu, QMessageBox, QDialogButtonBox
+
+from ORStools import (
+ RESOURCE_PREFIX,
+ PLUGIN_NAME,
+ DEFAULT_COLOR,
+ __version__,
+ __email__,
+ __web__,
+ __help__,
+)
+from ORStools.common import (
+ client,
+ directions_core,
+ PROFILES,
+ PREFERENCES,
+)
from ORStools.gui import directions_gui
from ORStools.utils import exceptions, maptools, logger, configmanager, transform
from .ORStoolsDialogConfig import ORStoolsDialogConfigMain
from .ORStoolsDialogUI import Ui_ORStoolsDialogBase
-from . import resources_rc
def on_config_click(parent):
@@ -76,24 +77,25 @@ def on_help_click():
def on_about_click(parent):
"""Slot for click event of About button/menu entry."""
- info = QCoreApplication.translate('@default', 'ORS Tools provides access to openrouteservice routing functionalities.' \
- '
' \
- '
If waypoints are selected in the list, only these will be deleted. Else all waypoints will be deleted.
")) self.routing_fromline_list.setToolTip(_translate("ORStoolsDialogBase", "Select waypoints from the map!")) self.advances_group.setTitle(_translate("ORStoolsDialogBase", "Advanced Configuration")) - self.optimization_group.setToolTip(_translate("ORStoolsDialogBase", "Enabling Traveling Salesman will erase all other advanced configuration and assume the preference to be fastest.
")) + self.optimization_group.setToolTip(_translate("ORStoolsDialogBase", "Enabling Traveling Salesman will omit all other advanced configuration and assume the preference to be fastest.
")) self.optimization_group.setTitle(_translate("ORStoolsDialogBase", "Traveling Salesman")) self.label_4.setText(_translate("ORStoolsDialogBase", "\n" "