Skip navigation
All Places > ArcGIS Survey123 > Blog > Author: dlengtonesri-southafrica-esridist

For the 2019 Esri Southern African User Conference I challenged myself and looked for a solution to a requirement that one of our clients have.

In a nutshell the client wants a survey that follows a preset workflow, for this example we will create a solution for a workflow that involves inspections done at regular intervals when a house is being built.


Survey Worflow

Let's consider the following status values for a building inspection survey:


Here are some user requirements for this survey:

  • The initial status is always Excavation
  • The inspector should not be able to skip a status
  • The inspector can only move on to the next status in the chain, not backwards
  • Inspection questions for a specific status can only be edited when that status is the active status


How will we go about forcing this workflow in Survey123?

There are some hidden challenges in getting this to work. The biggest hurdle is that in Survey123 you cannot compare a field to it's own or previous value, so you cannot tell Survey123 the next status can only be Foundation when the current status is Excavation. This has lots of design implications and one would be tempted to stop looking for a solution right here. But this is where you need to think a little bit out of the box...


So how does the end result work? Lets take a look:




In the above screenshot the following can be seen:

  • The survey initialises with only the Excavation status being visible in the select_one question. The inspector is not able to select any other status.
  • The survey only has the Excavation Checklist enabled
  • The inspector cannot skip a status or go back to any other status
  • The inspector cannot complete any other checklist of the survey - only the active status' checklist


The Solution


Lets look at the bits and pieces to make this work:


We need a way to only allow the inspector to choose one "next" status and this needs to be based on the "current" status. So we need a select_one that is filtered according to the "current" status:


Notice that we added a filter column called "prevval" to the choices for the "level" list. In this example "level" refers to the status of the inspection. Also you will notice that the levels run from 1 to 6 and the corresponding "prevval" filter is one level behind. So if the "prevval" or current status is zero (0) then the list will filter and only show Excavation as an option.


Ok, time for the interesting part of this solution. Since we cannot filter the list called "level" with itself we need to find a way of retrieving the inspection status into a new variable. This is done by quering the current status value from field "status" from the feature layer with a JavaScript pulldata function:

We do the max function so that for newly created records we start with a level of zero(0) - the pulldata returns nothing since there isnt a record created yet to query so we convert it to a 0 with the max function.


Basically this is it! There are some other work to be done, such as writing the JavaScript file to query the data from your hosted Feature Layer. If there's enough interest I will post the source files after cleaning it up a bit.

After some valuable inputs I have reworked this example to cater for the GOTCHA where a user deletes an image in the middle of the sequence of photos - lets hope its fool proof now! It definitely isnt perfect but hopefully it gives someone else out there an idea of what can be done - and with a little extra effort make it completely fool proof...


Do you have the requirement that your field workers might want to take/attach multiple photos to a survey but you dont want to use a repeat to capture them, since you know its harder to access those photos later?  Just adding multiple image fields to your survey leaves your user interface cluttered like this:


Thats pretty ugly right?


One solution to unclutter the interface is to show the second photo field only once a user has already taken a photo. And after the user took the second photo, the third photo field becomes available... etc etc...


That can be achieved easily by setting the relevant field of each photo field with a calculation such as checking the value of the previous photo field:



The trick to get this to work when a user decides to delete a photo in the middle of a sequence of photos already added is to do the relevant calculation only once with the help of the once() function.


Doesn't this look much less cluttered:


After taking the first photo, the second one is opened up:

Do you want to create a break-out link from your survey to Twitter, WhatsApp, eMail etc?


Here are a few examples of how the HTML tags should look like to achieve this (just substitute your fields into the examples and paste it into a survey note field):


 Open the device's default mapping application (such as Google Maps) with the coordinates of the survey:

concat('<a href="geo:', ${Lat}, ',' , ${Long}, concat('">Display on the Map</a>'))


 Open Google Maps with driving directions to coordinates:

concat('<a href="', ${Lat}, ',' , ${Long}, concat('&amp;ll=">Directions</a>'))


 Open the device's default email application with a to email address:

concat('<a href="mailto:', ${Email},concat('">', ${Email}))


 Create a Tweet from your survey:

