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) 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: vertex_found = True vertex_type = "from_vertex" from_point = n_nearest_feature.geometry().asPoint() from_vertex_cost = n_nearest_feature['cost'] if n_nearest_vertex_id == to_vertex_id: vertex_found = True 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) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = from_point.distance(segment_point) #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #self.feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = from_vertex_cost + dist_edge + dist_to_segment raster_data[i, j] = pixel_cost elif vertex_type == "to_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY( [nearest_vertex.point(), to_point]) res = nearest_edge_geometry.closestSegmentWithContext( current_pixel_midpoint) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = to_point.distance(segment_point) #self.feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #self.feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #self.feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = to_vertex_cost + dist_edge + dist_to_segment 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 processAlgorithm(self, parameters, context, feedback): feedback.pushInfo( self.tr( "[QNEAT3Algorithm] This is a QNEAT3 Algorithm: '{}'".format( self.displayName()))) network = self.parameterAsVectorLayer(parameters, self.INPUT, context) #QgsVectorLayer startPoint = self.parameterAsPoint(parameters, self.START_POINT, context, network.sourceCrs()) #QgsPointXY max_dist = self.parameterAsDouble(parameters, self.MAX_DIST, context) #float cell_size = self.parameterAsInt(parameters, self.CELL_SIZE, context) #int strategy = self.parameterAsEnum(parameters, self.STRATEGY, context) #int interpolation_method = self.parameterAsEnum(parameters, self.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 bothValue = self.parameterAsString(parameters, self.VALUE_BOTH, context) #str defaultDirection = self.parameterAsEnum(parameters, self.DEFAULT_DIRECTION, context) #int 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 output_path = self.parameterAsOutputLayer(parameters, self.OUTPUT, context) 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) 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], 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..." ) net.calcIsoTinInterpolation(iso_pointcloud_layer, cell_size, output_path) feedback.setProgress(99) else: #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) 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() 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 = 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: 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) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = from_point.distance(segment_point) #feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = from_vertex_cost + dist_edge + dist_to_segment raster_routingcost_data[i, j] = pixel_cost elif vertex_type == "to_vertex": nearest_edge_geometry = QgsGeometry().fromPolylineXY( [nearest_vertex.point(), to_point]) res = nearest_edge_geometry.closestSegmentWithContext( current_pixel_midpoint) segment_point = res[ 1] #[0: distance, 1: point, 2: left_of, 3: epsilon for snapping] dist_to_segment = segment_point.distance( current_pixel_midpoint) dist_edge = to_point.distance(segment_point) #feedback.pushInfo("dist_to_segment = {}".format(dist_to_segment)) #feedback.pushInfo("dist_on_edge = {}".format(dist_edge)) #feedback.pushInfo("cost = {}".format(from_vertex_cost)) pixel_cost = to_vertex_cost + dist_edge + dist_to_segment 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) results = {} results[self.OUTPUT] = output_path return results
def topology(self): """Add to nearest segments each vertex in a polygon layer.""" threshold = self.dist_thr # Distance threshold to create nodes dup_thr = self.dup_thr straight_thr = self.straight_thr tp = 0 td = 0 if log.app_level <= logging.DEBUG: debshp = DebugWriter("debug_topology.shp", self) geometries = { f.id(): QgsGeometry(f.geometry()) for f in self.getFeatures() } index = self.get_index() to_change = {} nodes = set() pbar = self.get_progressbar(_("Topology"), len(geometries)) for (gid, geom) in geometries.items(): if geom.area() < config.min_area: continue for point in frozenset(Geometry.get_outer_vertices(geom)): if point not in nodes: area_of_candidates = Point(point).boundingBox(threshold) fids = index.intersects(area_of_candidates) for fid in fids: g = QgsGeometry(geometries[fid]) (p, ndx, ndxa, ndxb, dist_v) = g.closestVertex(point) (dist_s, closest, vertex) = g.closestSegmentWithContext(point)[:3] va = Point(g.vertexAt(ndxa)) vb = Point(g.vertexAt(ndxb)) note = "" if dist_v == 0: dist_a = va.sqrDist(point) dist_b = vb.sqrDist(point) if dist_a < dup_thr**2: g.deleteVertex(ndxa) note = "dupe refused by isGeosValid" if Geometry.is_valid(g): note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % ( dist_a, va.x(), va.y(), point.x(), point.y(), ) nodes.add(p) nodes.add(va) td += 1 if dist_b < dup_thr**2: g.deleteVertex(ndxb) note = "dupe refused by isGeosValid" if Geometry.is_valid(g): note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % ( dist_b, vb.x(), vb.y(), point.x(), point.y(), ) nodes.add(p) nodes.add(vb) td += 1 elif dist_v < dup_thr**2: g.moveVertex(point.x(), point.y(), ndx) note = "dupe refused by isGeosValid" if Geometry.is_valid(g): note = "Merge dup. %.10f %.5f,%.5f->%.5f,%.5f" % ( dist_v, p.x(), p.y(), point.x(), point.y(), ) nodes.add(p) td += 1 elif (dist_s < threshold**2 and closest != va and closest != vb): va = Point(g.vertexAt(vertex)) vb = Point(g.vertexAt(vertex - 1)) angle = abs(point.azimuth(va) - point.azimuth(vb)) note = "Topo refused by angle: %.2f" % angle if abs(180 - angle) <= straight_thr: note = "Topo refused by insertVertex" if g.insertVertex(point.x(), point.y(), vertex): note = "Topo refused by isGeosValid" if Geometry.is_valid(g): note = "Add topo %.6f %.5f,%.5f" % ( dist_s, point.x(), point.y(), ) tp += 1 if note.startswith("Merge") or note.startswith("Add"): to_change[fid] = g geometries[fid] = g if note and log.app_level <= logging.DEBUG: debshp.add_point(point, note) if len(to_change) > BUFFER_SIZE: self.writer.changeGeometryValues(to_change) to_change = {} pbar.update() pbar.close() if len(to_change) > 0: self.writer.changeGeometryValues(to_change) if td: log.debug(_("Merged %d close vertices in the '%s' layer"), td, self.name()) report.values["vertex_close_" + self.name()] = td if tp: log.debug(_("Created %d topological points in the '%s' layer"), tp, self.name()) report.values["vertex_topo_" + self.name()] = tp
def split_line_at_points( polyline_input, point_features, point_feature_id_field="id", start_node_id=None, end_node_id=None, ): """Split line at points Args: polyline (QgsPolyline): point_features (iteratable object of QgsFeature or list of dictonaries with id and geometry ('geom'): point_feature_id_field (str): fieldname if the id field of the point_features start_node_id (str or int): node id of point at begin of polyline end_node_id (str or int): node id of point at end of polyline Returns: (list of dict): Splitted polyline into parts as dictonary with: geom: Polyline geometry start_node_id: id of node at starting point of line end_node_id: id of node at end point of line distance at line: is distance of original line at the begin of this line part """ snap_points = [] source_crs = QgsCoordinateReferenceSystem(4326) dest_crs = QgsCoordinateReferenceSystem(3857) transform = QgsCoordinateTransform(source_crs, dest_crs, QgsProject.instance()) transform_back = QgsCoordinateTransform(dest_crs, source_crs, QgsProject.instance()) polyline = QgsGeometry(polyline_input) polyline.transform(transform) for point in point_features: if type(point) == QgsFeature: point = { point_feature_id_field: point[point_feature_id_field], "geom": point.geometry(), } if hasattr(point["geom"], "asPoint"): geom = point["geom"].asPoint() else: geom = point["geom"] geom = QgsGeometry.fromPointXY(geom) geom.transform(transform) geom = geom.asPoint() closest_seg = polyline.closestSegmentWithContext(geom) # get nearest point (related to the point) on the line point_on_line = closest_seg[1] point_geom_on_line = QgsPoint(point_on_line[0], point_on_line[1]) # get nr of vertex (at the end of the line where the closest point is # found end_vertex_nr = closest_seg[2] start_vertex_nr = end_vertex_nr - 1 p1 = polyline.asPolyline()[start_vertex_nr] # first vertex p2 = point_geom_on_line distance_on_subline = math.hypot(p2.x() - p1.x(), p2.y() - p1.y()) snap_points.append(( start_vertex_nr, distance_on_subline, point_geom_on_line, point[point_feature_id_field], )) # order on vertex nr and if same vertex nr on distance snap_points.sort(key=lambda x: x[1]) snap_points.sort(key=lambda x: x[0]) # create line parts line_parts = [] line_points = [] start_point_id = start_node_id total_line_distance = 0 for i, vertex in enumerate(polyline.asPolyline()): line_points.append(QgsPoint(vertex[0], vertex[1])) # get points after this vertex split_points_on_segment = [p for p in snap_points if p[0] == i] for point in split_points_on_segment: # only add another point if point is not equal to last vertex # todo: throw error when at begin or end vertex of original line? # todo: what to do of multiple points on same location? line_points.append(point[2]) geom = QgsGeometry.fromPolyline(line_points) # length, unit_type = d.convertMeasurement( # d.computeDistance(line_points), # Qgis.Meters, Qgis.Meters, False) # Qgis.Degrees length = geom.length() # add line parts line_parts.append({ "geom": geom.transform(transform_back), "start_point_id": start_point_id, "end_point_id": point[3], "distance_at_line": total_line_distance, "length": length, }) # create starting point of new line line_points = [point[2]] start_point_id = point[3] total_line_distance += length # last part of the line geom = QgsGeometry.fromPolyline(line_points) length = geom.length() # length, something = d.convertMeasurement( # d.computeDistance(line_points), # Qgis.Meters, Qgis.Meters, False) line_parts.append({ "geom": geom, "start_point_id": start_point_id, "end_point_id": end_node_id, "distance_at_line": total_line_distance, "length": length, }) return line_parts