Skip to content

Conversation

@latin-panda
Copy link
Collaborator

@latin-panda latin-panda commented Dec 18, 2025

Closes #507

ODK XForms spec: https://getodk.github.io/xforms-spec/#events
User-facing docs: start-geopoint (odk:setgeopoint with odk-instance-first-load)

It's WIP - Questions to clarify:

  1. Does it need to show an indicator just like Collect does? (I think we decided not add it, but double-checking)

    Any time a survey with a start-geopoint question is opened in Collect, the enumerator will see a warning that the form tracks device location .... A location icon will be displayed in the Android status bar while the geolocation is being requested by Collect.

  2. The odk-new-repeat is fired when a new repeat instance is created. At that moment, geolocation is requested once. Subsequent repeat instances reuse this same geolocation reading, so all instances end up with the same location value. That's expected, correct?

  3. The background-geopoint is an odk:setgeopoint with xforms-value-changed, that, as per the specs:

    lets you automatically capture a single geolocation in geopoint format when the user changes the value of another question.

    3.1 The location is requested once, and subsequent value changes reuse the same geolocation reading (or empty string if no permissions given) for all questions. Is that correct? This is a lighter and less complex option than requesting the location every time a question value changes.

I have verified this PR works in these browsers (latest versions):

  • Chrome
  • Firefox
  • Safari (macOS)
  • Safari (iOS)
  • Chrome for Android
  • Not applicable

What else has been done to verify that this works as intended?

Displays error message when it cannot access the geolocation
Displays multiple messages in the top error banner
Tests all events except new repeat instance
test-all-events.mp4
Tests new repeat instance event
test-setgeopoint.mp4

Why is this the best possible solution? Were any other approaches considered?

Reuses the existing action structure, sets the value without re-evaluating the expression (since it comes from the geolocation provider), and reuses geopoint codecs for validation.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

Existing features will continue to work as usual with no changes.
Forms with setgeopoint will now request geolocation in the background, which may affect low-end phones by increasing resource usage (battery, CPU), as expected. The geolocation request stops after 20 seconds, in accordance with the specs.

Do we need any specific form for testing your changes? If so, please attach one.

There are 3 forms in the common package to test the feature. I also have a form that includes all events in one form:

Form: setgeopoint with events
<?xml version="1.0" encoding="utf-8"?>
<h:html xmlns="http://www.w3.org/2002/xforms" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa" xmlns:odk="http://www.opendatakit.org/xforms">
  <h:head>
    <h:title>combined-setgeopoint-test</h:title>
    <model>
      <instance>
        <data id="combined-setgeopoint-test">
          <required_text />
          <location_first_load />
          <location_instance_load />
          <trigger_text />
          <location_value_changed />
          <setvalue_target />
          <person jr:template="">
            <age />
            <location_new_repeat />
          </person>
        </data>
      </instance>
      <bind nodeset="/data/required_text" type="string" required="true()" />
      <bind nodeset="/data/location_first_load" type="string" readonly="true()" />
      <bind nodeset="/data/location_instance_load" type="string" readonly="true()" />
      <bind nodeset="/data/trigger_text" type="string" />
      <bind nodeset="/data/location_value_changed" type="string" readonly="true()" />
      <bind nodeset="/data/setvalue_target" type="string" readonly="true()" />
      <bind nodeset="/data/person/age" type="integer" />
      <bind nodeset="/data/person/location_new_repeat" type="string" readonly="true()" />
      <odk:setgeopoint ref="/data/location_first_load" event="odk-instance-first-load" />
      <odk:setgeopoint ref="/data/location_instance_load" event="odk-instance-load" />
    </model>
  </h:head>
  <h:body>
    <input ref="/data/required_text">
      <label>Required Text</label>
    </input>
    <input ref="/data/location_first_load">
      <label>Location (odk-instance-first-load)</label>
    </input>
    <input ref="/data/location_instance_load">
      <label>Location (odk-instance-load)</label>
    </input>
    <input ref="/data/trigger_text">
      <label>Trigger Text (change to trigger value-changed)</label>
      <odk:setgeopoint ref="/data/location_value_changed" event="xforms-value-changed" />
      <odk:setvalue ref="/data/setvalue_target" event="xforms-value-changed" value="/data/trigger_text" />
    </input>
    <input ref="/data/location_value_changed">
      <label>Location (xforms-value-changed)</label>
    </input>
    <input ref="/data/setvalue_target">
      <label>Setvalue Target</label>
    </input>
    <group ref="/data/person">
      <label>Person Repeat Group</label>
      <repeat nodeset="/data/person">
        <input ref="/data/person/age">
          <label>Age</label>
        </input>
        <odk:setgeopoint event="odk-new-repeat" ref="/data/person/location_new_repeat" />
        <input ref="/data/person/location_new_repeat">
          <label>Location (odk-new-repeat)</label>
        </input>
      </repeat>
    </group>
  </h:body>
</h:html>

What's changed

  • Introduces a geolocation provider interface in the engine and enables support for consuming it in the primary instance, which is later used for dispatching geolocation actions in createInstanceValueState.ts.
  • Reuses most of the action structure implemented for setValue
  • Adds scenario tests
  • Introduces a singleton geolocation service that operates independently without affecting other question types requesting location permission. It includes a tear-down method to free memory.

@changeset-bot
Copy link

changeset-bot bot commented Dec 18, 2025

🦋 Changeset detected

Latest commit: 91f6799

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@getodk/xforms-engine Minor
@getodk/web-forms Minor
@getodk/scenario Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

if (action) {
registerAction(context, setValue, action);
if (action.element.nodeName === 'odk:setgeopoint') {
context.parent.rootDocument.getBackgroundGeopoint()?.then((value: string) => {
Copy link
Collaborator Author

@latin-panda latin-panda Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The primary instance is the parent, not the context itself :(

@latin-panda
Copy link
Collaborator Author

@garethbowen This code isn't pretty and still experimental, but I wanted to get a version working first and get your thoughts from the engine side.

In the screenshot, the xform-engine is working; it sets the value when the event runs, and the value comes from a promise.

Screenshot 2025-12-19 at 2 15 04 AM

@latin-panda latin-panda changed the title feat: add setgeometry function feat: add setgeopoint function Dec 24, 2025
@latin-panda latin-panda changed the title feat: add setgeopoint function feat: add setgeopoint function (start-geopoint) Dec 24, 2025
@latin-panda
Copy link
Collaborator Author

Testing is complete, and I need to clarify a few small questions with the team later before moving this PR to ready.

@latin-panda latin-panda changed the title feat: add setgeopoint function (start-geopoint) feat(#507): add setgeopoint function Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add setgeopoint action

3 participants