Skip to main content

Search Informal Landmarks and Local Aliases

The business problem​

Ghanaians navigate by landmark, not street address, but landmark names are often short, generic, and shared by dozens of unrelated places — "Market," "Junction," "Station" describe hundreds of locations each; "Circle" and "37" are single words that mean one very specific place in Accra and nothing useful anywhere else. A naive text search over landmark names returns a long, unranked list of near-identical matches for exactly the queries that matter most. This guide explains how GET /v2/landmarks/geocode actually ranks results, and how to use its parameters instead of the raw query string to do the disambiguation.

What AfriHex returns​

Landmark search runs in one of two modes, and they rank results differently — read data.query/data.anchor in the response to know which mode you got:

ModeTriggered byRanked by
Name searchq alonescore — 0–1 trigram (substring) similarity to the query text
Anchored searchnear (a point) or anchor (a landmark name, geocoded first)distance_m — metres from the anchor, closest first
Viewport searchbboxEvery landmark whose centroid falls in the box — no ranking, it's a map query, not a search

bbox takes precedence over q/near/anchor if more than one is supplied. There is no separate alias list — each landmark has one canonical name (plus an optional name_local) — so disambiguating a generic word is entirely a matter of which parameters you add, not a database of alternate names to search against.

Before you begin​

  • Know which of the three modes fits your UI: a search box typically wants name search; a "landmarks near me" list or a route corridor wants anchored search; a map viewport wants bbox.
  • Have a fallback anchor ready (device GPS, or the user's last known location) — name search alone on a generic word is close to useless without one.

Copyable request​

A bare generic term, ranked only by text similarity — every "Market" in the country is a plausible match, so score alone won't separate them:

curl "https://api.afrihex.com/v2/landmarks/geocode?q=market&limit=5" \
-H "X-API-Key: $AFRIHEX_API_KEY"

The same term, anchored to a point — now ranked by actual distance, which is what "which Market did they mean" really depends on:

curl "https://api.afrihex.com/v2/landmarks/geocode?q=market&near=5.556,-0.196&radius=2000" \
-H "X-API-Key: $AFRIHEX_API_KEY"

Full example response​

{
"success": true,
"data": {
"query": "market",
"results": [
{
"id": 88,
"slug": "makola-market",
"name": "Makola Market",
"kind": "market",
"region_code": "GH-AA",
"source": "osm",
"confidence": 90,
"centroid": { "lat": 5.5497, "lng": -0.2115 },
"distance_m": 1840,
"street": "Kojo Thompson Road"
}
]
}
}

Field-by-field explanation​

  • score (name search only) — trigram similarity, 0–1. A short generic word scores similarly against every landmark containing it — this field alone can't tell "Makola Market" from "Kaneshie Market"; add near, anchor, kind, or region to actually disambiguate.
  • distance_m (anchored search only) — metres from your near/anchor point. For a term like "Circle" or "37" — effectively unique within a region but ambiguous nationally — pairing a loose q with a region or near filter is far more reliable than trying to make the text match more specific.
  • confidence (0–100, integer) — how well-documented this landmark record is, independent of how well it matched your query. A landmark can score confidence: 100 and still not be the one your user meant if score/distance_m say it's a weak match. Don't confuse this with provenance.confidence (0.0–1.0) elsewhere in the API — see Understanding Confidence and Verification for the full set of same-named-but-different fields.
  • source (osm / field / user_submitted) — where the landmark record itself came from, not how sure the match is. This is a different vocabulary from provenance.source (device_gps/geocoded_text/etc.) used elsewhere in the API — the schema explicitly normalizes one to the other, but they're not literally the same field.
  • kind — a coarse category (market, church, hospital, …). Cheap and effective disambiguation: q=station&kind=transport narrows "Station" dramatically before distance or text similarity even get involved.

Error and edge cases​

  • Zero results for a well-known name. Landmark coverage is real but incomplete (~130 photo-matched landmarks and growing, many more without photos) — a genuine miss is possible, not necessarily a bad query. Fall back to GET /v2/search?q= (place/address search) as a second attempt — see Addresses & Geocoding.
  • Many equally-plausible matches for a generic term with no anchor. Expected behaviour for words like "Market" or "Junction" — this isn't a bug to work around with a smarter query string, it's the actual ambiguity of the term. Require a near/anchor/region/kind filter in your UI before running the search, rather than trying to rank an inherently ambiguous list.
  • anchor (a landmark name) doesn't resolve. It's geocoded first, using the same name search as q — if the anchor name itself is ambiguous or missing, the whole anchored search fails. Prefer near (a coordinate) over anchor (a name) whenever you already have a device location.
  • A photo_url shows a clearly wrong or foreign image. Known, flagged limitation — the name-based photo matcher isn't geo-verified yet (~130 landmarks affected). Treat photos as illustrative, not authoritative.

Production checklist​

  • Every generic-word search in your UI is paired with near, anchor, region, or kind — never q alone for short/common terms.
  • Zero-result landmark searches fall back to place/address search rather than showing an empty state.
  • score/distance_m/confidence are shown to users (or used in ranking logic) as three separate signals, not blended into one number.
  • Landmark photos are labelled as illustrative, or hidden, until you've independently confirmed them for landmarks that matter to your flow.