Example #1
0
    def processAlgorithm(self, progress):
        layer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER))

        input_wkb = layer.wkbType()
        if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
            output_wkb = QgsWkbTypes.MultiPoint
        elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
            output_wkb = QgsWkbTypes.MultiLineString
        if QgsWkbTypes.hasZ(input_wkb):
            output_wkb = QgsWkbTypes.addZ(output_wkb)
        if QgsWkbTypes.hasM(input_wkb):
            output_wkb = QgsWkbTypes.addM(output_wkb)

        writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(layer.fields(), output_wkb, layer.crs())

        features = vector.features(layer)
        total = 100.0 / len(features)

        for current, input_feature in enumerate(features):
            output_feature = input_feature
            input_geometry = input_feature.geometry()
            if input_geometry:
                output_geometry = QgsGeometry(input_geometry.geometry().boundary())
                if not output_geometry:
                    raise GeoAlgorithmExecutionException(self.tr("Error calculating boundary"))

                output_feature.setGeometry(output_geometry)

            writer.addFeature(output_feature)
            progress.setPercentage(int(current * total))

        del writer
Example #2
0
 def _setRubberBandMarker(self, geom):
     m = QgsRubberBand(self.qgisIface.mapCanvas(), False)  # not polygon
     if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
         linegeom = geom
     elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PolygonGeometry:
         linegeom = QgsGeometry.fromPolylineXY(geom.asPolygon()[0])
     m.setToGeometry(linegeom, None)
     m.setColor(QColor(self.config['rubber_color']))
     m.setWidth(self.config['rubber_width'])
     return m
Example #3
0
    def outputWkbType(self, input_wkb):
        if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
            output_wkb = QgsWkbTypes.MultiPoint
        elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
            output_wkb = QgsWkbTypes.MultiLineString
        if QgsWkbTypes.hasZ(input_wkb):
            output_wkb = QgsWkbTypes.addZ(output_wkb)
        if QgsWkbTypes.hasM(input_wkb):
            output_wkb = QgsWkbTypes.addM(output_wkb)

        return output_wkb
Example #4
0
 def _setMarkerGeom(self, geom):
     if geom.isMultipart():
         geometries = self._extractAsSingle(geom)
         for g in geometries:
             self._setMarkerGeom(g)
     else:
         if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
             m = self._setPointMarker(geom)
         elif QgsWkbTypes.geometryType(geom.wkbType()) in (QgsWkbTypes.LineGeometry, QgsWkbTypes.PolygonGeometry):
             m = self._setRubberBandMarker(geom)
         self.markers.append( m )
Example #5
0
 def convertToLineStrings(self, geom):
     if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
         raise QgsProcessingException(
             self.tr('Cannot convert from {0} to LineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
     elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
         if QgsWkbTypes.isMultiType(geom.wkbType()):
             return geom.asGeometryCollection()
         else:
             #line to line
             return [geom]
     else:
         # polygons to lines
         # we just use the boundary here - that consists of all rings in the (multi)polygon
         boundary = QgsGeometry(geom.constGet().boundary())
         # boundary will be multipart
         return boundary.asGeometryCollection()
Example #6
0
 def convertToMultiLineStrings(self, geom):
     if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
         raise QgsProcessingException(
             self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
     elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
         if QgsWkbTypes.isMultiType(geom.wkbType()):
             return [geom]
         else:
             # line to multiLine
             ml = QgsMultiLineString()
             ml.addGeometry(geom.constGet().clone())
             return [QgsGeometry(ml)]
     else:
         # polygons to multilinestring
         # we just use the boundary here - that consists of all rings in the (multi)polygon
         return [QgsGeometry(geom.constGet().boundary())]
Example #7
0
 def convertToPolygon(self, geom):
     if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry and geom.constGet().nCoordinates() < 3:
         raise QgsProcessingException(
             self.tr('Cannot convert from Point to Polygon').format(QgsWkbTypes.displayString(geom.wkbType())))
     elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
         # multipoint with at least 3 points
         # TODO: mega inefficient - needs rework when geometry iterators land
         # (but at least it doesn't lose Z/M values)
         points = []
         for g in geom.constGet().coordinateSequence():
             for r in g:
                 for p in r:
                     points.append(p)
         linestring = QgsLineString(points)
         linestring.close()
         p = QgsPolygon()
         p.setExteriorRing(linestring)
         return [QgsGeometry(p)]
     elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
         if QgsWkbTypes.isMultiType(geom):
             parts = []
             for i in range(geom.constGet().numGeometries()):
                 p = QgsPolygon()
                 linestring = geom.constGet().geometryN(i).clone()
                 linestring.close()
                 p.setExteriorRing(linestring)
                 parts.append(QgsGeometry(p))
             return QgsGeometry.collectGeometry(parts)
         else:
             # linestring to polygon
             p = QgsPolygon()
             linestring = geom.constGet().clone()
             linestring.close()
             p.setExteriorRing(linestring)
             return [QgsGeometry(p)]
     else:
         #polygon
         if QgsWkbTypes.isMultiType(geom):
             return geom.asGeometryCollection()
         else:
             return [geom]
Example #8
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)

        input_wkb = source.wkbType()
        if QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.LineGeometry:
            output_wkb = QgsWkbTypes.MultiPoint
        elif QgsWkbTypes.geometryType(input_wkb) == QgsWkbTypes.PolygonGeometry:
            output_wkb = QgsWkbTypes.MultiLineString
        if QgsWkbTypes.hasZ(input_wkb):
            output_wkb = QgsWkbTypes.addZ(output_wkb)
        if QgsWkbTypes.hasM(input_wkb):
            output_wkb = QgsWkbTypes.addM(output_wkb)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
                                               source.fields(), output_wkb, source.sourceCrs())

        features = source.getFeatures()
        total = 100.0 / source.featureCount()

        for current, input_feature in enumerate(features):
            if feedback.isCanceled():
                break
            output_feature = input_feature
            input_geometry = input_feature.geometry()
            if input_geometry:
                output_geometry = QgsGeometry(input_geometry.geometry().boundary())
                if not output_geometry:
                    raise GeoAlgorithmExecutionException(
                        self.tr('Error calculating boundary'))

                output_feature.setGeometry(output_geometry)

            sink.addFeature(output_feature)
            feedback.setProgress(int(current * total))

        return {self.OUTPUT_LAYER: dest_id}
Example #9
0
    def processAlgorithm(self, context, feedback):
        layerA = dataobjects.getLayerFromString(
            self.getParameterValue(self.INPUT_A))
        splitLayer = dataobjects.getLayerFromString(
            self.getParameterValue(self.INPUT_B))

        sameLayer = self.getParameterValue(
            self.INPUT_A) == self.getParameterValue(self.INPUT_B)
        fieldList = layerA.fields()

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fieldList, QgsWkbTypes.multiType(layerA.wkbType()), layerA.crs(),
            context)

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])

        for aSplitFeature in QgsProcessingUtils.getFeatures(
                splitLayer, context, request):
            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = QgsProcessingUtils.getFeatures(layerA, context)

        if QgsProcessingUtils.featureCount(layerA, context) == 0:
            total = 100
        else:
            total = 100.0 / QgsProcessingUtils.featureCount(layerA, context)

        for current, inFeatA in enumerate(features):
            inGeom = inFeatA.geometry()
            attrsA = inFeatA.attributes()
            outFeat.setAttributes(attrsA)

            if inGeom.isMultipart():
                inGeoms = []

                for g in inGeom.asGeometryCollection():
                    inGeoms.append(g)
            else:
                inGeoms = [inGeom]

            lines = spatialIndex.intersects(inGeom.boundingBox())

            if len(lines) > 0:  # has intersection of bounding boxes
                splittingLines = []

                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

                for i in lines:
                    try:
                        splitGeom = splitGeoms[i]
                    except:
                        continue

                    # check if trying to self-intersect
                    if sameLayer:
                        if inFeatA.id() == i:
                            continue

                    if engine.intersects(splitGeom.geometry()):
                        splittingLines.append(splitGeom)

                if len(splittingLines) > 0:
                    for splitGeom in splittingLines:
                        splitterPList = None
                        outGeoms = []

                        split_geom_engine = QgsGeometry.createGeometryEngine(
                            splitGeom.geometry())
                        split_geom_engine.prepareGeometry()

                        while len(inGeoms) > 0:
                            inGeom = inGeoms.pop()

                            if inGeom.isNull(
                            ):  # this has been encountered and created a run-time error
                                continue

                            if split_geom_engine.intersects(inGeom.geometry()):
                                inPoints = vector.extractPoints(inGeom)
                                if splitterPList is None:
                                    splitterPList = vector.extractPoints(
                                        splitGeom)

                                try:
                                    result, newGeometries, topoTestPoints = inGeom.splitGeometry(
                                        splitterPList, False)
                                except:
                                    QgsMessageLog.logMessage(
                                        self.
                                        tr('Geometry exception while splitting'
                                           ), self.tr('Processing'),
                                        QgsMessageLog.WARNING)
                                    result = 1

                                # splitGeometry: If there are several intersections
                                # between geometry and splitLine, only the first one is considered.
                                if result == 0:  # split occurred
                                    if inPoints == vector.extractPoints(
                                            inGeom):
                                        # bug in splitGeometry: sometimes it returns 0 but
                                        # the geometry is unchanged
                                        outGeoms.append(inGeom)
                                    else:
                                        inGeoms.append(inGeom)

                                        for aNewGeom in newGeometries:
                                            inGeoms.append(aNewGeom)
                                else:
                                    outGeoms.append(inGeom)
                            else:
                                outGeoms.append(inGeom)

                        inGeoms = outGeoms

            parts = []

            for aGeom in inGeoms:
                passed = True

                if QgsWkbTypes.geometryType(
                        aGeom.wkbType()) == QgsWkbTypes.LineGeometry:
                    numPoints = aGeom.geometry().numPoints()

                    if numPoints <= 2:
                        if numPoints == 2:
                            passed = not aGeom.geometry().isClosed(
                            )  # tests if vertex 0 = vertex 1
                        else:
                            passed = False
                            # sometimes splitting results in lines of zero length

                if passed:
                    parts.append(aGeom)

            if len(parts) > 0:
                outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
                writer.addFeature(outFeat)

            feedback.setProgress(int(current * total))
        del writer
