Пример #1
0
    def find_line_distz(self):

        # Get start and end nodes zs and deltazs from table
        pipe_geom = self.pipe_ft.geometry()
        (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes(self.params, self.pipe_ft.geometry())
        start_node_z = start_node_ft.attribute(Junction.field_name_elev)
        if start_node_z == NULL:
            start_node_z = 0
        start_node_deltaz = start_node_ft.attribute(Junction.field_name_delta_z)
        end_node_z = end_node_ft.attribute(Junction.field_name_elev)
        if end_node_z == NULL:
            end_node_z = 0
        end_node_deltaz = end_node_ft.attribute(Junction.field_name_delta_z)

        total_dist = 0
        dist_z = OrderedDict()
        pipe_geom_v2 = pipe_geom.geometry()
        dist_z[0] = start_node_z + start_node_deltaz

        # Interpolate deltaZs for remaining vertices
        for p in range(1, pipe_geom_v2.vertexCount(0, 0)):
            vertex = pipe_geom_v2.vertexAt(QgsVertexId(0, 0, p, QgsVertexId.SegmentVertex))
            vertex_prev = pipe_geom_v2.vertexAt(QgsVertexId(0, 0, p - 1, QgsVertexId.SegmentVertex))
            total_dist += math.sqrt((vertex.x() - vertex_prev.x()) ** 2 + (vertex.y() - vertex_prev.y()) ** 2)

            # Interpolate delta z for vertex using distance from nodes and delta z of nodes
            z = (total_dist / self.pipe_ft.geometry().length() * (end_node_z - start_node_z)) + start_node_z

            # z = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, QgsPoint(vertex.x(), vertex.y()))
            delta_z = (total_dist / self.pipe_ft.geometry().length() * (end_node_deltaz - start_node_deltaz)) + start_node_deltaz
            dist_z[total_dist] = z + delta_z

        return dist_z
Пример #2
0
 def addGeometry(self, geom):
     """
     Add geometry into the index
     :param geom:QgsAbstractGeometryV2
     :return:
     """
     for iPart in xrange(geom.partCount()):
         for iRing in xrange(geom.ringCount(iPart)):
             nVerts = geom.vertexCount(iPart, iRing)
             if isinstance(geom, QgsMultiPolygonV2):
                 nVerts -= 1
             elif isinstance(geom, QgsPolygonV2):
                 nVerts -= 1
             elif isinstance(geom, QgsCircularStringV2):
                 nVerts -= 1
             for iVert in xrange(nVerts):
                 idx = CoordIdx(
                     geom,
                     QgsVertexId(iPart, iRing, iVert,
                                 QgsVertexId.SegmentVertex))
                 idx1 = CoordIdx(
                     geom,
                     QgsVertexId(iPart, iRing, iVert + 1,
                                 QgsVertexId.SegmentVertex))
                 self.coordIdxs.append(idx)
                 self.coordIdxs.append(idx1)
                 self.addPoint(idx)
                 if iVert < nVerts - 1:
                     self.addSegment(idx, idx1)
Пример #3
0
    def find_line_distz3D(self):

        pipe_geom_v2 = self.pipe_ft.geometry().get()

        # Find start and end nodes (needed to know delta z)
        (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes(
            self.params, self.pipe_ft.geometry())
        start_node_deltaz = start_node_ft.attribute(
            Junction.field_name_delta_z)
        end_node_deltaz = end_node_ft.attribute(Junction.field_name_delta_z)

        total_dist = 0
        dist_z = OrderedDict()
        dist_z[0] = pipe_geom_v2.vertexAt(
            QgsVertexId(0, 0, 0,
                        QgsVertexId.SegmentVertex)).z()  #+ start_node_deltaz

        for p in range(1, pipe_geom_v2.vertexCount(0, 0)):
            vertex = pipe_geom_v2.vertexAt(
                QgsVertexId(0, 0, p, QgsVertexId.SegmentVertex))
            vertex_prev = pipe_geom_v2.vertexAt(
                QgsVertexId(0, 0, p - 1, QgsVertexId.SegmentVertex))
            total_dist += math.sqrt((vertex.x() - vertex_prev.x())**2 +
                                    (vertex.y() - vertex_prev.y())**2)

            # Interpolate delta z for vertex using distance from nodes and delta z of nodes
            delta_z = (
                total_dist / self.pipe_ft.geometry().length() *
                (end_node_deltaz - start_node_deltaz)) + start_node_deltaz
            dist_z[total_dist] = vertex.z()  #+ delta_z

        return dist_z
 def test_move_operation(self):
     operation = QgsAnnotationItemEditOperationMoveNode(
         'item id', QgsVertexId(1, 2, 3), QgsPoint(4, 5), QgsPoint(6, 7))
     self.assertEqual(operation.itemId(), 'item id')
     self.assertEqual(operation.nodeId(), QgsVertexId(1, 2, 3))
     self.assertEqual(operation.before(), QgsPoint(4, 5))
     self.assertEqual(operation.after(), QgsPoint(6, 7))
Пример #5
0
    def find_distance(self, pipe_geom_v2, vertex_nr):
        total_dist = 0
        for p in range(1, vertex_nr + 1):
            vertex = pipe_geom_v2.vertexAt(QgsVertexId(0, 0, p, QgsVertexId.SegmentVertex))
            vertex_prev = pipe_geom_v2.vertexAt(QgsVertexId(0, 0, p - 1, QgsVertexId.SegmentVertex))
            total_dist += math.sqrt((vertex.x() - vertex_prev.x()) ** 2 + (vertex.y() - vertex_prev.y()) ** 2)

        return total_dist
Пример #6
0
    def test_apply_edit(self):
        """
        Test applying edits to a layer
        """
        layer = QgsAnnotationLayer(
            'test',
            QgsAnnotationLayer.LayerOptions(
                QgsProject.instance().transformContext()))
        self.assertTrue(layer.isValid())

        polygon_item_id = layer.addItem(
            QgsAnnotationPolygonItem(
                QgsPolygon(
                    QgsLineString([
                        QgsPoint(12, 13),
                        QgsPoint(14, 13),
                        QgsPoint(14, 15),
                        QgsPoint(12, 13)
                    ]))))
        linestring_item_id = layer.addItem(
            QgsAnnotationLineItem(
                QgsLineString(
                    [QgsPoint(11, 13),
                     QgsPoint(12, 13),
                     QgsPoint(12, 15)])))
        marker_item_id = layer.addItem(
            QgsAnnotationMarkerItem(QgsPoint(12, 13)))

        rc = QgsRenderContext()
        self.assertCountEqual(
            layer.itemsInBounds(QgsRectangle(1, 1, 20, 20), rc),
            [polygon_item_id, linestring_item_id, marker_item_id])

        # can't apply a move to an item which doesn't exist in the layer
        self.assertEqual(
            layer.applyEdit(
                QgsAnnotationItemEditOperationMoveNode('xxx',
                                                       QgsVertexId(0, 0, 2),
                                                       QgsPoint(14, 15),
                                                       QgsPoint(19, 15))),
            Qgis.AnnotationItemEditOperationResult.Invalid)

        # apply move to polygon
        self.assertEqual(
            layer.applyEdit(
                QgsAnnotationItemEditOperationMoveNode(polygon_item_id,
                                                       QgsVertexId(0, 0, 2),
                                                       QgsPoint(14, 15),
                                                       QgsPoint(19, 15))),
            Qgis.AnnotationItemEditOperationResult.Success)

        self.assertEqual(
            layer.item(polygon_item_id).geometry().asWkt(),
            'Polygon ((12 13, 14 13, 19 15, 12 13))')
        # ensure that spatial index was updated
        self.assertCountEqual(
            layer.itemsInBounds(QgsRectangle(18, 1, 20, 16), rc),
            [polygon_item_id])
Пример #7
0
    def test_basic(self):
        node = QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(1, 2),
                                     Qgis.AnnotationItemNodeType.VertexHandle)
        self.assertEqual(node.point(), QgsPointXY(1, 2))
        self.assertEqual(node.id(), QgsVertexId(0, 0, 1))

        node.setPoint(QgsPointXY(3, 4))
        self.assertEqual(node.point(), QgsPointXY(3, 4))

        self.assertEqual(node.type(), Qgis.AnnotationItemNodeType.VertexHandle)
Пример #8
0
 def testQgsVertexId(self):
     v = QgsVertexId()
     self.assertEqual(v.__repr__(), '<QgsVertexId: -1,-1,-1>')
     v = QgsVertexId(1, 2, 3)
     self.assertEqual(v.__repr__(), '<QgsVertexId: 1,2,3>')
     v = QgsVertexId(1, 2, 3, _type=QgsVertexId.CurveVertex)
     self.assertEqual(v.__repr__(), '<QgsVertexId: 1,2,3 CurveVertex>')
    def test_apply_delete_node_edit(self):
        item = QgsAnnotationPolygonItem(
            QgsPolygon(
                QgsLineString([
                    QgsPoint(12, 13),
                    QgsPoint(14, 13),
                    QgsPoint(14, 15),
                    QgsPoint(14.5, 15.5),
                    QgsPoint(14.5, 16.5),
                    QgsPoint(14.5, 17.5),
                    QgsPoint(12, 13)
                ])))
        self.assertEqual(
            item.geometry().asWkt(),
            'Polygon ((12 13, 14 13, 14 15, 14.5 15.5, 14.5 16.5, 14.5 17.5, 12 13))'
        )

        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationDeleteNode(
                    '', QgsVertexId(0, 0, 1), QgsPoint(14, 13))),
            Qgis.AnnotationItemEditOperationResult.Success)
        self.assertEqual(
            item.geometry().asWkt(),
            'Polygon ((12 13, 14 15, 14.5 15.5, 14.5 16.5, 14.5 17.5, 12 13))')
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationDeleteNode(
                    '', QgsVertexId(0, 0, 2), QgsPoint(14.5, 15.5))),
            Qgis.AnnotationItemEditOperationResult.Success)
        self.assertEqual(
            item.geometry().asWkt(),
            'Polygon ((12 13, 14 15, 14.5 16.5, 14.5 17.5, 12 13))')
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationDeleteNode(
                    '', QgsVertexId(0, 0, 7), QgsPoint(14, 15))),
            Qgis.AnnotationItemEditOperationResult.Invalid)
        self.assertEqual(
            item.geometry().asWkt(),
            'Polygon ((12 13, 14 15, 14.5 16.5, 14.5 17.5, 12 13))')
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationDeleteNode(
                    '', QgsVertexId(0, 0, 0), QgsPoint(12, 13))),
            Qgis.AnnotationItemEditOperationResult.Success)
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationDeleteNode(
                    '', QgsVertexId(0, 0, 0), QgsPoint(12, 13))),
            Qgis.AnnotationItemEditOperationResult.ItemCleared)
        self.assertEqual(item.geometry().asWkt(), 'Polygon EMPTY')
