Example #1
0
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
Example #2
0
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 QgsPointXY(snapped.x(), snapped.y())
Example #3
0
    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)
Example #4
0
    def delete_invalid_geometries(self, query_small_area=lambda feat: True):
        """
        Delete invalid geometries.

        Test if any of it acute angle vertex could be deleted.
        Also removes zig-zag and spike vertex (see Point.get_spike_context).
        """
        if log.app_level <= logging.DEBUG:
            debshp = DebugWriter("debug_notvalid.shp", self, QgsFields(),
                                 WKBPolygon)
            debshp2 = DebugWriter("debug_spikes.shp", self)
        to_change = {}
        to_clean = []
        to_move = {}
        parts = 0
        rings = 0
        zz = 0
        spikes = 0
        geometries = {}
        msg = _("Delete invalid geometries")
        pbar = self.get_progressbar(msg, len(geometries))
        for feat in self.getFeatures():
            fid = feat.id()
            geom = feat.geometry()
            badgeom = False
            pn = 0
            for polygon in Geometry.get_multipolygon(geom):
                f = QgsFeature(QgsFields())
                g = Geometry.fromPolygonXY(polygon)
                if g.area() < config.min_area and query_small_area(feat):
                    parts += 1
                    geom.deletePart(pn)
                    to_change[fid] = geom
                    f.setGeometry(QgsGeometry(g))
                    if log.app_level <= logging.DEBUG:
                        debshp.addFeature(f)
                    continue
                pn += 1
                for i, ring in enumerate(polygon):
                    if badgeom:
                        break
                    skip = False
                    for n, v in enumerate(ring[0:-1]):
                        (
                            angle_v,
                            angle_a,
                            ndx,
                            ndxa,
                            is_acute,
                            is_zigzag,
                            is_spike,
                            vx,
                        ) = Point(v).get_spike_context(geom)
                        if skip or not is_acute:
                            skip = False
                            continue
                        g = Geometry.fromPolygonXY([ring])
                        f.setGeometry(QgsGeometry(g))
                        g.deleteVertex(n)
                        if not g.isGeosValid() or g.area() < config.min_area:
                            if i > 0:
                                rings += 1
                                geom.deleteRing(i)
                                to_change[fid] = geom
                                if log.app_level <= logging.DEBUG:
                                    debshp.addFeature(f)
                            else:
                                badgeom = True
                                to_clean.append(fid)
                                if log.app_level <= logging.DEBUG:
                                    debshp.addFeature(f)
                            break
                        if len(ring) > 4:  # (can delete vertexs)
                            va = Point(geom.vertexAt(ndxa))
                            if is_zigzag:
                                g = QgsGeometry(geom)
                                if ndxa > ndx:
                                    g.deleteVertex(ndxa)
                                    g.deleteVertex(ndx)
                                    skip = True
                                else:
                                    g.deleteVertex(ndx)
                                    g.deleteVertex(ndxa)
                                valid = g.isGeosValid()
                                if valid:
                                    geom = g
                                    zz += 1
                                    to_change[fid] = g
                                if log.app_level <= logging.DEBUG:
                                    debshp2.add_point(
                                        va,
                                        "zza %d %d %d %f" %
                                        (fid, ndx, ndxa, angle_a),
                                    )
                                    debshp2.add_point(
                                        v,
                                        "zz %d %d %d %s" %
                                        (fid, ndx, len(ring), valid),
                                    )
                            elif is_spike:
                                g = QgsGeometry(geom)
                                to_move[va] = vx
                                g.moveVertex(vx.x(), vx.y(), ndxa)
                                g.deleteVertex(ndx)
                                valid = g.isGeosValid()
                                if valid:
                                    spikes += 1
                                    skip = ndxa > ndx
                                    geom = g
                                    to_change[fid] = g
                                if log.app_level <= logging.DEBUG:
                                    debshp2.add_point(vx,
                                                      "vx %d %d" % (fid, ndx))
                                    debshp2.add_point(
                                        va, "va %d %d %d %f" %
                                        (fid, ndx, ndxa, angle_a))
                                    debshp2.add_point(
                                        v,
                                        "v %d %d %d %s" %
                                        (fid, ndx, len(ring), valid),
                                    )
                geometries[fid] = geom
            if geom.area() < config.min_area and query_small_area(feat):
                to_clean.append(fid)
                if fid in to_change:
                    del to_change[fid]
            pbar.update()
        pbar.close()
        if to_move:
            for fid, geom in geometries.items():
                if fid in to_clean:
                    continue
                n = 0
                v = Point(geom.vertexAt(n))
                while v.x() != 0 or v.y() != 0:
                    if v in to_move:
                        g = QgsGeometry(geom)
                        vx = to_move[v]
                        if log.app_level <= logging.DEBUG:
                            debshp2.add_point(v, "mv %d %d" % (fid, n))
                            debshp2.add_point(vx, "mvx %d %d" % (fid, n))
                        g.moveVertex(vx.x(), vx.y(), n)
                        if g.isGeosValid():
                            geom = g
                            to_change[fid] = g
                    n += 1
                    v = Point(geom.vertexAt(n))
        if to_change:
            self.writer.changeGeometryValues(to_change)
        if parts:
            msg = _("Deleted %d invalid part geometries in the '%s' layer")
            log.debug(msg, parts, self.name())
            report.values["geom_parts_" + self.name()] = parts
        if rings:
            msg = _("Deleted %d invalid ring geometries in the '%s' layer")
            log.debug(msg, rings, self.name())
            report.values["geom_rings_" + self.name()] = rings
        if to_clean:
            self.writer.deleteFeatures(to_clean)
            msg = _("Deleted %d invalid geometries in the '%s' layer")
            log.debug(msg, len(to_clean), self.name())
            report.values["geom_invalid_" + self.name()] = len(to_clean)
        if zz:
            msg = _("Deleted %d zig-zag vertices in the '%s' layer")
            log.debug(msg, zz, self.name())
            report.values["vertex_zz_" + self.name()] = zz
        if spikes:
            msg = _("Deleted %d spike vertices in the '%s' layer")
            log.debug(msg, spikes, self.name())
            report.values["vertex_spike_" + self.name()] = spikes
Example #5
0
 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