Example #10
0
    def get_boundary_features_not_covered_by_plots(self, db, plot_layer,
                                                   boundary_layer,
                                                   more_bfs_layer, less_layer,
                                                   error_layer, id_field):
        """
        Return all boundary features that have errors when checking if they are covered by plots.
        This takes into account both geometric and alphanumeric (topology table) errors.
        """
        dict_uuid_plot = get_uuid_dict(plot_layer, db.names, db.names.T_ID_F)
        dict_uuid_boundary = get_uuid_dict(boundary_layer, db.names,
                                           db.names.T_ID_F)
        plot_as_lines_layer = processing.run("ladm_col:polygonstolines", {
            'INPUT': plot_layer,
            'OUTPUT': 'memory:'
        })['OUTPUT']

        # create dict with layer data
        id_field_idx = plot_as_lines_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_plot_as_lines = {
            feature[id_field]: feature
            for feature in plot_as_lines_layer.getFeatures(request)
        }

        id_field_idx = boundary_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_boundary = {
            feature[id_field]: feature
            for feature in boundary_layer.getFeatures(request)
        }

        exp_more = '"{}" is not null and "{}" is not null'.format(
            db.names.MORE_BFS_T_LC_BOUNDARY_F, db.names.MORE_BFS_T_LC_PLOT_F)
        list_more_bfs = [{
            'plot_id':
            feature[db.names.MORE_BFS_T_LC_PLOT_F],
            'boundary_id':
            feature[db.names.MORE_BFS_T_LC_BOUNDARY_F]
        } for feature in more_bfs_layer.getFeatures(exp_more)]

        exp_less = '"{}" is not null and "{}" is not null'.format(
            db.names.LESS_BFS_T_LC_BOUNDARY_F, db.names.LESS_BFS_T_LC_PLOT_F)
        list_less = [{
            'plot_id': feature[db.names.LESS_BFS_T_LC_PLOT_F],
            'boundary_id': feature[db.names.LESS_BFS_T_LC_BOUNDARY_F]
        } for feature in less_layer.getFeatures(exp_less)]

        tmp_inner_rings_layer = self.geometry.get_inner_rings_layer(
            db.names, plot_layer, db.names.T_ID_F)
        inner_rings_layer = processing.run(
            "native:addautoincrementalfield", {
                'INPUT': tmp_inner_rings_layer,
                'FIELD_NAME': 'AUTO',
                'START': 0,
                'GROUP_FIELDS': [],
                'SORT_EXPRESSION': '',
                'SORT_ASCENDING': True,
                'SORT_NULLS_FIRST': False,
                'OUTPUT': 'memory:'
            })['OUTPUT']

        id_field_idx = inner_rings_layer.fields().indexFromName(id_field)
        auto_idx = inner_rings_layer.fields().indexFromName('AUTO')
        request = QgsFeatureRequest().setSubsetOfAttributes(
            [id_field_idx, auto_idx])
        dict_inner_rings = {
            '{}-{}'.format(feature[id_field], feature['AUTO']): feature
            for feature in inner_rings_layer.getFeatures(request)
        }

        # spatial joins between boundary and inner rings
        # we use as input layer the rings because it is the existing information
        # in the terrain polygons and filters better because they are less records
        spatial_join_inner_rings_boundary_layer = processing.run(
            "qgis:joinattributesbylocation",
            {
                'INPUT': boundary_layer,
                'JOIN': inner_rings_layer,
                'PREDICATE': [0],  # Intersects
                'JOIN_FIELDS': [id_field, 'AUTO'],
                'METHOD': 0,
                'DISCARD_NONMATCHING': True,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        list_spatial_join_boundary_inner_rings = list()
        list_spatial_join_boundary_plot_ring = list()
        for feature in spatial_join_inner_rings_boundary_layer.getFeatures():
            # The id field has the same name for both layers
            # This list is only used to check plot's inner rings without boundaries
            list_spatial_join_boundary_inner_rings.append({
                'plot_ring_id':
                '{}-{}'.format(feature[id_field + '_2'], feature['AUTO']),
                'boundary_id':
                feature[id_field]
            })

            # list create for filter inner rings from spatial join with between plot and boundary
            list_spatial_join_boundary_plot_ring.append({
                'plot_id':
                feature[id_field + '_2'],
                'boundary_id':
                feature[id_field]
            })

        # Spatial join between boundary and plots as lines
        spatial_join_boundary_plot_layer = processing.run(
            "qgis:joinattributesbylocation", {
                'INPUT': boundary_layer,
                'JOIN': plot_as_lines_layer,
                'PREDICATE': [0],
                'JOIN_FIELDS': [id_field],
                'METHOD': 0,
                'DISCARD_NONMATCHING': True,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        # The id field has the same name for both layers
        list_spatial_join_boundary_plot = [{
            'plot_id': feature[id_field + '_2'],
            'boundary_id': feature[id_field]
        } for feature in spatial_join_boundary_plot_layer.getFeatures()]

        #####################################################
        # Validation of geometric errors
        #####################################################

        # Identify plots with geometry problems and remove coincidence in spatial join between plot as line and boundary
        # and inner_rings and boundary. If the geometry fails, there is no need to check further topological rules for
        # plots

        errors_boundary_plot_diffs = self.geometry.difference_boundary_plot(
            db.names, boundary_layer, plot_as_lines_layer, db.names.T_ID_F)
        for error_diff in errors_boundary_plot_diffs:
            boundary_id = error_diff['id']
            # All boundaries with geometric errors are eliminated. It is not necessary check more
            # in spatial join between boundary and plot as line
            for item_sj in list_spatial_join_boundary_plot.copy():
                if item_sj['boundary_id'] == boundary_id:
                    list_spatial_join_boundary_plot.remove(item_sj)

            # All boundaries with geometric errors are eliminated. It is not necessary check more
            # in spatial join between boundary and inner_rings
            for item_sj in list_spatial_join_boundary_inner_rings.copy():
                if item_sj['boundary_id'] == boundary_id:
                    list_spatial_join_boundary_inner_rings.remove(item_sj)

        ######################################################
        # Validation of errors in alphanumeric topology tables
        ######################################################

        # start validation in alphanumeric topology tables for more_bfs
        # remove spatial join intersection with geometries that no contain lines. Because it is not necessary to check
        for item_sj in list_spatial_join_boundary_plot.copy():
            boundary_id = item_sj['boundary_id']
            plot_id = item_sj['plot_id']

            if item_sj in list_spatial_join_boundary_plot_ring:
                # it is removed because it is registered in the spatial join between rings and boundaries
                # and it shouldn't be registered in the topology table of more_bfs
                list_spatial_join_boundary_plot.remove(item_sj)
            else:
                boundary_geom = dict_boundary[boundary_id].geometry()
                plot_geom = dict_plot_as_lines[plot_id].geometry()
                intersection = boundary_geom.intersection(plot_geom)

                if not intersection.isEmpty():
                    if intersection.type() != QgsWkbTypes.LineGeometry:
                        if intersection.type() == QgsWkbTypes.UnknownGeometry:
                            has_line = False
                            for part in intersection.asGeometryCollection():
                                if part.isMultipart():
                                    for i in range(part.numGeometries()):
                                        if QgsWkbTypes.geometryType(
                                                part.geometryN(i).wkbType()
                                        ) == QgsWkbTypes.LineGeometry:
                                            has_line = True
                                            break
                                else:
                                    if part.type() == QgsWkbTypes.LineGeometry:
                                        has_line = True
                                        break
                            if not has_line:
                                # Remove point intersections plot-boundary
                                list_spatial_join_boundary_plot.remove(item_sj)
                        else:
                            list_spatial_join_boundary_plot.remove(item_sj)

        # Check relation between plot and boundary not registered in more_bfs
        errors_not_in_more_bfs = list()
        errors_duplicate_in_more_bfs = list()
        for item_sj_bp in list_spatial_join_boundary_plot:
            count_more_bfs = list_more_bfs.count(item_sj_bp)
            if count_more_bfs > 1:
                errors_duplicate_in_more_bfs.append(
                    (item_sj_bp['plot_id'], item_sj_bp['boundary_id']))
            elif count_more_bfs == 0:
                # Check for the special case of two contiguous plots, one of them covers the common boundary, but the
                # other one does not! This should be still a geometry error but is not captured by the code above. Only
                # in this point of the whole checks we can validate between the individual boundary and the individual
                # plot.
                boundary_geom = dict_boundary[
                    item_sj_bp['boundary_id']].geometry()
                plot_geom = dict_plot_as_lines[
                    item_sj_bp['plot_id']].geometry()
                intersection = boundary_geom.intersection(plot_geom)

                if not intersection.isEmpty():
                    if intersection.isGeosEqual(boundary_geom):
                        errors_not_in_more_bfs.append(
                            (item_sj_bp['plot_id'], item_sj_bp['boundary_id']))
                    else:
                        errors_boundary_plot_diffs.append({
                            'id':
                            item_sj_bp['boundary_id'],
                            'id_plot':
                            item_sj_bp['plot_id'],
                            'geometry':
                            boundary_geom
                        })

        # finalize validation in more_bfs table

        # start validation in less table
        errors_not_in_less = list()
        errors_duplicate_in_less = list()
        # start validation in alphanumeric topology tables for less
        # remove spatial join intersection with geometries that do not contain lines.
        # Because it is not necessary to check topology register
        for inner_ring in list_spatial_join_boundary_inner_rings:
            boundary_id = inner_ring['boundary_id']
            plot_ring_id = inner_ring['plot_ring_id']

            boundary_geom = dict_boundary[boundary_id].geometry()
            inner_ring_geom = dict_inner_rings[plot_ring_id].geometry()

            # check intersections difference to line, we check that collections do not have lines parts
            intersection = boundary_geom.intersection(inner_ring_geom)
            has_line = False

            if not intersection.isEmpty():
                if intersection.type() != QgsWkbTypes.LineGeometry:
                    if intersection.type() == QgsWkbTypes.UnknownGeometry:
                        for part in intersection.asGeometryCollection():
                            if part.isMultipart():
                                for i in range(part.numGeometries()):
                                    if QgsWkbTypes.geometryType(
                                            part.geometryN(i).wkbType()
                                    ) == QgsWkbTypes.LineGeometry:
                                        has_line = True
                                        break
                            else:
                                if part.type() == QgsWkbTypes.LineGeometry:
                                    has_line = True
                                    break
                else:
                    has_line = True

            if has_line:
                tmp_dict_plot_boundary = {
                    'plot_id': int(plot_ring_id.split('-')[0]),
                    'boundary_id': boundary_id
                }
                count_less = list_less.count(tmp_dict_plot_boundary)

                if count_less > 1:
                    errors_duplicate_in_less.append(
                        (plot_ring_id, boundary_id))  # duplicate in less table
                elif count_less == 0:
                    errors_not_in_less.append(
                        (plot_ring_id,
                         boundary_id))  # no registered less table
        # finalize validation for less table

        features = list()

        # boundary not covered by plot
        for boundary_plot_diff in errors_boundary_plot_diffs:
            boundary_id = boundary_plot_diff['id']
            boundary_geom = boundary_plot_diff['geometry']
            plot_id = boundary_plot_diff[
                'id_plot'] if 'id_plot' in boundary_plot_diff else None
            new_feature = QgsVectorLayerUtils().createFeature(
                error_layer, boundary_geom, {
                    0:
                    dict_uuid_boundary.get(boundary_id),
                    1:
                    dict_uuid_plot.get(plot_id),
                    2:
                    self.quality_rules_manager.get_error_message(
                        QUALITY_RULE_ERROR_CODE_E200301),
                    3:
                    QUALITY_RULE_ERROR_CODE_E200301
                })
            features.append(new_feature)

        # No registered more bfs
        if errors_not_in_more_bfs:
            for error_more_bfs in set(errors_not_in_more_bfs):
                plot_id = error_more_bfs[0]  # plot_id
                boundary_id = error_more_bfs[1]  # boundary_id
                geom_boundary = dict_boundary[boundary_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_boundary, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200304),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200304
                    })
                features.append(new_feature)

        # Duplicate in more bfs
        if errors_duplicate_in_more_bfs:
            for error_more_bfs in set(errors_duplicate_in_more_bfs):
                plot_id = error_more_bfs[0]  # plot_id
                boundary_id = error_more_bfs[1]  # boundary_id
                geom_boundary = dict_boundary[boundary_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_boundary, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200302),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200302
                    })
                features.append(new_feature)

        # No registered less
        if errors_not_in_less:
            for error_less in set(errors_not_in_less):
                plot_ring_id = error_less[0]  # plot_ring_id
                plot_id = int(plot_ring_id.split('-')[0])  # plot_id
                boundary_id = error_less[1]  # boundary_id
                geom_ring = dict_inner_rings[plot_ring_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_ring, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200305),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200305
                    })
                features.append(new_feature)

        # Duplicate in less
        if errors_duplicate_in_less:
            for error_less in set(errors_duplicate_in_less):
                plot_ring_id = error_less[0]  # plot_ring_id
                plot_id = int(plot_ring_id.split('-')[0])  # plot_id
                boundary_id = error_less[1]  # boundary_id
                geom_ring = dict_inner_rings[plot_ring_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_ring, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200303),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200303
                    })
                features.append(new_feature)

        return features
Example #11
0
 def processAlgorithm(self, parameters, context, feedback):
     source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
     angle = self.parameterAsDouble(parameters, self.PrmTransformRotation, context)
     scale = self.parameterAsDouble(parameters, self.PrmTransformScale, context)
     azimuth = self.parameterAsDouble(parameters, self.PrmTransformAzimuth, context)
     distance = self.parameterAsDouble(parameters, self.PrmTransformDistance, context)
     units = self.parameterAsInt(parameters, self.PrmTransformUnits, context)
     
     to_meters = conversionToMeters(units)
     distance = distance * to_meters
     src_crs = source.sourceCrs()
     wkbtype = source.wkbType()
     
         
     (sink, dest_id) = self.parameterAsSink(parameters,
         self.PrmOutputLayer, context, source.fields(), wkbtype, src_crs)
             
     geom_to_4326 = QgsCoordinateTransform(src_crs, epsg4326, QgsProject.instance())
     to_sink_crs = QgsCoordinateTransform(epsg4326, src_crs, QgsProject.instance())
     
     geomtype = QgsWkbTypes.geometryType(wkbtype)
         
     
     featureCount = source.featureCount()
     total = 100.0 / featureCount if featureCount else 0
     
     iterator = source.getFeatures()
     for cnt, feature in enumerate(iterator):
         if feedback.isCanceled():
             break
         geom = feature.geometry()
         # Find the centroid of the vector shape. We will resize everything based on this
         centroid = geom.centroid().asPoint()
         centroid = geom_to_4326.transform(centroid.x(), centroid.y())
         cy = centroid.y()
         cx = centroid.x()
         if distance != 0:
             g = geod.Direct(cy, cx, azimuth, distance, Geodesic.LATITUDE | Geodesic.LONGITUDE)
             new_centroid = QgsPoint(g['lon2'], g['lat2'])
         else:
             new_centroid = centroid
         
         # Find the x & y coordinates of the new centroid
         ncy = new_centroid.y()
         ncx = new_centroid.x()
         
         vertices = geom.vertices()
         for vcnt, vertex in enumerate(vertices):
             v = geom_to_4326.transform(vertex.x(), vertex.y())
             l = geod.Inverse(cy, cx, v.y(), v.x())
             vdist = l['s12']
             vazi = l['azi1']
             if scale != 1:
                 vdist = vdist * scale
             if angle != 0:
                 vazi += angle
             g = geod.Direct(ncy, ncx, vazi, vdist, Geodesic.LATITUDE | Geodesic.LONGITUDE)
             new_vertex = to_sink_crs.transform(g['lon2'], g['lat2'])
             geom.moveVertex(new_vertex.x(), new_vertex.y(), vcnt)
         feature.setGeometry(geom)
         sink.addFeature(feature)
                 
         if cnt % 100 == 0:
             feedback.setProgress(int(cnt * total))
             
     return {self.PrmOutputLayer: dest_id}