Пример #10
0
    def test_equality(self):
        node = QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(1, 2),
                                     Qgis.AnnotationItemNodeType.VertexHandle)
        node2 = QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(1, 2),
                                      Qgis.AnnotationItemNodeType.VertexHandle)
        self.assertEqual(node, node2)

        node2.setPoint(QgsPointXY(3, 4))
        self.assertNotEqual(node, node2)

        node = QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(1, 2),
                                     Qgis.AnnotationItemNodeType.VertexHandle)
        node2 = QgsAnnotationItemNode(QgsVertexId(0, 0, 2), QgsPointXY(1, 2),
                                      Qgis.AnnotationItemNodeType.VertexHandle)
        self.assertNotEqual(node, node2)
Пример #11
0
    def set_feature(self, feature: Optional[QgsFeature]):
        """
        Sets the feature to show in the model
        """
        self.beginResetModel()
        self.feature = feature

        self.vertices = []

        self.has_z = self.feature.geometry().constGet().is3D(
        ) if self.feature is not None and self.feature.hasGeometry() else True
        self.has_m = self.feature.geometry().constGet().isMeasure(
        ) if self.feature is not None and self.feature.hasGeometry() else True

        if self.feature is not None and self.feature.hasGeometry():
            geom = self.feature.geometry().constGet()

            vid = QgsVertexId()
            while True:
                ok, vertex = geom.nextVertex(vid)
                if ok:
                    self.vertices.append((vid, vertex))
                else:
                    break

        self.endResetModel()
    def createFeature(self,
                      feedback,
                      feature_id,
                      type,
                      geometries,
                      class_field=None):
        attrs = [feature_id]
        if class_field is not None:
            attrs.append(class_field)

        multi_point = QgsMultiPoint()

        for g in geometries:
            if feedback.isCanceled():
                break

            vid = QgsVertexId()
            while True:
                if feedback.isCanceled():
                    break
                found, point = g.constGet().nextVertex(vid)
                if found:
                    multi_point.addGeometry(point)
                else:
                    break

        geometry = QgsGeometry(multi_point)
        output_geometry = None
        if type == 0:
            # envelope
            rect = geometry.boundingBox()
            output_geometry = QgsGeometry.fromRect(rect)
            attrs.append(rect.width())
            attrs.append(rect.height())
            attrs.append(rect.area())
            attrs.append(rect.perimeter())
        elif type == 1:
            # oriented rect
            output_geometry, area, angle, width, height = geometry.orientedMinimumBoundingBox(
            )
            attrs.append(width)
            attrs.append(height)
            attrs.append(angle)
            attrs.append(area)
            attrs.append(2 * width + 2 * height)
        elif type == 2:
            # circle
            output_geometry, center, radius = geometry.minimalEnclosingCircle(
                segments=72)
            attrs.append(radius)
            attrs.append(math.pi * radius * radius)
        elif type == 3:
            # convex hull
            output_geometry = geometry.convexHull()
            attrs.append(output_geometry.constGet().area())
            attrs.append(output_geometry.constGet().perimeter())
        f = QgsFeature()
        f.setAttributes(attrs)
        f.setGeometry(output_geometry)
        return f
