Skip to content

Use Poracle's geocode endpoint instead of calling Nominatim directly (adds Photon and Google) #845

Description

@hokiepokedad2

Requested in Discord, and committed to for the next release. ReactMap v1.50 added the same thing as geocoderProvider: "photon".

PoracleNG already supports Photon, so most of this is not ours to write. [geocoding] provider takes none, nominatim, photon or google, and processor/internal/geocoding/ carries a provider per name behind a two-layer cache, a circuit breaker and a concurrency limiter.

The reason our maps don't is that PoracleWeb bypasses all of it and calls the provider URL itself:

var url = $"{config.ProviderUrl.TrimEnd('/')}/search?addressdetails=1&q={...}&format=json&limit=5";
var response = await client.GetStringAsync(url);
return this.Content(response, "application/json");   // Nominatim's wire shape IS our API contract

ProviderUrl is Poracle's providerURL, so an operator who switches Poracle to Photon leaves us pointed at a Photon instance while still speaking Nominatim. Search silently returns nothing.

Do not add a provider setting. Use Poracle's endpoint.

GET /api/geocode/forward?q= resolves through whichever provider the operator configured and returns an already-normalised array — verified against a live instance:

[{"latitude":37.5385087,"longitude":-77.43428,"city":"Richmond","country":"United States"}]

Photon, Google and Nominatim all arrive in that one shape, cached and circuit-broken. Switching to it gets Photon support without PoracleWeb parsing a single provider payload, and it does not grow when Poracle adds a fifth provider.

An earlier draft of this issue proposed a geocoder_provider site setting and per-provider parsing here. That was wrong twice over: it duplicates work Poracle has already done, and it creates a second place to configure the geocoder that can disagree with the first.

What is actually left for us

Forward search. Point LocationController.Geocode at {poracle}/api/geocode/forward and map its result onto GeocodingResult so the five frontend consumers are untouched — location-dialog (twice, reading address.house_number / road / city / town / village), area-list (twice) and dashboard (twice). That is one mapping against a stable Poracle shape, not one per provider. Compose display_name from the parts, and render latitude/longitude as strings, because the SPA parses them as strings today.

Reverse geocoding has no endpoint, and that is the real gap. Poracle does reverse internally — [geocoding] forward_only exists precisely to switch it off — but only /geocode/forward is exposed. Until there is a /geocode/reverse, LocationController.ReverseGeocode has nothing provider-agnostic to call. Options, in preference order:

  1. Ask upstream to expose it. Small, symmetric with forward, and the internal call already exists.
  2. Keep the direct provider call for reverse only, adding Photon's /reverse shape. Reintroduces per-provider parsing on one path, which is what the rest of this issue exists to avoid.

Filing (1) upstream is a prerequisite; do not start (2) until it is refused.

provider: "none" should hide the search box. Poracle publishes providerURL in /api/config/poracleWeb but not provider, so we cannot currently tell "no geocoder configured" from "geocoder configured". providerURL being empty is a near-enough proxy today but is not the real signal — google needs no URL. Worth asking upstream to publish provider alongside it; it is a one-field addition to the same struct that gained availableLanguages in jfberry/PoracleNG#194.

Watch for

Nominatim must not regress. Every existing install uses it, and this replaces its call path wholesale rather than adding a branch beside it. The existing-provider tests matter more than the Photon ones.

Poracle's result is thinner than Nominatim's raw payload. Nominatim returns addressdetails with road, house_number, postcode, town, village; the forward endpoint returned only city and country for the query above. Establish what it actually carries for a street-level query before assuming getAddressPrimary still has what it reads, and if fields are missing, that is a third thing to raise upstream rather than a reason to go back to calling providers directly.

A geocoding failure must stay quiet. The address line is decoration on the dashboard and the areas page; a 503 from an unreachable provider should leave the coordinates showing, not surface an error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions