Skip to content

feat(formulus): observations map view with offline support - #1043

Draft
najuna-brian wants to merge 3 commits into
OpenDataEnsemble:devfrom
najuna-brian:feat/formulus-observations-map
Draft

najuna-brian wants to merge 3 commits into
OpenDataEnsemble:devfrom
najuna-brian:feat/formulus-observations-map

Conversation

@najuna-brian

@najuna-brian najuna-brian commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Closes #553

What this adds

A Map view on the Formulus Observations screen, next to the existing table (the compact table half of #553 already landed in ObservationListTable).

  • List | Map toggle in the filter area. Both views use the same filters (form, sync status, search).
  • Native MapLibre map (@maplibre/maplibre-react-native 11.5.1, BSD, no Google Play Services): observations are clustered on the GPU and coloured by form type (colour-blind-safe palette). Unsynced observations have an orange outline.
  • Tap a point → card with form, date, sync state and GPS accuracy → open the observation. Tapping a cluster zooms in; points at the same spot open a list instead.
  • Legend, fit to all observations, scale bar, OpenStreetMap attribution.
  • A notice when some matching observations have no location, or when the cap (5,000) is reached.

Offline behaviour (Formulus is offline-first)

  • Points, clusters, counts, the card and opening an observation all read from the device database; nothing waits for the network.
  • Cluster counts are bundled images, not map text, so they render without downloaded map fonts.
  • Background map, in order: map files shipped in the app bundle (maps/*.mbtiles, *.pmtiles, or maps/style.json) → areas MapLibre has cached from earlier online use → a plain background with a short "offline" notice.
  • A study can point app.config.json → maps at another tile provider or style URL. A style URL is cached on the device so it still loads offline.
  • Downloading map areas on the phone is only offered when the study's tile provider allows bulk download; it is never offered for the public OpenStreetMap servers, whose usage policy forbids it.
  • The network is checked once in the background (4 s timeout) only to decide whether to show the offline notice.

Performance

  • The map code is lazy-loaded and the map unmounts when the screen is not focused, so the List view and app start-up do not pay for it.
  • The map query uses the existing form_type / deleted indexes and never reads observation data. It does not touch observation_index.
  • Size: libmaplibre.so is about 10.8 MB (arm64-v8a) / 7.9 MB (armeabi-v7a) uncompressed; the existing per-ABI APK splits keep each device to one copy.

Not in this PR

  • Shipping map packages separately from the app bundle (versioned download), fonts for non-Latin map labels, and a Formulus bridge API for custom apps to open the map.

Testing

  • pnpm run test: 67 suites / 533 tests pass (new: map query, basemap resolution, cluster badges).
  • Type check and lint are clean for the changed files.
  • Android debug build with MapLibre compiles and installs (x86_64 emulator: ./gradlew app:installDebug -PabiFilters=x86_64 -PreactNativeArchitectures=x86_64; the second flag is needed so libNitroSound.so is built for the emulator).
  • On-device checks with a seeded dataset (10,455 observations across Uganda, including stacked points, points without GPS and an outlier) are in progress. Screenshots of the List and Map views, online and offline, will be attached in the comments.

formulus/scripts/generate-map-cluster-badges.py regenerates the cluster badge images (Pillow ≥ 10.1).

Reads only the stored geolocation (never data) with the same filters as the
observations table, caps the result, and reports how many matches have no
usable location. Refs OpenDataEnsemble#553
Native MapLibre map with GPU clustering; cluster counts are bundled images so
they render without map fonts or network. Basemap comes from app.config.json
maps, bundled mbtiles/pmtiles or style in the app bundle, with OpenStreetMap
as the online default. Everything shown is read from the device; the network
is only probed in the background. Refs OpenDataEnsemble#553
…tions

Map view shares the table's filters, loads lazily, unmounts when the screen is
not focused, and explains when the background is unavailable offline. Area
downloads are only offered for tile providers that allow them. Strings in
en/pt/fr/sw. Refs OpenDataEnsemble#553

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

[Formulus] Update "Observations" screen w/table and map

1 participant