diff --git a/Qneat3Framework.py b/Qneat3Framework.py index f531caa..8d3eba7 100644 --- a/Qneat3Framework.py +++ b/Qneat3Framework.py @@ -3,7 +3,7 @@ *************************************************************************** Qneat3Framework.py --------------------- - + Date : January 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -24,7 +24,7 @@ from numpy import arange, meshgrid, linspace, nditer, zeros from osgeo import osr -from qgis.core import QgsProject, QgsPoint, QgsVectorLayer, QgsRasterLayer, QgsFeature, QgsFeatureSink, QgsFeatureRequest, QgsFields, QgsField, QgsGeometry, QgsPointXY, QgsLineString, QgsProcessingException, QgsDistanceArea, QgsUnitTypes +from qgis.core import QgsProject, QgsPoint, QgsVectorLayer, QgsRasterLayer, QgsFeature, QgsFeatureSink, QgsFeatureRequest, QgsFields, QgsField, QgsGeometry, QgsPointXY, QgsLineString, QgsProcessingException, QgsDistanceArea, QgsUnitTypes from qgis.analysis import QgsVectorLayerDirector, QgsNetworkDistanceStrategy, QgsNetworkSpeedStrategy, QgsGraphAnalyzer, QgsGraphBuilder, QgsInterpolator, QgsTinInterpolator, QgsGridFileWriter from qgis.PyQt.QtCore import QVariant @@ -38,7 +38,7 @@ class Qneat3Network(): Provides basic logic for more advanced network analysis algorithms """ - def __init__(self, + def __init__(self, input_network, #QgsProcessingParameterFeatureSource input_points, #[QgsPointXY] or QgsProcessingParameterFeatureSource or QgsVectorLayer --> Implement List of QgsFeatures [QgsFeatures] input_strategy, #int @@ -52,12 +52,12 @@ def __init__(self, input_defaultSpeed, #float input_tolerance, #float feedback #feedback object from processing (log window) - ): - + ): + """ Constructor for a Qneat3Network object. @type input_network: QgsProcessingParameterFeatureSource - @param input_network: input network dataset from processing algorithm + @param input_network: input network dataset from processing algorithm @type input_points: QgsProcessingParameterFeatureSource/QgsVectorLayer/[QgsPointXY] @param input_points: input point dataset from processing algorithm @type input_strategy: int @@ -81,17 +81,17 @@ def __init__(self, @type feedback: QgsProcessingFeedback @param feedback: feedback object from processing algorithm """ - + #initialize feedback self.feedback = feedback - + self.feedback.pushInfo("[QNEAT3Network][__init__] Setting up parameters") self.AnalysisCrs = input_analysisCrs - + #enable polygon calculation in geographic coordinate systems - distUnit = self.AnalysisCrs.mapUnits() - self.meter_to_unit_factor = QgsUnitTypes.fromUnitToUnitFactor(QgsUnitTypes.DistanceMeters, distUnit) - + # distUnit = self.AnalysisCrs.mapUnits() + # self.meter_to_unit_factor = QgsUnitTypes.fromUnitToUnitFactor(QgsUnitTypes.DistanceMeters, distUnit) + #init direction fields self.feedback.pushInfo("[QNEAT3Network][__init__] Setting up network direction parameters") self.directedAnalysis = self.setNetworkDirection((input_directionFieldName, input_forwardValue, input_backwardValue, input_bothValue, input_defaultDirection)) @@ -109,18 +109,21 @@ def __init__(self, else: self.list_input_points = getListOfPoints(input_points) #[QgsPointXY] self.input_points = input_points - + #Setup cost-strategy pattern. self.feedback.pushInfo("[QNEAT3Network][__init__] Setting analysis strategy: {}".format(input_strategy)) self.default_speed = input_defaultSpeed - + self.setNetworkStrategy(input_strategy, input_network, input_speedField, input_defaultSpeed) #add the strategy to the QgsGraphDirector self.director.addStrategy(self.strategy) - self.builder = QgsGraphBuilder(self.AnalysisCrs, True, input_tolerance) + self.builder = QgsGraphBuilder(self.AnalysisCrs, False, input_tolerance) #tell the graph-director to make the graph using the builder object and tie the start point geometry to the graph - + #switched the transformation option to False because we're already checking that all inputs use the same CRS + #consistent with the Entry & Exit cost portion of this plugin, which was not handling coordinate transformations + #by default this is using WGS84 ellipsoidal distance calculations by not passing a 4th parameter for an ellipsoid acronym, resulting in linear units of METERS + self.feedback.pushInfo("[QNEAT3Network][__init__] Start tying analysis points to the graph and building it.") self.feedback.pushInfo("[QNEAT3Network][__init__] This is a compute intensive task and may take some time depending on network size") start_local_time = time.localtime() @@ -134,27 +137,24 @@ def __init__(self, self.feedback.pushInfo("[QNEAT3Network][__init__] End Time: {}".format(time.strftime(":%Y-%m-%d %H:%M:%S", end_local_time))) self.feedback.pushInfo("[QNEAT3Network][__init__] Total Build Time: {}".format(end_time-start_time)) self.feedback.pushInfo("[QNEAT3Network][__init__] Analysis setup complete") - - - def setNetworkDirection(self, directionArgs): + + + def setNetworkDirection(self, directionArgs): if directionArgs.count("") == 0: self.directedAnalysis = True self.directionFieldId, self.input_forwardValue, self.input_backwardValue, self.input_bothValue, self.input_defaultDirection = directionArgs else: self.directedAnalysis = False - + def setNetworkStrategy(self, input_strategy, input_network, input_speedField, input_defaultSpeed): - distUnit = self.AnalysisCrs.mapUnits() - unit_to_meter_factor = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) - speedFieldId = getFieldIndexFromQgsProcessingFeatureSource(input_network, input_speedField) if input_strategy == 0: self.strategy = QgsNetworkDistanceStrategy() self.strategy_int = 0 else: - self.strategy = QgsNetworkSpeedStrategy(speedFieldId, float(input_defaultSpeed), unit_to_meter_factor * 1000.0 / 3600.0) + self.strategy = QgsNetworkSpeedStrategy(speedFieldId, float(input_defaultSpeed), 1000.0 / 3600.0) self.strategy_int = 1 - self.multiplier = 3600 + # self.multiplier = 3600 def calcDijkstra(self, startpoint_id, criterion): """Calculates Dijkstra on whole network beginning from one startPoint. Returns a list containing a TreeId-Array and Cost-Array that match up with their indices [[tree],[cost]] """ @@ -163,11 +163,11 @@ def calcDijkstra(self, startpoint_id, criterion): dijkstra_query.insert(0, tree) dijkstra_query.insert(1, cost) return dijkstra_query - + def calcShortestTree(self, startpoint_id, criterion): tree = QgsGraphAnalyzer.shortestTree(self.network, startpoint_id, criterion) return tree - + def calcIsoPoints(self, analysis_point_list, max_dist): iso_pointcloud = dict() @@ -176,13 +176,13 @@ def calcIsoPoints(self, analysis_point_list, max_dist): dijkstra_query = self.calcDijkstra(point.network_vertex_id, 0) tree = dijkstra_query[0] cost = dijkstra_query[1] - + current_start_point_id = point.point_id #id of the input point current_vertex_id = point.network_vertex_id entry_cost = point.entry_cost - + field_type = getFieldDatatypeFromPythontype(current_start_point_id) - + #startpoints are not part of the Query so they have to be added manually before #dikstra is called. start_vertex_feat = QgsFeature() @@ -198,12 +198,12 @@ def calcIsoPoints(self, analysis_point_list, max_dist): pt_m.addMValue(entry_cost) geom = QgsGeometry(pt_m) start_vertex_feat.setGeometry(geom) - + iso_pointcloud.update({current_vertex_id: start_vertex_feat}) - + i = 0 while i < len(cost): - #as long as costs at vertex i is greater than iso_distance and there exists an incoming edge (tree[i]!=-1) + #as long as costs at vertex i is greater than iso_distance and there exists an incoming edge (tree[i]!=-1) #consider it as a possible catchment polygon element if tree[i] != -1: fromVertexId = self.network.edge(tree[i]).toVertex() @@ -211,7 +211,7 @@ def calcIsoPoints(self, analysis_point_list, max_dist): #if the costs of the current vertex are lower than the radius, append the vertex id to results. if real_cost <= max_dist: #build feature - + feat = QgsFeature() fields = QgsFields() fields.append(QgsField('vertex_id', QVariant.Int, '', 254, 0)) @@ -226,7 +226,7 @@ def calcIsoPoints(self, analysis_point_list, max_dist): pt_m.addMValue((500-cost[fromVertexId])*2) geom = QgsGeometry(pt_m) feat.setGeometry(geom) - + if fromVertexId not in iso_pointcloud: #ERROR: FIRST POINT IN POINTCLOUD WILL NEVER BE ADDED iso_pointcloud.update({fromVertexId: feat}) @@ -236,80 +236,80 @@ def calcIsoPoints(self, analysis_point_list, max_dist): #iso_pointcloud.pop(toVertexId) iso_pointcloud.update({fromVertexId: feat}) #count up to next vertex - i = i + 1 + i = i + 1 if (i%10000)==0: self.feedback.pushInfo("[QNEAT3Network][calcIsoPoints] Added {} Nodes to iso pointcloud...".format(i)) - + return iso_pointcloud.values() #list of QgsFeature (=QgsFeatureList) - - def calcQneatInterpolation(self,iso_pointcloud_featurelist, resolution, interpolation_raster_path): + + def calcQneatInterpolation(self,iso_pointcloud_featurelist, resolution, interpolation_raster_path): #prepare spatial index uri = 'PointM?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&key=vertex_id&index=yes'.format(self.AnalysisCrs.authid()) - + mIsoPointcloud = QgsVectorLayer(uri, "mIsoPointcloud_layer", "memory") mIsoPointcloud_provider = mIsoPointcloud.dataProvider() mIsoPointcloud_provider.addFeatures(iso_pointcloud_featurelist, QgsFeatureSink.FastInsert) - + #implement spatial index for lines (closest line, etc...) spt_idx = QgsSpatialIndex(mIsoPointcloud.getFeatures(QgsFeatureRequest()), self.feedback) - + #prepare numpy coordinate grids NoData_value = -9999 raster_rectangle = mIsoPointcloud.extent() - + #top left point xmin = raster_rectangle.xMinimum() ymin = raster_rectangle.yMinimum() xmax = raster_rectangle.xMaximum() ymax = raster_rectangle.yMaximum() - + cols = int((xmax - xmin) / resolution) rows = int((ymax - ymin) / resolution) - + output_interpolation_raster = gdal.GetDriverByName('GTiff').Create(interpolation_raster_path, cols, rows, 1, gdal.GDT_Float64 ) output_interpolation_raster.SetGeoTransform((xmin, resolution, 0, ymax, 0, -resolution)) - + band = output_interpolation_raster.GetRasterBand(1) band.SetNoDataValue(NoData_value) - + #initialize zero array with 2 dimensions (according to rows and cols) raster_data = zeros(shape=(rows, cols)) - + #compute raster cell MIDpoints x_pos = linspace(xmin+(resolution/2), xmax -(resolution/2), raster_data.shape[1]) y_pos = linspace(ymax-(resolution/2), ymin + (resolution/2), raster_data.shape[0]) - x_grid, y_grid = meshgrid(x_pos, y_pos) - + x_grid, y_grid = meshgrid(x_pos, y_pos) + self.feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Beginning with interpolation') total_work = rows * cols counter = 0 - + self.feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Total workload: {} cells'.format(total_work)) self.feedback.setProgress(0) for i in range(rows): for j in range(cols): current_pixel_midpoint = QgsPointXY(x_grid[i,j],y_grid[i,j]) - + nearest_vertex_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, 1)[0] - + nearest_feature = mIsoPointcloud.getFeature(nearest_vertex_fid) - + nearest_vertex = self.network.vertex(nearest_feature['vertex_id']) - + edges = nearest_vertex.incomingEdges() + nearest_vertex.outgoingEdges() - + vertex_found = False nearest_counter = 2 while vertex_found == False: n_nearest_feature_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, nearest_counter)[nearest_counter-1] n_nearest_feature = mIsoPointcloud.getFeature(n_nearest_feature_fid) n_nearest_vertex_id = n_nearest_feature['vertex_id'] - + for edge_id in edges: from_vertex_id = self.network.edge(edge_id).fromVertex() to_vertex_id = self.network.edge(edge_id).toVertex() - - if n_nearest_vertex_id == from_vertex_id: + + if n_nearest_vertex_id == from_vertex_id: vertex_found = True vertex_type = "from_vertex" from_point = n_nearest_feature.geometry().asPoint() @@ -319,14 +319,14 @@ def calcQneatInterpolation(self,iso_pointcloud_featurelist, resolution, interpol vertex_type = "to_vertex" to_point = n_nearest_feature.geometry().asPoint() to_vertex_cost = n_nearest_feature['cost'] - + nearest_counter = nearest_counter + 1 """ if nearest_counter == 5: vertex_found = True vertex_type = "end_vertex" """ - + if vertex_type == "from_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY([from_point, nearest_vertex.point()]) res = nearest_edge_geometry.closestSegmentWithContext(current_pixel_midpoint) @@ -351,98 +351,98 @@ def calcQneatInterpolation(self,iso_pointcloud_featurelist, resolution, interpol raster_data[i,j] = pixel_cost else: pixel_cost = -99999#nearest_feature['cost'] + (nearest_vertex.point().distance(current_pixel_midpoint)) - - + + """ nearest_feature_pointxy = nearest_feature.geometry().asPoint() nearest_feature_cost = nearest_feature['cost'] - + dist_to_vertex = current_pixel_midpoint.distance(nearest_feature_pointxy) #implement time cost pixel_cost = dist_to_vertex + nearest_feature_cost - + raster_data[i,j] = pixel_cost """ counter = counter+1 if counter%1000 == 0: self.feedback.pushInfo("[QNEAT3Network][calcQneatInterpolation] Interpolated {} cells...".format(counter)) self.feedback.setProgress((counter/total_work)*100) - - + + band.WriteArray(raster_data) outRasterSRS = osr.SpatialReference() outRasterSRS.ImportFromWkt(self.AnalysisCrs.toWkt()) output_interpolation_raster.SetProjection(outRasterSRS.ExportToWkt()) band.FlushCache() - - + + def calcIsoTinInterpolation(self, iso_point_layer, resolution, interpolation_raster_path): if self.AnalysisCrs.isGeographic(): raise QgsProcessingException('The TIN-Interpolation algorithm in QGIS is designed to work with projected coordinate systems.Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') - + layer_data = QgsInterpolator.LayerData() QgsInterpolator.LayerData - + layer_data.source = iso_point_layer #in QGIS2: vectorLayer layer_data.valueSource = QgsInterpolator.ValueAttribute layer_data.interpolationAttribute = 1 #take second field to get costs layer_data.sourceType = QgsInterpolator.SourcePoints tin_interpolator = QgsTinInterpolator([layer_data], QgsTinInterpolator.Linear) - + rect = iso_point_layer.extent() ncol = int((rect.xMaximum() - rect.xMinimum()) / resolution) nrows = int((rect.yMaximum() - rect.yMinimum()) / resolution) - + writer = QgsGridFileWriter(tin_interpolator, interpolation_raster_path, rect, ncol, nrows) writer.writeFile(self.feedback) # Creating .asc raste return QgsRasterLayer(interpolation_raster_path, "temp_qneat3_interpolation_raster") def calcIsoContours(self, max_dist, interval, interpolation_raster_path): featurelist = [] - + try: import matplotlib.pyplot as plt except: return featurelist - + ds_in = gdal.Open(interpolation_raster_path) band_in = ds_in.GetRasterBand(1) xsize_in = band_in.XSize ysize_in = band_in.YSize - + geotransform_in = ds_in.GetGeoTransform() - + srs = osr.SpatialReference() srs.ImportFromWkt( ds_in.GetProjectionRef() ) raster_values = band_in.ReadAsArray(0, 0, xsize_in, ysize_in) raster_values[raster_values < 0] = max_dist + 1000 #necessary to produce rectangular array from raster #nodata values get replaced by the maximum value + 1 - + x_pos = linspace(geotransform_in[0], geotransform_in[0] + geotransform_in[1] * raster_values.shape[1], raster_values.shape[1]) y_pos = linspace(geotransform_in[3], geotransform_in[3] + geotransform_in[5] * raster_values.shape[0], raster_values.shape[0]) - x_grid, y_grid = meshgrid(x_pos, y_pos) - + x_grid, y_grid = meshgrid(x_pos, y_pos) + start = interval end = interval * ceil(max_dist/interval) +interval - + levels = arange(start, end, interval) - + fid = 0 for current_level in nditer(levels): self.feedback.pushInfo("[QNEAT3Network][calcIsoContours] Calculating {}-level contours".format(current_level)) contours = plt.contourf(x_grid, y_grid, raster_values, [0, current_level], antialiased=True) - + for collection in contours.collections: - for contour_paths in collection.get_paths(): + for contour_paths in collection.get_paths(): for polygon in contour_paths.to_polygons(): x = polygon[:,0] y = polygon[:,1] polylinexy_list = [QgsPointXY(i[0], i[1]) for i in zip(x,y)] - + feat = QgsFeature() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) @@ -453,61 +453,61 @@ def calcIsoContours(self, max_dist, interval, interpolation_raster_path): feat['id'] = fid feat['cost_level'] = float(current_level) featurelist.insert(0, feat) - - fid=fid+1 + + fid=fid+1 return featurelist - - + + def calcIsoPolygons(self, max_dist, interval, interpolation_raster_path): featurelist = [] - + try: import matplotlib.pyplot as plt except: return featurelist - + ds_in = gdal.Open(interpolation_raster_path) band_in = ds_in.GetRasterBand(1) xsize_in = band_in.XSize ysize_in = band_in.YSize - + geotransform_in = ds_in.GetGeoTransform() - + srs = osr.SpatialReference() srs.ImportFromWkt( ds_in.GetProjectionRef() ) raster_values = band_in.ReadAsArray(0, 0, xsize_in, ysize_in) raster_values[raster_values < 0] = max_dist + 1000 #necessary to produce rectangular array from raster #nodata values get replaced by the maximum value + 1 - + x_pos = linspace(geotransform_in[0], geotransform_in[0] + geotransform_in[1] * raster_values.shape[1], raster_values.shape[1]) y_pos = linspace(geotransform_in[3], geotransform_in[3] + geotransform_in[5] * raster_values.shape[0], raster_values.shape[0]) - x_grid, y_grid = meshgrid(x_pos, y_pos) - + x_grid, y_grid = meshgrid(x_pos, y_pos) + start = interval end = interval * ceil(max_dist/interval) +interval - + levels = arange(start, end, interval) fid = 0 for current_level in nditer(levels): self.feedback.pushInfo("[QNEAT3Network][calcIsoPolygons] calculating {}-level contours".format(current_level)) contours = plt.contourf(x_grid, y_grid, raster_values, [0, current_level], antialiased=True) - - + + for collection in contours.collections: - for contour_path in collection.get_paths(): - + for contour_path in collection.get_paths(): + polygon_list = [] - + for vertex in contour_path.to_polygons(): x = vertex[:,0] y = vertex[:,1] polylinexy_list = [QgsPointXY(i[0], i[1]) for i in zip(x,y)] polygon_list.append(polylinexy_list) - + feat = QgsFeature() fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) @@ -517,67 +517,67 @@ def calcIsoPolygons(self, max_dist, interval, interpolation_raster_path): feat.setGeometry(geom) feat['id'] = fid feat['cost_level'] = float(current_level) - + featurelist.insert(0, feat) - fid=fid+1 + fid=fid+1 """Maybe move to algorithm""" #featurelist = featurelist[::-1] #reverse self.feedback.pushInfo("[QNEAT3Network][calcIsoPolygons] number of elements in contour_featurelist: {}".format(len(featurelist))) return featurelist - + class Qneat3AnalysisPoint(): - - def __init__(self, layer_name, feature, point_id_field_name, net, vertex_geom, entry_cost_calculation_method, feedback): + + def __init__(self, layer_name, feature, point_id_field_name, net, vertex_geom, feedback): self.layer_name = layer_name self.point_feature = feature - self.point_id = feature[point_id_field_name] + self.point_id = feature[point_id_field_name] self.point_geom = feature.geometry().asPoint() self.network_vertex_id = self.getNearestVertexId(net.network, vertex_geom) self.network_vertex = self.getNearestVertex(net.network, vertex_geom) self.crs = net.AnalysisCrs self.strategy = net.strategy_int self.entry_speed = net.default_speed + #BETTER TO ALWAYS USE ELLIPSOIDAL for consistency with the NETWORK COST calculation by the NETWORK BUILDER + ''' if entry_cost_calculation_method == 0: self.entry_cost = self.calcEntryCostEllipsoidal(feedback) elif entry_cost_calculation_method == 1: self.entry_cost = self.calcEntryCostPlanar(feedback) else: self.entry_cost = self.calcEntryCostEllipsoidal(feedback) - + ''' + self.entry_cost = self.calcEntryCostEllipsoidal(feedback) + def calcEntryCostEllipsoidal(self, feedback): dist_calculator = QgsDistanceArea() - dist_calculator.setSourceCrs(QgsProject().instance().crs(), QgsProject().instance().transformContext()) - dist_calculator.setEllipsoid(QgsProject().instance().crs().ellipsoidAcronym()) + dist_calculator.setSourceCrs(self.crs, QgsProject().instance().transformContext()) #set distance calculator CRS to that of the input network + dist_calculator.setEllipsoid("WGS84") #always use WGS84 for distance calculations for consistency with the QgsNetworkBuilder dist = dist_calculator.measureLine([self.point_geom, self.network_vertex.point()]) feedback.pushInfo("[QNEAT3Network][calcEntryCostEllipsoidal] Ellipsoidal entry cost to vertex {} = {}".format(self.network_vertex_id, dist)) if self.strategy == 0: return dist else: - distUnit = self.crs.mapUnits() - unit_to_meter_factor = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) - return dist/(self.entry_speed*(unit_to_meter_factor * 1000.0 / 3600.0)) #length/(m/s) todo: Make dynamic - + return dist/(self.entry_speed*(1000.0 / 3600.0)) #length/(m/s) todo: Make dynamic + + # I THINK THIS FUNCITON AND ITS OPTIONS IN EACH ALGORITHM SHOULD BE REMOVED. ALWAYS USE ELLIPSOIDAL def calcEntryCostPlanar(self, feedback): dist = self.calcEntryLinestring().length() feedback.pushInfo("[QNEAT3Network][calcEntryCostPlanar] Planar entry cost to vertex {} = {}".format(self.network_vertex_id, dist)) if self.strategy == 0: return dist else: - distUnit = self.crs.mapUnits() - unit_to_meter_factor = QgsUnitTypes.fromUnitToUnitFactor(distUnit, QgsUnitTypes.DistanceMeters) - return dist/(self.entry_speed*(unit_to_meter_factor * 1000.0 / 3600.0)) #length/(m/s) todo: Make dynamic + return dist/(self.entry_speed*(1000.0 / 3600.0)) #length/(m/s) todo: Make dynamic def calcEntryLinestring(self): return QgsGeometry.fromPolylineXY([self.point_geom, self.network_vertex.point()]) - + def getNearestVertexId(self, network, vertex_geom): return network.findVertex(vertex_geom) - + def getNearestVertex(self, network, vertex_geom): return network.vertex(self.getNearestVertexId(network, vertex_geom)) - + def __str__(self): - return u"Qneat3AnalysisPoint: {} analysis_id: {:30} FROM {:30} TO {:30} network_id: {:d}".format(self.layer_name, self.point_id, self.point_geom.__str__(), self.network_vertex.point().__str__(), self.network_vertex_id) - + return u"Qneat3AnalysisPoint: {} analysis_id: {:30} FROM {:30} TO {:30} network_id: {:d}".format(self.layer_name, self.point_id, self.point_geom.__str__(), self.network_vertex.point().__str__(), self.network_vertex_id) diff --git a/algs/IsoAreaAsContoursFromLayer.py b/algs/IsoAreaAsContoursFromLayer.py index bbd301c..35b948a 100644 --- a/algs/IsoAreaAsContoursFromLayer.py +++ b/algs/IsoAreaAsContoursFromLayer.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsContourFromLayer.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterField, QgsProcessingParameterNumber, @@ -68,7 +69,6 @@ class IsoAreaAsContoursFromLayer(QgisAlgorithm): CELL_SIZE = "CELL_SIZE" INTERVAL = "INTERVAL" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -88,27 +88,28 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaascontoursfromlayer' def displayName(self): return self.tr('Iso-Area as Contours (from Layer)') - + def shortHelpString(self): return "General:
"\ "This algorithm implements iso-area contours to return the isochrone areas for a maximum cost level and interval levels on a given network dataset for a layer of points.
"\ "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm are two layers:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -121,13 +122,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) @@ -140,7 +138,7 @@ def initAlgorithm(self, config=None): self.START_POINTS, optional=False)) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, @@ -152,15 +150,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -180,7 +174,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -191,7 +185,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -199,7 +193,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT_INTERPOLATION, self.tr('Output Interpolation'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_CONTOURS, self.tr('Output Contours'), QgsProcessing.TypeVectorLine)) - + def processAlgorithm(self, parameters, context, feedback): feedback.pushInfo(self.tr("[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format(self.displayName()))) network = self.parameterAsSource(parameters, self.INPUT, context) #QgsProcessingFeatureSource @@ -210,7 +204,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -221,47 +214,52 @@ def processAlgorithm(self, parameters, context, feedback): tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float output_path = self.parameterAsOutputLayer(parameters, self.OUTPUT_INTERPOLATION, context) #string - analysisCrs = context.project().crs() - input_coordinates = getListOfPoints(startPoints) - + analysisCrs = network.sourceCrs() + input_coordinates = getListOfPoints(startPoints) + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != startPoints.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] - + + list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints(list_apoints, max_dist+(max_dist*0.1)) feedback.setProgress(50) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(70) - + fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost_level', QVariant.Double, '', 20, 7)) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_CONTOURS, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Contours using numpy and matplotlib...") contour_featurelist = net.calcIsoContours(max_dist, interval, output_path) feedback.setProgress(90) - + sink.addFeatures(contour_featurelist, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) - + results = {} results[self.OUTPUT_INTERPOLATION] = output_path results[self.OUTPUT_CONTOURS] = dest_id return results - diff --git a/algs/IsoAreaAsContoursFromPoint.py b/algs/IsoAreaAsContoursFromPoint.py index 62dba7a..3f394d7 100644 --- a/algs/IsoAreaAsContoursFromPoint.py +++ b/algs/IsoAreaAsContoursFromPoint.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsContourFromPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterPoint, QgsProcessingParameterField, @@ -68,7 +69,6 @@ class IsoAreaAsContoursFromPoint(QgisAlgorithm): CELL_SIZE = "CELL_SIZE" INTERVAL = "INTERVAL" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -88,7 +88,7 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaascontoursfrompoint' @@ -101,15 +101,16 @@ def shortHelpString(self): "It accounts for points outside of the network (eg. non-network-elements) and increments the isochrone areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm are two layers:"\ - "" - - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -122,20 +123,17 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterPoint(self.START_POINT, self.tr('Start point'))) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, @@ -147,15 +145,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -175,7 +169,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -186,7 +180,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -194,7 +188,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT_INTERPOLATION, self.tr('Output Interpolation'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_CONTOURS, self.tr('Output Contours'), QgsProcessing.TypeVectorLine)) - + def processAlgorithm(self, parameters, context, feedback): feedback.pushInfo(self.tr("[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format(self.displayName()))) network = self.parameterAsSource(parameters, self.INPUT, context) #QgsProcessingFeatureSource @@ -204,7 +198,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -218,44 +211,49 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != context.project().crs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) - + + analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], feedback) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], (max_dist+(max_dist*0.1))) feedback.setProgress(50) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(70) - + fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost_level', QVariant.Double, '', 20, 7)) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_CONTOURS, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Contours using numpy and matplotlib...") contour_featurelist = net.calcIsoContours(max_dist, interval, output_path) feedback.setProgress(90) - + sink.addFeatures(contour_featurelist, QgsFeatureSink.FastInsert) feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) - + results = {} results[self.OUTPUT_INTERPOLATION] = output_path results[self.OUTPUT_CONTOURS] = dest_id return results - diff --git a/algs/IsoAreaAsInterpolationFromLayer.py b/algs/IsoAreaAsInterpolationFromLayer.py index 9603886..5ee59c3 100644 --- a/algs/IsoAreaAsInterpolationFromLayer.py +++ b/algs/IsoAreaAsInterpolationFromLayer.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsInterpolationFromLayer.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : March 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -36,6 +36,7 @@ from qgis.core import (QgsFeatureSink, QgsVectorLayer, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterField, QgsProcessingParameterNumber, @@ -62,7 +63,6 @@ class IsoAreaAsInterpolationFromLayer(QgisAlgorithm): MAX_DIST = "MAX_DIST" CELL_SIZE = "CELL_SIZE" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -81,28 +81,29 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaasinterpolationfromlayer' def displayName(self): return self.tr('Iso-Area as Interpolation (from Layer)') - + def shortHelpString(self): return "General:
"\ "This algorithm implements iso-area analysis to return the network-distance interpolation for a maximum cost level on a given network dataset for a layer of points.
"\ "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ - "" - - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -115,13 +116,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) @@ -134,7 +132,7 @@ def initAlgorithm(self, config=None): self.START_POINTS, optional=False)) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.CELL_SIZE, @@ -142,15 +140,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -170,7 +164,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -181,12 +175,12 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(p) - + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output Interpolation'))) def processAlgorithm(self, parameters, context, feedback): @@ -198,7 +192,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -209,34 +202,39 @@ def processAlgorithm(self, parameters, context, feedback): tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float output_path = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) - analysisCrs = context.project().crs() + analysisCrs = network.sourceCrs() input_coordinates = getListOfPoints(startPoints) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != startPoints.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] - + + list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints(list_apoints, max_dist) feedback.setProgress(70) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(99) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") - feedback.setProgress(100) - + feedback.setProgress(100) + results = {} results[self.OUTPUT] = output_path return results - diff --git a/algs/IsoAreaAsInterpolationFromPoint.py b/algs/IsoAreaAsInterpolationFromPoint.py index 7c90578..a28b372 100644 --- a/algs/IsoAreaAsInterpolationFromPoint.py +++ b/algs/IsoAreaAsInterpolationFromPoint.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsInterpolationPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : March 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -36,6 +36,7 @@ from qgis.core import (QgsFeatureSink, QgsVectorLayer, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterPoint, QgsProcessingParameterField, @@ -62,7 +63,6 @@ class IsoAreaAsInterpolationFromPoint(QgisAlgorithm): MAX_DIST = "MAX_DIST" CELL_SIZE = "CELL_SIZE" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -81,7 +81,7 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaasinterpolationfrompoint' @@ -94,14 +94,15 @@ def shortHelpString(self): "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -114,20 +115,17 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterPoint(self.START_POINT, self.tr('Start point'))) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.CELL_SIZE, @@ -135,15 +133,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -163,7 +157,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -174,12 +168,12 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(p) - + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output Interpolation'))) def processAlgorithm(self, parameters, context, feedback): @@ -190,7 +184,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -204,32 +197,37 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != context.project().crs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) - + + analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], feedback) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], max_dist) feedback.setProgress(70) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(99) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") - feedback.setProgress(100) - + feedback.setProgress(100) + results = {} results[self.OUTPUT] = output_path return results - diff --git a/algs/IsoAreaAsPointcloudFromLayer.py b/algs/IsoAreaAsPointcloudFromLayer.py index 1e273ff..98f7e4d 100644 --- a/algs/IsoAreaAsPointcloudFromLayer.py +++ b/algs/IsoAreaAsPointcloudFromLayer.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsPointcloudFromLayer.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -39,6 +39,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterField, QgsProcessingParameterNumber, @@ -64,7 +65,6 @@ class IsoAreaAsPointcloudFromLayer(QgisAlgorithm): ID_FIELD = 'ID_FIELD' MAX_DIST = "MAX_DIST" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -83,7 +83,7 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaaspointcloudfromlayer' @@ -96,15 +96,16 @@ def shortHelpString(self): "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ "
"\ - "You may use the output pointcloud as input for further analyses." - + "You may use the output pointcloud as input for further analyses."\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -117,13 +118,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) @@ -140,15 +138,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -168,7 +162,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -179,12 +173,12 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(p) - + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Output Pointcloud'), QgsProcessing.TypeVectorPoint)) @@ -197,7 +191,6 @@ def processAlgorithm(self, parameters, context, feedback): max_dist = self.parameterAsDouble(parameters, self.MAX_DIST, context)#float strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -209,31 +202,36 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = getListOfPoints(startPoints) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != startPoints.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] - + + list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] + fields = QgsFields() fields.append(QgsField('vertex_id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 254, 7)) fields.append(QgsField('origin_point_id', getFieldDatatype(startPoints, id_field))) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, network.sourceCrs()) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints(list_apoints, max_dist) feedback.setProgress(90) - + sink.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") - feedback.setProgress(100) - + feedback.setProgress(100) + results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/IsoAreaAsPointcloudFromPoint.py b/algs/IsoAreaAsPointcloudFromPoint.py index 95e9583..f81f54a 100644 --- a/algs/IsoAreaAsPointcloudFromPoint.py +++ b/algs/IsoAreaAsPointcloudFromPoint.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsPointcloudFromPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -39,6 +39,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterPoint, QgsProcessingParameterField, @@ -64,7 +65,6 @@ class IsoAreaAsPointcloudFromPoint(QgisAlgorithm): START_POINT = 'START_POINT' MAX_DIST = "MAX_DIST" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -83,28 +83,29 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaaspointcloudfrompoint' def displayName(self): return self.tr('Iso-Area as Pointcloud (from Point)') - + def shortHelpString(self): return "General:
"\ "This algorithm implements iso-pointcloud analysis to return all network nodes reachable within a maximum cost level as pointcloud on a given network dataset for a manually chosen point.
"\ "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ "
"\ - "You may use the output pointcloud as input for further analyses." - + "You may use the output pointcloud as input for further analyses."\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -117,13 +118,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) @@ -134,15 +132,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -162,7 +156,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -173,12 +167,12 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(p) - + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Output Pointcloud'), QgsProcessing.TypeVectorPoint)) @@ -190,7 +184,6 @@ def processAlgorithm(self, parameters, context, feedback): max_dist = self.parameterAsDouble(parameters, self.MAX_DIST, context)#float strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -203,31 +196,36 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != context.project().crs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) - + analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], feedback) + fields = QgsFields() fields.append(QgsField('vertex_id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost', QVariant.Double, '', 254, 7)) fields.append(QgsField('origin_point_id',QVariant.String, '', 254, 7)) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.Point, network.sourceCrs()) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], max_dist) feedback.setProgress(90) - + sink.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") - feedback.setProgress(100) - + feedback.setProgress(100) + results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/IsoAreaAsPolygonsFromLayer.py b/algs/IsoAreaAsPolygonsFromLayer.py index 75b0107..e53de54 100644 --- a/algs/IsoAreaAsPolygonsFromLayer.py +++ b/algs/IsoAreaAsPolygonsFromLayer.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsPolygonFromLayer.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : April 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterField, QgsProcessingParameterNumber, @@ -68,7 +69,6 @@ class IsoAreaAsPolygonsFromLayer(QgisAlgorithm): CELL_SIZE = "CELL_SIZE" INTERVAL = "INTERVAL" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -88,27 +88,28 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaaspolygonsfromlayer' def displayName(self): return self.tr('Iso-Area as Polygons (from Layer)') - + def shortHelpString(self): return "General:
"\ "This algorithm implements iso-area analysis to return the iso-area polygons for a maximum cost level and interval levels on a given network dataset for a layer of points.
"\ "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm are two layers:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -121,13 +122,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Vector layer representing network'), [QgsProcessing.TypeVectorLine])) @@ -140,7 +138,7 @@ def initAlgorithm(self, config=None): self.START_POINTS, optional=False)) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, @@ -157,10 +155,6 @@ def initAlgorithm(self, config=None): defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -180,7 +174,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -191,7 +185,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -199,7 +193,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT_INTERPOLATION, self.tr('Output Interpolation'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_POLYGONS, self.tr('Output Polygon'), QgsProcessing.TypeVectorPolygon)) - + def processAlgorithm(self, parameters, context, feedback): feedback.pushInfo(self.tr("[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format(self.displayName()))) network = self.parameterAsSource(parameters, self.INPUT, context) #QgsProcessingFeatureSource @@ -210,7 +204,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -223,45 +216,49 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = getListOfPoints(startPoints) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != startPoints.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] - + + list_apoints = [Qneat3AnalysisPoint("from", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(startPoints))] + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints(list_apoints, max_dist+(max_dist*0.1)) feedback.setProgress(50) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(70) - + fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost_level', QVariant.Double, '', 20, 7)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_POLYGONS, context, fields, QgsWkbTypes.Polygon, network.sourceCrs()) - + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_POLYGONS, context, fields, QgsWkbTypes.Polygon, network.sourceCrs()) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Polygons using numpy and matplotlib...") polygon_featurelist = net.calcIsoPolygons(max_dist, interval, output_path) feedback.setProgress(90) - + sink.addFeatures(polygon_featurelist, QgsFeatureSink.FastInsert) feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) - + results = {} results[self.OUTPUT_INTERPOLATION] = output_path results[self.OUTPUT_POLYGONS] = dest_id return results - - diff --git a/algs/IsoAreaAsPolygonsFromPoint.py b/algs/IsoAreaAsPolygonsFromPoint.py index 80a6d06..5aa1ed2 100644 --- a/algs/IsoAreaAsPolygonsFromPoint.py +++ b/algs/IsoAreaAsPolygonsFromPoint.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsPolygonFromPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : April 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFields, QgsField, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterPoint, QgsProcessingParameterField, @@ -68,7 +69,6 @@ class IsoAreaAsPolygonsFromPoint(QgisAlgorithm): CELL_SIZE = "CELL_SIZE" INTERVAL = "INTERVAL" STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -88,27 +88,28 @@ def group(self): def groupId(self): return 'isoareas' - + def shortHelpString(self): return "General:
"\ "This algorithm implements iso-area analysis to return the iso-area polygons for a maximum cost level and interval levels on a given network dataset for a manually chosen point.
"\ "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm are two layers:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def name(self): return 'isoareaaspolygonsfrompoint' def displayName(self): return self.tr('Iso-Area as Polygons (from Point)') - + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -121,20 +122,17 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterPoint(self.START_POINT, self.tr('Start Point'))) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, @@ -146,15 +144,11 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -174,7 +168,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -185,7 +179,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -193,7 +187,7 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT_INTERPOLATION, self.tr('Output Interpolation'))) self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_POLYGONS, self.tr('Output Polygon'), QgsProcessing.TypeVectorPolygon)) - + def processAlgorithm(self, parameters, context, feedback): feedback.pushInfo(self.tr("[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format(self.displayName()))) network = self.parameterAsSource(parameters, self.INPUT, context) #QgsProcessingFeatureSource @@ -203,7 +197,6 @@ def processAlgorithm(self, parameters, context, feedback): cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context)#int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -217,45 +210,49 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != context.project().crs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) - + + analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], feedback) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], max_dist+(max_dist*0.1)) feedback.setProgress(50) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(70) - + fields = QgsFields() fields.append(QgsField('id', QVariant.Int, '', 254, 0)) fields.append(QgsField('cost_level', QVariant.Double, '', 20, 7)) - - (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_POLYGONS, context, fields, QgsWkbTypes.Polygon, network.sourceCrs()) - + + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_POLYGONS, context, fields, QgsWkbTypes.Polygon, network.sourceCrs()) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Polygons using numpy and matplotlib...") polygon_featurelist = net.calcIsoPolygons(max_dist, interval, output_path) feedback.setProgress(90) - + sink.addFeatures(polygon_featurelist, QgsFeatureSink.FastInsert) feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) - + results = {} results[self.OUTPUT_INTERPOLATION] = output_path results[self.OUTPUT_POLYGONS] = dest_id return results - - diff --git a/algs/IsoAreaAsQneatInterpolationFromPoint.py b/algs/IsoAreaAsQneatInterpolationFromPoint.py index 4663d47..a9919e2 100644 --- a/algs/IsoAreaAsQneatInterpolationFromPoint.py +++ b/algs/IsoAreaAsQneatInterpolationFromPoint.py @@ -3,10 +3,10 @@ *************************************************************************** IsoAreaAsQneatInterpolationPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : July 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -45,6 +45,7 @@ QgsFeatureRequest, QgsGeometry, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterPoint, QgsProcessingParameterField, @@ -72,7 +73,6 @@ class IsoAreaAsQneatInterpolationFromPoint(QgisAlgorithm): CELL_SIZE = "CELL_SIZE" STRATEGY = 'STRATEGY' METHOD = 'METHOD' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -91,7 +91,7 @@ def group(self): def groupId(self): return 'isoareas' - + def name(self): return 'isoareaasqneatinterpolationfrompoint' @@ -104,14 +104,15 @@ def shortHelpString(self): "It accounts for points outside of the network (eg. non-network-elements) and increments the iso-areas cost regarding to distance/default speed value. Distances are measured accounting for ellipsoids.
Please, only use a projected coordinate system (eg. no WGS84) for this kind of analysis.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -124,24 +125,21 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - + self.METHODS = [self.tr('QGIS TIN-Interpolation (faster but not exact)'), self.tr('QNEAT-Interpolation (slower but more exact') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterPoint(self.START_POINT, self.tr('Start point'))) self.addParameter(QgsProcessingParameterNumber(self.MAX_DIST, - self.tr('Size of Iso-Area (distance or time value)'), + self.tr('Maximum cost level of Iso-Area'), QgsProcessingParameterNumber.Double, 2500.0, False, 0, 99999999.99)) self.addParameter(QgsProcessingParameterNumber(self.CELL_SIZE, @@ -149,7 +147,7 @@ def initAlgorithm(self, config=None): QgsProcessingParameterNumber.Integer, 10, False, 1, 99999999)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) self.addParameter(QgsProcessingParameterEnum(self.METHOD, @@ -158,10 +156,6 @@ def initAlgorithm(self, config=None): defaultValue=1)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=1)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -181,7 +175,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -192,12 +186,12 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) self.addParameter(p) - + self.addParameter(QgsProcessingParameterRasterDestination(self.OUTPUT, self.tr('Output Interpolation'))) def processAlgorithm(self, parameters, context, feedback): @@ -209,7 +203,6 @@ def processAlgorithm(self, parameters, context, feedback): strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int interpolation_method = self.parameterAsEnum(parameters, self.METHOD, context)#int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -223,24 +216,30 @@ def processAlgorithm(self, parameters, context, feedback): analysisCrs = network.sourceCrs() input_coordinates = [startPoint] input_point = getFeatureFromPointParameter(startPoint) - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != startPoint.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") - feedback.setProgress(10) + feedback.setProgress(10) net = Qneat3Network(network, input_coordinates, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], entry_cost_calc_method, feedback) - + + analysis_point = Qneat3AnalysisPoint("point", input_point, "point_id", net, net.list_tiedPoints[0], feedback) + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Pointcloud...") iso_pointcloud = net.calcIsoPoints([analysis_point], max_dist) feedback.setProgress(70) - + uri = "Point?crs={}&field=vertex_id:int(254)&field=cost:double(254,7)&field=origin_point_id:string(254)&index=yes".format(analysisCrs.authid()) - + iso_pointcloud_layer = QgsVectorLayer(uri, "iso_pointcloud_layer", "memory") iso_pointcloud_provider = iso_pointcloud_layer.dataProvider() iso_pointcloud_provider.addFeatures(iso_pointcloud, QgsFeatureSink.FastInsert) - + feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") if interpolation_method == 0: feedback.pushInfo("[QNEAT3Algorithm] Calculating Iso-Interpolation-Raster using QGIS TIN-Interpolator...") @@ -252,84 +251,84 @@ def processAlgorithm(self, parameters, context, feedback): #prepare numpy coordinate grids NoData_value = -9999 raster_rectangle = iso_pointcloud_layer.extent() - + #implement spatial index for lines (closest line, etc...) spt_idx = QgsSpatialIndex(iso_pointcloud_layer.getFeatures(QgsFeatureRequest()), feedback) - + #top left point xmin = raster_rectangle.xMinimum() ymin = raster_rectangle.yMinimum() xmax = raster_rectangle.xMaximum() ymax = raster_rectangle.yMaximum() - + cols = int((xmax - xmin) / cell_size) rows = int((ymax - ymin) / cell_size) - + output_interpolation_raster = gdal.GetDriverByName('GTiff').Create(output_path, cols, rows, 1, gdal.GDT_Float64 ) output_interpolation_raster.SetGeoTransform((xmin, cell_size, 0, ymax, 0, -cell_size)) - + band = output_interpolation_raster.GetRasterBand(1) band.SetNoDataValue(NoData_value) - + #initialize zero array with 2 dimensions (according to rows and cols) raster_routingcost_data = zeros(shape=(rows, cols)) - + #compute raster cell MIDpoints x_pos = linspace(xmin+(cell_size/2), xmax -(cell_size/2), raster_routingcost_data.shape[1]) y_pos = linspace(ymax-(cell_size/2), ymin + (cell_size/2), raster_routingcost_data.shape[0]) - x_grid, y_grid = meshgrid(x_pos, y_pos) - + x_grid, y_grid = meshgrid(x_pos, y_pos) + feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Beginning with interpolation') total_work = rows * cols counter = 0 - + feedback.pushInfo('[QNEAT3Network][calcQneatInterpolation] Total workload: {} cells'.format(total_work)) feedback.setProgress(0) for i in range(rows): for j in range(cols): current_pixel_midpoint = QgsPointXY(x_grid[i,j],y_grid[i,j]) - + nearest_vertex_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, 1)[0] - + nearest_feature = iso_pointcloud_layer.getFeature(nearest_vertex_fid) - + nearest_vertex = net.network.vertex(nearest_feature['vertex_id']) - - #yields a list of all incoming and outgoing edges - edges = nearest_vertex.incomingEdges() + nearest_vertex.outgoingEdges() - + + #yields a list of all incoming and outgoing edges + edges = nearest_vertex.incomingEdges() + nearest_vertex.outgoingEdges() + vertex_found = False nearest_counter = 2 while vertex_found == False: #find the second nearest vertex (eg, the vertex with least cost of all edges incoming to the first nearest vertex) - second_nearest_feature_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, nearest_counter)[nearest_counter-1] + second_nearest_feature_fid = spt_idx.nearestNeighbor(current_pixel_midpoint, nearest_counter)[nearest_counter-1] second_nearest_feature = iso_pointcloud_layer.getFeature(second_nearest_feature_fid) second_nearest_vertex_id = second_nearest_feature['vertex_id'] - + for edge_id in edges: from_vertex_id = net.network.edge(edge_id).fromVertex() to_vertex_id = net.network.edge(edge_id).toVertex() - - if second_nearest_vertex_id == from_vertex_id: + + if second_nearest_vertex_id == from_vertex_id: vertex_found = True vertex_type = "from_vertex" from_point = second_nearest_feature.geometry().asPoint() from_vertex_cost = second_nearest_feature['cost'] - + if second_nearest_vertex_id == to_vertex_id: vertex_found = True vertex_type = "to_vertex" to_point = second_nearest_feature.geometry().asPoint() to_vertex_cost = second_nearest_feature['cost'] - - + + nearest_counter = nearest_counter + 1 """ if nearest_counter == 5: vertex_found = True vertex_type = "end_vertex" """ - + if vertex_type == "from_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY([from_point, nearest_vertex.point()]) res = nearest_edge_geometry.closestSegmentWithContext(current_pixel_midpoint) @@ -354,34 +353,34 @@ def processAlgorithm(self, parameters, context, feedback): raster_routingcost_data[i,j] = pixel_cost else: pixel_cost = -99999#nearest_feature['cost'] + (nearest_vertex.point().distance(current_pixel_midpoint)) - - + + """ nearest_feature_pointxy = nearest_feature.geometry().asPoint() nearest_feature_cost = nearest_feature['cost'] - + dist_to_vertex = current_pixel_midpoint.distance(nearest_feature_pointxy) #implement time cost pixel_cost = dist_to_vertex + nearest_feature_cost - + raster_data[i,j] = pixel_cost """ counter = counter+1 if counter%1000 == 0: feedback.pushInfo("[QNEAT3Network][calcQneatInterpolation] Interpolated {} cells...".format(counter)) feedback.setProgress((counter/total_work)*100) - - + + band.WriteArray(raster_routingcost_data) outRasterSRS = osr.SpatialReference() outRasterSRS.ImportFromWkt(net.AnalysisCrs.toWkt()) output_interpolation_raster.SetProjection(outRasterSRS.ExportToWkt()) band.FlushCache() - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") - feedback.setProgress(100) - + feedback.setProgress(100) + results = {} results[self.OUTPUT] = output_path - return results \ No newline at end of file + return results diff --git a/algs/OdMatrixFromLayersAsLines.py b/algs/OdMatrixFromLayersAsLines.py index 1d82d3d..789fb00 100644 --- a/algs/OdMatrixFromLayersAsLines.py +++ b/algs/OdMatrixFromLayersAsLines.py @@ -3,10 +3,10 @@ *************************************************************************** OdMatrixFromLayersAsLines.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -41,6 +41,7 @@ QgsGeometry, QgsFeatureSink, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource, @@ -65,9 +66,8 @@ class OdMatrixFromLayersAsLines(QgisAlgorithm): FROM_POINT_LAYER = 'FROM_POINT_LAYER' FROM_ID_FIELD = 'FROM_ID_FIELD' TO_POINT_LAYER = 'TO_POINT_LAYER' - TO_ID_FIELD = 'TO_ID_FIELD' + TO_ID_FIELD = 'TO_ID_FIELD' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -86,28 +86,29 @@ def group(self): def groupId(self): return 'networkbaseddistancematrices' - + def name(self): return 'OdMatrixFromLayersAsLines' def displayName(self): return self.tr('OD Matrix from Layers as Lines (m:n)') - + def shortHelpString(self): return "General:
"\ - "This algorithm implements OD-Matrix analysis to return the matrix of origin-destination pairs as lines yielding network based costs on a given network dataset between two layer of points (m:n).
"\ + "This algorithm implements OD Matrix analysis to return the matrix of origin-destination pairs as lines yielding network based costs on a given network dataset between two layer of points (m:n).
"\ "It accounts for points outside of the network (eg. non-network-elements). Distances are measured accounting for ellipsoids, entry-, exit-, network- and total costs are listed in the result attribute-table.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ - "" - - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + + def print_typestring(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -120,47 +121,40 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Network Layer'), + self.tr('Network layer'), [QgsProcessing.TypeVectorLine])) - + self.addParameter(QgsProcessingParameterFeatureSource(self.FROM_POINT_LAYER, - self.tr('From-Point Layer'), + self.tr('Input point layer (origin points)'), [QgsProcessing.TypeVectorPoint])) - + self.addParameter(QgsProcessingParameterField(self.FROM_ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Input unique ID field'), None, self.FROM_POINT_LAYER, optional=False)) - + self.addParameter(QgsProcessingParameterFeatureSource(self.TO_POINT_LAYER, - self.tr('To-Point Layer'), + self.tr('Target point layer (destination points)'), [QgsProcessing.TypeVectorPoint])) - + self.addParameter(QgsProcessingParameterField(self.TO_ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Target unique ID field'), None, self.TO_POINT_LAYER, optional=False)) - + self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -180,7 +174,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -191,7 +185,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -208,7 +202,6 @@ def processAlgorithm(self, parameters, context, feedback): to_id_field = self.parameterAsString(parameters, self.TO_ID_FIELD, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -217,9 +210,15 @@ def processAlgorithm(self, parameters, context, feedback): speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) #str defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) #float tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float - + analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != from_points.sourceCrs() or from_points.sourceCrs() != to_points.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + #Points of both layers have to be merged into one layer --> then tied to the Qneat3Network #get point list of from layer from_coord_list = getListOfPoints(from_points) @@ -227,34 +226,35 @@ def processAlgorithm(self, parameters, context, feedback): to_coord_list = getListOfPoints(to_points) merged_coords = from_coord_list + to_coord_list - + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") net = Qneat3Network(network, merged_coords, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) - + #read the merged point-list seperately for the two layers --> index at the first element of the second layer begins at len(firstLayer) and gets added the index of the current point of layer b. - list_from_apoints = [Qneat3AnalysisPoint("from", feature, from_id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(from_points))] - list_to_apoints = [Qneat3AnalysisPoint("to", feature, to_id_field, net, net.list_tiedPoints[from_coord_list_length+i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(to_points))] - + list_from_apoints = [Qneat3AnalysisPoint("from", feature, from_id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(from_points))] + list_to_apoints = [Qneat3AnalysisPoint("to", feature, to_id_field, net, net.list_tiedPoints[from_coord_list_length+i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(to_points))] + feat = QgsFeature() fields = QgsFields() - output_id_field_data_type = getFieldDatatype(from_points, from_id_field) - fields.append(QgsField('origin_id', output_id_field_data_type, '', 254, 0)) - fields.append(QgsField('destination_id', output_id_field_data_type, '', 254, 0)) + orig_id_field_data_type = getFieldDatatype(from_points, from_id_field) + dest_id_field_data_type = getFieldDatatype(to_points, to_id_field) + fields.append(QgsField('InputID', orig_id_field_data_type, '', 254, 0)) + fields.append(QgsField('TargetID', dest_id_field_data_type, '', 254, 0)) fields.append(QgsField('entry_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('network_cost', QVariant.Double, '', 20, 7)) fields.append(QgsField('exit_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('total_cost', QVariant.Double, '', 20,7)) feat.setFields(fields) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) - + total_workload = float(len(from_coord_list)*len(to_coord_list)) feedback.pushInfo("[QNEAT3Algorithm] Expecting total workload of {} iterations".format(int(total_workload))) - - + + current_workstep_number = 0 - + for start_point in list_from_apoints: #optimize in case of undirected (not necessary to call calcDijkstra as it has already been calculated - can be replaced by reading from list) dijkstra_query = net.calcDijkstra(start_point.network_vertex_id, 0) @@ -262,8 +262,8 @@ def processAlgorithm(self, parameters, context, feedback): if (current_workstep_number%1000)==0: feedback.pushInfo("[QNEAT3Algorithm] {} OD-pairs processed...".format(current_workstep_number)) if dijkstra_query[0][query_point.network_vertex_id] == -1: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id #do not populate cost field so that it defaults to null sink.addFeature(feat, QgsFeatureSink.FastInsert) else: @@ -271,23 +271,22 @@ def processAlgorithm(self, parameters, context, feedback): network_cost = dijkstra_query[1][query_point.network_vertex_id] exit_cost = query_point.entry_cost total_cost = network_cost + entry_cost + exit_cost - + feat.setGeometry(QgsGeometry.fromPolylineXY([start_point.point_geom, query_point.point_geom])) - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['entry_cost'] = entry_cost feat['network_cost'] = network_cost feat['exit_cost'] = exit_cost feat['total_cost'] = total_cost - sink.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) current_workstep_number=current_workstep_number+1 feedback.setProgress((current_workstep_number/total_workload)*100) - + feedback.pushInfo("[QNEAT3Algorithm] Total number of OD-pairs processed: {}".format(current_workstep_number)) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/OdMatrixFromLayersAsTable.py b/algs/OdMatrixFromLayersAsTable.py index 0ed6b5f..320c52c 100644 --- a/algs/OdMatrixFromLayersAsTable.py +++ b/algs/OdMatrixFromLayersAsTable.py @@ -3,10 +3,10 @@ *************************************************************************** OdMatrixFromLayersAsTable.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFeature, QgsFeatureSink, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource, @@ -64,9 +65,8 @@ class OdMatrixFromLayersAsTable(QgisAlgorithm): FROM_POINT_LAYER = 'FROM_POINT_LAYER' FROM_ID_FIELD = 'FROM_ID_FIELD' TO_POINT_LAYER = 'TO_POINT_LAYER' - TO_ID_FIELD = 'TO_ID_FIELD' + TO_ID_FIELD = 'TO_ID_FIELD' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -85,7 +85,7 @@ def group(self): def groupId(self): return 'networkbaseddistancematrices' - + def name(self): return 'OdMatrixFromLayersAsTable' @@ -98,14 +98,15 @@ def shortHelpString(self): "It accounts for points outside of the network (eg. non-network-elements). Distances are measured accounting for ellipsoids, entry-, exit-, network- and total costs are listed in the result attribute-table.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one table:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def print_typestring(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -118,46 +119,39 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)')] - - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time')] self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network layer'), [QgsProcessing.TypeVectorLine])) - + self.addParameter(QgsProcessingParameterFeatureSource(self.FROM_POINT_LAYER, - self.tr('From-Point Layer'), + self.tr('Input point layer (origin points)'), [QgsProcessing.TypeVectorPoint])) - + self.addParameter(QgsProcessingParameterField(self.FROM_ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Input unique ID field'), None, self.FROM_POINT_LAYER, optional=False)) - + self.addParameter(QgsProcessingParameterFeatureSource(self.TO_POINT_LAYER, - self.tr('To-Point Layer'), + self.tr('Target point layer (destination points)'), [QgsProcessing.TypeVectorPoint])) - + self.addParameter(QgsProcessingParameterField(self.TO_ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Target unique ID field'), None, self.TO_POINT_LAYER, optional=False)) - + self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -177,7 +171,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -188,7 +182,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -204,8 +198,7 @@ def processAlgorithm(self, parameters, context, feedback): to_points = self.parameterAsSource(parameters, self.TO_POINT_LAYER, context) to_id_field = self.parameterAsString(parameters, self.TO_ID_FIELD, context) strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int + directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -214,9 +207,15 @@ def processAlgorithm(self, parameters, context, feedback): speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) #str defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) #float tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float - + analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != from_points.sourceCrs() or from_points.sourceCrs() != to_points.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + #Points of both layers have to be merged into one layer --> then tied to the Qneat3Network #get point list of from layer from_coord_list = getListOfPoints(from_points) @@ -224,35 +223,36 @@ def processAlgorithm(self, parameters, context, feedback): to_coord_list = getListOfPoints(to_points) merged_coords = from_coord_list + to_coord_list - + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") net = Qneat3Network(network, merged_coords, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) - + #read the merged point-list seperately for the two layers --> index at the first element of the second layer begins at len(firstLayer) and gets added the index of the current point of layer b. - list_from_apoints = [Qneat3AnalysisPoint("from", feature, from_id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(from_points))] - list_to_apoints = [Qneat3AnalysisPoint("to", feature, to_id_field, net, net.list_tiedPoints[from_coord_list_length+i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(to_points))] - + list_from_apoints = [Qneat3AnalysisPoint("from", feature, from_id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(from_points))] + list_to_apoints = [Qneat3AnalysisPoint("to", feature, to_id_field, net, net.list_tiedPoints[from_coord_list_length+i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(to_points))] + feat = QgsFeature() fields = QgsFields() - output_id_field_data_type = getFieldDatatype(from_points, from_id_field) - fields.append(QgsField('origin_id', output_id_field_data_type, '', 254, 0)) - fields.append(QgsField('destination_id', output_id_field_data_type, '', 254, 0)) + orig_id_field_data_type = getFieldDatatype(from_points, from_id_field) + dest_id_field_data_type = getFieldDatatype(to_points, to_id_field) + fields.append(QgsField('InputID', orig_id_field_data_type, '', 254, 0)) + fields.append(QgsField('TargetID', dest_id_field_data_type, '', 254, 0)) fields.append(QgsField('entry_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('network_cost', QVariant.Double, '', 20, 7)) fields.append(QgsField('exit_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('total_cost', QVariant.Double, '', 20,7)) feat.setFields(fields) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, network.sourceCrs()) - + total_workload = float(len(from_coord_list)*len(to_coord_list)) feedback.pushInfo("[QNEAT3Algorithm] Expecting total workload of {} iterations".format(int(total_workload))) - - + + current_workstep_number = 0 - + for start_point in list_from_apoints: #optimize in case of undirected (not necessary to call calcDijkstra as it has already been calculated - can be replaced by reading from list) dijkstra_query = net.calcDijkstra(start_point.network_vertex_id, 0) @@ -260,27 +260,26 @@ def processAlgorithm(self, parameters, context, feedback): if (current_workstep_number%1000)==0: feedback.pushInfo("[QNEAT3Algorithm] {} OD-pairs processed...".format(current_workstep_number)) if dijkstra_query[0][query_point.network_vertex_id] == -1: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id #do not populate cost field so that it defaults to null sink.addFeature(feat, QgsFeatureSink.FastInsert) else: network_cost = dijkstra_query[1][query_point.network_vertex_id] - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['entry_cost'] = start_point.entry_cost feat['network_cost'] = network_cost feat['exit_cost'] = query_point.entry_cost feat['total_cost'] = network_cost + start_point.entry_cost + query_point.entry_cost - sink.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) current_workstep_number=current_workstep_number+1 feedback.setProgress((current_workstep_number/total_workload)*100) - + feedback.pushInfo("[QNEAT3Algorithm] Total number of OD-pairs processed: {}".format(current_workstep_number)) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/OdMatrixFromPointsAsCsv.py b/algs/OdMatrixFromPointsAsCsv.py index e4439ba..1bac9d5 100644 --- a/algs/OdMatrixFromPointsAsCsv.py +++ b/algs/OdMatrixFromPointsAsCsv.py @@ -3,10 +3,10 @@ *************************************************************************** OdMatrixFromPointsAsCsv.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -35,6 +35,7 @@ from qgis.PyQt.QtGui import QIcon from qgis.core import (QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterFileDestination, QgsProcessingParameterFeatureSource, @@ -57,9 +58,8 @@ class OdMatrixFromPointsAsCsv(QgisAlgorithm): INPUT = 'INPUT' POINTS = 'POINTS' - ID_FIELD = 'ID_FIELD' + ID_FIELD = 'ID_FIELD' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -78,27 +78,28 @@ def group(self): def groupId(self): return 'networkbaseddistancematrices' - + def name(self): return 'OdMatrixFromPointsAsCsv' def displayName(self): - return self.tr('OD-Matrix from Points as CSV (n:n)') - + return self.tr('OD Matrix from Points as CSV (n:n)') + def shortHelpString(self): return "General:
"\ - "This algorithm implements OD-Matrix analysis to return the matrix of origin-destination pairs as csv-file yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ + "This algorithm implements OD Matrix analysis to return the matrix of origin-destination pairs as csv-file yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ "It accounts for points outside of the network (eg. non-network-elements). Distances are measured accounting for ellipsoids, entry-, exit-, network- and total costs are listed in the result attribute-table.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one file:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def print_typestring(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -111,35 +112,27 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Network Layer'), + self.tr('Network layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterFeatureSource(self.POINTS, - self.tr('Point Layer'), + self.tr('Input point layer (origin points)'), [QgsProcessing.TypeVectorPoint])) self.addParameter(QgsProcessingParameterField(self.ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Input unique ID field'), None, self.POINTS, optional=False)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -159,7 +152,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -170,7 +163,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -185,7 +178,6 @@ def processAlgorithm(self, parameters, context, feedback): id_field = self.parameterAsString(parameters, self.ID_FIELD, context) #str strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -196,26 +188,33 @@ def processAlgorithm(self, parameters, context, feedback): tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float output_path = self.parameterAsFileOutput(parameters, self.OUTPUT, context) #str (filepath) feedback.pushInfo(pluginPath) - + analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != points.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") net = Qneat3Network(network, points, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) - - list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] - + + list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] + total_workload = float(pow(len(list_analysis_points),2)) feedback.pushInfo("[QNEAT3Algorithm] Expecting total workload of {} iterations".format(int(total_workload))) - + with open(output_path, 'w', newline='') as csvfile: csv_writer = csv.writer(csvfile, delimiter=';', - quotechar='|', + quotechar='|', quoting=csv.QUOTE_MINIMAL) #write header - csv_writer.writerow(["origin_id","destination_id","entry_cost", "network_cost", "exit_cost", "total_cost"]) - + csv_writer.writerow(["InputID","TargetID","entry_cost", "network_cost", "exit_cost", "total_cost"]) + current_workstep_number = 0 - + for start_point in list_analysis_points: #optimize in case of undirected (not necessary to call calcDijkstra as it has already been calculated - can be replaced by reading from list) dijkstra_query = net.calcDijkstra(start_point.network_vertex_id, 0) @@ -234,11 +233,10 @@ def processAlgorithm(self, parameters, context, feedback): csv_writer.writerow([start_point.point_id, query_point.point_id, entry_cost, network_cost, exit_cost, total_cost]) current_workstep_number=current_workstep_number+1 feedback.setProgress((current_workstep_number/total_workload)*100) - + feedback.pushInfo("[QNEAT3Algorithm] Total number of OD-pairs processed: {}".format(current_workstep_number)) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") results = {self.OUTPUT: output_path} return results - diff --git a/algs/OdMatrixFromPointsAsLines.py b/algs/OdMatrixFromPointsAsLines.py index ae72a99..1b77c85 100644 --- a/algs/OdMatrixFromPointsAsLines.py +++ b/algs/OdMatrixFromPointsAsLines.py @@ -3,10 +3,10 @@ *************************************************************************** OdMatrixFromPointsAsLines.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -41,6 +41,7 @@ QgsFeature, QgsFeatureSink, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource, @@ -63,9 +64,8 @@ class OdMatrixFromPointsAsLines(QgisAlgorithm): INPUT = 'INPUT' POINTS = 'POINTS' - ID_FIELD = 'ID_FIELD' + ID_FIELD = 'ID_FIELD' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -84,27 +84,28 @@ def group(self): def groupId(self): return 'networkbaseddistancematrices' - + def name(self): return 'OdMatrixFromPointsAsLines' def displayName(self): - return self.tr('OD-Matrix from Points as Lines (n:n)') + return self.tr('OD Matrix from Points as Lines (n:n)') def shortHelpString(self): return "General:
"\ - "This algorithm implements OD-Matrix analysis to return the matrix of origin-destination pairs as lines yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ + "This algorithm implements OD Matrix analysis to return the matrix of origin-destination pairs as lines yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ "It accounts for points outside of the network (eg. non-network-elements). Distances are measured accounting for ellipsoids, entry-, exit-, network- and total costs are listed in the result attribute-table.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one layer:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def print_typestring(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -118,33 +119,26 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)')] - - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time')] self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterFeatureSource(self.POINTS, - self.tr('Point Layer'), + self.tr('Input point layer (origin points)'), [QgsProcessing.TypeVectorPoint])) self.addParameter(QgsProcessingParameterField(self.ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Unique point ID field'), None, self.POINTS, optional=False)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -164,7 +158,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -175,7 +169,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -191,7 +185,6 @@ def processAlgorithm(self, parameters, context, feedback): id_field = self.parameterAsString(parameters, self.ID_FIELD, context) #str strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -200,35 +193,41 @@ def processAlgorithm(self, parameters, context, feedback): speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) #str defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) #float tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float - + analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != points.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") net = Qneat3Network(network, points, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) - - list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] - + + list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] + feat = QgsFeature() fields = QgsFields() output_id_field_data_type = getFieldDatatype(points, id_field) - fields.append(QgsField('origin_id', output_id_field_data_type, '', 254, 0)) - fields.append(QgsField('destination_id', output_id_field_data_type, '', 254, 0)) + fields.append(QgsField('InputID', output_id_field_data_type, '', 254, 0)) + fields.append(QgsField('TargetID', output_id_field_data_type, '', 254, 0)) fields.append(QgsField('entry_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('network_cost', QVariant.Double, '', 20, 7)) fields.append(QgsField('exit_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('total_cost', QVariant.Double, '', 20,7)) feat.setFields(fields) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) - + total_workload = float(pow(len(list_analysis_points),2)) feedback.pushInfo("[QNEAT3Algorithm] Expecting total workload of {} iterations".format(int(total_workload))) - - + + current_workstep_number = 0 - + for start_point in list_analysis_points: #optimize in case of undirected (not necessary to call calcDijkstra as it has already been calculated - can be replaced by reading from list) dijkstra_query = net.calcDijkstra(start_point.network_vertex_id, 0) @@ -236,35 +235,34 @@ def processAlgorithm(self, parameters, context, feedback): if (current_workstep_number%1000)==0: feedback.pushInfo("[QNEAT3Algorithm] {} OD-pairs processed...".format(current_workstep_number)) if query_point.point_id == start_point.point_id: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['entry_cost'] = 0.0 feat['network_cost'] = 0.0 feat['exit_cost'] = 0.0 feat['total_cost'] = 0.0 sink.addFeature(feat, QgsFeatureSink.FastInsert) elif dijkstra_query[0][query_point.network_vertex_id] == -1: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id #do not populate cost field so that it defaults to null sink.addFeature(feat, QgsFeatureSink.FastInsert) else: network_cost = dijkstra_query[1][query_point.network_vertex_id] feat.setGeometry(QgsGeometry.fromPolylineXY([start_point.point_geom, query_point.point_geom])) - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['entry_cost'] = start_point.entry_cost feat['network_cost'] = network_cost feat['exit_cost'] = query_point.entry_cost feat['total_cost'] = network_cost + start_point.entry_cost + query_point.entry_cost - sink.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) current_workstep_number=current_workstep_number+1 feedback.setProgress((current_workstep_number/total_workload)*100) - + feedback.pushInfo("[QNEAT3Algorithm] Total number of OD-pairs processed: {}".format(current_workstep_number)) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/OdMatrixFromPointsAsTable.py b/algs/OdMatrixFromPointsAsTable.py index 0c3a315..f8c789d 100644 --- a/algs/OdMatrixFromPointsAsTable.py +++ b/algs/OdMatrixFromPointsAsTable.py @@ -3,10 +3,10 @@ *************************************************************************** OdMatrixFromPointsAsTable.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -40,6 +40,7 @@ QgsFeature, QgsFeatureSink, QgsProcessing, + QgsProcessingException, QgsProcessingParameterEnum, QgsProcessingParameterFeatureSink, QgsProcessingParameterFeatureSource, @@ -62,9 +63,8 @@ class OdMatrixFromPointsAsTable(QgisAlgorithm): INPUT = 'INPUT' POINTS = 'POINTS' - ID_FIELD = 'ID_FIELD' + ID_FIELD = 'ID_FIELD' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -83,7 +83,7 @@ def group(self): def groupId(self): return 'networkbaseddistancematrices' - + def name(self): return 'OdMatrixFromPointsAsTable' @@ -92,18 +92,19 @@ def displayName(self): def shortHelpString(self): return "General:
"\ - "This algorithm implements OD-Matrix analysis to return the matrix of origin-destination pairs as table yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ + "This algorithm implements OD Matrix analysis to return the matrix of origin-destination pairs as table yielding network based costs on a given network dataset between the elements of one point layer(n:n).
"\ "It accounts for points outside of the network (eg. non-network-elements). Distances are measured accounting for ellipsoids, entry-, exit-, network- and total costs are listed in the result attribute-table.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is one table:"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def print_typestring(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -116,34 +117,26 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)')] - - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time')] self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, - self.tr('Network Layer'), + self.tr('Network layer'), [QgsProcessing.TypeVectorLine])) self.addParameter(QgsProcessingParameterFeatureSource(self.POINTS, - self.tr('Point Layer'), + self.tr('Input point layer (origin points)'), [QgsProcessing.TypeVectorPoint])) self.addParameter(QgsProcessingParameterField(self.ID_FIELD, - self.tr('Unique Point ID Field'), + self.tr('Input unique ID field'), None, self.POINTS, optional=False)) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -163,7 +156,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -174,7 +167,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -188,8 +181,7 @@ def processAlgorithm(self, parameters, context, feedback): points = self.parameterAsSource(parameters, self.POINTS, context) #QgsProcessingFeatureSource id_field = self.parameterAsString(parameters, self.ID_FIELD, context) #str strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int + directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -198,35 +190,41 @@ def processAlgorithm(self, parameters, context, feedback): speedFieldName = self.parameterAsString(parameters, self.SPEED_FIELD, context) #str defaultSpeed = self.parameterAsDouble(parameters, self.DEFAULT_SPEED, context) #float tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float - + analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != points.sourceCrs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + feedback.pushInfo("[QNEAT3Algorithm] Building Graph...") net = Qneat3Network(network, points, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) - - list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] - + + list_analysis_points = [Qneat3AnalysisPoint("point", feature, id_field, net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(getFeaturesFromQgsIterable(net.input_points))] + feat = QgsFeature() fields = QgsFields() output_id_field_data_type = getFieldDatatype(points, id_field) - fields.append(QgsField('origin_id', output_id_field_data_type, '', 254, 0)) - fields.append(QgsField('destination_id', output_id_field_data_type, '', 254, 0)) + fields.append(QgsField('InputID', output_id_field_data_type, '', 254, 0)) + fields.append(QgsField('TargetID', output_id_field_data_type, '', 254, 0)) fields.append(QgsField('entry_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('network_cost', QVariant.Double, '', 20, 7)) fields.append(QgsField('exit_cost', QVariant.Double, '', 20,7)) fields.append(QgsField('total_cost', QVariant.Double, '', 20,7)) feat.setFields(fields) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.NoGeometry, network.sourceCrs()) - + total_workload = float(pow(len(list_analysis_points),2)) feedback.pushInfo("[QNEAT3Algorithm] Expecting total workload of {} iterations".format(int(total_workload))) - - + + current_workstep_number = 0 - + for start_point in list_analysis_points: #optimize in case of undirected (not necessary to call calcDijkstra as it has already been calculated - can be replaced by reading from list) dijkstra_query = net.calcDijkstra(start_point.network_vertex_id, 0) @@ -234,32 +232,31 @@ def processAlgorithm(self, parameters, context, feedback): if (current_workstep_number%1000)==0: feedback.pushInfo("[QNEAT3Algorithm] {} OD-pairs processed...".format(current_workstep_number)) if query_point.point_id == start_point.point_id: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['network_cost'] = 0.0 sink.addFeature(feat, QgsFeatureSink.FastInsert) elif dijkstra_query[0][query_point.network_vertex_id] == -1: - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id #do not populate cost field so that it defaults to null sink.addFeature(feat, QgsFeatureSink.FastInsert) else: network_cost = dijkstra_query[1][query_point.network_vertex_id] - feat['origin_id'] = start_point.point_id - feat['destination_id'] = query_point.point_id + feat['InputID'] = start_point.point_id + feat['TargetID'] = query_point.point_id feat['entry_cost'] = start_point.entry_cost feat['network_cost'] = network_cost feat['exit_cost'] = query_point.entry_cost feat['total_cost'] = start_point.entry_cost + network_cost + query_point.entry_cost - sink.addFeature(feat, QgsFeatureSink.FastInsert) + sink.addFeature(feat, QgsFeatureSink.FastInsert) current_workstep_number=current_workstep_number+1 feedback.setProgress(current_workstep_number/total_workload) - + feedback.pushInfo("[QNEAT3Algorithm] Total number of OD-pairs processed: {}".format(current_workstep_number)) - + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") results = {} results[self.OUTPUT] = dest_id return results - diff --git a/algs/ShortestPathBetweenPoints.py b/algs/ShortestPathBetweenPoints.py index 89e3e6f..d71106b 100644 --- a/algs/ShortestPathBetweenPoints.py +++ b/algs/ShortestPathBetweenPoints.py @@ -3,10 +3,10 @@ *************************************************************************** ShortestPathPointToPoint.py --------------------- - - Partially based on QGIS3 network analysis algorithms. - Copyright 2016 Alexander Bruy - + + Partially based on QGIS3 network analysis algorithms. + Copyright 2016 Alexander Bruy + Date : February 2018 Copyright : (C) 2018 by Clemens Raffler Email : clemens dot raffler at gmail dot com @@ -67,7 +67,6 @@ class ShortestPathBetweenPoints(QgisAlgorithm): START_POINT = 'START_POINT' END_POINT = 'END_POINT' STRATEGY = 'STRATEGY' - ENTRY_COST_CALCULATION_METHOD = 'ENTRY_COST_CALCULATION_METHOD' DIRECTION_FIELD = 'DIRECTION_FIELD' VALUE_FORWARD = 'VALUE_FORWARD' VALUE_BACKWARD = 'VALUE_BACKWARD' @@ -86,13 +85,13 @@ def group(self): def groupId(self): return 'networkanalysis' - + def name(self): return 'shortestpathpointtopoint' def displayName(self): return self.tr('Shortest path (point to point)') - + def shortHelpString(self): return "General:
"\ "This algorithm implements the Dijkstra-Search to return the shortest path between two points on a given network dataset.
"\ @@ -100,14 +99,15 @@ def shortHelpString(self): "separate entry- and exit-costs. Distances are measured accounting for ellipsoids.

"\ "Parameters (required):
"\ "Following Parameters must be set to run the algorithm:"\ - "
"\ + "
"\ "Parameters (optional):
"\ "There are also a number of optional parameters to implement direction dependent shortest paths and provide information on speeds on the networks edges."\ "
"\ "Output:
"\ "The output of the algorithm is a Layer containing a single linestring, the attributes showcase the"\ - "" - + ""\ + "Shortest distance cost units are meters and Fastest time cost units are seconds." + def msg(self, var): return "Type:"+str(type(var))+" repr: "+var.__str__() @@ -120,14 +120,10 @@ def initAlgorithm(self, config=None): (self.tr('Backward direction'), QgsVectorLayerDirector.DirectionBackward), (self.tr('Both directions'), QgsVectorLayerDirector.DirectionBoth)]) - self.STRATEGIES = [self.tr('Shortest Path (distance optimization)'), - self.tr('Fastest Path (time optimization)') + self.STRATEGIES = [self.tr('Shortest distance'), + self.tr('Fastest time') ] - self.ENTRY_COST_CALCULATION_METHODS = [self.tr('Ellipsoidal'), - self.tr('Planar (only use with projected CRS)')] - - self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Network Layer'), [QgsProcessing.TypeVectorLine])) @@ -136,15 +132,11 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterPoint(self.END_POINT, self.tr('End point'))) self.addParameter(QgsProcessingParameterEnum(self.STRATEGY, - self.tr('Optimization Criterion'), + self.tr('Path type to calculate'), self.STRATEGIES, defaultValue=0)) params = [] - params.append(QgsProcessingParameterEnum(self.ENTRY_COST_CALCULATION_METHOD, - self.tr('Entry Cost calculation method'), - self.ENTRY_COST_CALCULATION_METHODS, - defaultValue=0)) params.append(QgsProcessingParameterField(self.DIRECTION_FIELD, self.tr('Direction field'), None, @@ -164,7 +156,7 @@ def initAlgorithm(self, config=None): list(self.DIRECTIONS.keys()), defaultValue=2)) params.append(QgsProcessingParameterField(self.SPEED_FIELD, - self.tr('Speed field'), + self.tr('Speed field (km/h)'), None, self.INPUT, optional=True)) @@ -175,7 +167,7 @@ def initAlgorithm(self, config=None): params.append(QgsProcessingParameterNumber(self.TOLERANCE, self.tr('Topology tolerance'), QgsProcessingParameterNumber.Double, - 0.0, False, 0, 99999999.99)) + 0.00001, False, 0, 99999999.99)) for p in params: p.setFlags(p.flags() | QgsProcessingParameterDefinition.FlagAdvanced) @@ -193,7 +185,6 @@ def processAlgorithm(self, parameters, context, feedback): endPoint = self.parameterAsPoint(parameters, self.END_POINT, context, network.sourceCrs()) #QgsPointXY strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int - entry_cost_calc_method = self.parameterAsEnum(parameters, self.ENTRY_COST_CALCULATION_METHOD, context) #int directionFieldName = self.parameterAsString(parameters, self.DIRECTION_FIELD, context) #str (empty if no field given) forwardValue = self.parameterAsString(parameters, self.VALUE_FORWARD, context) #str backwardValue = self.parameterAsString(parameters, self.VALUE_BACKWARD, context) #str @@ -204,43 +195,49 @@ def processAlgorithm(self, parameters, context, feedback): tolerance = self.parameterAsDouble(parameters, self.TOLERANCE, context) #float analysisCrs = network.sourceCrs() - + + if analysisCrs.isGeographic(): + raise QgsProcessingException('QNEAT3 algorithms are designed to work with projected coordinate systems. Please use a projected coordinate system (eg. UTM zones) instead of geographic coordinate systems (eg. WGS84)!') + + if analysisCrs != context.project().crs(): + raise QgsProcessingException('QNEAT3 algorithms require that all inputs to be the same projected coordinate reference system (including project coordinate system).') + input_qgspointxy_list = [startPoint,endPoint] input_points = [getFeatureFromPointParameter(startPoint),getFeatureFromPointParameter(endPoint)] - + feedback.pushInfo(self.tr('[QNEAT3Algorithm] Building Graph')) feedback.setProgress(10) net = Qneat3Network(network, input_qgspointxy_list, strategy, directionFieldName, forwardValue, backwardValue, bothValue, defaultDirection, analysisCrs, speedFieldName, defaultSpeed, tolerance, feedback) feedback.setProgress(40) - - list_analysis_points = [Qneat3AnalysisPoint("point", feature, "point_id", net, net.list_tiedPoints[i], entry_cost_calc_method, feedback) for i, feature in enumerate(input_points)] - + + list_analysis_points = [Qneat3AnalysisPoint("point", feature, "point_id", net, net.list_tiedPoints[i], feedback) for i, feature in enumerate(input_points)] + start_vertex_idx = list_analysis_points[0].network_vertex_id end_vertex_idx = list_analysis_points[1].network_vertex_id - + feedback.pushInfo("[QNEAT3Algorithm] Calculating shortest path...") feedback.setProgress(50) - + dijkstra_query = net.calcDijkstra(start_vertex_idx,0) - + if dijkstra_query[0][end_vertex_idx] == -1: raise QgsProcessingException(self.tr('Could not find a path from start point to end point - Check your graph or alter the input points.')) - + path_elements = [list_analysis_points[1].point_geom] #start route with the endpoint outside the network - path_elements.append(net.network.vertex(end_vertex_idx).point()) #then append the corresponding vertex of the graph - + path_elements.append(net.network.vertex(end_vertex_idx).point()) #then append the corresponding vertex of the graph + count = 1 current_vertex_idx = end_vertex_idx while current_vertex_idx != start_vertex_idx: current_vertex_idx = net.network.edge(dijkstra_query[0][current_vertex_idx]).fromVertex() path_elements.append(net.network.vertex(current_vertex_idx).point()) - - + + count = count + 1 if count%10 == 0: feedback.pushInfo("[QNEAT3Algorithm] Taversed {} Nodes...".format(count)) - - path_elements.append(list_analysis_points[0].point_geom) #end path with startpoint outside the network + + path_elements.append(list_analysis_points[0].point_geom) #end path with startpoint outside the network feedback.pushInfo("[QNEAT3Algorithm] Total number of Nodes traversed: {}".format(count+1)) path_elements.reverse() #reverse path elements because it was built from end to start @@ -248,39 +245,34 @@ def processAlgorithm(self, parameters, context, feedback): end_exit_cost = list_analysis_points[1].entry_cost cost_on_graph = dijkstra_query[1][end_vertex_idx] total_cost = start_entry_cost + cost_on_graph + end_exit_cost - + feedback.pushInfo("[QNEAT3Algorithm] Writing path-feature...") feedback.setProgress(80) feat = QgsFeature() - + fields = QgsFields() - fields.append(QgsField('start_id', QVariant.String, '', 254, 0)) - fields.append(QgsField('start_coordinates', QVariant.String, '', 254, 0)) + fields.append(QgsField('start', QVariant.String, '', 254, 0)) fields.append(QgsField('start_entry_cost', QVariant.Double, '', 20, 7)) - fields.append(QgsField('end_id', QVariant.String, '', 254, 0)) - fields.append(QgsField('end_coordinates', QVariant.String, '', 254, 0)) + fields.append(QgsField('end', QVariant.String, '', 254, 0)) fields.append(QgsField('end_exit_cost', QVariant.Double, '', 20, 7)) fields.append(QgsField('cost_on_graph', QVariant.Double, '', 20, 7)) fields.append(QgsField('total_cost', QVariant.Double, '', 20, 7)) feat.setFields(fields) - + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, fields, QgsWkbTypes.LineString, network.sourceCrs()) - - feat['start_id'] = "A" - feat['start_coordinates'] = startPoint.toString() + + feat['start'] = startPoint.toString() feat['start_entry_cost'] = start_entry_cost - feat['end_id'] = "B" - feat['end_coordinates'] = endPoint.toString() + feat['end'] = endPoint.toString() feat['end_exit_cost'] = end_exit_cost feat['cost_on_graph'] = cost_on_graph - feat['total_cost'] = total_cost + feat['total_cost'] = total_cost geom = QgsGeometry.fromPolylineXY(path_elements) feat.setGeometry(geom) - + sink.addFeature(feat, QgsFeatureSink.FastInsert) - feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") + feedback.pushInfo("[QNEAT3Algorithm] Ending Algorithm") feedback.setProgress(100) results = {} results[self.OUTPUT] = dest_id return results -