← back to La Socrata Ingester
Viewer: CD/ZIP quick-jump control on the Map view
e6f035643f8cf1c0da499ac739bd3f24a84648a6 · 2026-08-12 11:57:41 -0700 · Steve Abrams
- On-map 'Jump to' control (top-left, clear of the zoom control) with CD + ZIP
selects. Picking one sets the filter and the existing fitBounds flies the map
to that area; clearing removes it. Options carry live counts and are drawn from
a stable unfiltered facet list (allFacets, fetched once at boot) so every CD/ZIP
is always offered even when a filter is active. Composes with deep-links + saved
views via the shared state.filters.
Author: Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
M viewer/public/index.html
Diff
commit e6f035643f8cf1c0da499ac739bd3f24a84648a6
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 12 11:57:41 2026 -0700
Viewer: CD/ZIP quick-jump control on the Map view
- On-map 'Jump to' control (top-left, clear of the zoom control) with CD + ZIP
selects. Picking one sets the filter and the existing fitBounds flies the map
to that area; clearing removes it. Options carry live counts and are drawn from
a stable unfiltered facet list (allFacets, fetched once at boot) so every CD/ZIP
is always offered even when a filter is active. Composes with deep-links + saved
views via the shared state.filters.
Author: Steve Abrams
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
viewer/public/index.html | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/viewer/public/index.html b/viewer/public/index.html
index 936b821..17f0328 100644
--- a/viewer/public/index.html
+++ b/viewer/public/index.html
@@ -88,6 +88,9 @@
/* map */
#map{height:100%;width:100%}
.mapwrap{position:absolute;inset:0}
+ .mapjump{position:absolute;top:10px;left:52px;z-index:500;background:#fff;border:1px solid var(--line);border-radius:8px;
+ box-shadow:0 2px 8px rgba(0,0,0,.12);padding:5px 8px;display:flex;align-items:center;gap:6px;font-size:11px;color:var(--muted)}
+ .mapjump select{font-size:11px;padding:3px 6px;border:1px solid var(--line);border-radius:6px;background:#fff;color:var(--ink);max-width:150px}
.legend{position:absolute;bottom:14px;right:14px;z-index:500;background:#fff;border:1px solid var(--line);border-radius:8px;padding:8px 11px;font-size:11px;box-shadow:0 2px 8px rgba(0,0,0,.12)}
.legend b{display:block;margin-bottom:5px;font-size:11px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}
.legend .row{display:flex;align-items:center;gap:6px;margin:2px 0}
@@ -179,6 +182,11 @@
</div>
<div id="mapview" class="mapwrap hidden">
<div id="map"></div>
+ <div class="mapjump" id="mapjump">
+ <span>Jump to</span>
+ <select id="jumpcd"><option value="">CD…</option></select>
+ <select id="jumpzip"><option value="">ZIP…</option></select>
+ </div>
<div class="legend" id="legend"></div>
</div>
</main>
@@ -441,8 +449,21 @@ function renderMap(){
if(pts.length) map.fitBounds(pts,{padding:[30,30],maxZoom:14});
$('#legend').innerHTML = `<b>${$('#colorby').selectedOptions[0].text} · ${state.rows.filter(r=>r.lat).length} shown</b>`+
legendItems().map(([l,c])=>`<div class="row"><span class="dot" style="background:${c}"></span>${escapeHtml(l)}</div>`).join('');
+ renderMapJump();
setTimeout(()=>map.invalidateSize(),50);
}
+// ---- CD / ZIP quick-jump on the map (filters + the existing fitBounds flies there) ----
+let allFacets = {}; // stable full CD/ZIP lists (unfiltered), so the jump menu always offers every value
+function renderMapJump(){
+ const cd=$('#jumpcd'), zip=$('#jumpzip'); if(!cd||!zip) return;
+ const jf = k => (allFacets[k]&&allFacets[k].length)?allFacets[k]:(facetData[k]||[]);
+ const fill=(sel,items,label,cur)=>{ sel.innerHTML=`<option value="">${label}</option>`+
+ (items||[]).map(o=>`<option value="${escapeAttr(o.v)}" ${String(cur||'')===String(o.v)?'selected':''}>${escapeHtml(o.v)} (${Number(o.c).toLocaleString()})</option>`).join(''); };
+ fill(cd, jf('cd'), 'CD…', state.filters.cd);
+ fill(zip, jf('zip'), 'ZIP…', state.filters.zip);
+}
+$('#jumpcd').onchange=e=>{ const v=e.target.value; if(v) state.filters.cd=v; else delete state.filters.cd; reload(); };
+$('#jumpzip').onchange=e=>{ const v=e.target.value; if(v) state.filters.zip=v; else delete state.filters.zip; reload(); };
function syncURL(){ // mirror filters+sort+view+color into the URL so any view is shareable/bookmarkable
const p=new URLSearchParams();
@@ -617,6 +638,7 @@ document.addEventListener('click',e=>{ if(!e.target.closest('#savedwrap')) $('#s
hydrateFromQuery(location.search, false); // boot: deep-link params win, else persisted prefs
syncControls();
loadFacets(); loadRows(false);
+ fetch(api('/api/facets')).then(r=>r.json()).then(d=>{ allFacets=d; renderMapJump(); }).catch(()=>{}); // stable full CD/ZIP lists for the map jump
})();
</script>
</body>
← 48e81f3 Viewer: per-column filter inputs in the List header
·
back to La Socrata Ingester
·
auto-data-snapshot: 2026-08-18T08:10:11 (1 data files) — dat b16e3ff →