diff --git a/ORStools/ORStoolsPlugin.py b/ORStools/ORStoolsPlugin.py
index e95558de..b22866cd 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,16 @@ def __init__(self, iface):
self.plugin_dir = os.path.dirname(__file__)
# initialize locale
- locale = QSettings().value('locale/userLocale')[0:2]
+ locale = QSettings().value("locale/userLocale")[0:2]
locale_path = os.path.join(
- self.plugin_dir,
- 'i18n',
- 'orstools_{}.qm'.format(locale))
+ 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..dcf05192 100644
--- a/ORStools/common/__init__.py
+++ b/ORStools/common/__init__.py
@@ -28,25 +28,35 @@
"""
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",
+]
diff --git a/ORStools/common/client.py b/ORStools/common/client.py
index 042f3afe..1534f4ec 100644
--- a/ORStools/common/client.py
+++ b/ORStools/common/client.py
@@ -57,33 +57,33 @@ 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.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 +124,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 +141,24 @@ 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,16 +167,20 @@ def request(self,
self._check_status()
except exceptions.OverQueryLimit as e:
-
# Let the instances know something happened
# noinspection PyUnresolvedReferences
self.overQueryLimit.emit()
logger.log(f"{e.__class__.__name__}: {str(e)}", 1)
- return self.request(url, params, first_request_time, retry_counter + 1, post_json)
+ 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 +188,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 +208,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
diff --git a/ORStools/common/directions_core.py b/ORStools/common/directions_core.py
index 038319a6..875a2437 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,22 @@ 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 +78,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 +120,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 +147,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 +186,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 +230,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..fe7b0a92 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,14 @@ 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 +92,9 @@ 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):
"""
@@ -97,7 +107,9 @@ def get_fields(self):
fields.append(QgsField(self.id_field_name, self.id_field_type)) # ID field
fields.append(QgsField("CENTER_LON", QVariant.String))
fields.append(QgsField("CENTER_LAT", QVariant.String))
- fields.append(QgsField(self.field_dimension_name, QVariant.Int)) # Dimension field
+ fields.append(
+ QgsField(self.field_dimension_name, QVariant.Int)
+ ) # Dimension field
fields.append(QgsField("AA_MODE", QVariant.String))
fields.append(QgsField("TOTAL_POP", QVariant.String))
@@ -119,22 +131,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 +175,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,15 +203,18 @@ 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:
symbol.changeSymbolLayer(0, symbol_layer)
# create renderer object
- category = QgsRendererCategory(unique_value, symbol, str(unique_value) + legend_suffix)
+ category = QgsRendererCategory(
+ unique_value, symbol, str(unique_value) + legend_suffix
+ )
# entry for the list of category items
categories.append(category)
diff --git a/ORStools/common/networkaccessmanager.py b/ORStools/common/networkaccessmanager.py
index cb9197da..971a0d32 100644
--- a/ORStools/common/networkaccessmanager.py
+++ b/ORStools/common/networkaccessmanager.py
@@ -21,45 +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.parse
-from qgis.PyQt.QtCore import (QUrl,
- QEventLoop)
+from qgis.PyQt.QtCore import QUrl, QEventLoop
-from qgis.PyQt.QtNetwork import (QNetworkRequest,
- QNetworkReply)
+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:
@@ -92,6 +92,7 @@ def __delitem__(self, key):
class Response(Map):
pass
+
class NetworkAccessManager(object):
"""
This class mimics httplib2 by using QgsNetworkAccessManager for all
@@ -145,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
@@ -153,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:
@@ -180,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()
@@ -195,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()):
@@ -207,32 +217,34 @@ 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 ...
# Let's log the whole call for debugging purposes:
- self.msg_log("Sending %s request to %s" % (method.upper(), req.url().toString()))
+ self.msg_log(
+ "Sending %s request to %s" % (method.upper(), req.url().toString())
+ )
self.on_abort = False
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
@@ -271,7 +283,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
@@ -284,19 +296,25 @@ def requestTimedOut(self, reply):
def replyFinished(self):
err = self.reply.error()
httpStatus = self.reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
- httpStatusMessage = self.reply.attribute(QNetworkRequest.HttpReasonPhraseAttribute)
+ httpStatusMessage = self.reply.attribute(
+ QNetworkRequest.HttpReasonPhraseAttribute
+ )
self.http_call_result.status_code = httpStatus
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()
@@ -308,7 +326,9 @@ 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
@@ -334,7 +354,9 @@ def replyFinished(self):
else:
# Handle redirections
- redirectionUrl = self.reply.attribute(QNetworkRequest.RedirectionTargetAttribute)
+ redirectionUrl = self.reply.attribute(
+ QNetworkRequest.RedirectionTargetAttribute
+ )
if redirectionUrl is not None and redirectionUrl != self.reply.url():
if redirectionUrl.isRelative():
redirectionUrl = self.reply.url().resolved(redirectionUrl)
@@ -354,14 +376,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 2ed30e14..0c5e5501 100644
--- a/ORStools/gui/ORStoolsDialog.py
+++ b/ORStools/gui/ORStoolsDialog.py
@@ -31,26 +31,40 @@
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
@@ -75,24 +89,27 @@ 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.' \
- '
' \
- '
' \
- '' \
- '
' \
- '' \
- 'Author: HeiGIT gGmbH
' \
- 'Email: {1}
' \
- 'Web: {2}
' \
- 'Repo: ' \
- 'github.com/GIScience/orstools-qgis-plugin
' \
- 'Version: {3}').format(DEFAULT_COLOR, __email__, __web__, __version__)
+ info = QCoreApplication.translate(
+ "@default",
+ 'ORS Tools provides access to openrouteservice routing functionalities.'
+ "
"
+ ""
+ ''
+ "
"
+ ""
+ "Author: HeiGIT gGmbH
"
+ 'Email: {1}
'
+ 'Web: {2}
'
+ 'Repo: '
+ "github.com/GIScience/orstools-qgis-plugin
"
+ "Version: {3}",
+ ).format(DEFAULT_COLOR, __email__, __web__, __version__)
QMessageBox.information(
parent,
- QCoreApplication.translate('@default', 'About {}').format(PLUGIN_NAME),
- info
+ QCoreApplication.translate("@default", "About {}").format(PLUGIN_NAME),
+ info,
)
@@ -130,33 +147,28 @@ def create_icon(f):
"""
return QIcon(RESOURCE_PREFIX + f)
- icon_plugin = create_icon('icon_orstools.png')
+ icon_plugin = create_icon("icon_orstools.png")
self.actions = [
QAction(
icon_plugin,
PLUGIN_NAME, # tr text
- self.iface.mainWindow() # parent
+ self.iface.mainWindow(), # parent
),
# Config dialog
QAction(
- create_icon('icon_settings.png'),
- self.tr('Provider Settings'),
- self.iface.mainWindow()
+ create_icon("icon_settings.png"),
+ self.tr("Provider Settings"),
+ self.iface.mainWindow(),
),
# About dialog
QAction(
- create_icon('icon_about.png'),
- self.tr('About'),
- self.iface.mainWindow()
+ create_icon("icon_about.png"), self.tr("About"), self.iface.mainWindow()
),
# Help page
QAction(
- create_icon('icon_help.png'),
- self.tr('Help'),
- self.iface.mainWindow()
- )
-
+ create_icon("icon_help.png"), self.tr("Help"), self.iface.mainWindow()
+ ),
]
# Create menu
@@ -172,8 +184,12 @@ def create_icon(f):
# Connect slots to events
self.actions[0].triggered.connect(self._init_gui_control)
- self.actions[1].triggered.connect(lambda: on_config_click(parent=self.iface.mainWindow()))
- self.actions[2].triggered.connect(lambda: on_about_click(parent=self.iface.mainWindow()))
+ self.actions[1].triggered.connect(
+ lambda: on_config_click(parent=self.iface.mainWindow())
+ )
+ self.actions[2].triggered.connect(
+ lambda: on_about_click(parent=self.iface.mainWindow())
+ )
self.actions[3].triggered.connect(on_help_click)
# Add keyboard shortcut
@@ -188,7 +204,7 @@ def unload(self):
# Remove action for keyboard shortcut
self.iface.unregisterMainWindowAction(self.actions[0])
-
+
del self.dlg
# @staticmethod
@@ -213,17 +229,21 @@ def _init_gui_control(self):
# If not checked, GUI would be rebuilt every time!
if self.first_start:
self.first_start = False
- self.dlg = ORStoolsDialog(self.iface, self.iface.mainWindow()) # setting parent enables modal view
+ self.dlg = ORStoolsDialog(
+ self.iface, self.iface.mainWindow()
+ ) # setting parent enables modal view
# Make sure plugin window stays open when OK is clicked by reconnecting the accepted() signal
self.dlg.global_buttons.accepted.disconnect(self.dlg.accept)
self.dlg.global_buttons.accepted.connect(self.run_gui_control)
- self.dlg.avoidpolygon_dropdown.setFilters(QgsMapLayerProxyModel.PolygonLayer)
+ self.dlg.avoidpolygon_dropdown.setFilters(
+ QgsMapLayerProxyModel.PolygonLayer
+ )
# Populate provider box on window startup, since can be changed from multiple menus/buttons
- providers = configmanager.read_config()['providers']
+ providers = configmanager.read_config()["providers"]
self.dlg.provider_combo.clear()
for provider in providers:
- self.dlg.provider_combo.addItem(provider['name'], provider)
+ self.dlg.provider_combo.addItem(provider["name"], provider)
self.dlg.show()
@@ -245,7 +265,7 @@ def run_gui_control(self):
self.dlg.annotations = []
provider_id = self.dlg.provider_combo.currentIndex()
- provider = configmanager.read_config()['providers'][provider_id]
+ provider = configmanager.read_config()["providers"][provider_id]
# if there are no coordinates, throw an error message
if not self.dlg.routing_fromline_list.count():
@@ -256,12 +276,14 @@ def run_gui_control(self):
Did you forget to set routing waypoints?
Use the 'Add Waypoint' button to add up to 50 waypoints.
- """
+ """,
)
return
# if no API key is present, when ORS is selected, throw an error message
- if not provider['key'] and provider['base_url'].startswith('https://api.openrouteservice.org'):
+ if not provider["key"] and provider["base_url"].startswith(
+ "https://api.openrouteservice.org"
+ ):
QMessageBox.critical(
self.dlg,
"Missing API key",
@@ -270,54 +292,58 @@ def run_gui_control(self):
If you don't have an API key, please visit https://openrouteservice.org/sign-up to get one.
Then enter the API key for openrouteservice provider in Web ► ORS Tools ► Provider Settings or the
- settings symbol in the main ORS Tools GUI, next to the provider dropdown."""
+ settings symbol in the main ORS Tools GUI, next to the provider dropdown.""",
)
return
clnt = client.Client(provider)
- clnt_msg = ''
+ clnt_msg = ""
directions = directions_gui.Directions(self.dlg)
params = None
try:
params = directions.get_parameters()
if self.dlg.optimization_group.isChecked():
- if len(params['jobs']) <= 1: # Start/end locations don't count as job
+ if len(params["jobs"]) <= 1: # Start/end locations don't count as job
QMessageBox.critical(
self.dlg,
"Wrong number of waypoints",
"""At least 3 or 4 waypoints are needed to perform routing optimization.
Remember, the first and last location are not part of the optimization.
- """
+ """,
)
return
- response = clnt.request('/optimization', {}, post_json=params)
- feat = directions_core.get_output_features_optimization(response, params['vehicles'][0]['profile'])
+ response = clnt.request("/optimization", {}, post_json=params)
+ feat = directions_core.get_output_features_optimization(
+ response, params["vehicles"][0]["profile"]
+ )
else:
- params['coordinates'] = directions.get_request_line_feature()
+ params["coordinates"] = directions.get_request_line_feature()
profile = self.dlg.routing_travel_combo.currentText()
# abort on empty avoid polygons layer
- if 'options' in params and 'avoid_polygons' in params['options']\
- and params['options']['avoid_polygons'] == {}:
+ if (
+ "options" in params
+ and "avoid_polygons" in params["options"]
+ and params["options"]["avoid_polygons"] == {}
+ ):
QMessageBox.warning(
self.dlg,
"Empty layer",
"""
The specified avoid polygon(s) layer does not contain any features.
Please add polygons to the layer or uncheck avoid polygons.
- """
+ """,
)
msg = "The request has been aborted!"
logger.log(msg, 0)
self.dlg.debug_text.setText(msg)
return
- response = clnt.request('/v2/directions/' + profile + '/geojson', {}, post_json=params)
+ response = clnt.request(
+ "/v2/directions/" + profile + "/geojson", {}, post_json=params
+ )
feat = directions_core.get_output_feature_directions(
- response,
- profile,
- params['preference'],
- directions.options
+ response, profile, params["preference"], directions.options
)
layer_out.dataProvider().addFeature(feat)
@@ -334,10 +360,11 @@ def run_gui_control(self):
self.dlg.debug_text.setText(msg)
return
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
-
+ except (
+ exceptions.ApiError,
+ exceptions.InvalidKey,
+ exceptions.GenericServerError,
+ ) as e:
logger.log(f"{e.__class__.__name__}: {str(e)}", 2)
clnt_msg += f"{e.__class__.__name__}: ({str(e)})
"
raise
@@ -388,15 +415,17 @@ def __init__(self, iface, parent=None):
self.routing_preference_combo.addItems(PREFERENCES)
# Change OK and Cancel button names
- self.global_buttons.button(QDialogButtonBox.Ok).setText(self.tr('Apply'))
- self.global_buttons.button(QDialogButtonBox.Cancel).setText(self.tr('Close'))
+ self.global_buttons.button(QDialogButtonBox.Ok).setText(self.tr("Apply"))
+ self.global_buttons.button(QDialogButtonBox.Cancel).setText(self.tr("Close"))
# Set up signals/slots
# Config/Help dialogs
self.provider_config.clicked.connect(lambda: on_config_click(self))
self.help_button.clicked.connect(on_help_click)
- self.about_button.clicked.connect(lambda: on_about_click(parent=self._iface.mainWindow()))
+ self.about_button.clicked.connect(
+ lambda: on_about_click(parent=self._iface.mainWindow())
+ )
self.provider_refresh.clicked.connect(self._on_prov_refresh_click)
# Routing tab
@@ -404,25 +433,42 @@ def __init__(self, iface, parent=None):
self.routing_fromline_clear.clicked.connect(self._on_clear_listwidget_click)
# Batch
- self.batch_routing_points.clicked.connect(lambda: processing.execAlgorithmDialog(
- f'{PLUGIN_NAME}:directions_from_points_2_layers'))
- self.batch_routing_point.clicked.connect(lambda: processing.execAlgorithmDialog(
- f'{PLUGIN_NAME}:directions_from_points_1_layer'))
- self.batch_routing_line.clicked.connect(lambda: processing.execAlgorithmDialog(
- f'{PLUGIN_NAME}:directions_from_polylines_layer'))
- self.batch_iso_point.clicked.connect(lambda: processing.execAlgorithmDialog(
- f'{PLUGIN_NAME}:isochrones_from_point'))
- self.batch_iso_layer.clicked.connect(lambda: processing.execAlgorithmDialog(
- f'{PLUGIN_NAME}:isochrones_from_layer'))
- self.batch_matrix.clicked.connect(lambda: processing.execAlgorithmDialog(f'{PLUGIN_NAME}:matrix_from_layers'))
+ self.batch_routing_points.clicked.connect(
+ lambda: processing.execAlgorithmDialog(
+ f"{PLUGIN_NAME}:directions_from_points_2_layers"
+ )
+ )
+ self.batch_routing_point.clicked.connect(
+ lambda: processing.execAlgorithmDialog(
+ f"{PLUGIN_NAME}:directions_from_points_1_layer"
+ )
+ )
+ self.batch_routing_line.clicked.connect(
+ lambda: processing.execAlgorithmDialog(
+ f"{PLUGIN_NAME}:directions_from_polylines_layer"
+ )
+ )
+ self.batch_iso_point.clicked.connect(
+ lambda: processing.execAlgorithmDialog(
+ f"{PLUGIN_NAME}:isochrones_from_point"
+ )
+ )
+ self.batch_iso_layer.clicked.connect(
+ lambda: processing.execAlgorithmDialog(
+ f"{PLUGIN_NAME}:isochrones_from_layer"
+ )
+ )
+ self.batch_matrix.clicked.connect(
+ lambda: processing.execAlgorithmDialog(f"{PLUGIN_NAME}:matrix_from_layers")
+ )
def _on_prov_refresh_click(self):
"""Populates provider dropdown with fresh list from config.yml"""
- providers = configmanager.read_config()['providers']
+ providers = configmanager.read_config()["providers"]
self.provider_combo.clear()
for provider in providers:
- self.provider_combo.addItem(provider['name'], provider)
+ self.provider_combo.addItem(provider["name"], provider)
def _on_clear_listwidget_click(self):
"""Clears the contents of the QgsListWidget and the annotations."""
@@ -433,7 +479,9 @@ def _on_clear_listwidget_click(self):
row = self.routing_fromline_list.row(item)
self.routing_fromline_list.takeItem(row)
if self.annotations:
- self.project.annotationManager().removeAnnotation(self.annotations.pop(row))
+ self.project.annotationManager().removeAnnotation(
+ self.annotations.pop(row)
+ )
else:
# else clear all items and annotations
self.routing_fromline_list.clear()
@@ -454,7 +502,9 @@ def _linetool_annotate_point(self, point, idx):
annotation.setMapPosition(point)
annotation.setMapPositionCrs(map_crs)
- return QgsMapCanvasAnnotationItem(annotation, self._iface.mapCanvas()).annotation()
+ return QgsMapCanvasAnnotationItem(
+ annotation, self._iface.mapCanvas()
+ ).annotation()
def _clear_annotations(self):
"""Clears annotations"""
@@ -472,7 +522,9 @@ def _on_linetool_init(self):
self.line_tool = maptools.LineTool(self._iface.mapCanvas())
self._iface.mapCanvas().setMapTool(self.line_tool)
- self.line_tool.pointDrawn.connect(lambda point, idx: self._on_linetool_map_click(point, idx))
+ self.line_tool.pointDrawn.connect(
+ lambda point, idx: self._on_linetool_map_click(point, idx)
+ )
self.line_tool.doubleClicked.connect(self._on_linetool_map_doubleclick)
def _on_linetool_map_click(self, point, idx):
@@ -481,7 +533,9 @@ def _on_linetool_map_click(self, point, idx):
transformer = transform.transformToWGS(map_crs)
point_wgs = transformer.transform(point)
- self.routing_fromline_list.addItem(f"Point {idx}: {point_wgs.x():.6f}, {point_wgs.y():.6f}")
+ self.routing_fromline_list.addItem(
+ f"Point {idx}: {point_wgs.x():.6f}, {point_wgs.y():.6f}"
+ )
annotation = self._linetool_annotate_point(point, idx)
self.annotations.append(annotation)
diff --git a/ORStools/gui/ORStoolsDialogConfig.py b/ORStools/gui/ORStoolsDialogConfig.py
index 46c8b023..c1b49c59 100644
--- a/ORStools/gui/ORStoolsDialogConfig.py
+++ b/ORStools/gui/ORStoolsDialogConfig.py
@@ -64,14 +64,20 @@ def accept(self):
collapsible_boxes = self.providers.findChildren(QgsCollapsibleGroupBox)
for idx, box in enumerate(collapsible_boxes):
- current_provider = self.temp_config['providers'][idx]
- current_provider['key'] = box.findChild(QtWidgets.QLineEdit, box.title() + "_key_text").text()
- current_provider['base_url'] = box.findChild(QtWidgets.QLineEdit, box.title() + "_base_url_text").text()
- timeout_input = box.findChild(QtWidgets.QLineEdit, box.title() + "_timeout_text")
+ current_provider = self.temp_config["providers"][idx]
+ current_provider["key"] = box.findChild(
+ QtWidgets.QLineEdit, box.title() + "_key_text"
+ ).text()
+ current_provider["base_url"] = box.findChild(
+ QtWidgets.QLineEdit, box.title() + "_base_url_text"
+ ).text()
+ timeout_input = box.findChild(
+ QtWidgets.QLineEdit, box.title() + "_timeout_text"
+ )
# https://doc.qt.io/qt-5/qvalidator.html#State-enum
if timeout_input.validator().State() != 2:
self._adjust_timeout_input(timeout_input)
- current_provider['timeout'] = int(timeout_input.text())
+ current_provider["timeout"] = int(timeout_input.text())
configmanager.write_config(self.temp_config)
self.close()
@@ -87,7 +93,7 @@ def _adjust_timeout_input(input_line_edit: QLineEdit):
val = input_line_edit.validator()
text = input_line_edit.text()
if not text:
- input_line_edit.setText('60')
+ input_line_edit.setText("60")
elif int(text) < val.bottom():
input_line_edit.setText(str(val.bottom()))
elif int(text) > val.top():
@@ -96,12 +102,14 @@ def _adjust_timeout_input(input_line_edit: QLineEdit):
def _build_ui(self):
"""Builds the UI on dialog startup."""
- for provider_entry in self.temp_config['providers']:
- self._add_box(provider_entry['name'],
- provider_entry['base_url'],
- provider_entry['key'],
- provider_entry['timeout'],
- new=False)
+ for provider_entry in self.temp_config["providers"]:
+ self._add_box(
+ provider_entry["name"],
+ provider_entry["base_url"],
+ provider_entry["key"],
+ provider_entry["timeout"],
+ new=False,
+ )
self.gridLayout.addWidget(self.providers, 0, 0, 1, 3)
@@ -115,19 +123,25 @@ def _add_provider(self):
self._collapse_boxes()
# Show quick user input dialog
- provider_name, ok = QInputDialog.getText(self, self.tr("New ORS provider"), self.tr("Enter a name for the provider"))
+ provider_name, ok = QInputDialog.getText(
+ 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, new=True)
+ self._add_box(provider_name, "http://localhost:8082/ors", "", 60, new=True)
def _remove_provider(self):
"""Remove list of providers from list."""
- providers = [provider['name'] for provider in self.temp_config['providers']]
+ providers = [provider["name"] for provider in self.temp_config["providers"]]
- provider, ok = QInputDialog.getItem(self,
- self.tr("Remove ORS provider"),
- self.tr("Choose provider to remove"),
- providers, 0, False)
+ provider, ok = QInputDialog.getItem(
+ self,
+ self.tr("Remove ORS provider"),
+ self.tr("Choose provider to remove"),
+ providers,
+ 0,
+ False,
+ )
if ok:
box_remove = self.providers.findChild(QgsCollapsibleGroupBox, provider)
self.gridLayout.removeWidget(box_remove)
@@ -135,7 +149,7 @@ def _remove_provider(self):
# delete from in-memory self.temp_config
provider_id = providers.index(provider)
- del self.temp_config['providers'][provider_id]
+ del self.temp_config["providers"][provider_id]
def _collapse_boxes(self):
"""Collapse all QgsCollapsibleGroupBoxes."""
@@ -143,12 +157,7 @@ def _collapse_boxes(self):
for box in collapsible_boxes:
box.setCollapsed(True)
- def _add_box(self,
- name,
- url,
- key,
- timeout,
- new=False):
+ def _add_box(self, name, url, key, timeout, new=False):
"""
Adds a provider box to the QWidget layout and self.temp_config.
@@ -165,23 +174,18 @@ def _add_box(self,
:type new: boolean
"""
if new:
- self.temp_config['providers'].append(
- dict(
- name=name,
- base_url=url,
- key=key,
- timeout=timeout
- )
+ self.temp_config["providers"].append(
+ dict(name=name, base_url=url, key=key, timeout=timeout)
)
provider = QgsCollapsibleGroupBox(self.providers)
provider.setObjectName(name)
provider.setTitle(name)
gridLayout_3 = QtWidgets.QGridLayout(provider)
- gridLayout_3.setObjectName(name + '_grid')
+ gridLayout_3.setObjectName(name + "_grid")
key_label = QtWidgets.QLabel(provider)
- key_label.setObjectName(name + '_key_label')
- key_label.setText(self.tr('API Key'))
+ key_label.setObjectName(name + "_key_label")
+ key_label.setText(self.tr("API Key"))
gridLayout_3.addWidget(key_label, 0, 0, 1, 1)
key_text = QtWidgets.QLineEdit(provider)
key_text.setObjectName(name + "_key_text")
@@ -207,4 +211,6 @@ def _add_box(self,
gridLayout_3.addWidget(timeout_text, 5, 0, 1, 4)
self.verticalLayout.addWidget(provider)
- provider.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ provider.setSizePolicy(
+ QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed
+ )
diff --git a/ORStools/gui/directions_gui.py b/ORStools/gui/directions_gui.py
index 28525995..d3bb7c97 100644
--- a/ORStools/gui/directions_gui.py
+++ b/ORStools/gui/directions_gui.py
@@ -106,7 +106,7 @@ def get_request_line_feature(self):
item = layers_list.item(idx).text()
param, coords = item.split(":")
- coordinates.append([float(coord) for coord in coords.split(', ')])
+ coordinates.append([float(coord) for coord in coords.split(", ")])
return [[round(x, 6), round(y, 6)] for x, y in coordinates]
@@ -125,35 +125,35 @@ def get_parameters(self):
route_pref = self.dlg.routing_preference_combo.currentText()
params = {
- 'preference': route_pref,
- 'geometry': 'true',
- 'instructions': 'false',
- 'elevation': True,
- 'id': 1,
+ "preference": route_pref,
+ "geometry": "true",
+ "instructions": "false",
+ "elevation": True,
+ "id": 1,
}
# Get Advanced parameters
if self.dlg.routing_avoid_tags_group.isChecked():
avoid_boxes = self.dlg.routing_avoid_tags_group.findChildren(QCheckBox)
if any(box.isChecked() for box in avoid_boxes):
- self.options['avoid_features'] = _get_avoid_options(avoid_boxes)
+ self.options["avoid_features"] = _get_avoid_options(avoid_boxes)
if self.dlg.routing_avoid_countries_group.isChecked():
countries_text = self.dlg.countries_text.value()
if countries_text:
- countries = countries_text.split(',')
+ countries = countries_text.split(",")
if all(map(lambda x: x.isdigit(), countries)):
countries = [int(x) for x in countries]
- self.options['avoid_countries'] = countries
+ self.options["avoid_countries"] = countries
if self.dlg.avoidpolygon_group.isChecked():
layer = self.dlg.avoidpolygon_dropdown.currentLayer()
if layer:
polygons = _get_avoid_polygons(layer)
- self.options['avoid_polygons'] = polygons
+ self.options["avoid_polygons"] = polygons
if self.options:
- params['options'] = self.options
+ params["options"] = self.options
return params
@@ -162,34 +162,30 @@ def _get_optimize_parameters(self):
coordinates = self.get_request_line_feature()
params = {
- 'jobs': list(),
- 'vehicles': [{
- "id": 0,
- "profile": self.dlg.routing_travel_combo.currentText()
- }],
- 'options': {'g': True}
+ "jobs": list(),
+ "vehicles": [
+ {"id": 0, "profile": self.dlg.routing_travel_combo.currentText()}
+ ],
+ "options": {"g": True},
}
if self.dlg.fix_end.isChecked():
end = coordinates.pop(-1)
- params['vehicles'][0]['end'] = end
+ params["vehicles"][0]["end"] = end
elif self.dlg.fix_start.isChecked():
start = coordinates.pop(0)
- params['vehicles'][0]['start'] = start
+ params["vehicles"][0]["start"] = start
elif self.dlg.fix_both.isChecked():
start = coordinates.pop(0)
end = coordinates.pop(-1)
- params['vehicles'][0]['start'] = start
- params['vehicles'][0]['end'] = end
+ params["vehicles"][0]["start"] = start
+ params["vehicles"][0]["end"] = end
elif self.dlg.round_trip.isChecked():
start = coordinates.pop(0)
- params['vehicles'][0]['start'] = start
- params['vehicles'][0]['end'] = start
+ params["vehicles"][0]["start"] = start
+ params["vehicles"][0]["end"] = start
for coord in coordinates:
- params['jobs'].append({
- "location": coord,
- "id": coordinates.index(coord)
- })
+ params["jobs"].append({"location": coord, "id": coordinates.index(coord)})
return params
diff --git a/ORStools/gui/resources_rc.py b/ORStools/gui/resources_rc.py
index 810d3b19..c638c273 100644
--- a/ORStools/gui/resources_rc.py
+++ b/ORStools/gui/resources_rc.py
@@ -6526,18 +6526,25 @@
\x00\x00\x01\x69\x54\xec\x86\x12\
"
-qt_version = QtCore.qVersion().split('.')
-if qt_version < ['5', '8', '0']:
+qt_version = QtCore.qVersion().split(".")
+if qt_version < ["5", "8", "0"]:
rcc_version = 1
qt_resource_struct = qt_resource_struct_v1
else:
rcc_version = 2
qt_resource_struct = qt_resource_struct_v2
+
def qInitResources():
- QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qRegisterResourceData(
+ rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
def qCleanupResources():
- QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data)
+ QtCore.qUnregisterResourceData(
+ rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data
+ )
+
qInitResources()
diff --git a/ORStools/proc/base_processing_algorithm.py b/ORStools/proc/base_processing_algorithm.py
index 4dd21120..e2c128ad 100644
--- a/ORStools/proc/base_processing_algorithm.py
+++ b/ORStools/proc/base_processing_algorithm.py
@@ -27,23 +27,30 @@
***************************************************************************/
"""
from PyQt5.QtCore import QCoreApplication, QSettings
-from qgis.core import (QgsProcessing,
- QgsProcessingAlgorithm,
- QgsProcessingContext,
- QgsProcessingParameterDefinition,
- QgsProcessingParameterEnum,
- QgsProcessingParameterString,
- QgsProcessingParameterFeatureSink,
- QgsProcessingParameterFeatureSource,
- QgsProcessingFeedback
- )
+from qgis.core import (
+ QgsProcessing,
+ QgsProcessingAlgorithm,
+ QgsProcessingContext,
+ QgsProcessingParameterDefinition,
+ QgsProcessingParameterEnum,
+ QgsProcessingParameterString,
+ QgsProcessingParameterFeatureSink,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingFeedback,
+)
from typing import Any
from PyQt5.QtGui import QIcon
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,
+ PROFILES,
+ AVOID_BORDERS,
+ AVOID_FEATURES,
+ ADVANCED_PARAMETERS,
+)
from ..utils.processing import read_help_file
from ..gui.directions_gui import _get_avoid_polygons
@@ -57,15 +64,15 @@ def __init__(self):
Default attributes used in all child classes
"""
super().__init__()
- self.ALGO_NAME = ''
- self.GROUP = ''
+ self.ALGO_NAME = ""
+ self.GROUP = ""
self.IN_PROVIDER = "INPUT_PROVIDER"
self.IN_PROFILE = "INPUT_PROFILE"
self.IN_AVOID_FEATS = "INPUT_AVOID_FEATURES"
self.IN_AVOID_BORDERS = "INPUT_AVOID_BORDERS"
self.IN_AVOID_COUNTRIES = "INPUT_AVOID_COUNTRIES"
self.IN_AVOID_POLYGONS = "INPUT_AVOID_POLYGONS"
- self.OUT = 'OUTPUT'
+ self.OUT = "OUTPUT"
self.PARAMETERS = None
def createInstance(self) -> Any:
@@ -93,7 +100,7 @@ def shortHelpString(self):
"""
Displays the sidebar help in the algorithm window
"""
- locale = QSettings().value('locale/userLocale')[0:2]
+ locale = QSettings().value("locale/userLocale")[0:2]
return read_help_file(algorithm=self.ALGO_NAME, locale=locale)
@@ -108,18 +115,20 @@ def icon(self) -> QIcon:
"""
Icon used for algorithm in QGIS toolbox
"""
- return QIcon(RESOURCE_PREFIX + f'icon_{self.groupId()}.png')
+ return QIcon(RESOURCE_PREFIX + f"icon_{self.groupId()}.png")
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 configmanager.read_config()["providers"]
+ ]
return QgsProcessingParameterEnum(
self.IN_PROVIDER,
- self.tr("Provider", 'ORSBaseProcessingAlgorithm'),
+ self.tr("Provider", "ORSBaseProcessingAlgorithm"),
providers,
- defaultValue=providers[0]
+ defaultValue=providers[0],
)
def profile_parameter(self) -> QgsProcessingParameterEnum:
@@ -127,11 +136,11 @@ def profile_parameter(self) -> QgsProcessingParameterEnum:
Parameter definition for profile, used in all child classes
"""
return QgsProcessingParameterEnum(
- self.IN_PROFILE,
- self.tr("Travel mode", 'ORSBaseProcessingAlgorithm'),
- PROFILES,
- defaultValue=PROFILES[0]
- )
+ self.IN_PROFILE,
+ self.tr("Travel mode", "ORSBaseProcessingAlgorithm"),
+ PROFILES,
+ defaultValue=PROFILES[0],
+ )
def output_parameter(self) -> QgsProcessingParameterFeatureSink:
"""
@@ -146,42 +155,49 @@ def option_parameters(self) -> [QgsProcessingParameterDefinition]:
return [
QgsProcessingParameterEnum(
self.IN_AVOID_FEATS,
- self.tr("Features to avoid", 'ORSBaseProcessingAlgorithm'),
+ self.tr("Features to avoid", "ORSBaseProcessingAlgorithm"),
AVOID_FEATURES,
defaultValue=None,
optional=True,
- allowMultiple=True
+ allowMultiple=True,
),
QgsProcessingParameterEnum(
self.IN_AVOID_BORDERS,
- self.tr("Types of borders to avoid", 'ORSBaseProcessingAlgorithm'),
+ self.tr("Types of borders to avoid", "ORSBaseProcessingAlgorithm"),
AVOID_BORDERS,
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterString(
self.IN_AVOID_COUNTRIES,
- self.tr("Comma-separated list of ids of countries to avoid", 'ORSBaseProcessingAlgorithm'),
+ self.tr(
+ "Comma-separated list of ids of countries to avoid",
+ "ORSBaseProcessingAlgorithm",
+ ),
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterFeatureSource(
self.IN_AVOID_POLYGONS,
- self.tr("Polygons to avoid", 'ORSBaseProcessingAlgorithm'),
+ self.tr("Polygons to avoid", "ORSBaseProcessingAlgorithm"),
types=[QgsProcessing.TypeVectorPolygon],
- optional=True
- )
+ optional=True,
+ ),
]
@staticmethod
- def _get_ors_client_from_provider(provider: str, feedback: QgsProcessingFeedback) -> client.Client:
+ def _get_ors_client_from_provider(
+ provider: str, feedback: QgsProcessingFeedback
+ ) -> client.Client:
"""
Connects client to provider and returns a client instance for requests to the ors API
"""
- providers = configmanager.read_config()['providers']
+ providers = configmanager.read_config()["providers"]
ors_provider = providers[provider]
ors_client = client.Client(ors_provider)
- ors_client.overQueryLimit.connect(lambda: feedback.reportError("OverQueryLimit: Retrying..."))
+ ors_client.overQueryLimit.connect(
+ lambda: feedback.reportError("OverQueryLimit: Retrying...")
+ )
return ors_client
def parseOptions(self, parameters: dict, context: QgsProcessingContext) -> dict:
@@ -189,19 +205,23 @@ def parseOptions(self, parameters: dict, context: QgsProcessingContext) -> dict:
features_raw = parameters[self.IN_AVOID_FEATS]
if features_raw:
- options['avoid_features'] = [dict(enumerate(AVOID_FEATURES))[feat] for feat in features_raw]
+ options["avoid_features"] = [
+ dict(enumerate(AVOID_FEATURES))[feat] for feat in features_raw
+ ]
borders_raw = parameters[self.IN_AVOID_BORDERS]
if borders_raw:
- options['avoid_borders'] = dict(enumerate(AVOID_BORDERS))[borders_raw]
+ options["avoid_borders"] = dict(enumerate(AVOID_BORDERS))[borders_raw]
countries_raw = parameters[self.IN_AVOID_COUNTRIES]
if countries_raw:
- options['avoid_countries'] = list(map(int, countries_raw.split(',')))
+ options["avoid_countries"] = list(map(int, countries_raw.split(",")))
- polygons_layer = self.parameterAsLayer(parameters, self.IN_AVOID_POLYGONS, context)
+ polygons_layer = self.parameterAsLayer(
+ parameters, self.IN_AVOID_POLYGONS, context
+ )
if polygons_layer:
- options['avoid_polygons'] = _get_avoid_polygons(polygons_layer)
+ options["avoid_polygons"] = _get_avoid_polygons(polygons_layer)
return options
@@ -211,19 +231,26 @@ def initAlgorithm(self, configuration):
Combines default and algorithm parameters and adds them in order to the
algorithm dialog window.
"""
- parameters = [self.provider_parameter(), self.profile_parameter()] + self.PARAMETERS + self.option_parameters() + [self.output_parameter()]
+ parameters = (
+ [self.provider_parameter(), self.profile_parameter()]
+ + self.PARAMETERS
+ + self.option_parameters()
+ + [self.output_parameter()]
+ )
for param in parameters:
if param.name() in ADVANCED_PARAMETERS:
if self.GROUP == "Matrix":
- param.setFlags(param.flags()| QgsProcessingParameterDefinition.FlagHidden)
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.FlagHidden
+ )
else:
# flags() is a wrapper around an enum of ints for type-safety.
# Flags are added by or-ing values, much like the union operator would work
- param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)
+ param.setFlags(
+ param.flags() | QgsProcessingParameterDefinition.FlagAdvanced
+ )
- self.addParameter(
- param
- )
+ self.addParameter(param)
def tr(self, string, context=None):
context = context or self.__class__.__name__
diff --git a/ORStools/proc/directions_lines_proc.py b/ORStools/proc/directions_lines_proc.py
index c39dfff2..07c16643 100644
--- a/ORStools/proc/directions_lines_proc.py
+++ b/ORStools/proc/directions_lines_proc.py
@@ -27,14 +27,15 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsCoordinateReferenceSystem,
- QgsProcessing,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterEnum,
- QgsPointXY,
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsCoordinateReferenceSystem,
+ QgsProcessing,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterEnum,
+ QgsPointXY,
+)
from ORStools.common import directions_core, PROFILES, PREFERENCES, OPTIMIZATION_MODES
from ORStools.utils import transform, exceptions, logger
@@ -45,9 +46,10 @@
# noinspection PyPep8Naming
class ORSDirectionsLinesAlgorithm(ORSBaseProcessingAlgorithm):
"""Algorithm class for Directions Lines."""
+
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'directions_from_polylines_layer'
+ self.ALGO_NAME = "directions_from_polylines_layer"
self.GROUP = "Directions"
self.IN_LINES = "INPUT_LINE_LAYER"
self.IN_FIELD = "INPUT_LAYER_FIELD"
@@ -71,7 +73,7 @@ def __init__(self):
self.IN_PREFERENCE,
self.tr("Travel preference"),
PREFERENCES,
- defaultValue=PREFERENCES[0]
+ defaultValue=PREFERENCES[0],
),
QgsProcessingParameterEnum(
self.IN_OPTIMIZE,
@@ -79,11 +81,13 @@ def __init__(self):
OPTIMIZATION_MODES,
defaultValue=None,
optional=True,
- )
+ ),
]
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
@@ -94,11 +98,7 @@ def processAlgorithm(self, parameters, context, feedback):
options = self.parseOptions(parameters, context)
# Get parameter values
- source = self.parameterAsSource(
- parameters,
- self.IN_LINES,
- context
- )
+ source = self.parameterAsSource(parameters, self.IN_LINES, context)
# parameters[self.IN_FIELD] returns a PyQt5.QtCore.QVariant with "NULL" as content
# in case of absence of self.IN_FIELD.
@@ -111,18 +111,25 @@ def processAlgorithm(self, parameters, context, feedback):
get_fields_options = dict()
if source_field_name:
get_fields_options.update(
- from_type=source.fields().field(source_field_name).type(),
- from_name=source_field_name
- )
+ from_type=source.fields().field(source_field_name).type(),
+ from_name=source_field_name,
+ )
sink_fields = directions_core.get_fields(**get_fields_options, line=True)
- (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context, sink_fields,
- source.wkbType(),
- QgsCoordinateReferenceSystem.fromEpsgId(4326))
+ (sink, dest_id) = self.parameterAsSink(
+ parameters,
+ self.OUT,
+ context,
+ sink_fields,
+ source.wkbType(),
+ QgsCoordinateReferenceSystem.fromEpsgId(4326),
+ )
count = source.featureCount()
- for num, (line, field_value) in enumerate(self._get_sorted_lines(source, source_field_name)):
+ for num, (line, field_value) in enumerate(
+ self._get_sorted_lines(source, source_field_name)
+ ):
# Stop the algorithm if cancel button has been clicked
if feedback.isCanceled():
break
@@ -130,26 +137,31 @@ def processAlgorithm(self, parameters, context, feedback):
try:
if optimization_mode is not None:
params = get_params_optimize(line, profile, optimization_mode)
- response = ors_client.request('/optimization', {}, post_json=params)
+ response = ors_client.request("/optimization", {}, post_json=params)
- sink.addFeature(directions_core.get_output_features_optimization(
- response,
- profile,
- from_value=field_value
- ))
+ sink.addFeature(
+ directions_core.get_output_features_optimization(
+ response, profile, from_value=field_value
+ )
+ )
else:
- params = directions_core.build_default_parameters(preference, point_list=line, options=options)
- response = ors_client.request('/v2/directions/' + profile + '/geojson', {}, post_json=params)
-
- sink.addFeature(directions_core.get_output_feature_directions(
- response,
- profile,
- preference,
- from_value=field_value
- ))
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
+ params = directions_core.build_default_parameters(
+ preference, point_list=line, options=options
+ )
+ response = ors_client.request(
+ "/v2/directions/" + profile + "/geojson", {}, post_json=params
+ )
+
+ sink.addFeature(
+ directions_core.get_output_feature_directions(
+ response, profile, preference, from_value=field_value
+ )
+ )
+ except (
+ exceptions.ApiError,
+ exceptions.InvalidKey,
+ exceptions.GenericServerError,
+ ) as e:
msg = f"Feature ID {num} caused a {e.__class__.__name__}:\n{str(e)}"
feedback.reportError(msg)
logger.log(msg)
@@ -181,10 +193,16 @@ def _get_sorted_lines(layer, field_name):
if QgsWkbTypes.flatType(layer.wkbType()) == QgsWkbTypes.MultiLineString:
# TODO: only takes the first polyline geometry from the multiline geometry currently
# Loop over all polyline geometries
- line = [x_former.transform(QgsPointXY(point)) for point in feat.geometry().asMultiPolyline()[0]]
+ line = [
+ x_former.transform(QgsPointXY(point))
+ for point in feat.geometry().asMultiPolyline()[0]
+ ]
elif QgsWkbTypes.flatType(layer.wkbType()) == QgsWkbTypes.LineString:
- line = [x_former.transform(QgsPointXY(point)) for point in feat.geometry().asPolyline()]
+ line = [
+ x_former.transform(QgsPointXY(point))
+ for point in feat.geometry().asPolyline()
+ ]
yield line, field_value
diff --git a/ORStools/proc/directions_points_layer_proc.py b/ORStools/proc/directions_points_layer_proc.py
index 2c7fddb0..134aa483 100644
--- a/ORStools/proc/directions_points_layer_proc.py
+++ b/ORStools/proc/directions_points_layer_proc.py
@@ -27,14 +27,15 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsCoordinateReferenceSystem,
- QgsProcessing,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterEnum,
- QgsPointXY,
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsCoordinateReferenceSystem,
+ QgsProcessing,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterEnum,
+ QgsPointXY,
+)
from ORStools.common import directions_core, PROFILES, PREFERENCES, OPTIMIZATION_MODES
from ORStools.utils import transform, exceptions, logger
@@ -48,7 +49,7 @@ class ORSDirectionsPointsLayerAlgo(ORSBaseProcessingAlgorithm):
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'directions_from_points_1_layer'
+ self.ALGO_NAME = "directions_from_points_1_layer"
self.GROUP = "Directions"
self.IN_POINTS = "INPUT_POINT_LAYER"
self.IN_FIELD = "INPUT_LAYER_FIELD"
@@ -67,20 +68,20 @@ def __init__(self):
description=self.tr("Layer ID Field"),
parentLayerParameterName=self.IN_POINTS,
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterField(
name=self.IN_SORTBY,
description=self.tr("Sort Points by"),
parentLayerParameterName=self.IN_POINTS,
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterEnum(
self.IN_PREFERENCE,
self.tr("Travel preference"),
PREFERENCES,
- defaultValue=PREFERENCES[0]
+ defaultValue=PREFERENCES[0],
),
QgsProcessingParameterEnum(
self.IN_OPTIMIZE,
@@ -88,11 +89,13 @@ def __init__(self):
OPTIMIZATION_MODES,
defaultValue=None,
optional=True,
- )
+ ),
]
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
@@ -103,33 +106,37 @@ def processAlgorithm(self, parameters, context, feedback):
options = self.parseOptions(parameters, context)
# Get parameter values
- source = self.parameterAsSource(
- parameters,
- self.IN_POINTS,
- context
- )
+ source = self.parameterAsSource(parameters, self.IN_POINTS, context)
source_field_name = parameters[self.IN_FIELD]
get_fields_options = dict()
if source_field_name:
get_fields_options.update(
from_type=source.fields().field(source_field_name).type(),
- from_name=source_field_name
+ from_name=source_field_name,
)
sink_fields = directions_core.get_fields(**get_fields_options, line=True)
- (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context,
- sink_fields,
- QgsWkbTypes.LineString,
- QgsCoordinateReferenceSystem.fromEpsgId(4326))
+ (sink, dest_id) = self.parameterAsSink(
+ parameters,
+ self.OUT,
+ context,
+ sink_fields,
+ QgsWkbTypes.LineString,
+ QgsCoordinateReferenceSystem.fromEpsgId(4326),
+ )
sort_by = parameters[self.IN_SORTBY]
if sort_by:
- def sort(f): return f.attribute(sort_by)
+
+ def sort(f):
+ return f.attribute(sort_by)
else:
- def sort(f): return f.id()
+
+ def sort(f):
+ return f.id()
count = source.featureCount()
@@ -150,7 +157,9 @@ def sort(f): return f.id()
for point in feat.geometry().asMultiPoint():
points.append(x_former.transform(QgsPointXY(point)))
input_points.append(points)
- from_values.append(feat[source_field_name] if source_field_name else None)
+ from_values.append(
+ feat[source_field_name] if source_field_name else None
+ )
for num, (points, from_value) in enumerate(zip(input_points, from_values)):
# Stop the algorithm if cancel button has been clicked
@@ -160,26 +169,31 @@ def sort(f): return f.id()
try:
if optimization_mode is not None:
params = get_params_optimize(points, profile, optimization_mode)
- response = ors_client.request('/optimization', {}, post_json=params)
+ response = ors_client.request("/optimization", {}, post_json=params)
- sink.addFeature(directions_core.get_output_features_optimization(
- response,
- profile,
- from_value=from_value
- ))
+ sink.addFeature(
+ directions_core.get_output_features_optimization(
+ response, profile, from_value=from_value
+ )
+ )
else:
- params = directions_core.build_default_parameters(preference, point_list=points, options=options)
- response = ors_client.request('/v2/directions/' + profile + '/geojson', {}, post_json=params)
-
- sink.addFeature(directions_core.get_output_feature_directions(
- response,
- profile,
- preference,
- from_value=from_value
- ))
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
+ params = directions_core.build_default_parameters(
+ preference, point_list=points, options=options
+ )
+ response = ors_client.request(
+ "/v2/directions/" + profile + "/geojson", {}, post_json=params
+ )
+
+ sink.addFeature(
+ directions_core.get_output_feature_directions(
+ response, profile, preference, from_value=from_value
+ )
+ )
+ except (
+ exceptions.ApiError,
+ exceptions.InvalidKey,
+ exceptions.GenericServerError,
+ ) as e:
msg = f"Feature ID {from_value} caused a {e.__class__.__name__}:\n{str(e)}"
feedback.reportError(msg)
logger.log(msg)
diff --git a/ORStools/proc/directions_points_layers_proc.py b/ORStools/proc/directions_points_layers_proc.py
index 08a293b1..cae81049 100644
--- a/ORStools/proc/directions_points_layers_proc.py
+++ b/ORStools/proc/directions_points_layers_proc.py
@@ -27,13 +27,14 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsCoordinateReferenceSystem,
- QgsProcessing,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterEnum,
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsCoordinateReferenceSystem,
+ QgsProcessing,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterEnum,
+)
from ORStools.common import directions_core, PROFILES, PREFERENCES
from ORStools.utils import transform, exceptions, logger
@@ -42,12 +43,11 @@
# noinspection PyPep8Naming
class ORSDirectionsPointsLayersAlgo(ORSBaseProcessingAlgorithm):
-
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'directions_from_points_2_layers'
+ self.ALGO_NAME = "directions_from_points_2_layers"
self.GROUP = "Directions"
- self.MODE_SELECTION: list = ['Row-by-Row', 'All-by-All']
+ self.MODE_SELECTION: list = ["Row-by-Row", "All-by-All"]
self.IN_START = "INPUT_START_LAYER"
self.IN_START_FIELD = "INPUT_START_FIELD"
self.IN_SORT_START_BY = "INPUT_SORT_START_BY"
@@ -74,7 +74,7 @@ def __init__(self):
description=self.tr("Sort Start Points by"),
parentLayerParameterName=self.IN_START,
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterFeatureSource(
name=self.IN_END,
@@ -93,26 +93,28 @@ def __init__(self):
description=self.tr("Sort End Points by"),
parentLayerParameterName=self.IN_END,
defaultValue=None,
- optional=True
+ optional=True,
),
QgsProcessingParameterEnum(
self.IN_PREFERENCE,
self.tr("Travel preference"),
PREFERENCES,
- defaultValue=PREFERENCES[0]
+ defaultValue=PREFERENCES[0],
),
QgsProcessingParameterEnum(
self.IN_MODE,
self.tr("Layer mode"),
self.MODE_SELECTION,
- defaultValue=self.MODE_SELECTION[0]
- )
+ defaultValue=self.MODE_SELECTION[0],
+ ),
]
# TODO: preprocess parameters to options the range cleanup below:
# https://www.qgis.org/pyqgis/master/core/Processing/QgsProcessingAlgorithm.html#qgis.core.QgsProcessingAlgorithm.preprocessParameters
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
@@ -123,44 +125,45 @@ def processAlgorithm(self, parameters, context, feedback):
options = self.parseOptions(parameters, context)
# Get parameter values
- source = self.parameterAsSource(
- parameters,
- self.IN_START,
- context
- )
+ source = self.parameterAsSource(parameters, self.IN_START, context)
source_field_name = parameters[self.IN_START_FIELD]
- source_field = source.fields().field(source_field_name) if source_field_name else None
+ source_field = (
+ source.fields().field(source_field_name) if source_field_name else None
+ )
sort_start_by = parameters[self.IN_SORT_START_BY]
if sort_start_by:
- def sort_start(f): return f.attribute(sort_start_by)
+
+ def sort_start(f):
+ return f.attribute(sort_start_by)
else:
- def sort_start(f): return f.id()
- destination = self.parameterAsSource(
- parameters,
- self.IN_END,
- context
- )
+ def sort_start(f):
+ return f.id()
+
+ destination = self.parameterAsSource(parameters, self.IN_END, context)
destination_field_name = parameters[self.IN_END_FIELD]
- destination_field = destination.fields().field(destination_field_name) if destination_field_name else None
+ destination_field = (
+ destination.fields().field(destination_field_name)
+ if destination_field_name
+ else None
+ )
sort_end_by = parameters[self.IN_SORT_END_BY]
if sort_end_by:
- def sort_end(f): return f.attribute(sort_end_by)
+
+ def sort_end(f):
+ return f.attribute(sort_end_by)
else:
- def sort_end(f): return f.id()
+
+ def sort_end(f):
+ return f.id()
route_dict = self._get_route_dict(
- source,
- source_field,
- sort_start,
- destination,
- destination_field,
- sort_end
+ source, source_field, sort_start, destination, destination_field, sort_end
)
- if mode == 'Row-by-Row':
+ if mode == "Row-by-Row":
route_count = min([source.featureCount(), destination.featureCount()])
else:
route_count = source.featureCount() * destination.featureCount()
@@ -173,35 +176,50 @@ def sort_end(f): return f.id()
field_types.update({"to_type": destination_field.type()})
sink_fields = directions_core.get_fields(**field_types)
- (sink, dest_id) = self.parameterAsSink(parameters, self.OUT, context, sink_fields,
- QgsWkbTypes.LineString,
- QgsCoordinateReferenceSystem.fromEpsgId(4326))
+ (sink, dest_id) = self.parameterAsSink(
+ parameters,
+ self.OUT,
+ context,
+ sink_fields,
+ QgsWkbTypes.LineString,
+ QgsCoordinateReferenceSystem.fromEpsgId(4326),
+ )
counter = 0
- for coordinates, values in directions_core.get_request_point_features(route_dict, mode):
+ for coordinates, values in directions_core.get_request_point_features(
+ route_dict, mode
+ ):
# Stop the algorithm if cancel button has been clicked
if feedback.isCanceled():
break
- params = directions_core.build_default_parameters(preference, coordinates=coordinates, options=options)
+ params = directions_core.build_default_parameters(
+ preference, coordinates=coordinates, options=options
+ )
try:
- response = ors_client.request('/v2/directions/' + profile + '/geojson', {}, post_json=params)
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
+ response = ors_client.request(
+ "/v2/directions/" + 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)
continue
- sink.addFeature(directions_core.get_output_feature_directions(
- response,
- profile,
- preference,
- from_value=values[0],
- to_value=values[1]
- ))
+ sink.addFeature(
+ directions_core.get_output_feature_directions(
+ response,
+ profile,
+ preference,
+ from_value=values[0],
+ to_value=values[1],
+ )
+ )
counter += 1
feedback.setProgress(int(100.0 / route_count * counter))
@@ -209,7 +227,9 @@ def sort_end(f): return f.id()
return {self.OUT: dest_id}
@staticmethod
- def _get_route_dict(source, source_field, sort_start, destination, destination_field, sort_end):
+ def _get_route_dict(
+ source, source_field, sort_start, destination, destination_field, sort_end
+ ):
"""
Compute route_dict from input layer.
@@ -231,18 +251,30 @@ def _get_route_dict(source, source_field, sort_start, destination, destination_f
route_dict = dict()
source_feats = sorted(list(source.getFeatures()), key=sort_start)
x_former_source = transform.transformToWGS(source.sourceCrs())
- route_dict['start'] = dict(
- geometries=[x_former_source.transform(feat.geometry().asPoint()) for feat in source_feats],
- values=[feat.attribute(source_field.name()) if source_field else feat.id() for feat in source_feats],
+ route_dict["start"] = dict(
+ geometries=[
+ x_former_source.transform(feat.geometry().asPoint())
+ for feat in source_feats
+ ],
+ values=[
+ feat.attribute(source_field.name()) if source_field else feat.id()
+ for feat in source_feats
+ ],
)
destination_feats = sorted(list(destination.getFeatures()), key=sort_end)
x_former_destination = transform.transformToWGS(destination.sourceCrs())
- route_dict['end'] = dict(
- geometries=[x_former_destination.transform(feat.geometry().asPoint()) for feat in destination_feats],
- values=[feat.attribute(destination_field.name()) if destination_field else feat.id() for feat in
- destination_feats
- ],
+ route_dict["end"] = dict(
+ geometries=[
+ x_former_destination.transform(feat.geometry().asPoint())
+ for feat in destination_feats
+ ],
+ values=[
+ feat.attribute(destination_field.name())
+ if destination_field
+ else feat.id()
+ for feat in destination_feats
+ ],
)
return route_dict
diff --git a/ORStools/proc/isochrones_layer_proc.py b/ORStools/proc/isochrones_layer_proc.py
index 8fdd7367..da0b3849 100644
--- a/ORStools/proc/isochrones_layer_proc.py
+++ b/ORStools/proc/isochrones_layer_proc.py
@@ -27,16 +27,17 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsCoordinateReferenceSystem,
- QgsProcessing,
- QgsProcessingUtils,
- QgsProcessingException,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- QgsProcessingParameterString,
- QgsProcessingParameterEnum
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsCoordinateReferenceSystem,
+ QgsProcessing,
+ QgsProcessingUtils,
+ QgsProcessingException,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFeatureSource,
+ QgsProcessingParameterString,
+ QgsProcessingParameterEnum,
+)
from ORStools.common import isochrones_core, PROFILES, DIMENSIONS
from ORStools.proc.base_processing_algorithm import ORSBaseProcessingAlgorithm
@@ -47,20 +48,20 @@
class ORSIsochronesLayerAlgo(ORSBaseProcessingAlgorithm):
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'isochrones_from_layer'
- self.GROUP = 'Isochrones'
+ self.ALGO_NAME = "isochrones_from_layer"
+ self.GROUP = "Isochrones"
self.IN_POINTS = "INPUT_POINT_LAYER"
self.IN_FIELD = "INPUT_FIELD"
- self.IN_METRIC = 'INPUT_METRIC'
- self.IN_RANGES = 'INPUT_RANGES'
- self.IN_KEY = 'INPUT_APIKEY'
- self.IN_DIFFERENCE = 'INPUT_DIFFERENCE'
+ self.IN_METRIC = "INPUT_METRIC"
+ self.IN_RANGES = "INPUT_RANGES"
+ self.IN_KEY = "INPUT_APIKEY"
+ self.IN_DIFFERENCE = "INPUT_DIFFERENCE"
self.PARAMETERS = [
QgsProcessingParameterFeatureSource(
name=self.IN_POINTS,
description=self.tr("Input Point layer"),
- types=[QgsProcessing.TypeVectorPoint]
+ types=[QgsProcessing.TypeVectorPoint],
),
# QgsProcessingParameterBoolean(
# name=self.IN_DIFFERENCE,
@@ -68,21 +69,23 @@ def __init__(self):
# )
QgsProcessingParameterField(
name=self.IN_FIELD,
- description=self.tr("Input layer ID Field (mutually exclusive with Point option)"),
+ description=self.tr(
+ "Input layer ID Field (mutually exclusive with Point option)"
+ ),
parentLayerParameterName=self.IN_POINTS,
- optional=True
+ optional=True,
),
QgsProcessingParameterEnum(
name=self.IN_METRIC,
description=self.tr("Dimension"),
options=DIMENSIONS,
- defaultValue=DIMENSIONS[0]
+ defaultValue=DIMENSIONS[0],
),
QgsProcessingParameterString(
name=self.IN_RANGES,
description=self.tr("Comma-separated ranges [min or m]"),
- defaultValue="5, 10"
- )
+ defaultValue="5, 10",
+ ),
]
# Save some important references
@@ -95,14 +98,16 @@ def __init__(self):
# TODO: preprocess parameters to options the range cleanup below:
# https://www.qgis.org/pyqgis/master/core/Processing/QgsProcessingAlgorithm.html#qgis.core.QgsProcessingAlgorithm.prepareAlgorithm
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
dimension = dict(enumerate(DIMENSIONS))[parameters[self.IN_METRIC]]
- factor = 60 if dimension == 'time' else 1
+ factor = 60 if dimension == "time" else 1
ranges_raw = parameters[self.IN_RANGES]
- ranges_proc = [x * factor for x in map(int, ranges_raw.split(','))]
+ ranges_proc = [x * factor for x in map(int, ranges_raw.split(","))]
# self.difference = self.parameterAsBool(parameters, self.IN_DIFFERENCE, context)
source = self.parameterAsSource(parameters, self.IN_POINTS, context)
@@ -113,7 +118,8 @@ def processAlgorithm(self, parameters, context, feedback):
requests = []
if QgsWkbTypes.flatType(source.wkbType()) == QgsWkbTypes.MultiPoint:
raise QgsProcessingException(
- "TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer.")
+ "TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer."
+ )
# Get ID field properties
id_field_name = parameters[self.IN_FIELD]
@@ -124,26 +130,34 @@ def processAlgorithm(self, parameters, context, feedback):
self.isochrones.set_parameters(profile, dimension, factor, *parameter_options)
- for locations, id_value in self.get_sorted_feature_parameters(source, id_field_name):
+ for locations, id_value in self.get_sorted_feature_parameters(
+ source, id_field_name
+ ):
# Stop the algorithm if cancel button has been clicked
if feedback.isCanceled():
break
- requests.append({
- "locations": locations,
- "range_type": dimension,
- "range": ranges_proc,
- "attributes": ['total_pop'],
- "id": id_value,
- "options": options
- })
-
- (sink, self.dest_id) = self.parameterAsSink(parameters, self.OUT, context,
- self.isochrones.get_fields(),
- QgsWkbTypes.Polygon,
- # Needs Multipolygon if difference parameter will ever be
- # reactivated
- self.crs_out)
+ requests.append(
+ {
+ "locations": locations,
+ "range_type": dimension,
+ "range": ranges_proc,
+ "attributes": ["total_pop"],
+ "id": id_value,
+ "options": options,
+ }
+ )
+
+ (sink, self.dest_id) = self.parameterAsSink(
+ parameters,
+ self.OUT,
+ context,
+ self.isochrones.get_fields(),
+ QgsWkbTypes.Polygon,
+ # Needs Multipolygon if difference parameter will ever be
+ # reactivated
+ self.crs_out,
+ )
for num, params in enumerate(requests):
if feedback.isCanceled():
@@ -152,14 +166,18 @@ def processAlgorithm(self, parameters, context, feedback):
# If feature causes error, report and continue with next
try:
# Populate features from response
- response = ors_client.request('/v2/isochrones/' + profile, {}, post_json=params)
+ response = ors_client.request(
+ "/v2/isochrones/" + profile, {}, post_json=params
+ )
- for isochrone in self.isochrones.get_features(response, params['id']):
+ for isochrone in self.isochrones.get_features(response, params["id"]):
sink.addFeature(isochrone)
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
+ 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)
@@ -178,7 +196,9 @@ def postProcessAlgorithm(self, context, feedback):
return {self.OUT: self.dest_id}
@staticmethod
- def get_sorted_feature_parameters(layer: QgsProcessingParameterFeatureSource, id_field_name: str):
+ def get_sorted_feature_parameters(
+ layer: QgsProcessingParameterFeatureSource, id_field_name: str
+ ):
"""
Generator to yield geometry and id of features sorted by feature ID. Careful: feat.id() is not necessarily
permanent
diff --git a/ORStools/proc/isochrones_point_proc.py b/ORStools/proc/isochrones_point_proc.py
index f1594ece..a6b1d123 100644
--- a/ORStools/proc/isochrones_point_proc.py
+++ b/ORStools/proc/isochrones_point_proc.py
@@ -27,13 +27,14 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsCoordinateReferenceSystem,
- QgsProcessingUtils,
- QgsProcessingParameterString,
- QgsProcessingParameterEnum,
- QgsProcessingParameterPoint,
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsCoordinateReferenceSystem,
+ QgsProcessingUtils,
+ QgsProcessingParameterString,
+ QgsProcessingParameterEnum,
+ QgsProcessingParameterPoint,
+)
from ORStools.common import isochrones_core, PROFILES, DIMENSIONS
from ORStools.utils import exceptions, logger
@@ -44,30 +45,32 @@
class ORSIsochronesPointAlgo(ORSBaseProcessingAlgorithm):
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'isochrones_from_point'
+ self.ALGO_NAME = "isochrones_from_point"
self.GROUP = "Isochrones"
self.IN_POINT = "INPUT_POINT"
- self.IN_METRIC = 'INPUT_METRIC'
- self.IN_RANGES = 'INPUT_RANGES'
- self.IN_KEY = 'INPUT_APIKEY'
- self.IN_DIFFERENCE = 'INPUT_DIFFERENCE'
+ self.IN_METRIC = "INPUT_METRIC"
+ self.IN_RANGES = "INPUT_RANGES"
+ self.IN_KEY = "INPUT_APIKEY"
+ self.IN_DIFFERENCE = "INPUT_DIFFERENCE"
self.PARAMETERS = [
QgsProcessingParameterPoint(
name=self.IN_POINT,
- description=self.tr("Input Point from map canvas (mutually exclusive with layer option)"),
- optional=True
+ description=self.tr(
+ "Input Point from map canvas (mutually exclusive with layer option)"
+ ),
+ optional=True,
),
QgsProcessingParameterEnum(
name=self.IN_METRIC,
description=self.tr("Dimension"),
options=DIMENSIONS,
- defaultValue=DIMENSIONS[0]
+ defaultValue=DIMENSIONS[0],
),
QgsProcessingParameterString(
name=self.IN_RANGES,
description=self.tr("Comma-separated ranges [min or m]"),
- defaultValue="5, 10"
- )
+ defaultValue="5, 10",
+ ),
]
# Save some important references
@@ -80,14 +83,16 @@ def __init__(self):
# TODO: preprocess parameters to options the range cleanup below:
# https://www.qgis.org/pyqgis/master/core/Processing/QgsProcessingAlgorithm.html#qgis.core.QgsProcessingAlgorithm.preprocessParameters
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
dimension = dict(enumerate(DIMENSIONS))[parameters[self.IN_METRIC]]
- factor = 60 if dimension == 'time' else 1
+ factor = 60 if dimension == "time" else 1
ranges_raw = parameters[self.IN_RANGES]
- ranges_proc = [x * factor for x in map(int, ranges_raw.split(','))]
+ ranges_proc = [x * factor for x in map(int, ranges_raw.split(","))]
options = self.parseOptions(parameters, context)
@@ -100,29 +105,39 @@ def processAlgorithm(self, parameters, context, feedback):
"locations": [[round(point.x(), 6), round(point.y(), 6)]],
"range_type": dimension,
"range": ranges_proc,
- "attributes": ['total_pop'],
+ "attributes": ["total_pop"],
"id": None,
- "options": options
+ "options": options,
}
- (sink, self.dest_id) = self.parameterAsSink(parameters, self.OUT, context,
- self.isochrones.get_fields(),
- QgsWkbTypes.Polygon,
- # Needs Multipolygon if difference parameter will ever be
- # reactivated
- self.crs_out)
+ (sink, self.dest_id) = self.parameterAsSink(
+ parameters,
+ self.OUT,
+ context,
+ self.isochrones.get_fields(),
+ QgsWkbTypes.Polygon,
+ # Needs Multipolygon if difference parameter will ever be
+ # reactivated
+ self.crs_out,
+ )
try:
- response = ors_client.request('/v2/isochrones/' + profile, {}, post_json=params)
+ response = ors_client.request(
+ "/v2/isochrones/" + profile, {}, post_json=params
+ )
# Populate features from response
- for isochrone in self.isochrones.get_features(response, params['id']):
+ for isochrone in self.isochrones.get_features(response, params["id"]):
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)}"
+ 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)
diff --git a/ORStools/proc/matrix_proc.py b/ORStools/proc/matrix_proc.py
index ee7c34a3..2cdedb54 100644
--- a/ORStools/proc/matrix_proc.py
+++ b/ORStools/proc/matrix_proc.py
@@ -27,15 +27,16 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes,
- QgsFeature,
- QgsProcessing,
- QgsFields,
- QgsField,
- QgsProcessingException,
- QgsProcessingParameterField,
- QgsProcessingParameterFeatureSource,
- )
+from qgis.core import (
+ QgsWkbTypes,
+ QgsFeature,
+ QgsProcessing,
+ QgsFields,
+ QgsField,
+ QgsProcessingException,
+ QgsProcessingParameterField,
+ QgsProcessingParameterFeatureSource,
+)
from PyQt5.QtCore import QVariant
@@ -48,7 +49,7 @@
class ORSMatrixAlgo(ORSBaseProcessingAlgorithm):
def __init__(self):
super().__init__()
- self.ALGO_NAME = 'matrix_from_layers'
+ self.ALGO_NAME = "matrix_from_layers"
self.GROUP = "Matrix"
self.IN_START = "INPUT_START_LAYER"
self.IN_START_FIELD = "INPUT_START_FIELD"
@@ -82,7 +83,9 @@ def __init__(self):
]
def processAlgorithm(self, parameters, context, feedback):
- ors_client = self._get_ors_client_from_provider(parameters[self.IN_PROVIDER], feedback)
+ ors_client = self._get_ors_client_from_provider(
+ parameters[self.IN_PROVIDER], feedback
+ )
# Get profile value
profile = dict(enumerate(PROFILES))[parameters[self.IN_PROFILE]]
@@ -91,28 +94,29 @@ def processAlgorithm(self, parameters, context, feedback):
# options = self.parseOptions(parameters, context)
# Get parameter values
- source = self.parameterAsSource(
- parameters,
- self.IN_START,
- context
- )
+ source = self.parameterAsSource(parameters, self.IN_START, context)
source_field_name = parameters[self.IN_START_FIELD]
- source_field = source.fields().field(source_field_name) if source_field_name else None
-
- destination = self.parameterAsSource(
- parameters,
- self.IN_END,
- context
+ source_field = (
+ source.fields().field(source_field_name) if source_field_name else None
)
+
+ destination = self.parameterAsSource(parameters, self.IN_END, context)
destination_field_name = parameters[self.IN_END_FIELD]
- destination_field = destination.fields().field(destination_field_name) if destination_field_name else None
+ destination_field = (
+ destination.fields().field(destination_field_name)
+ if destination_field_name
+ else None
+ )
# Abort when MultiPoint type
- if (QgsWkbTypes.flatType(source.wkbType()) or QgsWkbTypes.flatType(destination.wkbType()))\
- == QgsWkbTypes.MultiPoint:
+ if (
+ QgsWkbTypes.flatType(source.wkbType())
+ or QgsWkbTypes.flatType(destination.wkbType())
+ ) == QgsWkbTypes.MultiPoint:
raise QgsProcessingException(
- "TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer.")
+ "TypeError: Multipoint Layers are not accepted. Please convert to single geometry layer."
+ )
# Get source and destination features
sources_features = list(source.getFeatures())
@@ -122,32 +126,48 @@ def processAlgorithm(self, parameters, context, feedback):
destinations_amount = destination.featureCount()
# Allow for 50 features in source if source == destination
- source_equals_destination = parameters['INPUT_START_LAYER'] == parameters['INPUT_END_LAYER']
+ source_equals_destination = (
+ parameters["INPUT_START_LAYER"] == parameters["INPUT_END_LAYER"]
+ )
if source_equals_destination:
features = sources_features
x_former = transform.transformToWGS(source.sourceCrs())
- features_points = [x_former.transform(feat.geometry().asPoint()) for feat in features]
+ features_points = [
+ x_former.transform(feat.geometry().asPoint()) for feat in features
+ ]
else:
x_former = transform.transformToWGS(source.sourceCrs())
- sources_features_x_formed = [x_former.transform(feat.geometry().asPoint()) for feat in sources_features]
+ sources_features_x_formed = [
+ x_former.transform(feat.geometry().asPoint())
+ for feat in sources_features
+ ]
x_former = transform.transformToWGS(destination.sourceCrs())
- destination_features_x_formed = [x_former.transform(feat.geometry().asPoint()) for feat in
- destination_features]
+ destination_features_x_formed = [
+ x_former.transform(feat.geometry().asPoint())
+ for feat in destination_features
+ ]
features_points = sources_features_x_formed + destination_features_x_formed
# Get IDs
- sources_ids = list(range(sources_amount)) if source_equals_destination else list(range(sources_amount))
- destination_ids = list(range(sources_amount)) if source_equals_destination else list(
- range(sources_amount, sources_amount + destinations_amount))
+ sources_ids = (
+ list(range(sources_amount))
+ if source_equals_destination
+ else list(range(sources_amount))
+ )
+ destination_ids = (
+ list(range(sources_amount))
+ if source_equals_destination
+ else list(range(sources_amount, sources_amount + destinations_amount))
+ )
params = {
- 'locations': [[point.x(), point.y()] for point in features_points],
- 'sources': sources_ids,
- 'destinations': destination_ids,
- 'metrics': ["duration", "distance"],
- 'id': 'Matrix'
+ "locations": [[point.x(), point.y()] for point in features_points],
+ "sources": sources_ids,
+ "destinations": destination_ids,
+ "metrics": ["duration", "distance"],
+ "id": "Matrix",
# 'options': options
}
@@ -162,39 +182,45 @@ def processAlgorithm(self, parameters, context, feedback):
# Make request and catch ApiError
try:
- response = ors_client.request('/v2/matrix/' + profile, {}, post_json=params)
+ response = ors_client.request("/v2/matrix/" + profile, {}, post_json=params)
- except (exceptions.ApiError,
- exceptions.InvalidKey,
- exceptions.GenericServerError) as e:
+ 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.NoGeometry
+ parameters, self.OUT, context, sink_fields, QgsWkbTypes.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]
+ 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
- ])
+ 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)
@@ -204,7 +230,6 @@ def processAlgorithm(self, parameters, context, feedback):
# Change to be consistent
@staticmethod
def get_fields(source_type=QVariant.Int, destination_type=QVariant.Int):
-
fields = QgsFields()
fields.append(QgsField("FROM_ID", source_type))
fields.append(QgsField("TO_ID", destination_type))
diff --git a/ORStools/proc/provider.py b/ORStools/proc/provider.py
index e72368be..be8e6335 100644
--- a/ORStools/proc/provider.py
+++ b/ORStools/proc/provider.py
@@ -41,11 +41,9 @@
class ORStoolsProvider(QgsProcessingProvider):
-
def __init__(self):
QgsProcessingProvider.__init__(self)
-
def unload(self):
"""
Unloads the provider. Any tear-down steps required by the provider
@@ -68,7 +66,7 @@ def loadAlgorithms(self):
@staticmethod
def icon():
- return QIcon(RESOURCE_PREFIX + 'icon_orstools.png')
+ return QIcon(RESOURCE_PREFIX + "icon_orstools.png")
@staticmethod
def id():
@@ -98,4 +96,4 @@ def longName():
(version 2.2.1)". This string should be localised. The default
implementation returns the same string as name().
"""
- return PLUGIN_NAME + ' plugin v' + __version__
+ return PLUGIN_NAME + " plugin v" + __version__
diff --git a/ORStools/utils/configmanager.py b/ORStools/utils/configmanager.py
index c84e87f6..26e4d614 100644
--- a/ORStools/utils/configmanager.py
+++ b/ORStools/utils/configmanager.py
@@ -53,7 +53,7 @@ def write_config(new_config):
:param new_config: new provider settings after altering in dialog.
:type new_config: dict
"""
- with open(CONFIG_PATH, 'w') as f:
+ with open(CONFIG_PATH, "w") as f:
yaml.safe_dump(new_config, f)
diff --git a/ORStools/utils/convert.py b/ORStools/utils/convert.py
index addd763f..3e267a2a 100644
--- a/ORStools/utils/convert.py
+++ b/ORStools/utils/convert.py
@@ -51,7 +51,7 @@ def decode_polyline(polyline, is3d=False):
index += 1
result += b << shift
shift += 5
- if b < 0x1f:
+ if b < 0x1F:
break
lat += (~result >> 1) if (result & 1) != 0 else (result >> 1)
@@ -62,7 +62,7 @@ def decode_polyline(polyline, is3d=False):
index += 1
result += b << shift
shift += 5
- if b < 0x1f:
+ if b < 0x1F:
break
lng += ~(result >> 1) if (result & 1) != 0 else (result >> 1)
@@ -74,14 +74,16 @@ def decode_polyline(polyline, is3d=False):
index += 1
result += b << shift
shift += 5
- if b < 0x1f:
+ if b < 0x1F:
break
if (result & 1) != 0:
z += ~(result >> 1)
else:
- z += (result >> 1)
+ z += result >> 1
- points.append([round(lng * 1e-5, 6), round(lat * 1e-5, 6), round(z * 1e-2, 1)])
+ points.append(
+ [round(lng * 1e-5, 6), round(lat * 1e-5, 6), round(z * 1e-2, 1)]
+ )
else:
points.append([round(lng * 1e-5, 6), round(lat * 1e-5, 6)])
diff --git a/ORStools/utils/exceptions.py b/ORStools/utils/exceptions.py
index 5828688b..ac8fe7d7 100644
--- a/ORStools/utils/exceptions.py
+++ b/ORStools/utils/exceptions.py
@@ -34,6 +34,7 @@
class ApiError(Exception):
"""Represents an exception returned by the remote API."""
+
def __init__(self, status, message=None):
self.status = status
self.message = message
@@ -47,6 +48,7 @@ def __str__(self):
class InvalidKey(Exception):
"""only called for 403"""
+
def __init__(self, status, message):
self.status = status
self.message = message
@@ -74,6 +76,7 @@ def __str__(self):
class Timeout(Exception):
"""The request timed out."""
+
pass
diff --git a/ORStools/utils/logger.py b/ORStools/utils/logger.py
index 6fdaeca7..cd6bf301 100644
--- a/ORStools/utils/logger.py
+++ b/ORStools/utils/logger.py
@@ -31,6 +31,7 @@
from ORStools import PLUGIN_NAME
+
def log(message, level_in=0):
"""
Writes to QGIS inbuilt logger accessible through panel.
diff --git a/ORStools/utils/maptools.py b/ORStools/utils/maptools.py
index ce9f94ef..ae347d0b 100644
--- a/ORStools/utils/maptools.py
+++ b/ORStools/utils/maptools.py
@@ -27,9 +27,8 @@
***************************************************************************/
"""
-from qgis.core import (QgsWkbTypes)
-from qgis.gui import (QgsMapToolEmitPoint,
- QgsRubberBand)
+from qgis.core import QgsWkbTypes
+from qgis.gui import QgsMapToolEmitPoint, QgsRubberBand
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtGui import QColor
@@ -48,7 +47,9 @@ def __init__(self, canvas):
self.canvas = canvas
QgsMapToolEmitPoint.__init__(self, self.canvas)
- self.rubberBand = QgsRubberBand(mapCanvas=self.canvas, geometryType=QgsWkbTypes.LineGeometry)
+ self.rubberBand = QgsRubberBand(
+ mapCanvas=self.canvas, geometryType=QgsWkbTypes.LineGeometry
+ )
self.rubberBand.setStrokeColor(QColor(DEFAULT_COLOR))
self.rubberBand.setWidth(3)
diff --git a/ORStools/utils/processing.py b/ORStools/utils/processing.py
index 93bf09ca..4b619097 100644
--- a/ORStools/utils/processing.py
+++ b/ORStools/utils/processing.py
@@ -35,7 +35,9 @@
from ORStools.common import OPTIMIZATION_MODES
-def get_params_optimize(point_list: List[QgsPointXY], ors_profile: str, mode: int) -> dict:
+def get_params_optimize(
+ point_list: List[QgsPointXY], ors_profile: str, mode: int
+) -> dict:
"""
Build parameters for optimization endpoint
@@ -55,10 +57,7 @@ def get_params_optimize(point_list: List[QgsPointXY], ors_profile: str, mode: in
elif mode == OPTIMIZATION_MODES.index("Round Trip"):
start = end = point_list.pop(0)
- vehicle = {
- "id": 0,
- "profile": ors_profile
- }
+ vehicle = {"id": 0, "profile": ors_profile}
if start:
vehicle.update({"start": [round(start.x(), 6), round(start.y(), 6)]})
@@ -66,12 +65,15 @@ def get_params_optimize(point_list: List[QgsPointXY], ors_profile: str, mode: in
vehicle.update({"end": [round(end.x(), 6), round(end.y(), 6)]})
params = {
- 'jobs': [{
- "location": [round(point.x(), 6), round(point.y(), 6)],
- "id": point_list.index(point)
- } for point in point_list],
- 'vehicles': [vehicle],
- 'options': {'g': True}
+ "jobs": [
+ {
+ "location": [round(point.x(), 6), round(point.y(), 6)],
+ "id": point_list.index(point),
+ }
+ for point in point_list
+ ],
+ "vehicles": [vehicle],
+ "options": {"g": True},
}
return params
@@ -82,19 +84,15 @@ def read_help_file(algorithm: str, locale: str = ""):
Returns the contents of a file from the help folder
:rtype: str
"""
- extension = '_' + locale if locale else ''
+ extension = "_" + locale if locale else ""
- i18n_file = os.path.join(
- BASE_DIR,
- 'help',
- f"{algorithm}{extension}.help"
- )
+ i18n_file = os.path.join(BASE_DIR, "help", f"{algorithm}{extension}.help")
- file = i18n_file if os.path.isfile(i18n_file) else os.path.join(
- BASE_DIR,
- 'help',
- f"{algorithm}.help"
+ file = (
+ i18n_file
+ if os.path.isfile(i18n_file)
+ else os.path.join(BASE_DIR, "help", f"{algorithm}.help")
)
- with open(file, encoding='utf-8') as help_file:
+ with open(file, encoding="utf-8") as help_file:
msg = help_file.read()
return msg
diff --git a/ORStools/utils/transform.py b/ORStools/utils/transform.py
index c5d08ce3..cd603e02 100644
--- a/ORStools/utils/transform.py
+++ b/ORStools/utils/transform.py
@@ -27,10 +27,7 @@
***************************************************************************/
"""
-from qgis.core import (QgsCoordinateReferenceSystem,
- QgsCoordinateTransform,
- QgsProject
- )
+from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject
def transformToWGS(old_crs):