diff --git a/valhalla/common/matrix_core.py b/valhalla/common/matrix_core.py index 2f94b4a..657d6a9 100644 --- a/valhalla/common/matrix_core.py +++ b/valhalla/common/matrix_core.py @@ -29,7 +29,11 @@ from qgis.core import (QgsFeature, QgsFields, - QgsField) + QgsField, + QgsPointXY, + QgsGeometry) + +from valhalla.utils import convert, logger def get_fields(from_type=QVariant.String, to_type=QVariant.String, from_name="FROM_ID", to_name="TO_ID"): @@ -63,7 +67,7 @@ def get_fields(from_type=QVariant.String, to_type=QVariant.String, from_name="FR return fields -def get_output_features_matrix(response, profile, options={}, source_attrs=[], destination_attrs=[]): +def get_output_features_matrix(response, profile, options={}, matrix_geometries=False, source_attrs=[], destination_attrs=[]): """ Build output feature based on response attributes for directions endpoint. @@ -76,19 +80,22 @@ def get_output_features_matrix(response, profile, options={}, source_attrs=[], d :param options: Costing options being used. :type options: dict + :param matrix_geometries: Whether we want geometries for each connection. + :type matrix_geometries: bool + :param source_attrs: Attribute values of the source features. :type source_attrs: list of any :param destination_attrs: Attribute values of the destination features. :type destination_attrs: list of any - :returns: Ouput features with attributes and geometry set. + :returns: Output features with attributes and geometry set. :rtype: list of QgsFeature """ feats = [] - sources = response['sources'][0] - targets = response['targets'][0] + sources = response['sources'] + targets = response['targets'] for o, origin in enumerate(response['sources_to_targets']): try: from_id = source_attrs[o] @@ -117,6 +124,10 @@ def get_output_features_matrix(response, profile, options={}, source_attrs=[], d json.dumps(options), ] ) + if matrix_geometries and destination.get("shape"): + shape = destination.get("shape", "") + qgis_coords = [QgsPointXY(x, y) for y, x in convert.decode_polyline6(shape)] + feat.setGeometry(QgsGeometry.fromPolylineXY(qgis_coords)) feats.append(feat) return feats diff --git a/valhalla/gui/ValhallaDialog.py b/valhalla/gui/ValhallaDialog.py index 67e23d5..7d6b5d8 100644 --- a/valhalla/gui/ValhallaDialog.py +++ b/valhalla/gui/ValhallaDialog.py @@ -364,22 +364,28 @@ def run_gui_control(self): self.project.addMapLayer(point_layer) elif method == 'sources_to_targets': - layer_out = QgsVectorLayer("None", 'Matrix_Valhalla', "memory") + matrix_geometries = self.dlg.matrix_geometries.isChecked() + layer_out = QgsVectorLayer("LineString?crs=EPSG:4326" if matrix_geometries else "None", f'Matrix {profile.capitalize()}', "memory") layer_out.dataProvider().addAttributes(matrix_core.get_fields()) layer_out.updateFields() matrix = matrix_gui.Matrix(self.dlg) params = matrix.get_parameters() params.update(extra_params) + params.update(time_params) + if matrix_geometries: + params["shape_format"] = "polyline6" response = clnt.request('/sources_to_targets', post_json=params) feats = matrix_core.get_output_features_matrix( response, profile, - matrix.costing_options + matrix.costing_options, + matrix_geometries ) for feat in feats: layer_out.dataProvider().addFeature(feat) + layer_out.updateExtents() self.project.addMapLayer(layer_out) elif method == 'locate': diff --git a/valhalla/gui/ValhallaDialogConfigUI_ui.py b/valhalla/gui/ValhallaDialogConfigUI_ui.py index 6476bcf..1e1a078 100644 --- a/valhalla/gui/ValhallaDialogConfigUI_ui.py +++ b/valhalla/gui/ValhallaDialogConfigUI_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'valhalla/gui/ValhallaDialogConfigUI.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. diff --git a/valhalla/gui/ValhallaDialogUI.ui b/valhalla/gui/ValhallaDialogUI.ui index 981fe6c..313232c 100644 --- a/valhalla/gui/ValhallaDialogUI.ui +++ b/valhalla/gui/ValhallaDialogUI.ui @@ -548,7 +548,7 @@ Method configuration - true + false false @@ -762,6 +762,35 @@ + + + + Matrix + + + true + + + + + + Geometries + + + + + + + + + + true + + + + + + @@ -829,6 +858,9 @@ Locate + + true + @@ -1838,7 +1870,7 @@ 16777215 - 27 + 24 @@ -1943,7 +1975,7 @@ 16777215 - 27 + 24 diff --git a/valhalla/gui/ValhallaDialogUI_ui.py b/valhalla/gui/ValhallaDialogUI_ui.py index 70e1d2b..7d0987c 100644 --- a/valhalla/gui/ValhallaDialogUI_ui.py +++ b/valhalla/gui/ValhallaDialogUI_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'valhalla/gui/ValhallaDialogUI.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -267,7 +267,7 @@ def setupUi(self, ValhallaDialogBase): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.mGroupBox_11.sizePolicy().hasHeightForWidth()) self.mGroupBox_11.setSizePolicy(sizePolicy) - self.mGroupBox_11.setCollapsed(True) + self.mGroupBox_11.setCollapsed(False) self.mGroupBox_11.setSaveCollapsedState(False) self.mGroupBox_11.setObjectName("mGroupBox_11") self.verticalLayout_9 = QtWidgets.QVBoxLayout(self.mGroupBox_11) @@ -332,6 +332,20 @@ def setupUi(self, ValhallaDialogBase): self.contours_distance.setObjectName("contours_distance") self.gridLayout_16.addWidget(self.contours_distance, 3, 1, 1, 5) self.verticalLayout_9.addWidget(self.isochrone_group) + self.matrix_group = gui.QgsCollapsibleGroupBox(self.mGroupBox_11) + self.matrix_group.setCollapsed(True) + self.matrix_group.setObjectName("matrix_group") + self.formLayout_3 = QtWidgets.QFormLayout(self.matrix_group) + self.formLayout_3.setObjectName("formLayout_3") + self.label_51 = QtWidgets.QLabel(self.matrix_group) + self.label_51.setObjectName("label_51") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_51) + self.matrix_geometries = QtWidgets.QCheckBox(self.matrix_group) + self.matrix_geometries.setText("") + self.matrix_geometries.setChecked(True) + self.matrix_geometries.setObjectName("matrix_geometries") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.matrix_geometries) + self.verticalLayout_9.addWidget(self.matrix_group) self.mGroupBox_12 = gui.QgsCollapsibleGroupBox(self.mGroupBox_11) self.mGroupBox_12.setCollapsed(True) self.mGroupBox_12.setObjectName("mGroupBox_12") @@ -351,6 +365,7 @@ def setupUi(self, ValhallaDialogBase): self.verticalLayout.addWidget(self.widget_3) self.verticalLayout_9.addWidget(self.mGroupBox_12) self.mGroupBox_13 = gui.QgsCollapsibleGroupBox(self.mGroupBox_11) + self.mGroupBox_13.setCollapsed(True) self.mGroupBox_13.setObjectName("mGroupBox_13") self.formLayout = QtWidgets.QFormLayout(self.mGroupBox_13) self.formLayout.setObjectName("formLayout") @@ -838,7 +853,7 @@ def setupUi(self, ValhallaDialogBase): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.avoidlocation_group.sizePolicy().hasHeightForWidth()) self.avoidlocation_group.setSizePolicy(sizePolicy) - self.avoidlocation_group.setMaximumSize(QtCore.QSize(16777215, 27)) + self.avoidlocation_group.setMaximumSize(QtCore.QSize(16777215, 24)) self.avoidlocation_group.setCheckable(True) self.avoidlocation_group.setChecked(False) self.avoidlocation_group.setCollapsed(True) @@ -877,7 +892,7 @@ def setupUi(self, ValhallaDialogBase): sizePolicy.setHeightForWidth(self.valhalla_log_group.sizePolicy().hasHeightForWidth()) self.valhalla_log_group.setSizePolicy(sizePolicy) self.valhalla_log_group.setMinimumSize(QtCore.QSize(0, 0)) - self.valhalla_log_group.setMaximumSize(QtCore.QSize(16777215, 27)) + self.valhalla_log_group.setMaximumSize(QtCore.QSize(16777215, 24)) self.valhalla_log_group.setFlat(True) self.valhalla_log_group.setCollapsed(True) self.valhalla_log_group.setSaveCollapsedState(False) @@ -1017,6 +1032,8 @@ def retranslateUi(self, ValhallaDialogBase): self.contours_distance.setStatusTip(_translate("ValhallaDialogBase", "Comma separated list of decimal distances in kilometers for the contours.")) self.contours_distance.setWhatsThis(_translate("ValhallaDialogBase", "Comma separated list of decimal distances in kilometers for the contours.")) self.contours_distance.setPlaceholderText(_translate("ValhallaDialogBase", "Experimental")) + self.matrix_group.setTitle(_translate("ValhallaDialogBase", "Matrix")) + self.label_51.setText(_translate("ValhallaDialogBase", "Geometries")) self.mGroupBox_12.setToolTip(_translate("ValhallaDialogBase", "Extract OSM roads and their attributes from a locally available OSM PBF file. Needs osmium!!")) self.mGroupBox_12.setStatusTip(_translate("ValhallaDialogBase", "Extract OSM roads and their attributes from a locally available OSM PBF file. Needs osmium!!")) self.mGroupBox_12.setWhatsThis(_translate("ValhallaDialogBase", "Extract OSM roads and their attributes from a locally available OSM PBF file. Needs osmium!!")) diff --git a/valhalla/gui/ValhallaExtraParamsDialogUI_ui.py b/valhalla/gui/ValhallaExtraParamsDialogUI_ui.py index 12b0587..538d9c1 100644 --- a/valhalla/gui/ValhallaExtraParamsDialogUI_ui.py +++ b/valhalla/gui/ValhallaExtraParamsDialogUI_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'valhalla/gui/ValhallaExtraParamsDialogUI.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. diff --git a/valhalla/gui/ValhallaLocateDialog_ui.py b/valhalla/gui/ValhallaLocateDialog_ui.py index 1beb39c..93ee805 100644 --- a/valhalla/gui/ValhallaLocateDialog_ui.py +++ b/valhalla/gui/ValhallaLocateDialog_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'valhalla/gui/ValhallaLocateDialog.ui' # -# Created by: PyQt5 UI code generator 5.15.9 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. diff --git a/valhalla/metadata.txt b/valhalla/metadata.txt index a35a89b..644833e 100644 --- a/valhalla/metadata.txt +++ b/valhalla/metadata.txt @@ -4,7 +4,7 @@ qgisMinimumVersion=3.0 qgisMaximumVersion=3.99 description=Valhalla routing, isochrones and matrix calculations for QGIS -version=2.4.2 +version=2.5.0 author=GIS • OPS UG email=nils@gis-ops.com @@ -18,7 +18,8 @@ about=Valhalla provides access to most of the functions of the open-source Valha The plugin accesses remote or local Valhalla HTTP APIs. If you want to quickly get a local setup, try our Valhalla Docker image: https://github.com/gis-ops/docker-valhalla. -changelog=2023/04/29 v2.4.2 Fix some datetime & processing stuff, CAREFUL: will remove iso poly styling +changelog=2024/03/26 v2.5.0 Fix matrix & add optional matrix geometries and time options + 2023/04/29 v2.4.2 Fix some datetime & processing stuff, CAREFUL: will remove iso poly styling 2023/04/29 v2.4.1 Bit more logging for isochrone processing algos 2023/03/12 v2.4.0 Add multimodal routing for debugging; fix time options 2022/12/28 v2.3.0 Implement centroid endpoint; fixed #2; avoid_polygons; enhance /locate to accept heading & radius; add time options diff --git a/valhalla/proc/matrix/matrix_auto.py b/valhalla/proc/matrix/matrix_auto.py index 0c15703..f807ef6 100644 --- a/valhalla/proc/matrix/matrix_auto.py +++ b/valhalla/proc/matrix/matrix_auto.py @@ -43,7 +43,7 @@ from .. import HELP_DIR from ... import RESOURCE_PREFIX, __help__ from ...common import client, matrix_core -from ...utils import configmanager, transform, exceptions,logger +from ...utils import configmanager, transform, exceptions, logger from ..costing_params import CostingAuto from ..request_builder import get_locations, get_costing_options, get_avoid_locations @@ -309,6 +309,7 @@ def processAlgorithm(self, parameters, context, feedback): response, self.PROFILE, costing_params, + False, source_attributes, destination_attributes )