Survey123 – Progress / Increment bar

Let’s kick the year off with something special – A customized progress /  increment bar for Survey123!

For a recent survey that I designed I wanted to give the user visible feedback of a risk factor calculation. Have a look at the video below to see how striking the end result is:

Let’s break the Progress / Increment Bar down into it’s capabilities:

  1. A color ramp to visualize the score (similar to the score distress bar)
  2. A progress increment indicator (□□□□□□□□□□)
  3. Expand and contract the bar according to the score

How was this achieved? Let’s look at the components needed to make this work:

Color Ramp

The color ramp changes according to the score, which runs from 0 to 10 in this case. We could sit down and pretend to be graphic artists and create a color ramp from scratch, or we can use one of the handy websites which creates beautiful color ramps for us!

Let’s head to RGB Gradient Generator or any RGB gradient generator of your choosing.

Now choose your start color and your end color for your ramp (in my case green to red) and choose the number of steps required (in this case 11, which corresponds with our allowable score values):


Next you need to choose a generated gradient and copy the gradient indexes and corresponding hex values to Notepad++ in order to create a CSV file which will act as a color value lookup which is based on the score calculated:


Now we have a handy way to color the bar according to the score calculated. To retrieve the color for each score, we simply have to use the pulldata() function of Survey123:

${color} = pulldata('ColorLookup', 'hexvalue', 'colorvalue', string(int(${score})))

In order to use this hex color we need to use some html magic:

<font color="#',${color},'">

Progress Indicator

The progress indicator is actually a string of HTML Unicode characters (UTF-8).

Let’s go shop for a pair of Unicode characters to use for the progress bar at UTF-8 Geometric Shapes

You can play around with the various character options but in essence you need a pair of Unicode characters; one to show up as “filled” and colored in, and one that seems to be “empty”:


I found it useful to add the character pair strings to a CSV file since it abstracts the solution:


In the CSV file I simply Copied & Pasted each character 10 times to make two strings that are each 10 characters long. Item 1 in the CSV then represents the filled in section of the progress bar (e.g. ■■■■■■■) and Item 2 represents the “empty” part of the progress bar (e.g □□□).

We now have two strings of 10 characters each and we can access them in our survey with the pulldata() function:

${barString} = pulldata('CharStringLookup', 'textvalue', 'item', '1')
${emptyString} = pulldata('CharStringLookup', 'textvalue', 'item', '2')

Expanding and Contracting the Bar

Now we can color our progress bar and we can visually differentiate between the “filled” in part and the “empty” part of the progress bar by using our Unicode character pair. All that is left is to cut our strings to size (according to the score e.g. 1/10) and combine and color it according to the color ramp already calculated.

Our score in the survey can run from 0 to 10 so the progress bar should mimic that by filling up from 0 to 10. We can achieve this by using the SubStr(<string>,<start>,<end>) function on each of our character strings. When the filled in part expands, the empty part should contract, so there is an inverse relation between their lengths.

Using two substring functions we can easily cut our two strings to size:


Now all we need to do is to set the color of the filled in section to our ramp color and then concatenate the strings together with the concat() function:

concat('<font color="#',${color},'">', 

substr(${barString},0,${scorelength}), '<font color="black">',

substr(${emptyString},0,${scoreinvertlength}),'<br>',string(${score}), ' / ' ,${scoremax})

The second font color setting colors the empty part of the progress bar in black for a nice contrast in colors.

As always, feel free to use and adapt as needed!

The source files for demonstration can be found here:  Zip file


Survey123 with a single attribute for repeating generic questions (instead of multiple attributes); and an auto-incremented list!

Example Requirement:


You are being tasked with creating a Disaster Risk Assessment form (as above) which requires the field worker to capture the risk factor associated with 14 Hazard Items:

  1. Avalanche
  2. Animal disease outbreak
  3. Drought
  4. Earthquake
  5. Epidemic
  6. Flood
  7. Hurricane
  8. Landslide
  9. Pandemic
  10. Tornado
  11. Tsunami
  12. Volcanic eruption
  13. Wildfire
  14. Winter storm

Each of these Hazard Items require the field worker to assess the risk factors of the hazard as follows:

  1. Affected Area (Very small=1, Small=2, Medium=3, Large=4, Very large=5)
  2. Probability (0% chance=1, 30% chance=2, 60% chance=3, 80% chance=4, 100% chance=5)
  3. Frequency (> 20 years = 1, 1 to 20 years = 2, Annually = 3, Monthly = 4, Weekly = 5)
  4. Predictability (100% Predictable = 1, Fairly Accurate to Predict = 2, 50/50 predictable = 3, Slightly Predictable = 4, Cannot Predict = 5)
  5. Magnitude (Low = 1, Low to Medium = 2, Medium = 3, Medium  to High = 4, High = 5)