Пример #13
0
 def __polygonVertexId(self, polygon_v2):
     """
     To get the id of the selected vertex from a polygon
     :param polygon_v2: the polygon as polygonV2
     :return: id as QgsVertexId
     """
     eR = polygon_v2.exteriorRing()
     if self.__selectedVertex < eR.numPoints():
         return QgsVertexId(0, 0, self.__selectedVertex, 1)
     else:
         sel = self.__selectedVertex - eR.numPoints()
         for num in xrange(polygon_v2.numInteriorRings()):
             iR = polygon_v2.interiorRing(num)
             if sel < iR.numPoints():
                 return QgsVertexId(0, num + 1, sel, 1)
             sel -= iR.numPoints()
Пример #14
0
    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)
Пример #15
0
 def test_nodes(self):
     """
     Test nodes for item
     """
     item = QgsAnnotationLineItem(
         QgsLineString(
             [QgsPoint(12, 13),
              QgsPoint(14, 13),
              QgsPoint(14, 15)]))
     self.assertEqual(item.nodes(), [
         QgsAnnotationItemNode(QgsVertexId(0, 0, 0), QgsPointXY(12, 13),
                               Qgis.AnnotationItemNodeType.VertexHandle),
         QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(14, 13),
                               Qgis.AnnotationItemNodeType.VertexHandle),
         QgsAnnotationItemNode(QgsVertexId(0, 0, 2), QgsPointXY(14, 15),
                               Qgis.AnnotationItemNodeType.VertexHandle)
     ])
Пример #16
0
 def polygonVertexId(polygon_v2, vertex_id):
     """
     To get the id of the selected vertex from a polygon
     :param polygon_v2: the polygon as polygonV2
     :param vertex_id: selected vertex
     :return: id as QgsVertexId
     """
     eR = polygon_v2.exteriorRing()
     if vertex_id < eR.numPoints():
         return QgsVertexId(0, 0, vertex_id, 1)
     else:
         sel = vertex_id - eR.numPoints()
         for num in range(polygon_v2.numInteriorRings()):
             iR = polygon_v2.interiorRing(num)
             if sel < iR.numPoints():
                 return QgsVertexId(0, num + 1, sel, 1)
             sel -= iR.numPoints()
         return QgsVertexId()
Пример #17
0
 def polyLineSize(self, geom, iPart, iRing):
     """
     Gets the number of vertexes
     :param geom: QgsAbstractGeometryV2
     :param iPart: int
     :param iRing: int
     :return:
     """
     nVerts = geom.vertexCount(iPart, iRing)
     if isinstance(geom, QgsMultiPolygonV2) or isinstance(
             geom, QgsPolygonV2) or isinstance(geom, QgsCircularStringV2):
         front = geom.vertexAt(
             QgsVertexId(iPart, iRing, 0, QgsVertexId.SegmentVertex))
         back = geom.vertexAt(
             QgsVertexId(iPart, iRing, nVerts - 1,
                         QgsVertexId.SegmentVertex))
         if front == back:
             return nVerts - 1
     return nVerts
 def run(self):
     segment_data = ''
     try:
         features = self.axial_layer.getFeatures()
         # I leave all the if clauses outside the for loop to gain some speed
         vid = QgsVertexId()
         if self.id != '':
             if self.weight != '':
                 for f in features:
                     nr = 0
                     while f.geometry().vertexIdFromVertexNr(nr + 1, vid):
                         segment_data += str(f.attribute(self.id)) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr).y()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr + 1).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr + 1).y()) + "\t"
                         segment_data += str(f.attribute(self.weight)) + "\n"
                         nr += 1
             else:
                 for f in features:
                     nr = 0
                     while f.geometry().vertexIdFromVertexNr(nr + 1, vid):
                         segment_data += str(f.attribute(self.id)) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr).y()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr + 1).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr + 1).y()) + "\n"
                         nr += 1
         else:
             if self.weight != '':
                 for f in features:
                     nr = 0
                     while f.geometry().vertexIdFromVertexNr(nr + 1, vid):
                         segment_data += str(f.id()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr).y()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr + 1).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr + 1).y()) + "\t"
                         segment_data += str(f.attribute(self.weight)) + "\n"
                         nr += 1
             else:
                 for f in features:
                     nr = 0
                     while f.geometry().vertexIdFromVertexNr(nr + 1, vid):
                         segment_data += str(f.id()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr).y()) + "\t"
                         segment_data += str(f.geometry().vertexAt(nr + 1).x()) + "\t" + \
                                         str(f.geometry().vertexAt(nr + 1).y()) + "\n"
                         nr += 1
         self.status.emit('Model exported for analysis.')
         self.result.emit(segment_data)
     except:
         self.error.emit('Exporting segment map failed.')