Example #12
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        fields = source.fields()
        fields.append(QgsField('vertex_pos', QVariant.Int))
        fields.append(QgsField('vertex_index', QVariant.Int))
        fields.append(QgsField('vertex_part', QVariant.Int))
        if QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PolygonGeometry:
            fields.append(QgsField('vertex_part_ring', QVariant.Int))
        fields.append(QgsField('vertex_part_index', QVariant.Int))
        fields.append(QgsField('distance', QVariant.Double))
        fields.append(QgsField('angle', QVariant.Double))

        wkb_type = QgsWkbTypes.Point
        if QgsWkbTypes.hasM(source.wkbType()):
            wkb_type = QgsWkbTypes.addM(wkb_type)
        if QgsWkbTypes.hasZ(source.wkbType()):
            wkb_type = QgsWkbTypes.addZ(wkb_type)

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

        vertex_indices_string = self.parameterAsString(parameters, self.VERTICES, context)
        indices = []
        for vertex in vertex_indices_string.split(','):
            try:
                indices.append(int(vertex))
            except:
                raise QgsProcessingException(
                    self.tr('\'{}\' is not a valid vertex index').format(vertex))

        features = source.getFeatures(QgsFeatureRequest(), QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
        total = 100.0 / source.featureCount() if source.featureCount() else 0

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

            input_geometry = f.geometry()
            if not input_geometry:
                sink.addFeature(f, QgsFeatureSink.FastInsert)
            else:
                total_vertices = input_geometry.constGet().nCoordinates()

                for vertex in indices:
                    if vertex < 0:
                        vertex_index = total_vertices + vertex
                    else:
                        vertex_index = vertex

                    if vertex_index < 0 or vertex_index >= total_vertices:
                        continue

                    (success, vertex_id) = input_geometry.vertexIdFromVertexNr(vertex_index)

                    distance = input_geometry.distanceToVertex(vertex_index)
                    angle = math.degrees(input_geometry.angleAtVertex(vertex_index))

                    output_feature = QgsFeature()
                    attrs = f.attributes()
                    attrs.append(vertex)
                    attrs.append(vertex_index)
                    attrs.append(vertex_id.part)
                    if QgsWkbTypes.geometryType(source.wkbType()) == QgsWkbTypes.PolygonGeometry:
                        attrs.append(vertex_id.ring)
                    attrs.append(vertex_id.vertex)
                    attrs.append(distance)
                    attrs.append(angle)
                    output_feature.setAttributes(attrs)

                    point = input_geometry.vertexAt(vertex_index)
                    output_feature.setGeometry(QgsGeometry(point))

                    sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Example #13
0
def make_features_compatible(new_features, input_layer):
    """Try to make the new features compatible with old features by:

    - converting single to multi part
    - dropping additional attributes
    - adding back M/Z values
    - drop Z/M
    - convert multi part to single part

    :param new_features: new features
    :type new_features: list of QgsFeatures
    :param input_layer: input layer
    :type input_layer: QgsVectorLayer
    :return: modified features
    :rtype: list of QgsFeatures
    """

    input_wkb_type = input_layer.wkbType()
    result_features = []
    for new_f in new_features:
        # Fix attributes
        if new_f.fields().count() > 0:
            attributes = []
            for field in input_layer.fields():
                if new_f.fields().indexFromName(field.name()) >= 0:
                    attributes.append(new_f[field.name()])
                else:
                    attributes.append(None)
            f = QgsFeature(input_layer.fields())
            f.setAttributes(attributes)
            f.setGeometry(new_f.geometry())
            new_f = f
        else:
            lendiff = len(new_f.attributes()) - len(input_layer.fields())
            if lendiff > 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                f.setAttributes(new_f.attributes()[:len(input_layer.fields())])
                new_f = f
            elif lendiff < 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                attributes = new_f.attributes() + [None for i in range(-lendiff)]
                f.setAttributes(attributes)
                new_f = f

        # Check if we need geometry manipulation
        new_f_geom_type = QgsWkbTypes.geometryType(new_f.geometry().wkbType())
        new_f_has_geom = new_f_geom_type not in (QgsWkbTypes.UnknownGeometry, QgsWkbTypes.NullGeometry)
        input_layer_has_geom = input_wkb_type not in (QgsWkbTypes.NoGeometry, QgsWkbTypes.Unknown)

        # Drop geometry if layer is geometry-less
        if not input_layer_has_geom and new_f_has_geom:
            f = QgsFeature(input_layer.fields())
            f.setAttributes(new_f.attributes())
            new_f = f
            result_features.append(new_f)
            continue  # skip the rest

        if input_layer_has_geom and new_f_has_geom and \
                new_f.geometry().wkbType() != input_wkb_type:  # Fix geometry
            # Single -> Multi
            if (QgsWkbTypes.isMultiType(input_wkb_type) and not
                    new_f.geometry().isMultipart()):
                new_geom = new_f.geometry()
                new_geom.convertToMultiType()
                new_f.setGeometry(new_geom)
            # Drop Z/M
            if (new_f.geometry().constGet().is3D() and not QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropZValue()
                new_f.setGeometry(new_geom)
            if (new_f.geometry().constGet().isMeasure() and not QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropMValue()
                new_f.setGeometry(new_geom)
            # Add Z/M back (set it to 0)
            if (not new_f.geometry().constGet().is3D() and QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addZValue(0.0)
                new_f.setGeometry(new_geom)
            if (not new_f.geometry().constGet().isMeasure() and QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addMValue(0.0)
                new_f.setGeometry(new_geom)
            # Multi -> Single
            if (not QgsWkbTypes.isMultiType(input_wkb_type) and
                    new_f.geometry().isMultipart()):
                g = new_f.geometry()
                g2 = g.constGet()
                for i in range(g2.partCount()):
                    # Clone or crash!
                    g4 = QgsGeometry(g2.geometryN(i).clone())
                    f = QgsVectorLayerUtils.createFeature(input_layer, g4, {i: new_f.attribute(i) for i in range(new_f.fields().count())})
                    result_features.append(f)
            else:
                result_features.append(new_f)
        else:
            result_features.append(new_f)
    return result_features
Example #14
0
    def processAlgorithm(self, progress):
        layerA = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_A))
        splitLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_B))

        sameLayer = self.getParameterValue(self.INPUT_A) == self.getParameterValue(self.INPUT_B)
        fieldList = layerA.fields()

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList,
                                                                     layerA.wkbType(), layerA.crs())

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])

        for aSplitFeature in vector.features(splitLayer, request):
            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = vector.features(layerA)

        if len(features) == 0:
            total = 100
        else:
            total = 100.0 / float(len(features))

        multiGeoms = 0 # how many multi geometries were encountered

        for current, inFeatA in enumerate(features):
            inGeom = inFeatA.geometry()

            if inGeom.isMultipart():
                multiGeoms += 1
                # MultiGeometries are not allowed because the result of a splitted part cannot be clearly defined:
                # 1) add both new parts as new features
                # 2) store one part as a new feature and the other one as part of the multi geometry
                # 2a) which part should be which, seems arbitrary
            else:
                attrsA = inFeatA.attributes()
                outFeat.setAttributes(attrsA)
                inGeoms = [inGeom]
                lines = spatialIndex.intersects(inGeom.boundingBox())

                if len(lines) > 0:  # has intersection of bounding boxes
                    splittingLines = []

                    engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                    engine.prepareGeometry()

                    for i in lines:
                        try:
                            splitGeom = splitGeoms[i]
                        except:
                            continue

                        # check if trying to self-intersect
                        if sameLayer:
                            if inFeatA.id() == i:
                                continue

                        if engine.intersects(splitGeom.geometry()):
                            splittingLines.append(splitGeom)

                    if len(splittingLines) > 0:
                        for splitGeom in splittingLines:
                            splitterPList = None
                            outGeoms = []

                            split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry())
                            split_geom_engine.prepareGeometry()

                            while len(inGeoms) > 0:
                                inGeom = inGeoms.pop()

                                if split_geom_engine.intersects(inGeom.geometry()):
                                    inPoints = vector.extractPoints(inGeom)
                                    if splitterPList == None:
                                        splitterPList = vector.extractPoints(splitGeom)

                                    try:
                                        result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
                                    except:
                                        ProcessingLog.addToLog(ProcessingLog.LOG_WARNING,
                                                               self.tr('Geometry exception while splitting'))
                                        result = 1

                                    # splitGeometry: If there are several intersections
                                    # between geometry and splitLine, only the first one is considered.
                                    if result == 0:  # split occurred
                                        if inPoints == vector.extractPoints(inGeom):
                                            # bug in splitGeometry: sometimes it returns 0 but
                                            # the geometry is unchanged
                                            QgsMessageLog.logMessage("appending")
                                            outGeoms.append(inGeom)
                                        else:
                                            inGeoms.append(inGeom)

                                            for aNewGeom in newGeometries:
                                                inGeoms.append(aNewGeom)
                                    else:
                                        QgsMessageLog.logMessage("appending else")
                                        outGeoms.append(inGeom)
                                else:
                                    outGeoms.append(inGeom)

                            inGeoms = outGeoms

                for aGeom in inGeoms:
                    passed = True

                    if QgsWkbTypes.geometryType( aGeom.wkbType() )  == QgsWkbTypes.LineGeometry \
                            and not QgsWkbTypes.isMultiType(aGeom.wkbType()):
                        passed = len(aGeom.asPolyline()) > 2

                        if not passed:
                            passed = (len(aGeom.asPolyline()) == 2 and
                                      aGeom.asPolyline()[0] != aGeom.asPolyline()[1])
                            # sometimes splitting results in lines of zero length

                    if passed:
                        outFeat.setGeometry(aGeom)
                        writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        if multiGeoms > 0:
            ProcessingLog.addToLog(ProcessingLog.LOG_INFO,
                                   self.tr('Feature geometry error: %s input features ignored due to multi-geometry.') % str(multiGeoms))

        del writer
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        line_source = self.parameterAsSource(parameters, self.LINES, context)

        sameLayer = parameters[self.INPUT] == parameters[self.LINES]

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               source.fields(), QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs())

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])
        request.setDestinationCrs(source.sourceCrs())

        for aSplitFeature in line_source.getFeatures(request):
            if feedback.isCanceled():
                break

            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = source.getFeatures()

        total = 100.0 / source.featureCount() if source.featureCount() else 100

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

            inGeom = inFeatA.geometry()
            attrsA = inFeatA.attributes()
            outFeat.setAttributes(attrsA)

            if inGeom.isMultipart():
                inGeoms = []

                for g in inGeom.asGeometryCollection():
                    inGeoms.append(g)
            else:
                inGeoms = [inGeom]

            lines = spatialIndex.intersects(inGeom.boundingBox())

            if len(lines) > 0:  # has intersection of bounding boxes
                splittingLines = []

                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

                for i in lines:
                    try:
                        splitGeom = splitGeoms[i]
                    except:
                        continue

                    # check if trying to self-intersect
                    if sameLayer:
                        if inFeatA.id() == i:
                            continue

                    if engine.intersects(splitGeom.geometry()):
                        splittingLines.append(splitGeom)

                if len(splittingLines) > 0:
                    for splitGeom in splittingLines:
                        splitterPList = None
                        outGeoms = []

                        split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry())
                        split_geom_engine.prepareGeometry()

                        while len(inGeoms) > 0:
                            if feedback.isCanceled():
                                break

                            inGeom = inGeoms.pop()

                            if inGeom.isNull():  # this has been encountered and created a run-time error
                                continue

                            if split_geom_engine.intersects(inGeom.geometry()):
                                inPoints = vector.extractPoints(inGeom)
                                if splitterPList is None:
                                    splitterPList = vector.extractPoints(splitGeom)

                                try:
                                    result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
                                except:
                                    feedback.reportError(self.tr('Geometry exception while splitting'))
                                    result = 1

                                # splitGeometry: If there are several intersections
                                # between geometry and splitLine, only the first one is considered.
                                if result == 0:  # split occurred
                                    if inPoints == vector.extractPoints(inGeom):
                                        # bug in splitGeometry: sometimes it returns 0 but
                                        # the geometry is unchanged
                                        outGeoms.append(inGeom)
                                    else:
                                        inGeoms.append(inGeom)

                                        for aNewGeom in newGeometries:
                                            inGeoms.append(aNewGeom)
                                else:
                                    outGeoms.append(inGeom)
                            else:
                                outGeoms.append(inGeom)

                        inGeoms = outGeoms

            parts = []

            for aGeom in inGeoms:
                if feedback.isCanceled():
                    break

                passed = True

                if QgsWkbTypes.geometryType(aGeom.wkbType()) == QgsWkbTypes.LineGeometry:
                    numPoints = aGeom.geometry().numPoints()

                    if numPoints <= 2:
                        if numPoints == 2:
                            passed = not aGeom.geometry().isClosed()  # tests if vertex 0 = vertex 1
                        else:
                            passed = False
                            # sometimes splitting results in lines of zero length

                if passed:
                    parts.append(aGeom)

            if len(parts) > 0:
                outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
                sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))
        return {self.OUTPUT: dest_id}
