Events support multiple structured locations based on RFC 9073 VLOCATION.
| Type | Description | Form Input | structured_data |
|---|---|---|---|
PHYSICAL |
In-person venue | Nominatim search or manual name | OSM URL |
ONLINE |
Virtual meeting | Meeting URL | The URL itself |
interface EventLocation {
name: string; // Required: "Hafenbar", "Zoom Call", "My backyard"
location_type: "PHYSICAL" | "ONLINE";
description?: string; // Additional instructions
structured_data?: string; // OSM URL or meeting URL
}| Field | Max Length |
|---|---|
name |
500 characters |
description |
2,000 characters |
structured_data |
2,048 characters |
| Max locations per event | 5 |
Two modes:
- Simple: Just a name (e.g., "My backyard")
- Full: Nominatim search → name + OSM URL
- 1-second debounce (rate limit compliance)
- Returns OSM node/way/relation
structured_datastores the OSM URL:https://www.openstreetmap.org/node/1573053883
When coordinates are available, an embedded OSM map preview is shown:
function buildMapEmbedUrl(lat: number, lon: number): string {
const delta = 0.005; // ~500m bbox
const bbox = `${lon - delta},${lat - delta},${lon + delta},${lat + delta}`;
return `https://www.openstreetmap.org/export/embed.html?bbox=${bbox}&layer=mapnik&marker=${lat},${lon}`;
}For virtual meetings:
name: Display name (e.g., "Zoom Call")structured_data: The meeting URL (validated as http/https)
Platform icons are inferred at display time from the URL domain.
LocationFields component (components/event/create/location-fields.tsx):
- Card-based UI for multiple locations
- Location type selector (Physical/Online)
- Physical: Nominatim search with keyboard navigation (↑↓ Enter Escape)
- Online: URL input with validation
- Map preview for selected physical locations
- Add/remove locations (max 5)
LocationDisplay component (components/event/detail/location-display.tsx):
- Renders all locations in a Card below the event description
- For each physical location with OSM link:
- Map Links: Google Maps, Apple Maps, OpenStreetMap (all include name + coordinates)
- BTCMap Integration: Fetches payment data from BTCMap API
- Payment Icons: On-chain (₿), Lightning (⚡), NFC (📱) when Bitcoin accepted
- Map Preview: Embedded OSM map with marker
// Google Maps - uses search with coordinates as center
`https://www.google.com/maps/search/?api=1&query=${name}¢er=${lat},${lon}`
// Apple Maps - uses OSM data, coordinates match well
`https://maps.apple.com/?q=${name}&ll=${lat},${lon}&z=17`
// BTCMap merchant page
`https://btcmap.org/merchant/${osmType}:${osmId}`Fetches from https://api.btcmap.org/v2/elements/{osm_type}:{osm_id} to check:
| Tag | Icon | Meaning |
|---|---|---|
currency:XBT=yes |
Required | Bitcoin accepted |
payment:onchain=yes |
₿ | On-chain payments |
payment:lightning=yes |
⚡ | Lightning payments |
payment:lightning_contactless=yes |
📱 | NFC Lightning |
components/ui/map-preview.tsx - shared between creation and display:
<MapPreview lat={47.3769} lon={8.5417} className="h-[150px]" />{
"locations": [
{
"name": "Hafenbar zur Metzgerhalle",
"location_type": "PHYSICAL",
"description": "Enter through the back door",
"structured_data": "https://www.openstreetmap.org/node/1573053883"
},
{
"name": "Zoom Backup",
"location_type": "ONLINE",
"structured_data": "https://zoom.us/j/123456789"
}
]
}Physical locations with structured_data can:
- Link directly to OpenStreetMap
- Fetch coordinates for map display
- (Future) Fetch BTCMap payment tags
Legacy location and geo string fields have been removed from:
- pubky-app-specs (PubkyAppEvent)
- pubky-nexus (EventDetails)
- eventky (types, forms)
No migration needed as few events existed with the old format.