I am looking to autogenerate consecutive numbers per group by an attribute form default expression whilst completing a survey using QField for QGIS.
Requirements:
- A job number will only be created when a separate column, "SpanClear" = No.
- I also require the job numbers to reset to 1 for each unique group in the column named "CircuitID".
- As this is a survey to be completed in QField, the attributes start off empty/null and will be populated on a mobile device
- Each time a job is identified (by "SpanClear" = 'No'), the jobs should be consecutively numbered, 1, 2, 3… etc.
- I require an expression to add in the default value of the attributes form (if there is a better way, please let me know)
I have searched through many previous questions and although there are workarounds using plugins, virtual layers and tools in QGIS, I require an expression so it will work in QField.
I want to automate the numbering to reduce chances of errors during surveying.
The expression I have created so far is:
CASE
WHEN "SpanClear" = 'No'
AND
"JobNo" is NULL
THEN $id=array_first(array_agg($id, "CircuitID"))
WHEN "JobNo" is not NULL
THEN array_agg((maximum("JobNo")+1),"CircuitID")
ELSE
NULL
END
The above expression names the first job per group "1", but any following jobs are set to "0".
Is what I am asking for even possible through an expression? I'm not a developer so I may have missed something obvious.
How it will look in QField
How the current expression is autopopulating
How the expression looks in Attributes Form
Best Answer
I am not sure what you meant to achieve with the
$id=array_first(array_agg($id, "CircuitID"))
part of your expression, as that's what is returning the1
and0
values.It's checking whether the current feature's
$id
value matches the first$id
value in the sameCircuitID
group (according to no particular order...). If it does - it returns a1
(true), if not, a0
(false). Only one feature can result in a match, hence why you start with one1
and all the rest in thatCircuitID
group have a value of0
.I presume you meant to use that as a condition for another expression that would generate the incremental value, but because you preceded it with
THEN
- it's actually returning the check itself.This default value expression in the
JobNo
widget will get you started:The first 3 lines should be self-explanatory, but here is what is happening with line 4 working inside out:
maximum()
function. You don't need to use thearray_agg()
function here asmaximum()
can already aggregate/group by within the function itself. This finds the maximum value forJobNo
within the group that has the sameCircuitID
as your current feature.The second
coalesce()
function. Checks if the result of step 1 isNULL
, and if it is, returns0
instead. This is for situations where there is nomaximum()
because you are entering a newCircuitID
(so no existing features to group), or the existingCircuitID
group has not been updated (so allJobNo
values in that group areNULL
and a maximum cannot be calculated).+1
- this increments the result of step 2 (which is either the maximumJobNo
for yourCircuitID
group, so you go one up, or it's 0, so you start your sequence at 1).The first
coalesce()
function. Checks ifJobNo
already has a value in it - if it does, then it leaves it alone. Otherwise, it applies the above sequence. This is so that if you check 'Apply default value on update' in the widget, and you go back to a feature that already has aJobNo
applied (say job number 2 out of a current max of 4 in thatCircuitID
group), if you update a completely unrelated field like comments, it doesn't recalculate theJobNo
(overwrite the existing value of 2 to 5, the new maximum + 1).Make sure your form is properly designed so you make sure
CircuitID
andSpanClear
cannot be null for a form to be saved.You may need to tweak the expression depending on how you want your form to behave when you go back and edit records.
One option may be to disable 'Apply default value on update' so that once the
JobNo
is calculated the first time, it is locked in place.But what if you initially said
SpanClear = No
for a record and now you want to revise that toSpanClear = Yes
(or vice-versa?)If it already had a
JobNo
do you want to retain it overwrite it withNULL
? If the latter, do you want all the otherJobNo
values in thatCircuitID
group to readjust (in which case you'll need a virtual field)? or are you ok with there being a gap in the sequence? etc.