Пример #19
0
    def btn_ok_clicked(self):
        new_zs = self.static_canvas.pipe_line.get_ydata()
        pipe_geom_v2 = self.pipe_ft.geometry().get()
        for p in range(pipe_geom_v2.vertexCount(0, 0)):
            vertex_id = QgsVertexId(0, 0, p, QgsVertexId.SegmentVertex)
            vertex = pipe_geom_v2.vertexAt(vertex_id)
            new_pos_pt = QgsPoint(vertex.x(), vertex.y())
            new_pos_pt.addZValue(new_zs[p])

            LinkHandler.move_link_vertex(self.params, self.params.pipes_vlay,
                                         self.pipe_ft, new_pos_pt, p)

        # Update delta z for nodes
        (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes(
            self.params, self.pipe_ft.geometry())
        start_node_elev = start_node_ft.attribute(Junction.field_name_elev)
        # end_node_deltaz = end_node_ft.attribute(Junction.field_name_delta_z)
        end_node_elev = end_node_ft.attribute(Junction.field_name_elev)

        start_node_new_deltaz = new_zs[0] - start_node_elev
        end_node_new_deltaz = new_zs[-1] - end_node_elev

        start_node_ft.setAttribute(
            start_node_ft.fieldNameIndex(Junction.field_name_delta_z),
            start_node_new_deltaz)
        end_node_ft.setAttribute(
            end_node_ft.fieldNameIndex(Junction.field_name_delta_z),
            end_node_new_deltaz)

        # Update start node elevation attribute
        start_node_lay = NetworkUtils.find_node_layer(self.params,
                                                      start_node_ft.geometry())
        vector_utils.update_attribute(start_node_lay, start_node_ft,
                                      Junction.field_name_delta_z,
                                      float(start_node_new_deltaz))

        # Update end node elevation attribute
        end_node_lay = NetworkUtils.find_node_layer(self.params,
                                                    end_node_ft.geometry())
        vector_utils.update_attribute(end_node_lay, end_node_ft,
                                      Junction.field_name_delta_z,
                                      float(end_node_new_deltaz))

        # Update pipe length
        # Calculate 3D length
        pipe_geom_2 = self.pipe_ft.geometry()
        if self.params.dem_rlay is not None:
            length_3d = LinkHandler.calc_3d_length(self.params, pipe_geom_2)
        else:
            length_3d = pipe_geom_2.length()
        vector_utils.update_attribute(self.params.pipes_vlay, self.pipe_ft,
                                      Pipe.field_name_length, length_3d)

        self.setVisible(False)
Пример #20
0
    def motion_notify_callback(self, event):

        if self.parent.toolbar._active is not None:
            return

        if not self.showverts:
            return
        if self._ind is None:
            return
        if event.inaxes is None:
            return
        if event.button != 1:
            return

        vertices = self.pipe_patch.get_path().vertices
        x, y = vertices[self._ind][0], event.ydata
        if self._ind != 0 and self._ind != len(vertices) - 1:
            return

        vertices[self._ind] = x, y

        pipe_geom_v2 = self.pipe_ft.geometry().get()

        # Interpolate remaining vertices
        for v in range(1, len(vertices) - 1):
            distance = self.find_distance(self.pipe_ft.geometry().get(), v)
            vertex = pipe_geom_v2.vertexAt(
                QgsVertexId(0, 0, v, QgsVertexId.SegmentVertex))
            z = raster_utils.read_layer_val_from_coord(
                self.params.dem_rlay, QgsPoint(vertex.x(), vertex.y()))

            # Inteporlate deltaZs
            (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes(
                self.params, self.pipe_ft.geometry())
            start_node_elev = start_node_ft.attribute(Junction.field_name_elev)
            # end_node_deltaz = end_node_ft.attribute(Junction.field_name_delta_z)
            end_node_elev = end_node_ft.attribute(Junction.field_name_elev)
            start_node_deltaz = vertices[0][1] - start_node_elev
            end_node_deltaz = vertices[-1][1] - end_node_elev

            delta_z = (
                distance / self.pipe_ft.geometry().length() *
                (end_node_deltaz - start_node_deltaz)) + start_node_deltaz

            # z = (distance / self.pipe_ft.geometry().length() * (vertices[-1][1] - vertices[0][1])) + vertices[0][1]
            vertices[v] = vertices[v][0], z + delta_z

        self.pipe_line.set_data(list(zip(*vertices)))

        self.restore_region(self.background)
        self.axes.draw_artist(self.pipe_patch)
        self.axes.draw_artist(self.pipe_line)
        self.blit(self.axes.bbox)
Пример #21
0
 def test_nodes(self):
     """
     Test nodes for item
     """
     item = QgsAnnotationPolygonItem(
         QgsPolygon(
             QgsLineString([
                 QgsPoint(12, 13),
                 QgsPoint(14, 13),
                 QgsPoint(14, 15),
                 QgsPoint(12, 13)
             ])))
     # nodes shouldn't form a closed ring
     self.assertEqual(item.nodes(), [
         QgsAnnotationItemNode(QgsVertexId(0, 0, 0), QgsPointXY(12, 13),
                               Qgis.AnnotationItemNodeType.VertexHandle),
         QgsAnnotationItemNode(QgsVertexId(0, 0, 1), QgsPointXY(14, 13),
                               Qgis.AnnotationItemNodeType.VertexHandle),
         QgsAnnotationItemNode(QgsVertexId(0, 0, 2), QgsPointXY(14, 15),
                               Qgis.AnnotationItemNodeType.VertexHandle)
     ])
Пример #22
0
 def _vertex_id(part, ring, vertex):
     vertex_id = QgsVertexId(part, ring, vertex, QgsVertexId.SegmentVertex)
     if not vertex_id.IsValid():
         vertex_id = QgsVertexId(part, ring, vertex, QgsVertexId.CurveVertex)
         if not vertex_id.IsValid():
             raise ValueError("Invalid vertex addressing.")
     return vertex_id
Пример #23
0
    def test_apply_move_node_edit(self):
        item = QgsAnnotationPolygonItem(
            QgsPolygon(
                QgsLineString([
                    QgsPoint(12, 13),
                    QgsPoint(14, 13),
                    QgsPoint(14, 15),
                    QgsPoint(12, 13)
                ])))
        self.assertEqual(item.geometry().asWkt(),
                         'Polygon ((12 13, 14 13, 14 15, 12 13))')

        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationMoveNode('',
                                                       QgsVertexId(0, 0, 1),
                                                       QgsPoint(14, 13),
                                                       QgsPoint(17, 18))),
            Qgis.AnnotationItemEditOperationResult.Success)
        self.assertEqual(item.geometry().asWkt(),
                         'Polygon ((12 13, 17 18, 14 15, 12 13))')
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationMoveNode('',
                                                       QgsVertexId(0, 0, 3),
                                                       QgsPoint(12, 13),
                                                       QgsPoint(19, 20))),
            Qgis.AnnotationItemEditOperationResult.Success)
        self.assertEqual(item.geometry().asWkt(),
                         'Polygon ((19 20, 17 18, 14 15, 19 20))')
        self.assertEqual(
            item.applyEdit(
                QgsAnnotationItemEditOperationMoveNode('',
                                                       QgsVertexId(0, 0, 4),
                                                       QgsPoint(14, 15),
                                                       QgsPoint(19, 20))),
            Qgis.AnnotationItemEditOperationResult.Invalid)
        self.assertEqual(item.geometry().asWkt(),
                         'Polygon ((19 20, 17 18, 14 15, 19 20))')
