def average_linestrings(line1: QgsLineString, line2: QgsLineString, weight: float = 1) -> QgsLineString: """ Averages two linestring geometries """ g1 = line1.clone() # project points from g2 onto g1 for n in range(line2.numPoints()): vertex = line2.pointN(n) _, pt, after, _ = g1.closestSegment(vertex) g1.insertVertex(QgsVertexId(0, 0, after.vertex), pt) # iterate through vertices in g1 out = [] for n in range(g1.numPoints()): vertex = g1.pointN(n) _, pt, after, _ = line2.closestSegment(vertex) # average pts x = (vertex.x() * weight + pt.x()) / (weight + 1) y = (vertex.y() * weight + pt.y()) / (weight + 1) out.append(QgsPoint(x, y)) return QgsLineString(out)
def validate_constraints(self, sim_geom, first, last): """Validate the spatial relationship in order maintain topological structure Three distinct spatial relation are tested in order to assure that each bend reduce will continue to maintain the topological structure in a feature between the features: - Simplicity: Adequate validation is done to make sure that the bend reduction will not cause the feature to cross itself. - Intersection : Adequate validation is done to make sure that a line from other features will not intersect the bend being reduced - Sidedness: Adequate validation is done to make sure that a line is not completely contained in the bend. This situation can happen when a ring in a polygon complete;y lie in a bend ans after bend reduction, the the ring falls outside the polygon which make it invalid. Note if the topological structure is wrong before the bend correction no correction will be done on these errors. :param: sim_geom: Geometry used to validate constraints :param: first: Index of the start vertice of the subline :param: last: Index of the last vertice of the subline :return: Flag indicating if the spatial constraints are valid for this subline simplification :rtype: Bool """ constraints_valid = True qgs_points = [sim_geom.qgs_geom.vertexAt(i) for i in range(first, last+1)] qgs_geom_new_subline = QgsGeometry(QgsLineString(qgs_points[0], qgs_points[-1])) qgs_geom_old_subline = QgsGeometry(QgsLineString(qgs_points)) qgs_geoms_with_itself, qgs_geoms_with_others = \ self.rb_collection.get_segment_intersect(sim_geom.id, qgs_geom_old_subline.boundingBox(), qgs_geom_old_subline) # First: check if the bend reduce line string is an OGC simple line constraints_valid = GeoSimUtil.validate_simplicity(qgs_geoms_with_itself, qgs_geom_new_subline) # Second: check that the new line does not intersect with any other line or points if constraints_valid and len(qgs_geoms_with_others) >= 1: constraints_valid = GeoSimUtil.validate_intersection(qgs_geoms_with_others, qgs_geom_new_subline) # Third: check that inside the subline to simplify there is no feature completely inside it. This would cause a # sidedness or relative position error if constraints_valid and len(qgs_geoms_with_others) >= 1: qgs_ls_old_subline = QgsLineString(qgs_points) qgs_ls_old_subline.addVertex(qgs_points[0]) # Close the line with the start point qgs_geom_old_subline = QgsGeometry(qgs_ls_old_subline.clone()) # Next two lines used to transform a self intersecting line into a valid MultiPolygon qgs_geom_unary = QgsGeometry.unaryUnion([qgs_geom_old_subline]) qgs_geom_polygonize = QgsGeometry.polygonize([qgs_geom_unary]) if qgs_geom_polygonize.isSimple(): constraints_valid = GeoSimUtil.validate_sidedness(qgs_geoms_with_others, qgs_geom_polygonize) else: print("Polygonize not valid") constraints_valid = False return constraints_valid
def test_case32(self): title = "Test 32: Normalization of in vector layer" print (title) vl = QgsVectorLayer("LineString", "temporary_polygon", "memory") pr = vl.dataProvider() fet = QgsFeature() fet.setId(1) qgs_line = QgsLineString((QgsPoint(0,0,0),QgsPoint(10,10,0),QgsPoint(20,20,0))) qgs_geom = QgsGeometry(qgs_line.clone()) fet.setGeometry(qgs_geom) pr.addFeatures([fet]) vl.updateExtents() feedback = QgsProcessingFeedback() qgs_features, geom_type = ReduceBend.normalize_in_vector_layer(vl, feedback) val0 = len(qgs_features) == 1 qgs_geom = qgs_features[0].geometry() val1 = qgs_geom.wkbType() == QgsWkbTypes.LineString self.assertTrue(val0 and val1, title)