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:"\
- "
- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level for Iso-Area
- Cost Intervals for Iso-Area Bands
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Contour Intervals in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm are two layers:"\
- "- TIN-Interpolation Distance Raster
- Iso-Area Contours with cost levels as attributes
"
-
+ "- TIN-Interpolation Distance Raster
- Iso-Area Contours with cost levels as attributes
"\
+ "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:"\
- "- Network Layer
- Startpoint
- Maximum cost level for Iso-Area
- Cost Intervals for Iso-Area Bands
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Contour Intervals in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm are two layers:"\
- "- TIN-Interpolation Distance Raster
- Iso-Area Contours with cost levels as attributes
"
-
-
+ "- TIN-Interpolation Distance Raster
- Iso-Area Contours with cost levels as attributes
"\
+ "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:"\
- "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level for Iso-Area
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
- "- TIN-Interpolation Distance Raster
"
-
-
+ "- TIN-Interpolation Distance Raster
"\
+ "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:"\
- "- Network Layer
- Startpoint
- Maximum cost level for Iso-Area
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
- "- TIN-Interpolation Distance Raster
"
-
+ "- TIN-Interpolation Distance Raster
"\
+ "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:"\
- "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level for Iso-Area
- Cost Strategy
"\
+ "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
"- Point layer of reachable network nodes
"\
- "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:"\
- "- Network Layer
- Startpoint
- Unique Point ID Field (numerical)
- Maximum cost level for Iso-Area
- Cost Strategy
"\
+ "- Network Layer
- Startpoint
- Unique Point ID Field (numerical)
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
"- Point layer of reachable network nodes
"\
- "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:"\
- "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level for Iso-Area
- Cost Intervals for Iso-Area Bands
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint Layer
- Unique Point ID Field (numerical)
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Contour Intervals in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm are two layers:"\
- "- TIN-Interpolation Distance Raster
- Iso-Area Polygons with cost levels as attributes
"
-
+ "- TIN-Interpolation Distance Raster
- Iso-Area Polygons with cost levels as attributes
"\
+ "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:"\
- "- Network Layer
- Startpoint
- Maximum cost level for Iso-Area
- Cost Intervals for Iso-Area Bands
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Contour Intervals in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm are two layers:"\
- "- TIN-Interpolation Distance Raster
- Iso-Area Polygons with cost levels as attributes
"
-
+ "- TIN-Interpolation Distance Raster
- Iso-Area Polygons with cost levels as attributes
"\
+ "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:"\
- "- Network Layer
- Startpoint
- Maximum cost level for Iso-Area
- Cellsize in Meters (increase default when analyzing larger networks)
- Cost Strategy
"\
+ "- Network Layer
- Startpoint
- Maximum cost level of Iso-Area in distance (meters) or time (seconds)
- Cellsize in Meters (increase default when analyzing larger networks)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
- "- TIN-Interpolation Distance Raster
"
-
+ "- TIN-Interpolation Distance Raster
"\
+ "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:"\
- "- Network Layer
- From-Point Layer
- Unique From-Point ID Field (numerical)
- To-Point Layer
- Unique To-Point ID Field (numerical)
- Cost Strategy
"\
+ "- Network layer
- Input point layer (origin points)
- Input unique ID field
- Target point layer
- Target unique ID field (destination points)
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
- "- OD-Matrix as lines with network based distances as attributes
"
-
-
+ "- OD Matrix as lines with network based distances as attributes
"\
+ "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:"\
- "- Network Layer
- From-Point Layer
- Unique From-Point ID Field (numerical)
- To-Point Layer
- Unique To-Point ID Field (numerical)
- Cost Strategy
"\
+ "- Network layer
- Input point layer (origin points)
- Input unique ID field
- Target point layer (destination points)
- Target unique ID field
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one table:"\
- "- OD-Matrix as table with network based distances as attributes
"
-
+ "- OD Matrix as table with network based distances as attributes
"\
+ "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:"\
- "- Network Layer
- Point Layer
- Unique Point ID Field (numerical)
- Cost Strategy
"\
+ "- Network layer
- Input point layer (origin points)
- Input unique ID field
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one file:"\
- "- OD-Matrix as csv-file with network based distances as attributes
"
-
+ "- OD Matrix as csv-file with network based distances as attributes
"\
+ "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:"\
- "- Network Layer
- Point Layer
- Unique Point ID Field (numerical)
- Cost Strategy
"\
+ "- Network layer
- Input point layer (origin points)
- Input unique ID
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one layer:"\
- "- OD-Matrix as lines with network based distances as attributes
"
-
+ "- OD Matrix as lines with network based distances as attributes
"\
+ "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:"\
- "- Network Layer
- Point Layer
- Unique Point ID Field (numerical)
- Cost Strategy
"\
+ "- Network Layer
- Input point layer (origin points)
- Input unique ID field
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is one table:"\
- "- OD-Matrix as table with network based distances as attributes
"
-
+ "- OD Matrix as table with network based distances as attributes
"\
+ "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:"\
- "- Network Layer
- Startpoint Coordinates
- Endpoint Coordinates
- Cost Strategy
"\
+ "- Network Layer
- Startpoint Coordinates
- Endpoint Coordinates
- Path type to calculate
"\
"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."\
"- Direction Field
- Value for forward direction
- Value for backward direction
- Value for both directions
- Default direction
- Speed Field
- Default Speed (affects entry/exit costs)
- Topology tolerance
"\
"Output:
"\
"The output of the algorithm is a Layer containing a single linestring, the attributes showcase the"\
- "- Name and coordinates of startpoint
- Name and coordinates of endpoint
- Entry-cost to enter network
- Exit-cost to exit network
- Cost of shortest path on graph
- Total cost as sum of all cost elements
"
-
+ "- Name and coordinates of startpoint
- Name and coordinates of endpoint
- Entry-cost to enter network
- Exit-cost to exit network
- Cost of shortest path on graph
- Total cost as sum of all cost elements
"\
+ "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
-