Пример #24
0
    def test_transient_move_operation(self):
        item = QgsAnnotationLineItem(
            QgsLineString(
                [QgsPoint(12, 13),
                 QgsPoint(14, 13),
                 QgsPoint(14, 15)]))
        self.assertEqual(item.geometry().asWkt(),
                         'LineString (12 13, 14 13, 14 15)')

        res = item.transientEditResults(
            QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 1),
                                                   QgsPoint(14, 13),
                                                   QgsPoint(17, 18)))
        self.assertEqual(res.representativeGeometry().asWkt(),
                         'LineString (12 13, 17 18, 14 15)')
        def _get_uncommon_vertices(_common_vertices, geometry: QgsGeometry):
            res = []

            const_geom = geometry.constGet()
            vid = QgsVertexId()

            vertex_no = 1
            while True:
                ok, vertex = const_geom.nextVertex(vid)
                if ok:
                    if not QgsPointXY(vertex) in _common_vertices:
                        res.append(vertex_no)
                    vertex_no += 1
                else:
                    break

            return res
Пример #26
0
    def __ok(self, withVertex, withPoint):
        line_v2, curved = GeometryV2.asLineV2(
            self.__selectedFeature.geometry())
        vertex_v2 = QgsPointV2()
        vertex_id = QgsVertexId()
        line_v2.closestSegment(QgsPointV2(self.__mapPoint), vertex_v2,
                               vertex_id, 0)

        x0 = line_v2.xAt(vertex_id.vertex - 1)
        y0 = line_v2.yAt(vertex_id.vertex - 1)
        d0 = Finder.sqrDistForCoords(x0, vertex_v2.x(), y0, vertex_v2.y())
        x1 = line_v2.xAt(vertex_id.vertex)
        y1 = line_v2.yAt(vertex_id.vertex)
        d1 = Finder.sqrDistForCoords(x1, vertex_v2.x(), y1, vertex_v2.y())
        z0 = line_v2.zAt(vertex_id.vertex - 1)
        z1 = line_v2.zAt(vertex_id.vertex)
        vertex_v2.addZValue((d0 * z1 + d1 * z0) / (d0 + d1))

        if withPoint:
            pt_feat = QgsFeature(self.__layer.pendingFields())
            pt_feat.setGeometry(QgsGeometry(vertex_v2))

            if self.__layer.editFormConfig().suppress(
            ) == QgsEditFormConfig.SuppressOn:
                self.__layer.addFeature(pt_feat)
            else:
                self.__iface.openFeatureForm(self.__layer, pt_feat)

        if withVertex:
            line_v2.insertVertex(vertex_id, vertex_v2)
            self.__lastLayer.changeGeometry(self.__selectedFeature.id(),
                                            QgsGeometry(line_v2))
        self.__lastLayer.removeSelection()
        self.__rubber.reset()
        self.__lastFeatureId = None
        self.__selectedFeature = None
        self.__isEditing = False
