You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Three bundled plugins independently hit Nominatim / Wikipedia for "lat/lon ↔ name" lookups, none of which cache effectively:
info.lua — reverse geocodes the map centre every 5s via Nominatim's /reverse. URL contains the live lat/lon, so any pan-and-pan-back re-fetches even though the answer for "Tokyo" hasn't changed.
search/init.lua (forward) — Nominatim /search?q=.... Re-issuing the same query (e.g. opening the palette, searching "Tokyo", closing, re-opening, searching "Tokyo" again) re-fetches.
wiki/init.lua — Wikipedia geosearch by lat/lon. Same micro-pan problem as info.
(traceroute already uses fetch_cached — IP→geo is one URL per host, so URL-keyed caching is fine there.)
ttymap.http:fetch_cached(url, ttl) already exists as a URL-keyed HTTP-level disk cache, but URL-keying is the wrong granularity for spatial queries: every 0.0001° camera move makes a new URL, every "Tokyo " vs " tokyo" makes a new URL.
Nominatim's terms of service explicitly require local caching + max 1 req/s. Fixing this is overdue.
Proposal
A bundled lib at runtime/lua/ttymap/geocode.lua that does semantic caching on top of ttymap.storage + ttymap.http:
-- Reverse: coordinates → canonical place name
geo.reverse(35.6586, 139.7454, function(display_name, source) end)
-- Sync cache reads
local lat, lon, name = geo.cached_forward("Tokyo Tower")
local name = geo.cached_reverse(35.6586, 139.7454)
```
Internals
Forward keying: `normalize(query)` — trim + lowercase + collapse whitespace. So "Tokyo", " Tokyo ", "tokyo" share an entry.
Reverse keying: spatial bucketing — quantize lat/lon to a coarser grid (default ~0.001° ≈ 100m at the equator, configurable per-call). `cache_key = string.format("%.3f_%.3f", lat, lon)`. Means any pan within the bucket hits cache; the existing 5s throttle in info.lua becomes redundant.
Persistence: `ttymap.storage:open("geocode")`, two key namespaces. Forward entries: `fwd_.json`; reverse: `rev_.json`. Atomic write via storage's existing primitive.
Endpoints configurable via `require("ttymap.geocode").forward_endpoint` / `.reverse_endpoint` — defaults pointing at Nominatim, but a user can swap to a self-hosted instance.
TTL: long (or none) by default. Place names don't change. Honour standard `ttl_seconds` field on the config table for users who want it; default infinity.
Eviction / unbounded growth
Realistically: even an active user generates maybe O(1000) unique geocode entries lifetime. Each is a small JSON blob. Total disk usage is negligible. Punt eviction until someone reports a problem. `ttymap.storage` doesn't have an LRU primitive anyway, and adding one is a separate feature (see also #296 for tile cache eviction).
Migrations
In the same PR (once the lib lands):
`info.lua` — drop the 5s throttle + manual fetch; replace with `geo.reverse(lat, lon, cb)`. Bucketing replaces the time-based throttle.
`search/init.lua` — replace `ttymap.http:fetch(nominatim.url(query))` with `geo.forward(query, cb)`. The async palette provider drains the lib's cb instead of polling a raw job.
`wiki/init.lua` — `geo.reverse(lat, lon, cb)` for the place-name lookup; the geosearch itself stays as raw HTTP (it's not a 1:1 lat/lon→string lookup, returns N nearby articles).
Out of scope
LRU eviction — defer until reported as a real problem.
Server-side cache invalidation — TTL-only; users with `refresh`-like needs can clear the namespace via `ttymap.storage:open("geocode"):delete(key)` manually.
Bulk geocoding API — single-key only; if a plugin needs N lookups it just calls N times and the lib coalesces.
Motivation
Three bundled plugins independently hit Nominatim / Wikipedia for "lat/lon ↔ name" lookups, none of which cache effectively:
info.lua— reverse geocodes the map centre every 5s via Nominatim's/reverse. URL contains the live lat/lon, so any pan-and-pan-back re-fetches even though the answer for "Tokyo" hasn't changed.search/init.lua(forward) — Nominatim/search?q=.... Re-issuing the same query (e.g. opening the palette, searching "Tokyo", closing, re-opening, searching "Tokyo" again) re-fetches.wiki/init.lua— Wikipedia geosearch by lat/lon. Same micro-pan problem as info.traceroutealready usesfetch_cached— IP→geo is one URL per host, so URL-keyed caching is fine there.)ttymap.http:fetch_cached(url, ttl)already exists as a URL-keyed HTTP-level disk cache, but URL-keying is the wrong granularity for spatial queries: every 0.0001° camera move makes a new URL, every "Tokyo " vs " tokyo" makes a new URL.Nominatim's terms of service explicitly require local caching + max 1 req/s. Fixing this is overdue.
Proposal
A bundled lib at
runtime/lua/ttymap/geocode.luathat does semantic caching on top ofttymap.storage+ttymap.http:```lua
local geo = require "ttymap.geocode"
-- Forward: query string → coordinates + canonical display name
geo.forward("Tokyo Tower", function(lat, lon, display_name, source)
-- source ∈ "memory" | "storage" | "network"
end)
-- Reverse: coordinates → canonical place name
geo.reverse(35.6586, 139.7454, function(display_name, source) end)
-- Sync cache reads
local lat, lon, name = geo.cached_forward("Tokyo Tower")
local name = geo.cached_reverse(35.6586, 139.7454)
```
Internals
Eviction / unbounded growth
Realistically: even an active user generates maybe O(1000) unique geocode entries lifetime. Each is a small JSON blob. Total disk usage is negligible. Punt eviction until someone reports a problem. `ttymap.storage` doesn't have an LRU primitive anyway, and adding one is a separate feature (see also #296 for tile cache eviction).
Migrations
In the same PR (once the lib lands):
Out of scope
Refs