mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 05:42:22 +00:00
feat(shipwrecks): period picker like air accidents, sharper icons
- Replace the four era chips with the start and end year pickers the air-accidents layer uses (the same RowRange control, ported as is); the era colours stay as a legend under the row. - Rasterize the boat marker at 144 px so large markers stay sharp. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
dcd0f2cccd
commit
112ffd4386
25 files changed
+439
-71
No files matched your search
+1
-1
@@ -40,7 +40,7 @@ How to read this:
|
||||
| **NIFC WFIGS** (Wildland Fire Interagency Geospatial Services, ArcGIS feature service) | Fire Perimeters layer: current interagency wildfire incident perimeters, containment, and incident facts | U.S. public domain (interagency wildland-fire data published through the [NIFC Open Data portal](https://data-nifc.opendata.arcgis.com/)); fetched keyless through `/api/fire-perimeters` (5-minute server cache) with `exceededTransferLimit` paging | "Wildfire perimeters: National Interagency Fire Center (WFIGS)" |
|
||||
| **S2 Underground GhostMaps** (`github.com/s2underground/GhostMaps`) | S2 GhostMaps layer: the current edition of S2's KMZ intel products (Common Intelligence Picture, 30-day kinetic pull, Border Crisis Map, Iran and Yemen products, newest incident drop). Keyless. `/api/ghostmaps` reads the branch head from git's ref advertisement every 15 s (`GHOSTMAPS_POLL_SECONDS`, minimum 5 s), lists the tree through the GitHub REST API only when the head changes (optional `GITHUB_TOKEN`), downloads files by commit-pinned raw URLs into `.gev-cache/ghostmaps/` and pushes new catalogs to browsers over server-sent events | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) (repository README: "S2 Underground © 2025 by S2A1"); non-commercial, attribution required; files are fetched at runtime, never stored in this repository | "S2 GhostMaps intel products: S2 Underground © by S2A1, CC BY-NC-SA 4.0" — shown in-app |
|
||||
| **EMODnet Human Activities — Ship wrecks** (`ows.emodnet-humanactivities.eu/wfs`, layer `emodnet:heritageshipwrecks`) | Shipwrecks layer: about 7,000 wrecks in European waters (name, sinking year and text, vessel type, least depth and its quality, position accuracy, period, cargo, protected status). Keyless WFS GeoJSON, fetched by `/api/shipwrecks` once a day; rows sourced from wrecksite.eu are skipped and text naming people is dropped | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) (EMODnet catalogue record `e965088b-a265-4517-84df-c49b156af8a7`; data originator AND-International, compiled from SHOM, National Monuments Service, Historic England and the Oxford Roman Economy Project); fetched at runtime, not stored in this repository | "Shipwrecks (Europe): EMODnet Human Activities (data originator AND-International, …), CC BY 4.0" — shown in-app |
|
||||
| **Wikidata — shipwrecks** (`query.wikidata.org/sparql`) | Shipwrecks layer: shipwreck items (Q852190) with a coordinate and at least one Wikipedia article: name, ship type, sinking date, death count, description and article link. Keyless, fetched by `/api/shipwrecks` once a day | [CC0](https://creativecommons.org/publicdomain/zero/1.0/); fetched at runtime | "Shipwrecks (worldwide): Wikidata (CC0)" — shown in-app |
|
||||
| **Wikidata — shipwrecks** (`query.wikidata.org/sparql`) | Shipwrecks layer: shipwreck items (Q852190) with a coordinate and at least one Wikipedia article, plus sinking events (Q906512, where the death toll lives): name, ship type, sinking date with its precision, deaths and survivors (counts only), description and article link. Keyless, fetched by `/api/shipwrecks` once a day | [CC0](https://creativecommons.org/publicdomain/zero/1.0/); fetched at runtime | "Shipwrecks (worldwide): Wikidata (CC0)" — shown in-app |
|
||||
| **InciWeb** (inciweb.wildfire.gov) | Per-incident "InciWeb ↗" card links to official incident pages (narratives, evacuation/closure notices) | U.S. government public incident information. Links are matched best-effort by incident name and state, with incident page origin and update times checked on selection. Same-origin `/api/fire-perimeters/inciweb` routes cache the catalog for 1 hour and publication metadata for 30 minutes; linked pages remain InciWeb content | "Incident information: InciWeb (inciweb.wildfire.gov)" |
|
||||
| **FAA Special Use Airspace** (`services6.arcgis.com` FeatureServer `Special_Use_Airspace`) | Prohibited & Restricted Airspace layer: US prohibited (P) and restricted (R) special-use airspace polygons (not the UAS Facility Map or UAS flight restrictions), altitude limits from the `*_VAL`/`*_UOM`/`*_CODE` fields, times of use, and controlling agency | U.S. public domain (FAA aeronautical data published through the [FAA AIS Open Data portal](https://adds-faa.opendata.arcgis.com/)); fetched keyless through `/api/drone-airspace` (1-hour server cache). Informational only — not a substitute for official UAS flight-planning tools | "Prohibited & restricted airspace: FAA Special Use Airspace" |
|
||||
| **OpenFreeMap / OpenMapTiles** | Vector tiles | OpenStreetMap data under ODbL 1.0 | OpenFreeMap © OpenMapTiles |
|
||||
|
||||
+10
-6
@@ -116,14 +116,18 @@ read: EMODnet's long histories (`ship_char`, `obj_desc`) are not, and any
|
||||
sinking or cargo text that names a person (owner, master, a diver who reported
|
||||
it) is dropped whole; deaths appear only as a Wikidata count.
|
||||
|
||||
The globe draws a small hull marker per wreck, ground-clamped with the depth
|
||||
test off and the horizon cull GhostMaps uses, coloured by era: before 1900
|
||||
The globe draws a round marker with a hull and mast per wreck, ground-clamped
|
||||
with the depth test off and the horizon cull GhostMaps uses, sized by deaths as
|
||||
on Air accidents and coloured by era (a legend under the row): before 1900
|
||||
(ancient wrecks dated only by period count here), 1900–1945, after 1945, and
|
||||
date unknown. Four row chips show or hide each era (session-only, not in the
|
||||
share link). Clicking a marker opens a `stateless` card with the name, vessel
|
||||
type, sinking date or year (or the archaeological dating), what happened, deaths,
|
||||
date unknown. A period filter under the row picks a start year and an end year
|
||||
side by side, the same control as Air accidents (two pickers over the years in
|
||||
the data; a reversed pair is swapped); undated wrecks show only when every year
|
||||
is selected. The filter is session-only (not in the share link). Clicking a marker opens a `stateless` card laid out like the air-accident card: deaths and
|
||||
survivors first, then the sinking date or year (or the archaeological dating) and
|
||||
waters, vessel type, what happened,
|
||||
least depth and its quality, how well the position is known, period notes or
|
||||
the Wikidata description, cargo or finds, protected status, waters and source.
|
||||
the Wikidata description, cargo or finds, protected status and source.
|
||||
Clicking the card opens the Wikipedia article (else the Wikidata item), the
|
||||
wreck's own web page when EMODnet lists one, else EMODnet's portal. Voice does
|
||||
not toggle this layer yet (prompt budget, as for the airspace layer).
|
||||
|
||||
@@ -1214,6 +1214,7 @@
|
||||
"src/data/firmsIntensity.js",
|
||||
"src/data/firmsIntensity.test.mjs",
|
||||
"src/ui/layerPanel/RowSwitch.vue",
|
||||
"src/ui/layerPanel/RowRange.vue",
|
||||
"src/ui/layerPanel/RowSlider.vue",
|
||||
"src/sources/phaseTiming.test.mjs",
|
||||
"scripts/build-census-places.mjs",
|
||||
@@ -1271,5 +1272,6 @@
|
||||
"src/layers/shipwrecks/records.test.mjs",
|
||||
"src/layers/shipwrecks/cards.test.mjs",
|
||||
"src/layers/shipwrecks/eras.test.mjs",
|
||||
"src/layers/shipwrecks/period.test.mjs",
|
||||
"src/data/shipwrecksProxy.test.mjs"
|
||||
]
|
||||
@@ -686,6 +686,7 @@
|
||||
"src/ui/rail/focusSafeChildren.js",
|
||||
"src/ui/layerPanel/RowText.vue",
|
||||
"src/ui/layerPanel/RowSwitch.vue",
|
||||
"src/ui/layerPanel/RowRange.vue",
|
||||
"src/ui/layerPanel/RowSlider.vue",
|
||||
"src/ui/railTimeline.js",
|
||||
"src/ui/clearLayersControl.js",
|
||||
@@ -1023,6 +1024,7 @@
|
||||
"src/ui/rail/focusSafeChildren.js",
|
||||
"src/ui/layerPanel/RowText.vue",
|
||||
"src/ui/layerPanel/RowSwitch.vue",
|
||||
"src/ui/layerPanel/RowRange.vue",
|
||||
"src/ui/layerPanel/RowSlider.vue",
|
||||
"src/ui/railTimeline.js",
|
||||
"src/ui/layers.js",
|
||||
@@ -2812,6 +2814,7 @@
|
||||
"src/ui/rail/focusSafeChildren.js",
|
||||
"src/ui/layerPanel/RowText.vue",
|
||||
"src/ui/layerPanel/RowSwitch.vue",
|
||||
"src/ui/layerPanel/RowRange.vue",
|
||||
"src/ui/layerPanel/RowSlider.vue",
|
||||
"src/ui/railTimeline.js",
|
||||
"src/ui/layers.js",
|
||||
@@ -2945,6 +2948,7 @@
|
||||
"src/layers/shipwrecks/records.js",
|
||||
"src/layers/shipwrecks/cards.js",
|
||||
"src/layers/shipwrecks/eras.js",
|
||||
"src/layers/shipwrecks/period.js",
|
||||
"src/app/layers/droneAirspace.js",
|
||||
"src/layers/droneAirspace/index.js",
|
||||
"src/layers/droneAirspace/source.js",
|
||||
@@ -3702,6 +3706,7 @@
|
||||
"src/layers/shipwrecks/records.js",
|
||||
"src/layers/shipwrecks/cards.js",
|
||||
"src/layers/shipwrecks/eras.js",
|
||||
"src/layers/shipwrecks/period.js",
|
||||
"src/app/layers/droneAirspace.js",
|
||||
"src/layers/droneAirspace/index.js",
|
||||
"src/layers/droneAirspace/source.js",
|
||||
@@ -4769,6 +4774,7 @@
|
||||
"src/layers/shipwrecks/records.js",
|
||||
"src/layers/shipwrecks/cards.js",
|
||||
"src/layers/shipwrecks/eras.js",
|
||||
"src/layers/shipwrecks/period.js",
|
||||
"src/sources/httpBody.js",
|
||||
"src/data/geoFileImport.js",
|
||||
"src/i18n/marker.js",
|
||||
@@ -4802,6 +4808,7 @@
|
||||
"src/layers/shipwrecks/records.js",
|
||||
"src/layers/shipwrecks/cards.js",
|
||||
"src/layers/shipwrecks/eras.js",
|
||||
"src/layers/shipwrecks/period.js",
|
||||
"src/layers/ghostMaps/catalog.js",
|
||||
"src/sources/httpBody.js",
|
||||
"src/i18n/marker.js"
|
||||
|
||||
@@ -3439,5 +3439,11 @@
|
||||
"Fatalities: {count}": "الوفيات: {count}",
|
||||
"Survivors: {count}": "الناجون: {count}",
|
||||
"Click the card to open the article ↗": "انقر على البطاقة لفتح المقالة ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "يأتي حطام السفن التراثي الأوروبي من EMODnet Human Activities، ويأتي الحطام البارز في العالم من ويكي بيانات. يبيّن اللون متى غرقت السفينة، ويكبر حجم العلامة مع عدد الوفيات. انقر على علامة لعرض بطاقتها."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "يأتي حطام السفن التراثي الأوروبي من EMODnet Human Activities، ويأتي الحطام البارز في العالم من ويكي بيانات. يبيّن اللون متى غرقت السفينة، ويكبر حجم العلامة مع عدد الوفيات. انقر على علامة لعرض بطاقتها.",
|
||||
"All years": "كل السنوات",
|
||||
"Period": "الفترة",
|
||||
"From": "من",
|
||||
"To": "إلى",
|
||||
"First year of the shipwreck period": "السنة الأولى من فترة الغرق",
|
||||
"Last year of the shipwreck period": "السنة الأخيرة من فترة الغرق"
|
||||
}
|
||||
@@ -3135,5 +3135,11 @@
|
||||
"Fatalities: {count}": "Todesopfer: {count}",
|
||||
"Survivors: {count}": "Überlebende: {count}",
|
||||
"Click the card to open the article ↗": "Karte anklicken, um den Artikel zu öffnen ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Europäische Kulturerbe-Wracks stammen von EMODnet Human Activities, bekannte Wracks weltweit von Wikidata. Die Farbe zeigt, wann das Schiff sank; die Markierung wächst mit der Zahl der Todesopfer. Klicke auf eine Markierung für ihre Karte."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Europäische Kulturerbe-Wracks stammen von EMODnet Human Activities, bekannte Wracks weltweit von Wikidata. Die Farbe zeigt, wann das Schiff sank; die Markierung wächst mit der Zahl der Todesopfer. Klicke auf eine Markierung für ihre Karte.",
|
||||
"All years": "Alle Jahre",
|
||||
"Period": "Zeitraum",
|
||||
"From": "Von",
|
||||
"To": "Bis",
|
||||
"First year of the shipwreck period": "Erstes Jahr des Schiffbruch-Zeitraums",
|
||||
"Last year of the shipwreck period": "Letztes Jahr des Schiffbruch-Zeitraums"
|
||||
}
|
||||
@@ -3211,5 +3211,11 @@
|
||||
"Fatalities: {count}": "Fallecidos: {count}",
|
||||
"Survivors: {count}": "Supervivientes: {count}",
|
||||
"Click the card to open the article ↗": "Haz clic en la ficha para abrir el artículo ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Los pecios históricos europeos proceden de EMODnet Human Activities; los naufragios notables de todo el mundo, de Wikidata. El color indica cuándo se hundió el barco; el marcador crece con el número de muertos. Haz clic en un marcador para ver su ficha."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Los pecios históricos europeos proceden de EMODnet Human Activities; los naufragios notables de todo el mundo, de Wikidata. El color indica cuándo se hundió el barco; el marcador crece con el número de muertos. Haz clic en un marcador para ver su ficha.",
|
||||
"All years": "Todos los años",
|
||||
"Period": "Periodo",
|
||||
"From": "Desde",
|
||||
"To": "Hasta",
|
||||
"First year of the shipwreck period": "Primer año del periodo de naufragios",
|
||||
"Last year of the shipwreck period": "Último año del periodo de naufragios"
|
||||
}
|
||||
@@ -3211,5 +3211,11 @@
|
||||
"Fatalities: {count}": "Morts : {count}",
|
||||
"Survivors: {count}": "Survivants : {count}",
|
||||
"Click the card to open the article ↗": "Cliquez sur la fiche pour ouvrir l'article ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Les épaves patrimoniales européennes viennent d'EMODnet Human Activities ; les épaves notables du monde entier viennent de Wikidata. La couleur indique quand le navire a coulé ; le repère grossit avec le nombre de morts. Cliquez sur un repère pour voir sa fiche."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Les épaves patrimoniales européennes viennent d'EMODnet Human Activities ; les épaves notables du monde entier viennent de Wikidata. La couleur indique quand le navire a coulé ; le repère grossit avec le nombre de morts. Cliquez sur un repère pour voir sa fiche.",
|
||||
"All years": "Toutes les années",
|
||||
"Period": "Période",
|
||||
"From": "Du",
|
||||
"To": "Au",
|
||||
"First year of the shipwreck period": "Première année de la période des naufrages",
|
||||
"Last year of the shipwreck period": "Dernière année de la période des naufrages"
|
||||
}
|
||||
@@ -3211,5 +3211,11 @@
|
||||
"Fatalities: {count}": "Vittime: {count}",
|
||||
"Survivors: {count}": "Sopravvissuti: {count}",
|
||||
"Click the card to open the article ↗": "Fai clic sulla scheda per aprire l'articolo ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "I relitti storici europei provengono da EMODnet Human Activities; i relitti noti di tutto il mondo da Wikidata. Il colore indica quando la nave è affondata; l'indicatore cresce con il numero di vittime. Fai clic su un indicatore per la sua scheda."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "I relitti storici europei provengono da EMODnet Human Activities; i relitti noti di tutto il mondo da Wikidata. Il colore indica quando la nave è affondata; l'indicatore cresce con il numero di vittime. Fai clic su un indicatore per la sua scheda.",
|
||||
"All years": "Tutti gli anni",
|
||||
"Period": "Periodo",
|
||||
"From": "Dal",
|
||||
"To": "Al",
|
||||
"First year of the shipwreck period": "Primo anno del periodo dei naufragi",
|
||||
"Last year of the shipwreck period": "Ultimo anno del periodo dei naufragi"
|
||||
}
|
||||
@@ -3059,5 +3059,11 @@
|
||||
"Fatalities: {count}": "死者: {count}",
|
||||
"Survivors: {count}": "生存者: {count}",
|
||||
"Click the card to open the article ↗": "カードをクリックして記事を開く ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "欧州の歴史的な難破船はEMODnet Human Activities、世界の著名な難破船はWikidataによります。色は沈没した時期を示し、マーカーは死者数に応じて大きくなります。マーカーをクリックするとカードが開きます。"
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "欧州の歴史的な難破船はEMODnet Human Activities、世界の著名な難破船はWikidataによります。色は沈没した時期を示し、マーカーは死者数に応じて大きくなります。マーカーをクリックするとカードが開きます。",
|
||||
"All years": "全期間",
|
||||
"Period": "期間",
|
||||
"From": "開始",
|
||||
"To": "終了",
|
||||
"First year of the shipwreck period": "難破の期間の開始年",
|
||||
"Last year of the shipwreck period": "難破の期間の終了年"
|
||||
}
|
||||
@@ -3211,5 +3211,11 @@
|
||||
"Fatalities: {count}": "Mortos: {count}",
|
||||
"Survivors: {count}": "Sobreviventes: {count}",
|
||||
"Click the card to open the article ↗": "Clique na ficha para abrir o artigo ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Os naufrágios históricos europeus vêm do EMODnet Human Activities; os naufrágios notáveis do mundo vêm do Wikidata. A cor mostra quando o navio afundou; o marcador cresce com o número de mortos. Clique num marcador para ver a ficha."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Os naufrágios históricos europeus vêm do EMODnet Human Activities; os naufrágios notáveis do mundo vêm do Wikidata. A cor mostra quando o navio afundou; o marcador cresce com o número de mortos. Clique num marcador para ver a ficha.",
|
||||
"All years": "Todos os anos",
|
||||
"Period": "Período",
|
||||
"From": "Início",
|
||||
"To": "Fim",
|
||||
"First year of the shipwreck period": "Primeiro ano do período dos naufrágios",
|
||||
"Last year of the shipwreck period": "Último ano do período dos naufrágios"
|
||||
}
|
||||
@@ -3287,5 +3287,11 @@
|
||||
"Fatalities: {count}": "Погибших: {count}",
|
||||
"Survivors: {count}": "Выживших: {count}",
|
||||
"Click the card to open the article ↗": "Нажмите на карточку, чтобы открыть статью ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Исторические затонувшие суда Европы — из EMODnet Human Activities, известные кораблекрушения по всему миру — из Wikidata. Цвет показывает, когда судно затонуло; маркер растёт с числом погибших. Нажмите на маркер, чтобы открыть карточку."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Исторические затонувшие суда Европы — из EMODnet Human Activities, известные кораблекрушения по всему миру — из Wikidata. Цвет показывает, когда судно затонуло; маркер растёт с числом погибших. Нажмите на маркер, чтобы открыть карточку.",
|
||||
"All years": "Все годы",
|
||||
"Period": "Период",
|
||||
"From": "С",
|
||||
"To": "По",
|
||||
"First year of the shipwreck period": "Первый год периода кораблекрушений",
|
||||
"Last year of the shipwreck period": "Последний год периода кораблекрушений"
|
||||
}
|
||||
@@ -3287,5 +3287,11 @@
|
||||
"Fatalities: {count}": "Загиблих: {count}",
|
||||
"Survivors: {count}": "Тих, хто вижив: {count}",
|
||||
"Click the card to open the article ↗": "Натисніть картку, щоб відкрити статтю ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Історичні затонулі судна Європи — з EMODnet Human Activities, відомі корабельні аварії світу — з Wikidata. Колір показує, коли судно затонуло; маркер збільшується з кількістю загиблих. Натисніть маркер, щоб відкрити картку."
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "Історичні затонулі судна Європи — з EMODnet Human Activities, відомі корабельні аварії світу — з Wikidata. Колір показує, коли судно затонуло; маркер збільшується з кількістю загиблих. Натисніть маркер, щоб відкрити картку.",
|
||||
"All years": "Усі роки",
|
||||
"Period": "Період",
|
||||
"From": "З",
|
||||
"To": "По",
|
||||
"First year of the shipwreck period": "Перший рік періоду корабельних аварій",
|
||||
"Last year of the shipwreck period": "Останній рік періоду корабельних аварій"
|
||||
}
|
||||
@@ -3059,5 +3059,11 @@
|
||||
"Fatalities: {count}": "遇难:{count}",
|
||||
"Survivors: {count}": "幸存者:{count}",
|
||||
"Click the card to open the article ↗": "点击卡片打开文章 ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "欧洲历史沉船来自 EMODnet Human Activities;全球知名沉船来自 Wikidata。颜色表示船只沉没的时期,标记大小随遇难人数增加。点击标记查看卡片。"
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "欧洲历史沉船来自 EMODnet Human Activities;全球知名沉船来自 Wikidata。颜色表示船只沉没的时期,标记大小随遇难人数增加。点击标记查看卡片。",
|
||||
"All years": "所有年份",
|
||||
"Period": "时段",
|
||||
"From": "起",
|
||||
"To": "止",
|
||||
"First year of the shipwreck period": "沉船时段的起始年份",
|
||||
"Last year of the shipwreck period": "沉船时段的结束年份"
|
||||
}
|
||||
@@ -3059,5 +3059,11 @@
|
||||
"Fatalities: {count}": "罹難:{count}",
|
||||
"Survivors: {count}": "倖存者:{count}",
|
||||
"Click the card to open the article ↗": "點擊卡片開啟文章 ↗",
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "歐洲歷史沉船來自 EMODnet Human Activities;全球知名沉船來自 Wikidata。顏色表示船隻沉沒的時期,標記大小隨罹難人數增加。點擊標記查看卡片。"
|
||||
"European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.": "歐洲歷史沉船來自 EMODnet Human Activities;全球知名沉船來自 Wikidata。顏色表示船隻沉沒的時期,標記大小隨罹難人數增加。點擊標記查看卡片。",
|
||||
"All years": "所有年份",
|
||||
"Period": "時段",
|
||||
"From": "起",
|
||||
"To": "迄",
|
||||
"First year of the shipwreck period": "沉船時段的起始年份",
|
||||
"Last year of the shipwreck period": "沉船時段的結束年份"
|
||||
}
|
||||
@@ -19,15 +19,3 @@ export function eraOf(record) {
|
||||
if (year <= 1945) return '1900-1945';
|
||||
return 'after-1945';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a requested set of eras; unknown names are ignored and an empty
|
||||
* or missing request keeps the current set.
|
||||
* @param {unknown} value Array of era ids.
|
||||
* @param {Set<string>} current
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
export function normalizeEras(value, current) {
|
||||
if (!Array.isArray(value)) return new Set(current);
|
||||
return new Set(value.filter((era) => SHIPWRECK_ERAS.includes(era)));
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { SHIPWRECK_ERAS, eraOf, normalizeEras } from './eras.js';
|
||||
import { eraOf } from './eras.js';
|
||||
|
||||
test('each wreck falls in one era', () => {
|
||||
assert.equal(eraOf({ year: 1588 }), 'before-1900');
|
||||
@@ -11,13 +11,3 @@ test('each wreck falls in one era', () => {
|
||||
assert.equal(eraOf({ year: null }), 'undated');
|
||||
assert.equal(eraOf(null), 'undated');
|
||||
});
|
||||
|
||||
test('era requests keep known eras only', () => {
|
||||
const all = new Set(SHIPWRECK_ERAS);
|
||||
assert.deepEqual(
|
||||
[...normalizeEras(['after-1945', 'bogus'], all)],
|
||||
['after-1945'],
|
||||
);
|
||||
assert.deepEqual([...normalizeEras('after-1945', all)], SHIPWRECK_ERAS);
|
||||
assert.deepEqual([...normalizeEras([], all)], []);
|
||||
});
|
||||
@@ -2,10 +2,17 @@ import * as Cesium from 'cesium';
|
||||
import { formatNumber, msg, t, tn } from '../../i18n/index.js';
|
||||
import { createHorizonCull } from '../streetLevel/view.js';
|
||||
import { ERA_COLORS, ERA_LABELS, describeShipwreck } from './cards.js';
|
||||
import { SHIPWRECK_ERAS, eraOf, normalizeEras } from './eras.js';
|
||||
import { SHIPWRECK_ERAS, eraOf } from './eras.js';
|
||||
import {
|
||||
inPeriod,
|
||||
normalizePeriod,
|
||||
periodRange,
|
||||
yearExtent,
|
||||
} from './period.js';
|
||||
export * from './records.js';
|
||||
export * from './cards.js';
|
||||
export * from './eras.js';
|
||||
export * from './period.js';
|
||||
export { createShipwrecksSource } from './source.js';
|
||||
|
||||
export const SHIPWRECKS_LAYER_ID = 'shipwrecks';
|
||||
@@ -28,13 +35,14 @@ export function markerPixelSize(casualties) {
|
||||
|
||||
/**
|
||||
* A round marker in the era's colour with a sinking hull and mast in the
|
||||
* middle, as an SVG data URL.
|
||||
* middle, as an SVG data URL. It is rasterized at 144 px, four times the
|
||||
* largest marker, so big markers stay sharp on high-density screens.
|
||||
* @param {string} color CSS colour.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function wreckMarkerImage(color) {
|
||||
const svg =
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36">` +
|
||||
`<svg xmlns="http://www.w3.org/2000/svg" width="144" height="144" viewBox="0 0 36 36">` +
|
||||
`<circle cx="18" cy="18" r="16" fill="${color}" stroke="#ffffff" stroke-width="2"/>` +
|
||||
`<path d="M8 21h20l-3.5 5h-13z" fill="#0b1420"/>` +
|
||||
`<path d="M17 8v12M17 9l6 7h-6" fill="none" stroke="#0b1420" stroke-width="2.2" stroke-linejoin="round"/>` +
|
||||
@@ -69,7 +77,9 @@ export function createShipwrecksLayer({
|
||||
let _selectedId = null;
|
||||
let _selectedCardId = null;
|
||||
let _selectedLink = null;
|
||||
let _eras = new Set(SHIPWRECK_ERAS);
|
||||
let _period = { from: null, to: null };
|
||||
let _extent = null;
|
||||
let _range = null;
|
||||
const _images = Object.fromEntries(
|
||||
SHIPWRECK_ERAS.map((era) => [era, wreckMarkerImage(ERA_COLORS[era])]),
|
||||
);
|
||||
@@ -86,16 +96,16 @@ export function createShipwrecksLayer({
|
||||
onChange: render,
|
||||
});
|
||||
|
||||
/** Shown: its era chip is on. */
|
||||
/** Shown: its year is inside the period (undated ones only for all years). */
|
||||
function visible(record) {
|
||||
return Boolean(record) && _eras.has(eraOf(record));
|
||||
return Boolean(record) && inPeriod(record, _range);
|
||||
}
|
||||
|
||||
function applyPin(pin) {
|
||||
pin.entity.show = pin.inFront && visible(pin.record);
|
||||
}
|
||||
|
||||
/** Re-apply the era chips to every marker and the open card. */
|
||||
/** Re-apply the period to every marker and the open card. */
|
||||
function applyFilter() {
|
||||
for (const pin of _pins) applyPin(pin);
|
||||
if (_selectedId && !visible(_records.get(_selectedId))) clearSelection();
|
||||
@@ -105,6 +115,8 @@ export function createShipwrecksLayer({
|
||||
|
||||
function rebuild(records) {
|
||||
_records = new Map(records.map((record) => [record.id, record]));
|
||||
_extent = yearExtent(records);
|
||||
_range = periodRange(_period, _extent);
|
||||
_pins = [];
|
||||
if (!_dataSource) return;
|
||||
_dataSource.entities.suspendEvents();
|
||||
@@ -257,18 +269,46 @@ export function createShipwrecksLayer({
|
||||
picking?.unregisterPickOwner?.(SHIPWRECKS_LAYER_ID);
|
||||
}
|
||||
|
||||
function toggleEra(era) {
|
||||
if (_eras.has(era)) _eras.delete(era);
|
||||
else _eras.add(era);
|
||||
applyFilter();
|
||||
}
|
||||
|
||||
function countOf(era) {
|
||||
let count = 0;
|
||||
for (const record of _records.values()) if (eraOf(record) === era) count++;
|
||||
for (const record of _records.values())
|
||||
if (eraOf(record) === era && inPeriod(record, _range)) count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
/** The start and end year pickers, side by side, as on Air accidents. */
|
||||
function periodControls() {
|
||||
if (!_extent || _extent.min === _extent.max) return {};
|
||||
const range = periodRange(_period, _extent);
|
||||
const valueText = range.all
|
||||
? t('All years')
|
||||
: range.from === range.to
|
||||
? String(range.from)
|
||||
: `${range.from}–${range.to}`;
|
||||
const options = [];
|
||||
for (let year = _extent.max; year >= _extent.min; year--)
|
||||
options.push({ value: year, label: String(year) });
|
||||
return {
|
||||
range: {
|
||||
id: 'period',
|
||||
label: t('Period'),
|
||||
valueText,
|
||||
from: {
|
||||
label: t('From'),
|
||||
ariaLabel: t('First year of the shipwreck period'),
|
||||
value: range.from,
|
||||
},
|
||||
to: {
|
||||
label: t('To'),
|
||||
ariaLabel: t('Last year of the shipwreck period'),
|
||||
value: range.to,
|
||||
},
|
||||
options,
|
||||
params: (end, value) => ({ [end]: Number(value) }),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const layer = {
|
||||
id: SHIPWRECKS_LAYER_ID,
|
||||
name: 'Shipwrecks',
|
||||
@@ -347,17 +387,16 @@ export function createShipwrecksLayer({
|
||||
_rowListener = null;
|
||||
},
|
||||
|
||||
/** The era filter: `eras` lists the era chips that are on. */
|
||||
/** The period filter: `from` and `to` are years (null = oldest / latest in the data). */
|
||||
getParams() {
|
||||
return { eras: SHIPWRECK_ERAS.filter((era) => _eras.has(era)) };
|
||||
return { ..._period };
|
||||
},
|
||||
|
||||
setParams(params = {}) {
|
||||
if (!('eras' in params)) return true;
|
||||
const next = normalizeEras(params.eras, _eras);
|
||||
if (next.size === _eras.size && [...next].every((era) => _eras.has(era)))
|
||||
return true;
|
||||
_eras = next;
|
||||
const next = normalizePeriod(params, _period);
|
||||
if (next.from === _period.from && next.to === _period.to) return true;
|
||||
_period = next;
|
||||
_range = periodRange(_period, _extent);
|
||||
applyFilter();
|
||||
return true;
|
||||
},
|
||||
@@ -368,17 +407,16 @@ export function createShipwrecksLayer({
|
||||
|
||||
getRowControls() {
|
||||
return {
|
||||
chips: SHIPWRECK_ERAS.map((era) => ({
|
||||
id: era,
|
||||
label: t(ERA_LABELS[era]),
|
||||
active: _eras.has(era),
|
||||
chips: [],
|
||||
...periodControls(),
|
||||
// The colours are a legend: when the ship sank.
|
||||
legend: SHIPWRECK_ERAS.map((era) => ({
|
||||
text: t(ERA_LABELS[era]),
|
||||
color: ERA_COLORS[era],
|
||||
title: tn(countOf(era), '{count} wreck', '{count} wrecks', {
|
||||
blurb: tn(countOf(era), '{count} wreck', '{count} wrecks', {
|
||||
count: formatNumber(countOf(era)),
|
||||
}),
|
||||
onClick: () => toggleEra(era),
|
||||
})),
|
||||
legend: [],
|
||||
info: t('EMODnet (European waters) · Wikidata (worldwide)'),
|
||||
infoTitle: t(
|
||||
'European heritage wrecks come from EMODnet Human Activities; notable wrecks worldwide come from Wikidata. Colour shows when the ship sank; marker size grows with deaths. Click a marker for its card.',
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* The sinking year, or null when it is unknown (ancient wrecks dated only by
|
||||
* period, "C1st AD", have none).
|
||||
*/
|
||||
export function recordYear(record) {
|
||||
return Number.isInteger(record?.year) ? record.year : null;
|
||||
}
|
||||
|
||||
/** First and last year present in the records (null when none is dated). */
|
||||
export function yearExtent(records) {
|
||||
let min = Infinity;
|
||||
let max = -Infinity;
|
||||
for (const record of records) {
|
||||
const year = recordYear(record);
|
||||
if (year === null) continue;
|
||||
if (year < min) min = year;
|
||||
if (year > max) max = year;
|
||||
}
|
||||
return Number.isFinite(min) ? { min, max } : null;
|
||||
}
|
||||
|
||||
function yearParam(value) {
|
||||
if (value === null) return null;
|
||||
const year =
|
||||
typeof value === 'string' && /^-?\d+$/.test(value) ? Number(value) : value;
|
||||
return Number.isInteger(year) && Math.abs(year) < 10000 ? year : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a (partial) period request into the current one. `from: null` means
|
||||
* the first year in the data and `to: null` the latest; an invalid year keeps
|
||||
* the current one.
|
||||
* @param {{from?: number|string|null, to?: number|string|null}} params
|
||||
* @param {{from: number|null, to: number|null}} current
|
||||
* @returns {{from: number|null, to: number|null}}
|
||||
*/
|
||||
export function normalizePeriod(
|
||||
params = {},
|
||||
current = { from: null, to: null },
|
||||
) {
|
||||
const next = { ...current };
|
||||
for (const end of ['from', 'to']) {
|
||||
if (!(end in params)) continue;
|
||||
const year = yearParam(params[end]);
|
||||
if (year !== undefined) next[end] = year;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* The inclusive year range a period covers over the data's extent. Years are
|
||||
* clamped to the data, and a start after the end is read as the reverse.
|
||||
* @returns {{from: number, to: number, all: boolean}|null}
|
||||
*/
|
||||
export function periodRange(period, extent) {
|
||||
if (!extent) return null;
|
||||
const clamp = (year, fallback) =>
|
||||
Math.min(extent.max, Math.max(extent.min, year ?? fallback));
|
||||
const start = clamp(period.from, extent.min);
|
||||
const end = clamp(period.to, extent.max);
|
||||
const from = Math.min(start, end);
|
||||
const to = Math.max(start, end);
|
||||
return { from, to, all: from === extent.min && to === extent.max };
|
||||
}
|
||||
|
||||
/** Whether a record falls in the range; undated records only when all years show. */
|
||||
export function inPeriod(record, range) {
|
||||
if (!range || range.all) return true;
|
||||
const year = recordYear(record);
|
||||
return year !== null && year >= range.from && year <= range.to;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
inPeriod,
|
||||
normalizePeriod,
|
||||
periodRange,
|
||||
recordYear,
|
||||
yearExtent,
|
||||
} from './period.js';
|
||||
|
||||
const RECORDS = [
|
||||
{ year: 1588 },
|
||||
{ year: 1912 },
|
||||
{ year: 2012 },
|
||||
{ year: null, ancient: true },
|
||||
];
|
||||
|
||||
test('years come from the record; the extent ignores undated wrecks', () => {
|
||||
assert.equal(recordYear(RECORDS[1]), 1912);
|
||||
assert.equal(recordYear(RECORDS[3]), null);
|
||||
assert.deepEqual(yearExtent(RECORDS), { min: 1588, max: 2012 });
|
||||
assert.equal(yearExtent([{ year: null }]), null);
|
||||
});
|
||||
|
||||
test('all years show every wreck; a narrower period hides undated ones', () => {
|
||||
const extent = yearExtent(RECORDS);
|
||||
const all = periodRange({ from: null, to: null }, extent);
|
||||
assert.deepEqual(all, { from: 1588, to: 2012, all: true });
|
||||
assert.ok(RECORDS.every((record) => inPeriod(record, all)));
|
||||
const wars = periodRange(normalizePeriod({ from: '1914', to: 1945 }), extent);
|
||||
assert.deepEqual(
|
||||
RECORDS.map((record) => inPeriod(record, wars)),
|
||||
[false, false, false, false],
|
||||
);
|
||||
const reversed = periodRange({ from: 1950, to: 1900 }, extent);
|
||||
assert.deepEqual(reversed, { from: 1900, to: 1950, all: false });
|
||||
assert.equal(inPeriod(RECORDS[1], reversed), true);
|
||||
});
|
||||
+21
-2
@@ -445,6 +445,8 @@ export class LayerPanel {
|
||||
this._selectListItem(layerId, itemId, generation),
|
||||
onSlider: (layerId, sliderId, value) =>
|
||||
this._moveSlider(layerId, sliderId, value, generation),
|
||||
onRange: (layerId, rangeId, end, value) =>
|
||||
this._pickRangeEnd(layerId, rangeId, end, value, generation),
|
||||
}),
|
||||
id === null ? this._toggleContainer : this._groupContainers.get(id),
|
||||
);
|
||||
@@ -474,7 +476,7 @@ export class LayerPanel {
|
||||
}
|
||||
|
||||
/**
|
||||
* A layer's row chips, one-of-many switch, slider, color legend, info line
|
||||
* A layer's row chips, one-of-many switch, slider, start/end pickers, color legend, info line
|
||||
* and ordered list. The block
|
||||
* is hidden while the layer is off (or while a dependency owner has
|
||||
* surrendered it), so a quiet row stays quiet. Chips and list items are
|
||||
@@ -501,6 +503,7 @@ export class LayerPanel {
|
||||
const infoText = controls?.info ? String(controls.info) : '';
|
||||
const segmented = controls?.segmented || null;
|
||||
const slider = controls?.slider || null;
|
||||
const range = controls?.range || null;
|
||||
// The info line, once shown, stays in place and hides when empty.
|
||||
if (infoText) row.info = true;
|
||||
return {
|
||||
@@ -509,10 +512,12 @@ export class LayerPanel {
|
||||
legend.length === 0 &&
|
||||
!infoText &&
|
||||
!segmented &&
|
||||
!slider,
|
||||
!slider &&
|
||||
!range,
|
||||
chips,
|
||||
segmented,
|
||||
slider,
|
||||
range,
|
||||
legend,
|
||||
info: row.info
|
||||
? { text: infoText, title: controls?.infoTitle || '' }
|
||||
@@ -577,6 +582,20 @@ export class LayerPanel {
|
||||
this.setLayerParams(id, slider.params(value), { origin: 'user' });
|
||||
}
|
||||
|
||||
_pickRangeEnd(id, rangeId, end, value, generation = this._generation) {
|
||||
if (this._destroyed || this._generation !== generation) return;
|
||||
const range = this._rowControlsFor(id)?.range;
|
||||
if (
|
||||
!range ||
|
||||
range.id !== rangeId ||
|
||||
(end !== 'from' && end !== 'to') ||
|
||||
typeof range.params !== 'function' ||
|
||||
!this.isEnabled(id)
|
||||
)
|
||||
return;
|
||||
this.setLayerParams(id, range.params(end, value), { origin: 'user' });
|
||||
}
|
||||
|
||||
_selectListItem(id, itemId, generation = this._generation) {
|
||||
if (this._destroyed || this._generation !== generation) return;
|
||||
const item = this._rowControlsFor(id)?.list?.items?.find(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import RailChips from '../rail/RailChips.vue';
|
||||
import RailRowList from '../rail/RailRowList.vue';
|
||||
import RowRange from './RowRange.vue';
|
||||
import RowSlider from './RowSlider.vue';
|
||||
import RowSwitch from './RowSwitch.vue';
|
||||
import RowText from './RowText.vue';
|
||||
@@ -15,6 +16,7 @@ defineProps({
|
||||
onChip: { type: Function, default: () => {} },
|
||||
onListItem: { type: Function, default: () => {} },
|
||||
onSlider: { type: Function, default: () => {} },
|
||||
onRange: { type: Function, default: () => {} },
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -68,6 +70,13 @@ defineProps({
|
||||
:on-input="
|
||||
(value) => onSlider(item.id, item.controls.slider.id, value)
|
||||
"
|
||||
/><RowRange
|
||||
v-if="item.controls.range"
|
||||
:range="item.controls.range"
|
||||
:on-select="
|
||||
(end, value) =>
|
||||
onRange(item.id, item.controls.range.id, end, value)
|
||||
"
|
||||
/><span
|
||||
v-for="(entry, index) in item.controls.legend"
|
||||
:key="index"
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<script setup>
|
||||
// A row's start and end pickers on one line (e.g. the Air Accidents period).
|
||||
// Both ends share one option list; `onSelect` reports which end moved
|
||||
// ('from' or 'to') and the chosen option value.
|
||||
defineProps({
|
||||
range: { type: Object, required: true },
|
||||
onSelect: { type: Function, default: () => {} },
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="data-toggle-range" :data-range-id="range.id"
|
||||
><div class="data-toggle-slider-head"
|
||||
><span class="data-toggle-slider-label">{{ range.label }}</span
|
||||
><span class="data-toggle-slider-value gev-slider-value">{{
|
||||
range.valueText
|
||||
}}</span></div
|
||||
><div class="data-toggle-range-ends"
|
||||
><label
|
||||
v-for="end in ['from', 'to']"
|
||||
:key="end"
|
||||
class="data-toggle-range-end"
|
||||
><span class="data-toggle-range-caption">{{ range[end].label }}</span
|
||||
><select
|
||||
class="data-toggle-range-select"
|
||||
:data-range-end="end"
|
||||
:aria-label="range[end].ariaLabel || undefined"
|
||||
:value="String(range[end].value)"
|
||||
@change="onSelect(end, $event.target.value)"
|
||||
><option
|
||||
v-for="option in range.options"
|
||||
:key="option.value"
|
||||
:value="String(option.value)"
|
||||
>{{ option.label }}</option
|
||||
></select
|
||||
></label
|
||||
></div
|
||||
></div
|
||||
>
|
||||
</template>
|
||||
@@ -159,3 +159,62 @@ test('a row switch and slider render their markup and report choices', () => {
|
||||
assert.equal(segments[1].classList.contains('active'), true);
|
||||
render(null, container);
|
||||
});
|
||||
|
||||
test('a row range renders start and end pickers and reports which end moved', () => {
|
||||
document.body.replaceChildren();
|
||||
const container = document.body.appendChild(document.createElement('div'));
|
||||
const picks = [];
|
||||
const item = {
|
||||
...row(false),
|
||||
key: 'air-accidents',
|
||||
id: 'air-accidents',
|
||||
controls: {
|
||||
hidden: false,
|
||||
chips: [],
|
||||
segmented: null,
|
||||
slider: null,
|
||||
range: {
|
||||
id: 'period',
|
||||
label: 'Period',
|
||||
valueText: '2010–2024',
|
||||
from: { label: 'From', ariaLabel: 'First year', value: 2010 },
|
||||
to: { label: 'To', ariaLabel: 'Last year', value: 2024 },
|
||||
options: [2024, 2010, 2007].map((year) => ({
|
||||
value: year,
|
||||
label: String(year),
|
||||
})),
|
||||
},
|
||||
legend: [],
|
||||
info: null,
|
||||
list: null,
|
||||
},
|
||||
};
|
||||
render(
|
||||
h(LayerRows, {
|
||||
items: [item],
|
||||
onRange: (id, rangeId, end, value) =>
|
||||
picks.push([id, rangeId, end, value]),
|
||||
}),
|
||||
container,
|
||||
);
|
||||
const block = container.querySelector('.data-toggle-range');
|
||||
assert.equal(block.dataset.rangeId, 'period');
|
||||
assert.equal(
|
||||
block.querySelector('.data-toggle-slider-value').textContent,
|
||||
'2010–2024',
|
||||
);
|
||||
const [from, to] = block.querySelectorAll('select.data-toggle-range-select');
|
||||
assert.equal(from.getAttribute('aria-label'), 'First year');
|
||||
assert.equal(from.value, '2010');
|
||||
assert.equal(to.value, '2024');
|
||||
assert.deepEqual(
|
||||
[...block.querySelectorAll('.data-toggle-range-caption')].map(
|
||||
(caption) => caption.textContent,
|
||||
),
|
||||
['From', 'To'],
|
||||
);
|
||||
to.value = '2007';
|
||||
to.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
assert.deepEqual(picks, [['air-accidents', 'period', 'to', '2007']]);
|
||||
render(null, container);
|
||||
});
|
||||
@@ -1067,6 +1067,43 @@ html:not([lang='en']) .data-row-list-text {
|
||||
letter-spacing: 0.8px;
|
||||
}
|
||||
|
||||
/* Optional start and end pickers on one line (Air Accidents period). The
|
||||
shared field style paints each select. */
|
||||
.data-toggle-range {
|
||||
flex-basis: 100%;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.data-toggle-range-ends {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.data-toggle-range-end {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.data-toggle-range-caption {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 8px;
|
||||
letter-spacing: 1.2px;
|
||||
color: var(--text-dim);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.data-toggle-range-select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 24px;
|
||||
font: 10px/1 var(--font-mono);
|
||||
}
|
||||
|
||||
/* Optional per-layer quantitative slider (Active Fires minimum FRP). The
|
||||
track is painted by the layer (`--slider-track`, a stepped color ramp), so
|
||||
the slider doubles as the marker legend; ticks sit under their thresholds. */
|
||||
|
||||
Reference in new issue
Block a user