Пример #27
0
    def processAlgorithm(
            self,  # pylint: disable=missing-function-docstring,too-many-statements,too-many-branches,too-many-locals
            parameters,
            context,
            feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, source.fields(),
                                               source.wkbType(),
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        threshold = self.parameterAsDouble(parameters, self.THRESHOLD, context)
        fields = self.parameterAsFields(parameters, self.FIELDS, context)
        field_indices = [source.fields().lookupField(f) for f in fields]
        index = QgsSpatialIndex()
        roads = {}

        total = 10.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

        for current, feature in enumerate(features):
            if feedback.isCanceled():
                break

            if feature.geometry().isMultipart():
                if feature.geometry().constGet().numGeometries() > 1:
                    raise QgsProcessingException(
                        self.tr('Only single-part geometries are supported'))
                part1 = feature.geometry().constGet().geometryN(0).clone()
                feature.setGeometry(part1)

            index.addFeature(feature)
            roads[feature.id()] = feature

            feedback.setProgress(int(current * total))

        collapsed = {}
        processed = set()

        total = 85.0 / len(roads)
        current = 0
        for _id, f in roads.items():
            if feedback.isCanceled():
                break

            current += 1
            feedback.setProgress(10 + current * total)

            if _id in processed:
                continue

            box = f.geometry().boundingBox()
            box.grow(threshold)

            similar_candidates = index.intersects(box)
            if not similar_candidates:
                collapsed[_id] = f
                processed.add(_id)
                continue

            candidate = f.geometry()
            candidate_attrs = [f.attributes()[i] for i in field_indices]

            parts = []

            for t in similar_candidates:
                if t == _id:
                    continue

                other = roads[t]
                other_attrs = [other.attributes()[i] for i in field_indices]
                if other_attrs != candidate_attrs:
                    continue

                dist = candidate.hausdorffDistance(other.geometry())
                if dist < threshold:
                    parts.append(t)

            if len(parts) == 0:
                collapsed[_id] = f
                continue

            # todo fix this
            if len(parts) > 1:
                continue
            assert len(parts) == 1, len(parts)

            other = roads[parts[0]].geometry()
            averaged = QgsGeometry(
                GeometryUtils.average_linestrings(candidate.constGet(),
                                                  other.constGet()))

            # reconnect touching lines
            bbox = candidate.boundingBox()
            bbox.combineExtentWith(other.boundingBox())
            touching_candidates = index.intersects(bbox)

            for touching_candidate in touching_candidates:
                if touching_candidate in (_id, parts[0]):
                    continue

                # print(touching_candidate)

                touching_candidate_geom = roads[touching_candidate].geometry()
                # either the start or end of touching_candidate_geom touches candidate
                start = QgsGeometry(
                    touching_candidate_geom.constGet().startPoint())
                end = QgsGeometry(
                    touching_candidate_geom.constGet().endPoint())

                moved_start = False
                moved_end = False
                for cc in [candidate, other]:
                    #  if start.touches(cc):
                    start_line = start.shortestLine(cc)
                    if start_line.length() < 0.00000001:
                        # start touches, move to touch averaged line
                        averaged_line = start.shortestLine(averaged)
                        new_start = averaged_line.constGet().endPoint()
                        touching_candidate_geom.get().moveVertex(
                            QgsVertexId(0, 0, 0), new_start)
                        # print('moved start')
                        moved_start = True
                        continue
                    end_line = end.shortestLine(cc)
                    if end_line.length() < 0.00000001:
                        # endtouches, move to touch averaged line
                        averaged_line = end.shortestLine(averaged)
                        new_end = averaged_line.constGet().endPoint()
                        touching_candidate_geom.get().moveVertex(
                            QgsVertexId(
                                0, 0,
                                touching_candidate_geom.constGet().numPoints()
                                - 1), new_end)
                        # print('moved end')
                        moved_end = True
                        # break

                index.deleteFeature(roads[touching_candidate])
                if moved_start and moved_end:
                    if touching_candidate in collapsed:
                        del collapsed[touching_candidate]
                    processed.add(touching_candidate)
                else:
                    roads[touching_candidate].setGeometry(
                        touching_candidate_geom)
                    index.addFeature(roads[touching_candidate])
                    if touching_candidate in collapsed:
                        collapsed[touching_candidate].setGeometry(
                            touching_candidate_geom)

            index.deleteFeature(f)
            index.deleteFeature(roads[parts[0]])

            ff = QgsFeature(roads[parts[0]])
            ff.setGeometry(averaged)
            index.addFeature(ff)
            roads[ff.id()] = ff

            ff = QgsFeature(f)
            ff.setGeometry(averaged)
            index.addFeature(ff)
            roads[_id] = ff

            collapsed[_id] = ff
            processed.add(_id)
            processed.add(parts[0])

        total = 5.0 / len(processed)
        current = 0
        for _, f in collapsed.items():
            if feedback.isCanceled():
                break

            sink.addFeature(f, QgsFeatureSink.FastInsert)
            current += 1
            feedback.setProgress(95 + int(current * total))

        return {self.OUTPUT: dest_id}
Пример #28
0
    def snapGeometry(self, geometry, snapTolerance, mode=PreferNodes):
        """
        Snaps a QgsGeometry in the reference layer
        :param geometry: QgsGeometry
        :param snapTolerance: float
        :param mode: DsgGeometrySnapper.PreferNodes or DsgGeometrySnapper.PreferClosest
        :return:
        """
        center = QgsPointV2(geometry.boundingBox().center())

        # Get potential reference features and construct snap index
        refGeometries = []
        searchBounds = geometry.boundingBox()
        searchBounds.grow(snapTolerance)
        # filter by bounding box to get candidates
        refFeatureIds = self.index.intersects(searchBounds)

        # End here in case we don't find candidates
        if len(refFeatureIds) == 0:
            return geometry

        # speeding up the process to consider only intersecting geometries
        refFeatureRequest = QgsFeatureRequest().setFilterFids(refFeatureIds)
        for refFeature in self.referenceLayer.getFeatures(refFeatureRequest):
            refGeometry = refFeature.geometry()
            segments = self.breakQgsGeometryIntoSegments(refGeometry)
            # testing intersection
            for segment in segments:
                if segment.intersects(searchBounds):
                    refGeometries.append(segment)

        # End here in case we don't find geometries
        if len(refGeometries) == 0:
            return geometry

        # building geometry index
        refDict, index = self.buildReferenceIndex(refGeometries)
        refSnapIndex = DsgSnapIndex(center, 10 * snapTolerance)
        for geom in refGeometries:
            refSnapIndex.addGeometry(geom.geometry())

        # Snap geometries
        subjGeom = geometry.geometry().clone()
        subjPointFlags = []

        # Pass 1: snap vertices of subject geometry to reference vertices
        for iPart in xrange(subjGeom.partCount()):
            subjPointFlags.append([])
            for iRing in xrange(subjGeom.ringCount(iPart)):
                subjPointFlags[iPart].append([])
                for iVert in xrange(self.polyLineSize(subjGeom, iPart, iRing)):
                    vidx = QgsVertexId(iPart, iRing, iVert,
                                       QgsVertexId.SegmentVertex)
                    p = QgsPointV2(subjGeom.vertexAt(vidx))
                    pF = QgsPoint(p.toQPointF())
                    snapPoint, snapSegment = refSnapIndex.getSnapItem(
                        p, snapTolerance)
                    success = snapPoint or snapSegment
                    if not success:
                        subjPointFlags[iPart][iRing].append(
                            DsgGeometrySnapper.Unsnapped)
                    else:
                        if mode == DsgGeometrySnapper.PreferNodes:
                            # Prefer snapping to point
                            if snapPoint:
                                subjGeom.moveVertex(vidx,
                                                    snapPoint.getSnapPoint(p))
                                subjPointFlags[iPart][iRing].append(
                                    DsgGeometrySnapper.SnappedToRefNode)
                            elif snapSegment:
                                subjGeom.moveVertex(
                                    vidx, snapSegment.getSnapPoint(p))
                                subjPointFlags[iPart][iRing].append(
                                    DsgGeometrySnapper.SnappedToRefSegment)
                        elif mode == DsgGeometrySnapper.PreferClosest:
                            nodeSnap = None
                            segmentSnap = None
                            distanceNode = sys.float_info.max
                            distanceSegment = sys.float_info.max
                            if snapPoint:
                                nodeSnap = snapPoint.getSnapPoint(p)
                                nodeSnapF = QgsPoint(nodeSnap.toQPointF())
                                distanceNode = nodeSnapF.sqrDist(pF)
                            if snapSegment:
                                segmentSnap = snapSegment.getSnapPoint(p)
                                segmentSnapF = QgsPoint(
                                    segmentSnap.toQPointF())
                                distanceSegment = segmentSnapF.sqrDist(pF)
                            if snapPoint and (distanceNode < distanceSegment):
                                subjGeom.moveVertex(vidx, nodeSnap)
                                subjPointFlags[iPart][iRing].append(
                                    DsgGeometrySnapper.SnappedToRefNode)
                            elif snapSegment:
                                subjGeom.moveVertex(vidx, segmentSnap)
                                subjPointFlags[iPart][iRing].append(
                                    DsgGeometrySnapper.SnappedToRefSegment)

        #nothing more to do for points
        if isinstance(subjGeom, QgsPointV2):
            return QgsGeometry(subjGeom)

        # SnapIndex for subject feature
        subjSnapIndex = DsgSnapIndex(center, 10 * snapTolerance)
        subjSnapIndex.addGeometry(subjGeom)

        origSubjGeom = subjGeom.clone()
        origSubjSnapIndex = DsgSnapIndex(center, 10 * snapTolerance)
        origSubjSnapIndex.addGeometry(origSubjGeom)

        # Pass 2: add missing vertices to subject geometry
        for refGeom in refGeometries:
            for iPart in xrange(refGeom.geometry().partCount()):
                for iRing in xrange(refGeom.geometry().ringCount(iPart)):
                    for iVert in xrange(
                            self.polyLineSize(refGeom.geometry(), iPart,
                                              iRing)):
                        point = refGeom.geometry().vertexAt(
                            QgsVertexId(iPart, iRing, iVert,
                                        QgsVertexId.SegmentVertex))
                        # QgsPoint used to calculate squared distance
                        pointF = QgsPoint(point.toQPointF())
                        snapPoint, snapSegment = subjSnapIndex.getSnapItem(
                            point, snapTolerance)
                        success = snapPoint or snapSegment
                        if success:
                            # Snap to segment, unless a subject point was already snapped to the reference point
                            if snapPoint and (QgsPoint(
                                    snapPoint.getSnapPoint(point).toQPointF()).
                                              sqrDist(pointF) < 1E-16):
                                continue
                            elif snapSegment:
                                # Look if there is a closer reference segment, if so, ignore this point
                                pProj = snapSegment.getSnapPoint(point)
                                pProjF = QgsPoint(pProj.toQPointF())
                                closest = refSnapIndex.getClosestSnapToPoint(
                                    point, pProj)
                                closestF = QgsPoint(closest.toQPointF())
                                if pProjF.sqrDist(pointF) > pProjF.sqrDist(
                                        closestF):
                                    continue
                                # If we are too far away from the original geometry, do nothing
                                if not origSubjSnapIndex.getSnapItem(
                                        point, snapTolerance):
                                    continue
                                idx = snapSegment.idxFrom
                                subjGeom.insertVertex(
                                    QgsVertexId(idx.vidx.part, idx.vidx.ring,
                                                idx.vidx.vertex + 1,
                                                QgsVertexId.SegmentVertex),
                                    point)
                                subjPointFlags[idx.vidx.part][
                                    idx.vidx.ring].insert(
                                        idx.vidx.vertex + 1,
                                        DsgGeometrySnapper.SnappedToRefNode)
                                subjSnapIndex = DsgSnapIndex(
                                    center, 10 * snapTolerance)
                                subjSnapIndex.addGeometry(subjGeom)

        # Pass 3: remove superfluous vertices: all vertices which are snapped to a segment and not preceded or succeeded by an unsnapped vertex
        for iPart in xrange(subjGeom.partCount()):
            for iRing in xrange(subjGeom.ringCount(iPart)):
                ringIsClosed = subjGeom.vertexAt(
                    QgsVertexId(
                        iPart, iRing, 0,
                        QgsVertexId.SegmentVertex)) == subjGeom.vertexAt(
                            QgsVertexId(iPart, iRing,
                                        subjGeom.vertexCount(iPart, iRing) - 1,
                                        QgsVertexId.SegmentVertex))
                nVerts = self.polyLineSize(subjGeom, iPart, iRing)
                iVert = 0
                while iVert < nVerts:
                    iPrev = (iVert - 1 + nVerts) % nVerts
                    iNext = (iVert + 1) % nVerts
                    pMid = subjGeom.vertexAt(
                        QgsVertexId(iPart, iRing, iVert,
                                    QgsVertexId.SegmentVertex))
                    pPrev = subjGeom.vertexAt(
                        QgsVertexId(iPart, iRing, iPrev,
                                    QgsVertexId.SegmentVertex))
                    pNext = subjGeom.vertexAt(
                        QgsVertexId(iPart, iRing, iNext,
                                    QgsVertexId.SegmentVertex))

                    pointOnSeg = self.projPointOnSegment(pMid, pPrev, pNext)
                    pointOnSegF = QgsPoint(pointOnSeg.toQPointF())
                    pMidF = QgsPoint(pMid.toQPointF())
                    dist = pointOnSegF.sqrDist(pMidF)

                    if subjPointFlags[iPart][iRing][iVert] == DsgGeometrySnapper.SnappedToRefSegment \
                     and subjPointFlags[iPart][iRing][iPrev] != DsgGeometrySnapper.Unsnapped \
                     and subjPointFlags[iPart][iRing][iNext] != DsgGeometrySnapper.Unsnapped \
                     and dist < 1E-12:
                        if (ringIsClosed and nVerts > 3) or (not ringIsClosed
                                                             and nVerts > 2):
                            subjGeom.deleteVertex(
                                QgsVertexId(iPart, iRing, iVert,
                                            QgsVertexId.SegmentVertex))
                            del subjPointFlags[iPart][iRing][iVert]
                            iVert -= 1
                            nVerts -= 1
                        else:
                            # Don't delete vertices if this would result in a degenerate geometry
                            break
                    iVert += 1
        return QgsGeometry(subjGeom)
Пример #29
0
    def processAlgorithm(
            self,  # pylint: disable=missing-function-docstring,too-many-statements,too-many-branches,too-many-locals
            parameters,
            context,
            feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)

        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, source.fields(),
                                               QgsWkbTypes.LineString,
                                               source.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        roundabout_expression_string = self.parameterAsExpression(
            parameters, self.EXPRESSION, context)

        # step 1 - find all roundabouts
        exp = QgsExpression(roundabout_expression_string)
        expression_context = self.createExpressionContext(
            parameters, context, source)
        exp.prepare(expression_context)

        roundabouts = []
        not_roundabouts = {}
        not_roundabout_index = QgsSpatialIndex()

        total = 10.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

        _id = 1
        for current, feature in enumerate(features):
            if feedback.isCanceled():
                break

            def add_feature(f, _id, geom, is_roundabout):
                output_feature = QgsFeature(f)
                output_feature.setGeometry(geom)
                output_feature.setId(_id)
                if is_roundabout:
                    roundabouts.append(output_feature)
                else:
                    not_roundabouts[output_feature.id()] = output_feature
                    not_roundabout_index.addFeature(output_feature)

            expression_context.setFeature(feature)

            is_roundabout = exp.evaluate(expression_context)
            if not feature.geometry().wkbType() == QgsWkbTypes.LineString:
                geom = feature.geometry()
                for p in geom.parts():
                    add_feature(feature, _id, QgsGeometry(p.clone()),
                                is_roundabout)
                    _id += 1
            else:
                add_feature(feature, _id, feature.geometry(), is_roundabout)
                _id += 1

            # Update the progress bar
            feedback.setProgress(int(current * total))

        feedback.pushInfo(
            self.tr('Found {} roundabout parts'.format(len(roundabouts))))
        feedback.pushInfo(
            self.tr('Found {} not roundabouts'.format(len(not_roundabouts))))

        if feedback.isCanceled():
            return {self.OUTPUT: dest_id}

        all_roundabouts = QgsGeometry.unaryUnion(
            [r.geometry() for r in roundabouts])
        feedback.setProgress(20)
        all_roundabouts = all_roundabouts.mergeLines()
        feedback.setProgress(25)

        total = 70.0 / all_roundabouts.constGet().numGeometries(
        ) if all_roundabouts.isMultipart() else 1

        for current, roundabout in enumerate(all_roundabouts.parts()):
            touching = not_roundabout_index.intersects(
                roundabout.boundingBox())
            if not touching:
                continue

            if feedback.isCanceled():
                break

            roundabout_engine = QgsGeometry.createGeometryEngine(roundabout)
            roundabout_engine.prepareGeometry()
            roundabout_geom = QgsGeometry(roundabout.clone())
            roundabout_centroid = roundabout_geom.centroid()

            other_points = []

            # find all touching roads, and move the touching part to the centroid
            for t in touching:
                touching_geom = not_roundabouts[t].geometry()
                touching_road = touching_geom.constGet().clone()
                if not roundabout_engine.touches(touching_road):
                    # print('not touching!!')
                    continue

                # work out if start or end of line touched the roundabout
                nearest = roundabout_geom.nearestPoint(touching_geom)
                _, v = touching_geom.closestVertexWithContext(
                    nearest.asPoint())

                if v == 0:
                    # started at roundabout
                    other_points.append((touching_road.endPoint(), True, t))
                else:
                    # ended at roundabout
                    other_points.append((touching_road.startPoint(), False, t))

            if not other_points:
                continue

            # see if any incoming segments originate at the same place ("V" patterns)
            averaged = set()
            for point1, started_at_roundabout1, id1 in other_points:
                if id1 in averaged:
                    continue

                if feedback.isCanceled():
                    break

                parts_to_average = [id1]
                for point2, _, id2 in other_points:
                    if id2 == id1:
                        continue

                    if point2 != point1:
                        # todo tolerance?
                        continue

                    parts_to_average.append(id2)

                if len(parts_to_average) == 1:
                    # not a <O pattern, just a round coming straight to the roundabout
                    line = not_roundabouts[id1].geometry().constGet().clone()
                    if started_at_roundabout1:
                        # extend start of line to roundabout centroid
                        line.moveVertex(QgsVertexId(0, 0, 0),
                                        roundabout_centroid.constGet())
                    else:
                        # extend end of line to roundabout centroid
                        line.moveVertex(
                            QgsVertexId(0, 0,
                                        line.numPoints() - 1),
                            roundabout_centroid.constGet())

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[parts_to_average[0]])
                    not_roundabouts[parts_to_average[0]].setGeometry(
                        QgsGeometry(line))
                    not_roundabout_index.addFeature(
                        not_roundabouts[parts_to_average[0]])

                elif len(parts_to_average) == 2:
                    # <O pattern
                    src_part, other_part = parts_to_average  # pylint: disable=unbalanced-tuple-unpacking
                    averaged.add(src_part)
                    averaged.add(other_part)

                    averaged_line = GeometryUtils.average_linestrings(
                        not_roundabouts[src_part].geometry().constGet(),
                        not_roundabouts[other_part].geometry().constGet())

                    if started_at_roundabout1:
                        # extend start of line to roundabout centroid
                        averaged_line.moveVertex(
                            QgsVertexId(0, 0, 0),
                            roundabout_centroid.constGet())
                    else:
                        # extend end of line to roundabout centroid
                        averaged_line.moveVertex(
                            QgsVertexId(0, 0,
                                        averaged_line.numPoints() - 1),
                            roundabout_centroid.constGet())

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[src_part])
                    not_roundabouts[src_part].setGeometry(
                        QgsGeometry(averaged_line))
                    not_roundabout_index.addFeature(not_roundabouts[src_part])

                    not_roundabout_index.deleteFeature(
                        not_roundabouts[other_part])
                    del not_roundabouts[other_part]

            feedback.setProgress(25 + int(current * total))

        total = 5.0 / len(not_roundabouts)
        current = 0
        for _, f in not_roundabouts.items():
            if feedback.isCanceled():
                break

            sink.addFeature(f, QgsFeatureSink.FastInsert)
            current += 1
            feedback.setProgress(95 + int(current * total))

        return {self.OUTPUT: dest_id}