Example #16
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            areaName = vector.createUniqueFieldName('area', fields)
            fields.append(QgsField(areaName, QVariant.Double))
            perimeterName = vector.createUniqueFieldName('perimeter', fields)
            fields.append(QgsField(perimeterName, QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            lengthName = vector.createUniqueFieldName('length', fields)
            fields.append(QgsField(lengthName, QVariant.Double))
        else:
            xName = vector.createUniqueFieldName('xcoord', fields)
            fields.append(QgsField(xName, QVariant.Double))
            yName = vector.createUniqueFieldName('ycoord', fields)
            fields.append(QgsField(yName, QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                zName = vector.createUniqueFieldName('zcoord', fields)
                fields.append(QgsField(zName, QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                zName = vector.createUniqueFieldName('mvalue', fields)
                fields.append(QgsField(zName, QVariant.Double))

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, wkb_type, source.sourceCrs())

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
        measureTotal = self.parameterAsBool(parameters, self.PrmMeasureTotalLength, context)
        units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context)
        autoStyle = self.parameterAsBool(parameters, self.PrmAutomaticStyline, context)

        srcCRS = source.sourceCrs()

        f = QgsFields()
        f.append(QgsField("label", QVariant.String))
        f.append(QgsField("distance", QVariant.Double))
        f.append(QgsField("units", QVariant.String))
        if not measureTotal:
            f.append(QgsField("heading_to", QVariant.Double))
            f.append(QgsField("total_distance", QVariant.Double))

        (sink, dest_id) = self.parameterAsSink(
            parameters, self.PrmOutputLayer, context, f, QgsWkbTypes.LineString, srcCRS)

        if srcCRS != epsg4326:
            geomTo4326 = QgsCoordinateTransform(srcCRS, epsg4326, QgsProject.instance())
            toSinkCrs = QgsCoordinateTransform(epsg4326, srcCRS, QgsProject.instance())

        wkbtype = source.wkbType()
        geomtype = QgsWkbTypes.geometryType(wkbtype)

        featureCount = source.featureCount()
        total = 100.0 / featureCount if featureCount else 0

        iterator = source.getFeatures()
        for cnt, feature in enumerate(iterator):
            if feedback.isCanceled():
                break

            if geomtype == QgsWkbTypes.LineGeometry:
                if feature.geometry().isMultipart():
                    ptdata = [feature.geometry().asMultiPolyline()]
                else:
                    ptdata = [[feature.geometry().asPolyline()]]
            else: #polygon
                if feature.geometry().isMultipart():
                    ptdata = feature.geometry().asMultiPolygon()
                else:
                    ptdata = [feature.geometry().asPolygon()]
            if len(ptdata) < 1:
                continue

            for seg in ptdata:
                if len(seg) < 1:
                    continue
                if measureTotal:
                    for pts in seg:
                        numpoints = len(pts)
                        if numpoints < 2:
                            continue
                        f = QgsFeature()
                        f.setGeometry(QgsGeometry.fromPolylineXY(pts))
                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            ptStart = geomTo4326.transform(ptStart)
                        # Calculate the total distance of this line segment
                        distance = 0.0
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            if srcCRS != epsg4326: # Convert to 4326
                                ptEnd = geomTo4326.transform(ptEnd)
                            l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            distance += l['s12']
                            ptStart = ptEnd

                        distance = self.unitDistance(units, distance) # Distance converted to the selected unit of measure
                        attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units] ]
                        f.setAttributes(attr)
                        sink.addFeature(f)
                else:
                    for pts in seg:
                        numpoints = len(pts)
                        if numpoints < 2:
                            continue
                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            ptStart = geomTo4326.transform(ptStart)
                        # Calculate the total distance of this line segment
                        totalDistance = 0.0
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            if srcCRS != epsg4326: # Convert to 4326
                                ptEnd = geomTo4326.transform(ptEnd)
                            l = geod.Inverse(ptStart.y(), ptStart.x(), ptEnd.y(), ptEnd.x())
                            totalDistance += l['s12']
                            ptStart = ptEnd

                        totalDistance = self.unitDistance(units, totalDistance) # Distance converted to the selected unit of measure

                        ptStart = QgsPointXY(pts[0].x(), pts[0].y())
                        if srcCRS != epsg4326: # Convert to 4326
                            pt1 = geomTo4326.transform(ptStart)
                        else:
                            pt1 = ptStart
                        for x in range(1,numpoints):
                            ptEnd = QgsPointXY(pts[x].x(), pts[x].y())
                            f = QgsFeature()
                            f.setGeometry(QgsGeometry.fromPolylineXY([ptStart, ptEnd]))
                            if srcCRS != epsg4326: # Convert to 4326
                                pt2 = geomTo4326.transform(ptEnd)
                            else:
                                pt2 = ptEnd
                            l = geod.Inverse(pt1.y(), pt1.x(), pt2.y(), pt2.x())
                            ptStart = ptEnd
                            pt1 = pt2
                            distance = self.unitDistance(units, l['s12'])
                            attr = ["{:.2f} {}".format(distance, unitsAbbr[units]), distance, unitsAbbr[units], l['azi1'],totalDistance ]
                            f.setAttributes(attr)
                            sink.addFeature(f)

            if cnt % 100 == 0:
                feedback.setProgress(int(cnt * total))
        if autoStyle and context.willLoadLayerOnCompletion(dest_id):
            context.layerToLoadOnCompletionDetails(dest_id).setPostProcessor(StylePostProcessor.create())

        return {self.PrmOutputLayer: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.PrmInputLayer, context)
        preserve_final_pt = self.parameterAsBool(parameters, self.PrmPreserveFinalPoint, context)
        min_distance = self.parameterAsDouble(parameters, self.PrmMinDistance, context)
        units = self.parameterAsInt(parameters, self.PrmUnitsOfMeasure, context)

        # Get the minimum distance in meters
        min_distance = min_distance * conversionToMeters(units)

        wkbtype = source.wkbType()

        num_bad = 0
        if QgsWkbTypes.geometryType(wkbtype) != QgsWkbTypes.LineGeometry:
            feedback.reportError(tr("Please select a valid line layer."))
            return({})
            
        layercrs = source.sourceCrs()
        (sink, dest_id) = self.parameterAsSink(
            parameters, self.PrmOutputLayer, context, source.fields(), wkbtype, layercrs)

        if layercrs != epsg4326:
            transto4326 = QgsCoordinateTransform(layercrs, epsg4326, QgsProject.instance())
            transfrom4326 = QgsCoordinateTransform(epsg4326, layercrs, QgsProject.instance())

        total = 100.0 / source.featureCount() if source.featureCount() else 0
        iterator = source.getFeatures()
        num_bad = 0
        for cnt, feature in enumerate(iterator):
            if feedback.isCanceled():
                break
            geom = feature.geometry()
            # Force the geometry to be in degrees so that we can use the geodesic algorithms
            if layercrs != epsg4326:
                results = geom.transform(transto4326)
                
            num_parts = geom.constGet().partCount()
            # feedback.pushInfo('num_parts {}'.format(num_parts))
            try:
                fline = QgsFeature()
                fgeom = QgsGeometry()
                is_valid = False
                for part in geom.constGet().parts():
                    vertex_cnt = part.vertexCount()
                    # feedback.pushInfo('vertex_cnt {}'.format(vertex_cnt))
                    if vertex_cnt <= 1: # line of 1 or less points is invalid
                        continue
                    pts = []
                    for vcnt, vertex in enumerate(part.vertices()):
                        if vcnt == 0:
                            # This is the first point so we will save it
                            pts.append(vertex)
                            ptLast = vertex
                        else:
                            ptNext = vertex
                            gline = geod.InverseLine(ptLast.y(), ptLast.x(), ptNext.y(), ptNext.x())
                            if gline.s13 >= min_distance:
                                pts.append(ptNext)
                                ptLast = ptNext
                            elif (vcnt == vertex_cnt - 1) and preserve_final_pt:
                                if len(pts) >= 2:
                                    # We have two or more points already on our pts list. Check to see if
                                    # the next to last entry distance and the end point are greater than the 
                                    # the minimum distance. If so replace the last entry with this one else
                                    # because the user has selected to preserve the last end point we will
                                    # accept the end point.
                                    gline = geod.InverseLine(pts[-2].y(), pts[-2].x(), ptNext.y(), ptNext.x())
                                    if gline.s13 >= min_distance:
                                        pts[-1] = ptNext
                                    else:
                                        pts.append(ptNext)
                                elif len(pts) == 1:
                                    # We have only the beginning point of the line and because the user has
                                    # selected that the last point to be preserved we will allow a distance less
                                    # than the minimum so that the line can be preserved.
                                    pts.append(ptNext)
                    if len(pts) > 1: # There must be more than one point to make a valid line
                        fgeom.addPoints(pts, QgsWkbTypes.LineGeometry)
                        is_valid = True
                if is_valid: # Only save the feature if it is valid
                    fline.setAttributes(feature.attributes())
                    if layercrs != epsg4326:
                        fgeom.transform(transfrom4326)
                    fline.setGeometry(fgeom)
                    sink.addFeature(fline)
                else:
                    num_bad += 1
            except Exception:
                '''s = traceback.format_exc()
                feedback.pushInfo(s)'''
                num_bad += 1
            
            feedback.setProgress(int(cnt * total))

        if num_bad > 0:
            feedback.pushInfo(tr("{} out of {} features from input layer were invalid and were skipped.".format(num_bad, source.featureCount())))

        return {self.PrmOutputLayer: dest_id}
Example #19
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        # Unit:
        # 0 - degree
        # 1 - radian
        # 2 - grade
        unit = self.parameterAsEnum(parameters, self.UNIT, context)
        # Interval:
        # 0 - [0 ; Pi[
        # 1 - [0 ; Pi/2[
        interval = self.parameterAsEnum(parameters, self.INTERVAL, context)
        # Calculate with projection:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal (TODO: NOT USED AT THE MOMENT)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        rounded = self.parameterAsBoolean(parameters, self.ROUNDED, context)

        classification = self.parameterAsBoolean(parameters,
                                                 self.CLASSIFICATION, context)
        classification_step = self.parameterAsDouble(parameters,
                                                     self.CLASSIFICATION_STEP,
                                                     context)

        histogram = self.parameterAsBoolean(parameters, self.HISTOGRAM,
                                            context)
        histogram_step = self.parameterAsDouble(parameters,
                                                self.HISTOGRAM_STEP, context)

        wkb_type = source.wkbType()

        # TODO ? improvement: test if the layer contains at least one segment
        if QgsWkbTypes.geometryType(wkb_type) != QgsWkbTypes.LineGeometry:
            # TODO IMPROVE FEEDBACK
            feedback.reportError(
                'The layer geometry type is different from a line')
            return {}

        fields = source.fields()

        new_fields = QgsFields()
        new_fields.append(QgsField('orientation', QVariant.Double))
        if classification:
            new_fields.append(QgsField('classification', QVariant.Double))

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

        coord_transform = None

        self.distance_area = QgsDistanceArea()
        # if method == 2:
        #     self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
        #     self.distance_area.setEllipsoid(context.ellipsoid())
        if method == 1:
            if not context.project():
                raise QgsProcessingException(
                    self.tr('No project is available in this context'))
            coord_transform = QgsCoordinateTransform(source.sourceCrs(),
                                                     context.project().crs(),
                                                     context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            out_feature = f
            attrs = f.attributes()
            geom = f.geometry()
            if geom:
                if coord_transform is not None:
                    geom.transform(coord_transform)

                orientation = angle(geom, unit, interval, rounded)
                if orientation is not None:
                    if classification:
                        class_int = int(orientation / classification_step)
                        attrs.extend([orientation, class_int])
                    else:
                        attrs.extend([orientation])

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            out_feature.setAttributes(attrs)
            sink.addFeature(out_feature, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Example #20
0
 def _group_geom_name(self, geom_str):
     geom = QgsWkbTypes.geometryDisplayString(
         QgsWkbTypes.geometryType(
         QgsWkbTypes.parseType(
         geom_str))) if geom_str else self.NO_GEOM
     return geom 
Example #21
0
 def drop_z_from_geom(cls, geom: QgsGeometry, geom_type: QgsWkbTypes):
     target_type = cls._get_non_z_geom_type(geom_type)
     type_to_convert = QgsWkbTypes.geometryType(target_type)
     return geom.convertToType(type_to_convert), target_type
Example #22
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

        fields = QgsProcessingUtils.combineFields(fields, new_fields)
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields, wkb_type,
                                               source.sourceCrs())

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(),
                                            context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(),
                                                    context.project().crs(),
                                                    context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
    def requestFlightPathLayer(self):
        # https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/vector.html#memory-provider
        filmsBySourceType = self.getSelection()
        epsg = 4326
        vectorCrs = QgsCoordinateReferenceSystem(
            epsg, QgsCoordinateReferenceSystem.EpsgCrsId)
        flightPathPointLayer = QgsVectorLayer(
            "MultiPoint?crs=epsg:{0}".format(epsg), "FlugwegePunkt", "memory")
        flightPathLineLayer = QgsVectorLayer(
            "MultiLineString?crs=epsg:{0}".format(epsg), "FlugwegeLinie",
            "memory")
        flightPathPointLayer.setCrs(vectorCrs)
        flightPathPointLayer.setCrs(vectorCrs)

        flightPathPointProvider = flightPathPointLayer.dataProvider()
        flightPathLineProvider = flightPathLineLayer.dataProvider()

        # add fields
        fields = [
            QgsField("filmnummer", QVariant.String),
            QgsField("flugweg_quelle", QVariant.String),
            QgsField("flugdatum", QVariant.String),
            QgsField("fotograf", QVariant.String),
            QgsField("pilot", QVariant.String),
            QgsField("flugzeug", QVariant.String),
            QgsField("abflug_zeit", QVariant.String),
            QgsField("ankunft_zeit", QVariant.String),
            QgsField("flugzeit", QVariant.String),
            QgsField("abflug_flughafen", QVariant.String),
            QgsField("ankunft_flughafen", QVariant.String),
            QgsField("wetter", QVariant.String),
            QgsField("target", QVariant.String),
            QgsField("orientation", QVariant.String)
        ]

        flightPathPointProvider.addAttributes(fields)
        flightPathLineProvider.addAttributes(fields)

        flightPathPointLayer.updateFields(
        )  # tell the vector layer to fetch changes from the provider
        flightPathLineLayer.updateFields()

        pointFeatureList = []
        lineFeatureList = []

        # Point: Flight GPS
        for filmNumber in filmsBySourceType[0]:
            # load .shp file, get geometries as multigeometry
            pointFeatureList.append(
                self.getFeatureWithMultiGeomFromOgrShp(filmNumber, ".shp",
                                                       ogr.wkbMultiPoint,
                                                       "flight_gps",
                                                       vectorCrs))

        # Point: Camera GPS
        for filmNumber in filmsBySourceType[2]:
            # load _gps.shp file, get geometries as multigeometry
            pointFeatureList.append(
                self.getFeatureWithMultiGeomFromOgrShp(filmNumber, "_gps.shp",
                                                       ogr.wkbMultiPoint,
                                                       "camera_gps",
                                                       vectorCrs))

        # Point: Image Mapping
        for filmNumber in filmsBySourceType[4]:
            # load image_cp based on orientation from APIS db
            pointFeatureList.append(
                self.getFeatureWithMultiGeomFromSpatialite(
                    filmNumber,
                    QgsWkbTypes.geometryType(QgsWkbTypes.MultiPoint),
                    "image_mapping"))

        # Line: Flight GPS
        for filmNumber in filmsBySourceType[1]:
            # load _lin.shp file, get geometries as multigeometry
            lineFeatureList.append(
                self.getFeatureWithMultiGeomFromOgrShp(filmNumber, "_lin.shp",
                                                       ogr.wkbMultiLineString,
                                                       "flight_gps",
                                                       vectorCrs))

        # Line: Camera GPS
        for filmNumber in filmsBySourceType[3]:
            # load _gps.shp file create line with Points2Path, get geometry as multigeometry
            lineFeatureList.append(
                self.multiPointToLineString(
                    self.getFeatureWithMultiGeomFromOgrShp(filmNumber,
                                                           "_gps.shp",
                                                           ogr.wkbMultiPoint,
                                                           "camera_gps",
                                                           vectorCrs,
                                                           sortBy='bildnr')))

        # Line: Image Mapping
        for filmNumber in filmsBySourceType[5]:
            # load image_cp based on orientation from APIS db create line with Points2Path, get geometry as multigeometry
            lineFeatureList.append(
                self.multiPointToLineString(
                    self.getFeatureWithMultiGeomFromSpatialite(
                        filmNumber,
                        QgsWkbTypes.geometryType(QgsWkbTypes.MultiPoint),
                        "image_mapping")))

        flightPathPointProvider.addFeatures(
            [pF for pF in pointFeatureList if pF is not None])
        flightPathLineProvider.addFeatures(
            [lF for lF in lineFeatureList if lF is not None])

        # update layer's extent when new features have been added
        # because change of extent in provider is not propagated to the layer
        flightPathPointLayer.updateExtents()
        flightPathLineLayer.updateExtents()

        return flightPathPointLayer, flightPathLineLayer
Example #24
0
    def processAlgorithm(self, parameters, context, feedback):
        sourceA = self.parameterAsSource(parameters, self.INPUT, context)
        sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

        geomType = QgsWkbTypes.multiType(sourceA.wkbType())

        fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS,
                                         context)
        fieldsB = self.parameterAsFields(parameters, self.OVERLAY_FIELDS,
                                         context)

        fieldListA = QgsFields()
        field_indices_a = []
        if len(fieldsA) > 0:
            for f in fieldsA:
                idxA = sourceA.fields().lookupField(f)
                if idxA >= 0:
                    field_indices_a.append(idxA)
                    fieldListA.append(sourceA.fields()[idxA])
        else:
            fieldListA = sourceA.fields()
            field_indices_a = [i for i in range(0, fieldListA.count())]

        fieldListB = QgsFields()
        field_indices_b = []
        if len(fieldsB) > 0:
            for f in fieldsB:
                idxB = sourceB.fields().lookupField(f)
                if idxB >= 0:
                    field_indices_b.append(idxB)
                    fieldListB.append(sourceB.fields()[idxB])
        else:
            fieldListB = sourceB.fields()
            field_indices_b = [i for i in range(0, fieldListB.count())]

        output_fields = QgsProcessingUtils.combineFields(
            fieldListA, fieldListB)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, output_fields,
                                               geomType, sourceA.sourceCrs())

        outFeat = QgsFeature()
        indexB = QgsSpatialIndex(
            sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(
                []).setDestinationCrs(sourceA.sourceCrs(),
                                      context.transformContext())), feedback)

        total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1
        count = 0

        for featA in sourceA.getFeatures(
                QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)):
            if feedback.isCanceled():
                break

            if not featA.hasGeometry():
                continue

            geom = featA.geometry()
            atMapA = featA.attributes()
            intersects = indexB.intersects(geom.boundingBox())

            request = QgsFeatureRequest().setFilterFids(intersects)
            request.setDestinationCrs(sourceA.sourceCrs(),
                                      context.transformContext())
            request.setSubsetOfAttributes(field_indices_b)

            engine = None
            if len(intersects) > 0:
                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(geom.constGet())
                engine.prepareGeometry()

            for featB in sourceB.getFeatures(request):
                if feedback.isCanceled():
                    break

                tmpGeom = featB.geometry()
                if engine.intersects(tmpGeom.constGet()):
                    out_attributes = [
                        featA.attributes()[i] for i in field_indices_a
                    ]
                    out_attributes.extend(
                        [featB.attributes()[i] for i in field_indices_b])
                    int_geom = QgsGeometry(geom.intersection(tmpGeom))
                    if int_geom.wkbType(
                    ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(
                            int_geom.wkbType(
                            )) == QgsWkbTypes.GeometryCollection:
                        int_com = geom.combine(tmpGeom)
                        int_geom = QgsGeometry()
                        if int_com:
                            int_sym = geom.symDifference(tmpGeom)
                            int_geom = QgsGeometry(int_com.difference(int_sym))
                    if int_geom.isEmpty() or not int_geom.isGeosValid():
                        raise QgsProcessingException(
                            self.tr('GEOS geoprocessing error: One or '
                                    'more input features have invalid '
                                    'geometry.'))
                    try:
                        if QgsWkbTypes.geometryType(int_geom.wkbType(
                        )) == QgsWkbTypes.geometryType(geomType):
                            int_geom.convertToMultiType()
                            outFeat.setGeometry(int_geom)
                            outFeat.setAttributes(out_attributes)
                            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                    except:
                        raise QgsProcessingException(
                            self.tr('Feature geometry error: One or more '
                                    'output features ignored due to invalid '
                                    'geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        return {self.OUTPUT: dest_id}
Example #25
0
    def processAlgorithm(self, parameters, context, feedback):
        ''' Here is where the processing itself takes place. '''
        #
        if not is_dependencies_satisfied:
            return {}

# Init
# The number of features in the input layer could be trimmed to user selection.
        the_layer = self.parameterAsSource(parameters, self.THE_LAYER, context)
        gok = QgsWkbTypes.geometryType(
            the_layer.wkbType()) == QgsWkbTypes.PointGeometry
        if the_layer is None or not gok:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.THE_LAYER))
        #
        bCHscal = self.parameterAsBool(parameters, self.BSCALE, context)
        if bCHscal:
            # Use another channel for scaling. All data from that channel will be used.
            scally = self.parameterAsSource(parameters, self.SCALLY, context)
            if scally is None or the_layer.wkbType() != QgsWkbTypes.Point:
                raise QgsProcessingException(
                    self.invalidSourceError(parameters, self.SCALLY))
        fidu_fld = self.parameterAsString(parameters, self.FID_FLD, context)
        data_fld = self.parameterAsString(parameters, self.DATA_FLD, context)
        line_fld = self.parameterAsString(parameters, self.LINE_FLD, context)
        invP = self.parameterAsBool(parameters, self.INVERTP, context)
        dumval = self.parameterAsDouble(parameters, self.DUMVAL, context)
        scale = self.parameterAsDouble(parameters, self.SCALE, context)
        offset = self.parameterAsDouble(parameters, self.OFFSET, context)
        join_to_line = self.parameterAsBool(parameters, self.JOINL, context)

        data = the_layer.fields().at(the_layer.fields().lookupField(data_fld))
        fidu = the_layer.fields().at(the_layer.fields().lookupField(fidu_fld))
        if not data.isNumeric() or not fidu.isNumeric():
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.THE_LAYER))

        line = the_layer.fields().at(the_layer.fields().lookupField(line_fld))
        data_ix = the_layer.fields().lookupField(data_fld)
        line_ix = the_layer.fields().lookupField(line_fld)
        fidu_ix = the_layer.fields().lookupField(fidu_fld)

        # Set output vector layer: point(X, Y, M) M is data value at that point
        output_wkb = QgsWkbTypes.LineString
        output_wkb = QgsWkbTypes.addM(output_wkb)

        # Fields of stacked profiles vector
        line_def = the_layer.fields().at(line_ix)
        fields = QgsFields()
        if line_def is not None:
            fields = QgsFields()
            fields.append(QgsField('Line', QVariant.String, '', 16))
            fields.append(QgsField('Type', QVariant.String, '', 2))
            fields.append(QgsField('NbPts', QVariant.Int, '', 10, 0))
            fields.append(QgsField('Azimuth', QVariant.Double, '', 10, 6))
            fields.append(QgsField('DistEP', QVariant.Double, '', 10, 2))
            fields.append(QgsField('Length', QVariant.Double, '', 10, 2))
        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields, output_wkb,
                                               the_layer.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        # Get the features and fields of interest
        features = the_layer.getFeatures(
            QgsFeatureRequest().setSubsetOfAttributes(
                [fidu_ix, line_ix, data_ix]),
            QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)

        # CSV
        # Find min/max of data values for all lines and save each line in a csv file
        # Then process each line separately: can have any number of lines...
        lines = []
        xyzf = []
        lineN = ''
        nL = 0
        TL = 0.
        total = 60.0 / the_layer.featureCount() if the_layer.featureCount(
        ) else 0
        stat = QgsStatisticalSummary()
        for current, ft in enumerate(features):
            if feedback.isCanceled():
                break
            feedback.setProgress(int(current * total))
            if not ft.hasGeometry():
                continue
            #
            if ft[line.name()] != lineN:
                if xyzf != []:
                    lines.append([lineN, nL])
                    the_csv = os.path.join(self.tmpDir, '%s.csv' % str(lineN))
                    with codecs.open(the_csv, 'w', 'utf-8') as fo:
                        fo.write('X,Y,FID,Data\n')
                        for ar in xyzf:
                            fo.write(','.join(map(str, ar)))
                            fo.write('\n')
                    le = sqrt((xyzf[0][0] - xyzf[-1][0])**2 +
                              (xyzf[0][1] - xyzf[-1][1])**2)
                    if le > TL:
                        TL = le
                    xyzf = []
                    nL = 0
                lineN = ft[line.name()]
            #
            rdata = float(ft[data.name()])
            fiduu = int(ft[fidu.name()])
            if abs(rdata - dumval) < 1e-6:
                # Dummy value: skip
                continue
            #
            stat.addVariant(ft[data.name()])
            # how to handle QgsMultiPoint ???
            if (the_layer.wkbType() == QgsWkbTypes.MultiPoint
                    or the_layer.wkbType() == QgsWkbTypes.MultiPointM
                    or the_layer.wkbType() == QgsWkbTypes.MultiPointZ
                    or the_layer.wkbType() == QgsWkbTypes.MultiPointZM
                    or the_layer.wkbType() == QgsWkbTypes.MultiPoint25D):
                # Suppose they all have the same attributes:
                #  in this case it seems useless to get more than the first point, but...
                points = ft.geometry().constGet().clone()
            else:
                points = [ft.geometry().constGet().clone()]
            try:
                for point in points:
                    xyzf.append([point.x(), point.y(), fiduu, rdata])
                    nL += 1
            except:
                pass
        # last line
        if xyzf != []:
            lines.append([lineN, nL])
            the_csv = os.path.join(self.tmpDir, '%s.csv' % str(lineN))
            with codecs.open(the_csv, 'w', 'utf-8') as fo:
                fo.write('X,Y,FID,Data\n')
                for ar in xyzf:
                    fo.write(','.join(map(str, ar)))
                    fo.write('\n')
            le = sqrt((xyzf[0][0] - xyzf[-1][0])**2 +
                      (xyzf[0][1] - xyzf[-1][1])**2)
            if le > TL:
                TL = le
        #
        stat.finalize()
        self.dmean = stat.mean()
        self.mult = TL / (stat.max() - stat.min())
        #
        if bCHscal:
            # Scaling field: retrieve its stats
            scch_fld = self.parameterAsString(parameters, self.SCALCH, context)
            scch = scally.fields().at(scally.fields().lookupField(scch_fld))
            scch_ix = scally.fields().lookupField(scch_fld)
            scch_f = scally.getFeatures(
                QgsFeatureRequest().setSubsetOfAttributes([scch_ix]),
                QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
            stat = QgsStatisticalSummary()
            for current, ft in enumerate(scch_f):
                stat.addVariant(ft[scch.name()])
            stat.finalize()
            self.dmean = stat.mean()
            self.mult = TL / (stat.max() - stat.min())
        #
        if invP:
            iv = -1
        else:
            iv = 1

# Profile
        total = 40.0 / (len(lines) + 1)
        # For each line:
        for current, z in enumerate(lines):
            line = z[0]
            if feedback.isCanceled():
                break
            if not ft.hasGeometry():
                continue
            feedback.setProgress(int(current * total) + 60.)

            # Read line back from csv
            the_csv = os.path.join(self.tmpDir, '%s.csv' % str(line))
            if not os.path.exists(the_csv):
                raise ValueError(
                    'It seems parameters are swaped: LINE <-> DATA!')

            ar = pd.read_csv(the_csv)
            ar = ar.sort_values('FID')

            # Create the profile
            px, py = self._do_profile(ar, iv, scale, offset)

            #Construct vector layer
            f = QgsFeature()
            typeL = str(self.type)
            azimut = float(self.azimut)
            Len = float(self.length)
            CLen = float(self.clength)
            f.setAttributes(
                [str(line), typeL,
                 int(len(px)), azimut, Len, CLen])
            line_pts = [
                QgsPoint(x, y, m=m) for x, y, m in zip(px, py, ar.Data)
            ]
            if join_to_line:
                # Join profile to its line
                e = len(ar) - 1
                ar0 = [QgsPoint(ar.X[0], ar.Y[0], m=0.)]
                ar1 = [QgsPoint(ar.X[e], ar.Y[e], m=0.)]
                line_pts = ar0 + line_pts + ar1
            #
            f.setGeometry(QgsGeometry(QgsLineString(line_pts)))
            sink.addFeature(f, QgsFeatureSink.FastInsert)
            # Delete temp csv file
            try:
                os.remove(the_csv)
            except:
                pass

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        fields = source.fields()
        fields.append(QgsField('vertex_pos', QVariant.Int))
        fields.append(QgsField('vertex_index', QVariant.Int))
        fields.append(QgsField('vertex_part', QVariant.Int))
        if QgsWkbTypes.geometryType(
                source.wkbType()) == QgsWkbTypes.PolygonGeometry:
            fields.append(QgsField('vertex_part_ring', QVariant.Int))
        fields.append(QgsField('vertex_part_index', QVariant.Int))
        fields.append(QgsField('distance', QVariant.Double))
        fields.append(QgsField('angle', QVariant.Double))

        wkb_type = QgsWkbTypes.Point
        if QgsWkbTypes.hasM(source.wkbType()):
            wkb_type = QgsWkbTypes.addM(wkb_type)
        if QgsWkbTypes.hasZ(source.wkbType()):
            wkb_type = QgsWkbTypes.addZ(wkb_type)

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

        vertex_indices_string = self.parameterAsString(parameters,
                                                       self.VERTICES, context)
        indices = []
        for vertex in vertex_indices_string.split(','):
            try:
                indices.append(int(vertex))
            except:
                raise QgsProcessingException(
                    self.tr('\'{}\' is not a valid vertex index').format(
                        vertex))

        features = source.getFeatures(
            QgsFeatureRequest(),
            QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks)
        total = 100.0 / source.featureCount() if source.featureCount() else 0

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

            input_geometry = f.geometry()
            if not input_geometry:
                sink.addFeature(f, QgsFeatureSink.FastInsert)
            else:
                total_vertices = input_geometry.constGet().nCoordinates()

                for vertex in indices:
                    if vertex < 0:
                        vertex_index = total_vertices + vertex
                    else:
                        vertex_index = vertex

                    if vertex_index < 0 or vertex_index >= total_vertices:
                        continue

                    (success, vertex_id
                     ) = input_geometry.vertexIdFromVertexNr(vertex_index)

                    distance = input_geometry.distanceToVertex(vertex_index)
                    angle = math.degrees(
                        input_geometry.angleAtVertex(vertex_index))

                    output_feature = QgsFeature()
                    attrs = f.attributes()
                    attrs.append(vertex)
                    attrs.append(vertex_index)
                    attrs.append(vertex_id.part)
                    if QgsWkbTypes.geometryType(
                            source.wkbType()) == QgsWkbTypes.PolygonGeometry:
                        attrs.append(vertex_id.ring)
                    attrs.append(vertex_id.vertex)
                    attrs.append(distance)
                    attrs.append(angle)
                    output_feature.setAttributes(attrs)

                    point = input_geometry.vertexAt(vertex_index)
                    output_feature.setGeometry(QgsGeometry(point))

                    sink.addFeature(output_feature, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Example #27
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
            if not QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('straightdis', QVariant.Double))
                new_fields.append(QgsField('sinuosity', QVariant.Double))
        else:
            new_fields.append(QgsField('xcoord', QVariant.Double))
            new_fields.append(QgsField('ycoord', QVariant.Double))
            if QgsWkbTypes.hasZ(source.wkbType()):
                self.export_z = True
                new_fields.append(QgsField('zcoord', QVariant.Double))
            if QgsWkbTypes.hasM(source.wkbType()):
                self.export_m = True
                new_fields.append(QgsField('mvalue', QVariant.Double))

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

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()

        if QgsWkbTypes.geometryType(wkb_type) != QgsWkbTypes.PolygonGeometry:
            # TODO IMPROVE FEEDBACK
            feedback.reportError('The layer geometry type is different from a polygon')
            return {}

        fields = source.fields()

        new_fields = QgsFields()
        new_fields.append(QgsField('perimeter', QVariant.Double))
        new_fields.append(QgsField('area', QVariant.Double))

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

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(), context.transformContext())
            self.distance_area.setEllipsoid(context.ellipsoid())
        elif method == 1:
            if not context.project():
                raise QgsProcessingException(self.tr('No project is available in this context'))
            coordTransform = QgsCoordinateTransform(source.sourceCrs(), context.project().crs(), context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                attrs.extend(self.polygon_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Example #29
0
def make_features_compatible(new_features, input_layer):
    """Try to make the new features compatible with old features by:

    - converting single to multi part
    - dropping additional attributes
    - adding back M/Z values
    - drop Z/M
    - convert multi part to single part

    :param new_features: new features
    :type new_features: list of QgsFeatures
    :param input_layer: input layer
    :type input_layer: QgsVectorLayer
    :return: modified features
    :rtype: list of QgsFeatures
    """

    input_wkb_type = input_layer.wkbType()
    result_features = []
    for new_f in new_features:
        # Fix attributes
        if new_f.fields().count() > 0:
            attributes = []
            for field in input_layer.fields():
                if new_f.fields().indexFromName(field.name()) >= 0:
                    attributes.append(new_f[field.name()])
                else:
                    attributes.append(None)
            f = QgsFeature(input_layer.fields())
            f.setAttributes(attributes)
            f.setGeometry(new_f.geometry())
            new_f = f
        else:
            lendiff = len(new_f.attributes()) - len(input_layer.fields())
            if lendiff > 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                f.setAttributes(new_f.attributes()[:len(input_layer.fields())])
                new_f = f
            elif lendiff < 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                attributes = new_f.attributes() + [
                    None for i in range(-lendiff)
                ]
                f.setAttributes(attributes)
                new_f = f

        # Check if we need geometry manipulation
        new_f_geom_type = QgsWkbTypes.geometryType(new_f.geometry().wkbType())
        new_f_has_geom = new_f_geom_type not in (QgsWkbTypes.UnknownGeometry,
                                                 QgsWkbTypes.NullGeometry)
        input_layer_has_geom = input_wkb_type not in (QgsWkbTypes.NoGeometry,
                                                      QgsWkbTypes.Unknown)

        # Drop geometry if layer is geometry-less
        if not input_layer_has_geom and new_f_has_geom:
            f = QgsFeature(input_layer.fields())
            f.setAttributes(new_f.attributes())
            new_f = f
            result_features.append(new_f)
            continue  # skip the rest

        if input_layer_has_geom and new_f_has_geom and \
                new_f.geometry().wkbType() != input_wkb_type:  # Fix geometry
            # Single -> Multi
            if (QgsWkbTypes.isMultiType(input_wkb_type)
                    and not new_f.geometry().isMultipart()):
                new_geom = new_f.geometry()
                new_geom.convertToMultiType()
                new_f.setGeometry(new_geom)
            # Drop Z/M
            if (new_f.geometry().constGet().is3D()
                    and not QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropZValue()
                new_f.setGeometry(new_geom)
            if (new_f.geometry().constGet().isMeasure()
                    and not QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropMValue()
                new_f.setGeometry(new_geom)
            # Add Z/M back (set it to 0)
            if (not new_f.geometry().constGet().is3D()
                    and QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addZValue(0.0)
                new_f.setGeometry(new_geom)
            if (not new_f.geometry().constGet().isMeasure()
                    and QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addMValue(0.0)
                new_f.setGeometry(new_geom)
            # Multi -> Single
            if (not QgsWkbTypes.isMultiType(input_wkb_type)
                    and new_f.geometry().isMultipart()):
                g = new_f.geometry()
                g2 = g.constGet()
                for i in range(g2.partCount()):
                    # Clone or crash!
                    g4 = QgsGeometry(g2.geometryN(i).clone())
                    f = QgsVectorLayerUtils.createFeature(
                        input_layer, g4, {
                            i: new_f.attribute(i)
                            for i in range(new_f.fields().count())
                        })
                    result_features.append(f)
            else:
                result_features.append(new_f)
        else:
            result_features.append(new_f)
    return result_features
Example #30
0
    def processAlgorithm(self, parameters, context, feedback):
        sourceA = self.parameterAsSource(parameters, self.INPUT, context)
        sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

        geomType = QgsWkbTypes.multiType(sourceA.wkbType())

        fieldsA = self.parameterAsFields(parameters, self.INPUT_FIELDS, context)
        fieldsB = self.parameterAsFields(parameters, self.OVERLAY_FIELDS, context)

        fieldListA = QgsFields()
        field_indices_a = []
        if len(fieldsA) > 0:
            for f in fieldsA:
                idxA = sourceA.fields().lookupField(f)
                if idxA >= 0:
                    field_indices_a.append(idxA)
                    fieldListA.append(sourceA.fields()[idxA])
        else:
            fieldListA = sourceA.fields()
            field_indices_a = [i for i in range(0, fieldListA.count())]

        fieldListB = QgsFields()
        field_indices_b = []
        if len(fieldsB) > 0:
            for f in fieldsB:
                idxB = sourceB.fields().lookupField(f)
                if idxB >= 0:
                    field_indices_b.append(idxB)
                    fieldListB.append(sourceB.fields()[idxB])
        else:
            fieldListB = sourceB.fields()
            field_indices_b = [i for i in range(0, fieldListB.count())]

        output_fields = QgsProcessingUtils.combineFields(fieldListA, fieldListB)

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               output_fields, geomType, sourceA.sourceCrs())

        outFeat = QgsFeature()
        indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())), feedback)

        total = 100.0 / sourceA.featureCount() if sourceA.featureCount() else 1
        count = 0

        for featA in sourceA.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(field_indices_a)):
            if feedback.isCanceled():
                break

            if not featA.hasGeometry():
                continue

            geom = featA.geometry()
            atMapA = featA.attributes()
            intersects = indexB.intersects(geom.boundingBox())

            request = QgsFeatureRequest().setFilterFids(intersects)
            request.setDestinationCrs(sourceA.sourceCrs())
            request.setSubsetOfAttributes(field_indices_b)

            engine = None
            if len(intersects) > 0:
                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(geom.geometry())
                engine.prepareGeometry()

            for featB in sourceB.getFeatures(request):
                if feedback.isCanceled():
                    break

                tmpGeom = featB.geometry()
                if engine.intersects(tmpGeom.geometry()):
                    out_attributes = [featA.attributes()[i] for i in field_indices_a]
                    out_attributes.extend([featB.attributes()[i] for i in field_indices_b])
                    int_geom = QgsGeometry(geom.intersection(tmpGeom))
                    if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
                        int_com = geom.combine(tmpGeom)
                        int_geom = QgsGeometry()
                        if int_com:
                            int_sym = geom.symDifference(tmpGeom)
                            int_geom = QgsGeometry(int_com.difference(int_sym))
                    if int_geom.isEmpty() or not int_geom.isGeosValid():
                        raise QgsProcessingException(
                            self.tr('GEOS geoprocessing error: One or '
                                    'more input features have invalid '
                                    'geometry.'))
                    try:
                        if QgsWkbTypes.geometryType(int_geom.wkbType()) == QgsWkbTypes.geometryType(geomType):
                            int_geom.convertToMultiType()
                            outFeat.setGeometry(int_geom)
                            outFeat.setAttributes(out_attributes)
                            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                    except:
                        raise QgsProcessingException(
                            self.tr('Feature geometry error: One or more '
                                    'output features ignored due to invalid '
                                    'geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        return {self.OUTPUT: dest_id}
Example #31
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        line_source = self.parameterAsSource(parameters, self.LINES, context)

        sameLayer = parameters[self.INPUT] == parameters[self.LINES]

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               source.fields(), QgsWkbTypes.multiType(source.wkbType()), source.sourceCrs())

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])
        request.setDestinationCrs(source.sourceCrs())

        for aSplitFeature in line_source.getFeatures(request):
            if feedback.isCanceled():
                break

            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = source.getFeatures()

        total = 100.0 / source.featureCount() if source.featureCount() else 100

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

            inGeom = inFeatA.geometry()
            attrsA = inFeatA.attributes()
            outFeat.setAttributes(attrsA)

            if inGeom.isMultipart():
                inGeoms = []

                for g in inGeom.asGeometryCollection():
                    inGeoms.append(g)
            else:
                inGeoms = [inGeom]

            lines = spatialIndex.intersects(inGeom.boundingBox())

            if len(lines) > 0:  # has intersection of bounding boxes
                splittingLines = []

                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

                for i in lines:
                    try:
                        splitGeom = splitGeoms[i]
                    except:
                        continue

                    # check if trying to self-intersect
                    if sameLayer:
                        if inFeatA.id() == i:
                            continue

                    if engine.intersects(splitGeom.geometry()):
                        splittingLines.append(splitGeom)

                if len(splittingLines) > 0:
                    for splitGeom in splittingLines:
                        splitterPList = None
                        outGeoms = []

                        split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry())
                        split_geom_engine.prepareGeometry()

                        while len(inGeoms) > 0:
                            if feedback.isCanceled():
                                break

                            inGeom = inGeoms.pop()

                            if inGeom.isNull():  # this has been encountered and created a run-time error
                                continue

                            if split_geom_engine.intersects(inGeom.geometry()):
                                inPoints = vector.extractPoints(inGeom)
                                if splitterPList is None:
                                    splitterPList = vector.extractPoints(splitGeom)

                                try:
                                    result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
                                except:
                                    feedback.reportError(self.tr('Geometry exception while splitting'))
                                    result = 1

                                # splitGeometry: If there are several intersections
                                # between geometry and splitLine, only the first one is considered.
                                if result == 0:  # split occurred
                                    if inPoints == vector.extractPoints(inGeom):
                                        # bug in splitGeometry: sometimes it returns 0 but
                                        # the geometry is unchanged
                                        outGeoms.append(inGeom)
                                    else:
                                        inGeoms.append(inGeom)

                                        for aNewGeom in newGeometries:
                                            inGeoms.append(aNewGeom)
                                else:
                                    outGeoms.append(inGeom)
                            else:
                                outGeoms.append(inGeom)

                        inGeoms = outGeoms

            parts = []

            for aGeom in inGeoms:
                if feedback.isCanceled():
                    break

                passed = True

                if QgsWkbTypes.geometryType(aGeom.wkbType()) == QgsWkbTypes.LineGeometry:
                    numPoints = aGeom.geometry().numPoints()

                    if numPoints <= 2:
                        if numPoints == 2:
                            passed = not aGeom.geometry().isClosed()  # tests if vertex 0 = vertex 1
                        else:
                            passed = False
                            # sometimes splitting results in lines of zero length

                if passed:
                    parts.append(aGeom)

            if len(parts) > 0:
                outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
                sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))
        return {self.OUTPUT: dest_id}
