コード例 #1
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        self.algRunner = AlgRunner()

        inputLyr = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        if inputLyr is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))
        overlayLyr = self.parameterAsVectorLayer(parameters, self.OVERLAY,
                                                 context)
        if overlayLyr is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.OVERLAY))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        onlySelectedOverlay = self.parameterAsBool(parameters,
                                                   self.SELECTED_OVERLAY,
                                                   context)
        behavior = self.parameterAsEnum(parameters, self.BEHAVIOR, context)

        multiStepFeedback = QgsProcessingMultiStepFeedback(4, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Populating temp layer...'))
        auxLyr = layerHandler.createAndPopulateUnifiedVectorLayer(
            [inputLyr],
            geomType=inputLyr.wkbType(),
            onlySelected=onlySelected,
            feedback=multiStepFeedback)
        multiStepFeedback.setCurrentStep(1)
        if onlySelectedOverlay:
            overlayLyr = layerHandler.createAndPopulateUnifiedVectorLayer(
                [overlayLyr],
                geomType=overlayLyr.wkbType(),
                onlySelected=onlySelectedOverlay,
                feedback=multiStepFeedback)
            overlayLyr.startEditing()
            overlayLyr.renameAttribute(0, 'fid')
            overlayLyr.renameAttribute(1, 'cl')
            overlayLyr.commitChanges()
        # 1- check method
        # 2- if overlay and keep, use clip and symetric difference
        # 3- if remove outside, use clip
        # 4- if remove inside, use symetric difference
        multiStepFeedback.setCurrentStep(2)
        multiStepFeedback.pushInfo(self.tr('Running overlay...'))
        outputLyr = self.runOverlay(auxLyr, overlayLyr, behavior, context,
                                    multiStepFeedback)
        multiStepFeedback.setCurrentStep(3)
        multiStepFeedback.pushInfo(self.tr('Updating original layer...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            [inputLyr],
            outputLyr,
            feedback=multiStepFeedback,
            onlySelected=onlySelected)

        return {self.OUTPUT: inputLyr}
コード例 #2
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()
        inputLyr = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        if inputLyr is None:
            raise QgsProcessingException(
                self.invalidSourceError(
                    parameters,
                    self.INPUT
                    )
                )
        onlySelected = self.parameterAsBool(
            parameters,
            self.SELECTED,
            context
            )
        tol = self.parameterAsDouble(
            parameters,
            self.TOLERANCE,
            context
            )

        multiStepFeedback = QgsProcessingMultiStepFeedback(3, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Populating temp layer...'))
        auxLyr = layerHandler.createAndPopulateUnifiedVectorLayer(
            [inputLyr],
            geomType=inputLyr.wkbType(),
            onlySelected=onlySelected,
            feedback=multiStepFeedback
            )
        
        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(
            self.tr(
                'Snapping geometries from layer {input} to grid with size {tol}...'
                ).format(input=inputLyr.name(), tol=tol)
            )
        snappedLayer = algRunner.runSnapToGrid(
            auxLyr,
            tol,
            context,
            feedback=multiStepFeedback
            )

        multiStepFeedback.setCurrentStep(2)
        multiStepFeedback.pushInfo(self.tr('Updating original layer...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            [inputLyr],
            snappedLayer,
            feedback=multiStepFeedback,
            onlySelected=onlySelected
            )

        return {self.OUTPUT: inputLyr}
コード例 #3
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()

        inputLyrList = self.parameterAsLayerList(parameters, self.INPUTLAYERS,
                                                 context)
        if inputLyrList is None or inputLyrList == []:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS))
        for layer in inputLyrList:
            if layer.featureCount() > 0:
                geomType = next(layer.getFeatures()).geometry().wkbType()
                break
        else:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS),
                self.tr("Provided layers have no features in it."))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        snap = self.parameterAsDouble(parameters, self.TOLERANCE, context)
        minArea = self.parameterAsDouble(parameters, self.MINAREA, context)
        self.prepareFlagSink(parameters, inputLyrList[0], geomType, context)

        multiStepFeedback = QgsProcessingMultiStepFeedback(3, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Building unified layer...'))
        # in order to check the topology of all layers as a whole, all features
        # are handled as if they formed a single layer
        coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
            inputLyrList,
            geomType=geomType,
            onlySelected=onlySelected,
            feedback=multiStepFeedback)
        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(
            self.tr('Running clean on unified layer...'))
        cleanedCoverage, error = algRunner.runClean(coverage, [
            algRunner.RMSA, algRunner.Break, algRunner.RmDupl,
            algRunner.RmDangle
        ],
                                                    context,
                                                    returnError=True,
                                                    snap=snap,
                                                    minArea=minArea,
                                                    feedback=multiStepFeedback)

        multiStepFeedback.setCurrentStep(2)
        multiStepFeedback.pushInfo(self.tr('Updating original layer...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            inputLyrList, cleanedCoverage, feedback=multiStepFeedback)
        self.flagCoverageIssues(cleanedCoverage, error, feedback)

        return {self.INPUTLAYERS: inputLyrList, self.FLAGS: self.flagSink}
コード例 #4
0
 def getGapsOfCoverageWithFrame(self,
                                coverage,
                                frameLyr,
                                context,
                                feedback=None,
                                onFinish=None):
     """
     Identifies all gaps inside coverage layer and between coverage and frame layer.
     :param coverage: (QgsVectorLayer) unified coverage layer.
     :param frameLyr: (QgsVectorLayer) frame layer.
     :param context: (QgsProcessingContext)
     :param feedback: (QgsProcessingFeedback) QGIS' object for progress tracking and controlling.
     :param onFinish: (list-of-str) list of alg names to be executed after difference alg.
     """
     # identify all holes in coverage layer first
     coverageHolesParam = {
         'INPUT': coverage,
         'FLAGS': 'memory:',
         'SELECTED': False
     }
     coverageHoles = processing.run('dsgtools:identifygaps',
                                    coverageHolesParam, None, feedback,
                                    context)['FLAGS']
     geometryHandler = GeometryHandler()
     gapSet = set()
     for feat in coverageHoles.getFeatures():
         for geom in geometryHandler.deaggregateGeometry(feat.geometry()):
             self.flagFeature(geom, self.tr('Gap in coverage layer'))
             gapSet.add(geom)
     # missing possible holes between coverage and frame, but gaps in coverage may cause invalid geometries
     # while executing difference alg. Since its already identified, "add" them to the coverage
     layerHandler = LayerHandler()
     filledCoverage = layerHandler.createAndPopulateUnifiedVectorLayer(
         [coverage, coverageHoles], QgsWkbTypes.Polygon)
     # dissolveParameters = {
     #     'INPUT' : filledCoverage,
     #     'FIELD':[],
     #     'OUTPUT':'memory:'
     # }
     # dissolveOutput = processing.run('native:dissolve', dissolveParameters, context = context)['OUTPUT']
     dissolveOutput = LayerHandler().runGrassDissolve(
         filledCoverage, context)
     differenceParameters = {
         'INPUT': frameLyr,
         'OVERLAY': dissolveOutput,
         'OUTPUT': 'memory:'
     }
     differenceOutput = processing.run('native:difference',
                                       differenceParameters, onFinish,
                                       feedback, context)
     for feat in differenceOutput['OUTPUT'].getFeatures():
         for geom in geometryHandler.deaggregateGeometry(feat.geometry()):
             if geom not in gapSet:
                 self.flagFeature(geom,
                                  self.tr('Gap in coverage with frame'))
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()
        inputLyrList = self.parameterAsLayerList(parameters, self.INPUTLAYERS,
                                                 context)
        if inputLyrList is None or inputLyrList == []:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        tol = self.parameterAsDouble(parameters, self.TOLERANCE, context)

        multiStepFeedback = QgsProcessingMultiStepFeedback(5, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Building unified layer...'))
        coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
            inputLyrList,
            geomType=QgsWkbTypes.MultiPolygon,
            onlySelected=onlySelected,
            feedback=multiStepFeedback)

        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(
            self.tr('Identifying dangles on {layer}...').format(
                layer=coverage.name()))
        dangleLyr = algRunner.runIdentifyDangles(coverage,
                                                 tol,
                                                 context,
                                                 feedback=multiStepFeedback,
                                                 onlySelected=onlySelected)

        multiStepFeedback.setCurrentStep(2)
        layerHandler.filterDangles(dangleLyr, tol, feedback=multiStepFeedback)

        multiStepFeedback.setCurrentStep(3)
        multiStepFeedback.pushInfo(
            self.tr('Snapping layer {layer} to dangles...').format(
                layer=coverage.name()))
        algRunner.runSnapLayerOnLayer(coverage,
                                      dangleLyr,
                                      tol,
                                      context,
                                      feedback=multiStepFeedback,
                                      onlySelected=onlySelected,
                                      behavior=0)

        multiStepFeedback.setCurrentStep(4)
        multiStepFeedback.pushInfo(self.tr('Updating original layers...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            inputLyrList, coverage, feedback=multiStepFeedback)

        return {self.INPUTLAYERS: inputLyrList}
コード例 #6
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()
        inputLyr = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        tol = self.parameterAsDouble(parameters, self.MIN_AREA, context)
        attributeBlackList = self.parameterAsFields(parameters,
                                                    self.ATTRIBUTE_BLACK_LIST,
                                                    context)
        ignoreVirtual = self.parameterAsBool(parameters,
                                             self.IGNORE_VIRTUAL_FIELDS,
                                             context)
        ignorePK = self.parameterAsBool(parameters, self.IGNORE_PK_FIELDS,
                                        context)

        tol = -1 if tol is None else tol
        nSteps = 4 if tol > 0 else 3
        multiStepFeedback = QgsProcessingMultiStepFeedback(nSteps, feedback)
        currentStep = 0
        multiStepFeedback.setCurrentStep(currentStep)
        multiStepFeedback.pushInfo(self.tr('Populating temp layer...\n'))
        unifiedLyr = layerHandler.createAndPopulateUnifiedVectorLayer(
            [inputLyr],
            geomType=QgsWkbTypes.MultiPolygon,
            attributeBlackList=attributeBlackList,
            onlySelected=onlySelected,
            feedback=multiStepFeedback)
        currentStep += 1

        if tol > 0:
            multiStepFeedback.setCurrentStep(currentStep)
            multiStepFeedback.pushInfo(
                self.tr('Adding size constraint field...\n'))
            unifiedLyr = layerHandler.addDissolveField(
                unifiedLyr, tol, feedback=multiStepFeedback)
            currentStep += 1

        multiStepFeedback.setCurrentStep(currentStep)
        multiStepFeedback.pushInfo(self.tr('Running dissolve...\n'))
        dissolvedLyr = algRunner.runDissolve(unifiedLyr,
                                             context,
                                             feedback=multiStepFeedback,
                                             field=['tupple'])
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            [inputLyr],
            dissolvedLyr,
            feedback=multiStepFeedback,
            onlySelected=onlySelected)

        return {self.OUTPUT: inputLyr}
コード例 #7
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()

        inputLyrList = self.parameterAsLayerList(parameters, self.INPUTLAYERS,
                                                 context)
        if inputLyrList is None or inputLyrList == []:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        snap = self.parameterAsDouble(parameters, self.SNAP, context)
        threshold = self.parameterAsDouble(parameters, self.DOUGLASPARAMETER,
                                           context)
        minArea = self.parameterAsDouble(parameters, self.MINAREA, context)
        self.prepareFlagSink(parameters, inputLyrList[0],
                             QgsWkbTypes.MultiPolygon, context)

        multiStepFeedback = QgsProcessingMultiStepFeedback(3, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Building unified layer...'))
        coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
            inputLyrList,
            geomType=QgsWkbTypes.MultiPolygon,
            onlySelected=onlySelected,
            feedback=multiStepFeedback)

        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(
            self.tr('Running clean on unified layer...'))
        simplifiedCoverage, error = algRunner.runDouglasSimplification(
            coverage,
            threshold,
            context,
            returnError=True,
            snap=snap,
            minArea=minArea,
            feedback=multiStepFeedback)

        multiStepFeedback.setCurrentStep(2)
        multiStepFeedback.pushInfo(self.tr('Updating original layer...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            inputLyrList,
            simplifiedCoverage,
            feedback=multiStepFeedback,
            onlySelected=onlySelected)

        self.flagCoverageIssues(simplifiedCoverage, error, feedback)

        return {self.INPUTLAYERS: inputLyrList, self.FLAGS: self.flag_id}
コード例 #8
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()

        inputLyr = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        if inputLyr is None:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUT))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        snap = self.parameterAsDouble(parameters, self.TOLERANCE, context)
        # if snap < 0 and snap != -1:
        #     raise QgsProcessingException(self.invalidParameterError(parameters, self.TOLERANCE))
        minArea = self.parameterAsDouble(parameters, self.MINAREA, context)
        self.prepareFlagSink(parameters, inputLyr, inputLyr.wkbType(), context)

        multiStepFeedback = QgsProcessingMultiStepFeedback(3, feedback)
        multiStepFeedback.setCurrentStep(0)
        multiStepFeedback.pushInfo(self.tr('Populating temp layer...'))
        auxLyr = layerHandler.createAndPopulateUnifiedVectorLayer(
            [inputLyr],
            geomType=inputLyr.wkbType(),
            onlySelected=onlySelected,
            feedback=multiStepFeedback)
        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(self.tr('Running clean...'))
        cleanedLyr, error = algRunner.runClean(auxLyr, \
                                                    [algRunner.RMSA, algRunner.Break, algRunner.RmDupl, algRunner.RmDangle], \
                                                    context, \
                                                    returnError=True, \
                                                    snap=snap, \
                                                    minArea=minArea,
                                                    feedback=multiStepFeedback)
        multiStepFeedback.setCurrentStep(2)
        multiStepFeedback.pushInfo(self.tr('Updating original layer...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            [inputLyr],
            cleanedLyr,
            feedback=multiStepFeedback,
            onlySelected=onlySelected)
        self.flagIssues(cleanedLyr, error, feedback)

        return {self.OUTPUT: inputLyr, self.FLAGS: self.flag_id}
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        geometryHandler = GeometryHandler()
        layerHandler = LayerHandler()
        inputLyrList = self.parameterAsLayerList(parameters, self.INPUTLAYERS,
                                                 context)
        if inputLyrList == []:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        tol = self.parameterAsDouble(parameters, self.TOLERANCE, context)
        self.prepareFlagSink(parameters, inputLyrList[0], QgsWkbTypes.Point,
                             context)
        for lyr in inputLyrList:
            if feedback.isCanceled():
                break
            self.runIdentifyOutOfBoundsAngles(lyr, onlySelected, tol, context)
        epsg = inputLyrList[0].crs().authid().split(':')[-1]
        coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
            inputLyrList, QgsWkbTypes.Point, epsg, onlySelected=onlySelected)
        cleanedCoverage = self.cleanCoverage(coverage, context)
        segmentDict = geometryHandler.getSegmentDict(cleanedCoverage)
        # Compute the number of steps to display within the progress bar and
        # get features from source
        # featureList, total = self.getIteratorAndFeatureCount(inputLyr)

        # for current, feat in enumerate(featureList):
        #     # Stop the algorithm if cancel button has been clicked
        #     if feedback.isCanceled():
        #         break
        #     outOfBoundsList = geometryHandler.getOutOfBoundsAngle(feat, tol)
        #     if outOfBoundsList:
        #         for item in outOfBoundsList:
        #             flagText = self.tr('Feature from layer {0} with id={1} has angle of value {2} degrees, which is lesser than the tolerance of {3} degrees.').format(inputLyr.name(), item['feat_id'], item['angle'], tol)
        #             self.flagFeature(item['geom'], flagText)
        #     # Update the progress bar
        #     feedback.setProgress(int(current * total))

        return {self.FLAGS: self.flag_id}
コード例 #10
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        geometryHandler = GeometryHandler()
        layerHandler = LayerHandler()
        inputLyrList = self.parameterAsLayerList(parameters, self.INPUTLAYERS,
                                                 context)
        if inputLyrList == []:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.INPUTLAYERS))
        frameLyr = self.parameterAsVectorLayer(parameters, self.FRAMELAYER,
                                               context)
        if frameLyr and frameLyr in inputLyrList:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.FRAMELAYER))
        isMulti = True
        for inputLyr in inputLyrList:
            isMulti &= QgsWkbTypes.isMultiType(int(inputLyr.wkbType()))
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        self.prepareFlagSink(parameters, inputLyrList[0], QgsWkbTypes.Polygon,
                             context)
        # Compute the number of steps to display within the progress bar and
        # get features from source

        coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
            inputLyrList, QgsWkbTypes.Polygon, onlySelected=onlySelected)
        lyr = self.overlayCoverage(coverage, context)
        if frameLyr:
            self.getGapsOfCoverageWithFrame(lyr, frameLyr, context)
        featureList, total = self.getIteratorAndFeatureCount(
            lyr
        )  #only selected is not applied because we are using an inner layer, not the original ones
        geomDict = self.getGeomDict(featureList, isMulti, feedback, total)
        self.raiseFlags(geomDict, feedback)
        QgsProject.instance().removeMapLayer(lyr)
        return {self.FLAGS: self.flag_id}