Пример #30
0
    def fromQgsGeometry(cls, geometry, z_func, transform_func, centroid=True, drop_z=False,
                        ccw2d=False, use_z_func_cache=False, use_earcut=False):
        geom = cls()

        if z_func:
            if use_z_func_cache:
                cache = FunctionCacheXY(z_func)
                z_func = cache.func
        else:
            z_func = lambda x, y: 0

        if drop_z:
            g = geometry.get()
            g.dropZValue()
        else:
            g = geometry.constGet()

        if centroid:
            if drop_z:
                zf = z_func
            else:
                # use z coordinate of first vertex (until QgsAbstractGeometry supports z coordinate of centroid)
                zf = lambda x, y: g.vertexAt(QgsVertexId(0, 0, 0)).z() + z_func(pt.x(), pt.y())

            pt = geometry.centroid().asPoint()
            geom.centroids.append(transform_func(pt.x(), pt.y(), zf(pt.x(), pt.y())))

        # vertex transform function
        if drop_z:
            v_func = lambda x, y, z: transform_func(x, y, z_func(x, y))
        else:
            v_func = lambda x, y, z: transform_func(x, y, z + z_func(x, y))

        # triangulation
        if use_earcut:
            vertices = []
            for poly in cls.nestedPointList(g):
                if len(poly) == 1 and len(poly[0]) == 4:
                    vertices.extend([v_func(pt.x(), pt.y(), pt.z()) for pt in poly[0][0:3]])
                else:
                    bnds = [[[pt.x(), pt.y(), pt.z()] for pt in bnd] for bnd in poly]
                    data = earcut.flatten(bnds)
                    v = data["vertices"]
                    triangles = earcut.earcut(v, data["holes"], 3)
                    vertices.extend([v_func(v[3 * i], v[3 * i + 1], v[3 * i + 2]) for i in triangles])
        else:
            tes = QgsTessellator(0, 0, False)
            addPolygon = tes.addPolygon
            for poly in cls.singleGeometries(g):
                addPolygon(poly, 0)

            # mp = tes.asMultiPolygon()     # not available
            data = tes.data()       # [x0, z0, -y0, x1, z1, -y1, ...]
            vertices = [v_func(x, -my, z) for x, z, my in [data[i:i + 3] for i in range(0, len(data), 3)]]

        if ccw2d:
            # orient triangles to counter-clockwise order
            tris = []
            for v0, v1, v2 in [vertices[i:i + 3] for i in range(0, len(vertices), 3)]:
                if GeometryUtils.isClockwise([v0, v1, v2, v0]):
                    tris.append([v0, v2, v1])
                else:
                    tris.append([v0, v1, v2])
            geom.triangles = tris
        else:
            # use original vertex order
            geom.triangles = [vertices[i:i + 3] for i in range(0, len(vertices), 3)]

        return geom