From c5ed52db4b9461cad61e215d1248e6246ee01e02 Mon Sep 17 00:00:00 2001 From: Matt Johnson Date: Tue, 26 May 2026 18:43:08 +0000 Subject: [PATCH] Read-only Leaflet map preview for multi-bbox editor (v0.9.10) Adds a passive Leaflet map to the generic model_list editor that renders every bbox row as a labeled translucent rectangle, auto-detected via the min/max lon+lat sub-fields (TomTom incidents). Read-only: no drag/draw, precise tuning stays in the per-row coord inputs. Rectangles redraw live on input/add/remove; viewport fits only on initial render so typing never jumps the map. Non-bbox model_lists (StateConfig, TileCoord) are unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../gui/templates/_partials/bbox_map.html | 78 +++++++++++++++++++ .../gui/templates/_partials/model_list.html | 8 ++ tests/test_gui_adapter_edit.py | 22 ++++++ 3 files changed, 108 insertions(+) create mode 100644 src/central/gui/templates/_partials/bbox_map.html diff --git a/src/central/gui/templates/_partials/bbox_map.html b/src/central/gui/templates/_partials/bbox_map.html new file mode 100644 index 0000000..4aaffbb --- /dev/null +++ b/src/central/gui/templates/_partials/bbox_map.html @@ -0,0 +1,78 @@ +{# Read-only Leaflet preview of every bbox row in a model_list whose row model + exposes min_lon/min_lat/max_lon/max_lat (e.g. TomTom incidents). Passive: + no drag-to-edit, no click-to-draw — precise tuning stays in the per-row coord + inputs below. Mirrors _region_picker.html's init (tile layer, invalidateSize, + fitBounds). Rectangles redraw live as inputs change; the viewport only fits + on initial render so typing never makes the map jump. + + The map div is rendered ABOVE the row table, so init() is deferred until the + DOM is parsed — otherwise the inline script would run before .model-list-body + exists and the rows would be unreadable. #} +
+
+
+ + diff --git a/src/central/gui/templates/_partials/model_list.html b/src/central/gui/templates/_partials/model_list.html index 27ad552..ac882fb 100644 --- a/src/central/gui/templates/_partials/model_list.html +++ b/src/central/gui/templates/_partials/model_list.html @@ -2,6 +2,12 @@ Driven entirely by field.sub_fields — no adapter-name branching. Vanilla IIFE JS, matching _region_picker.html. #} {% set rows = (form_data[field.name] if form_data and field.name in form_data else field.current_value) or [] %} +{# Auto-detect a bbox row model (min/max lon+lat) to show a read-only map + preview; non-bbox model_lists (StateConfig, TileCoord) render no map. #} +{% set _bbox_keys = ['min_lon', 'min_lat', 'max_lon', 'max_lat'] %} +{% set _ns = namespace(hits=0) %} +{% for sub in field.sub_fields %}{% if sub.name in _bbox_keys %}{% set _ns.hits = _ns.hits + 1 %}{% endif %}{% endfor %} +{% set is_bbox = _ns.hits == 4 %}
{% if field.description %}{{ field.description }}{% endif %} @@ -9,6 +15,8 @@ {{ errors[field.name] }} {% endif %} + {% if is_bbox %}{% include "_partials/bbox_map.html" %}{% endif %} +
{% for sub in field.sub_fields %}{% endfor %} diff --git a/tests/test_gui_adapter_edit.py b/tests/test_gui_adapter_edit.py index 1579304..d7cb04f 100644 --- a/tests/test_gui_adapter_edit.py +++ b/tests/test_gui_adapter_edit.py @@ -190,3 +190,25 @@ class TestGetRoute: ctx = tmpl.TemplateResponse.call_args.kwargs["context"] assert ctx["quota"]["calls_per_month"] == 1920 # 720+480+720 assert ctx["quota"]["blocked"] is False + + +class TestBboxMapPreview: + """v0.9.10 — read-only Leaflet preview of bbox rows in the model_list editor.""" + + def test_tomtom_renders_bbox_map(self): + """bbox row model (min/max lon+lat) → map div + Leaflet init present.""" + fields = describe_fields(TomTomIncidentsAdapter.settings_schema, _SETTINGS) + quota = TomTomIncidentsAdapter.quota_estimate(TomTomIncidentsSettings(**_SETTINGS), 1800) + out = _render("adapters_edit.html", _ctx(_SETTINGS, fields, quota)) + assert "bbox-map" in out # map container present + assert "L.map(" in out and "L.rectangle(" in out # Leaflet init present + + def test_state_511_atis_no_bbox_map(self): + """Non-bbox model_list (StateConfig) → generic editor, no map (no regression).""" + from central.adapters.state_511_atis import State511ATISAdapter + s = {"states": [{"code": "ID", "base_url": "https://511.idaho.gov"}]} + out = _render("adapters_edit.html", + _ctx(s, describe_fields(State511ATISAdapter.settings_schema, s), None, + name="state_511_atis", display="511 ATIS")) + assert "model-list" in out # generic editor still renders + assert "bbox-map" not in out # but no map div
{{ sub.label }}