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 canvasMoveEvent(self, event): if self.fixed_points: self.rb.reset() if len(self.fixed_points) == 1: pt1 = self.fixed_points[0] pt2 = QgsPoint(event.snapPoint()) rb_line = QgsLineString(pt1, pt2) self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs()) else: rb_line = QgsLineString(self.fixed_points) rb_line.addVertex(QgsPoint(event.snapPoint())) self.rb.setToGeometry(QgsGeometry().fromPolyline(rb_line), QgsProject().instance().crs()) self.rb.show()
def segmentizeBoundaries(self, geom): """geom: QgsGeometry (polygon or multi-polygon)""" xmin, ymax = (self.xmin, self.ymax) xres, yres = (self.xres, self.yres) z_func = self.valueOnSurface polys = [] for polygon in PolygonGeometry.nestedPointXYList(geom): rings = QgsMultiLineString() for i, bnd in enumerate(polygon): if GeometryUtils.isClockwise(bnd) ^ (i > 0): # xor bnd.reverse() # outer boundary should be ccw. inner boundaries should be cw. ring = QgsLineString() v = bnd[0] # QgsPointXY x0, y0 = (v.x(), v.y()) nx0 = (x0 - xmin) / xres ny0 = (ymax - y0) / yres ns0 = abs(ny0 + nx0) for v in bnd[1:]: x1, y1 = (v.x(), v.y()) nx1 = (x1 - xmin) / xres ny1 = (ymax - y1) / yres ns1 = abs(ny1 + nx1) p = set([0]) for v0, v1 in [[nx0, nx1], [ny0, ny1], [ns0, ns1]]: k = ceil(min(v0, v1)) n = floor(max(v0, v1)) for j in range(k, n + 1): p.add((j - v0) / (v1 - v0)) if 1 in p: p.remove(1) for m in sorted(p): x = x0 + (x1 - x0) * m y = y0 + (y1 - y0) * m ring.addVertex(QgsPoint(x, y, z_func(x, y))) x0, y0 = (x1, y1) nx0, ny0, ns0 = (nx1, ny1, ns1) ring.addVertex(QgsPoint(x0, y0, z_func(x0, y0))) # last vertex rings.addGeometry(ring) polys.append(QgsGeometry(rings)) return polys
def splitPolygon(self, geom): z_func = lambda x, y: self.valueOnSurface(x, y) or 0 cache = FunctionCacheXY(z_func) z_func = cache.func polygons = QgsMultiPolygon() for poly in self._splitPolygon(geom): p = QgsPolygon() ring = QgsLineString() for pt in poly[0]: ring.addVertex(QgsPoint(pt.x(), pt.y(), z_func(pt.x(), pt.y()))) p.setExteriorRing(ring) for bnd in poly[1:]: ring = QgsLineString() for pt in bnd: ring.addVertex(QgsPoint(pt.x(), pt.y(), z_func(pt.x(), pt.y()))) p.addInteriorRing(ring) polygons.addGeometry(p) return QgsGeometry(polygons)
def coord2ptsZ(coords): lineStr = QgsLineString() coords = coords.strip() clist = re.split('\s+', coords) for pt in clist: c = pt.split(',') if len(c) >= 6: '''This is invalid KML syntax, but given some KMLs have been formatted this way the invalid exception is looked for. There should be a space between line string coordinates. This looks for a comma between them and also assumes it is formatted as lat,lon,altitude,lat,lon,altitude...''' i = 0 while i < len(c) - 1: try: lon = float(c[i]) lat = float(c[i + 1]) except: lon = 0.0 lat = 0.0 try: altitude = float(c[i + 2]) except: altitude = 0 lineStr.addVertex(QgsPoint(lon, lat, altitude)) i += 3 else: altitude = 0 try: lon = float(c[0]) lat = float(c[1]) if len(c) >= 3: altitude = float(c[2]) except: lon = 0.0 lat = 0.0 lineStr.addVertex(QgsPoint(lon, lat, altitude)) return (lineStr)
def prepare_constraint_lines(self, parameters, context, feedback): sections = self.parameterAsSource(parameters, self.SECTIONS, context) # Merge constraint lines files outputs = processing.run( "native:mergevectorlayers", { "LAYERS": parameters[self.CONSTRAINT_LINES], "CRS": sections.sourceCrs(), "OUTPUT": QgsProcessingUtils.generateTempFilename( "constraint_lines.shp"), }, context=context, feedback=feedback, is_child_algorithm=True, ) constraint_lines = outputs["OUTPUT"] feedback.setCurrentStep(2) if feedback.isCanceled(): return {} # Add lines with first and last points of sections request = QgsFeatureRequest() request.addOrderBy('"sec_id"', True, True) request.addOrderBy('"p_id"', True, True) previous_sec_id = None previous_point = None left_line = QgsLineString() right_line = QgsLineString() total = total = (100.0 / sections.featureCount() if sections.featureCount() else 0) for current, f in enumerate(sections.getFeatures(request)): if previous_sec_id is None or previous_sec_id != f.attribute( "sec_id"): if previous_sec_id is not None: right_line.addVertex(previous_point) left_line.addVertex(f.geometry().constGet()) previous_sec_id = f.attribute("sec_id") previous_point = f.geometry().constGet() feedback.setProgress(int(current * total)) right_line.addVertex(previous_point) constraint_lines_layer = QgsVectorLayer(constraint_lines, "constraint_lines", "ogr") dp = constraint_lines_layer.dataProvider() left = QgsFeature() left.setGeometry(QgsGeometry(left_line)) right = QgsFeature() right.setGeometry(QgsGeometry(right_line)) dp.addFeatures([left, right]) return constraint_lines
def processAlgorithmOutput(self, results, parameters, context, feedback): output_fields = QgsFields() result_type = self.RESULT_TYPE[self.params["OUTPUT_RESULT_TYPE"]] if result_type == "NORMAL": output_fields.append( QgsField("search_id", QVariant.String, "text", 255)) output_fields.append( QgsField("location_id", QVariant.String, "text", 255)) output_fields.append( QgsField("travel_time", QVariant.Double, "text", 255)) output_fields.append( QgsField("distance", QVariant.Double, "text", 255)) output_fields.append( QgsField("departure_time", QVariant.String, "text", 255)) output_fields.append( QgsField("arrival_time", QVariant.String, "text", 255)) else: output_fields.append(QgsField("type", QVariant.String, "text", 255)) output_fields.append(QgsField("mode", QVariant.String, "text", 255)) output_fields.append( QgsField("directions", QVariant.String, "text", 255)) output_crs = EPSG4326 output_type = QgsWkbTypes.LineString (sink, sink_id) = self.parameterAsSink(parameters, "OUTPUT", context, output_fields, output_type, output_crs) for result in results: for location in result["locations"]: if result_type == "NORMAL": # Create the geom geom = QgsLineString() for part in location["properties"][0]["route"]["parts"]: for coord in part["coords"]: point = QgsPoint(coord["lng"], coord["lat"]) if geom.endPoint() != point: geom.addVertex(point) # Create the feature feature = QgsFeature(output_fields) feature.setGeometry(geom) feature.setAttribute(0, result["search_id"]) feature.setAttribute(1, location["id"]) feature.setAttribute( 2, location["properties"][0]["travel_time"]) feature.setAttribute(3, location["properties"][0]["distance"]) feature.setAttribute( 4, location["properties"][0]["route"]["departure_time"]) feature.setAttribute( 5, location["properties"][0]["route"]["arrival_time"]) sink.addFeature(feature, QgsFeatureSink.FastInsert) else: for part in location["properties"][0]["route"]["parts"]: # Create the geom geom = QgsLineString() for coord in part["coords"]: point = QgsPoint(coord["lng"], coord["lat"]) geom.addVertex(point) # Create the feature feature_d = QgsFeature(output_fields) feature_d.setGeometry(geom) feature_d.setAttribute(0, part["type"]) feature_d.setAttribute(1, part["mode"]) feature_d.setAttribute(2, part["directions"]) sink.addFeature(feature_d, QgsFeatureSink.FastInsert) feedback.pushDebugInfo("TimeFilterAlgorithm done !") # to get hold of the layer in post processing self.sink_id = sink_id return {"OUTPUT": sink_id}