Example #32
0
    def processAlgorithm(self, progress):
        layerA = dataobjects.getObjectFromUri(
            self.getParameterValue(self.INPUT_A))
        splitLayer = dataobjects.getObjectFromUri(
            self.getParameterValue(self.INPUT_B))

        sameLayer = self.getParameterValue(
            self.INPUT_A) == self.getParameterValue(self.INPUT_B)
        fieldList = layerA.fields()

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(
            fieldList, layerA.wkbType(), layerA.crs())

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])

        for aSplitFeature in vector.features(splitLayer, request):
            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = vector.features(layerA)

        if len(features) == 0:
            total = 100
        else:
            total = 100.0 / float(len(features))

        multiGeoms = 0  # how many multi geometries were encountered

        for current, inFeatA in enumerate(features):
            inGeom = inFeatA.geometry()

            if inGeom.isMultipart():
                multiGeoms += 1
                # MultiGeometries are not allowed because the result of a splitted part cannot be clearly defined:
                # 1) add both new parts as new features
                # 2) store one part as a new feature and the other one as part of the multi geometry
                # 2a) which part should be which, seems arbitrary
            else:
                attrsA = inFeatA.attributes()
                outFeat.setAttributes(attrsA)
                inGeoms = [inGeom]
                lines = spatialIndex.intersects(inGeom.boundingBox())

                if len(lines) > 0:  # has intersection of bounding boxes
                    splittingLines = []

                    engine = QgsGeometry.createGeometryEngine(
                        inGeom.geometry())
                    engine.prepareGeometry()

                    for i in lines:
                        try:
                            splitGeom = splitGeoms[i]
                        except:
                            continue

                        # check if trying to self-intersect
                        if sameLayer:
                            if inFeatA.id() == i:
                                continue

                        if engine.intersects(splitGeom.geometry()):
                            splittingLines.append(splitGeom)

                    if len(splittingLines) > 0:
                        for splitGeom in splittingLines:
                            splitterPList = None
                            outGeoms = []

                            split_geom_engine = QgsGeometry.createGeometryEngine(
                                splitGeom.geometry())
                            split_geom_engine.prepareGeometry()

                            while len(inGeoms) > 0:
                                inGeom = inGeoms.pop()

                                if split_geom_engine.intersects(
                                        inGeom.geometry()):
                                    inPoints = vector.extractPoints(inGeom)
                                    if splitterPList == None:
                                        splitterPList = vector.extractPoints(
                                            splitGeom)

                                    try:
                                        result, newGeometries, topoTestPoints = inGeom.splitGeometry(
                                            splitterPList, False)
                                    except:
                                        ProcessingLog.addToLog(
                                            ProcessingLog.LOG_WARNING,
                                            self.
                                            tr('Geometry exception while splitting'
                                               ))
                                        result = 1

                                    # splitGeometry: If there are several intersections
                                    # between geometry and splitLine, only the first one is considered.
                                    if result == 0:  # split occurred
                                        if inPoints == vector.extractPoints(
                                                inGeom):
                                            # bug in splitGeometry: sometimes it returns 0 but
                                            # the geometry is unchanged
                                            QgsMessageLog.logMessage(
                                                "appending")
                                            outGeoms.append(inGeom)
                                        else:
                                            inGeoms.append(inGeom)

                                            for aNewGeom in newGeometries:
                                                inGeoms.append(aNewGeom)
                                    else:
                                        QgsMessageLog.logMessage(
                                            "appending else")
                                        outGeoms.append(inGeom)
                                else:
                                    outGeoms.append(inGeom)

                            inGeoms = outGeoms

                for aGeom in inGeoms:
                    passed = True

                    if QgsWkbTypes.geometryType( aGeom.wkbType() )  == QgsWkbTypes.LineGeometry \
                            and not QgsWkbTypes.isMultiType(aGeom.wkbType()):
                        passed = len(aGeom.asPolyline()) > 2

                        if not passed:
                            passed = (len(aGeom.asPolyline()) == 2
                                      and aGeom.asPolyline()[0] !=
                                      aGeom.asPolyline()[1])
                            # sometimes splitting results in lines of zero length

                    if passed:
                        outFeat.setGeometry(aGeom)
                        writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))

        if multiGeoms > 0:
            ProcessingLog.addToLog(
                ProcessingLog.LOG_INFO,
                self.
                tr('Feature geometry error: %s input features ignored due to multi-geometry.'
                   ) % str(multiGeoms))

        del writer
