2

How can I join these lines depending on the Trigger field. The condition is as follows, the fields must be ordered in ascending order after the fid field from 1 to 68. Thus, I want to join all my lines starting with the first one that has trigger 1 and the next ones that have the value 0 and stop at the first value with 1 and start again.

Edit: I understand. But how can I solve this problem? How to join these lines? Trigger refers to the angle. If it is 0 then the angle between 2 segments is less than x degrees, and if it is 1 the angle is greater than x degrees. So, I want if the first line is not a trigger, I want to join it with the first line that is the trigger.

Tony Pasca
  • 167
  • 5
  • 2
    Add a new field, create same id for the group of lines you want to merge, and then run dissolve tool using the unique id field. The first 3 lines give them id=1 and the seconds three lines give them id=2 and so on, then run dissolve tool using this id field. – ahmadhanb Dec 09 '20 at 09:03
  • @ahmadhanb, I think you misinterpreted the question. The OP want to merge lines from up to down (from lower to bigger "fid") where "Trigger" starts with 1 and over all 0s until a new 1 appears (this condition forms a group). So, the So, the real question is how to create a new grouping field rather than merging or dissolving which is easy. – Taras Dec 09 '20 at 09:09
  • How do you create the same id for the group of lines I want to merge? Is there a function? I would not want to do this manually, because this layer is a test one, I want to apply this on a layer with a lot of inputs. – Tony Pasca Dec 09 '20 at 09:09
  • 1
    @Taras Yes, that's what I want – Tony Pasca Dec 09 '20 at 09:10
  • @Tony Pasca, are you free to use Python as well, or maybe you are entitled to using the Field Calculator only? – Taras Dec 09 '20 at 09:13
  • @Taras I want to integrate this step into a graphical modeler as part of a more complex model. But I got stuck at this point. – Tony Pasca Dec 09 '20 at 09:15
  • I would like it to be a function in the Field Calculator, or several processing steps. I don't know if I can integrate a python script into my model. – Tony Pasca Dec 09 '20 at 09:21
  • Unfortunately, this link does not help me solve the problem. – Tony Pasca Dec 09 '20 at 09:49

2 Answers2

3

To give you an idea how this could be done, here is a solution in Python to add a new groupid field, you can use to dissolve your lines:

# Change your settings here:
layername = 'line' 
triggerfield = 'Trigger' # field must already exist
groupfieldname = 'mygroup' # will be added if does not exist
newlineindicator = 1

no changes needed below

layer = QgsProject.instance().mapLayersByName(layername)[0] # get the layer layer.startEditing() # start editing the layer layer.dataProvider().addAttributes([QgsField(groupfieldname,QVariant.Int)]) # add a group field layer.commitChanges() # save new added field to prevent crash when searching for it later layer.startEditing() # start editing the layer again groupid = 0 # initialize groupid fieldindex = layer.fields().indexFromName(groupfieldname) # get fieldindex of groupfield

for feat in layer.getFeatures(): # itereate over layer if feat[triggerfield] == newlineindicator: # if a new trigger appears, increase groupcounter groupid += 1 attrs = { fieldindex : groupid } # prepare attributes layer.dataProvider().changeAttributeValues({ feat.id() : attrs }) # assign current groupid to current feature layer.commitChanges() # save changes

Result:

enter image description here

MrXsquared
  • 34,292
  • 21
  • 67
  • 117
  • Thank you. This is what I want to do too. Is there any possibility to do this in the field calculator? – Tony Pasca Dec 09 '20 at 13:09
  • @TonyPasca yes, you can transform this into a custom function you can then use in field calculator. But you wont be able to use this custom function within a complex graphical model as stated in my previous comment, because you need to have access to the layername so you can iterate over the whole layer. Anyhow, this function would be more inefficient in fieldcalculator since you iterate over the whole layer as many times as features exisit in this layer. – MrXsquared Dec 09 '20 at 13:15
  • @TonyPasca yes that should be no problem. – MrXsquared Dec 16 '20 at 15:55
  • Can you guide me to do this?@MrXsquared – Tony Pasca Dec 16 '20 at 16:08
  • @TonyPasca Thats not a discussion for comments. You can start by using the template provided by QGIS https://docs.qgis.org/3.4/en/docs/user_manual/processing/scripts.html#best-practices-for-writing-script-algorithms (you will find this one as well in processing toolbox via "Create new script from template") or examples here on GIS SE, e.g. https://gis.stackexchange.com/a/363630/107424 where you can find the same thing as processing script as well as pure python console script. If you get stuck, feel free to open a question about. – MrXsquared Dec 16 '20 at 16:17
  • Can you help me a little please? @MrXsquared https://gis.stackexchange.com/questions/382410/convert-scripts-for-processing-toolbox – Tony Pasca Dec 18 '20 at 12:24
3

There is a way to directly create a field value using a QGIS expression in the field calculator to group together all those features that you want to join. Based on the value of this field, you than can use the function Aggregate to join features with the same value (see second screenshot below). Bots steps should be easy to automate with a model.

Taking your data structure and field names, the expression looks like this (see below for explanation how the expression works):

array_last (
    array_sort (
        array_foreach (
            array_remove_all ( 
                array_agg ( 
                    if ( 
                        "trigger" = 1, 
                        "fid" , 
                        0
                    ) 
                ) ,
            0) ,
        if (
            if ( 
                "trigger" = 1, 
                "fid" , 
                0 
                ) = @element, 
                @element, 
                if (
                    "fid" > @element ,  
                    @element,0
                )
        )
        ) 
    )
)

enter image description here

Than you can use this field together with the function Menu Processing / Toolbox / Aggregate to joint these features together.

enter image description here

To explain the idea behind the expression: for better understanding, let's call the "starting-segment" of the joined line you finally want to get the "head" - and the subsequent segments that are joined to it the "tails".

  1. Get the fid-values of the heads, let's call it "group-ids". Final goal is to assign each tail-segment to a head (or: each tail should be assigned the group-id value of the next smaller head: 2 and 3 should be assigned to 1, 5 and 6 to 4 etc.) a) If the trigger is 1 (head), get the value of the id: this is the group-id. Else (for tails) set a value of 0. b) Aggregate these values creating an array. c) Remove all 0 values from the array. We thus get an array of group-ids (=fid of heads): [1,4,7,9,12]. We finally want to assign every feature to one of these values from the array.

  2. Now iterate over all features of the layer with each of these group-ids from the array with the expression array_foreach: a) For the features with trigger = 1 (heads) we have no problem, these already have the right group-id (corresponding to their fid) b) In the other cases (tails, trigger = 0), we must assign new values. We thus compare each input value from the array (list of all group-ids) with the fid and keep the group-id if it is smaller than the fid (as we want to assign to the next smaller value). Otherwise we set it to 0. So in no case there will result a value from the array that is bigger than the fid.

  3. Sort the array to be sure we have the values in the right order (smallest to biggest) - to remove the final zeros from the last postition (see screenshot below). Without setting a sort order, we get the default value ascending=true.

  4. Get the last value from the sorted array, thus the last (biggest) group-id that is (remember step 2b) in no case bigger than the fid AND (in case trigger=1) is equal to the fid (step 2a) AND is always a number from the array we created in 1.

See a screenshot for the intermediate results of step 1 and 2:

enter image description here

Now it's easy to use the aggregate function with the field created as described above.

Babel
  • 71,072
  • 14
  • 78
  • 208