1

I am having trouble with a script.

The last script here in this link is working on QGIS 2.x and I tried to modify it to use it in QGIS 3.x but I can't till now

Merging adjacent lines in QGIS

I am trying till this moment this is the script in QGIS v2

##Lines=vector line

from qgis.core import *
import processing
def find_adjacent(selected_ids): # for finding adjacent features
    outlist = []
    outinds = []
    outset = set()                                  
    for j, l in enumerate(selected_ids):            
        as_set = set(l)                             
        inds = []               
        for k in outset.copy():
            if outlist[k] & as_set:
                outset.remove(k)
                as_set |= outlist[k]            
                                                
                inds.extend(outinds[k])
        outset.add(j)                   
        outlist.append(as_set)          
        outinds.append(inds + [j])      
    outinds = [outinds[j] for j in outset]      
    del outset, outlist 
    result = [[selected_ids[j] for j in k] for k in outinds]    
    return result   

layer = processing.getObject(Lines)     #get input layer from GUI
crs = layer.crs().toWkt()

# Create the output layer
outLayer = QgsVectorLayer('Linestring?crs='+ crs, 'snapped' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

already_processed = []
for feat in layer.getFeatures():
    attrs = feat.attributes()
    geom = feat.geometry()
    curr_id = feat["id"]
    if curr_id not in already_processed:
        query = '"id" = %s' % (curr_id)
        selection = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
        selected_ids = [k.geometry().asPolyline() for k in selection]       
        feedback.pushInfo("selected_ids")
        feedback.pushInfo(str(selected_ids))
        adjacent_feats = find_adjacent(selected_ids)
        feedback.pushInfo("adjacent_feats")
        feedback.pushInfo(str(adjacent_feats))
        for f in adjacent_feats:
            first = True
            for x in xrange(0, len(f)):
                geom = (QgsGeometry.fromPolyline([QgsPoint(w) for w in f[x]]))
                if first:
                    outFeat = QgsFeature()
                    outFeat.setGeometry(geom)
                    outGeom = outFeat.geometry()
                    first = False
                else:
                    outGeom = outGeom.combine(geom)
            outFeat.setAttributes(attrs)
            outFeat.setGeometry(outGeom)
            prov.addFeatures([outFeat])
        already_processed.append(curr_id)
    else:
        continue

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

and this is my attempt to modify the code in QGIS 3.x

from PyQt5.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsFeature,
                       QgsFeatureRequest,
                       QgsGeometry,
                       QgsPoint,
                       QgsProject
                       )
import processing


class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'
    def tr(self, string):
        return QCoreApplication.translate('Processing', string)
    def createInstance(self):
        return ExampleProcessingAlgorithm()
    def name(self):
        return 'myscript'
    def displayName(self):
        return self.tr('My Script')
    def group(self):
        return self.tr('Example scripts')
    def groupId(self):   
        return 'examplescripts'
    def shortHelpString(self):
        return self.tr("Example algorithm short description")
    def initAlgorithm(self, config=None):
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Input layer'),
                [QgsProcessing.TypeVectorAnyGeometry]
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.OUTPUT,
                self.tr('Output layer')
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        source = self.parameterAsSource(
            parameters,
            self.INPUT,
            context
        )

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

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

        if sink is None:
            raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))



        def find_adjacent(selected_ids): # for finding adjacent features
            outlist = []
            outinds = []
            outset = set()                                  
            for j, l in enumerate(selected_ids):           
                as_set = set(l)                            
                inds = []               
                for k in outset.copy():
                    if outlist[k] & as_set:
                        outset.remove(k)
                        as_set |= outlist[k]            
                                                       
                        inds.extend(outinds[k])
                outset.add(j)                   #1st iteration outset = {0}
                outlist.append(as_set)          #1st iteration outlist = { l }
                outinds.append(inds + [j])      #1st iteration outinds = [0]
            outinds = [outinds[j] for j in outset]      
            del outset, outlist 
            result = [[selected_ids[j] for j in k] for k in outinds]    
            return result   
        # Compute the number of steps to display within the progress bar and
        # get features from source
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()
        already_processed = []
        for current, feature in enumerate(features):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break

            attrs = feature.attributes()
            geom = feature.geometry()
            curr_id = feature["strahler"]
            if curr_id not in already_processed:
                query = '"strahler" = %s' % (curr_id)
                selection = source.getFeatures(QgsFeatureRequest().setFilterExpression(query))
                selected_ids = [k.geometry().asMultiPolyline() for k in selection]      #here I tried asPolyline as the QGIS v2 but it tells me "TypeError: MultiLineString geometry cannot be converted to a polyline. Only single line or curve types are permitted." so I made it as MultiPolyline 
                #feedback.pushInfo(str(selected_ids))
                adjacent_feats = find_adjacent(selected_ids[0][0])
                feedback.pushInfo(str(adjacent_feats))
                for f in adjacent_feats:
                    first = True
                    for x in range(0, len(f)):
                        geom = (QgsGeometry.fromPolyline([QgsPoint(w) for w in f[x]])) 
                        if first:
                            outFeat = QgsFeature()
                            outFeat.setGeometry(geom)
                            outGeom = outFeat.geometry()
                            first = False
                        else:
                            outGeom = outGeom.combine(geom)
                    outFeat.setAttributes(attrs)
                    outFeat.setGeometry(outGeom)
                    sink.addFeatures([outFeat])
                already_processed.append(curr_id)
            else:
                continue

        # Add the layer to the Layers panel
        return {self.OUTPUT: sink}

