nips/41.md

5.7 KiB
Raw Blame History

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.
  • ["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 112.

  • 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., 78 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: 12 chars
    • Region/state: 34
    • Metro/city: 56
    • Neighborhood/POI: 79
  • 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, no a,i,l,o.
  • Publishing precise geohashes reveals location; authors may choose a coarser precision if needed.