5.7 KiB
NIP-41
Places (geohash ladder addressable locations)
draft
optional
Clients need a simple, relay-indexable way to discover physical places (e.g., RV parks, cafés, venues) and to query them efficiently on maps. It should work with relays that don't have "Search Capability" (NIP-50). This NIP defines a parameterized replaceable event for “Places” that are geohash-addressable via a ladder of g
tags—from coarse to fine precision—so clients can query by exact #g
matches without prefix search.
2. Event Kind
- Kind:
33000
- Type: Addressable (NIP-01)
- Parameterized key: the first
["d", <identifier>]
tag (e.g., a random ID)
3. Required/Recommended Fields
3.1. Required fields/tags
["d", "<stable-id>"]
— a random ID (e.g.,permit:FL:44-54-00003
,slug:lochloosa-harbor
, etc.)- At least one
["g", "<geohash>"]
tag.
The recommended pattern is to include a ladder of geohash prefixes from coarse to fine precision (e.g.,d
,dj
,dj3
,dj3p
, …). Recommended precision is at least 7. This enables fast map queries with#g
matching at various zoom levels.
3.2. Recommended tags
["name", "<display name>"]
["website", "<website url>"]
— official website or listing["image", "<absolute url>"]
— cover image["phone", "<E.164 or human>"]
["payment_method", "<currency code>", "<type>"]
- e.g.: ["payment_method", "USD", "cash"]["address", "<street>", "<city>", "<region>", "<postal>", "<country>"]
["amenity", "<amenity type>"]
["hours", "<day of week abbrevation>", "<shift one>", "<shift two>", ...]
- e.g., ["hours","thu","09:00/12:30","13:30/17:00"]["L", "physical-location"]
["l", "<physical-location-type>", "physical-location"]
- freeform type labels used to reduce results for usecase (e.g.,business
,rv-park
,restaurant
, etc.)
3.3. Optional tags
- Non-indexed labels for usecase:
["rv_spaces", "<count>"]
["booking_provider", "<booking provider name>", "<specific booking provider id>"]
3.4. Content
content
is a plain text short readable description.
4. Geohash (g
) Tag Rules
-
A
g
tag value is a standard Base32 geohash (lowercase), length 1–12. -
Include multiple
g
tags forming a coarse → fine ladder for the same point, e.g.:["g","d"] ["g","dj"] ["g","dj3"] ["g","dj3p"] ["g","dj3pz"] ["g","dj3pzs"] ["g","dj3pzsu"] ["g","dj3pzsuc"]
-
Reason: relays and clients can match
#g
exactly at any zoom level. The Place then appears in map queries that request any of those zoom-appropriate buckets.
Publishers should generate the ladder from a single point (lat/lon → geohash) and decide the deepest precision they care to expose (e.g., 7–8 chars for point-of-interest).
5. Querying
5.1. Relay filters
-
Fetch all Places within a geohash bucket:
{ "kinds": [33000], "#g": [ "dhvj", "dhvm", "dhvn", "dhvp", "dhvq", "dhvr", "dhvt", "dhvw", "dhvx", "djj0", "djj2", "djj8" ], "#l": ["rv-park"] }
-
Zoom out: use shorter prefixes already present as
#g
values (e.g.,["dhv"]
). -
Fetch/refresh a specific place (parameterized key):
{ "kinds": [33000], "authors": ["<pubkey>"], "#d": ["permit:FL:44-54-00003"] }
6. Example (valid per this NIP)
{
"kind": 33000,
"pubkey": "598cece47f7ed9516a30d43f0045b6cfb78454af3829c18a941c4196959345ee",
"created_at": 1758720595,
"tags": [
["d", "permit:FL:1234567"],
["L", "physical-location"],
["l", "business", "physical-location"],
["l", "rv-park", "physical-location"],
["name", "Lochloosa Harbor RV Park"],
["payment_method", "USD", "credit_card"],
["payment_method", "USD", "apple_pay"],
["payment_method", "USD", "cash"],
["payment_method", "BTC", "onchain"],
["payment_method", "BTC", "lightning"],
["hours", "mon", "09:00/17:00"],
["hours", "tue", "09:00/17:00"],
["hours", "wed", "09:00/20:00"],
["hours", "thu", "09:00/12:30", "13:30/17:00"],
["hours", "fri", "10:00/22:00"],
["hours", "sat", "10:00/24:00"],
["hours", "sun"],
["amenity", "WiFi"],
["amenity", "Washer / Dryer"],
["website", "https://westernbtc.com"],
["image", "https://lochloosaharbor.example/cover.jpg"],
["phone", "+1-352-555-0123"],
["address", "123 Harbor Rd", "Hawthorne", "FL", "32640", "USA"],
["booking_provider", "campspot", "lochloosa-harbor"],
["booking_url", "https://example.com"],
["rv_spaces", "82"],
["g", "d"],
["g", "dj"],
["g", "dj3"],
["g", "dj3p"],
["g", "dj3pz"],
["g", "dj3pzs"],
["g", "dj3pzsu"],
["g", "dj3pzsuc"]
],
"content": "Some description.",
"id": "8121da23881ebfa4be496392a78130699d97e434206c2995465d0df8cbc9fa40",
"sig": "dd00e06be69158902987e1c5d643d47b284e52f367988e62e1d644a6f0d15fb5b0c5e86ccd954c4117a10df435b15825568fd07d533b447c2062450d0b2f0f0f"
}
7. Client Guidance
- Clients may choose to
limit
returned events at a more coarse precision. - For map tiles/zoom levels, choose the geohash precision to query:
- World/continent: 1–2 chars
- Region/state: 3–4
- Metro/city: 5–6
- Neighborhood/POI: 7–9
- Query one or more
#g
values covering the viewport. (For polygons, derive covering geohash buckets and query all of them.)
8. Examples
9. Validation Rules and Privacy
- Must have at least one
["d", ...]
and one["g", ...]
. ["g", ...]
must be valid Base32 geohash, lowercase, noa,i,l,o
.- Publishing precise geohashes reveals location; authors may choose a coarser precision if needed.