Example #33
0
    def processAlgorithm(self, parameters, context, feedback):
        sourceA = self.parameterAsSource(parameters, self.INPUT, context)
        sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

        geomType = QgsWkbTypes.multiType(sourceA.wkbType())
        fields = QgsProcessingUtils.combineFields(sourceA.fields(),
                                                  sourceB.fields())

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT,
                                               context, fields, geomType,
                                               sourceA.sourceCrs())

        featA = QgsFeature()
        featB = QgsFeature()
        outFeat = QgsFeature()

        indexA = QgsSpatialIndex(sourceA, feedback)
        indexB = QgsSpatialIndex(
            sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes(
                []).setDestinationCrs(sourceA.sourceCrs(),
                                      context.transformContext())), feedback)

        total = 100.0 / (sourceA.featureCount() *
                         sourceB.featureCount()) if sourceA.featureCount(
                         ) and sourceB.featureCount() else 1
        count = 0

        for featA in sourceA.getFeatures():
            if feedback.isCanceled():
                break

            lstIntersectingB = []
            geom = featA.geometry()
            atMapA = featA.attributes()
            intersects = indexB.intersects(geom.boundingBox())
            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    # This really shouldn't happen, as we haven't
                    # edited the input geom at all
                    feedback.pushInfo(
                        self.
                        tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                           ))
            else:
                request = QgsFeatureRequest().setFilterFids(
                    intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(),
                                          context.transformContext())

                engine = QgsGeometry.createGeometryEngine(geom.constGet())
                engine.prepareGeometry()

                for featB in sourceB.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        int_geom = geom.intersection(tmpGeom)
                        lstIntersectingB.append(tmpGeom)

                        if not int_geom:
                            # There was a problem creating the intersection
                            feedback.pushInfo(
                                self.
                                tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                                   ))
                            int_geom = QgsGeometry()
                        else:
                            int_geom = QgsGeometry(int_geom)

                        if int_geom.wkbType(
                        ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(
                                int_geom.wkbType(
                                )) == QgsWkbTypes.GeometryCollection:
                            # Intersection produced different geomety types
                            temp_list = int_geom.asGeometryCollection()
                            for i in temp_list:
                                if i.type() == geom.type():
                                    int_geom = QgsGeometry(i)
                                    try:
                                        int_geom.convertToMultiType()
                                        outFeat.setGeometry(int_geom)
                                        outFeat.setAttributes(atMapA + atMapB)
                                        sink.addFeature(
                                            outFeat, QgsFeatureSink.FastInsert)
                                    except:
                                        feedback.pushInfo(
                                            self.
                                            tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                                               ))
                        else:
                            # Geometry list: prevents writing error
                            # in geometries of different types
                            # produced by the intersection
                            # fix #3549
                            if QgsWkbTypes.geometryType(int_geom.wkbType(
                            )) == QgsWkbTypes.geometryType(geomType):
                                try:
                                    int_geom.convertToMultiType()
                                    outFeat.setGeometry(int_geom)
                                    outFeat.setAttributes(atMapA + atMapB)
                                    sink.addFeature(outFeat,
                                                    QgsFeatureSink.FastInsert)
                                except:
                                    feedback.pushInfo(
                                        self.
                                        tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                                           ))

                # the remaining bit of featA's geometry
                # if there is nothing left, this will just silently fail and we're good
                diff_geom = QgsGeometry(geom)
                if len(lstIntersectingB) != 0:
                    intB = QgsGeometry.unaryUnion(lstIntersectingB)
                    diff_geom = diff_geom.difference(intB)

                if diff_geom.wkbType(
                ) == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(
                        diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
                    temp_list = diff_geom.asGeometryCollection()
                    for i in temp_list:
                        if i.type() == geom.type():
                            diff_geom = QgsGeometry(i)
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(
                        self.
                        tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                           ))

            count += 1
            feedback.setProgress(int(count * total))

        length = len(sourceA.fields())
        atMapA = [None] * length

        for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(
                sourceA.sourceCrs(), context.transformContext())):
            if feedback.isCanceled():
                break

            add = False
            geom = featA.geometry()
            diff_geom = QgsGeometry(geom)
            atMap = [None] * length
            atMap.extend(featA.attributes())
            intersects = indexA.intersects(geom.boundingBox())

            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(
                        self.
                        tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                           ))
            else:
                request = QgsFeatureRequest().setFilterFids(
                    intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(),
                                          context.transformContext())

                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(diff_geom.constGet())
                engine.prepareGeometry()

                for featB in sourceA.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        add = True
                        diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
                    else:
                        try:
                            # Ihis only happens if the bounding box
                            # intersects, but the geometry doesn't
                            diff_geom.convertToMultiType()
                            outFeat.setGeometry(diff_geom)
                            outFeat.setAttributes(atMap)
                            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                        except:
                            feedback.pushInfo(
                                self.
                                tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                                   ))

            if add:
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(
                        self.
                        tr('Feature geometry error: One or more output features ignored due to invalid geometry.'
                           ))

            count += 1
            feedback.setProgress(int(count * total))

        return {self.OUTPUT: dest_id}