concat('<a href=" tweet sent from Survey123! #ArcGIS','">Create a Tweet</a>')


 Create a WhatsApp message from your survey:

concat('<a href="whatsapp://send?text=This is a WhatsApp message from Survey123!">Create a Whatsapp message</a>')


 Open Collector with a specific web map:

${Collector_Intent} = concat('arcgis-collector://?itemID=68085fc05c401ca5716e5677a6128c')
concat('<a href="',${Collector_Intent},'">Open Collector</a>')

(first create a calculated field named Collector_Intent and then use that field in the second calculated field)


 Open a specific survey (with parameters) from Survey123:

${S123_Intent} = concat('arcgis-survey123://?itemID=887d507cee48d18ad3cdfaf9f081e6&field:Service_Rating=1')
concat('<a href="',${S123_Intent},'">Open specific survey</a>')

(first create a calculated field named S123_Intent and then use that field in the second calculated field)


 Open an SMS (with the coordinates in the body) from Survey123 (this works in Android - not tested on iOS):

concat('<a href="sms:','08212345678','?body=', ${Lat}, ',' , ${Long}, concat('">SMS</a>'))

(to get the coordinates - just use the pulldata() function)


 Open a local file (such as a PDF help file or an image located in your survey's media folder) from Survey123 - this works and has been tested on Android:

concat('<a href="file:///storage/emulated/0/ArcGIS/My Surveys/549c05bae0f14e388000486ce4de950c/esriinfo/media/Example.pdf" class="link external button button-raised button-fill color-blue">Demo</a>')

(549c05bae0f14e388000486ce4de950c will be different for each survey. It refers to the survey item id)



I will add more examples as the need arises 



This blog post provides a solution for:

    Capturing multiple locations per survey

    Calculating straight-line distances between a base location point in the survey and other repeating locations         captured


Consider the scenario where your survey must assist in the administration of new liquor licence applications and the law stipulates: New liquor premises must be located at least five hundred meters (500m) away from schools, places of worship, recreation facilities, rehabilitation or retreat centers, residential areas and public institutions.


For this requirement Survey123 can be a valuable tool and in this blog post I will show you how you can enable this functionality in a Survey123 form:


Step 1: Create a new survey with Survey123 Connect

Step 2: Create a geopoint field in the main survey which will be used to capture the location of the new licence premises (the base point or new_licence_location field in the survey below)

Step 3: Create a repeat section to capture the occurrences of nearby schools, places of worship etc. Each with a premise type and a geopoint (the nearby_location field in the survey below)


Your survey design should look similar to this:



Step 4: Now, split both the coordinates sets into their x,y pairs and convert them to radians:



Do the same for the nearby_location geopoint field:


Step 5: Use the X, Y (lat,long) pairs to perform the distance calculation (in meters) with the Haversine formula:

acos(sin(${gps_lat_end})*sin(${gps_lat}) + 
cos(${gps_lat_end})*cos(${gps_lat})*cos(${gps_long}-${gps_long_end}) ) * 6371000


After making some cosmetic enhancements (and adding the 500m stipulation) your survey should look like this:



The user can add multiple locations (Nearby Premises) at will in the repeat section and each will have it's own distance calculation to the base point (New Licence Location).

The Survey123 Excel Design file is attached to this blog. 

Feel free to use and adapt as you require!


A customized but "Out-of-the-Box" 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:

    A color ramp to visualize the score (similar to the score distress bar)

    A progress / increment indicator (■■■■■■■□□□)

    Expand and contract the bar according to the score


How was the end result achieved? Let's look at the three components needed to make this work:

1. Color Ramp

The color ramp changes from green to red according to the score entered, 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},'">

2. 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. 10/10 ■■■■■■) and Item 2 represents the "empty" part of the progress bar (e.g 0/10 □□□□□□□□□□).


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')

3. 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},'">',
   '<font color="black">', 
   string(${score}), ' / ' ,${scoremax})

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


The source files can be found here:  AGOL Resource


Thank you for reading this Blog!

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

Filter Blog

By date: By tag: