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

PolyGeo
  • 65,136
  • 29
  • 109
  • 338
Mahmoud Adel
  • 111
  • 10

1 Answers1

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}





Mahmoud Adel
  • 111
  • 10