def snapToPrecision(geom, precision): snapped = QgsGeometry(geom) if precision == 0.0: return snapped i = 0 p = snapped.vertexAt(i) while p.x() != 0.0 and p.y() != 0.0: x = round(p.x() / precision, 0) * precision y = round(p.y() / precision, 0) * precision snapped.moveVertex(x, y, i) i = i + 1 p = snapped.vertexAt(i) return snapped
def processFeatures(self, reaches, reach_layer, wastewater_node_layer, distance_threshold): ids = list() to_ids = list() # Gather ids of connected networkelements # to_ids are also gathered separately, because they can be either # reaches or nodes for reach in reaches: if reach['rp_from_fk_wastewater_networkelement']: ids.append(reach['rp_from_fk_wastewater_networkelement']) if reach['rp_to_fk_wastewater_networkelement']: ids.append(reach['rp_to_fk_wastewater_networkelement']) to_ids.append(reach['rp_to_fk_wastewater_networkelement']) # Get all nodes on which to snap quoted_ids = [QgsExpression.quotedValue(objid) for objid in ids] node_request = QgsFeatureRequest() filter_expression = '"obj_id" IN ({ids})'.format( ids=','.join(quoted_ids)) node_request.setFilterExpression(filter_expression) node_request.setSubsetOfAttributes([]) nodes = dict() for node in wastewater_node_layer.getFeatures(node_request): nodes[node['obj_id']] = node # Get all reaches on which to snap quoted_to_ids = [QgsExpression.quotedValue(objid) for objid in to_ids] reach_request = QgsFeatureRequest() filter_expression = '"obj_id" IN ({ids})'.format( ids=','.join(quoted_to_ids)) reach_request.setFilterExpression(filter_expression) reach_request.setSubsetOfAttributes([]) target_reaches = dict() for target_reach in reach_layer.getFeatures(reach_request): target_reaches[target_reach['obj_id']] = target_reach for reach in reaches: reach_geometry = QgsGeometry(reach.geometry()) from_id = reach['rp_from_fk_wastewater_networkelement'] if from_id in list(nodes.keys()): if distance_threshold == 0 or reach_geometry.sqrDistToVertexAt(nodes[from_id].geometry().asPoint(), 0) < distance_threshold: reach_geometry.moveVertex( nodes[from_id].geometry().constGet(), 0) to_id = reach['rp_to_fk_wastewater_networkelement'] if to_id in list(nodes.keys()): last_vertex = reach_geometry.constGet().nCoordinates() - 1 if distance_threshold == 0 or reach_geometry.sqrDistToVertexAt(nodes[to_id].geometry().asPoint(), last_vertex) < distance_threshold: reach_geometry.moveVertex( nodes[to_id].geometry().constGet(), last_vertex) if to_id in list(target_reaches.keys()): last_vertex = reach_geometry.constGet().nCoordinates() - 1 target_reach = target_reaches[to_id] distance, point, min_distance_point, after_vertex = target_reach.geometry( ).closestSegmentWithContext(QgsPointXY(reach_geometry.vertexAt(last_vertex))) if distance_threshold == 0 or distance < distance_threshold: reach_geometry.moveVertex( point.x(), point.y(), last_vertex) reach.setGeometry(reach_geometry) reach_layer.updateFeature(reach)
def addZtoPointFeatures(self, inputPoints, inputCrs, fieldIdWithZVals=-1, override=True): #pointsListZ=[] #list of QgsFeature() featuresWithZ = [] try: #check if pointsLayer is emty #if inputPoints.featureCount!=0: for feat in inputPoints: #if geom.hasZ==False or geom.hasZ==True and override==True: # get Raster Values for each point feature featZ = QgsFeature(feat.fields()) #Copy of the Feature geom = feat.geometry() #create a copy of the geometry for temporary transform to the project.crs() wkb = feat.geometry().asWkb() #self.feedback.pushInfo("addZtoPointFeatures1 " + geom.asWkt()) geom2CRS = QgsGeometry() geom2CRS.fromWkb(wkb) #transform geom to rasterLayer.crs() if crs a different if not inputCrs.authid() == self.rasterLayer.crs().authid(): trafo = QgsCoordinateTransform(inputCrs, self.rasterLayer.crs(), QgsProject.instance()) #transform clip Geom to SrcLayer.crs Reverse geom2CRS.transform(trafo, QgsCoordinateTransform.ForwardTransform, False) pinPoint = None if geom.isMultipart(): pinPoint = geom.vertexAt( 0) #nimm den ersten Punkt, wenn es ein MultiPoint ist pin2Crs = geom2CRS.vertexAt(0) else: pinPoint = geom.asPoint() pin2Crs = geom2CRS.asPoint() rastVal = None if fieldIdWithZVals > -1: rastVal = feat[fieldIdWithZVals] if rastVal is None or str(rastVal) == 'NULL': felder = feat.fields() feld = felder.field(fieldIdWithZVals) self.feedback.reportError('Error on Feature ' + str(feat.id())) self.feedback.reportError('Feature Attributes :' + str(feat.attributes())) raise QgsProcessingException('Z value on field ' + feld.name() + ' is empty!') else: #hole Z-Wert von DGM rastVal = self.interpolator.linear( pin2Crs) #QgsPointXY(4459566.0, 5613959.0)) #self.feedback.pushInfo("addZtoPointFeatures2 " + geom.asWkt() + " 2Crs: " + geom2CRS.asWkt()) #self.feedback.pushInfo("rastVal " + str(pinPoint)+ " = in RasterCrs:"+ str(pin2Crs)+ " = "+ str(rastVal)) #rastSample = rasterLayer.dataProvider().identify(pin2Crs, QgsRaster.IdentifyFormatValue).results() if not rastVal is None or str( rastVal ) != 'NULL': # ToDo der Leerwert muss noch beruecksichtigt werden wkt = "PointZ(" + str(pinPoint.x()) + " " + str( pinPoint.y()) + " " + str(rastVal) + ")" #self.feedback.reportError(wkt) #construct new Feature in source Crs ptZ = QgsPoint(pinPoint.x(), pinPoint.y()) ptZ.addZValue(rastVal) geomZ = QgsGeometry.fromWkt(wkt) featZ.setGeometry(geomZ) featZ.setAttributes(feat.attributes()) featuresWithZ.append(featZ) else: self.feedback.reportError('Point Feature: ' + feat.id() + ' ' + wkt + ' ' + feat.attributes()) raise QgsProcessingException( "No valid RasterValue for this position: " + str(round(pinPoint.x(), 1)) + " " + str(round(pinPoint.y(), 1)) + ' raster value: ' + str(rastVal)) #self.feedback.pushInfo("addZtoPointFeatures " + str(len(featuresWithZ))+ " Objekte") except Exception as err: msg = "Error: add Z to Point Features {0} \n {1}".format( err.args, repr(err)) self.feedback.reportError(msg) featuresWithZ = [] raise QgsProcessingException(msg) #print("Error addZtoPointFeatures")#,str(err.args) + ";" + str(repr(err))) #raise QgsProcessingException("Error addZtoPointFeatures " + str(err.args))# + ";" + str(repr(err))) return featuresWithZ
def simplify(self): """ Reduce the number of vertices in a polygon layer. * Delete vertex if the angle with its adjacents is near of the straight angle for less than 'straight_thr' degrees in all its parents. * Delete vertex if the distance to the segment formed by its parents is less than 'cath_thr' meters. """ if log.app_level <= logging.DEBUG: debshp = DebugWriter("debug_simplify.shp", self) killed = 0 to_change = {} # Clean non corners (parents_per_vertex, geometries) = self.get_parents_per_vertex_and_geometries() pbar = self.get_progressbar(_("Simplify"), len(parents_per_vertex)) for wkt, parents in parents_per_vertex.items(): point = Point(wkt) # Test if this vertex is a 'corner' in any of its parent polygons for fid in parents: geom = geometries[fid] (angle, is_acute, is_corner, cath) = point.get_corner_context(geom) debmsg = "angle=%.1f, is_acute=%s, is_corner=%s, cath=%.4f" % ( angle, is_acute, is_corner, cath, ) if is_corner: break msg = "Keep" if not is_corner: killed += 1 # delete the vertex from all its parents. for fid in frozenset(parents): g = QgsGeometry(geometries[fid]) (__, ndx, __, __, __) = g.closestVertex(point) (ndxa, ndxb) = g.adjacentVertices(ndx) v = g.vertexAt(ndx) va = g.vertexAt(ndxa) vb = g.vertexAt(ndxb) invalid_ring = v == va or v == vb or va == vb g.deleteVertex(ndx) msg = "Refused" if Geometry.is_valid(g) and not invalid_ring: parents.remove(fid) geometries[fid] = g to_change[fid] = g msg = "Deleted" if log.app_level <= logging.DEBUG: debshp.add_point(point, msg + " " + debmsg) 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 killed > 0: log.debug(_("Simplified %d vertices in the '%s' layer"), killed, self.name()) report.values["vertex_simplify_" + self.name()] = killed
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 in_mask(self, feature, srid=None): if feature is None: # expression overview return False if self.layer is None: return False try: # layer is not None but destroyed ? self.layer.id() except: self.reset_mask_layer() return False # mask layer empty due to unloaded memlayersaver plugin > no filtering if self.layer.featureCount() == 0: return True mask_geom, bbox = self.mask_geometry() geom = QgsGeometry(feature.geometry()) if not geom.isGeosValid(): geom = geom.buffer(0.0, 1) if geom is None: return False if srid is not None and self.layer.crs().postgisSrid() != srid: src_crs = QgsCoordinateReferenceSystem(srid) dest_crs = self.layer.crs() xform = QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance()) try: geom.transform(xform) except: # transformation error. Check layer projection. pass if geom.type() == QgsWkbTypes.PolygonGeometry: if self.parameters.polygon_mask_method == 2 and not self.has_point_on_surface: self.parameters.polygon_mask_method = 1 if self.parameters.polygon_mask_method == 0: # this method can only work when no geometry simplification is involved return (mask_geom.overlaps(geom) or mask_geom.contains(geom)) elif self.parameters.polygon_mask_method == 1: # the fastest method, but with possible inaccuracies pt = geom.vertexAt(0) return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(geom.centroid()) elif self.parameters.polygon_mask_method == 2: # will always work pt = geom.vertexAt(0) return bbox.contains(QgsPointXY(pt)) and mask_geom.contains(geom.pointOnSurface()) else: return False elif geom.type() == QgsWkbTypes.LineGeometry: if self.parameters.line_mask_method == 0: return mask_geom.intersects(geom) elif self.parameters.line_mask_method == 1: return mask_geom.contains(geom) else: return False elif geom.type() == QgsWkbTypes.PointGeometry: return mask_geom.intersects(geom) else: return False
def in_mask(self, feature, srid=None): if feature is None: # expression overview return False if self.layer is None: return False try: # layer is not None but destroyed ? self.layer.id() except: self.reset_mask_layer() return False # mask layer empty due to unloaded memlayersaver plugin > no filtering if self.layer.featureCount() == 0: return True mask_geom, bbox = self.mask_geometry() geom = QgsGeometry(feature.geometry()) if not geom.isGeosValid(): geom = geom.buffer(0.0, 1) if geom is None: return False if srid is not None and self.layer.crs().postgisSrid() != srid: src_crs = QgsCoordinateReferenceSystem(srid) dest_crs = self.layer.crs() xform = QgsCoordinateTransform(src_crs, dest_crs, QgsProject.instance()) try: geom.transform(xform) except Exception as e: for m in e.args: QgsMessageLog.logMessage("in_mask - {}".format(m), "Extensions") # transformation error. Check layer projection. pass if geom.type() == QgsWkbTypes.PolygonGeometry: if (self.parameters.polygon_mask_method == 2 and not self.has_point_on_surface): self.parameters.polygon_mask_method = 1 if self.parameters.polygon_mask_method == 0: # this method can only work when no geometry simplification is involved return mask_geom.overlaps(geom) or mask_geom.contains(geom) elif self.parameters.polygon_mask_method == 1: # the fastest method, but with possible inaccuracies pt = geom.vertexAt(0) return bbox.contains(QgsPointXY(pt)) and mask_geom.contains( geom.centroid()) elif self.parameters.polygon_mask_method == 2: # will always work pt = geom.vertexAt(0) return bbox.contains(QgsPointXY(pt)) and mask_geom.contains( geom.pointOnSurface()) else: return False elif geom.type() == QgsWkbTypes.LineGeometry: if self.parameters.line_mask_method == 0: return mask_geom.intersects(geom) elif self.parameters.line_mask_method == 1: return mask_geom.contains(geom) else: return False elif geom.type() == QgsWkbTypes.PointGeometry: return mask_geom.intersects(geom) else: return False