Gallery Location Design
Goal
Add location to galleries using H3 geohashing and Nominatim geocoding, with auto-suggest from photo EXIF GPS data. Display locations on gallery cards and provide location-based feeds.
Data Model
Gallery lexicon (social.grain.gallery): Add optional location field referencing social.grain.defs#location.
Location type (social.grain.defs#location):
{
"type": "object",
"required": ["name", "value"],
"properties": {
"name": { "type": "string", "description": "Display name (e.g. Tokyo, Japan)" },
"value": { "type": "string", "description": "H3 cell index at resolution 5" }
}
}
Gallery view (social.grain.gallery.defs#galleryView): Add optional location with same shape.
Database: The gallery table gains location_name and location_value columns.
EXIF extraction: Extend extractExif() in image-resize.ts to grab GPSLatitude/GPSLongitude from exifr. These are transient (not stored) — used only client-side to auto-suggest location.
Dependencies: Add h3-js package.
Client Utilities
Port from atconf-astro:
app/lib/utils/h3.ts—latLonToH3(lat, lon, resolution=5)using h3-jsapp/lib/utils/nominatim.ts—searchLocations(query)for forward geocoding,reverseGeocode(lat, lon)for EXIF GPS auto-suggest. Both hitnominatim.openstreetmap.org.
Create Flow (Step 2)
Add LocationInput component below the description field:
- Text input with map pin icon
- Debounced Nominatim search (300ms, min 2 chars)
- Dropdown with up to 5 results
- On select: convert lat/lon to H3 cell, store name + h3Index
- Click-outside closes dropdown
- Optional — galleries without location are fine
EXIF GPS auto-suggest: On entering step 2, if the first photo has GPS coords, reverse-geocode via Nominatim and pre-fill the location input. User can clear or override.
On publish: Include location: { name, value } in the gallery record if set.
Display
On GalleryCard: Show map pin icon + location name below EXIF info, linking to /location/[h3].
Feeds
server/xrpc/getLocations.ts: Top locations by gallery count, cached 5min (same pattern as getCameras).
server/feeds/location.ts: Filter galleries by H3 cell match.
Lexicons: Add location param to getFeed.json. Create social.grain.unspecced.getLocations lexicon.
UI: Location pills in sidebar and mobile drawer. /location/[h3] route with DetailHeader (location name) and FeedList.
Hydration
Update _hydrate.ts to read location_name/location_value from the gallery row and include location: { name, value } in the hydrated gallery view.
Files to Create/Modify
Create:
app/lib/utils/h3.tsapp/lib/utils/nominatim.tsapp/lib/components/atoms/LocationInput.svelteserver/feeds/location.tsserver/xrpc/getLocations.tsapp/routes/location/[h3]/+page.tsapp/routes/location/[h3]/+page.sveltelexicons/social/grain/unspecced/getLocations.json
Modify:
lexicons/social/grain/defs.json— add location typelexicons/social/grain/gallery/gallery.json— add location fieldlexicons/social/grain/gallery/defs.json— add location to galleryViewlexicons/dev/hatk/getFeed.json— add location paramapp/lib/utils/image-resize.ts— extract GPS from EXIFapp/routes/create/+page.svelte— add LocationInput to step 2app/lib/components/molecules/GalleryCard.svelte— display locationapp/lib/components/organisms/SidebarRight.svelte— location pillsapp/lib/components/organisms/MobileDrawer.svelte— location pillsapp/lib/queries.ts— add locationFeedQuery, locationsQueryserver/feeds/_hydrate.ts— include location in gallery viewdb/schema.sql— add columnspackage.json— add h3-js