Find nearest geopoint

Synopsis of the problem. I have a list of predefined geopoints, which are previous sampling locations. These currently exist as an external csv but will become an entity list (let's call it samplepoints.csv). In the form, when the surveyor is in the field and opens and starts the ODK Collect form, I want the form to automatically identify the nearest point in samplepoints.csv and the distance to this nearest point

What I have done to solve it. The distance() function and this excellent sample form get me most of the way there. In particular the Basic Reverse Geocoding which is a very similar example to what I want to achieve. But here we define a distance threshold and check how many points are within that threshold. I can't have that threshold, I just need the nearest point.

The problem that remains. How do I develop this logic in the sample form

count(instance('pois')/root/item[distance(geometry, ${location}) < 50])
if (${nearby_count} = 1, instance('pois')/root/item[distance(geometry, ${location}) < 50]/name, '')
if (${nearby_count} = 1, instance('pois')/root/item[distance(geometry, ${location}) < 50]/label, '')

To give me the nearest point and that distance without the <50 or any <X distance threshold (and without the if (${nearby_count} = 1)

Another consideration is that I will be dealing with large numbers and increasing numbers of points. so a one-by-one solution would be suboptimal.

Any help would be great. I am sure that I have missed something obvious, but I am just not getting there

Throwing ideas at the wall-

  • Must the user be given the nearest point, or could they open a map and see the points as well as their location, then select the one that is closest (or most easily accessible, i.e. closest is across a river and travel distance via bridge is greater than one on the same side of the river)
    • You would probably have to filter the list of points somehow to prune it down if it is in the 1000s or map performance will suffer, could prune with a choice filter where distance < x.
  • while I guess you could progressively test counts at smaller and smaller distance < x tests until count = 1, (or larger and larger until it goes from 0 to 1) eg;
    • if(count(instance('pois')/root/item[distance(geometry, ${location}) < 50]) = 1, instance('pois')/root/item[distance(geometry, ${location}) < 50]/name, if (count(instance('pois')/root/item[distance(geometry, ${location}) < 20]) = 1, instance('pois')/root/item[distance(geometry, ${location}) < 20]/name, if (count(instance('pois')/root/item[distance(geometry, ${location}) < 10]) = 1, instance('pois')/root/item[distance(geometry, ${location}) < 10]/name, if (count(instance('pois')/root/item[distance(geometry, ${location}) < 5]) = 1, instance('pois')/root/item[distance(geometry, ${location}) < 5]/name, 'too many items within 5m'))))
  • If the count is often >1 and you can't find the sweet spot from the above, could you choice filter to a distance that will give you 1 to a small number of options, then the user can select each option in turn which will calculate the distance to the selected location and show it in a note, so after quickly tapping each one to view all distances they finally choose the one with the smallest distance and continue from there.
  • could you instead build a repeat with a dynamic count for the (total / pruned?) set of points, and within each repeat, pull the point, calculate the distance, then find the min of the nodeset and get the associated point details? Would be nasty with a huge number to calculate it this way though and hopefully the distances have no duplicates.
1 Like

@ahblake Many thanks for giving this so much thought. Based on this ideas and mapping out the workflow and use cases I have a beta solution which I am now testing. Goes a bit as follows:

  1. There is one site in the entities list that is <50m away, this is the normal use case. That site is automatically selected (user doesn't get a choice, but I may need to put in an escape channel)
  2. There are more than one site <50m away. An unusual case, but does exist. The user is given a map and asked to select from the pre-filtered sites.
  3. There are no sites wihtin 50m, but are sites within 1km which may be part of the same cluster. User creates a new site and can associate it with the cluster in which case the name and properties are read from the cluster. This creates a new entity
  4. A new site and cluster are created, this also creates a new entity

Will do the testing now and find all the ways that my logic falls down

I'm glad there was something useful in what i could provide! Hope it works out.