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.

8
  • 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 9, 2020 at 9: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 9, 2020 at 9: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 9, 2020 at 9:09
  • 1
    @Taras Yes, that's what I want
    – Tony Pasca
    Dec 9, 2020 at 9:10
  • @Tony Pasca, are you free to use Python as well, or maybe you are entitled to using the Field Calculator only?
    – Taras
    Dec 9, 2020 at 9:13

2 Answers 2

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

6
  • Thank you. This is what I want to do too. Is there any possibility to do this in the field calculator?
    – Tony Pasca
    Dec 9, 2020 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 9, 2020 at 13:15
  • @TonyPasca yes that should be no problem.
    – MrXsquared
    Dec 16, 2020 at 15:55
  • Can you guide me to do this?@MrXsquared
    – Tony Pasca
    Dec 16, 2020 at 16:08
  • @TonyPasca Thats not a discussion for comments. You can start by using the template provided by QGIS docs.qgis.org/3.4/en/docs/user_manual/processing/… (you will find this one as well in processing toolbox via "Create new script from template") or examples here on GIS SE, e.g. 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, 2020 at 16:17
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.

1
  • It worked well. Thank you very much for this solution.
    – Tony Pasca
    Dec 9, 2020 at 15:03

Your Answer

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

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