A hazard score is calculated with the above risk factors as follow:

Score = Affected Area*0.5 + Probability + Predictability + Magnitude*1.5 + Frequency

Survey / Feature Class Design:

Having worked with Survey123 before we might be tempted to jump into it and simply create 14 questions, one for each Hazard Item listed in the requirement. The number of attributes quickly escalates however, since we also need to capture the associated Risk Factor for each Hazard Item – which results in a LOT of attributes! That doesn’t sound like a good design anymore, does it?

If we first designed this Feature Class in ArcGIS Pro we would probably design it simpler and lighter; perhaps a design like the one below, where each hazard item is not a separate attribute but a generic field which stores the Hazard Item descriptions:


That looks better, but how do we marry these two designs and also keep in mind the “limitations” of Survey123, seeing that Survey123 only allows one answer per “question”? An alternative would be to use a repeat section in the survey. A repeat section would give us the ability to add each Hazard Item as a (related) record whilst saving the Risk Factors in attributes.

Good, a Repeat Section it is… but how do we make it easy for the hapless field worker to capture 14 attributes and then still expect him/her to remember which hazards have been captured and which ones haven’t? It would be downright cruel to have the user choose the hazard item from a drop-down, like this (mistakes will happen!):


Let us improve on this design.

It would be great if the field worker didn’t have to remember which hazard is next in the sequence, so if we can add an auto-incremented hazard list to the design it would be great! It would also be nice if the field worker could see some kind of progress as the survey is completed – for instance Hazard 5 of 14 captured.

Luckily Survey123 allows us to do all of these things. Look at the final design in action:


This solution comprises:

  • An auto-incremented Hazard Item (leaving no room for mistakes!)
  • An incremented counter showing the capturing progress (for example 1 of 14)
  • A clean database design that looks like this:SchemaS123
  • A happy field worker!

So how did we achieve this design?

In the repeat section of the survey, add a counter variable (count1) that we use for the increment:

calculate count1 count1 1
calculate counter Counter once(count(${count1}))

The count() function does a count of the number of repeats by simply counting all the count1 values. The once() function ensures that the count calculation only happens once, otherwise we will have an issue when the user traverses backwards through the list and the count() is recalculated.

Now, for the magic bit…

Create a CSV file with all the Hazard Items and their indexes in it. The file will look like this:


Now all we need is to hook up our incremental index (the ${Counter}) with the Hazard Item. This is done with the pulldata() function as follow, where HazardEntryLookup is our CSV file:

pulldata('HazardEntryLookup', 'hazard', 'hazardindex', string(${counter}))

Now, all that is left is to enable the 1 of 14, 2 of 14, etc functionality. This is done by specifying that the Repeat count should not exceed 14 (our number of hazard items). So setting the repeat_count option for the Repeat sorts that out nicely!

As always, here is the complete set of files used for this demonstration, feel free to use an adapt as necessary:

Source files

For my upcoming Blog I am going to show how to make an interactive progress / risk visualization bar in Survey123! Here is a sneak peek of the end result:


Mathematically Verifying South African ID Numbers with Survey123

This blog post describes how South African ID numbers can be verified mathematically in Survey123. South African ID numbers have the following format:


YYMMDD : Date of birth.
G  : Gender. 0-4 Female; 5-9 Male.
SSS  : Sequence No. for DOB/G combination.
C  : Citizenship. 0 SA; 1 Other.
A  : Usually 8, or 9
Z  : Control digit

The most challenging part of verifying the ID number is the control digit which is calculated by using the Luhn algorithm – this will be the focus of this blog post.

The best way to tackle complex mathematical functions in Survey123 is to break it up into separate mathematical calculations and using calculated fields:

The check digit is the last digit of the SA ID number so it can be retrieved with the following function: substr(${idnr}, string-length(${idnr}) – 1, string-length(${idnr})) where ${idnr} refers to the captured ID number.

Once you have an understanding of the substr() function the rest of the calculations used to verify the ID number is pretty much straight forward.

The survey’s XLSForm file can be found here (and can be freely used): SA Id number

  1. Copy the file to your downloads folder
  2. Open Survey123 Connect
  3. Create a New Survey and base it on an existing file
  4. Choose the Excel file that you have downloaded
  5. Your survey will be generated


  1. The SA ID Number does not indicate if a user was born in 19yy or 20yy so both options are catered for – with a logic test to see if the birth date is in the future (age not greater than zero)
  2. Race is no longer indicated in the SA ID Number