Geocoding β
Most of your members will never have a hand-placed pin. They'll have an address β Calle 23 #45-12, Cartagena, BolΓvar, Colombia β and an expectation that the map just works. Geocoding is the background process that takes those addresses, sends them to a provider, and stores the result so they appear on the map.

The two coordinate columns β
Every members row and every org_units row carries two independent location columns:
map_locationβ the exact pin. Hand-placed by a staff member who clicked Use my location or typedlat,lnginto the location field. This is ground truth and the geocoder never touches it.estimated_map_locationβ the approximate coordinate. Written by the geocoder when an address field changes. Cheap, scales to thousands of records, but only as accurate as the address itself.
Both can be present at the same time. The source selector decides which one the map draws.
When the geocoder runs β
The geocoder is triggered by any change to an address-shaped field on a member or org unit:
address(the street line)city_id(which city the record belongs to)state_id/country_id(the larger admin areas)
When one of those fields changes, the record is marked estimated_geocode_status = 'pending' and added to a queue. The save itself returns immediately β you don't wait for the geocoder. A background worker picks the job up within a minute or so and writes the result.
If only the name or the phone number changes, the geocoder doesn't re-run. It only cares about address-shaped inputs.
The estimated_geocode_status field β
This column tells you exactly where each record is in the pipeline. Four values:
| Status | Meaning |
|---|---|
pending | The record is queued. A worker will pick it up shortly. New records that have an address always start here. |
ok | The geocoder succeeded. estimated_map_location is populated and the record will appear on the map. |
failed | A temporary failure (network blip, provider rate-limit). The worker will retry on a backoff. |
terminal | The geocoder gave up. The address could not be resolved after multiple attempts. Usually means the address is malformed, a free-text city the provider doesn't recognise, or a country combination with no matching place. |
You can see the badge on a member's profile under the location section β pending shows as grey, failed / terminal as red. The map members query filters out terminal and failed rows automatically, so you only see records that actually have coordinates.
Providers β
GCM ships three geocoding providers; the org admin picks one at Settings β Map β Geocoding provider:
- Nominatim (default) β OpenStreetMap's free service. No API key required, polite-use rate-limited, good global coverage but weaker on Latin American street addresses.
- Mapbox β paid, fast, structured. Requires a Mapbox access token in Settings β Map. Excellent on US/EU addresses, very good elsewhere.
- Google Maps β paid, premium quality. Requires a Google Maps Geocoding API key. The most accurate provider for most of the world but the most expensive.
The worker calls whichever provider the org has configured and writes the resulting lat,lng into estimated_map_location. The provider's verbatim response isn't stored β only the coordinate.
TIP
For most Latin American churches we recommend Mapbox or Google. Nominatim's free coverage is fine for English-language addresses but loses precision on apartment numbers, unit labels, and informal street naming.
The queue β
The queue lives in geocode_jobs. Each job carries the record type (member or org_unit), the record ID, a pending status, and a claim lease so two workers don't pick the same job up. The geocode-worker edge function runs on cron (every minute by default). On each tick it:
- Claims a batch of pending jobs atomically using
claim_geocode_jobs(withSKIP LOCKEDso it never blocks). - Calls the configured provider for each address.
- On success, writes
estimated_map_locationand setsestimated_geocode_status = 'ok'. - On a transient failure, leaves the status as
failedand lets the next tick retry. - After several retries, transitions to
terminaland stops trying.
A platform admin can also invoke the worker on demand to flush the queue immediately β useful right after importing a large member list.
Re-geocoding a single record β
Sometimes the geocoder placed a pin in the wrong neighbourhood β a road centroid for an apartment block, a city centre for an address without a street number β and you've since corrected the address. To force a fresh geocode:
- Open the member's profile (or the org unit editor).
- Find the location card.
- Click the Re-geocode button (it appears next to the location field; says Geocode if the record has never been geocoded, Re-geocode if it has).
- The status flips back to
pendingand the worker picks it up on the next tick.
A small green flash confirms the request was queued. The new coordinate usually appears within a minute. If it doesn't, the address probably failed β open the members list, filter by estimated_geocode_status = terminal, and check the addresses.
Re-geocoding in bulk β
After rolling out a new city/state/country reference table or switching providers, you may want every record re-geocoded. Bulk re-geocode is a platform-admin action: an SQL update flips every row in the org back to pending, the next worker tick starts processing, and Platform admin β Geocode jobs shows progress (pending / ok / failed counts). Because the worker is rate-limited and batched, a 5,000-member re-geocode takes a few hours.
Org units β
Org units use the exact same pipeline. Edit the address of a campus and the worker geocodes it just like a member. The org_units table carries the same map_location / estimated_map_location / estimated_geocode_status columns and the same statuses. See Org units on the map for how the resulting coordinates render.
What the map shows β
Records with estimated_geocode_status = 'pending', 'failed', or 'terminal' and no manual pin do not appear on the map β they have no coordinate. They sit silently in the directory until a coordinate is written. The pin count badge at the top of the map reflects only records with at least one valid coordinate.
If a member has an exact pin but their address geocoder result is also stored, the map's All locations mode draws the exact pin and ignores the estimate. That precedence is deliberate β ground truth beats a guess, every time.
Privacy β
The geocoder sees only the address, never the member's name. Address strings are sent to the configured provider over HTTPS. If your org's privacy posture forbids sending member addresses to a third-party API, switch the provider to None in Settings β Map and the queue will stop processing. Members without manual pins simply won't appear on the map.
Related β
- Map overview β the page that consumes these coordinates.
- Viewing members on the map β how the exact vs approximate badge is shown.
- Filters and layers β switch between exact, approximate, and mixed.
- Org units on the map β geocoding applies to campuses too.
- Members module β where you edit addresses and trigger re-geocodes.
- Creating org units β addresses entered here also flow through the geocoder.