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
Exemple #2
0
 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()
Exemple #3
0
    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
Exemple #4
0
    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)
Exemple #5
0
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)
Exemple #6
0
    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
Exemple #7
0
    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}