Example #34
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        if source is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))

        method = self.parameterAsEnum(parameters, self.METHOD, context)

        wkb_type = source.wkbType()
        fields = source.fields()

        new_fields = QgsFields()
        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            new_fields.append(QgsField('area', QVariant.Double))
            new_fields.append(QgsField('perimeter', QVariant.Double))
        elif QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.LineGeometry:
            new_fields.append(QgsField('length', QVariant.Double))
            if not QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('straightdis', QVariant.Double))
                new_fields.append(QgsField('sinuosity', QVariant.Double))
        else:
            if QgsWkbTypes.isMultiType(source.wkbType()):
                new_fields.append(QgsField('numparts', QVariant.Int))
            else:
                new_fields.append(QgsField('xcoord', QVariant.Double))
                new_fields.append(QgsField('ycoord', QVariant.Double))
                if QgsWkbTypes.hasZ(source.wkbType()):
                    self.export_z = True
                    new_fields.append(QgsField('zcoord', QVariant.Double))
                if QgsWkbTypes.hasM(source.wkbType()):
                    self.export_m = True
                    new_fields.append(QgsField('mvalue', QVariant.Double))

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

        coordTransform = None

        # Calculate with:
        # 0 - layer CRS
        # 1 - project CRS
        # 2 - ellipsoidal

        self.distance_area = QgsDistanceArea()
        if method == 2:
            self.distance_area.setSourceCrs(source.sourceCrs(),
                                            context.transformContext())
            self.distance_area.setEllipsoid(context.project().ellipsoid())
        elif method == 1:
            coordTransform = QgsCoordinateTransform(source.sourceCrs(),
                                                    context.project().crs(),
                                                    context.project())

        features = source.getFeatures()
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            outFeat = f
            attrs = f.attributes()
            inGeom = f.geometry()
            if inGeom:
                if coordTransform is not None:
                    inGeom.transform(coordTransform)

                if inGeom.type() == QgsWkbTypes.PointGeometry:
                    attrs.extend(self.point_attributes(inGeom))
                elif inGeom.type() == QgsWkbTypes.PolygonGeometry:
                    attrs.extend(self.polygon_attributes(inGeom))
                else:
                    attrs.extend(self.line_attributes(inGeom))

            # ensure consistent count of attributes - otherwise null
            # geometry features will have incorrect attribute length
            # and provider may reject them
            if len(attrs) < len(fields):
                attrs += [NULL] * (len(fields) - len(attrs))

            outFeat.setAttributes(attrs)
            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)

            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        unicity = self.parameterAsBoolean(parameters, self.UNICITY, context)

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

        wkb_type = source.wkbType()

        if (QgsWkbTypes.geometryType(wkb_type) != QgsWkbTypes.PolygonGeometry
                and QgsWkbTypes.geometryType(wkb_type) !=
                QgsWkbTypes.LineGeometry):
            feedback.reportError(
                'The layer geometry type is different from a line or a polygon'
            )
            return {}

        (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))

        all_segments = []

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

        if QgsWkbTypes.geometryType(wkb_type) == QgsWkbTypes.PolygonGeometry:
            if unicity:
                for current, f in enumerate(features):
                    if feedback.isCanceled():
                        break

                    if not f.hasGeometry():
                        continue
                    else:
                        for p in self.polygon_to_unique_segments(
                                f.geometry(), all_segments):
                            feat = QgsFeature()
                            feat.setAttributes(f.attributes())
                            feat.setGeometry(p)
                            sink.addFeature(feat, QgsFeatureSink.FastInsert)

                    feedback.setProgress(int(current * total))
            else:
                for current, f in enumerate(features):
                    if feedback.isCanceled():
                        break

                    if not f.hasGeometry():
                        continue
                    else:
                        for p in self.polygon_to_segments(f.geometry()):
                            feat = QgsFeature()
                            feat.setAttributes(f.attributes())
                            feat.setGeometry(p)
                            sink.addFeature(feat, QgsFeatureSink.FastInsert)

                    feedback.setProgress(int(current * total))
        else:  # LineGeometry
            if unicity:
                for current, f in enumerate(features):
                    if feedback.isCanceled():
                        break

                    if not f.hasGeometry():
                        continue
                    else:
                        for p in self.line_to_unique_segments(
                                f.geometry(), all_segments):
                            feat = QgsFeature()
                            feat.setAttributes(f.attributes())
                            feat.setGeometry(p)
                            sink.addFeature(feat, QgsFeatureSink.FastInsert)

                    feedback.setProgress(int(current * total))
            else:
                for current, f in enumerate(features):
                    if feedback.isCanceled():
                        break

                    if not f.hasGeometry():
                        continue
                    else:
                        for p in self.line_to_segments(f.geometry()):
                            feat = QgsFeature()
                            feat.setAttributes(f.attributes())
                            feat.setGeometry(p)
                            sink.addFeature(feat, QgsFeatureSink.FastInsert)

                    feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}