the output of this code is merging all features that had the common feature in one feature not the adjacent ones ---> because of asMultiPolyline

1
  • Please edit your question and include your QGIS 3 attempt and any exceptions (as text, not screenshots).
    – user2856
    Aug 18, 2020 at 22:22

1 Answer 1

1

Finally, I did it Wowww!! After many attempts and using the awesome line to debugging in pyQgis feedback.pushInfo() that helped me a lot to understand the logic, I succeeded

from PyQt5.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                      QgsFeatureSink,
                      QgsProcessingException,
                      QgsProcessingAlgorithm,
                      QgsProcessingParameterFeatureSource,
                      QgsProcessingParameterFeatureSink,
                      QgsFeature,
                      QgsFeatureRequest,
                      QgsGeometry,
                      QgsPoint,
                      QgsProject
                      )
import processing


class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):


   INPUT = 'INPUT'
   OUTPUT = 'OUTPUT'

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

   def createInstance(self):
       return ExampleProcessingAlgorithm()

   def name(self):
   
       return 'myscript'

   def displayName(self):
    
   
       return self.tr('My Script')

   def group(self):
    
       return self.tr('Example scripts')

   def groupId(self):
      
       return 'examplescripts'

   def shortHelpString(self):
    
       return self.tr("Example algorithm short description")

   def initAlgorithm(self, config=None):
     
       self.addParameter(
           QgsProcessingParameterFeatureSource(
               self.INPUT,
               self.tr('Input layer'),
               [QgsProcessing.TypeVectorLine]
           )
       )

      
       self.addParameter(
           QgsProcessingParameterFeatureSink(
               self.OUTPUT,
               self.tr('Output layer')
           )
       )

   def processAlgorithm(self, parameters, context, feedback):
     
       source = self.parameterAsSource(
           parameters,
           self.INPUT,
           context
       )

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

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

       # Send some information to the user
       feedback.pushInfo('CRS is {}'.format(source.sourceCrs().authid()))

      
       if sink is None:
           raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))



       def find_adjacent(selected_ids): # for finding adjacent features
           outlist = []
           outinds = []
           outset = set()                                  #create new empty set object
           #feedback.pushInfo("selected Ids before enumerate----------------")
           #feedback.pushInfo(str(selected_ids))
           for j, l in enumerate(selected_ids):
               #feedback.pushInfo("j ---------")
               #feedback.pushInfo(str(j))           #index
               #feedback.pushInfo("l ---------")
               #feedback.pushInfo(str(l))           #item
               as_set = set(l)
               #feedback.pushInfo("as_set--------------")
               #feedback.pushInfo(str(as_set))
               inds = []               
               for k in outset.copy():
                   #feedback.pushInfo("hi inside outset.copy()")
                   #feedback.pushInfo(str(k))
                   #feedback.pushInfo(str(as_set))
                   if outlist[k] & as_set:
                       outset.remove(k)
                       as_set |= outlist[k]            #as_set= as_set|outlist[k] --> concatenate as_set and outlist in one object [{outlist[k]},{as_set}]
                       #feedback.pushInfo("as_set-----in if--------")
                       #eedback.pushInfo(str(as_set))
                       inds.extend(outinds[k])         #extend means --> take the value of outinds[k] and make inds equal this vlaue
               outset.add(j)                   #1st iteration outset = {0}
               #feedback.pushInfo("outset -----in iteration--------")   
               #feedback.pushInfo(str(outset))                              #outset in iteration
               outlist.append(as_set)          #1st iteration outlist = [{<QgsPointXY: POINT(77.50014668735001067 17.55122868859714913)>, <QgsPointXY: POINT(77.3797785331275918 17.55958329056117151)>}]
               #feedback.pushInfo("outlist -----in iteration--------")
               #feedback.pushInfo(str(outlist))                             #outlist in iteration
               outinds.append(inds + [j])      #1st iteration outinds = [[0]]
               #feedback.pushInfo("outinds -----in iteration--------")
               #feedback.pushInfo(str(outinds))                             #outinds in iteration
               #feedback.pushInfo("inds-----------in iteration------------")
               #feedback.pushInfo(str(inds))                                #inds in iteration
           #feedback.pushInfo("outset-----------------------")
           #feedback.pushInfo(str(outset))                                   #outset
           #feedback.pushInfo("outlist-----------------------")
           #feedback.pushInfo(str(outlist))                                  #outlist
           #feedback.pushInfo("outinds-----------------------")
           #feedback.pushInfo(str(outinds))                                  #outinds
           
           outinds = [outinds[j] for j in outset]      
           set_list=[]
           for item in outset:
               set_list.append(item)
           list_points=[]
           for i in set_list:
               list_points.append(outlist[i])
           result_list_points=[]
           for k in list_points:
               item=[]
               for i in iter(k):
                   item.append(i)
               result_list_points.append(item)    
           result = [[selected_ids[j] for j in k] for k in outinds]  
           #feedback.pushInfo("result -----------------")
           #feedback.pushInfo(str(result))
           return result

       # Compute the number of steps to display within the progress bar and
       # get features from source
       total = 100.0 / source.featureCount() if source.featureCount() else 0
       features = source.getFeatures()
       already_processed = []
       
       for current, feature in enumerate(features):
           # Stop the algorithm if cancel button has been clicked
           if feedback.isCanceled():
               break

           attrs = feature.attributes()
           geom = feature.geometry()
           #feedback.pushInfo("geom-----------------")
           #feedback.pushInfo(str(geom))
           curr_id = feature["id"]
           
           if curr_id not in already_processed:
               query = '"id" = %s' % (curr_id)
               selection = source.getFeatures(QgsFeatureRequest().setFilterExpression(query))
               #feedback.pushInfo("filtered features with "+curr_id)
               #feedback.pushInfo(str(selection))
               selected_ids = []
               for feat in selection:
                   #feedback.pushInfo("feature----------------")
                   #feedback.pushInfo(str(feat))
                   for i in feat.geometry().asMultiPolyline():
                       #feedback.pushInfo(str(i))
                       selected_ids.append(i)
               #feedback.pushInfo("selected_ids----------------------------of "+curr_id)
               #feedback.pushInfo(str(selected_ids))
               #selected_ids = [k.geometry().asMultiPolyline() for k in selection]      #[repeat first term before 'for' with no of elements in selection ]
               #feedback.pushInfo("filtered features with "+curr_id)
               adjacent_feats = find_adjacent(selected_ids)
               #feedback.pushInfo("adjacent_feats------------------------")
               #feedback.pushInfo(str(adjacent_feats))
               for f in adjacent_feats:
                   first = True
                   
                   for x in range(0, len(f)):
                       geom = (QgsGeometry.fromPolyline([QgsPoint(w) for w in f[x]])) 
                       if first:
                           outFeat = QgsFeature()
                           outFeat.setGeometry(geom)
                           outGeom = outFeat.geometry()
                           first = False
                       else:
                           outGeom = outGeom.combine(geom)
                   outFeat.setAttributes(attrs)
                   outFeat.setGeometry(outGeom)
                   sink.addFeatures([outFeat])
               already_processed.append(curr_id)
           else:
               continue

       # Add the layer to the Layers panel
       return {self.OUTPUT: sink}





Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.