コード例 #11
0
class IdentifyDanglesAlgorithm(ValidationAlgorithm):
    INPUT = 'INPUT'
    SELECTED = 'SELECTED'
    TOLERANCE = 'TOLERANCE'
    LINEFILTERLAYERS = 'LINEFILTERLAYERS'
    POLYGONFILTERLAYERS = 'POLYGONFILTERLAYERS'
    TYPE = 'TYPE'
    IGNOREINNER = 'IGNOREINNER'
    FLAGS = 'FLAGS'

    def initAlgorithm(self, config):
        """
        Parameter setting.
        """
        self.addParameter(
            QgsProcessingParameterVectorLayer(self.INPUT,
                                              self.tr('Input layer'),
                                              [QgsProcessing.TypeVectorLine]))
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.SELECTED, self.tr('Process only selected features')))
        self.addParameter(
            QgsProcessingParameterNumber(
                self.TOLERANCE,
                self.tr('Search radius'),
                minValue=0,
                type=QgsProcessingParameterNumber.Double,
                defaultValue=2))
        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                self.LINEFILTERLAYERS,
                self.tr('Linestring Filter Layers'),
                QgsProcessing.TypeVectorLine,
                optional=True))
        self.addParameter(
            QgsProcessingParameterMultipleLayers(
                self.POLYGONFILTERLAYERS,
                self.tr('Polygon Filter Layers'),
                QgsProcessing.TypeVectorPolygon,
                optional=True))
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.TYPE, self.tr('Ignore dangle on unsegmented lines')))
        self.addParameter(
            QgsProcessingParameterBoolean(
                self.IGNOREINNER,
                self.tr('Ignore search radius on inner layer search')))
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.FLAGS,
                self.tr('{0} Flags').format(self.displayName())))

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        self.layerHandler = LayerHandler()
        inputLyr = self.parameterAsVectorLayer(parameters, self.INPUT, context)
        onlySelected = self.parameterAsBool(parameters, self.SELECTED, context)
        searchRadius = self.parameterAsDouble(parameters, self.TOLERANCE,
                                              context)
        lineFilterLyrList = self.parameterAsLayerList(parameters,
                                                      self.LINEFILTERLAYERS,
                                                      context)
        polygonFilterLyrList = self.parameterAsLayerList(
            parameters, self.POLYGONFILTERLAYERS, context)
        ignoreNotSplit = self.parameterAsBool(parameters, self.TYPE, context)
        ignoreInner = self.parameterAsBool(parameters, self.IGNOREINNER,
                                           context)
        self.prepareFlagSink(parameters, inputLyr, QgsWkbTypes.Point, context)

        # Compute the number of steps to display within the progress bar and
        # get features from source
        feedbackTotal = 3
        feedbackTotal += 1 if lineFilterLyrList or polygonFilterLyrList else 0
        feedbackTotal += 1 if not ignoreInner else 0
        multiStep = QgsProcessingMultiStepFeedback(feedbackTotal, feedback)
        currentStep = 0
        multiStep.setCurrentStep(currentStep)
        multiStep.pushInfo(self.tr('Building search structure...'))
        endVerticesDict = self.layerHandler.buildInitialAndEndPointDict(
            inputLyr, onlySelected=onlySelected, feedback=multiStep)

        #search for dangles candidates
        currentStep += 1
        multiStep.setCurrentStep(currentStep)
        multiStep.pushInfo(self.tr('Looking for dangles...'))
        pointList = self.searchDanglesOnPointDict(endVerticesDict, multiStep)
        #build filter layer
        currentStep += 1
        multiStep.setCurrentStep(currentStep)
        multiStep.pushInfo(self.tr('Filtering dangles candidates...'))
        filterLayer = self.buildFilterLayer(lineFilterLyrList,
                                            polygonFilterLyrList,
                                            context,
                                            multiStep,
                                            onlySelected=onlySelected)
        #filter pointList with filterLayer

        if filterLayer:
            currentStep += 1
            multiStep.setCurrentStep(currentStep)
            multiStep.pushInfo(
                self.tr('Filtering dangles candidates with filter...'))
            filteredPointList = self.filterPointListWithFilterLayer(
                pointList, filterLayer, searchRadius, multiStep)
        else:
            filteredPointList = pointList
        #filter with own layer
        if not ignoreInner:  #True when looking for dangles on contour lines
            currentStep += 1
            multiStep.setCurrentStep(currentStep)
            multiStep.pushInfo(self.tr('Filtering inner dangles...'))
            filteredPointList = self.filterPointListWithFilterLayer(
                filteredPointList,
                inputLyr,
                searchRadius,
                multiStep,
                isRefLyr=True,
                ignoreNotSplit=ignoreNotSplit)
        #build flag list with filtered points
        currentStep += 1
        multiStep.setCurrentStep(currentStep)
        multiStep.pushInfo(self.tr('Raising flags...'))
        if filteredPointList:
            # currentValue = feedback.progress()
            currentTotal = 100 / len(filteredPointList)
            for current, point in enumerate(filteredPointList):
                if multiStep.isCanceled():
                    break
                self.flagFeature(
                    QgsGeometry.fromPointXY(point),
                    self.tr('Dangle on {0}').format(inputLyr.name()))
                multiStep.setProgress(current * currentTotal)
        # feedback.setProgress(100)
        return {self.FLAGS: self.flag_id}

    def searchDanglesOnPointDict(self, endVerticesDict, feedback):
        """
        Counts the number of points on each endVerticesDict's key and returns a list of QgsPoint built from key candidate.
        """
        pointList = []
        nVertexes = len(endVerticesDict)
        localTotal = 100 / nVertexes if nVertexes else 0
        # actual search for dangles
        for current, point in enumerate(endVerticesDict):
            if feedback.isCanceled():
                break
            # this means we only have one occurrence of point, therefore it is a dangle
            if len(endVerticesDict[point]) <= 1:
                pointList.append(point)
            feedback.setProgress(localTotal * current)
        return pointList

    def buildFilterLayer(self,
                         lineLyrList,
                         polygonLyrList,
                         context,
                         feedback,
                         onlySelected=False):
        """
        Buils one layer of filter lines.
        Build unified layer is not used because we do not care for attributes here, only geometry.
        refLyr elements are also added.
        """
        if not (lineLyrList + polygonLyrList):
            return []
        lineLyrs = lineLyrList
        for polygonLyr in polygonLyrList:
            if feedback.isCanceled():
                break
            lineLyrs += [self.makeBoundaries(polygonLyr, context, feedback)]
        if not lineLyrs:
            return None
        unifiedLinesLyr = self.layerHandler.createAndPopulateUnifiedVectorLayer(
            lineLyrs, QgsWkbTypes.MultiLineString, onlySelected=onlySelected)
        filterLyr = self.cleanLayer(unifiedLinesLyr, [0, 6], context)
        return filterLyr

    def makeBoundaries(self, lyr, context, feedback):
        parameters = {'INPUT': lyr, 'OUTPUT': 'memory:'}
        output = processing.run("native:boundary", parameters, context=context)
        return output['OUTPUT']

    def cleanLayer(self,
                   inputLyr,
                   toolList,
                   context,
                   typeList=[0, 1, 2, 3, 4, 5, 6]):
        #TODO write one class that runs all processing stuff (model that tomorrow)
        output = QgsProcessingUtils.generateTempFilename('output.shp')
        error = QgsProcessingUtils.generateTempFilename('error.shp')
        parameters = {
            'input': inputLyr,
            'type': typeList,
            'tool': toolList,
            'threshold': '-1',
            '-b': False,
            '-c': True,
            'output': output,
            'error': error,
            'GRASS_REGION_PARAMETER': None,
            'GRASS_SNAP_TOLERANCE_PARAMETER': -1,
            'GRASS_MIN_AREA_PARAMETER': 0.0001,
            'GRASS_OUTPUT_TYPE_PARAMETER': 0,
            'GRASS_VECTOR_DSCO': '',
            'GRASS_VECTOR_LCO': ''
        }
        x = processing.run('grass7:v.clean', parameters, context=context)
        lyr = QgsProcessingUtils.mapLayerFromString(x['output'], context)
        return lyr

    def filterPointListWithFilterLayer(self,
                                       pointList,
                                       filterLayer,
                                       searchRadius,
                                       feedback,
                                       isRefLyr=False,
                                       ignoreNotSplit=False):
        """
        Builds buffer areas from each point and evaluates the intersecting lines. If there are more than two intersections, it is a dangle.
        """
        nPoints = len(pointList)
        localTotal = 100 / nPoints if nPoints else 0
        spatialIdx, allFeatureDict = self.buildSpatialIndexAndIdDict(
            filterLayer)
        notDangleList = []
        for current, point in enumerate(pointList):
            if feedback.isCanceled():
                break
            candidateCount = 0
            qgisPoint = QgsGeometry.fromPointXY(point)
            #search radius to narrow down candidates
            buffer = qgisPoint.buffer(searchRadius, -1)
            bufferBB = buffer.boundingBox()
            #gets candidates from spatial index
            candidateIds = spatialIdx.intersects(bufferBB)
            #if there is only one feat in candidateIds, that means that it is not a dangle
            bufferCount = len([
                id for id in candidateIds
                if buffer.intersects(allFeatureDict[id].geometry())
            ])
            for id in candidateIds:
                if not isRefLyr:
                    if buffer.intersects(allFeatureDict[id].geometry()) and \
                    qgisPoint.distance(allFeatureDict[id].geometry()) < 10**-9: #float problem, tried with intersects and touches and did not get results
                        notDangleList.append(point)
                        break
                else:
                    if ignoreNotSplit:
                        if buffer.intersects(allFeatureDict[id].geometry()) and \
                        (qgisPoint.distance(allFeatureDict[id].geometry()) < 10**-9 or \
                        qgisPoint.intersects(allFeatureDict[id].geometry())): #float problem, tried with intersects and touches and did not get results
                            candidateCount += 1
                    else:
                        if buffer.intersects(allFeatureDict[id].geometry()) and \
                        (qgisPoint.touches(allFeatureDict[id].geometry())): #float problem, tried with intersects and touches and did not get results
                            candidateCount += 1
                    if candidateCount == bufferCount:
                        notDangleList.append(point)
            feedback.setProgress(localTotal * current)
        filteredDangleList = [
            point for point in pointList if point not in notDangleList
        ]
        return filteredDangleList

    def buildSpatialIndexAndIdDict(self, inputLyr):
        """
        creates a spatial index for the centroid layer
        """
        spatialIdx = QgsSpatialIndex()
        idDict = {}
        for feat in inputLyr.getFeatures():
            spatialIdx.insertFeature(feat)
            idDict[feat.id()] = feat
        return spatialIdx, idDict

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'identifydangles'

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return self.tr('Identify Dangles')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr('Quality Assurance Tools (Identification Processes)')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'DSGTools: Quality Assurance Tools (Identification Processes)'

    def tr(self, string):
        return QCoreApplication.translate('IdentifyDanglesAlgorithm', string)

    def createInstance(self):
        return IdentifyDanglesAlgorithm()