Example #36
0
    def processAlgorithm(self, progress):
        layerA = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_A))
        splitLayer = dataobjects.getObjectFromUri(self.getParameterValue(self.INPUT_B))

        sameLayer = self.getParameterValue(self.INPUT_A) == self.getParameterValue(self.INPUT_B)
        fieldList = layerA.fields()

        writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList,
                        QgsWkbTypes.multiType(layerA.wkbType()), layerA.crs())

        spatialIndex = QgsSpatialIndex()
        splitGeoms = {}
        request = QgsFeatureRequest()
        request.setSubsetOfAttributes([])

        for aSplitFeature in vector.features(splitLayer, request):
            splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
            spatialIndex.insertFeature(aSplitFeature)
            # honor the case that user has selection on split layer and has setting "use selection"

        outFeat = QgsFeature()
        features = vector.features(layerA)

        if len(features) == 0:
            total = 100
        else:
            total = 100.0 / float(len(features))

        for current, inFeatA in enumerate(features):
            inGeom = inFeatA.geometry()
            attrsA = inFeatA.attributes()
            outFeat.setAttributes(attrsA)

            if inGeom.isMultipart():
                inGeoms = []

                for g in inGeom.asGeometryCollection():
                    inGeoms.append(g)
            else:
                inGeoms = [inGeom]

            lines = spatialIndex.intersects(inGeom.boundingBox())

            if len(lines) > 0:  # has intersection of bounding boxes
                splittingLines = []

                engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
                engine.prepareGeometry()

                for i in lines:
                    try:
                        splitGeom = splitGeoms[i]
                    except:
                        continue

                    # check if trying to self-intersect
                    if sameLayer:
                        if inFeatA.id() == i:
                            continue

                    if engine.intersects(splitGeom.geometry()):
                        splittingLines.append(splitGeom)

                if len(splittingLines) > 0:
                    for splitGeom in splittingLines:
                        splitterPList = None
                        outGeoms = []

                        split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry())
                        split_geom_engine.prepareGeometry()

                        while len(inGeoms) > 0:
                            inGeom = inGeoms.pop()

                            if inGeom.isEmpty(): # this has been encountered and created a run-time error
                                continue

                            if split_geom_engine.intersects(inGeom.geometry()):
                                inPoints = vector.extractPoints(inGeom)
                                if splitterPList == None:
                                    splitterPList = vector.extractPoints(splitGeom)

                                try:
                                    result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
                                except:
                                    ProcessingLog.addToLog(ProcessingLog.LOG_WARNING,
                                                           self.tr('Geometry exception while splitting'))
                                    result = 1

                                # splitGeometry: If there are several intersections
                                # between geometry and splitLine, only the first one is considered.
                                if result == 0:  # split occurred
                                    if inPoints == vector.extractPoints(inGeom):
                                        # bug in splitGeometry: sometimes it returns 0 but
                                        # the geometry is unchanged
                                        outGeoms.append(inGeom)
                                    else:
                                        inGeoms.append(inGeom)

                                        for aNewGeom in newGeometries:
                                            inGeoms.append(aNewGeom)
                                else:
                                    outGeoms.append(inGeom)
                            else:
                                outGeoms.append(inGeom)

                        inGeoms = outGeoms

            parts = []

            for aGeom in inGeoms:
                passed = True

                if QgsWkbTypes.geometryType( aGeom.wkbType() )  == QgsWkbTypes.LineGeometry:
                    numPoints = aGeom.geometry().numPoints()

                    if numPoints <= 2:
                        if numPoints == 2:
                            passed = not aGeom.geometry().isClosed() # tests if vertex 0 = vertex 1
                        else:
                            passed = False
                            # sometimes splitting results in lines of zero length

                if passed:
                    parts.append(aGeom)

            if len(parts) > 0:
                outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
                writer.addFeature(outFeat)

            progress.setPercentage(int(current * total))
        del writer
Example #37
0
 def processAlgorithm(self, parameters, context, feedback):
     # get input variables
     source = self.parameterAsSource(parameters,self.INPUT,context)
     export_format = self.parameterAsEnum(parameters,self.EXPORT_FORMAT,context)
     output = self.parameterAsFileOutput(parameters,self.OUTPUT,context)
     
     # set new default values in config
     feedback.pushConsoleInfo(self.tr(f'Storing new default settings in config...'))
     self.config.set(self.module,'export_format',export_format)
     
     # coordinate transformation
     trans = QgsCoordinateTransform(source.sourceCrs(), QgsCoordinateReferenceSystem('EPSG:4326'), context.transformContext())
     
     # geometry type
     geom_type = QgsWkbTypes.geometryType(source.wkbType())
     
     if geom_type == QgsWkbTypes.LineGeometry:
         # fields to be created (empty)
         feedback.pushConsoleInfo(self.tr(f'Creating attribute fields...'))
         fields = QgsFields()
         
         # fields from feature source
         source_fields = source.fields()
         
         # if source does not have fid field, create it in fields
         if 'fid' not in source_fields.names():
             fields.append(QgsField('fid',QVariant.Int,'',4,0))
         
         # add all fields from source to fields variable
         for field in source_fields:
             fields.append(field)
         
         # get features from source
         features = source.getFeatures()
         
         # get vertices from features
         feedback.pushConsoleInfo(self.tr(f'Converting lines to vertices...'))
         points = self.lines_to_vertices(features,fields)
     
     elif geom_type == QgsWkbTypes.PointGeometry:
         # get points directly
         points = source.getFeatures()
     
     else:
         feedback.reportError(self.tr('Unknown Geometry WKB Type'),fatalError=True)
         return {}
     
     # empty table for point features and their attributes
     table = []
     
     # create dict holding all features with fieldnames and geometry
     feedback.pushConsoleInfo(self.tr(f'Extracting feature attributes...'))
     for i, feature in enumerate(points):
         feature_fields = feature.fields().names()
         feature_attributes = feature.attributes()
         
         # add minimum of required fields if they don't exist
         if not 'fid' in feature_fields:
             feature_fields.append('fid')
             feature_attributes.append(i+1)
         if not 'name' in feature_fields:
             feature_fields.append('name')
             feature_attributes.append('')
         
         # zip lists to dict
         feature_dict = dict(zip(feature_fields,feature_attributes))
         
         # get geometry of feature
         geom = feature.geometry().asPoint()
         
         # transform geometry to EPSG:4326 CRS
         feedback.pushConsoleInfo(self.tr(f'Adding coordinates...'))
         geom4326 = trans.transform(geom)
         feature_dict['lat_DD'] = geom4326.y()
         feature_dict['lon_DD'] = geom4326.x()
         
         # convert DD to DDM
         lat_ddm, lon_ddm = utils.dd2ddm(geom4326.y(), geom4326.x())
         feature_dict['lat_DDM'] = lat_ddm
         feature_dict['lon_DDM'] = lon_ddm
         
         # clean up NULL values:
         for key, value in feature_dict.items():
             if not type(value) in [float,int,str,bool]:
                 if value.isNull():
                     feature_dict[key] = None
         
         table.append(feature_dict)
     
     # export functions
     feedback.pushConsoleInfo(self.tr(f'Exporting table...'))
     if export_format == 0:
         error, result = self.export_csv(table,output)
     elif export_format == 1:
         error, result = self.export_SAM_route(table,output)
     
     if error:
         feedback.reportError(self.tr(result),fatalError=True)
         return {}
     
     # 100% done
     feedback.setProgress(100)
     feedback.pushInfo(self.tr(f'{utils.return_success()}! Export file created!\n'))
     
     result = {self.OUTPUT : output}
     
     return result
Example #38
0
    def processAlgorithm(self, parameters, context, feedback):
        sourceA = self.parameterAsSource(parameters, self.INPUT, context)
        sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

        geomType = QgsWkbTypes.multiType(sourceA.wkbType())
        fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields())

        (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
                                               fields, geomType, sourceA.sourceCrs())

        featA = QgsFeature()
        featB = QgsFeature()
        outFeat = QgsFeature()

        indexA = QgsSpatialIndex(sourceA, feedback)
        indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback)

        total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
        count = 0

        for featA in sourceA.getFeatures():
            if feedback.isCanceled():
                break

            lstIntersectingB = []
            geom = featA.geometry()
            atMapA = featA.attributes()
            intersects = indexB.intersects(geom.boundingBox())
            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    # This really shouldn't happen, as we haven't
                    # edited the input geom at all
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
            else:
                request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())

                engine = QgsGeometry.createGeometryEngine(geom.constGet())
                engine.prepareGeometry()

                for featB in sourceB.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        int_geom = geom.intersection(tmpGeom)
                        lstIntersectingB.append(tmpGeom)

                        if not int_geom:
                            # There was a problem creating the intersection
                            feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
                            int_geom = QgsGeometry()
                        else:
                            int_geom = QgsGeometry(int_geom)

                        if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
                            # Intersection produced different geomety types
                            temp_list = int_geom.asGeometryCollection()
                            for i in temp_list:
                                if i.type() == geom.type():
                                    int_geom = QgsGeometry(i)
                                    try:
                                        int_geom.convertToMultiType()
                                        outFeat.setGeometry(int_geom)
                                        outFeat.setAttributes(atMapA + atMapB)
                                        sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                                    except:
                                        feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
                        else:
                            # Geometry list: prevents writing error
                            # in geometries of different types
                            # produced by the intersection
                            # fix #3549
                            if QgsWkbTypes.geometryType(int_geom.wkbType()) == QgsWkbTypes.geometryType(geomType):
                                try:
                                    int_geom.convertToMultiType()
                                    outFeat.setGeometry(int_geom)
                                    outFeat.setAttributes(atMapA + atMapB)
                                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                                except:
                                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

                # the remaining bit of featA's geometry
                # if there is nothing left, this will just silently fail and we're good
                diff_geom = QgsGeometry(geom)
                if len(lstIntersectingB) != 0:
                    intB = QgsGeometry.unaryUnion(lstIntersectingB)
                    diff_geom = diff_geom.difference(intB)

                if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
                    temp_list = diff_geom.asGeometryCollection()
                    for i in temp_list:
                        if i.type() == geom.type():
                            diff_geom = QgsGeometry(i)
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMapA)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        length = len(sourceA.fields())
        atMapA = [None] * length

        for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs(), context.transformContext())):
            if feedback.isCanceled():
                break

            add = False
            geom = featA.geometry()
            diff_geom = QgsGeometry(geom)
            atMap = [None] * length
            atMap.extend(featA.attributes())
            intersects = indexA.intersects(geom.boundingBox())

            if len(intersects) < 1:
                try:
                    geom.convertToMultiType()
                    outFeat.setGeometry(geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
            else:
                request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
                request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())

                # use prepared geometries for faster intersection tests
                engine = QgsGeometry.createGeometryEngine(diff_geom.constGet())
                engine.prepareGeometry()

                for featB in sourceA.getFeatures(request):
                    atMapB = featB.attributes()
                    tmpGeom = featB.geometry()

                    if engine.intersects(tmpGeom.constGet()):
                        add = True
                        diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
                    else:
                        try:
                            # Ihis only happens if the bounding box
                            # intersects, but the geometry doesn't
                            diff_geom.convertToMultiType()
                            outFeat.setGeometry(diff_geom)
                            outFeat.setAttributes(atMap)
                            sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                        except:
                            feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            if add:
                try:
                    diff_geom.convertToMultiType()
                    outFeat.setGeometry(diff_geom)
                    outFeat.setAttributes(atMap)
                    sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
                except:
                    feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))

            count += 1
            feedback.setProgress(int(count * total))

        return {self.OUTPUT: dest_id}
    def processAlgorithm(self, parameters, context, feedback):
        ''' Here is where the processing itself takes place. '''
        #
        if not is_dependencies_satisfied:
            return {}

        # Input layer
        the_layer = self.parameterAsSource(parameters, self.THE_LAYER, context)
        if the_layer is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.THE_LAYER))
        # Output geometry
        if QgsWkbTypes.geometryType(
                the_layer.wkbType()) == QgsWkbTypes.PointGeometry:
            output_wkb = QgsWkbTypes.MultiPointZ
        elif QgsWkbTypes.geometryType(
                the_layer.wkbType()) == QgsWkbTypes.LineGeometry:
            output_wkb = QgsWkbTypes.MultiLineStringZ
        elif QgsWkbTypes.geometryType(
                the_layer.wkbType()) == QgsWkbTypes.PolygonGeometry:
            output_wkb = QgsWkbTypes.MultiPolygonZ
        else:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.THE_LAYER))
        # Y-value
        self._yval = self.parameterAsDouble(parameters, self.Y_VALUE, context)
        # Output file
        (sink, dest_id) = self.parameterAsSink(parameters,
                                               self.OUTPUT, context,
                                               the_layer.fields(), output_wkb,
                                               the_layer.sourceCrs())
        if sink is None:
            raise QgsProcessingException(
                self.invalidSinkError(parameters, self.OUTPUT))

        total = 100.0 / the_layer.featureCount() if the_layer.featureCount(
        ) else 0
        features = the_layer.getFeatures()
        for current, f in enumerate(features):
            if feedback.isCanceled():
                break

            # Copy attributes
            fz = QgsFeature()
            fz.setAttributes(f.attributes())

            # Get geometry
            geom = f.geometry()
            wkt = geom.asWkt()
            typ = wkt.split(" ")[0].strip()
            if typ in self._wktdef:
                nwkt = self._wktdef[typ](wkt)
            else:
                # Unrecognised geometry type
                raise ValueError('Unrecognised geometry type')

            fz.setGeometry(QgsGeometry.fromWkt(nwkt))

            # Store output feature
            sink.addFeature(fz, QgsFeatureSink.FastInsert)
            feedback.setProgress(int(current * total))

        return {self.OUTPUT: dest_id}