コード例 #12
0
    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        layerHandler = LayerHandler()
        algRunner = AlgRunner()

        layerHandler = LayerHandler()
        inputLyr = self.parameterAsVectorLayer(
            parameters,
            self.INPUT,
            context
            )
        onlySelected = self.parameterAsBool(
            parameters,
            self.SELECTED,
            context
            )
        tol = self.parameterAsDouble(
            parameters,
            self.TOLERANCE,
            context
            )
        attributeBlackList = self.parameterAsFields(
            parameters,
            self.ATTRIBUTE_BLACK_LIST,
            context
            )
        ignoreVirtual = self.parameterAsBool(
            parameters,
            self.IGNORE_VIRTUAL_FIELDS,
            context
            )
        ignorePK = self.parameterAsBool(
            parameters,
            self.IGNORE_PK_FIELDS,
            context
            )

        layerHandler.mergeLinesOnLayer(
            inputLyr,
            feedback=feedback,
            onlySelected=onlySelected,
            ignoreVirtualFields=ignoreVirtual,
            attributeBlackList=attributeBlackList,
            excludePrimaryKeys=ignorePK
            )
        #aux layer
        multiStepFeedback = QgsProcessingMultiStepFeedback(8, feedback)
        multiStepFeedback.setCurrentStep(0)
        if onlySelected:
            multiStepFeedback.pushInfo(self.tr('Building auxiliar layer...'))
            coverage = layerHandler.createAndPopulateUnifiedVectorLayer(
                [inputLyr],
                geomType=QgsWkbTypes.MultiPolygon,
                onlySelected=onlySelected,
                feedback=multiStepFeedback
                )
        else:
            coverage = inputLyr
        #dangles
        multiStepFeedback.setCurrentStep(1)
        multiStepFeedback.pushInfo(self.tr('Identifying dangles on {layer}...').format(layer=coverage.name()))
        dangleLyr = algRunner.runIdentifyDangles(
            inputLayer=coverage,
            searchRadius=tol,
            context=context,
            feedback=multiStepFeedback,
            onlySelected=False
            )
        #filter dangles
        multiStepFeedback.setCurrentStep(2)
        layerHandler.filterDangles(
            dangleLyr,
            tol,
            feedback=multiStepFeedback
            )
        #snap layer to dangles
        multiStepFeedback.setCurrentStep(3)
        multiStepFeedback.pushInfo(self.tr('Snapping layer {layer} to dangles...').format(layer=coverage.name()))
        algRunner.runSnapLayerOnLayer(
            coverage,
            dangleLyr,
            tol,
            context,
            feedback=multiStepFeedback,
            onlySelected=False, #this is done due to the aux layer usage
            behavior=0
            )
        #inner layer snap
        multiStepFeedback.setCurrentStep(4)
        multiStepFeedback.pushInfo(self.tr('Snapping layer {layer} with {layer}...').format(layer=coverage.name()))
        algRunner.runSnapLayerOnLayer(
            coverage,
            coverage,
            tol,
            context,
            feedback=multiStepFeedback,
            onlySelected=False, #this is done due to the aux layer usage
            behavior=0
            )
        #clean to break lines
        multiStepFeedback.setCurrentStep(5)
        multiStepFeedback.pushInfo(self.tr('Running clean on {layer}...').format(layer=coverage.name()))
        multiStepFeedback.pushInfo(self.tr('Running clean on unified layer...'))
        cleanedCoverage, error = algRunner.runClean(
            coverage,
            [
                algRunner.RMSA,
                algRunner.Break,
                algRunner.RmDupl,
                algRunner.RmDangle
                ],
            context,
            returnError=True,
            snap=snap,
            minArea=minArea,
            feedback=multiStepFeedback
            )
        #remove duplicated features
        multiStepFeedback.setCurrentStep(6)
        multiStepFeedback.pushInfo(self.tr('Removing duplicated features from {layer}...').format(layer=coverage.name()))
        algRunner.runRemoveDuplicatedFeatures(
            inputLyr=cleanedCoverage,
            context=context,
            onlySelected=False,
            attributeBlackList=attributeBlackList,
            excludePrimaryKeys=excludePrimaryKeys,
            ignorePK=ignorePK,
            ignoreVirtual=ignoreVirtual
        )
        #merging lines with same attributes
        multiStepFeedback.setCurrentStep(6)
        multiStepFeedback.pushInfo(self.tr('Merging lines from {layer} with same attribute set...').format(layer=coverage.name()))

        multiStepFeedback.setCurrentStep(7)
        multiStepFeedback.pushInfo(self.tr('Updating original layers...'))
        layerHandler.updateOriginalLayersFromUnifiedLayer(
            [inputLyr],
            coverage,
            feedback=multiStepFeedback,
            onlySelected=onlySelected
            )

        return {self.INPUTLAYERS : inputLyrList}