aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--eslint.config.mjs27
-rw-r--r--example/example.html (renamed from example.html)4
-rw-r--r--example/style.css (renamed from style2.css)0
-rw-r--r--index.html212
-rw-r--r--main.js8019
-rw-r--r--package-lock.json1712
-rw-r--r--package.json12
-rw-r--r--style.css367
-rw-r--r--vite.config.js10
10 files changed, 7107 insertions, 3258 deletions
diff --git a/README.md b/README.md
index 0ed0d3b..1943c72 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ to preview the production site.
# Author
-© 2024 [Guilhem Moulin](https://guilhem.se).
+© 2024-2025 [Guilhem Moulin](https://guilhem.se).
# Licensing
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..72b17bd
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,27 @@
+import js from "@eslint/js";
+import globals from "globals";
+import css from "@eslint/css";
+import { defineConfig } from "eslint/config";
+
+
+export default defineConfig([
+ {
+ files: ["src/*.js", "./*.js"],
+ plugins: { js },
+ extends: ["js/recommended"],
+ languageOptions: {
+ globals: globals.browser,
+ },
+ },
+ {
+ files: ["src/*.css", "./*.css"],
+ plugins: { css },
+ language: "css/css",
+ extends: ["css/recommended"],
+ rules: {
+ "css/no-important": "off",
+ /* too many false positives as of @eslint/css 0.9.0 */
+ "css/no-invalid-properties": "off",
+ },
+ },
+]);
diff --git a/example.html b/example/example.html
index 5dd0e01..d003598 100644
--- a/example.html
+++ b/example/example.html
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hemsida</title>
- <link rel="stylesheet" href="/style2.css">
+ <link rel="stylesheet" href="/example/style.css">
</head>
<body>
<div id="wrapper">
@@ -15,7 +15,7 @@
man välja lager, ladda ner kartan som PNG-fil, och få information om
de olika föremålen.</p>
</div>
- <iframe id="map" src="/#z=0&basemap=topowebb_nedtonad&layers=svk_lines+svk_pylons+svk_stations+svk_planned+mrr_appr_ec+mrr_appl_ec+mrr_appr_ogd+mrr_appl_ogd+mrr_appr_met+mrr_appl_met+mrr_appr_dl+mrr_appr_pc+vbk_area_current+vbk_station_completed+vbk_station_processed+vbk_station_approved+gigafactories" title="Webbkarta"></iframe>
+ <iframe id="map" src="/" title="Webbkarta"></iframe>
</div>
</body>
</html>
diff --git a/style2.css b/example/style.css
index dfeaee5..dfeaee5 100644
--- a/style2.css
+++ b/example/style.css
diff --git a/index.html b/index.html
index 4b4804e..1522c6a 100644
--- a/index.html
+++ b/index.html
@@ -11,70 +11,188 @@
<div id="map-control-container" aria-hidden="true">
<div id="layer-selection-panel"></div>
<div id="map-legend-panel"></div>
+ <div id="measure-panel"></div>
<div id="map-menu"></div>
</div>
<div id="popup"></div>
- <div class="modal" id="modal-info" tabindex="-1" aria-hidden="true">
+ <div class="modal" id="info-modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
<div class="modal-content">
- <div class="modal-header py-2">
- <h5 class="my-0">Källor och licensinformation</h5>
+ <div class="modal-header">
+ <div class="h5 m-0">Källor och licensinformation</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" title="Stäng" aria-label="Stäng"></button>
</div>
- <div class="modal-body pt-2">
- <ul class="mb-2">
- <li><i>Transmissionsnät för el</i> från
- <a href="https://svk.se" target="_blank">Svenska Kraftnät (SvK)</a>.
+ <div id="info-body" class="modal-body">
+ <div id="info-accordion" class="accordion accordion-flush"></div>
+ <ul class="list-group list-group-flush">
+ <li class="list-group-item">
+ <h6>Bakgrund kartor</h6>
+ <p>&copy; <a href="https://lantmateriet.se" target="_blank">Lantmäteriet</a></p>
+ <p>Licensvillkor: <a href="https://www.lantmateriet.se/sv/geodata/vara-produkter/oppna-data/#anchor-1" target="_blank">CC0 1.0 Universiell</a></p>
</li>
- <li><i>Dammregistret</i> från
- <a href="https://smhi.se" target="_blank">Sveriges meteorologiska och hydrologiska institut (SMHI)</a>,
- CC-BY-4.0 (<a href="https://www.smhi.se/data/oppna-data/villkor-for-anvandning-1.30622" target="_blank">öppna data</a>).
+ <li class="list-group-item">
+ <h6>Webbkartan</h6>
+ <p>&copy; 2024-2025 Guilhem Moulin</p>
+ <p>Licensvillkor: <a href="https://www.gnu.org/licenses/agpl-3.0.en.html" target="_blank">AGPLv3+</a></p>
+ <p class="small text-muted"><i class="bi bi-file-earmark-code"></i>
+ <a href="https://git.guilhem.org/KlimatanalysNorr/webmap" target="_blank">Källkod <i class="bi bi-box-arrow-up-right"></i></a></p>
</li>
- <li><i>Mineralrättsregistret</i> från
- <a href="https://www.sgu.se/bergsstaten/" target="_blank">Bergsstaten</a>,
- CC0 (<a href="https://creativecommons.org/publicdomain/zero/1.0/deed.sv" target="_blank">öppna data</a>).
+ <li class="list-group-item">
+ <h6>Backend verktyg</h6>
+ <p>&copy; 2024-2025 Guilhem Moulin</p>
+ <p>Licensvillkor: <a href="https://www.gnu.org/licenses/gpl-3.0.en.html" target="_blank">GPLv3+</a> och
+ (endast CGI) <a href="https://www.gnu.org/licenses/agpl-3.0.en.html" target="_blank">AGPLv3+</a></p>
+ <p class="small text-muted"><i class="bi bi-file-earmark-code"></i>
+ <a href="https://git.guilhem.org/KlimatanalysNorr/tools" target="_blank">Källkod <i class="bi bi-box-arrow-up-right"></i></a></p>
</li>
- <li><i>Vindbruk</i> från
- <a href="https://www.energimyndigheten.se/energisystem-och-analys/elproduktion/vindkraft/vindbrukskollen/" target="_blank">Länsstyrelserna och Energimyndigheten</a>,
- CC0 (<a href="https://ext-geodatakatalog-forv.lansstyrelsen.se/GeodataKatalogen/codelist/metadata/anvandningsrestriktioner.xml#CC01.0" target="_blank">öppna data</a>).
- </li>
- <li><i>Skogsbruk</i>, <i>Skogliga biotopskyddsområden</i> och <i>Naturvårdsavtal</i> från
- <a href="https://skogsstyrelsen.se" target="_blank">Skogsstyrelsen</a>,
- CC0 (<a href="https://www.skogsstyrelsen.se/sjalvservice/karttjanster/geodatatjanster/villkor-for-nyttjande-av-skogsstyrelsens-kartdatabaser/" target="_blank">öppna data</a>).
+ </ul>
+ <p class="small text-muted info-credits">Webbkartan är utvecklad av
+ <a data-mailto-b64="Z3VpbGhlbQ __AT__ ZnJpcG9zdA __DOT__ b3Jn" href="#" target="_blank" class="email-address-b64">Guilhem Moulin</a>
+ på uppdrag av
+ <a href="https://www.klimatanalysnorr.se" target="_blank">Klimatanalys Norr projektet</a>.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="modal" id="help-modal" tabindex="-1" aria-hidden="true">
+ <div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="btn-close" data-bs-dismiss="modal" title="Stäng" aria-label="Stäng"></button>
+ </div>
+ <div id="help-body" class="modal-body">
+ <h3>Navigering</h3>
+ <p>Kartan är interaktiv och kontrolleras med musen. Tryck ner på
+ musens första knapp och dra kartan för att byta koordinater.</p>
+ <p>Zoomnivån kan styras med musens rullhjul, eller alternativt med
+ knapparna och skjutreglaget längst upp till vänster. Den aktuella
+ skalan visas längst ner till vänster på kartan.</p>
+ <p>Både koordinater och zoomnivå sparas i URL:en, så om du delar
+ URL:en med någon annan eller sparar den i din webbläsares bokmärken
+ kommer senare besök att landa på samma plats samt zoomnivå på
+ kartan.</p>
+ <p>Klickar du på ett objekt på kartan – exempelvis ett
+ undersökningstillstånd eller ett naturreservat (läs mer om lagerval
+ nedan) – så visar det valda objektet med en cyanfärgad kantlinje
+ samt dyker ett fönster upp med information om just det objektet.
+ Både informationen och geometrier kommer från ansvarig myndighet.
+ I fall det finns flera objekt i närheten av just platsen du klickade
+ på, så antalet träffar visas högst upp på informationsfönstret
+ (exempelvis ”<span class="fst-italic text-muted">Träff 1 av 3</span>”) och du
+ kan välja mellan dem genom att klicka på pilarna
+ <span class="popover-header">
+ <button class="popover-button popover-button-prev text-muted help-button"></button>/<button class="popover-button popover-button-next text-muted help-button"></button>
+ </span> bredvid.
+ Informationsfönstret kan flyttas runt om det står i vägen, samt att
+ det förstoras om rutan är för liten.</p>
+
+ <h3>Funktionsknappar</h3>
+ <p>Det finns ett antal funktionsknappar längst upp till
+ höger på kartan. Om du låter muspekaren vila på varje
+ knapp, dyker en hjälptext upp.</p>
+
+ <ol id='help-describe-functions'>
+ <li data-for-button='layer-selection-button'>
+ <p>Här kan du välja mellan olika lager att ha på kartan.
+ Det finns massor med lager, och de grupperas ihop så att
+ det är enkelt att välja hela gruppen på en gång. Klickar
+ du på pilen
+ <span class="accordion"><span class="accordion-button collapsed help-button text-muted"></span></span>
+ kan du istället välja ett specifikt lager.</p>
+ <p>Längst ned finns knappar där du kan välja att se
+ administrativa gränser, samt välja mellan nedtonad eller färgad
+ bakgrundskarta.</p>
+ <p>Lager, precis som koordinater och zoomnivå, sparas i URL:en.
+ Så om du delar URL:en med någon annan eller sparar den i din
+ webbläsares bokmärken kommer senare besök att landa på samma vy
+ (eventuellt med uppdaterat underlag).</p>
</li>
- <li><i>Naturvårdsregistret</i> och <i>Naturvårdsavtal</i> från
- <a href="https://www.naturvardsverket.se/" target="_blank">Naturvårdsverket</a>,
- CC0 (<a href="https://geodata.naturvardsverket.se/nedladdning/naturvardsregistret/Naturvardsregistret_beskrivning_av_oppna_data.pdf" target="_blank">öppna data</a>).
+
+ <li data-for-button='map-legend-button'>
+ <p>Här ser du symboler för alla lager som valts ut.</p>
</li>
- <li><i>Riksintresse naturvård</i> och <i>frilufsliv</i> från
- <a href="https://www.naturvardsverket.se/" target="_blank">Naturvårdsverket</a>
- och
- <a href="https://www.lansstyrelsen.se/" target="_blank">Länsstyrelsen</a>,
- CC-BY-4.0 (<a href="https://ext-geodatakatalog-forv.lansstyrelsen.se/GeodataKatalogen/codelist/metadata/anvandningsrestriktioner.xml#CCby4.0" target="_blank">öppna data</a>).
+
+ <li data-for-button='measure-button'>
+ <p>Med det här verktyget kan du rita direkt på kartan och mäta
+ distanser eller ytor. Till exempel kan du lätt mäta avståndet
+ mellan en viss exploatering och ett skyddat område eller
+ kommungräns.</p>
</li>
- <li><i>Riksintresse Rennäringen</i> skikt från
- <a href="https://sametinget.se" target="_blank">Sametinget</a>,
- CC-BY-4.0 (<a href="https://ext-geodatakatalog-forv.lansstyrelsen.se/GeodataKatalogen/codelist/metadata/anvandningsrestriktioner.xml#CCby4.0" target="_blank">öppna data</a>).
- <i>Samebyarnas betesområden</i> och <i>flyttled</i> från
- &copy; <a href="https://sametinget.se" target="_blank">Sametingets</a>
- Rennäringens markanvändningsdatabas (IRENMARK).
+
+ <li data-for-button='age-filter-button'>
+ <p>Med det här verktyget kan du filtrera bort gamla
+ exploateringar, vilket underlättar övervakning. Det är bra att
+ kunna fokusera på nya exploateringen, då det annars snabbt kan
+ kännas överväldigande.</p>
+
+ <p>Om filtret är aktivt så blir knappen svart. Filterparametrar
+ sparas i URL:en, så en övervaknings-URL till ett specifik område
+ kan enkelt delas med andra eller bokmärkas.</p>
</li>
- <li>Bakgrund kartor från
- &copy; <a href="https://lantmateriet.se" target="_blank">Lantmäteriet</a>, CC0
- (<a href="https://www.lantmateriet.se/sv/geodata/vara-produkter/oppna-data/#anchor-1" target="_blank">öppna data</a>).
+
+ <li data-for-button='fullscreen-toggle'>
+ <p>Genom att klicka på knappen kan du aktivera eller inaktivera
+ helskärmsläget.</p>
+ <li>
+
+ <li data-for-button='export-to-image'>
+ <p>Genom att klicka på knappen kan du ladda ner den aktuella
+ kartvyn som en bild. Bilden kan då användas i en rapport eller
+ ett yttrande.</p>
</li>
- <li>Webbkartan:
- &copy; <a href="https://guilhem.se" target="_blank">Guilhem Moulin</a>, AGPLv3+.
- <a href="https://git.guilhem.org/KlimatanalysNorr/webmap" target="_blank">Källkod</a>.
+
+ <li data-for-button='info-button'>
+ <p>Här kan du se källor och licensvillkor för varje lager samt
+ själva kartverktyget.
+ För de flesta lager finns det också en produktlänk till den
+ ansvariga myndighetens webbplats.</p>
+ <p>Bredvid varje källa kan du se när den senast hämtades av
+ kartverktyget, samt när skikten generades på kartan (det vill
+ säga hur gammalt underlaget är på kartan).
+ Nedladdning samt uppdatering av underlag för kartan sker
+ automatiskt varje dag.</p>
</li>
- <li>Backend-verktyg:
- &copy; <a href="https://guilhem.se" target="_blank">Guilhem Moulin</a>, GPLv3+.
- <a href="https://git.guilhem.org/KlimatanalysNorr/tools" target="_blank">Källkod</a>.
+
+ <li data-for-button='help-button'>
+ <p>Om du klickar på den här knappen ser du det här
+ hjälpfönstret.</p>
</li>
- </ul>
- <p class="small mb-0">Webbkartan är utvecklad av
- <a href="https://guilhem.se" target="_blank">Guilhem Datakonsult</a> på uppdrag av
- <a href="https://www.klimatanalysnorr.se" target="_blank">Klimatanalys Norr projektet</a>.</p>
+ </ol>
+
+ <h3>Målgrupp och syftet</h3>
+ <p class="mb-2">Kartan kan användas som en hjälp i att välja vilka
+ exploateringar som är viktigast att bekämpa. Målgruppen för kartan
+ är individer och organisationer som vill:</p>
+ <ol>
+ <li>se kumulativa effekter av olika exploateringar i
+ norr</li>
+ <li>se var en viss exploatering ligger i förhållande
+ till ett område av intresse (ex formellt skydd, höga
+ naturvärden, riksintresse med flera)</li>
+ <li>övervaka de senaste exploateringarna i ett visst
+ område.</li>
+ </ol>
+
+ <p>Viktigt att komma ihåg är att allt underlag i kartan
+ kommer från olika myndigheter (Bergsstaten, Skogsstyrelsen,
+ Länsstyrelsen, Naturvårdsverket med flera) och ingen modell
+ finns för att assistera i själva avvägningen.
+ Med andra ord så samlar kartan in befintliga underlag från
+ olika myndigheter istället för att själv presentera något
+ nytt, och den data kartan presenterar är därmed information
+ som kan ingå i rapporter och yttranden (och ingen kan hävda
+ att underlaget är påhittat).</p>
+
+ <p>Underlagen för kartan uppdateras automatiskt varje dag.
+ Se ovan för detaljer om hur gammalt varje underlag är.</p>
+
+ <h3>Buggrapporter och feedback</h3>
+ <p>Tveka inte att skicka ett
+ <a data-mailto-b64="Z3VpbGhlbQ __AT__ ZnJpcG9zdA __DOT__ b3Jn" href="#" target="_blank" class="link-secondary email-address-b64">mejl
+ <i class="bi bi-envelope-at"></i></a>
+ med önskemål, buggrapporter, förslag till
+ förbättring med flera.
+ Kartverktyget samt tillhörande verktyg är alla fri programvara.</p>
</div>
</div>
</div>
diff --git a/main.js b/main.js
index 88ac044..9840746 100644
--- a/main.js
+++ b/main.js
@@ -18,8 +18,10 @@
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
+import TileLayerGL from 'ol/layer/WebGLTile.js';
import WMTS from 'ol/source/WMTS.js';
+import GeoTIFF from 'ol/source/GeoTIFF.js';
import WMTSTileGrid from 'ol/tilegrid/WMTS.js';
import FullScreen from 'ol/control/FullScreen.js';
@@ -34,8 +36,15 @@ import VectorTileLayer from 'ol/layer/VectorTile.js';
import VectorTile from 'ol/source/VectorTile.js';
import { createXYZ } from 'ol/tilegrid.js';
+import { toContext } from 'ol/render.js';
+import Polygon from 'ol/geom/Polygon.js';
+import LineString from 'ol/geom/LineString.js';
+import Point from 'ol/geom/Point.js';
+
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
+import Draw from 'ol/interaction/Draw.js';
+import { unByKey } from 'ol/Observable.js';
import CircleStyle from 'ol/style/Circle.js';
import Fill from 'ol/style/Fill.js';
@@ -44,6 +53,8 @@ import RegularShape from 'ol/style/RegularShape.js';
import Stroke from 'ol/style/Stroke.js';
import Style from 'ol/style/Style.js';
+import Geolocation from 'ol/Geolocation.js';
+
import proj4 from 'proj4';
import { get as getProjection } from 'ol/proj.js';
import { register as registerProjection } from 'ol/proj/proj4.js';
@@ -51,12 +62,14 @@ import { register as registerProjection } from 'ol/proj/proj4.js';
import { Modal, Popover } from 'bootstrap';
import './style.css';
+"use strict";
proj4.defs('EPSG:3006', '+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs');
registerProjection(proj4);
-const projection = getProjection('EPSG:3006');
+const PROJECTION = getProjection('EPSG:3006');
+const LOCALE = 'sv-SE';
/* Lantmäteriet uses a tile-scheme where the origin (upper-left corner) is at
* N8500000 E-1200000 (SWEREF99 TM), where each tile is 256×256 pixels, and where
@@ -69,7 +82,7 @@ const projection = getProjection('EPSG:3006');
* side) somehow centered on Norrbotten and Västerbotten, and zoom in from there.
* This represent a TILEROW (x) offset of 5, and a TILECOL (y) offset of 2.
*/
-const extent = [110720, 6927136, 1159296, 7975712];
+const EXTENT = [110720, 6927136, 1159296, 7975712];
/* XXX using the topowebbcache WMTS is fine for testing (as it doesn't require
* authentication) but not in production in a public instance as doing so would
@@ -87,82 +100,93 @@ const extent = [110720, 6927136, 1159296, 7975712];
* https://ext-geodatakatalog.lansstyrelsen.se/GeodataKatalogen/srv/swe/catalog.search#/map uses
* https://api.lantmateriet.se/open/topowebb-ccby/v1/wmts/token/3c3a9cf47e7cb5ea24542d40d19698/?layer=topowebb&style=default&tilematrixset=3006&Service=WMTS&Request=GetTile&Version=1.0.0&Format=image/png&TileMatrix=7&TileCol=237&TileRow=155
*/
-const baseMapSource = new WMTS({
- url: undefined,
- version: '1.0.0',
- style: 'default',
- matrixSet: '3006',
- format: 'image/png',
- tileGrid: new WMTSTileGrid({
- extent: extent,
- // https://www.lantmateriet.se/globalassets/geodata/geodatatjanster/tb_twk_visning-oversiktlig_v1.0.3.pdf
- tileSize: 256,
- origin: [-1200000, 8500000],
- resolutions: [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8],
- matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
- }),
- projection: projection,
- wrapX: false,
- crossOrigin: 'anonymous',
-});
-
-
-const view = new View({
- projection: projection,
- extent: extent,
- showFullExtent: true,
- /* center of the bbox of the Norrbotten and Västerbotten geometries */
- center: [694767.48, 7338176.57],
- zoom: 1,
- enableRotation: false,
- resolutions: [1024, 512, 256, 128, 64, 32, 16, 8],
- constrainResolution: false,
-});
-
-let baseMapLayer = 'topowebb';
-(function() {
- const params = new URLSearchParams(window.location.hash.substring(1));
- const x = parseFloat(params.get('x'));
- const y = parseFloat(params.get('y'));
- if (!isNaN(x) && !isNaN(y)) {
- view.setCenter([x, y]);
- }
- const z = parseFloat(params.get('z'));
- if (!isNaN(z)) {
- view.setZoom(z);
- }
-
- if (params.has('basemap')) {
- baseMapLayer = params.get('basemap');
- }
- baseMapSource.setUrl(`https://minkarta.lantmateriet.se/map/topowebbcache?LAYER=${encodeURIComponent(baseMapLayer)}`);
-})();
-
-
-const map = new Map({
- controls: [],
- view: view,
- layers: [
- new TileLayer({
- source: baseMapSource
+const [BASEMAP, MAP] = (function() {
+ const param = 'basemap';
+ const baseMap = Object.seal({
+ _layer: new URLSearchParams(location.hash.substring(1))?.get?.(param) ?? 'topowebb_nedtonad',
+ get layer() {
+ return this._layer;
+ },
+ get url() {
+ return 'https://minkarta.lantmateriet.se/map/topowebbcache?' +
+ 'LAYER=' + encodeURIComponent(this.layer);
+ },
+ set layer(layername) {
+ this._layer = layername;
+ baseMapSource.setUrl(this.url);
+ const searchParams = new URLSearchParams(location.hash.substring(1));
+ searchParams.set(param, layername);
+ location.hash = '#' + searchParams.toString();
+ },
+ });
+ const baseMapSource = new WMTS({
+ url: baseMap.url,
+ version: '1.0.0',
+ style: 'default',
+ matrixSet: '3006',
+ format: 'image/png',
+ tileGrid: new WMTSTileGrid({
+ extent: EXTENT,
+ // https://www.lantmateriet.se/globalassets/geodata/geodatatjanster/tb_twk_visning-oversiktlig_v1.0.3.pdf
+ tileSize: 256,
+ origin: [-1200000, 8500000],
+ resolutions: [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8],
+ matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
}),
- ],
- target: document.getElementById('map'),
-});
+ projection: PROJECTION,
+ wrapX: false,
+ crossOrigin: 'anonymous',
+ });
-const popup = document.getElementById('popup');
+ const view = new View({
+ projection: PROJECTION,
+ extent: EXTENT,
+ showFullExtent: true,
+ enableRotation: false,
+ resolutions: [1024, 512, 256, 128, 64, 32, 16, 8],
+ constrainResolution: false,
+ });
+ return [
+ baseMap,
+ new Map({
+ controls: [],
+ view: view,
+ layers: [
+ new TileLayer({
+ source: baseMapSource
+ }),
+ ],
+ target: document.getElementById('map'),
+ }),
+ ];
+})();
/* move the control container to the viewport */
-const container = document.getElementById('map-control-container');
+const CONTAINER_MAP = document.getElementById('map-control-container');
+const CONTAINER_STOPEVENT = MAP.getViewport().getElementsByClassName('ol-overlaycontainer-stopevent')[0];
(function() {
- const container0 = map.getViewport().getElementsByClassName('ol-overlaycontainer-stopevent')[0];
- container0.appendChild(document.getElementById('zoom-control'));
- container0.appendChild(container);
- container0.appendChild(document.getElementById('modal-info'));
-
- const backdrop = document.createElement('div');
- container0.appendChild(backdrop);
- backdrop.id = 'modal-info-backdrop';
+ CONTAINER_STOPEVENT.appendChild(document.getElementById('zoom-control'));
+ CONTAINER_STOPEVENT.appendChild(CONTAINER_MAP);
+ CONTAINER_STOPEVENT.appendChild(document.getElementById('info-modal'));
+ CONTAINER_STOPEVENT.appendChild(document.getElementById('help-modal'));
+
+ const info_backdrop = document.createElement('div');
+ CONTAINER_STOPEVENT.appendChild(info_backdrop);
+ info_backdrop.id = 'info-modal-backdrop';
+
+ const help_backdrop = document.createElement('div');
+ CONTAINER_STOPEVENT.appendChild(help_backdrop);
+ help_backdrop.id = 'help-modal-backdrop';
+
+ const age_filter = document.createElement('div');
+ age_filter.id = 'age-filter-modal';
+ age_filter.classList.add('modal');
+ age_filter.setAttribute('tabindex', '-1');
+ age_filter.setAttribute('aria-hidden', 'true');
+ CONTAINER_STOPEVENT.appendChild(age_filter);
+ const age_filter_backdrop = document.createElement('div');
+ age_filter_backdrop.id = 'age-filter-modal-backdrop';
+ CONTAINER_STOPEVENT.appendChild(age_filter_backdrop);
})();
/* zoom in/out */
@@ -185,7 +209,7 @@ const container = document.getElementById('map-control-container');
for (const btn of control.element.getElementsByTagName('button')) {
btn.classList.add('btn', 'btn-light');
}
- map.addControl(control);
+ MAP.addControl(control);
})();
/* zoom slider */
@@ -197,29 +221,29 @@ const container = document.getElementById('map-control-container');
for (const btn of control.element.getElementsByTagName('button')) {
btn.classList.add('btn', 'btn-light');
}
- map.addControl(control);
+ MAP.addControl(control);
})();
/* scale line */
(function() {
- const size = map.getSize();
+ const size = MAP.getSize();
const control = new ScaleLine({
units: 'metric',
minWidth: 150,
maxWidth: size[1] < 350 ? size[1] - 50 : 350,
- target: container,
+ target: CONTAINER_MAP,
});
control.element.classList.add('modal', 'modal-content');
- map.addControl(control);
+ MAP.addControl(control);
})();
-const menu = document.getElementById('map-menu');
+const MENU = document.getElementById('map-menu');
const TRAILING_ZEROES = /\.?0*$/;
/* "open in new tab" button */
if (window.location !== window.parent.location) {
const div = document.createElement('div');
- menu.appendChild(div);
+ MENU.appendChild(div);
div.classList.add('ol-unselectable', 'ol-control');
const btn = document.createElement('button');
@@ -234,13 +258,13 @@ if (window.location !== window.parent.location) {
btn.appendChild(i);
i.classList.add('bi', 'bi-box-arrow-up-right');
- btn.onclick = function(event) {
- const coordinates = view.getCenter();
+ btn.onclick = function() {
+ const coordinates = MAP.getView().getCenter();
const url = new URL(window.location.href);
const searchParams = new URLSearchParams(url.hash.substring(1));
searchParams.set('x', coordinates[0].toFixed(2).replace(TRAILING_ZEROES, ''));
searchParams.set('y', coordinates[1].toFixed(2).replace(TRAILING_ZEROES, ''));
- searchParams.set('z', view.getZoom().toFixed(3).replace(TRAILING_ZEROES, ''));
+ searchParams.set('z', MAP.getView().getZoom().toFixed(3).replace(TRAILING_ZEROES, ''));
url.hash = '#' + searchParams.toString();
return window.open(url.href, '_blank');
};
@@ -248,79 +272,53 @@ if (window.location !== window.parent.location) {
/* layer selection button and legend */
if (window.location === window.parent.location) {
- const btn = (function() {
- const div = document.createElement('div');
- menu.appendChild(div);
- div.id = 'layer-selection-button';
- div.classList.add('ol-unselectable', 'ol-control');
-
- const btn = document.createElement('button');
- div.appendChild(btn);
- btn.type = 'button';
- btn.title = 'Lagerval';
- btn.setAttribute('aria-label', btn.title);
- btn.setAttribute('aria-expanded', 'false');
- btn.classList.add('btn', 'btn-light');
-
- const i = document.createElement('i');
- btn.appendChild(i);
- i.classList.add('bi', 'bi-stack');
-
- return btn;
- })();
-
- const btn2 = (function() {
- const div = document.createElement('div');
- menu.appendChild(div);
- div.id = 'map-legend-button';
- div.classList.add('ol-unselectable', 'ol-control');
-
- const btn = document.createElement('button');
- div.appendChild(btn);
- btn.type = 'button';
- btn.title = 'Teckenförklaring';
- btn.setAttribute('aria-label', btn.title);
- btn.setAttribute('aria-expanded', 'false');
- btn.classList.add('btn', 'btn-light');
-
- const i = document.createElement('i');
- btn.appendChild(i);
- i.classList.add('bi', 'bi-list-task');
-
- return btn;
- })();
-
- const panel = document.getElementById('layer-selection-panel');
- btn.onclick = function(event) {
- if (btn.getAttribute('aria-expanded') === 'true') {
- panel.setAttribute('aria-hidden', 'true');
+ const buttons = Object.fromEntries([
+ {id: 'layer-selection', title: 'Lagerval', bi: 'stack'},
+ {id: 'map-legend', title: 'Teckenförklaring', bi: 'list-task'},
+ {id: 'measure', title: 'Mät i kartan', bi: 'rulers'},
+ {id: 'age-filter', title: 'Filtrera objekt efter ålder', bi: 'clock-history'},
+ ].map(function(x) {
+ const div = document.createElement('div');
+ MENU.appendChild(div);
+ div.id = x.id + '-button';
+ div.classList.add('ol-unselectable', 'ol-control');
+
+ const btn = document.createElement('button');
+ div.appendChild(btn);
+ btn.type = 'button';
+ btn.title = x.title;
+ btn.setAttribute('aria-label', btn.title);
btn.setAttribute('aria-expanded', 'false');
- btn.classList.replace('btn-dark', 'btn-light');
- } else {
- if (btn2.getAttribute('aria-expanded') === 'true') {
- btn2.click();
- }
- panel.setAttribute('aria-hidden', 'false');
- btn.setAttribute('aria-expanded', 'true');
- btn.classList.replace('btn-light', 'btn-dark');
- }
- };
-
- const panel2 = document.getElementById('map-legend-panel');
- btn2.onclick = function(event) {
- if (btn2.getAttribute('aria-expanded') === 'true') {
- panel2.setAttribute('aria-hidden', 'true');
- btn2.setAttribute('aria-expanded', 'false');
- btn2.classList.replace('btn-dark', 'btn-light');
- } else {
- if (btn.getAttribute('aria-expanded') === 'true') {
- btn.click();
- }
- panel2.setAttribute('aria-hidden', 'false');
- btn2.setAttribute('aria-expanded', 'true');
- btn2.classList.replace('btn-light', 'btn-dark');
+ btn.classList.add('btn', 'btn-light');
+
+ const i = document.createElement('i');
+ btn.appendChild(i);
+ i.classList.add('bi', 'bi-' + x.bi);
+ return [x.id, btn]
+ }));
+
+ Object.entries(buttons).forEach(function([id, btn]) {
+ const panel = document.getElementById(id + '-panel');
+ if (panel != null) {
+ btn.onclick = function() {
+ if (btn.getAttribute('aria-expanded') === 'true') {
+ panel.setAttribute('aria-hidden', 'true');
+ btn.setAttribute('aria-expanded', 'false');
+ btn.classList.replace('btn-dark', 'btn-light');
+ } else {
+ Object.values(buttons).forEach(function(btn2) {
+ /* close all other panels */
+ if (!btn.isEqualNode(btn2) && btn2.getAttribute('aria-expanded') === 'true') {
+ btn2.click();
+ }
+ });
+ panel.setAttribute('aria-hidden', 'false');
+ btn.setAttribute('aria-expanded', 'true');
+ btn.classList.replace('btn-light', 'btn-dark');
+ }
+ };
}
- };
+ });
}
/* fullscreen control */
@@ -341,20 +339,17 @@ if (window.location === window.parent.location) {
labelActive: labelActive,
tipLabel: titleInactive,
keys: true,
- target: menu,
- })
+ target: MENU,
+ });
const btn = control.element.getElementsByTagName('button')[0];
btn.classList.add('btn', classInactive);
btn.setAttribute('aria-label', btn.title);
- map.addControl(control);
+ MAP.addControl(control);
+ control.element.id = 'fullscreen-toggle'; /* for the help dialog */
control.addEventListener('enterfullscreen', function() {
- featureOverlayLayer.setVisible(false);
- const popover = Popover.getInstance(popup);
- if (popover !== null) {
- /* dispose popover as entering fullscreen messes up its position */
- popover.dispose();
- }
+ /* dispose popover as entering fullscreen messes up its position */
+ disposePopover();
const btn = control.element.getElementsByTagName('button')[0];
btn.classList.replace(classInactive, classActive);
@@ -366,14 +361,10 @@ if (window.location === window.parent.location) {
/* hide export button in fullscreen mode as it exits it */
exp.classList.add('d-none');
}
- })
+ });
control.addEventListener('leavefullscreen', function() {
- featureOverlayLayer.setVisible(false);
- const popover = Popover.getInstance(popup);
- if (popover !== null) {
- /* dispose popover as is might overflow the viewport */
- popover.dispose();
- }
+ /* dispose popover as is might overflow the viewport */
+ disposePopover();
const btn = control.element.getElementsByTagName('button')[0];
btn.classList.replace(classActive, classInactive);
@@ -384,7 +375,7 @@ if (window.location === window.parent.location) {
if (exp !== undefined) {
exp.classList.remove('d-none');
}
- })
+ });
}
/* export/download button */
@@ -403,17 +394,17 @@ if (window.location === window.parent.location) {
const i = document.createElement('i');
btn.appendChild(i);
i.classList.add('bi', 'bi-download');
- menu.appendChild(div);
+ MENU.appendChild(div);
- btn.onclick = function(event) {
- map.once('rendercomplete', function() {
+ btn.onclick = function() {
+ MAP.once('rendercomplete', function() {
const canvas0 = document.createElement('canvas');
- const size = map.getSize();
+ const size = MAP.getSize();
canvas0.width = size[0];
canvas0.height = size[1];
const context = canvas0.getContext('2d');
- map.getViewport().querySelectorAll('.ol-layer canvas, canvas.ol-layer').forEach(function(canvas) {
+ MAP.getViewport().querySelectorAll('.ol-layer canvas, canvas.ol-layer').forEach(function(canvas) {
if (canvas.width > 0) {
const opacity = canvas.parentNode.style.opacity || canvas.style.opacity;
context.globalAlpha = opacity === '' ? 1 : Number(opacity);
@@ -434,2474 +425,2637 @@ if (window.location === window.parent.location) {
});
});
- map.renderSync();
+ MAP.renderSync();
};
}
-/* info button */
+/* info and help buttons */
(function() {
- const div = document.createElement('div');
- menu.appendChild(div);
- div.id = 'info-button';
- div.classList.add('ol-unselectable', 'ol-control');
+ const add_button = function(x) {
+ const div = document.createElement('div');
+ MENU.appendChild(div);
+ div.id = x.id + '-button';
+ div.classList.add('ol-unselectable', 'ol-control');
- const btn = document.createElement('button');
- div.appendChild(btn);
- btn.type = 'button';
- btn.setAttribute('aria-expanded', 'false');
- btn.title = 'Visa information';
- btn.setAttribute('aria-label', btn.title);
- btn.classList.add('btn', 'btn-light');
+ const btn = document.createElement('button');
+ div.appendChild(btn);
+ btn.type = 'button';
+ btn.setAttribute('aria-expanded', 'false');
+ btn.title = x.title;
+ btn.setAttribute('aria-label', btn.title);
+ btn.classList.add('btn', 'btn-light');
- const i = document.createElement('i');
- btn.appendChild(i);
- i.classList.add('bi', 'bi-info-lg');
+ const i = document.createElement('i');
+ btn.appendChild(i);
+ i.classList.add('bi', 'bi-' + x.bi);
- const panel = document.getElementById('modal-info');
- const modal = new Modal(panel, {
- backdrop: false,
- });
+ const panel = document.getElementById(x.id + '-modal');
+ const modal = new Modal(panel, {
+ backdrop: false,
+ });
- const backdrop = document.getElementById('modal-info-backdrop');
- backdrop.onclick = function(event) {
- modal.hide();
- };
+ const backdrop = document.getElementById(x.id + '-modal-backdrop');
+ backdrop.onclick = function() {
+ modal.hide();
+ };
- panel.addEventListener('show.bs.modal', function() {
- backdrop.classList.add('modal-backdrop', 'show');
- btn.setAttribute('aria-expanded', 'true');
- btn.classList.replace('btn-light', 'btn-dark');
- });
- panel.addEventListener('hidden.bs.modal', function() {
- btn.classList.replace('btn-dark', 'btn-light');
- btn.setAttribute('aria-expanded', 'false');
- backdrop.classList.remove('modal-backdrop', 'show');
- });
+ panel.addEventListener('show.bs.modal', function() {
+ backdrop.classList.add('modal-backdrop', 'show');
+ btn.setAttribute('aria-expanded', 'true');
+ btn.classList.replace('btn-light', 'btn-dark');
+ });
+ panel.addEventListener('hide.bs.modal', function() {
+ /* XXX workaround for https://github.com/twbs/bootstrap/issues/41005#issuecomment-2585390544 */
+ const activeElement = document.activeElement;
+ if (activeElement instanceof HTMLElement) {
+ activeElement.blur();
+ }
+ });
- btn.onclick = function(event) {
- modal.toggle();
- };
-})();
+ panel.addEventListener('hidden.bs.modal', function() {
+ btn.classList.replace('btn-dark', 'btn-light');
+ btn.setAttribute('aria-expanded', 'false');
+ backdrop.classList.remove('modal-backdrop', 'show');
+ });
-/* we're all set, show the control container now */
-container.setAttribute('aria-hidden', 'false');
+ btn.onclick = function() {
+ modal.show();
+ };
-view.on('change', function(event) {
- featureOverlayLayer.setVisible(false);
- const popover = Popover.getInstance(popup);
- if (popover !== null) {
- popover.dispose();
- }
+ /* de-obfuscate email address */
+ const CLASSNAME = 'email-address-b64';
+ const ATTRNAME = 'data-mailto-b64';
+ for (const a of panel.getElementsByClassName(CLASSNAME)) {
+ if (a.tagName.toLowerCase() === 'a' && a.hasAttribute(ATTRNAME)) {
+ let href = 'mailto:';
+ for (const part of a.getAttribute(ATTRNAME).split(/\s+/)) {
+ switch (part) {
+ case '__AT__':
+ href += '@';
+ break;
+ case '__DOT__':
+ href += '.';
+ break;
+ default:
+ href += atob(part);
+ }
+ }
+ a.classList.remove(CLASSNAME);
+ a.removeAttribute(ATTRNAME);
+ a.href = href;
+ }
+ }
- const coordinates = view.getCenter();
- const searchParams = new URLSearchParams(location.hash.substring(1));
- searchParams.set('x', coordinates[0].toFixed(2).replace(TRAILING_ZEROES, ''));
- searchParams.set('y', coordinates[1].toFixed(2).replace(TRAILING_ZEROES, ''));
- searchParams.set('z', view.getZoom().toFixed(3).replace(TRAILING_ZEROES, ''));
- location.hash = '#' + searchParams.toString();
-});
+ return [panel, btn, modal];
+ };
+ /* info button */
+ (function() {
+ const [panel, btn, modal] = add_button({
+ id: 'info',
+ title: 'Källor och licensinformation',
+ bi: 'info-lg',
+ });
-/* TODO: this should really be refactored… */
-const layers = {
- mrr_appr_ec: {
- popoverTitle: 'Bearbetningskoncession \u2013 beviljad',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Ägare', 'owners'],
- ['Tillståndsid', 'licenceid', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Giltig från', 'validfrom'],
- ['Giltig till', 'validto'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- ['Ansökningsdatum', 'appl_date'],
- ['Beslutsdatum', 'dec_date'],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 22,
- fill: new Fill({
- color: [247, 170, 67, Math.max((.2-1)/8 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [151, 173, 23, 1],
- }),
- });
- }),
- },
- mrr_appl_ec: {
- popoverTitle: 'Bearbetningskoncession \u2013 ansökt',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Sökande', 'owners'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Ansökningsdatum', 'appl_date'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 25,
- fill: new Fill({
- color: [247, 170, 67, Math.max((.2-1)/8 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [197, 14, 31, 1],
- lineDash: width >= 1.5 ? [2 * width] : undefined,
- }),
- });
- }),
- },
- mrr_appr_met: {
- popoverTitle: 'Undersökningstillstånd, metaller och industrimineral \u2013 beviljad',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Ägare', 'owners'],
- ['Tillståndsid', 'licenceid', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Giltig från', 'validfrom'],
- ['Giltig till', 'validfrom'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- ['Ansökningsdatum', 'appl_date'],
- ['Beslutsdatum', 'dec_date'],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 24,
- fill: new Fill({
- color: [0, 0, 0, Math.max((.2-.4)/4 * z + .4, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [151, 173, 23, 1],
- }),
- });
- }),
- },
- mrr_appl_met: {
- popoverTitle: 'Undersökningstillstånd, metaller och industrimineral \u2013 ansökt',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Sökande', 'owners'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Ansökningsdatum', 'appl_date'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 26,
- fill: new Fill({
- color: [0, 0, 0, Math.max((.2-.4)/4 * z + .4, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [197, 14, 31, 1],
- lineDash: width >= 1.5 ? [2 * width] : undefined,
- }),
- });
- }),
- },
- mrr_appr_ogd: {
- popoverTitle: 'Undersökningstillstånd, olja, gas och diamant \u2013 beviljad',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Ägare', 'owners'],
- ['Tillståndsid', 'licenceid', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Giltig från', 'validfrom'],
- ['Giltig till', 'validto'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- ['Ansökningsdatum', 'appl_date'],
- ['Beslutsdatum', 'dec_date'],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 24,
- fill: new Fill({
- color: [30, 55, 87, Math.max((.2-.4)/4 * z + .4, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [151, 173, 23, 1],
- }),
- });
- }),
- },
- mrr_appl_ogd: {
- popoverTitle: 'Undersökningstillstånd, olja, gas och diamant \u2013 ansökt',
- popover: [
- ['Namn', 'name'],
- ['Koncessionsmineral', 'mineral'],
- ['Sökande', 'owners'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Ansökningsdatum', 'appl_date'],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 26,
- fill: new Fill({
- color: [30, 55, 87, Math.max((.2-.4)/4 * z + .4, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [197, 14, 31, 1],
- lineDash: width >= 1.5 ? [2 * width] : undefined,
- }),
- });
- }),
- },
- mrr_appr_dl: {
- popoverTitle: 'Markanvisning till koncession',
- popover: [
- ['Namn', 'name'],
- ['Tillhörande bearbetnings\u00ADkoncession(er)', 'conc_name'],
- ['Tillståndsid', 'licenceid', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Diarienummer', 'diarynr', { classes: ['feature-attr-dnr'] }],
- ['Ansökningsdatum', 'appl_date'],
- ['Beslutsdatum', 'dec_date'],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'export_date'],
- ],
- style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 20,
- fill: new Fill({
- color: [228, 53, 45, Math.max((.2-1)/6 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [151, 173, 23, 1],
- }),
+ panel.addEventListener('hidden.bs.modal', function() {
+ infoMetadataAccordions.forEach(function(x, idx) {
+ /* collapse all accordions */
+ const body = x.element.parentNode.parentNode;
+ const name = 'info-accordion-collapse-' + idx;
+ if (body.id === name) {
+ body.classList.remove('show');
+ }
+ if (body.parentNode !== null) {
+ const headers = body.parentNode.getElementsByClassName('accordion-header');
+ for (let i = 0; i < headers.length; i++) {
+ const buttons = headers[i].getElementsByClassName('accordion-button');
+ for (let j = 0; j < buttons.length; j++) {
+ const btn = buttons[j];
+ if (btn.getAttribute('data-bs-target') === '#' + name) {
+ btn.setAttribute('aria-expanded', 'false');
+ btn.classList.add('collapsed');
+ }
+ }
+ }
+ }
+ });
+ });
+
+ const dateFormatter = new Intl.DateTimeFormat(LOCALE);
+ btn.onclick = function() {
+ infoMetadataAccordions.forEach((x) => x.element.replaceChildren());
+ modal.show();
+ Promise.allSettled(Object.entries(mapLayers).map(function([grp,lyr]) {
+ const baseurl = lyr?.getSource?.()?.get?.('baseurl');
+ if (baseurl == null) {
+ return new Promise(() => { throw new Error(`Unknown source for "${grp}"`); });
+ }
+ return fetch(new URL('metadata.json', baseurl))
+ .then(function(resp0) {
+ if (resp0.status === 200) {
+ return resp0.json().then((x) => [grp,x]);
+ } else {
+ throw new Error(`${resp0.url} [${resp0.status}]`);
+ }
+ });
+ }))
+ .then(function(rs) {
+ const metadata = Object.fromEntries(rs.filter(function(r) {
+ if (r.status === 'fulfilled') {
+ return true;
+ } else if (r.status === 'rejected') {
+ console.log(r.reason);
+ }
+ return false;
+ }).map((r) => r.value));
+
+ infoMetadataAccordions.forEach(function(x) {
+ const ul = x.element;
+ const groupnames = new Set();
+ const last_updated = [];
+ x.items.forEach(function([groupname]) {
+ const layer_group = metadata[groupname];
+ if (layer_group == null) {
+ return;
+ }
+ if (!groupnames.has(groupname)) {
+ groupnames.add(groupname);
+ if (layer_group.last_updated != null) {
+ last_updated.push(layer_group.last_updated);
+ }
+ }
+ });
+ if (last_updated.length > 0) {
+ /* show creation time of the MVT layers */
+ const li = document.createElement('li');
+ li.classList.add('list-group-item', 'text-muted');
+ ul.appendChild(li);
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-map');
+ li.appendChild(i);
+ const t = document.createTextNode(
+ ' Lokalt skikt (vectiler) genererades ' +
+ last_updated
+ .sort()
+ .map((ts) => dateFormatter.format(new Date(ts)))
+ .join('; ') + '.'
+ );
+ li.appendChild(t);
+ }
+
+ const source_files = new Set();
+ x.items.forEach(function([groupname, layername]) {
+ /* for each source file associated with the accordion header, show copyright, license and timing information */
+ const layer_group = metadata[groupname];
+ if (layer_group?.layers == null || layer_group?.source_files == null) {
+ return;
+ }
+ const def = layer_group.layers[layername];
+ if (def?.source_files == null) {
+ return;
+ }
+ def.source_files.forEach(function(source_file) {
+ if (source_files.has(source_file)) {
+ return;
+ }
+ const x = layer_group.source_files[source_file];
+ source_files.add(source_file);
+
+ const li = document.createElement('li');
+ li.classList.add('list-group-item');
+ ul.appendChild(li);
+ const h = document.createElement('h6');
+ li.appendChild(h);
+ if (x.description != null) {
+ const t = document.createTextNode(x.description);
+ h.appendChild(t);
+ }
+
+ if (x.copyright != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ const t = document.createTextNode(x.copyright);
+ p.appendChild(t);
+ }
+
+ if (x.license != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ p.appendChild(document.createTextNode('Licensvillkor: '));
+ const t = document.createTextNode(x.license.name);
+ if (x.license.url == null) {
+ p.appendChild(t);
+ } else {
+ const a = document.createElement('a');
+ a.href = x.license.url;
+ a.target = '_blank';
+ a.appendChild(t);
+ p.appendChild(a);
+ }
+ }
+
+ if (x.product_url != null) {
+ const p = document.createElement('p');
+ li.appendChild(p);
+ const t = document.createTextNode('Produktlänk ');
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-box-arrow-up-right');
+ const a = document.createElement('a');
+ a.href = x.product_url;
+ a.target = '_blank';
+ a.appendChild(t);
+ a.appendChild(i);
+ p.appendChild(a);
+ }
+
+ if (x.last_modified != null) {
+ const p = document.createElement('p');
+ p.classList.add('small', 'text-muted');
+ li.appendChild(p);
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-file-earmark-code');
+ p.appendChild(i);
+ p.appendChild(document.createTextNode(' '));
+ const t0 = document.createTextNode('Källfil');
+ if (x.url == null) {
+ p.appendChild(t0);
+ } else {
+ const a = document.createElement('a');
+ p.appendChild(a);
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-box-arrow-up-right');
+ a.appendChild(t0);
+ a.appendChild(document.createTextNode(' '));
+ a.appendChild(i);
+ a.href = x.url;
+ a.target = '_blank';
+ }
+ const t1 = document.createTextNode(' ändrades senast ');
+ p.appendChild(t1);
+ const td = document.createTextNode(dateFormatter.format(new Date(x.last_modified)));
+ p.appendChild(td);
+ const t2 = document.createTextNode('.');
+ p.appendChild(t2);
+ }
+ });
+ });
+ });
});
- }),
+ };
+ })();
+
+ /* help button */
+ (function() {
+ const [panel] = add_button({
+ id: 'help',
+ title: 'Hjälp med att använda kartan',
+ bi: 'question-circle',
+ });
+
+ /* Use the text from the .html file but ensure that buttons are
+ * listed in the same order as the menu, and spell titles out. This
+ * avoids duplication and avoids that things would get out of sync */
+ const button_map = {};
+ const ol = panel.querySelector('#help-describe-functions');
+ if (ol != null && ol.tagName.toLowerCase() === 'ol') {
+ for (const li of ol.children) {
+ const id = li.getAttribute('data-for-button');
+ if (id == null || id === '') {
+ continue;
+ }
+ button_map[id] = li;
+ }
+ }
+
+ for (const node of MENU.children) {
+ if (node.id == null || node.id === '') {
+ continue
+ }
+ const btn = node.getElementsByTagName('button')[0];
+ if (btn == null || btn.tagName.toLowerCase() !== 'button') {
+ continue;
+ }
+ const btn2 = btn.cloneNode(true);
+ const title = btn2.title;
+ btn2.id = btn2.title = '';
+ for (const attr of btn.attributes) {
+ if (attr.name.toLowerCase().startsWith('aria-') || attr.name === 'id' || attr.name === 'title') {
+ btn2.removeAttribute(attr.name);
+ }
+ }
+
+ const h = document.createElement('h6');
+ h.classList.add('help-button-description');
+ h.appendChild(btn2);
+ if (title != null && title != '') {
+ const t = document.createTextNode(title)
+ h.appendChild(t);
+ }
+ btn2.classList.add('help-button');
+
+ ol.insertAdjacentElement('beforebegin', h);
+
+ const li = button_map[node.id];
+ if (li != null) {
+ /* move <li>'s children (paragraphs) to the main text */
+ while (li.children.length > 0) {
+ ol.insertAdjacentElement('beforebegin', li.firstElementChild);
+ }
+ }
+ }
+ ol.remove();
+ })();
+})();
+
+/* we're all set, show the control container now */
+CONTAINER_MAP.setAttribute('aria-hidden', 'false');
+
+/* age filter settings state */
+const ageFilterSettings = (function() {
+ const dateToTS = function(d) {
+ if (d != null) {
+ /* number of days since 1970-01-01; take both dates at 00:00:00.0 UTC */
+ return Math.floor(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate())/86_400_000);
+ }
+ };
+ return Object.seal({
+ _active: false,
+ get active() {
+ return this._active;
+ },
+ set active(b) {
+ ageFilterSetActive(b);
+ },
+ type: 'relative',
+ operator: '<=',
+ quantity: 1,
+ unit: 'y',
+ from: null,
+ to: null,
+ show_unknown: false,
+ _min_ts: null,
+ _max_ts: null,
+ getRelativeDate: function(quantity, unit) {
+ if (quantity == null || isNaN(quantity) || unit == null) {
+ return null;
+ }
+ /* use today noon localtime to avoid issues due to DST when substracting dates */
+ const d = new Date();
+ d.setHours(12, 0, 0, 0);
+ switch (unit) {
+ case 'd':
+ d.setDate(d.getDate() - quantity);
+ break;
+ case 'w':
+ d.setDate(d.getDate() - 7 * quantity);
+ break;
+ case 'm':
+ d.setMonth(d.getMonth() - quantity);
+ break;
+ case 'y':
+ d.setFullYear(d.getFullYear() - quantity);
+ break;
+ default:
+ return null;
+ }
+ return d;
+ },
+ setupMinMax: function() {
+ this._min_ts = this._max_ts = null;
+ switch (this.type) {
+ case 'relative': {
+ const date = this.getRelativeDate(this.quantity, this.unit);
+ const prop = {'<=':'_min_ts', '>=':'_max_ts'}[this.operator];
+ this[prop] = dateToTS(date);
+ break;
+ }
+ case 'interval': {
+ this._min_ts = dateToTS(this.from);
+ this._max_ts = dateToTS(this.to);
+ break;
+ }
+ }
+ },
+ });
+})();
+
+/* Layer style definitions */
+/* TODO: this should really be refactored, but probably not worth doing before the switch to
+ * WebGL, which is currently blocking on https://github.com/openlayers/openlayers/issues/15807
+ * and https://github.com/openlayers/openlayers/issues/16246 */
+const LAYERS = Object.seal({
+ adm: {
+ lansyta: {
+ legend: { zoomLevel: 3, type: 'linestring' },
+ style: [1.5, 2, 3, 3, 4, 4, 6, 6, 8, 8, 10, 10].map(function(width) {
+ return new Style({
+ zIndex: 0,
+ fill: null,
+ stroke: new Stroke({
+ width: width,
+ color: [212, 147, 208, 1],
+ }),
+ });
+ }),
+ },
+ kommunyta: {
+ legend: { zoomLevel: 3, type: 'linestring' },
+ style: [2, 2, 3, 3, 4, 4, 6, 6, 8, 8, 10, 10].map(function(width) {
+ return new Style({
+ zIndex: 0,
+ fill: null,
+ stroke: new Stroke({
+ width: width/2,
+ color: [212, 147, 208, 1],
+ }),
+ });
+ }),
+ },
},
- svk_ledningar: {
- popoverTitle: 'Kraftledning (befintlig)',
- popover: [
- ['Förläggning', 'Placement'],
- ['Spänning', 'Voltage', { unit: 'kV' }],
- ['Ledlängd', 'geom_length', { fn: 'length' }],
- ],
- style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
- return new Style({
- zIndex: 52,
- stroke: new Stroke({
- color: 'black',
- width: width,
- }),
- });
- }),
+ mrr: {
+ appr_ec: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 22,
+ fill: new Fill({
+ color: [247, 170, 67, Math.max((.2-1)/8 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [151, 173, 23, 1],
+ }),
+ });
+ }),
+ },
+ appl_ec: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 25,
+ fill: new Fill({
+ color: [247, 170, 67, Math.max((.2-1)/8 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [197, 14, 31, 1],
+ lineDash: width >= 1.5 ? [2 * width] : undefined,
+ }),
+ });
+ }),
+ },
+ appr_met: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 24,
+ fill: new Fill({
+ color: [0, 0, 0, Math.max((.2-.4)/4 * z + .4, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [151, 173, 23, 1],
+ }),
+ });
+ }),
+ },
+ appl_met: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 26,
+ fill: new Fill({
+ color: [0, 0, 0, Math.max((.2-.4)/4 * z + .4, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [197, 14, 31, 1],
+ lineDash: width >= 1.5 ? [2 * width] : undefined,
+ }),
+ });
+ }),
+ },
+ appr_ogd: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 24,
+ fill: new Fill({
+ color: [30, 55, 87, Math.max((.2-.4)/4 * z + .4, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [151, 173, 23, 1],
+ }),
+ });
+ }),
+ },
+ appl_ogd: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 26,
+ fill: new Fill({
+ color: [30, 55, 87, Math.max((.2-.4)/4 * z + .4, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [197, 14, 31, 1],
+ lineDash: width >= 1.5 ? [2 * width] : undefined,
+ }),
+ });
+ }),
+ },
+ appr_dl: {
+ legend: { zoomLevel: 4 },
+ style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 20,
+ fill: new Fill({
+ color: [228, 53, 45, Math.max((.2-1)/6 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [151, 173, 23, 1],
+ }),
+ });
+ }),
+ },
},
- svk_stolpar: {
- style: [undefined, undefined, undefined, undefined, undefined]
- .concat([3, 4, 5, 6, 8, 10, 15].map(function(radius) {
+
+ svk: {
+ ledningar: {
+ legend: { zoomLevel: 5, type: 'linestring', reuse_canvas: true },
+ style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
return new Style({
- zIndex: 51,
- image: new CircleStyle({
+ zIndex: 52,
+ stroke: new Stroke({
+ color: 'black',
+ width: width,
+ }),
+ });
+ }),
+ },
+ stolpar: {
+ legend: { zoomLevel: 5, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, undefined]
+ .concat([3, 4, 5, 6, 8, 10, 15].map(function(radius) {
+ return new Style({
+ zIndex: 51,
+ image: new CircleStyle({
+ radius: radius,
+ fill: new Fill({
+ color: 'black',
+ }),
+ }),
+ });
+ })),
+ },
+ transmissionsnatsprojekt: {
+ legend: { zoomLevel: 5, type: 'linestring' },
+ style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
+ return new Style({
+ zIndex: 53,
+ stroke: new Stroke({
+ color: 'black',
+ width: width,
+ lineDash: [4 * width],
+ }),
+ });
+ }),
+ },
+ stationer: {
+ legend: { zoomLevel: 3, type: 'point' },
+ style: [3, 4, 5, 6, 7, 8.5, 10].map(function(radius) {
+ return new Style({
+ zIndex: 50,
+ image: new RegularShape({
radius: radius,
+ points: 4,
+ angle: Math.PI/4,
fill: new Fill({
color: 'black',
}),
}),
});
- })),
- },
- svk_transmissionsnatsprojekt: {
- popoverTitle: 'Transmissionsnätsprojekt',
- popover: [
- ['Projektnamn', 'Name'],
- ['Spänning', 'Voltage', { unit: 'kV' }],
- ['Länk', 'Url', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) {
- return new Style({
- zIndex: 53,
- stroke: new Stroke({
- color: 'black',
- width: width,
- lineDash: [4 * width],
- }),
- });
- }),
- },
- svk_stationer: {
- style: [3, 4, 5, 6, 7, 8.5, 10].map(function(radius) {
- return new Style({
- zIndex: 50,
- image: new RegularShape({
- radius: radius,
- points: 4,
- angle: Math.PI/4,
+ })
+ .concat([.5, 1, 1.5, 2, 2].map(function(width) {
+ return new Style({
+ zIndex: 50,
fill: new Fill({
- color: 'black',
+ color: 'rgba(128, 128, 128, .7)',
}),
- }),
- });
- })
- .concat([.5, 1, 1.5, 2, 2].map(function(width) {
- return new Style({
- zIndex: 50,
- fill: new Fill({
- color: 'rgba(128, 128, 128, .7)',
- }),
- stroke: new Stroke({
- width: width,
- color: 'rgb(0, 0, 0)',
- }),
- });
- })),
+ stroke: new Stroke({
+ width: width,
+ color: 'rgb(0, 0, 0)',
+ }),
+ });
+ })),
+ },
},
- vbk_area_current: {
- popoverTitle: 'Projekteringsområde för vindbruk',
- popover: [
- ['Projektnamn', 'Projektnamn'],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Aktuella verk', 'AntalVerk'],
- ['Antal ej koordinatsatta verk', 'AntalEjXY'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Planerad byggstart', 'PlaneradByggstart'],
- ['Planerat drifttagande', 'PlaneratDrift'],
- ['Andringsansokan', 'AndringsansokanPagar'],
- ['Under Byggnation', 'UnderByggnation'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Senast uppdaterat', 'SenasteUppdaterat'],
- ],
- style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 10,
- fill: new Fill({
- color: [168, 198, 223, Math.max((.2-1)/8 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [56, 96, 130, 1],
- }),
- });
- }),
- },
- vbk_area_notcurrent: {
- popoverTitle: 'Projekteringsområde för vindbruk \u2013 ej aktuell',
- popover: [
- ['Projektnamn', 'Projektnamn'],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Aktuella verk', 'AntalVerk'],
- ['Antal ej koordinatsatta verk', 'AntalEjXY'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Planerad byggstart', 'PlaneradByggstart'],
- ['Planerat drifttagande', 'PlaneratDrift'],
- ['Andringsansokan', 'AndringsansokanPagar'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Senast uppdaterat', 'SenasteUppdaterat'],
- ],
- style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- return new Style({
- zIndex: 10,
- fill: new Fill({
- color: [222, 163, 199, Math.max((.2-1)/8 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [148, 55, 112, 1],
- lineDash: width >= 1.5 ? [2 * width] : undefined,
- }),
- });
- }),
- },
- vbk_station_completed: {
- popoverTitle: 'Vindkraftverk \u2013 uppfört',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Uppförandedatum', 'Uppfort'],
- ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-completed.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_processed: {
- popoverTitle: 'Vindkraftverk \u2013 handlagt',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-processed.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_approved: {
- popoverTitle: 'Vindkraftverk \u2013 beviljat',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-approved.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_revoked: {
- popoverTitle: 'Vindkraftverk \u2013 inte längre aktuell/återkallat',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-revoked.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_rejected: {
- popoverTitle: 'Vindkraftverk \u2013 avslagit/nekat',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-rejected.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_dismounted: {
- popoverTitle: 'Vindkraftverk \u2013 nedmonterat',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Uppförandedatum', 'Uppfort'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-dismounted.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
- },
- vbk_station_appealed: {
- popoverTitle: 'Vindkraftverk \u2013 överklagat',
- popover: [
- ['Verk-ID', 'VerkID', { classes: ['feature-objid'] }],
- ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }],
- ['Projektnamn', 'Projektnamn'],
- ['Status', 'Status'],
- ['Handlingstyp', 'Handlingstyp'],
- ['Totalhöjd', 'Totalhojd', { unit: 'm' }],
- ['Navhöjd', 'Navhojd', { unit: 'm' }],
- ['Rotordiameter', 'Rotordiameter', { unit: 'm' }],
- ['Maxeffekt', 'Maxeffekt', { unit: 'MW' }],
- ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }],
- ['Fabrikat', 'Fabrikat'],
- ['Modell', 'Modell'],
- ['Organisationsnamn', 'Organisationsnamn'],
- ['Organisationsnummer', 'Organisationsnummer', { classes: ['feature-orgnr'] }],
- ['Placering', 'Placering'],
- //['Kommun', 'KOMNAMN'],
- //['Län', 'LANSNAMN'],
- ['Elområde', 'ElNamn'],
- ['Datum för senaste uppdatering av verk', 'SenasteUppdaterat'],
- ],
- style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
- return scale === undefined ? undefined : new Style({
- zIndex: 99,
- image: new Icon({
- src: '/assets/icons/wind-turbine-appealed.svg',
- declutter: 'none',
- scale: scale,
- }),
- });
- }),
+ vbk: {
+ area_current: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: [168, 198, 223, Math.max((.2-1)/8 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [56, 96, 130, 1],
+ }),
+ });
+ }),
+ },
+ area_notcurrent: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: [222, 163, 199, Math.max((.2-1)/8 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [148, 55, 112, 1],
+ lineDash: width >= 1.5 ? [2 * width] : undefined,
+ }),
+ });
+ }),
+ },
+ offshore_completed: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 17,
+ fill: new Fill({
+ color: [38, 107, 29, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [38, 107, 29, 1],
+ }),
+ });
+ }),
+ },
+ offshore_approved: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 16,
+ fill: new Fill({
+ color: [56, 160, 44, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [56, 160, 44, 1],
+ }),
+ });
+ }),
+ },
+ offshore_amended: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ const w = z < 4 ? .5 : z <= 5 ? 1.5 : 4;
+ patternCanvas.width = width/2;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(247, 105, 162, 1)';
+ patternContext.beginPath();
+ patternContext.arc(.75*patternCanvas.width, .75*patternCanvas.height, 1.5*w, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 17,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: 2*w,
+ color: [247, 105, 162, 1],
+ lineDash: [8 * w],
+ }),
+ });
+ }),
+ },
+ offshore_rejected: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 11,
+ fill: new Fill({
+ color: [227, 26, 28, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [227, 26, 28, 1],
+ }),
+ });
+ }),
+ },
+ offshore_appealed: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 15,
+ fill: new Fill({
+ color: [177, 88, 40, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [177, 88, 40, 1],
+ }),
+ });
+ }),
+ },
+ offshore_applied: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 14,
+ fill: new Fill({
+ color: [255, 127, 0, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [255, 128, 0, 1],
+ }),
+ });
+ }),
+ },
+ offshore_consultation: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 13,
+ fill: new Fill({
+ color: [254, 217, 118, .65],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [254, 183, 82, 1],
+ }),
+ });
+ }),
+ },
+ offshore_investigation: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ const w = z < 4 ? .5 : z <= 5 ? 1.5 : 4;
+ patternCanvas.width = width*2;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(68, 90, 166, 1)';
+ patternContext.lineWidth = w;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 12,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: 2*w,
+ color: [68, 90, 166, 1],
+ lineDash: [8 * w],
+ }),
+ });
+ }),
+ },
+ offshore_revoked: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: [105, 61, 154, .5],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [105, 62, 153, 1],
+ }),
+ });
+ }),
+ },
+ turbine_completed: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-completed.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_processed: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-processed.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_approved: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-approved.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_revoked: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-revoked.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_rejected: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-rejected.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_dismounted: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-dismounted.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
+ turbine_appealed: {
+ legend: { zoomLevel: 7, type: 'point' },
+ style: [undefined, undefined, undefined, undefined, .125, .125, .25, .5, 1, 2, 4, 8].map(function(scale) {
+ return scale === undefined ? undefined : new Style({
+ zIndex: 99,
+ image: new Icon({
+ src: '/assets/icons/wind-turbine-appealed.svg',
+ declutter: 'none',
+ scale: scale,
+ }),
+ });
+ }),
+ },
},
- /* Documentation at
- * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/utforda-avverkningar---produktbeskrivning.pdf
- * */
- sks_clearcut_comp: {
- popoverTitle: 'Utförd avverkning',
- popover: [
- ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }],
- ['Registeringsår', 'ArendeAr'],
- ['Skogstyp', 'Skogstyp'],
- ['Areal anmält', 'AnmaldHa', { unit: 'ha' }],
- ['Areal naturlig föryngring', 'NatforHa', { unit: 'ha', fn: (v) => v === 0 ? '' : v }],
- //['Areal plantering', 'SkogsodlHa', { unit: 'ha', fn: (v) => v === 0 ? '' : v }],
- ['Avverkningstyp', 'Avverktyp'],
- ['Datum för avverkning', 'Avvdatum'],
- ['Ursprung för datum för avverkning', 'KallaDatum'],
- //['Ursprung för areal avverkning', 'KallaAreal'],
- //['Kommun', 'Kommun'],
- //['Län', 'Lan'],
- ['Areal för ytan', 'geom_area', { fn: 'area' }],
- ],
- style: [0, 0, 0, 0, 0, .5, .75, 1, 1, 1, 1, 1].map(function(width, z) {
- return new Style({
- zIndex: 10,
- fill: new Fill({
- color: [255, 102, 102, Math.max((.2-1)/8 * z + 1, 0)],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [204, 0, 0, 1],
- }),
- });
- }),
- },
- /* Documentation at
- * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/yttre-granser-for-avverkningsanmalda-omraden---produktbeskrivning.pdf
- * */
- sks_clearcut_appl: {
- popoverTitle: 'Avverkningsanmälansområde',
- popover: [
- ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }],
- ['Inkom datum', 'Inkomdatum'],
- ['Registeringsår', 'ArendeAr'],
- //['Skogstyp', 'Skogstyp'],
- ['Areal anmält', 'AnmaldHa', { unit: 'ha' }],
- ['Areal naturlig föryngring', 'NatforHa', { unit: 'ha', fn: (v) => v === 0 ? '' : v }],
- ['Areal plantering', 'SkogsodlHa', { unit: 'ha', fn: (v) => v === 0 ? '' : v }],
- ['Avverkningssäsong', 'AvvSasong'],
- //['Avverkningstyp', 'Avverktyp'],
- //['Ändamål', 'Andamal'],
- //['Kommun', 'Kommun'],
- //['Län', 'Lan'],
- ['Ärendestatus', 'ArendeStatus'],
- ['Avverkad areal', 'AvvHa', { unit: 'ha' }],
- ],
- style: [0, 0, 0, 0, 0, .5, .75, 1, 1, 1, 1, 1].map(function(width, z) {
- return new Style({
- zIndex: 10,
- fill: (width === undefined || width === 0) ?
- new Fill({ color: [255, 102, 102, Math.max((.2-1)/8 * z + 1, 0)*.75] }) :
- (function() {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- const slope = 45 * Math.PI/180;
- const spacing = z < 10 ? z*2 : 40;
- const len = Math.hypot(1, slope);
- const w = patternCanvas.width = Math.round(1/len + spacing)
- const h = patternCanvas.height = Math.round(slope/len + spacing * slope);
-
- patternContext.fillStyle = 'rgba(255, 102, 102, .1)';
- patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
- patternContext.strokeStyle = 'rgba(204, 0, 0, 1)';
- patternContext.lineWidth = Math.max(1, width/2);
- patternContext.beginPath();
- patternContext.moveTo(0, h);
- patternContext.lineTo(w, 0);
- patternContext.moveTo(-w, h);
- patternContext.lineTo(w, -h);
- patternContext.moveTo(0, 2*h);
- patternContext.lineTo(2*w, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Fill({ color: context.createPattern(patternCanvas, 'repeat') });
- })(),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [204, 0, 0, 1],
- lineDash: width >= 1.5 ? [2 * width] : undefined,
- }),
- });
- }),
+ avverk: {
+ /* Documentation at
+ * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/utforda-avverkningar---produktbeskrivning.pdf
+ * */
+ utford: {
+ legend: { zoomLevel: 7 },
+ style: [0, 0, 0, 0, 0, .5, .75, 1, 1, 1, 1, 1].map(function(width, z) {
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: [255, 102, 102, Math.max((.2-1)/8 * z + 1, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [204, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ /* Documentation at
+ * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/yttre-granser-for-avverkningsanmalda-omraden---produktbeskrivning.pdf
+ * */
+ anmald: {
+ legend: { zoomLevel: 7 },
+ style: [0, 0, 0, 0, 0, .5, .75, 1, 1, 1, 1, 1].map(function(width, z) {
+ return new Style({
+ zIndex: 10,
+ fill: (width === undefined || width === 0) ?
+ new Fill({ color: [255, 102, 102, Math.max((.2-1)/8 * z + 1, 0)*.75] }) :
+ (function() {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ const slope = 45 * Math.PI/180;
+ const spacing = z < 10 ? z*2 : 40;
+ const len = Math.hypot(1, slope);
+ const w = patternCanvas.width = Math.round(1/len + spacing);
+ const h = patternCanvas.height = Math.round(slope/len + spacing * slope);
+
+ patternContext.fillStyle = 'rgba(255, 102, 102, .1)';
+ patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
+ patternContext.strokeStyle = 'rgba(204, 0, 0, 1)';
+ patternContext.lineWidth = Math.max(1, width/2);
+ patternContext.beginPath();
+ patternContext.moveTo(0, h);
+ patternContext.lineTo(w, 0);
+ patternContext.moveTo(-w, h);
+ patternContext.lineTo(w, -h);
+ patternContext.moveTo(0, 2*h);
+ patternContext.lineTo(2*w, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Fill({ color: context.createPattern(patternCanvas, 'repeat') });
+ })(),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [204, 0, 0, 1],
+ lineDash: width >= 1.5 ? [2 * width] : undefined,
+ }),
+ });
+ }),
+ },
},
- nvr_tilltradesforbud: {
- popoverTitle: 'Tillträdesförbud',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Föreskriftsområde', 'FORSKRNAMN'],
- ['Namn', 'OBJEKTNAMN'],
- ['Beslutsstatus', 'BESLSTAT'],
- ['Föreskriftstyp', 'FORESKRTYP'],
- ['Föreskriftssubtyp', 'FORESKRIFT'],
- ['Från datum', 'FRANDATUM'],
- ['Till datum', 'TILLDATUM'],
- ['Beskrivning', 'BESKRIVN'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [1, 1.5, 2, 3, 3.5, 4, 5, 5, 6, 7, 8, 10].map(function(width, z) {
- return new Style({
- zIndex: 23,
- fill: new Fill({
- /* transparent fill so clicking the inside of the polygon triggers a popover */
- /* XXX could also use a custom renderer but that doesn't seem to work */
- color: [0, 0, 0, 0],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [255, 0, 0, 1],
- }),
- });
- }),
- },
- nvr_nationalpark: {
- popoverTitle: 'Nationalpark',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(0, 55, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 22,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [0, 55, 0, 1],
- }),
- });
- }),
- },
- nvr_naturreservat: {
- popoverTitle: 'Naturreservat',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(7, 181, 7, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 21,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [7, 181, 7, 1],
- }),
- });
- }),
- },
- nvr_naturreservat_kommunalt: {
- popoverTitle: 'Kommunalt naturreservat',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(7, 181, 7, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 20,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [7, 181, 7, 1],
- }),
- });
- }),
- },
- nvr_naturvardsomrade: {
- popoverTitle: 'Naturvårdsområde',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(176, 255, 176, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 19,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [176, 255, 176, 1],
- }),
- });
- }),
- },
- nvr_djur_och_vaxtskyddsomrade: {
- popoverTitle: 'Djur- och växtskyddsområde',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(255, 255, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 18,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [255, 255, 0, 1],
- }),
- });
- }),
- },
- nvr_kulturreservat: {
- popoverTitle: 'Kulturreservat',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(154, 102, 255, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 17,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [154, 102, 255, 1],
- }),
- });
- }),
- },
- nvr_vattenskyddsomrade: {
- popoverTitle: 'Vattenskyddsområden',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ikraftträdandedatum föreskrifter', 'IKRAFTDATF'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Tillsynsmyndighet', 'TILLSYNSMH'],
- ['Prövningsmyndighet tillstånd', 'PROVNMHTIL'],
- ['Prövningsmyndighet dispens', 'PROVNMHDIS'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(0, 105, 212, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 16,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [0, 105, 212, 1],
- }),
- });
- }),
- },
- nvr_landskapsbildsskyddsomrade: {
- popoverTitle: 'Landskapsbildsskyddsområde',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(135, 110, 71, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 15,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [134, 110, 71, 1],
- }),
- });
- }),
- },
- nvr_skogligt_biotopskyddsomrade: {
- popoverTitle: 'Biotopskydd i skogsmark',
- popover: [
- ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }],
- ['Biotopkategori', 'Biotyp'],
- ['Skogstyp', 'Naturtyp'],
- ['Registeringsår', 'ArendeAr'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Skogsmarksareal', 'AreaProd', { unit: 'ha' }],
- ['Beslutsdatum', 'Datbeslut'],
- ['Länk', 'Url', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(135, 90, 71, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 14,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [134, 90, 71, 1],
- }),
- });
- }),
- },
- nvr_ovrigt_biotopskyddsomrade: {
- popoverTitle: 'Biotopskydd utanför skogsmark',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(255, 95, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 13,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [255, 95, 0, 1],
- }),
- });
- }),
- },
- nvr_naturminne_yta: {
- popoverTitle: 'Naturminne (yta)',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(113, 0, 116, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 12,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [134, 0, 116, 1],
- }),
- });
- }),
- },
- nvr_naturminne_punkt: {
- popoverTitle: 'Naturminne (punkt)',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [undefined, undefined, undefined, undefined].concat([3, 4, 6, 8, 12, 16, 20, 24].map(function(width, z) {
- return new Style({
- zIndex: 12,
- image: new CircleStyle({
- radius: width,
+ skydd: {
+ tilltradesforbud: {
+ legend: { zoomLevel: 2 },
+ style: [1, 1.5, 2, 3, 3.5, 4, 5, 5, 6, 7, 8, 10].map(function(width) {
+ return new Style({
+ zIndex: 23,
+ fill: new Fill({
+ /* transparent fill so clicking the inside of the polygon triggers a popover */
+ /* XXX could also use a custom renderer but that doesn't seem to work */
+ color: [0, 0, 0, 0],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [255, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ nationalpark: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(0, 55, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 22,
fill: new Fill({
- color: 'rgba(113, 0, 116, .5)',
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [0, 55, 0, 1],
+ }),
+ });
+ }),
+ },
+ naturreservat: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(7, 181, 7, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 21,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [7, 181, 7, 1],
+ }),
+ });
+ }),
+ },
+ naturreservat_kommunalt: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(7, 181, 7, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 20,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [7, 181, 7, 1],
+ }),
+ });
+ }),
+ },
+ naturvardsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(176, 255, 176, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 19,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [176, 255, 176, 1],
+ }),
+ });
+ }),
+ },
+ djur_och_vaxtskyddsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 255, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 18,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [255, 255, 0, 1],
+ }),
+ });
+ }),
+ },
+ kulturreservat: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(154, 102, 255, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 17,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [154, 102, 255, 1],
+ }),
+ });
+ }),
+ },
+ vattenskyddsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(0, 105, 212, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 16,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [0, 105, 212, 1],
+ }),
+ });
+ }),
+ },
+ landskapsbildsskyddsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(135, 110, 71, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 15,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [134, 110, 71, 1],
+ }),
+ });
+ }),
+ },
+ skogligt_biotopskyddsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(135, 90, 71, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 14,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [134, 90, 71, 1],
+ }),
+ });
+ }),
+ },
+ ovrigt_biotopskyddsomrade: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 95, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 13,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [255, 95, 0, 1],
+ }),
+ });
+ }),
+ },
+ naturminne_yta: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(113, 0, 116, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 12,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [134, 0, 116, 1],
+ }),
+ });
+ }),
+ },
+ naturminne_punkt: {
+ legend: { zoomLevel: 6, type: 'point' },
+ style: [undefined, undefined, undefined, undefined].concat([3, 4, 6, 8, 12, 16, 20, 24].map(function(width) {
+ return new Style({
+ zIndex: 12,
+ image: new CircleStyle({
+ radius: width,
+ fill: new Fill({
+ color: 'rgba(113, 0, 116, .5)',
+ }),
+ stroke: new Stroke({
+ width: Math.log2(width)/2,
+ color: 'rgba(113, 0, 116, 1)',
+ }),
+ }),
+ });
+ }))
+ },
+ interimistiskt_forbud: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(168, 0, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 11,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [168, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ fageldirektivet: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width*2;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(230, 0, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.beginPath();
+ patternContext.lineWidth *= 6;
+ patternContext.moveTo(-.5*patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -.5*patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 1.5*patternCanvas.height);
+ patternContext.lineTo(1.5*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [230, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ habitatdirektivet: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width*2;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(0, 77, 168, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+ patternContext.beginPath();
+ patternContext.lineWidth *= 6;
+ patternContext.moveTo(0, -.5*patternCanvas.height);
+ patternContext.lineTo(1.5*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-.5*patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 1.5*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [0, 77, 168, 1],
+ }),
+ });
+ }),
+ },
+ helcom: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(130, 130, 130, 1)';
+ const r = z < 5 ? (z+1)*.75 : z*.5;
+ patternContext.beginPath();
+ patternContext.arc(.5*patternCanvas.width, .5*patternCanvas.height, r, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 9,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [130, 130, 130, 1],
+ }),
+ });
+ }),
+ },
+ ramsar: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(195, 0, 255, 1)';
+ const r = z < 5 ? (z+1)*.75 : z*.5;
+ patternContext.beginPath();
+ patternContext.arc(.25*patternCanvas.width, .25*patternCanvas.height, r, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 9,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [195, 0, 255, 1],
+ }),
+ });
+ }),
+ },
+ ospar: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(168, 0, 0, 1)';
+ const r = z < 5 ? (z+1)*.75 : z*.5;
+ patternContext.beginPath();
+ patternContext.arc(.25*patternCanvas.width, .75*patternCanvas.height, r, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 9,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [168, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ varldsarv: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(168, 0, 0, 1)';
+ const r = z < 5 ? (z+1)*.75 : z*.5;
+ patternContext.beginPath();
+ patternContext.arc(.75*patternCanvas.width, .25*patternCanvas.height, r, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 9,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [168, 0, 0, 1],
+ }),
+ });
+ }),
+ },
+ biosfarsomraden: {
+ legend: { zoomLevel: 1 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(131, 0, 219, 1)';
+ const r = z < 5 ? (z+1)*.75 : z*.5;
+ patternContext.beginPath();
+ patternContext.arc(.75*patternCanvas.width, .75*patternCanvas.height, r, 0, 2*Math.PI, true);
+ patternContext.fill();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 9,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [131, 0, 219, 1],
+ }),
+ });
+ }),
+ },
+ naturvardsavtal: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 0, 197, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 21,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [255, 0, 197, 1],
+ }),
+ });
+ }),
+ },
+ naturvardsavtal_skogsstyrelsen: {
+ legend: { zoomLevel: 2 },
+ style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 0, 197, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 20,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 4 ? .5 : z <= 5 ? 1 : 2,
+ color: [255, 0, 197, 1],
+ }),
+ });
+ }),
+ },
+ atervatningsavtal: {
+ legend: { zoomLevel: 0 },
+ style: [0, 1, 2, 3, 4, 5, 6].map(function(width) {
+ return new Style({
+ zIndex: 5,
+ fill: new Fill({
+ color: [255, 115, 0, .4],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: .5,
+ color: [255, 115, 0, 1],
+ }),
+ });
+ })
+ .concat([7, 8, 9, 10, 11].map(function() {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = 16;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 115, 0, 1)';
+ patternContext.lineWidth = 1;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 5,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
}),
stroke: new Stroke({
- width: Math.log2(width)/2,
- color: 'rgba(113, 0, 116, 1)',
+ width: 1.5,
+ color: [255, 115, 0, 1],
}),
- }),
- });
- }))
- },
- nvr_interimistiskt_forbud: {
- popoverTitle: 'Interimistiskt förbud',
- popover: [
- ['NVR-ID', 'NVRID', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Beslutsstatus', 'BESLSTATUS'],
- ['Beslutsdatum (bildande)', 'URSBESLDAT'],
- ['Ursprungligt gällandedatum', 'URSGALLDAT'],
- ['Senaste gällandedatum', 'SENGALLDAT'],
- ['Förvaltare', 'FORVALTARE'],
- ['IUCN-kategori', 'IUCNKAT'],
- ['Diarienummer', 'DIARIENR', { classes: ['feature-attr-dnr'] }],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsmyndighet', 'BESLMYND'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(168, 0, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 11,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [168, 0, 0, 1],
- }),
- });
- }),
- },
- nvr_fageldirektivet: {
- popoverTitle: 'Fågeldirektivet (SPA)',
- popover: [
- ['Områdeskod', 'SITE_CODE', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Områdestyp', 'OMRADESTYP'],
- ['Uppgiftslämnare', 'UPPLAMNARE'],
- ['SPA-datum', 'SPA_DATUM'],
- ['SCI-förslagsdatum', 'SCI_FORSL'],
- ['SCI-datum', 'SCI_DATUM'],
- ['SAC-datum', 'SAC_DATUM'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Kvalitet', 'KVALITET'],
- ['Kännetecken för området', 'KARAKTAR'],
- ['Arter', 'ARTER'],
- ['Naturtyper', 'NATURTYPER'],
- ['Bevarandeplan', 'BEVPLAN', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width*2;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(230, 0, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.beginPath();
- patternContext.lineWidth *= 6;
- patternContext.moveTo(-.5*patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -.5*patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 1.5*patternCanvas.height);
- patternContext.lineTo(1.5*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 10,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 4 ? .5 : z <= 5 ? 1 : 2,
- color: [230, 0, 0, 1],
- }),
- });
- }),
- },
- nvr_habitatdirektivet: {
- popoverTitle: 'Art- och habitatdirektivet (SCI)',
- popover: [
- ['Områdeskod', 'SITE_CODE', { classes: ['feature-objid'] }],
- ['Namn', 'NAMN'],
- ['Områdestyp', 'OMRADESTYP'],
- ['Uppgiftslämnare', 'UPPLAMNARE'],
- ['SPA-datum', 'SPA_DATUM'],
- ['SCI-förslagsdatum', 'SCI_FORSL'],
- ['SCI-datum', 'SCI_DATUM'],
- ['SAC-datum', 'SAC_DATUM'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Kvalitet', 'KVALITET'],
- ['Kännetecken för området', 'KARAKTAR'],
- ['Arter', 'ARTER'],
- ['Naturtyper', 'NATURTYPER'],
- ['Bevarandeplan', 'BEVPLAN', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width*2;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(0, 77, 168, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
- patternContext.beginPath();
- patternContext.lineWidth *= 6;
- patternContext.moveTo(0, -.5*patternCanvas.height);
- patternContext.lineTo(1.5*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-.5*patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 1.5*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 10,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 4 ? .5 : z <= 5 ? 1 : 2,
- color: [0, 77, 168, 1],
- }),
- });
- }),
- },
- nvr_helcom: {
- popoverTitle: 'Marina skyddade områden (Helcom MPA)',
- popover: [
- ['Namn', 'NAME'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(130, 130, 130, 1)';
- let r = z < 5 ? (z+1)*.75 : z*.5;
- patternContext.beginPath();
- patternContext.arc(.5*patternCanvas.width, .5*patternCanvas.height, r, 0, 2*Math.PI, true)
- patternContext.fill();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 9,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [130, 130, 130, 1],
- }),
- });
- }),
- },
- nvr_ramsar: {
- popoverTitle: 'Ramsar-områden (Våtmarkskonventionen)',
- popover: [
- ['Ramsar-ID', 'RAMSAR_ID', { classes: ['feature-objid'] }],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Namn', 'NAMN'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'LAND_HA', { unit: 'ha' }],
- ['Areal vatten', 'VATTEN_HA', { unit: 'ha' }],
- ['Skogsmarksareal', 'SKOG_HA', { unit: 'ha' }],
- ['Ursprungligt beslutsdatum', 'URSPR_BESL'],
- ['Senaste beslutsdatum', 'SEN_BESLUT'],
- ['Rättsakt', 'LEGAL_ACT'],
- ['Länk', 'LINK', { fn: function(v) {
- if (v === undefined || v === null || v === '') {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(195, 0, 255, 1)';
- let r = z < 5 ? (z+1)*.75 : z*.5;
- patternContext.beginPath();
- patternContext.arc(.25*patternCanvas.width, .25*patternCanvas.height, r, 0, 2*Math.PI, true)
- patternContext.fill();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 9,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [195, 0, 255, 1],
- }),
- });
- }),
- },
- nvr_ospar: {
- popoverTitle: 'Marina skyddade områden (Ospar MPA)',
- popover: [
- ['Ursprung', 'ORIGIN'],
- ['N2000-namn', 'NAMN_N2000'],
- ['MPA-ID', 'MPA_ID', { classes: ['feature-objid'] }],
- ['MPA-namn', 'MPA_NAMN'],
- ['N2000-ID', 'N2000_SITE', { classes: ['feature-objid'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(168, 0, 0, 1)';
- let r = z < 5 ? (z+1)*.75 : z*.5;
- patternContext.beginPath();
- patternContext.arc(.25*patternCanvas.width, .75*patternCanvas.height, r, 0, 2*Math.PI, true)
- patternContext.fill();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 9,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [168, 0, 0, 1],
- }),
- });
- }),
- },
- nvr_varldsarv: {
- popoverTitle: 'Världsarv med mycket höga naturvärden (Unesco)',
- popover: [
- ['Namn', 'NAMN'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(168, 0, 0, 1)';
- let r = z < 5 ? (z+1)*.75 : z*.5;
- patternContext.beginPath();
- patternContext.arc(.75*patternCanvas.width, .25*patternCanvas.height, r, 0, 2*Math.PI, true)
- patternContext.fill();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 9,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [168, 0, 0, 1],
- }),
- });
- }),
- },
- nvr_biosfarsomraden: {
- popoverTitle: 'Biosfärsområde (Unesco)',
- popover: [
- ['Namn', 'NAMN'],
- ['Skyddstyp', 'SKYDDSTYP'],
- ['Länk', 'LINK', { fn: function(v) {
- if (v === undefined || v === null || v === '') {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(131, 0, 219, 1)';
- let r = z < 5 ? (z+1)*.75 : z*.5;
- patternContext.beginPath();
- patternContext.arc(.75*patternCanvas.width, .75*patternCanvas.height, r, 0, 2*Math.PI, true)
- patternContext.fill();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 9,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [131, 0, 219, 1],
- }),
- });
- }),
- },
- nvr_naturvardsavtal: {
- popoverTitle: 'Naturvårdsavtal (Naturvårdsverket, Länsstyrelsen)',
- popover: [
- ['ID', 'ID', { classes: ['feature-objid'] }],
- ['Namn', 'OBJNAMN'],
- ['Fastighet', 'FASTBET', { classes: ['feature-objid'] }],
- ['Giltig från', 'DATSTART'],
- ['Giltig till', 'DATSLUT'],
- ['Diarienummer', 'DIARIENRNV', { classes: ['feature-attr-dnr'] }],
- ['Satus', 'STATUS'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(255, 0, 197, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 21,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [255, 0, 197, 1],
- }),
- });
- }),
- },
- nvr_naturvardsavtal_skogsstyrelsen: {
- popoverTitle: 'Naturvårdsavtal (Skogsstyrelsen)',
- popover: [
- ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }],
- ['Registeringsår', 'ArendeAr'],
- ['Biotopkategori', 'NvaTyp'],
- ['Skogstyp', 'Naturtyp'],
- ['Avtalsdatum', 'DatAvtal'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Skogsmarksareal', 'AreaProd', { unit: 'ha' }],
- ['Länk', 'Url', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Undertyp', 'Undertyp'],
- ],
- style: [4, 8, 16, 16, 32, 32, 64, 64, 64, 128, 128, 128].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(255, 0, 197, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 20,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [255, 0, 197, 1],
- }),
- });
- }),
+ });
+ })),
+ },
},
- ri_naturvard: {
- popoverTitle: 'Riksintresse naturvård',
- popover: [
- ['Namn', 'NAMN'],
- ['Skydd', 'SKYDD'],
- ['Ämnesområde', 'AMNESOMRAD'],
- ['Beskrivning', 'BESKRIVNIN', { fn: function(v) {
- if (v === undefined || v === null || !(v.startsWith('http://') || v.startsWith('https://'))) {
- return v;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsdatum', 'BESLUTSDAT'],
- ['Original-ID', 'ORGINALID', { classes: ['feature-objid'] }],
- ['Riks-ID', 'RIKSID', { classes: ['feature-objid'] }],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(154, 230, 0, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [154, 230, 0, 1],
- }),
- });
- }),
- },
- ri_friluftsliv: {
- popoverTitle: 'Riksintresse friluftsliv',
- popover: [
- ['Namn', 'NAMN'],
- ['Skydd', 'SKYDD'],
- ['Ämnesområde', 'AMNESOMR'],
- ['Områdesnummer', 'OMRADESNR', { classes: ['feature-objid'] }],
- ['Länk värdebeskrivning', 'LANK_VARDE', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Lagrum', 'LAGRUM'],
- ['Beslutsdatum', 'BESLDATUM'],
- ['Ärendenummer', 'ARENDENR', { classes: ['feature-attr-dnr'] }],
- ['Länk, beslut', 'LANK_BESLU', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Aktivitet', 'AKTIVITET'],
- ['Naturtyp', 'NATURTYP'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Areal land', 'AREA_LAND_', { unit: 'ha' }],
- ['Areal vatten', 'AREA_VATTE', { unit: 'ha' }],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(0, 127, 232, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
- color: [0, 127, 232, 1],
- }),
- });
- }),
- },
- ri_rorligt_friluftsliv: {
- popoverTitle: 'Riksintresse rörligt friluftsliv (MB 4 kap 1§ och 2§)',
- popover: [
- ['Namn', 'NAMN'],
- //['Original-ID', 'ORIGINALID', { classes: ['feature-objid'] }],
- ['Beskrivning', 'BESKRIVNIN'],
- //['Metodbeskrivning', 'METODBESKR'],
- //['Tillk. datum', 'TILLKDATUM'],
- //['Rev. datum', 'REVDATUM'],
- ['Anmärkning', 'ANM'],
- ['Länk', 'OBJEKTLANK', { fn: function(v) {
- if (v === null) {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Referens', 'REFERENS'],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(187, 227, 212, .25)';
- patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
- patternContext.strokeStyle = 'rgba(56, 151, 117, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
- patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, 2*patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
- color: [56, 151, 117, 1],
- lineDash: [width/4, width/3],
- }),
- });
- }),
- },
- ri_obruten_kust: {
- popoverTitle: 'Riksintresse obruten kust (MB 4 kap 3§)',
- popover: [
- ['Namn', 'NAMN'],
- //['Original-ID', 'ORIGINALID', { classes: ['feature-objid'] }],
- ['Beskrivning', 'BESKRIVNIN'],
- //['Metodbeskrivning', 'METODBESKR'],
- //['Tillk. datum', 'TILLKDATUM'],
- //['Rev. datum', 'REVDATUM'],
- ['Anmärkning', 'ANM'],
- ['Objekttyp', 'OBJTYP'],
- ['Länk', 'OBJEKTLANK', { fn: function(v) {
- if (v === null) {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Referens', 'REFERENS'],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(227, 227, 187, .25)';
- patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
- patternContext.strokeStyle = 'rgba(156, 158, 56, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
- color: [156, 158, 56, 1],
- lineDash: [width/4, width/3],
- }),
- });
- }),
- },
- ri_obrutet_fjall: {
- popoverTitle: 'Riksintresse obrutet fjäll (MB 4 kap 5§)',
- popover: [
- ['Namn', 'NAMN'],
- //['Original-ID', 'ORIGINALID', { classes: ['feature-objid'] }],
- ['Beskrivning', 'BESKRIVNIN'],
- ['Metodbeskrivning', 'METODBESKR'],
- ['Tillk. datum', 'TILLKDATUM'],
- //['Rev. datum', 'REVDATUM'],
- ['Länk', 'OBJEKTLANK', { fn: function(v) {
- if (v === null) {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Referens', 'REFERENS'],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = width;
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'rgba(255, 255, 209, .25)';
- patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
- patternContext.strokeStyle = 'rgba(219, 183, 60, 1)';
- patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(0, -patternCanvas.height);
- patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
- patternContext.stroke();
- patternContext.moveTo(-patternCanvas.width, 0);
- patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
- color: [219, 183, 60, 1],
- lineDash: [width/4, width/3],
- }),
- });
- }),
- },
- ri_skyddade_vattendrag: {
- popoverTitle: 'Riksintresse skyddade vattendrag (MB 4 kap 6§)',
- popover: [
- ['Namn', 'NAMN'],
- //['Original-ID', 'ORIGINALID', { classes: ['feature-objid'] }],
- ['Beskrivning', 'BESKRIVNIN'],
- ['Metodbeskrivning', 'METODBESKR'],
- ['Tillk. datum', 'TILLKDATUM'],
- ['Rev. datum', 'REVDATUM'],
- ['Anmärkning', 'ANM'],
- ['Digitaliseringsskala', 'DIG_SKALA'],
- ['Länk', 'OBJEKTLANK', { fn: function(v) {
- if (v === null) {
- return;
- }
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ['Areal', 'geom_area', { fn: 'area' }],
- ['Referens', 'REFERENS'],
- ],
- style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
- return new Style({
- zIndex: 8,
- fill: new Fill({
- color: [102, 157, 240, .25],
+ nv: {
+ naturvarde_sks: {
+ legend: { zoomLevel: 0 },
+ style: [0, 1, 2, 3, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: [255, 170, 0, .2],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: .5,
+ color: [255, 170, 0, .8],
+ }),
+ });
+ })
+ .concat([6, 7, 8, 9, 10, 11].map(function() {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = 16;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(255, 170, 0, 1)';
+ patternContext.lineWidth = 1;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: new Stroke({
+ width: 1.5,
+ color: [255, 170, 0, 1],
+ }),
+ });
+ })),
+ },
+ nyckelbiotop: {
+ legend: { zoomLevel: 0 },
+ style: [0, 1, 2, 3, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: [217, 148, 9, .2],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: .5,
+ color: [217, 148, 9, .8],
+ }),
+ });
+ })
+ .concat([6, 7, 8, 9, 10, 11].map(function() {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = 16;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(217, 148, 9, 1)';
+ patternContext.lineWidth = 1;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: new Stroke({
+ width: 1.5,
+ color: [217, 148, 9, 1],
+ }),
+ });
+ })),
+ },
+ nyckelbiotop_storskogsbruk: {
+ legend: { zoomLevel: 0 },
+ style: [0, 1, 2, 3, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: [217, 148, 9, .2],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: .5,
+ color: [217, 148, 9, .8],
+ }),
+ });
+ })
+ .concat([6, 7, 8, 9, 10, 11].map(function() {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = 16;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(217, 148, 9, 1)';
+ patternContext.lineWidth = 1;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: new Stroke({
+ width: 1.5,
+ color: [217, 148, 9, 1],
+ }),
+ });
+ })),
+ },
+ sumpskog: {
+ legend: { zoomLevel: 5 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ const w = Math.max(1, width);
+ patternCanvas.width = z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 6 : 8;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(158, 200, 215, 1)';
+ patternContext.lineWidth = w;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 5,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: w/2,
+ color: [158, 200, 215, 1],
+ }),
+ });
+ }),
+ },
+ pagaende_naturreservatsbildning: {
+ legend: { zoomLevel: 1 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.setLineDash([width/4, width/4]);
+ patternContext.strokeStyle = 'rgba(7, 181, 7, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(width/4, 0);
+ patternContext.lineTo(width/4, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.beginPath();
+ patternContext.lineDashOffset = width/4;
+ patternContext.moveTo(3*width/4, 0);
+ patternContext.lineTo(3*width/4, patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 10,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 3 : 4,
+ color: [7, 181, 7, 1],
+ lineDash: [width/8, width/4],
+ }),
+ });
}),
- stroke: width === 0 ? undefined : new Stroke({
- width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
- color: [41, 109, 197, 1],
- lineDash: [width/4, width/3],
+ },
+ snus: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width) {
+ return new Style({
+ zIndex: 4,
+ fill: new Fill({
+ color: [168,168,0,.2],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [168,77,0,.75],
+ }),
+ });
}),
- });
- }),
+ },
},
- ren_betesomraden: {
- popoverTitle: 'Samebyarnas betesområde',
- popover: [
- ['Sameby', 'NAMN'],
- ['Samebys typ', 'SAMEBY_TYP'],
- ['Signatur', 'SIGNATUR'],
- ['Aktualitet', 'AKTUALITET'],
- ],
- style: [1, 1.5, 2, 3, 3.5, 4, 5, 5, 6, 7, 8, 10].map(function(width, z) {
- return new Style({
- zIndex: 4,
- fill: new Fill({
- /* transparent fill so clicking the inside of the polygon triggers a popover */
- /* XXX could also use a custom renderer but that doesn't seem to work */
- color: [0, 0, 0, 0],
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [179, 153, 102, 1],
- }),
- });
- }),
- },
- ren_flyttled: {
- popoverTitle: 'Samebyarnas markanvändningsredovisning \u2013 flyttled',
- popover: [
- ['Led-ID', 'LED_ID', { classes: ['feature-objid'], fn: (v) => v === 0 ? '' : v }],
- ['Sameby #1', 'SAMEBY1'],
- ['Sameby #2', 'SAMEBY2'],
- ['Sameby #3', 'SAMEBY3'],
- ['Beskrivning', 'BESKRIVNIN'],
- ['Årstid', 'ARSTID'],
- ['Riksintresse', 'RIKSINTR'],
- ['Fast led', 'FAST_LED'],
- ['Aktualitet', 'AKTUALITET'],
- ['Signatur', 'SIGNATUR'],
- ['Ledlängd', 'geom_length', { fn: 'length' }],
- ],
- style: [.75, 1, 1.5, 2, 3, 4, 5, 5, 6, 7, 8, 10].map(function(width, z) {
- return new Style({
- zIndex: 7,
- stroke: new Stroke({
- width: 2*width,
- color: [119, 99, 59, 1],
- lineDash: [4 * width],
- }),
- });
- }),
- },
- ren_riks_ren: {
- popoverTitle: 'Riksintresse rennäring',
- popover: [
- ['Lagrum', 'LAGRUM'],
- ['Aktualitet', 'AKTUALITET'],
- ['Signatur', 'SIGNATUR'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
- const patternCanvas = document.createElement('canvas');
- const patternContext = patternCanvas.getContext('2d');
- patternCanvas.width = z < 4 ? 4 : z <= 5 ? 8 : Math.pow(2, Math.round(Math.log2(width) + 3));
- patternCanvas.height = patternCanvas.width;
- patternContext.fillStyle = 'transparent';
- patternContext.strokeStyle = 'rgba(179, 153, 102, 1)';
- patternContext.lineWidth = Math.max(1, width/2);
- patternContext.beginPath();
- patternContext.moveTo(0, 0);
- patternContext.lineTo(patternCanvas.width, 0);
- patternContext.stroke();
-
- const canvas = document.createElement('canvas');
- const context = canvas.getContext('2d');
- return new Style({
- zIndex: 6,
- fill: new Fill({
- color: context.createPattern(patternCanvas, 'repeat'),
- }),
- stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [179, 153, 102, 1],
- }),
- });
- }),
- },
- ren_omr_riks: {
- popoverTitle: '(Kärn)områden av riksintresse rennäring',
- popover: [
- ['Områdes-ID', 'OMR_NR', { classes: ['feature-objid'] }],
- ['Länk', 'LANK'],
- ['Årets runt', 'ARET_RUNT'],
- ['Sameby', 'SAMEBY'],
- ['Ansvarig', 'ANSVARIG'],
- ['Aktualitet', 'AKTUALITET'],
- ['Signatur', 'SIGNATUR'],
- ['Areal', 'geom_area', { fn: 'area' }],
- ],
- style: [.5, .5, 1, 1, 1, 1.5, 1.5, 1.5, 2, 2, 2, 2].map(function(width, z) {
+ ri: {
+ naturvard: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(154, 230, 0, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 8,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [154, 230, 0, 1],
+ }),
+ });
+ }),
+ },
+ friluftsliv: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(0, 127, 232, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 8,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 1 : z < 4 ? 2 : z <= 5 ? 4 : 8,
+ color: [0, 127, 232, 1],
+ }),
+ });
+ }),
+ },
+ rorligt_friluftsliv: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(187, 227, 212, .25)';
+ patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
+ patternContext.strokeStyle = 'rgba(56, 151, 117, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, patternCanvas.height);
+ patternContext.lineTo(patternCanvas.width, -patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, 2*patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 8,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
+ color: [56, 151, 117, 1],
+ lineDash: [width/4, width/3],
+ }),
+ });
+ }),
+ },
+ obruten_kust: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(227, 227, 187, .25)';
+ patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
+ patternContext.strokeStyle = 'rgba(156, 158, 56, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 8,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
+ color: [156, 158, 56, 1],
+ lineDash: [width/4, width/3],
+ }),
+ });
+ }),
+ },
+ obrutet_fjall: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = width;
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'rgba(255, 255, 209, .25)';
+ patternContext.fillRect(0, 0, patternCanvas.width, patternCanvas.height);
+ patternContext.strokeStyle = 'rgba(219, 183, 60, 1)';
+ patternContext.lineWidth = z < 4 ? .5 : z <= 5 ? 1 : 2;
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(0, -patternCanvas.height);
+ patternContext.lineTo(2*patternCanvas.width, patternCanvas.height);
+ patternContext.stroke();
+ patternContext.moveTo(-patternCanvas.width, 0);
+ patternContext.lineTo(patternCanvas.width, 2*patternCanvas.height);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 8,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
+ color: [219, 183, 60, 1],
+ lineDash: [width/4, width/3],
+ }),
+ });
+ }),
+ },
+ skyddade_vattendrag: {
+ legend: { zoomLevel: 0 },
+ style: [8, 16, 32, 32, 64, 64, 128, 128, 128, 256, 256, 256].map(function(width, z) {
return new Style({
- zIndex: 5,
+ zIndex: 8,
fill: new Fill({
- color: [203, 190, 163, Math.max((.3-.5)/8 * z + .5, 0)],
+ color: [102, 157, 240, .25],
}),
stroke: width === 0 ? undefined : new Stroke({
- width: width,
- color: [179, 153, 102, 1],
+ width: z < 2 ? 2 : z < 4 ? 4 : z <= 5 ? 8 : 16,
+ color: [41, 109, 197, 1],
+ lineDash: [width/4, width/3],
}),
});
}),
+ },
},
- /* Documentation at
- * https://www.smhi.se/polopoly_fs/1.34541!/dammprod%202013_3%2C%20beskrivning%2C%20SVAR2012_2.pdf
- * */
- misc_dammar: {
- popoverTitle: 'Damm',
- popover: [
- ['Dammenhetens namn', 'DNamn'],
- ['Dammanläggningens namn', 'Namn'],
- ['Länsnr', 'LST_OBJID', { classes: ['feature-objid'] }],
- ['Status', 'Status', { fn: (v) => v === 1 ? 'Befintlig damm' : v === 2 ? 'Fd. damm' : '' }],
- //['Regleringstyp', 'Regleringstyp'],
- ['Byggår', 'ByggAr'],
- ['Dammhöjd', 'DammHojd', { unit: 'm' }],
- ['Krönlängd', 'KronLangd', { unit: 'm' }],
- ['Fiskväg', 'Fiskvag', { fn: (v) =>
- v === 1 ? 'Bassängtrappa' :
- v === 2 ? 'Denilränna' :
- v === 3 ? 'Slitsränna' :
- v === 4 ? 'Omlöp' :
- v === 5 ? 'Inlöp' :
- v === 6 ? 'Ålledare' :
- v === 7 ? 'Smoltränna' :
- v === 8 ? 'Okänd typ' :
- v === 9 ? 'Ingen' :
- v === 10 ? 'Annan' :
- '' }],
- ['Huvudavrinningsområdesnummer', 'HARO', { classes: ['feature-objid'] } ],
- ['Vattendistrikt', 'Vattendistrikt', { classes: ['feature-objid'] } ],
- ['Verksamhet', 'Verksamhet', { fn: (v) =>
- v === 1 ? 'Kraftproduktion' :
- v === 2 ? 'Industri' :
- v === 3 ? 'Sjöfart' :
- v === 4 ? 'Invallning' :
- v === 5 ? 'Vattenförsörjning' :
- v === 6 ? 'Spegeldamm' :
- v === 7 ? 'Historisk' :
- v === 8 ? 'Övrigt' :
- '' }],
- ['Högsta dämningsgräns', 'DG', { unit: 'm' }],
- ['Lägsta sänkningsgräns', 'SG', { unit: 'm' }],
- ['Magasinsyta', 'MY', { unit: 'km²' }],
- ['Reglerbar volym', 'RV', { unit: 'Mm³' }],
- ['Kommentar', 'Kommentar'],
- ],
- style: [2, 3, 4, 4, 4, 6, 8, 8, 8, 10, 16, 32].map(function(width) {
- return new Style({
- zIndex: 59,
- image: new CircleStyle({
- radius: width,
+ ren: {
+ betesomrade: {
+ legend: { zoomLevel: 0 },
+ style: [1, 1.5, 2, 3, 3.5, 4, 5, 5, 6, 7, 8, 10].map(function(width) {
+ return new Style({
+ zIndex: 4,
fill: new Fill({
- color: 'rgb(219, 30, 42)',
+ /* transparent fill so clicking the inside of the polygon triggers a popover */
+ /* XXX could also use a custom renderer but that doesn't seem to work */
+ color: [0, 0, 0, 0],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [179, 153, 102, 1],
}),
+ });
+ }),
+ },
+ flyttled: {
+ legend: { zoomLevel: 2, type: 'linestring' },
+ style: [.75, 1, 1.5, 2, 3, 4, 5, 5, 6, 7, 8, 10].map(function(width) {
+ return new Style({
+ zIndex: 7,
stroke: new Stroke({
- width: Math.log2(width) * 2/5,
- color: 'rgb(128, 17, 25)',
+ width: 2*width,
+ color: [119, 99, 59, 1],
+ lineDash: [4 * width],
}),
- }),
- });
- }),
+ });
+ }),
+ },
+ riks_ren: {
+ legend: { zoomLevel: 1 },
+ style: [.5, 1, 1.5, 1.5, 2, 2, 2.5, 2.5, 3, 3.5, 4, 5].map(function(width, z) {
+ const patternCanvas = document.createElement('canvas');
+ const patternContext = patternCanvas.getContext('2d');
+ patternCanvas.width = z < 4 ? 4 : z <= 5 ? 8 : Math.pow(2, Math.round(Math.log2(width) + 3));
+ patternCanvas.height = patternCanvas.width;
+ patternContext.fillStyle = 'transparent';
+ patternContext.strokeStyle = 'rgba(179, 153, 102, 1)';
+ patternContext.lineWidth = Math.max(1, width/2);
+ patternContext.beginPath();
+ patternContext.moveTo(0, 0);
+ patternContext.lineTo(patternCanvas.width, 0);
+ patternContext.stroke();
+
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ return new Style({
+ zIndex: 6,
+ fill: new Fill({
+ color: context.createPattern(patternCanvas, 'repeat'),
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [179, 153, 102, 1],
+ }),
+ });
+ }),
+ },
+ omr_riks: {
+ legend: { zoomLevel: 2 },
+ style: [.5, .5, 1, 1, 1, 1.5, 1.5, 1.5, 2, 2, 2, 2].map(function(width, z) {
+ return new Style({
+ zIndex: 5,
+ fill: new Fill({
+ color: [203, 190, 163, Math.max((.3-.5)/8 * z + .5, 0)],
+ }),
+ stroke: width === 0 ? undefined : new Stroke({
+ width: width,
+ color: [179, 153, 102, 1],
+ }),
+ });
+ }),
+ },
},
- misc_gigafactories: {
- popoverTitle: 'Stor industrisatsning',
- popover: [
- ['Namn', 'Name'],
- ['Länk', 'Url', { fn: function(v) {
- const a = document.createElement('a');
- a.href = v;
- a.target = '_blank';
- const i = document.createElement('i');
- i.classList.add('bi', 'bi-box-arrow-up-right');
- a.appendChild(i);
- return a;
- }}],
- ],
- style: [4, 6, 7, 8, 10, 12].map(function(width) {
- return new Style({
- zIndex: 60,
- image: new CircleStyle({
- radius: width,
+ misc: {
+ /* Documentation at
+ * https://www.smhi.se/polopoly_fs/1.34541!/dammprod%202013_3%2C%20beskrivning%2C%20SVAR2012_2.pdf
+ * */
+ dammar: {
+ legend: { zoomLevel: 5, type: 'point' },
+ style: [2, 3, 4, 4, 4, 6, 8, 8, 8, 10, 16, 32].map(function(width) {
+ return new Style({
+ zIndex: 59,
+ image: new CircleStyle({
+ radius: width,
+ fill: new Fill({
+ color: 'rgb(219, 30, 42)',
+ }),
+ stroke: new Stroke({
+ width: Math.log2(width) * 2/5,
+ color: 'rgb(128, 17, 25)',
+ }),
+ }),
+ });
+ }),
+ },
+
+ gigafactories: {
+ legend: { zoomLevel: 1, type: 'point' },
+ style: [4, 6, 7, 8, 10, 12].map(function(width) {
+ return new Style({
+ zIndex: 60,
+ image: new CircleStyle({
+ radius: width,
+ fill: new Fill({
+ color: 'rgb(152, 78, 163)',
+ }),
+ stroke: new Stroke({
+ width: Math.log2(width) * 2/5,
+ color: 'rgb(119, 61, 128)',
+ }),
+ }),
+ });
+ })
+ .concat([1.5, 2, 2, 2, 2, 2].map(function(width) {
+ return new Style({
+ zIndex: 58,
fill: new Fill({
- color: 'rgb(152, 78, 163)',
+ color: 'rgba(152, 78, 163, .4)',
}),
stroke: new Stroke({
- width: Math.log2(width) * 2/5,
+ width: width,
color: 'rgb(119, 61, 128)',
}),
- }),
- });
- })
- .concat([1.5, 2, 2, 2, 2, 2].map(function(width) {
- return new Style({
- zIndex: 58,
- fill: new Fill({
- color: 'rgba(152, 78, 163, .4)',
- }),
- stroke: new Stroke({
- width: width,
- color: 'rgb(119, 61, 128)',
- }),
- });
- })),
+ });
+ })),
+ },
+ },
+
+ kskog: {
+ 1: { style: [ 56, 168, 0, .2] }, /* #1 Sannolikt kontinuitetsskog (preciserad) */
+ 2: { style: [169, 0, 230, .2] }, /* #2 Sannolikt påverkad kontinuitetsskog (preciserad) */
+ 3: { style: [152, 230, 0, .2] }, /* #3 Sannolikt kontinuitetsskog i fjällen (grövre precisering) */
+ 4: { style: [ 76, 115, 0, .2] }, /* #4 Potentiell kontinuitetsskog (2015) */
+ },
+});
+
+/* process URL parameters (other than 'basemap') */
+const STYLES = Object.seal(Object.fromEntries(Object.entries(LAYERS).map(([k,ls]) =>
+ [k, Object.seal(Object.fromEntries(Object.keys(ls).map((l) => [l, null])))])));
+(function() {
+ const view = MAP.getView();
+ const params = new URLSearchParams(window.location.hash.substring(1));
+ const x = parseFloat(params.get('x')),
+ y = parseFloat(params.get('y')),
+ z = parseFloat(params.get('z'));
+ if (!isNaN(x) && !isNaN(y)) {
+ view.setCenter([x, y]);
+ view.setZoom(isNaN(z) ? 1 : z);
+ } else {
+ /* center of the bbox of the Norrbotten and Västerbotten geometries */
+ view.setCenter([694767.48, 7338176.57]);
+ view.setZoom(1);
+ const geolocation = new Geolocation({
+ projection: view.getProjection(),
+ tracking: true,
+ });
+ const evt_key = geolocation.on('change:position', function() {
+ const pos = geolocation.getPosition();
+ if (pos == null) {
+ return;
+ }
+ /* ignore further geolocation position changes */
+ unByKey(evt_key);
+ geolocation.setTracking(false);
+
+ const params2 = new URLSearchParams(window.location.hash.substring(1));
+ /* ignore geolocation result if coordinates have changed meanwhile */
+ if (params2.has('x') || params2.has('y')) {
+ return;
+ }
+ /* ignore geolocation result if not within extent */
+ if (EXTENT[0] > pos[0] || pos[0] > EXTENT[2] || EXTENT[1] > pos[1] || pos[1] > EXTENT[3]) {
+ return;
+ }
+ view.setCenter(pos);
+ params2.set('x', pos[0].toFixed(2).replace(TRAILING_ZEROES, ''));
+ params2.set('y', pos[1].toFixed(2).replace(TRAILING_ZEROES, ''));
+ if (!params2.has('z')) {
+ const accuracy = geolocation.getAccuracy();
+ if (accuracy == null || accuracy < 0) {
+ view.setZoom(Math.max(view.getMinZoom(), 0));
+ } else {
+ /* infer resolution from accuracy, up to zoom level 7 (8px/m) */
+ const [width, height] = MAP.getSize();
+ const res = 8. * accuracy / Math.min(width, height);
+ view.setResolution(Math.max(res, view.getResolutionForZoom(7)));
+ }
+ params2.set('z', view.getZoom().toFixed(3).replace(TRAILING_ZEROES, ''));
+ }
+ location.hash = '#' + params2.toString();
+ });
+ }
+ if (!params.has('layers') || (!params.get('layers').match(/^\s*$/) &&
+ /* compat redirect/layer subst for old non-hierachical names */
+ !params.get('layers').split(' ').some((l) => l.includes('.')))) {
+ params.set('layers', [
+ 'svk.ledningar',
+ 'svk.stolpar',
+ 'svk.stationer',
+ 'svk.transmissionsnatsprojekt',
+ 'misc.gigafactories',
+ 'misc.dammar',
+ 'mrr.appr_ec',
+ 'mrr.appl_ec',
+ 'mrr.appr_ogd',
+ 'mrr.appl_ogd',
+ 'mrr.appr_met',
+ 'mrr.appl_met',
+ 'mrr.appr_dl',
+ 'vbk.area_current',
+ 'vbk.area_notcurrent',
+ ].join(' '));
+ location.hash = '#' + params.toString();
+ }
+
+ /* map each known parameter to a callback processing its value */
+ Object.entries({
+ 'layers': function(value) {
+ const layersParams = value.split(' ');
+ Object.entries(LAYERS).forEach(([k, v]) =>
+ Object.entries(v)
+ .filter(([l]) => layersParams.includes(k + '.' + l))
+ .forEach(([l,x]) => STYLES[k][l] = x.style ?? null));
+ },
+
+ 'age-filter': function(value) {
+ /* eslint-disable-next-line no-useless-escape */
+ const m0 = /^([ +\-]?)([0-9]+)([dwmy])$/.exec(value);
+ if (m0 != null) {
+ /* handling relative parameter values add some complexity, but it's worth doing since
+ * users can bookmark the URL with e.g., age-filter=-1y and visit it later with the
+ * timeframe shifted forward, thus not having to reconfigure the age filter */
+ ageFilterSettings.type = 'relative';
+ ageFilterSettings.operator = (m0[1] === ' ' || m0[1] === '+' || m0[1] === '') ? '>='
+ : m0[1] === '-' ? '<='
+ : null;
+ ageFilterSettings.quantity = parseInt(m0[2], 10);
+ ageFilterSettings.unit = m0[3];
+ ageFilterSettings.setupMinMax();
+ ageFilterSettings._active = true; /* don't call the setter as it's not initialized yet */
+ return;
+ }
+ const m1 = /^([0-9]{8})-([0-9]{8})$/.exec(value); /* YYYYMMDD */
+ if (m1 != null) {
+ const parse_date = (m) => new Date(
+ parseInt(m.slice(0,4), 10),
+ parseInt(m.slice(4,6), 10)-1,
+ parseInt(m.slice(6,8), 10),
+ 12 /* use 12:00:00.0 like for the <input type="date"> */
+ );
+ ageFilterSettings.type = 'interval';
+ ageFilterSettings.from = parse_date(m1[1]);
+ ageFilterSettings.to = parse_date(m1[2]);
+ ageFilterSettings.setupMinMax();
+ ageFilterSettings._active = true; /* don't call the setter as it's not initialized yet */
+ return;
+ }
+ //console.log(`Ignoring invalid value for 'age-filter' parameter: ${value}`);
+ },
+ 'show-unknown-age': function(value) {
+ if (value === '0') {
+ ageFilterSettings.show_unknown = false;
+ } else if (value === '1') {
+ ageFilterSettings.show_unknown = true;
+ }
+ },
+ })
+ .forEach(function([param, cb]) {
+ if (params.has(param)) {
+ cb(params.get(param));
+ }
+ });
+})();
+
+MAP.getView().on('change', function(event) {
+ const view = event.target;
+ disposePopover();
+
+ const coordinates = view.getCenter();
+ const searchParams = new URLSearchParams(location.hash.substring(1));
+ searchParams.set('x', coordinates[0].toFixed(2).replace(TRAILING_ZEROES, ''));
+ searchParams.set('y', coordinates[1].toFixed(2).replace(TRAILING_ZEROES, ''));
+ searchParams.set('z', view.getZoom().toFixed(3).replace(TRAILING_ZEROES, ''));
+ location.hash = '#' + searchParams.toString();
+});
+
+/* add layers to the map */
+const mapLayers = (function() {
+ const tileGrid = createXYZ({
+ extent: EXTENT,
+ tileSize: 1024,
+ maxResolution: 1024, /* = 1048576/1024 */
+ minZoom: 0,
+ maxZoom: 7,
+ });
+ const isVisible = function(k) {
+ const styles = STYLES[k];
+ return Object.keys(LAYERS[k]).some((l) => styles[l] != null);
+ };
+ const canWebGL2 = !!document.createElement('canvas').getContext('webgl2');
+
+ /* Note: layers are added in the order below, so leave SvK and
+ * misc at the end so they show up on top of suface features */
+ const rasterLayers = ['kskog'];
+ const vectorLayers = ['adm', 'nv', 'mrr', 'skydd', 'ren', 'ri', 'avverk', 'vbk', 'svk', 'misc'];
+ const canFilterByAge = ['avverk', 'mrr', 'vbk']; /* layers for which features are dated */
+
+ const ret = {};
+ if (!canWebGL2) {
+ rasterLayers.forEach((k) => ret[k] = null);
+ } else {
+ rasterLayers.forEach(function(k) {
+ const baseurl = new URL('/raster/' + k + '/', window.location.toString()).toString();
+ const source = new GeoTIFF({
+ sources: [{ url: baseurl + encodeURIComponent(k) + '.tiff' }],
+ normalize: false,
+ convertToRGB: false,
+ wrapX: false,
+ interpolate: false,
+ /* use the projection found in the source's metadata */
+ });
+ /* GeoTIFF doesn't allow retrieving the URL later, so we manually store the baseurl instead */
+ source.set('baseurl', baseurl, true);
+ ret[k] = new TileLayerGL({
+ /* Naturvårdsverket has a WMS server we could use instead, but by serving it ourselves
+ * we can filter on he various kskog classes */
+ source: source,
+ visible: false,
+ style: null, /* filled later */
+ });
+ MAP.addLayer(ret[k]);
+ });
}
-};
+ vectorLayers.forEach(function(k) {
+ const canFilterByAge0 = canFilterByAge.includes(k);
+ const styles = STYLES[k];
+ const baseurl = new URL('/tiles/' + k + '/', window.location.toString()).toString();
+ const source = new VectorTile({
+ url: baseurl + '{z}/{x}/{y}.pbf',
+ format: new MVT(),
+ projection: PROJECTION,
+ wrapX: false,
+ transition: 0,
+ tileGrid: tileGrid,
+ });
+ source.set('baseurl', baseurl, true);
+ ret[k] = new VectorTileLayer({
+ source: source,
+ /* XXX switch to 'hybrid' if there are perf issues; but that seems to
+ * put lines above points regardless of their respective z-index */
+ renderMode: 'hybrid',
+ declutter: false,
+ visible: isVisible(k),
+ style: function(feature, resolution) {
+ /* WARN: very hot code path! */
+ const properties = feature.getProperties();
+ if (ageFilterSettings.active) {
+ /* TODO avoid doing this checks for each feature; instead, set up a
+ * different style function if ageFilterSettings.active */
+ const ts = properties.ts;
+ if (ts == null) {
+ if (canFilterByAge0 && !ageFilterSettings.show_unknown) {
+ return;
+ }
+ } else if ((ageFilterSettings._min_ts !== null && ts < ageFilterSettings._min_ts) ||
+ (ageFilterSettings._max_ts !== null && ts > ageFilterSettings._max_ts)) {
+ return;
+ }
+ }
+ const style = styles[properties.layer];
+ if (!Array.isArray(style)) {
+ return style;
+ } else {
+ const maxi = style.length - 1;
+ const z = 10 /* Math.log2(maxResolution) */ - Math.log2(resolution);
+ /* use Math.floor() as VectorTile.js calls getZForResolution(resolution, 1) */
+ const i = z <= 0 ? 0 : z >= maxi ? maxi : Math.floor(z);
+ // console.log(`resolution=${resolution}, z=${z}, i=${i}`);
+ return style[i];
+ }
+ }
+ });
+ ret[k].set('layerGroup', k, true);
+ ret[k].set('canFilterByAge', canFilterByAge0, true);
+ MAP.addLayer(ret[k]);
+ });
+ return ret;
+})();
+
+/* layer hierarchy, for the layer selection, legend and info modal */
const layerHierarchy = [
{
text: 'Transmissionsnät för el',
children: [
{
text: 'Kraftledningar (befintliga)',
- layer: ['svk_ledningar', 'svk_stolpar'],
+ layer: ['svk.stolpar', 'svk.ledningar'],
},
{
text: 'Stationer',
- layer: 'svk_stationer',
+ layer: 'svk.stationer',
},
{
text: 'Transmissionsnätsprojekt ',
- layer: 'svk_transmissionsnatsprojekt',
+ layer: 'svk.transmissionsnatsprojekt',
},
],
},
{
text: 'Stora industrisatsningar',
- layer: 'misc_gigafactories',
+ layer: 'misc.gigafactories',
},
{
text: 'Dammar',
- layer: 'misc_dammar',
+ layer: 'misc.dammar',
},
{
text: 'Mineralrättigheter',
@@ -2911,11 +3065,11 @@ const layerHierarchy = [
children: [
{
text: 'Beviljad',
- layer: 'mrr_appr_ec',
+ layer: 'mrr.appr_ec',
},
{
text: 'Ansökt',
- layer: 'mrr_appl_ec',
+ layer: 'mrr.appl_ec',
},
],
},
@@ -2924,11 +3078,11 @@ const layerHierarchy = [
children: [
{
text: 'Beviljad',
- layer: 'mrr_appr_ogd',
+ layer: 'mrr.appr_ogd',
},
{
text: 'Ansökt',
- layer: 'mrr_appl_ogd',
+ layer: 'mrr.appl_ogd',
},
],
},
@@ -2937,17 +3091,17 @@ const layerHierarchy = [
children: [
{
text: 'Beviljad',
- layer: 'mrr_appr_met',
+ layer: 'mrr.appr_met',
},
{
text: 'Ansökt',
- layer: 'mrr_appl_met',
+ layer: 'mrr.appl_met',
},
],
},
{
text: 'Markanvisningar till koncession',
- layer: 'mrr_appr_dl',
+ layer: 'mrr.appr_dl',
},
],
},
@@ -2955,48 +3109,89 @@ const layerHierarchy = [
text: 'Vindbruk',
children: [
{
- text: 'Projekteringsområden',
+ text: 'Landbaserade projekteringsområden',
children: [
{
text: 'Aktuella',
- layer: 'vbk_area_current',
+ layer: 'vbk.area_current',
},
{
text: 'Ej aktuella',
- layer: 'vbk_area_notcurrent',
+ layer: 'vbk.area_notcurrent',
},
],
},
{
- text: 'Vindkraftverk',
+ text: 'Landbaserade vindkraftverk',
children: [
{
text: 'Uppförda',
- layer: 'vbk_station_completed',
+ layer: 'vbk.turbine_completed',
+ },
+ {
+ text: 'Beviljade',
+ layer: 'vbk.turbine_approved',
+ },
+ {
+ text: 'Avslagna/nekad',
+ layer: 'vbk.turbine_rejected',
},
{
text: 'Handläggs',
- layer: 'vbk_station_processed',
+ layer: 'vbk.turbine_processed',
},
{
- text: 'Beviljade',
- layer: 'vbk_station_approved',
+ text: 'Nedmonterade',
+ layer: 'vbk.turbine_dismounted',
+ },
+ {
+ text: 'Överklagade',
+ layer: 'vbk.turbine_appealed',
},
{
text: 'Inte längre aktuella/återkallade',
- layer: 'vbk_station_revoked',
+ layer: 'vbk.turbine_revoked',
},
+ ],
+ },
+ {
+ text: 'Havsbaserad vindkraft',
+ children: [
{
- text: 'Avslagna/nekad',
- layer: 'vbk_station_rejected',
+ text: 'Uppförd',
+ layer: 'vbk.offshore_completed',
},
{
- text: 'Nedmonterade',
- layer: 'vbk_station_dismounted',
+ text: 'Tillståndsansökan beviljad',
+ layer: 'vbk.offshore_approved',
},
{
- text: 'Överklagade',
- layer: 'vbk_station_appealed',
+ text: 'Ändringsansökan',
+ layer: 'vbk.offshore_amended',
+ },
+ {
+ text: 'Tillståndsansökan avslagen',
+ layer: 'vbk.offshore_rejected',
+ },
+ {
+ text: 'Överklagad',
+ layer: 'vbk.offshore_appealed',
+ },
+ {
+ text: 'Tillståndsansökan inlämnad',
+ layer: 'vbk.offshore_applied',
+ },
+ {
+ text: 'Samråd inför tillståndsansökan',
+ layer: 'vbk.offshore_consultation',
+ },
+ {
+ text: 'Inledande undersökningar',
+ layer: 'vbk.offshore_investigation',
+ },
+ {
+ text: 'Inte längre aktuell/återkallad',
+ layer: 'vbk.offshore_revoked',
},
],
},
@@ -3006,12 +3201,12 @@ const layerHierarchy = [
text: 'Skogsbruk',
children: [
{
- text: 'Uppförda (sedan 2000)',
- layer: 'sks_clearcut_comp',
+ text: 'Utförda avverkningar',
+ layer: 'avverk.utford',
},
{
- text: 'Anmälda',
- layer: 'sks_clearcut_appl',
+ text: 'Avverkningsanmälningar',
+ layer: 'avverk.anmald',
},
]
},
@@ -3019,59 +3214,59 @@ const layerHierarchy = [
text: 'Skyddad natur',
children: [
{
- text: 'Nationella skyddsformer',
+ text: 'Nationella skyddsformer från Naturvårdsregistret',
children: [
{
text: 'Tillträdesförbud',
- layer: 'nvr_tilltradesforbud',
+ layer: 'skydd.tilltradesforbud',
},
{
text: 'Nationalpark',
- layer: 'nvr_nationalpark',
+ layer: 'skydd.nationalpark',
},
{
text: 'Naturreservat',
- layer: 'nvr_naturreservat',
+ layer: 'skydd.naturreservat',
},
{
text: 'Kommunala naturreservat',
- layer: 'nvr_naturreservat_kommunalt',
+ layer: 'skydd.naturreservat_kommunalt',
},
{
text: 'Naturvårdsområden',
- layer: 'nvr_naturvardsomrade',
+ layer: 'skydd.naturvardsomrade',
},
{
text: 'Djur- och växtskyddsområden',
- layer: 'nvr_djur_och_vaxtskyddsomrade',
+ layer: 'skydd.djur_och_vaxtskyddsomrade',
},
{
text: 'Kulturreservat',
- layer: 'nvr_kulturreservat',
+ layer: 'skydd.kulturreservat',
},
{
text: 'Vattenskyddsområden',
- layer: 'nvr_vattenskyddsomrade',
+ layer: 'skydd.vattenskyddsomrade',
},
{
text: 'Landskapsbildsskyddsområden',
- layer: 'nvr_landskapsbildsskyddsomrade',
+ layer: 'skydd.landskapsbildsskyddsomrade',
},
{
text: 'Skogliga biotopskyddsområden',
- layer: 'nvr_skogligt_biotopskyddsomrade',
+ layer: 'skydd.skogligt_biotopskyddsomrade',
},
{
text: 'Övriga biotopskyddsområden',
- layer: 'nvr_ovrigt_biotopskyddsomrade',
+ layer: 'skydd.ovrigt_biotopskyddsomrade',
},
{
text: 'Naturminne',
- layer: [ 'nvr_naturminne_yta', 'nvr_naturminne_punkt' ],
+ layer: [ 'skydd.naturminne_yta', 'skydd.naturminne_punkt' ],
},
{
text: 'Interimistiskt förbud',
- layer: 'nvr_interimistiskt_forbud',
+ layer: 'skydd.interimistiskt_forbud',
},
],
},
@@ -3080,11 +3275,11 @@ const layerHierarchy = [
children: [
{
text: 'Fågeldirektivet (SPA)',
- layer: 'nvr_fageldirektivet',
+ layer: 'skydd.fageldirektivet',
},
{
text: 'Art- och habitatdirektivet (SCI)',
- layer: 'nvr_habitatdirektivet',
+ layer: 'skydd.habitatdirektivet',
},
],
},
@@ -3093,38 +3288,92 @@ const layerHierarchy = [
children: [
{
text: 'Marina skyddade områden (Helcom MPA)',
- layer: 'nvr_helcom',
+ layer: 'skydd.helcom',
},
{
text: 'Ramsar-områden (Våtmarkskonventionen)',
- layer: 'nvr_ramsar',
+ layer: 'skydd.ramsar',
},
{
text: 'Marina skyddade områden (Ospar MPA)',
- layer: 'nvr_ospar',
+ layer: 'skydd.ospar',
},
{
text: 'Världsarv med mycket höga naturvärden (UNESCO)',
- layer: 'nvr_varldsarv',
+ layer: 'skydd.varldsarv',
},
{
text: 'Biosfärsområden (UNESCO)',
- layer: 'nvr_biosfarsomraden',
+ layer: 'skydd.biosfarsomraden',
},
],
},
+ {
+ text: 'Naturvårdsavtal',
+ children: [
+ {
+ text: 'Naturvårdsverket, Länsstyrelserna',
+ layer: 'skydd.naturvardsavtal',
+ },
+ {
+ text: 'Skogsstyrelsen',
+ layer: 'skydd.naturvardsavtal_skogsstyrelsen',
+ }
+ ]
+ },
+ {
+ text: 'Återvätningsavtal',
+ layer: 'skydd.atervatningsavtal',
+ }
]
},
{
- text: 'Naturvårdsavtal',
+ text: 'Skogliga värden',
children: [
{
- text: 'Naturvårdsverket, Länsstyrelserna',
- layer: 'nvr_naturvardsavtal',
+ text: 'Objekt med naturvärden (Skogsstyrelsen)',
+ layer: 'nv.naturvarde_sks',
+ },
+ {
+ text: 'Nyckelbiotoper',
+ layer: 'nv.nyckelbiotop',
+ },
+ {
+ text: 'Nyckelbiotoper storskogsbruket',
+ layer: 'nv.nyckelbiotop_storskogsbruk',
+ },
+ {
+ text: 'Sumpskogar',
+ layer: 'nv.sumpskog',
},
{
- text: 'Skogsstyrelsen',
- layer: 'nvr_naturvardsavtal_skogsstyrelsen',
+ text: 'Pågående naturreservatsbildning',
+ layer: 'nv.pagaende_naturreservatsbildning',
+ },
+ {
+ text: 'Skyddsvärda statliga skogar',
+ layer: 'nv.snus',
+ },
+ {
+ text: 'Sannolikt och potentiell kontinuitetsskog',
+ children: [
+ {
+ text: 'Sannolikt kontinuitetsskog (preciserad)',
+ layer: 'kskog.1',
+ },
+ {
+ text: 'Sannolikt påverkad kontinuitetsskog (preciserad)',
+ layer: 'kskog.2',
+ },
+ {
+ text: 'Sannolikt kontinuitetsskog i fjällen (grövre precisering)',
+ layer: 'kskog.3',
+ },
+ {
+ text: 'Potentiell kontinuitetsskog (2015)',
+ layer: 'kskog.4',
+ },
+ ],
}
]
},
@@ -3133,27 +3382,27 @@ const layerHierarchy = [
children: [
{
text: 'Naturvård',
- layer: 'ri_naturvard',
+ layer: 'ri.naturvard',
},
{
text: 'Friluftsliv',
- layer: 'ri_friluftsliv',
+ layer: 'ri.friluftsliv',
},
{
text: 'Rörligt friluftsliv',
- layer: 'ri_rorligt_friluftsliv',
+ layer: 'ri.rorligt_friluftsliv',
},
{
text: 'Obruten kust',
- layer: 'ri_obruten_kust',
+ layer: 'ri.obruten_kust',
},
{
text: 'Obrutet fjäll',
- layer: 'ri_obrutet_fjall',
+ layer: 'ri.obrutet_fjall',
},
{
text: 'Skyddade vattendrag',
- layer: 'ri_skyddade_vattendrag',
+ layer: 'ri.skyddade_vattendrag',
},
]
},
@@ -3164,112 +3413,172 @@ const layerHierarchy = [
children: [
{
text: 'Betesområden',
- layer: 'ren_betesomraden',
+ layer: 'ren.betesomrade',
},
{
text: 'Flyttled',
- layer: 'ren_flyttled',
+ layer: 'ren.flyttled',
},
{
text: 'Riksintressen',
- layer: 'ren_riks_ren',
+ layer: 'ren.riks_ren',
},
{
text: '(Kärn)områden av riksintresse',
- layer: 'ren_omr_riks',
+ layer: 'ren.omr_riks',
},
]
},
+ {
+ text: 'Administrativa gränser',
+ type: 'switch',
+ collapse_children: true,
+ children: [
+ {
+ text: 'Länsgränser',
+ layer: 'adm.lansyta',
+ },
+ {
+ text: 'Kommungränser',
+ layer: 'adm.kommunyta',
+ },
+ ],
+ },
];
-const styles = (function() {
- const searchParams = new URLSearchParams(location.hash.substring(1));
- const layersParams = searchParams.has('layers') ? searchParams.get('layers').split(' ') : [];
- return Object.keys(layers).reduce(function(result, key) {
- if (layersParams.includes(key)) {
- result[key] = layers[key].style;
+/* legend panel */
+(function() {
+ const modal = document.getElementById('map-legend-panel');
+ modal.classList.add('modal');
+ modal.setAttribute('role', 'dialog');
+ modal.setAttribute('aria-hidden', 'true');
+
+ const content = document.createElement('div');
+ modal.appendChild(content);
+ content.classList.add('modal-content');
+
+ const body = document.createElement('div');
+ content.appendChild(body);
+ body.classList.add('modal-body');
+
+ const createLegend = function(ul, elem, classes) {
+ const li = document.createElement('li');
+ li.classList.add('list-group-item');
+ ul.appendChild(li);
+
+ const t = document.createTextNode(elem.text);
+ if (elem.layer === undefined) {
+ li.appendChild(t);
+ li.classList.add('map-legend-header');
+ } else {
+ li.classList.add('d-flex', 'flex-row');
+ const div = document.createElement('div');
+ div.classList.add('map-legend-symbol');
+ li.appendChild(div);
+ const parent_height = 24; /* see CSS */
+ const [width, height] = [32, 20];
+ const symbols = {
+ polygon: new Polygon([[
+ [0, (parent_height-height)/2],
+ [width, (parent_height-height)/2],
+ [width, (parent_height+height)/2],
+ [0, (parent_height+height)/2],
+ [0, (parent_height-height)/2],
+ ]]),
+ linestring: new LineString([
+ [0, parent_height/2],
+ [width, parent_height/2]
+ ]),
+ point: new Point(
+ [width/2, parent_height/2]
+ ),
+ };
+ let canvas, render;
+ (Array.isArray(elem.layer) ? elem.layer : [elem.layer])
+ .forEach(function(layer) {
+ /* add symbols for each layer */
+ const layerGroup = layer.split('.', 1).pop();
+ const layerName = layer.slice(layerGroup.length + 1);
+ if (mapLayers[layerGroup] == null || LAYERS[layerGroup]?.[layerName]?.style == null) {
+ console.log(`Could not find symbol for layer ${layer}, skipping`);
+ return;
+ }
+ const legend = LAYERS[layerGroup][layerName]?.legend;
+ if (legend === null) {
+ return; /* layer has opted out from legend */
+ }
+ if (canvas == null || !legend?.reuse_canvas) {
+ canvas = document.createElement('canvas');
+ div.appendChild(canvas);
+ render = toContext(canvas.getContext('2d'),
+ { size:[width, parent_height] }
+ );
+ }
+
+ if (mapLayers[layerGroup].getSource() instanceof GeoTIFF) {
+ /* raster source */
+ render.setFillStrokeStyle(new Fill({
+ color: LAYERS[layerGroup][layerName].style,
+ }));
+ return render.drawGeometry(symbols.polygon);
+ }
+ else if (mapLayers[layerGroup].getSource() instanceof VectorTile) {
+ /* vector source */
+ const style = Array.isArray(LAYERS[layerGroup][layerName].style) ?
+ LAYERS[layerGroup][layerName].style[legend?.zoomLevel ?? 5] :
+ LAYERS[layerGroup][layerName].style;
+ const legend_type = legend?.type ?? 'polygon';
+ if (legend_type === 'point' && style.getImage(1) instanceof Icon && style.getImage(1).getSrc()) {
+ /* use a new <img> element since .setStyle() returns the same one and doesn't work in that case */
+ const div2 = document.createElement('div');
+ const img = document.createElement('img');
+ div2.appendChild(img);
+ img.src = style.getImage(1).getSrc();
+ img.height = height;
+ div.replaceChild(div2, canvas);
+ } else {
+ const style2 = style.clone();
+ const stroke = style2.getStroke();
+ if (legend_type === 'polygon' && stroke && stroke.getWidth() > 1) {
+ /* don't stroke too wide to avoid spilling over boxes above/below */
+ stroke.setWidth(1);
+ }
+ render.setStyle(style2);
+ return render.drawGeometry(symbols[legend_type]);
+ }
+ }
+ else {
+ throw new Error(`Cannot show legend for ${layer}`);
+ }
+ });
+ const span = document.createElement('div');
+ span.appendChild(t);
+ li.appendChild(span);
}
- return result;
- }, {});
-})();
-const [vectorLayers, featureOverlayLayer] = (function() {
- const xyz = '/{z}/{x}/{y}.pbf';
- const tileGrid = createXYZ({
- extent: extent,
- tileSize: 1024,
- maxResolution: 1024, /* = 1048576/1024 */
- minZoom: 0,
- maxZoom: 7,
+ elem._legend = li;
+ if (elem.children != null && elem.children.length > 0) {
+ if (classes.length > 0) {
+ li.classList.add(classes[0]);
+ classes = classes.slice(1);
+ }
+ const ul2 = document.createElement('ul');
+ ul2.classList.add('list-group', 'list-group-flush');
+ ul.appendChild(ul2);
+ elem.children.forEach((elem2) => createLegend(ul2, elem2, classes));
+ }
+ };
+
+ const ul = document.createElement('ul');
+ ul.classList.add('list-group', 'list-group-flush');
+ body.appendChild(ul);
+ layerHierarchy.forEach(function(x) {
+ createLegend(ul, x, ['h4', 'h5', 'h6']);
});
- return [
- Object.fromEntries(
- ['mrr', 'nvr', 'ren', 'ri', 'sks', 'svk', 'vbk', 'misc']
- .map(function(k) {
- let visible = false;
- Object.keys(layers).forEach(function(lyr) {
- if (lyr.startsWith(k + '_')) {
- visible ||= styles[lyr] !== undefined;
- }
- });
- const vectorLayer = new VectorTileLayer({
- source: new VectorTile({
- url: '/tiles/' + k + xyz,
- format: new MVT(),
- projection: projection,
- wrapX: false,
- transition: 0,
- tileGrid: tileGrid,
- }),
- /* XXX switch to 'hybrid' if there are perf issues; but that seems to
- * put lines above points regardless of their respective z-index */
- renderMode: 'hybrid',
- declutter: false,
- visible: visible,
- style: function(feature, resolution) {
- const style = styles[k + '_' + feature.getProperties().layer];
- if (!Array.isArray(style)) {
- return style;
- } else {
- const maxi = style.length - 1;
- const z = 10 /* Math.log2(maxResolution) */ - Math.log2(resolution);
- /* use Math.floor() as VectorTile.js calls getZForResolution(resolution, 1) */
- const i = z <= 0 ? 0 : z >= maxi ? maxi : Math.floor(z);
- // console.log(`resolution=${resolution}, z=${z}, i=${i}`);
- return style[i];
- }
- }});
- vectorLayer.set('layerGroup', k, true);
- map.addLayer(vectorLayer);
- return [k, vectorLayer];
- })),
-
- /* We use a vector tile layer for featureOverlayLayer instead of a simple
- * vector layer overlay, since we don't want to clip selected geometries at
- * tile boundaries. It sounds overkill to load an entire tile layer to
- * display a single feature, but this shouldn't cause much overhead in
- * practice (the tiles are most likely cached already). */
- new VectorTileLayer({
- source: new VectorTile({
- urls: [],
- format: new MVT(),
- projection: projection,
- wrapX: false,
- transition: 0,
- tileGrid: tileGrid,
- }),
- zIndex: 65535,
- declutter: false,
- visible: false,
- renderMode: 'vector',
- map: map,
- style: null,
- }),
- ];
})();
-
/* layer selection panel */
+const infoMetadataAccordions = [];
(function() {
const modal = document.getElementById('layer-selection-panel');
modal.classList.add('modal');
@@ -3294,7 +3603,7 @@ const [vectorLayers, featureOverlayLayer] = (function() {
elem._layers = elem.layer === undefined ? []
: Array.isArray(elem.layer) ? elem.layer
: [elem.layer];
- if (elem.children !== undefined && elem.children.length > 0) {
+ if (elem.children != null && elem.children.length > 0) {
collectLayers(elem.children);
elem.children.forEach(function(child) {
child._layers.forEach((l) => elem._layers.push(l));
@@ -3305,55 +3614,82 @@ const [vectorLayers, featureOverlayLayer] = (function() {
const setIndeterminateAndChecked = function(list) {
return list.forEach(function(elem) {
- const layerStyles = elem._layers.map((lyr) => styles[lyr] !== undefined);
+ const layerStyles = elem._layers.map(function(lyr) {
+ const layerGroup = lyr.split('.', 1).pop();
+ const layerName = lyr.slice(layerGroup.length + 1);
+ return STYLES[layerGroup]?.[layerName] != null;
+ });
elem._input.indeterminate = elem._layers.length <= 1 ? false
: layerStyles.slice(1).some((v) => v !== layerStyles[0]);
- if (!elem._input.indeterminate) {
+ if (elem._input.indeterminate) {
+ elem._legend.classList.remove('d-none');
+ } else {
/* keep checked value if indeterminate */
- elem._input.checked = layerStyles.every((v) => v);
+ const checked = layerStyles.every((v) => v);
+ elem._input.checked = checked;
+ if (checked) {
+ elem._legend.classList.remove('d-none');
+ } else {
+ elem._legend.classList.add('d-none');
+ }
}
- if (elem.children !== undefined && elem.children.length > 0) {
+ if (elem.children != null && elem.children.length > 0) {
setIndeterminateAndChecked(elem.children);
}
});
};
const fixLayerVisibility = function() {
- const result = {}
- Object.keys(layers).forEach(function(lyr) {
- const layerGroup = lyr.split('_', 1)[0];
- if (result[layerGroup] === undefined) {
- result[layerGroup] = false;
+ Object.entries(mapLayers).forEach(function([k,lyr]) {
+ /* set palette for raster layers */
+ if (lyr != null && lyr.getSource() instanceof GeoTIFF) {
+ const styles = STYLES[k] ?? {};
+ const nodata = [0, 0, 0, .0];
+ const indices = Object.keys(LAYERS[k]).map((v) => [parseInt(v, 10), v]);
+ const n = Math.max(...indices.map(([i]) => i));
+ const palette = new Array(n+1).fill(nodata);
+ indices.forEach(([i,l]) => palette[i] = styles[l] ?? nodata);
+ /* XXX unfortunately calling .setStyle() makes the layer blink; using style variabse would
+ * be nicer, but `['palette', ['band', 1], [['var', 'val1'], …]]` fails as each palette index
+ * needs to be a color literal, even when `['var', 'val1']` evaluates to such, cf.
+ * https://github.com/openlayers/openlayers/blob/v10.6.0/src/ol/expr/expression.js#L976 */
+ lyr.setStyle({ color: ['palette', ['band', 1], palette] });
}
- result[layerGroup] ||= styles[lyr] !== undefined;
});
- Object.entries(result).forEach(function([lyr, visible]) {
- const v = vectorLayers[lyr];
- if (v !== undefined) {
- //console.log(lyr, visible);
- v.setVisible(visible);
- }
+ Object.entries(LAYERS).forEach(function([k,v]) {
+ const styles = STYLES[k];
+ const visible = Object.keys(v).some((l) => styles[l] != null);
+ // console.log(k, visible);
+ mapLayers[k]?.setVisible(visible);
});
+ const btn = document.getElementById('map-legend-button');
+ if (Object.values(STYLES).some((v) => Object.values(v).some((v2) => v2 != null))) {
+ btn.classList.remove('disabled');
+ } else {
+ btn.classList.add('disabled');
+ }
};
const onClickFunction = function(layerList, event) {
- featureOverlayLayer.setVisible(false);
- featureOverlayLayer.changed();
- const popover = Popover.getInstance(popup);
- if (popover !== null) {
- popover.dispose();
- }
+ disposePopover();
const searchParams = new URLSearchParams(location.hash.substring(1));
let layersParams = searchParams.get('layers') || '';
layersParams = layersParams.match(/^\s*$/) ? [] : layersParams.split(' ');
if (event.target.checked) {
layerList.forEach(function(lyr) {
- styles[lyr] = layers[lyr].style;
- if (!layersParams.includes(lyr)) {
- layersParams.push(lyr);
+ const layerGroup = lyr.split('.', 1).pop();
+ if (mapLayers[layerGroup] != null) {
+ /* keep unexisting layers (eg WebGL layers on a system without WebGL support) unselectable */
+ const layerName = lyr.slice(layerGroup.length + 1);
+ STYLES[layerGroup][layerName] = LAYERS[layerGroup][layerName]?.style ?? null;
+ if (!layersParams.includes(lyr)) {
+ layersParams.push(lyr);
+ }
}
});
} else {
layerList.forEach(function(lyr) {
- delete styles[lyr];
+ const layerGroup = lyr.split('.', 1).pop();
+ const layerName = lyr.slice(layerGroup.length + 1);
+ STYLES[layerGroup][layerName] = null;
});
layersParams = layersParams.filter((lyr) => !layerList.includes(lyr));
}
@@ -3361,9 +3697,9 @@ const [vectorLayers, featureOverlayLayer] = (function() {
fixLayerVisibility();
layerList
- .map((l) => l.split('_', 1)[0])
- .filter((v, i, arr) => arr.indexOf(v) === i)
- .forEach((l) => vectorLayers[l].getSource().changed());
+ .map((l) => l.split('.', 1)[0])
+ .filter((v, i, arr) => mapLayers[v] != null && arr.indexOf(v) === i)
+ .forEach((l) => mapLayers[l].getSource().changed());
searchParams.set('layers', layersParams.join(' '));
location.hash = '#' + searchParams.toString();
@@ -3372,7 +3708,7 @@ const [vectorLayers, featureOverlayLayer] = (function() {
let layerId = 0;
const addAccordionGroup = function(parentNode, children) {
const ul = document.createElement('ul');
- parentNode.appendChild(ul);
+ parentNode?.appendChild?.(ul);
ul.classList.add('list-group', 'list-group-flush');
children.forEach(function(child) {
@@ -3398,7 +3734,7 @@ const [vectorLayers, featureOverlayLayer] = (function() {
const textNode = document.createTextNode(child.text);
label.appendChild(textNode);
- if (child.children !== undefined && child.children.length > 0) {
+ if (child.children != null && child.children.length > 0) {
addAccordionGroup(li, child.children);
}
@@ -3453,8 +3789,18 @@ const [vectorLayers, featureOverlayLayer] = (function() {
const text0 = document.createTextNode(x.text);
label0.appendChild(text0);
- if (x.children === undefined || x.children.length === 0) {
- item.replaceChild(span0, header);
+ if (x.children == null || x.children.length === 0 || x.collapse_children) {
+ span0.removeAttribute('data-bs-toggle');
+ span0.removeAttribute('data-bs-target');
+ item.replaceChildren(span0);
+ if (x.type === 'switch') {
+ span0.classList.add('form-switch');
+ input0.setAttribute('role', 'switch');
+ }
+ if (x.children != null && x.children.length > 0) {
+ /* create inputs for the hash param logic but don't add them to the panel */
+ addAccordionGroup(null, x.children);
+ }
} else {
const body = document.createElement('div');
collapse.appendChild(body);
@@ -3492,44 +3838,1411 @@ const [vectorLayers, featureOverlayLayer] = (function() {
label.setAttribute('for', input.id);
label.innerHTML = 'Nedtonad bakgrund karta';
- input.checked = baseMapLayer === 'topowebb_nedtonad';
+ input.checked = BASEMAP.layer === 'topowebb_nedtonad';
input.onchange = function(event) {
- baseMapLayer = event.target.checked ? 'topowebb_nedtonad' : 'topowebb';
- baseMapSource.setUrl(`https://minkarta.lantmateriet.se/map/topowebbcache?LAYER=${encodeURIComponent(baseMapLayer)}`);
-
- const searchParams = new URLSearchParams(location.hash.substring(1));
- searchParams.set('basemap', baseMapLayer);
- location.hash = '#' + searchParams.toString();
+ BASEMAP.layer = event.target.checked ? 'topowebb_nedtonad' : 'topowebb';
};
})();
+
+ (function() {
+ const accordion = document.getElementById('info-accordion');
+ layerHierarchy.forEach(function(x, idx) {
+ const item = document.createElement('div');
+ accordion.appendChild(item);
+ item.classList.add('accordion-item');
+
+ const header = document.createElement('div');
+ item.appendChild(header);
+ header.classList.add('accordion-header');
+
+ const btn = document.createElement('button');
+ header.appendChild(btn);
+
+ const collapse = document.createElement('div');
+ item.appendChild(collapse);
+ collapse.id = 'info-accordion-collapse-' + idx;
+ collapse.classList.add('accordion-collapse', 'collapse');
+
+ btn.type = 'button';
+ btn.setAttribute('data-bs-toggle', 'collapse');
+ btn.setAttribute('data-bs-target', '#' + collapse.id);
+ btn.setAttribute('aria-expanded', 'false');
+ btn.setAttribute('aria-controls', collapse.id);
+ btn.classList.add('accordion-button', 'collapsed');
+
+ const t = document.createTextNode(x.text);
+ btn.appendChild(t);
+
+ const body = document.createElement('div');
+ body.classList.add('accordion-body');
+ collapse.appendChild(body);
+
+ const ul = document.createElement('ul');
+ ul.classList.add('list-group', 'list-group-flush');
+ body.appendChild(ul);
+
+ infoMetadataAccordions.push({
+ element: ul,
+ items: x._layers.map(function(k) {
+ const groupname = k.split('.', 1).pop();
+ return [ groupname, k.slice(groupname.length + 1) ];
+ }),
+ });
+ });
+ })();
})();
-/* legend panel */
+/* measurement panel */
(function() {
- const modal = document.getElementById('map-legend-panel');
- modal.classList.add('modal');
- modal.setAttribute('role', 'dialog');
- modal.setAttribute('aria-hidden', 'true');
+ const value = document.createTextNode(''),
+ unit = document.createTextNode('');
+ const reset = function() {
+ source.clear(true);
+ const f = { LineString: formatLength, Polygon: formatArea }[getMeasureMode()];
+ if (f == null) {
+ value.nodeValue = unit.nodeValue = '';
+ } else {
+ f(0);
+ }
+ };
- const content = document.createElement('div');
- modal.appendChild(content);
- content.classList.add('modal-content');
+ const source = new VectorSource({
+ wrapX: false,
+ });
- const body = document.createElement('div');
- content.appendChild(body);
- body.classList.add('modal-body');
- body.innerHTML = 'legend TODO';
+ let draw;
+ const buttons = Object.fromEntries([
+ {
+ id: 'cancel',
+ bi: 'trash',
+ title: 'Avbryt mätningen',
+ onclick: function() {
+ reset();
+ Object.values(buttons).forEach((btn) => btn.disabled = true);
+ draw.abortDrawing();
+ },
+ },
+ {
+ id: 'undo',
+ bi: 'arrow-counterclockwise',
+ title: 'Ta bort sista punkten',
+ onclick: function() {
+ draw.removeLastPoint();
+ const n = { LineString: 2, Polygon: 3 }[getMeasureMode()] ?? Infinity;
+ draw.getOverlay().getSource().forEachFeature(function(feature) {
+ const geom = feature.getGeometry();
+ if (geom.getType() === 'LineString') {
+ /* disable OK button if not enough points have been drawn (excluding cursor) */
+ buttons.ok.disabled = geom.getCoordinates().length - 1 < n;
+ return true; /* stop iterating */
+ }
+ });
+ }
+ },
+ {
+ id: 'ok',
+ bi: 'check-lg',
+ title: 'Slutför mätningen',
+ onclick: () => draw.finishDrawing(),
+ },
+ ].map(function(x, idx, arr) {
+ const btn = document.createElement('button');
+ btn.classList.add('btn', 'btn-outline-' + (idx < arr.length-1 ? 'secondary' : 'primary'));
+ btn.setAttribute('type', 'button');
+ btn.title = x.title;
+ btn.setAttribute('aria-label', btn.title);
+
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-' + x.bi);
+ btn.appendChild(i);
+
+ btn.onclick = x.onclick;
+ return [x.id, btn];
+ }));
+
+ const formatLength = (function() {
+ const formatters = [
+ { maximumFractionDigits: 0 },
+ { maximumFractionDigits: 1 },
+ { maximumFractionDigits: 2 },
+ ]
+ .map((fmt) => new Intl.NumberFormat(LOCALE, {
+ ...fmt,
+ minimumFractionDigits: fmt.maximumFractionDigits ?? 0,
+ }));
+ return function(v) {
+ if (v <= 100) { /* ≤ 100 m */
+ unit.nodeValue = 'm';
+ value.nodeValue = formatters[1].format(v);
+ } else if (v <= 5_000) { /* ≤ 5 km */
+ unit.nodeValue = 'm';
+ value.nodeValue = formatters[0].format(v);
+ } else {
+ unit.nodeValue = 'km';
+ value.nodeValue = formatters[2].format(v/1000);
+ }
+ };
+ })();
+ const formatArea = (function() {
+ const formatters = [
+ { maximumFractionDigits: 1 },
+ { maximumFractionDigits: 2 },
+ ]
+ /* XXX would be nice to use Intl.NumberFormat()'s unit support, but m² and km² are not
+ * supported currently, see https://github.com/tc39/ecma402/issues/767 */
+ .map((fmt) => new Intl.NumberFormat(LOCALE, {
+ ...fmt,
+ minimumFractionDigits: fmt.maximumFractionDigits ?? 0,
+ }));
+ return function(v) {
+ if (v < 10_000) { /* < 1 ha */
+ unit.nodeValue = 'm²';
+ value.nodeValue = formatters[0].format(v);
+ } else if (v < 100_000_000) { /* < 10000 ha (100 km²) */
+ unit.nodeValue = 'ha';
+ value.nodeValue = formatters[1].format(v/10_000);
+ } else {
+ unit.nodeValue = 'km²';
+ v /= 1_000_000;
+ const i = v < 1_000_000 ? 1 : 0; /* ≥10⁶ km² overflows the box with 2 decimals */
+ value.nodeValue = formatters[i].format(v);
+ }
+ };
+ })();
+
+ const setup = (function() {
+ const styles = {
+ Point: new Style({
+ image: new CircleStyle({
+ radius: 6,
+ fill: new Fill({
+ color: [0, 183, 255, 1],
+ }),
+ stroke: new Stroke({
+ color: [255, 255, 255, 1],
+ width: .5,
+ }),
+ }),
+ }),
+ LineString: [
+ new Style({
+ stroke: new Stroke({
+ color: [255, 255, 255, 1],
+ width: 4,
+ }),
+ }),
+ new Style({
+ stroke: new Stroke({
+ color: [0, 183, 255, 1],
+ width: 3,
+ lineDash: [10, 10],
+ }),
+ }),
+ ],
+ Polygon: new Style({
+ fill: new Fill({
+ color: [255, 255, 255, .5],
+ }),
+ }),
+ };
+ const layer = new VectorLayer({
+ source: source,
+ visible: false,
+ style: [
+ new Style({
+ fill: styles.Polygon.getFill(),
+ stroke: styles.LineString[0].getStroke(),
+ }),
+ (function() {
+ const s = styles.LineString[1].clone();
+ s?.getStroke?.()?.setLineDash?.(null);
+ return s;
+ })(),
+ ],
+ map: MAP,
+ });
+
+ return function(geom_type) {
+ if (draw != null) {
+ draw.abortDrawing();
+ MAP.removeInteraction(draw);
+ }
+ reset(); /* remove features when toggling between geom types */
+ Object.values(buttons).forEach((btn) => btn.disabled = true);
+ if (geom_type == null) {
+ layer.setVisible(false);
+ return;
+ }
+ draw = new Draw({
+ source: source,
+ type: geom_type,
+ style: function(feature) {
+ return styles[ feature.getGeometry().getType() ];
+ },
+ });
+ MAP.addInteraction(draw);
+ layer.setVisible(true);
+
+ let listener;
+ draw.on('drawstart', function(event) {
+ reset();
+ buttons.undo.disabled = buttons.cancel.disabled = false;
+ const geom = event.feature.getGeometry();
+ const [isComplete, measure] = {
+ LineString: [
+ /* 2 points drawn + cursor */
+ (g) => g.getCoordinates().length >= 3,
+ (g) => formatLength(g.getLength()),
+ ],
+ Polygon: [
+ /* 3 points drawn + cursor + 1st point */
+ (g) => g.getCoordinates()[0].length >= 5,
+ (g) => formatArea(g.getArea()),
+ ],
+ }[geom.getType()];
+ const btnOK = buttons.ok;
+ listener = geom.on('change', function(event) {
+ if (btnOK.disabled && isComplete(geom)) {
+ btnOK.disabled = false;
+ }
+ measure(event.target);
+ });
+ });
+ draw.on('drawend', function() {
+ unByKey(listener);
+ buttons.ok.disabled = buttons.undo.disabled = true;
+ });
+ draw.on('drawabort', function() {
+ unByKey(listener);
+ reset();
+ Object.values(buttons).forEach((btn) => btn.disabled = true);
+ });
+ };
+ })();
+
+ const [body, btn_close] = (function() {
+ const modal = document.getElementById('measure-panel');
+ modal.classList.add('modal');
+ modal.setAttribute('role', 'dialog');
+ modal.setAttribute('aria-hidden', 'true');
+
+ const content = document.createElement('div');
+ modal.appendChild(content);
+ content.classList.add('modal-content');
+
+ const header = document.createElement('div');
+ content.appendChild(header);
+ header.classList.add('modal-header');
+
+ const title = document.createElement('div');
+ title.classList.add('h5');
+ title.innerHTML = 'Mät i kartan';
+ header.appendChild(title);
+
+ const btn_close = document.createElement('button');
+ btn_close.classList.add('btn-close');
+ btn_close.type = 'button';
+ btn_close.title = 'Stäng';
+ btn_close.setAttribute('aria-label', btn_close.title);
+ header.appendChild(btn_close);
+
+ const body = document.createElement('div');
+ content.appendChild(body);
+ body.classList.add('modal-body');
+ return [body, btn_close];
+ })();
+
+ const getMeasureMode = (function() {
+ const btn_group = document.createElement('div');
+ btn_group.classList.add('btn-group');
+ btn_group.setAttribute('role', 'group');
+ btn_group.setAttribute('aria-label', 'Välj geometrityp');
+ body.appendChild(btn_group);
+
+ const radios = [
+ { id: 'LineString', text: 'Distans' },
+ { id: 'Polygon', text: 'Yta' },
+ ].map(function(x, idx) {
+ const radio = document.createElement('input');
+ radio.classList.add('btn-check');
+ radio.type = 'radio';
+ radio.checked = idx == 0;
+ radio.name = 'measure-geomtype';
+ radio.id = 'measure-geomtype-' + x.id;
+ radio.setAttribute('aria-expanded', 'false');
+ radio.setAttribute('autocomplete', 'off');
+ btn_group.appendChild(radio);
+
+ const lbl = document.createElement('label');
+ lbl.classList.add('btn', 'btn-lg', 'btn-outline-dark');
+ lbl.setAttribute('for', radio.id);
+ lbl.appendChild(document.createTextNode(x.text));
+ btn_group.appendChild(lbl);
+
+ radio.onclick = function() {
+ setup(x.id);
+ };
+ return [x.id, radio];
+ });
+
+ return () => radios.filter( (x) => x[1].checked )?.[0]?.[0];
+ })();
+
+ (function() {
+ const div = document.createElement('div');
+ div.classList.add('measure-value');
+ body.appendChild(div);
+ const span0 = document.createElement('span');
+ span0.appendChild(value);
+ div.appendChild(span0);
+ const span1 = document.createElement('span');
+ span1.classList.add('measure-unit');
+ span1.appendChild(unit);
+ div.appendChild(span1);
+ })();
+
+ (function() {
+ const btn_group = document.createElement('div');
+ btn_group.classList.add('btn-group');
+ btn_group.setAttribute('role', 'group');
+ body.appendChild(btn_group);
+
+ btn_group.appendChild(buttons.cancel);
+ btn_group.appendChild(buttons.undo);
+ btn_group.appendChild(buttons.ok);
+ })();
+
+ const button_menu = document.getElementById('measure-button')
+ .getElementsByTagName('button')[0];
+ button_menu.addEventListener('click', function(event) {
+ if (event.currentTarget.getAttribute('aria-expanded') === 'true') {
+ disposePopover();
+ setup(getMeasureMode());
+ } else {
+ setup(null);
+ /* XXX workaround for https://github.com/twbs/bootstrap/issues/41005#issuecomment-2585390544 */
+ const activeElement = document.activeElement;
+ if (activeElement.isEqualNode(btn_close)) {
+ activeElement.blur();
+ }
+ }
+ });
+ btn_close.onclick = () => button_menu.click();
})();
/* popup and feature overlays */
-(function() {
+const disposePopover = (function() {
+ /* return an <a> tag with the given URL and optional text */
+ const reURL = new RegExp('^https?://', 'i');
+ const formatLink = function(url, text) {
+ if (url == null || typeof url !== 'string' || !reURL.test(url)) {
+ return url;
+ }
+ const a = document.createElement('a');
+ a.href = url;
+ a.target = '_blank';
+ if (text != null && text !== '') {
+ const t = document.createTextNode(text + ' ');
+ a.appendChild(t);
+ }
+ const i = document.createElement('i');
+ i.classList.add('bi', 'bi-box-arrow-up-right');
+ a.appendChild(i);
+ return a;
+ };
+
+ /* test a condition on the field maps */
+ const condField = function(cond, k) {
+ if (Array.isArray(cond)) {
+ return cond.includes(k);
+ }
+ if (cond instanceof RegExp) {
+ return cond.test(k);
+ }
+ return cond(k);
+ };
+ /* filter fields by condition */
+ const filterFields = function(k, fields) {
+ return fields.map(function(v) {
+ if (v.cond == null || condField(v.cond, k)) {
+ return v;
+ }
+ }).filter((f) => f != null);
+ };
+ /* filter fields using a pre-built map */
+ const mapFields = function(k, fieldMap, fields) {
+ if (fields === undefined) {
+ return fieldMap.map((v) => k[v]);
+ }
+ return fields.map(function(v) {
+ if (!Array.isArray(v)) {
+ return fieldMap[v];
+ } else if (condField(v[1], k)) {
+ return fieldMap[v[0]];
+ }
+ }).filter((f) => f !== undefined);
+ };
+ /* pre-build the field map so we don't need to duplicate objects accross layers */
+ const mkFieldMap = function(fieldMap) {
+ return Object.fromEntries(Object.entries(fieldMap).map(function([k, o]) {
+ if (typeof o === 'string') {
+ return [k, Object.seal({key: k, desc: o})];
+ } else {
+ return [k, Object.seal(Object.assign(o, {key: k}))];
+ }
+ }));
+ };
+
+ const layers = {
+ svk: {
+ ledningar: {
+ title: 'Kraftledning (befintlig)',
+ fields: [
+ { key: 'Placement', desc: 'Förläggning' },
+ { key: 'Voltage', desc: 'Spänning', unit: 'kV' },
+ { key: 'geom_length', desc: 'Ledlängd', fn: 'length' },
+ ],
+ },
+ transmissionsnatsprojekt: {
+ title: 'Transmissionsnätsprojekt',
+ fields: [
+ { key: 'Name', desc: 'Projektnamn' },
+ { key: 'Voltage', desc: 'Spänning', unit: 'kV' },
+ { key: 'Url', desc: 'Länk', fn: formatLink },
+ ],
+ },
+ },
+
+ misc: {
+ gigafactories: {
+ title: 'Stor industrisatsning',
+ fields: [
+ { key: 'Name', desc: 'Namn' },
+ { key: 'Url', desc: 'Länk', fn: formatLink },
+ ],
+ },
+ dammar: {
+ /* Documentation at
+ * https://www.smhi.se/polopoly_fs/1.34541!/dammprod%202013_3%2C%20beskrivning%2C%20SVAR2012_2.pdf */
+ title: 'Damm',
+ fields: [
+ { key: 'DNamn', desc: 'Dammenhetens namn' },
+ { key: 'Namn', desc: 'Dammanläggningens namn' },
+ { key: 'LST_OBJID', desc: 'Länsnr', classes: ['feature-objid'] },
+ { key: 'Status', desc: 'Status', fn: (v) => v === 1 ? 'Befintlig damm' : v === 2 ? 'Fd. damm' : '' },
+ //{ key: 'Regleringstyp', desc: 'Regleringstyp' },
+ { key: 'ByggAr', desc: 'Byggår' },
+ { key: 'DammHojd', desc: 'Dammhöjd', unit: 'm' },
+ { key: 'KronLangd', desc: 'Krönlängd', unit: 'm' },
+ { key: 'Fiskvag', desc: 'Fiskväg', fn: (v) =>
+ v === 1 ? 'Bassängtrappa' :
+ v === 2 ? 'Denilränna' :
+ v === 3 ? 'Slitsränna' :
+ v === 4 ? 'Omlöp' :
+ v === 5 ? 'Inlöp' :
+ v === 6 ? 'Ålledare' :
+ v === 7 ? 'Smoltränna' :
+ v === 8 ? 'Okänd typ' :
+ v === 9 ? 'Ingen' :
+ v === 10 ? 'Annan' :
+ null },
+ { key: 'HARO', desc: 'Huvudavrinningsområdesnummer', classes: ['feature-objid'] },
+ { key: 'Vattendistrikt', desc: 'Vattendistrikt', classes: ['feature-objid'] },
+ { key: 'Verksamhet', desc: 'Verksamhet', fn: (v) =>
+ v === 1 ? 'Kraftproduktion' :
+ v === 2 ? 'Industri' :
+ v === 3 ? 'Sjöfart' :
+ v === 4 ? 'Invallning' :
+ v === 5 ? 'Vattenförsörjning' :
+ v === 6 ? 'Spegeldamm' :
+ v === 7 ? 'Historisk' :
+ v === 8 ? 'Övrigt' :
+ null },
+ { key: 'DG', desc: 'Högsta dämningsgräns', unit: 'm' },
+ { key: 'SG', desc: 'Lägsta sänkningsgräns', unit: 'm' },
+ { key: 'MY', desc: 'Magasinsyta', unit: 'km²' },
+ { key: 'RV', desc: 'Reglerbar volym', unit: 'Mm³' },
+ { key: 'Kommentar', desc: 'Kommentar' },
+ ],
+ },
+ },
+ };
+
+ layers.mrr = {};
+ (function() {
+ const fields = [
+ { key: 'name', desc: 'Namn' },
+ { key: 'mineral', desc: 'Koncessionsmineral', cond: (i) => i < 6 },
+ { key: 'owners', desc: 'Ägare', cond: [0,2,4] },
+ { key: 'owners', desc: 'Sökande', cond: [1,3,5] },
+ { key: 'conc_name', desc: 'Tillhörande bearbetnings\u00ADkoncession(er)', cond: [6] },
+ { key: 'licenceid', desc: 'Tillståndsid', classes: ['feature-attr-mrr-license-id'], cond: [0,2,4,6] },
+ { key: 'geom_area', desc: 'Areal', fn: 'area' },
+ { key: 'validfrom', desc: 'Giltig från', cond: [0,2,4] },
+ { key: 'validto', desc: 'Giltig till', cond: [0,2,4] },
+ { key: 'diarynr', desc: 'Diarienummer', classes: ['feature-attr-dnr'] },
+ { key: 'appl_date', desc: 'Ansökningsdatum' },
+ { key: 'dec_date', desc: 'Beslutsdatum', cond: [0,2,4,6] },
+ ];
+ Object.entries({
+ ec: 'Bearbetningskoncession',
+ met: 'Undersökningstillstånd, metaller och industrimineral',
+ ogd: 'Undersökningstillstånd, olja, gas och diamant',
+ })
+ .flatMap(([k, title]) => [
+ /* don't use Object.entries() to guaranty ordering */
+ ['appr', 'beviljad'], /* even index */
+ ['appl', 'ansökt'], /* odd index */
+ ].map(([a,b]) => [a + '_' + k, title + ' \u2013 ' + b]))
+ .concat([['appr_dl', 'Markanvisning till koncession']]) /* index #6 */
+ .forEach(([k, title], idx) => layers.mrr[k] = { title, fields: filterFields(idx, fields) });
+ })();
+
+ layers.vbk = {};
+ (function() {
+ const fieldMap = mkFieldMap({
+ Projektnamn: 'Projektnamn',
+ OmrID: { desc: 'Områdes-ID', classes: ['feature-objid'] },
+ AntalVerk: 'Aktuella verk',
+ AntalEjXY: 'Antal ej koordinatsatta verk',
+ Projektstatus: 'Projektstatus',
+ Diarienummer: 'Diarienummer',
+ geom_area: { desc: 'Areal', fn: 'area' },
+ Calprod: { desc: 'Beräknad årsproduktion', unit: 'GWh' },
+ PlaneradByggstart: 'Planerad byggstart',
+ PlaneratDrift: 'Planerat drifttagande',
+ AndringsansokanPagar: 'Ändringsansökan pågår',
+ UnderByggnation: 'Under byggnation',
+ Organisationsnamn: 'Verksamhetsutövare',
+ Organisationsnummer: { desc: 'Organisationsnummer', classes: ['feature-orgnr'] },
+ SamradsunderlagInlamnat: 'Samrådsunderlag inlämnat',
+ AnsokanInlamnat: 'Tillståndsansökan inlämnad',
+ AnsokanAterkallad: 'Tillståndsansökan återkallad',
+ AnsokanBeviljad: 'Tillståndsansökan beviljad',
+ AnsokanAvslagen: 'Tillståndsansökan avslagen',
+ AnsokanOverklagad: 'Överklagad',
+ Natura2000_Ansokan: 'Natura2000 ansökan',
+ Natura2000_Beslutdatum: 'Natura2000 beslutsdatum',
+ Uppfort: 'Parken uppförd',
+ PlaneratAntalVerkMin: 'Planerat antal verk (min)',
+ PlaneratAntalVerkMax: 'Planerat antal verk (max)',
+ PlaneradHojdMin: { desc: 'Panerad totalhöjd (min)', unit: 'm' },
+ PlaneradHojdMax: { desc: 'Panerad totalhöjd (max)', unit: 'm' },
+ PlaneradProduktionMin: { desc: 'Planerad årsproduktion (min)', unit: 'GWh' },
+ PlaneradProduktionMax: { desc: 'Planerad årsproduktion (max)', unit: 'GWh' },
+ BeviljatAntalVerk: 'Beviljat antal verk',
+ UppfortAntalVerk: 'Uppfört antal verk',
+ BeviljadMaxhojd: { desc: 'Beviljad maxhöjd', unit: 'm' },
+ InstalleradEffekt: { desc: 'Installerad effekt', unit: 'MW' },
+ ElNamn: 'Elområde',
+ SenasteUppdaterat: 'Senast uppdaterat',
+ });
+
+ Object.entries({
+ current: null,
+ notcurrent: ' \u2013 ej aktuell',
+ })
+ .forEach(([k, title]) => layers.vbk['area_' + k] = {
+ title: 'Landbaserad projekteringsområde för vindkraft' + (title ?? ''),
+ fields: mapFields(k, fieldMap, [
+ 'Projektnamn',
+ 'OmrID',
+ 'AntalVerk',
+ 'AntalEjXY',
+ 'geom_area',
+ 'Calprod',
+ 'PlaneradByggstart',
+ 'PlaneratDrift',
+ 'AndringsansokanPagar',
+ ['UnderByggnation', ['current']],
+ 'Organisationsnamn',
+ 'Organisationsnummer',
+ 'ElNamn',
+ 'SenasteUppdaterat',
+ ]),
+ });
+
+ [
+ ['completed', /* 0 */ 'uppförd'],
+ ['approved', /* 1 */ 'tillståndsansökan beviljad'],
+ ['amended', /* 2 */ 'ändringsansökan'],
+ ['rejected', /* 3 */ 'tillståndsansökan avslagen'],
+ ['appealed', /* 4 */ 'överklagad'],
+ ['applied', /* 5 */ 'tillståndsansökan inlämnad'],
+ ['consultation', /* 6 */ 'samråd inför tillståndsansökan'],
+ ['investigation', /* 7 */ 'inledande undersökningar'],
+ ['revoked', /* 8 */ 'inte aktuell eller återkallad'],
+ ]
+ .forEach(([k, title], idx) => layers.vbk['offshore_' + k] = {
+ title: 'Havsbaserad vindkraft \u2013 ' + title,
+ fields: mapFields(idx, fieldMap, [
+ 'Projektnamn',
+ 'OmrID',
+ 'Organisationsnamn',
+ 'Organisationsnummer',
+ 'Projektstatus',
+ 'Diarienummer',
+ ['AndringsansokanPagar', [1,2,4]],
+ 'geom_area',
+ ['SamradsunderlagInlamnat', (i) => i <= 6 || i === 8],
+ ['AnsokanInlamnat', (i) => i <= 5 || i === 8],
+ ['AnsokanAterkallad', [8]],
+ ['AnsokanBeviljad', [0,1,4,8]],
+ ['AnsokanAvslagen', [3,8]],
+ ['AnsokanOverklagad', [0,1,3,4,8]],
+ ['Natura2000_Ansokan', (i) => i !== 2],
+ ['Natura2000_Beslutdatum', (i) => i !== 2],
+ ['UnderByggnation', [1]],
+ ['PlaneratAntalVerkMin', (i) => i > 0],
+ ['PlaneratAntalVerkMax', (i) => i > 0],
+ ['PlaneradHojdMin', (i) => i > 0],
+ ['PlaneradHojdMax', (i) => i > 0],
+ ['PlaneradProduktionMin', (i) => i > 0],
+ ['PlaneradProduktionMax', (i) => i > 0],
+ ['PlaneradByggstart', (i) => i > 0],
+ ['Uppfort', [0,8]],
+ ['PlaneratDrift', (i) => i > 0],
+ ['BeviljatAntalVerk', [0,1,4,8]],
+ ['UppfortAntalVerk', [0,8]],
+ ['BeviljadMaxhojd', [0,1,4,8]],
+ ['InstalleradEffekt', [0]],
+ ['Calprod', [0]],
+ 'ElNamn',
+ 'SenasteUppdaterat',
+ ]),
+ });
+
+ Object.assign(fieldMap, mkFieldMap({
+ VerkID: { desc: 'Verk-ID', classes: ['feature-objid'] },
+ Status: 'Status',
+ Handlingstyp: 'Handlingstyp',
+ MB_Tillstand: 'Miljöbalken tillstånd tidsbegränsning',
+ Uppfort: 'Uppförandedatum',/* override previous def */
+ Totalhojd: { desc: 'Totalhöjd', unit: 'm' },
+ Navhojd: { desc: 'Navhöjd', unit: 'm' },
+ Rotordiameter: { desc: 'Rotordiameter', unit: 'm' },
+ Maxeffekt: { desc: 'Maxeffekt', unit: 'MW' },
+ Fabrikat: 'Fabrikat',
+ Modell: 'Modell',
+ Placering: 'Placering',
+ }));
+
+ [
+ ['completed', /* 0 */ 'uppfört'],
+ ['approved', /* 1 */ 'beviljat'],
+ ['rejected', /* 2 */ 'avslagit/nekat'],
+ ['processed', /* 3 */ 'handlagt'],
+ ['dismounted', /* 4 */ 'nedmonterat'],
+ ['appealed', /* 5 */ 'överklagat'],
+ ['revoked', /* 6 */ 'inte aktuell eller återkallad'],
+ ]
+ .forEach(([k, title], idx) => layers.vbk['turbine_' + k] = {
+ title: 'Landbaserad vindkraftverk \u2013 ' + title,
+ fields: mapFields(idx, fieldMap, [
+ 'VerkID',
+ 'OmrID',
+ 'Projektnamn',
+ 'Status',
+ 'Handlingstyp',
+ ['Uppfort', [0,4,6]],
+ 'MB_Tillstand',
+ 'Totalhojd',
+ 'Navhojd',
+ 'Rotordiameter',
+ 'Maxeffekt',
+ 'Calprod',
+ 'Fabrikat',
+ 'Modell',
+ 'Organisationsnamn',
+ 'Organisationsnummer',
+ 'Placering',
+ 'ElNamn',
+ 'SenasteUppdaterat',
+ ]),
+ });
+ })();
+
+ layers.avverk = {};
+ (function() {
+ const zeroIsNull = (v) => v > 0 ? v : null;
+ const fieldMap = mkFieldMap({
+ /* Documentation at
+ * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/utforda-avverkningar---produktbeskrivning.pdf
+ * and
+ * https://www.skogsstyrelsen.se/globalassets/sjalvservice/karttjanster/geodatatjanster/produktbeskrivningar/yttre-granser-for-avverkningsanmalda-omraden---produktbeskrivning.pdf
+ */
+ Beteckn: { desc: 'Ärendebeteckning', classes: ['feature-objid'] },
+ ArendeAr: 'Registeringsår',
+ Inkomdatum: 'Inkom datum',
+ Skogstyp: 'Skogstyp',
+ Avvdatum: 'Datum för avverkning',
+ KallaDatum: 'Ursprung för datum för avverkning',
+ AnmaldHa: { desc: 'Areal anmält', unit: 'ha' },
+ NatforHa: { desc: 'Areal naturlig föryngring', unit: 'ha', fn: zeroIsNull },
+ SkogsodlHa: { desc: 'Areal plantering', unit: 'ha', fn: zeroIsNull },
+ AvvSasong: 'Avverkningssäsong',
+ Avverktyp: 'Avverkningstyp',
+ ArendeStatus: 'Ärendestatus',
+ AvvHa: { desc: 'Avverkad areal', unit: 'ha' },
+ geom_area: { desc: 'Areal för ytan', fn: 'area' },
+ });
+
+ layers.avverk.utford = {
+ title: 'Utförd avverkning',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'ArendeAr',
+ 'Skogstyp',
+ 'AnmaldHa',
+ 'NatforHa',
+ 'Avverktyp',
+ 'Avvdatum',
+ 'KallaDatum',
+ 'geom_area',
+ ]),
+ };
+
+ layers.avverk.anmald = {
+ title: 'Avverkningsanmälansområde',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'Inkomdatum',
+ 'ArendeAr',
+ 'AnmaldHa',
+ 'NatforHa',
+ 'SkogsodlHa',
+ 'AvvSasong',
+ 'ArendeStatus',
+ 'AvvHa',
+ ]),
+ };
+ })();
+
+ layers.skydd = {};
+ (function() {
+ const fieldMap = mkFieldMap({
+ NVRID: { desc: 'NVR-ID', classes: ['feature-objid'] },
+ FORSKRNAMN: 'Föreskriftsområde',
+ OBJEKTNAMN: 'Namn',
+ NAMN: 'Namn',
+ BESLSTAT: 'Beslutsstatus',
+ FORESKRTYP: 'Föreskriftstyp',
+ FORESKRIFT: 'Föreskriftssubtyp',
+ FRANDATUM: 'Från datum',
+ TILLDATUM: 'Till datum',
+ BESKRIVN: 'Beskrivning',
+ geom_area: { desc: 'Areal', fn: 'area' },
+
+ SKYDDSTYP: 'Skyddstyp',
+ BESLSTATUS: 'Beslutsstatus',
+ URSBESLDAT: 'Beslutsdatum (bildande)',
+ URSGALLDAT: 'Ursprungligt gällandedatum',
+ SENGALLDAT: 'Senaste gällandedatum',
+ FORVALTARE: 'Förvaltare',
+ IUCNKAT: 'IUCN-kategori',
+ DIARIENR: { desc: 'Diarienummer', classes: ['feature-attr-dnr'] },
+ LAGRUM: 'Lagrum',
+ BESLMYND: 'Beslutsmyndighet',
+ LAND_HA: { desc: 'Areal land', unit: 'ha' },
+ VATTEN_HA: { desc: 'Areal vatten', unit: 'ha' },
+ SKOG_HA: { desc: 'Skogsmarksareal', unit: 'ha' },
+
+ IKRAFTDATF: 'Ikraftträdandedatum föreskrifter',
+ TILLSYNSMH: 'Tillsynsmyndighet',
+ PROVNMHTIL: 'Prövningsmyndighet tillstånd',
+ PROVNMHDIS: 'Prövningsmyndighet dispens',
+
+ NAME: 'Namn',
+ RAMSAR_ID: { desc: 'Ramsar-ID', classes: ['feature-objid'] },
+ LEGAL_ACT: 'Rättsakt',
+ URSPR_BESL: 'Ursprungligt beslutsdatum',
+ SEN_BESLUT: 'Senaste beslutsdatum',
+ LINK: { desc: 'Länk', fn: formatLink },
+ });
+
+ layers.skydd.tilltradesforbud = {
+ title: 'Tillträdesförbud',
+ fields: mapFields(fieldMap, [
+ 'NVRID',
+ 'FORSKRNAMN',
+ 'OBJEKTNAMN',
+ 'BESLSTAT',
+ 'FORESKRTYP',
+ 'FORESKRIFT',
+ 'FRANDATUM',
+ 'TILLDATUM',
+ 'BESKRIVN',
+ 'geom_area',
+ ]),
+ };
+
+ /* Nationella skyddsformer från Naturvårdsregistret */
+ const isSurface = (k) => !/_punkt$/.test(k);
+ Object.entries({
+ nationalpark: 'Nationalpark',
+ naturreservat: 'Naturreservat',
+ naturreservat_kommunalt: 'Kommunalt naturreservat',
+ naturvardsomrade: 'Naturvårdsområde',
+ djur_och_vaxtskyddsomrade: 'Djur- och växtskyddsområde',
+ kulturreservat: 'Kulturreservat',
+ vattenskyddsomrade: 'Vattenskyddsområden',
+ landskapsbildsskyddsomrade: 'Landskapsbildsskyddsområde',
+ ovrigt_biotopskyddsomrade: 'Biotopskydd utanför skogsmark',
+ naturminne_yta: 'Naturminne (yta)',
+ naturminne_punkt: 'Naturminne (punkt)',
+ interimistiskt_forbud: 'Interimistiskt förbud',
+ })
+ .forEach(([k, title]) => layers.skydd[k] = {
+ title: title,
+ fields: mapFields(k, fieldMap, [
+ 'NVRID',
+ 'NAMN',
+ 'SKYDDSTYP',
+ 'BESLSTATUS',
+ 'URSBESLDAT',
+ ['URSGALLDAT', (k) => k !== 'vattenskyddsomrade'],
+ ['SENGALLDAT', (k) => k !== 'vattenskyddsomrade'],
+ ['FORVALTARE', (k) => k !== 'vattenskyddsomrade'],
+ ['IKRAFTDATF', (k) => k === 'vattenskyddsomrade'],
+ 'IUCNKAT',
+ 'DIARIENR',
+ 'LAGRUM',
+ 'BESLMYND',
+ ['TILLSYNSMH', (k) => k === 'vattenskyddsomrade'],
+ ['PROVNMHTIL', (k) => k === 'vattenskyddsomrade'],
+ ['PROVNMHDIS', (k) => k === 'vattenskyddsomrade'],
+ ['geom_area', isSurface],
+ ['LAND_HA', isSurface],
+ ['VATTEN_HA', isSurface],
+ ['SKOG_HA', isSurface],
+ ]),
+ });
+
+ /* Natura 2000-områden */
+ (function() {
+ const fields = [
+ { key: 'SITE_CODE', desc: 'Områdeskod', classes: ['feature-objid'] },
+ { key: 'NAMN', desc: 'Namn' },
+ { key: 'OMRADESTYP', desc: 'Områdestyp' },
+ { key: 'UPPLAMNARE', desc: 'Uppgiftslämnare' },
+ { key: 'SPA_DATUM', desc: 'SPA-datum' },
+ { key: 'SCI_FORSL', desc: 'SCI-förslagsdatum' },
+ { key: 'SCI_DATUM', desc: 'SCI-datum' },
+ { key: 'SAC_DATUM', desc: 'SAC-datum' },
+ fieldMap.geom_area,
+ { key: 'KVALITET', desc: 'Kvalitet' },
+ { key: 'KARAKTAR', desc: 'Kännetecken för området' },
+ { key: 'ARTER', desc: 'Arter' },
+ { key: 'NATURTYPER', desc: 'Naturtyper' },
+ { key: 'BEVPLAN', desc: 'Bevarandeplan', fn: formatLink },
+ ];
+ Object.entries({
+ fageldirektivet: 'Fågeldirektivet (SPA)',
+ habitatdirektivet: 'Art- och habitatdirektivet (SCI)',
+ })
+ .forEach(([k, title]) => layers.skydd[k] = { title, fields });
+ })();
+
+ /* Områden med internationell status */
+ layers.skydd.helcom = {
+ title: 'Marina skyddade områden (Helcom MPA)',
+ fields: mapFields(fieldMap, [ 'NAME', 'geom_area' ]),
+ };
+
+ layers.skydd.ramsar = {
+ title: 'Ramsar-områden (Våtmarkskonventionen)',
+ fields: mapFields(fieldMap, [
+ 'RAMSAR_ID',
+ 'SKYDDSTYP',
+ 'NAMN',
+ 'geom_area',
+ 'LAND_HA',
+ 'VATTEN_HA',
+ 'SKOG_HA',
+ 'URSPR_BESL',
+ 'SEN_BESLUT',
+ 'LEGAL_ACT',
+ 'LINK',
+ ]),
+ };
+
+ layers.skydd.ospar = {
+ title: 'Marina skyddade områden (Ospar MPA)',
+ fields: [
+ { key: 'ORIGIN', desc: 'Ursprung' },
+ { key: 'NAMN_N2000', desc: 'N2000-namn' },
+ { key: 'MPA_ID', desc: 'MPA-ID', classes: ['feature-objid'] },
+ { key: 'MPA_NAMN', desc: 'MPA-namn' },
+ { key: 'N2000_SITE', desc: 'N2000-ID', classes: ['feature-objid'] },
+ fieldMap.geom_area,
+ ],
+ };
+
+ layers.skydd.varldsarv = {
+ title: 'Världsarv med mycket höga naturvärden (Unesco)',
+ fields: mapFields(fieldMap, [ 'NAMN', 'geom_area' ]),
+ };
+
+ layers.skydd.biosfarsomraden = {
+ title: 'Biosfärsområden (UNESCO)',
+ fields: [
+ { key: 'SKYDDSTYP', desc: 'Skyddstyp' },
+ { key: 'NAMN', desc: 'Namn' },
+ { key: 'LINK', desc: 'Länk', fn: formatLink },
+ fieldMap.geom_area,
+ ],
+ };
+
+ layers.skydd.naturvardsavtal = {
+ title: 'Naturvårdsavtal (Naturvårdsverket, Länsstyrelsen)',
+ fields: [
+ { key: 'ID', desc: 'ID', classes: ['feature-objid'] },
+ { key: 'OBJNAMN', desc: 'Namn' },
+ { key: 'FASTBET', desc: 'Fastighet', classes: ['feature-objid'] },
+ { key: 'DATSTART', desc: 'Giltig från' },
+ { key: 'DATSLUT', desc: 'Giltig till' },
+ { key: 'DIARIENRNV', desc: 'Diarienummer', classes: ['feature-attr-dnr'] },
+ { key: 'STATUS', desc: 'Satus' },
+ fieldMap.geom_area,
+ ],
+ };
+ })();
+
+ (function() {
+ const fieldMap = mkFieldMap({
+ Beteckn: { desc: 'Ärendebeteckning', classes: ['feature-objid'] },
+ Biotyp: { desc: 'Biotopkategori' },
+ Naturtyp: { desc: 'Skogstyp' },
+ ArendeAr: { desc: 'Registeringsår' },
+ geom_area: { desc: 'Areal', fn: 'area' },
+ AreaProd: { desc: 'Skogsmarksareal', unit: 'ha' },
+ Datbeslut: { desc: 'Beslutsdatum' },
+ Url: { desc: 'Länk', fn: (v) => formatLink(v, 'Skogens Pärlor') },
+ NvaTyp: 'Biotopkategori',
+ DatAvtal: 'Avtalsdatum',
+ Undertyp: 'Undertyp',
+ AvtalatDatum: 'Avtalat datum',
+ Objnamn: 'Objektnamn',
+ Datinv: 'Datum för fältinventering',
+ });
+
+ layers.skydd.skogligt_biotopskyddsomrade = {
+ title: 'Biotopskydd i skogsmark',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'Biotyp',
+ 'Naturtyp',
+ 'ArendeAr',
+ 'geom_area',
+ 'AreaProd',
+ 'Datbeslut',
+ 'Url',
+ ]),
+ };
+ layers.skydd.naturvardsavtal_skogsstyrelsen = {
+ title: 'Naturvårdsavtal (Skogsstyrelsen)',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'ArendeAr',
+ 'NvaTyp',
+ 'Naturtyp',
+ 'DatAvtal',
+ 'geom_area',
+ 'AreaProd',
+ 'Url',
+ 'Undertyp',
+ ]),
+ };
+
+ layers.skydd.atervatningsavtal = {
+ title: 'Återvätningsavtal',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'ArendeAr',
+ 'AvtalatDatum',
+ 'geom_area',
+ 'Url',
+ ]),
+ };
+
+ layers.nv = {};
+ Object.assign(fieldMap, mkFieldMap(Object.fromEntries(
+ [1,2,3].map((i) => [`Biotop${i}`, `Biotoptyp #${i}`]).concat(
+ [1,2,3,4,5,6,7,8].map((i) => [`Beskrivn${i}`, `Nyckelord #${i} som beskriver objektet`])
+ ))));
+ layers.nv.naturvarde_sks = {
+ title: 'Objekt med naturvärden (Skogsstyrelsen)',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'Objnamn',
+ 'Datinv',
+ 'Biotop1', 'Biotop2', 'Biotop3',
+ 'Beskrivn1', 'Beskrivn2', 'Beskrivn3',
+ 'geom_area',
+ 'Url',
+ ]),
+ };
+ layers.nv.nyckelbiotop = {
+ title: 'Nyckelbiotop (Skogsstyrelsen)',
+ fields: mapFields(fieldMap, [
+ 'Beteckn',
+ 'Objnamn',
+ 'Datinv',
+ 'Biotop1', 'Biotop2', 'Biotop3',
+ 'Beskrivn1', 'Beskrivn2', 'Beskrivn3', 'Beskrivn4', 'Beskrivn5', 'Beskrivn6', 'Beskrivn7', 'Beskrivn8',
+ 'geom_area',
+ 'Url',
+ ]),
+ };
+ layers.nv.nyckelbiotop_storskogsbruk = {
+ title: 'Nyckelbiotop (storskogsbruket)',
+ fields: [
+ { key: 'Org', desc: 'Uppgifter lämnade av' },
+ { key: 'InkomDatum', desc: 'Inkom datum' },
+ fieldMap.geom_area,
+ fieldMap.Url,
+ ],
+ };
+
+ layers.nv.sumpskog = {
+ title: 'Sumpskog',
+ fields: [
+ { key: 'Namn', desc: 'Objektnamn' },
+ { key: 'Tradtext', desc: 'Skogstyp' },
+ { key: 'Hydrtext', desc: 'Hydrologisk typ' },
+ { key: 'Delklass', desc: 'Klass på delobjektet' },
+ { key: 'Klassu', desc: 'Klass på objektet' },
+ { key: 'Lovandel', desc: 'Andel löv' },
+ { key: 'Andelva', desc: 'Andel öppet vatten' },
+ { key: 'Krontakn', desc: 'Krontäckning' },
+ { key: 'Huggklas', desc: 'Huggningsklass' },
+ { key: 'Ingrepp', desc: 'Ingrepp på delobjekt (max 4)' },
+ { key: 'Ingrpavv', desc: 'Grad av påverkan på delobjekt (max 4)' },
+ { key: 'Objnyck', desc: 'Nyckelord på objektnivå' },
+ { key: 'Delnyck', desc: 'Nyckelord på delobjektsnivå' },
+ { key: 'Flygar', desc: 'Flygbildsår' },
+ { key: 'Faltdat', desc: 'Datum för fältbesök' },
+ { key: 'Invtekn', desc: 'Inventeringsteknik' },
+ { key: 'Invdat', desc: 'Inventeringdatum' },
+ { key: 'Ansvmynd', desc: 'Ansvarig myndighet' },
+ fieldMap.geom_area,
+ fieldMap.Url,
+ ],
+ };
+ })();
+
+ layers.nv.pagaende_naturreservatsbildning = {
+ title: 'Pågående naturreservatsbildning',
+ fields: [
+ { key: 'NAMN', desc: 'Objektnamn' },
+ /* XXX unclear what "GRANSJUST" means, just a guess */
+ { key: 'GRANSJUST', desc: 'Senast justerat' },
+ { key: 'geom_area', desc: 'Areal', fn: 'area' },
+ ],
+ };
+
+ layers.nv.snus = {
+ title: 'Skyddsvärd statlig skog',
+ fields: [
+ { key: 'NAMN', desc: 'Objektnamn' },
+ { key: 'AR', desc: 'År' },
+ { key: 'NATURGEOGR', desc: 'Naturgeografisk region', classes: ['feature-objid'] },
+ { key: 'OBJEKTKATE', desc: 'Objektskategori', classes: ['feature-objid'] },
+ { key: 'MARKAGARE', desc: 'Markägare' },
+ { key: 'VARDEKARNA', desc: 'Areal värdekärna', unit: 'ha' },
+ { key: 'UTV_MARK', desc: 'Areal utvecklingsmark', unit: 'ha' },
+ { key: 'TOTAL_AREA', desc: 'Totalareal', unit: 'ha' },
+ { key: 'LAND', desc: 'Areal land', unit: 'ha' },
+ { key: 'VATTEN', desc: 'Areal vatten', unit: 'ha' },
+ { key: 'PROD_SKOG', desc: 'Areal produktiv skogsmark', unit: 'ha' },
+ { key: 'SKOG_O_FJG', desc: 'Areal produktiv skogsmark ovanför fjällnära gräns', unit: 'ha' },
+ { key: 'SKOG_N_FJG', desc: 'Areal produktiv skogsmark nedanför fjällnära gräns', unit: 'ha' },
+ { key: 'SKYDDSZON', desc: 'Areal skyddszon', unit: 'ha' },
+ { key: 'ARRO_MARK', desc: 'Areal arronderingsmark', unit: 'ha' },
+ { key: 'KRITERIER', desc: 'Kriterier för urval' },
+ { key: 'BESKRIVN', desc: 'Beskrivning av området' },
+ { key: 'LST_BEDOMN', desc: 'Länsstyrelsens bedömning' },
+ { key: 'KALLOR', desc: 'Källor' },
+ ],
+ };
+
+ layers.ri = {};
+ (function() {
+ const fieldMap = mkFieldMap({
+ NAMN: 'Namn',
+ SKYDD: 'Skydd',
+ AMNESOMRAD: 'Ämnesområde',
+ AMNESOMR: 'Ämnesområde',
+ OMRADESNR: { desc: 'Områdesnummer', classes: ['feature-objid'] },
+ BESKRIVNIN: { desc: 'Beskrivning', fn: formatLink },
+ LANK_VARDE: { desc: 'Länk värdebeskrivning', fn: formatLink },
+ LAGRUM: 'Lagrum',
+ BESLUTSDAT: 'Beslutsdatum',
+ BESLDATUM: 'Beslutsdatum',
+ ARENDENR: { desc: 'Ärendenummer', classes: ['feature-attr-dnr'] },
+ LANK_BESLU: { desc: 'Länk beslut', fn: formatLink },
+ AKTIVITET: 'Aktivitet',
+ NATURTYP: 'Naturtyp',
+ ORGINALID: { desc: 'Original-ID', classes: ['feature-objid'] },
+ RIKSID: { desc: 'Riks-ID', classes: ['feature-objid'] },
+ geom_area: { desc: 'Areal', fn: 'area' },
+ AREA_LAND_: { desc: 'Areal land', unit: 'ha' },
+ AREA_VATTE: { desc: 'Areal vatten', unit: 'ha' },
+ });
+ layers.ri.naturvard = {
+ title: 'Riksintresse naturvård',
+ fields: mapFields(fieldMap, [
+ 'NAMN',
+ 'SKYDD',
+ 'AMNESOMRAD',
+ 'BESKRIVNIN',
+ 'LAGRUM',
+ 'BESLUTSDAT',
+ 'ORGINALID',
+ 'RIKSID',
+ 'geom_area',
+ ]),
+ };
+ layers.ri.friluftsliv = {
+ title: 'Riksintresse friluftsliv',
+ fields: mapFields(fieldMap, [
+ 'NAMN',
+ 'SKYDD',
+ 'AMNESOMR',
+ 'OMRADESNR',
+ 'LANK_VARDE',
+ 'LAGRUM',
+ 'BESLDATUM',
+ 'ARENDENR',
+ 'LANK_BESLU',
+ 'AKTIVITET',
+ 'NATURTYP',
+ 'geom_area',
+ 'AREA_LAND_',
+ 'AREA_VATTE',
+ ]),
+ };
+
+ Object.assign(fieldMap, mkFieldMap({
+ METODBESKR: 'Metodbeskrivning',
+ TILLKDATUM: 'Tillkomstdatum',
+ REVDATUM: 'Revisionsdatum',
+ ANM: 'Anmärkning',
+ OBJEKTLANK: { desc: 'Objektlänk', fn: formatLink },
+ REFERENS: 'Referens',
+ OBJTYP: 'Objekttyp',
+ ORIGINALID: fieldMap.ORGINALID,
+ DIG_SKALA: { desc: 'Digitaliseringsskala', fn: (v) => v > 0 ? v : null },
+ }));
+ [
+ ['rorligt_friluftsliv', /* 0 */ 'rörligt friluftsliv (MB 4 kap 1§ och 2§)'],
+ ['obruten_kust', /* 1 */ 'obruten kust (MB 4 kap 3§)'],
+ ['obrutet_fjall', /* 2 */ 'obrutet fjäll (MB 4 kap 5§)'],
+ ['skyddade_vattendrag', /* 3 */ 'skyddade vattendrag (MB 4 kap 6§)'],
+ ]
+ .forEach(([k, title], idx) => layers.ri[k] = {
+ title: 'Riksintresse ' + title,
+ fields: mapFields(idx, fieldMap, [
+ 'NAMN',
+ 'BESKRIVNIN',
+ 'METODBESKR',
+ 'TILLKDATUM',
+ 'REVDATUM',
+ ['OBJTYP', [1]],
+ ['ANM', [0,1,3]],
+ ['DIG_SKALA', [3]],
+ 'OBJEKTLANK',
+ 'geom_area',
+ 'ORIGINALID',
+ 'REFERENS',
+ ]),
+ });
+ })();
+
+ layers.ren = {
+ betesomrade: {
+ title: 'Samebyarnas betesområde',
+ fields: [
+ { key: 'NAMN', desc: 'Sameby' },
+ { key: 'SAMEBY_TYP', desc: 'Samebys typ' },
+ { key: 'SIGNATUR', desc: 'Signatur' },
+ { key: 'AKTUALITET', desc: 'Aktualitet' },
+ { key: 'geom_area', desc: 'Areal', fn: 'area' },
+ ],
+ },
+ flyttled: {
+ title: 'Samebyarnas markanvändningsredovisning \u2013 flyttled',
+ fields: [
+ { key: 'LED_ID', desc: 'Led-ID', classes: ['feature-objid'], fn: (v) => v > 0 ? v : null },
+ { key: 'SAMEBY1', desc: 'Sameby #1' },
+ { key: 'SAMEBY2', desc: 'Sameby #2' },
+ { key: 'SAMEBY3', desc: 'Sameby #3' },
+ { key: 'BESKRIVNIN', desc: 'Beskrivning' },
+ { key: 'ARSTID', desc: 'Årstid' },
+ { key: 'RIKSINTR', desc: 'Riksintresse' },
+ { key: 'FAST_LED', desc: 'Fast led' },
+ { key: 'AKTUALITET', desc: 'Aktualitet' },
+ { key: 'SIGNATUR', desc: 'Signatur' },
+ { key: 'geom_length', desc: 'Ledlängd', fn: 'length' },
+ ],
+ },
+ riks_ren: {
+ title: 'Riksintresse rennäring',
+ fields: [
+ { key: 'LAGRUM', desc: 'Lagrum' },
+ { key: 'AKTUALITET', desc: 'Aktualitet' },
+ { key: 'SIGNATUR', desc: 'Signatur' },
+ { key: 'geom_area', desc: 'Areal', fn: 'area' },
+ ],
+ },
+ omr_riks: {
+ title: '(Kärn)områden av riksintresse rennäring',
+ fields: [
+ { key: 'OMR_NR', desc: 'Områdes-ID', classes: ['feature-objid'] },
+ { key: 'LANK', desc: 'Länk' },
+ { key: 'ARET_RUNT', desc: 'Årets runt' },
+ { key: 'SAMEBY', desc: 'Sameby' },
+ { key: 'ANSVARIG', desc: 'Ansvarig' },
+ { key: 'AKTUALITET', desc: 'Aktualitet' },
+ { key: 'SIGNATUR', desc: 'Signatur' },
+ { key: 'geom_area', desc: 'Areal', fn: 'area' },
+ ],
+ },
+ };
+
+ /* format value to HTML */
+ const numberFormatters = [
+ { },
+ { maximumFractionDigits: 1 },
+ { maximumFractionDigits: 2 },
+ ]
+ /* XXX would be nice to use Intl.NumberFormat()'s unit support, but m² and km² are not
+ * supported currently, see https://github.com/tc39/ecma402/issues/767 */
+ .map((fmt) => new Intl.NumberFormat(LOCALE, fmt));
+ const unitSeparator = '\u00A0'; /* U+00A0 NO-BREAK SPACE */
+ const formatValue = function(value, options) {
+ if (options?.fn == null) {
+ /* no-op */
+ } else if (typeof options.fn === 'function') {
+ value = options.fn(value);
+ } else if (options.fn === 'length' && typeof value === 'number' && options?.unit == null) {
+ if (value <= 5_000) { /* ≤ 5 km */
+ value = numberFormatters[1].format(value) + unitSeparator + 'm';
+ } else {
+ value = numberFormatters[2].format(value/1000) + unitSeparator + 'km';
+ }
+ } else if (options.fn === 'area' && typeof value === 'number' && options?.unit == null) {
+ if (value < 10_000) { /* < 1 ha */
+ value = numberFormatters[1].format(value) + unitSeparator + 'm²';
+ } else if (value < 100_000_000) { /* < 10000 ha (100 km²) */
+ value = numberFormatters[2].format(value/10_000) + unitSeparator + 'ha';
+ } else {
+ value = numberFormatters[2].format(value/1_000_000) + unitSeparator + 'km²';
+ }
+ }
+ if (value == null) {
+ return null;
+ }
+ if (value instanceof HTMLElement) {
+ return value;
+ }
+ switch (typeof value) {
+ case 'boolean':
+ return document.createTextNode(value ? 'Ja' : 'Nej');
+ case 'string':
+ return document.createTextNode(value);
+ case 'number':
+ if (options?.unit != null) {
+ return document.createTextNode(numberFormatters[0].format(value)
+ + unitSeparator + options.unit);
+ }
+ return document.createTextNode(value.toString());
+ default:
+ return null;
+ }
+ };
+
+ /* turn the properties into a fine <table> */
+ const formatFeaturePropertiesToHTML = function(properties) {
+ const table = document.createElement('table');
+ table.classList.add('table', 'table-sm', 'table-borderless', 'table-hover');
+
+ const tbody = document.createElement('tbody');
+ table.appendChild(tbody);
+
+ const def = layers[properties.layer_group][properties.layer];
+ def.fields.forEach(function(field) {
+ const tr = document.createElement('tr');
+ tbody.appendChild(tr);
+
+ const th = document.createElement('th');
+ th.setAttribute('scope', 'row');
+ tr.appendChild(th);
+ const textDesc = document.createTextNode(field.desc);
+ th.appendChild(textDesc);
+
+ const td = document.createElement('td');
+ tr.appendChild(td);
+ const v = formatValue(properties[field.key], field);
+ if (v != null) {
+ td.appendChild(v);
+ }
+ field.classes?.forEach?.((c) => td.classList.add(c));
+ });
+
+ const content = document.createElement('div');
+ if (def.title != null) {
+ const h = document.createElement('h6');
+ content.appendChild(h);
+ const textNode = document.createTextNode(def.title);
+ h.appendChild(textNode);
+ }
+
+ content.appendChild(table);
+ return content;
+ };
+
+ /* initialize popup overlay */
const popupOverlay = new Overlay({
stopEvent: true,
- element: popup,
+ element: document.getElementById('popup'),
});
- map.addOverlay(popupOverlay);
+ MAP.addOverlay(popupOverlay);
+
+ let featureOverlayLayer = null;
+ let overlayAttributes = [],
+ overlayAttrIdx = 0,
+ mapSources = {};
+ /* clear the highlighted feature list and make the overlay layer invisible */
+ const disposeFeatureOverlay = function() {
+ if (featureOverlayLayer?.getVisible?.()) {
+ featureOverlayLayer.setVisible(false);
+ featureOverlayLayer.changed();
+ }
+ /* clear the overlay list */
+ overlayAttributes = [];
+ overlayAttrIdx = 0;
+ mapSources = {};
+ }
- let popover, overlayAttributes = [], overlayAttrIdx = 0;
+ let popover = null;
+ /* clear overlay layer and dispose popover */
+ const disposePopover = function() {
+ disposeFeatureOverlay();
+ if (popover?.tip != null) {
+ popover.dispose();
+ }
+ };
+
+ /* initialize popover */
+ featureOverlayLayer = new VectorTileLayer({
+ zIndex: 65535,
+ declutter: false,
+ visible: false,
+ renderMode: 'vector',
+ style: null,
+ map: MAP,
+ });
const header = document.createElement('div');
header.classList.add('d-flex');
@@ -3538,23 +5251,24 @@ const [vectorLayers, featureOverlayLayer] = (function() {
headerGrabbingArea.classList.add('flex-grow-1', 'grabbing-area', 'pe-2', 'me-2');
header.appendChild(headerGrabbingArea);
+ const pageNode = document.createElement('h6');
+ headerGrabbingArea.appendChild(pageNode);
+
headerGrabbingArea.onmousedown = function(event) {
+ /* move the popover around */
if (event.button != 0) {
return;
}
- const popoverTip = Popover.getInstance(popup).tip;
+ const popoverTip = popover.tip;
if (popoverTip.classList.contains('popover-maximized')) {
return;
}
- pageNode.classList.add('grabbing-area-grabbed');
+ headerGrabbingArea.classList.add('grabbing-area-grabbed');
if (!popoverTip.classList.contains('popover-detached')) {
/* detach popover tip */
popoverTip.classList.add('popover-detached');
const rect = popoverTip.getBoundingClientRect();
- const maxHeight = document.getElementById('map').getBoundingClientRect().height - 1 - rect.top -
- popoverTip.getElementsByClassName('popover-header')[0].getBoundingClientRect().height;
-
const style = popoverTip.style;
style.display = 'none'; /* avoid reflows between the following assignments */
style.position = 'absolute';
@@ -3574,18 +5288,17 @@ const [vectorLayers, featureOverlayLayer] = (function() {
};
document.onmouseup = function(event) {
+ /* done moving around */
if (event.button != 0) {
return;
}
- pageNode.classList.remove('grabbing-area-grabbed');
+ headerGrabbingArea.classList.remove('grabbing-area-grabbed');
document.onmousemove = null;
document.onmouseup = null;
};
};
- const pageNode = document.createElement('h6');
- headerGrabbingArea.appendChild(pageNode);
-
+ /* current number page and total page count */
const pageNum = document.createElement('span');
const pageCount = document.createElement('span');
pageNode.appendChild(document.createTextNode('Träff '));
@@ -3593,48 +5306,48 @@ const [vectorLayers, featureOverlayLayer] = (function() {
pageNode.appendChild(document.createTextNode(' av '));
pageNode.appendChild(pageCount);
+ /* highlight a feature */
const featureOverlayStyle = new Style({
stroke: new Stroke({
color: 'rgba(0, 255, 255, .8)',
width: 3,
}),
});
- const updateFeatureOverlayLayer = function(layer_group, layer, id) {
- const lyr = vectorLayers[layer_group];
- if (lyr === undefined) {
+ const highlightFeature = function(layer_group, layer, id) {
+ const source = mapSources[layer_group];
+ if (source == null) {
return;
}
- const urls = lyr.getSource().getUrls();
- const source = featureOverlayLayer.getSource();
- if (source.getUrls().length < 1 || source.getUrls()[0] !== urls[0]) {
+ if (featureOverlayLayer.getSource() !== source) {
+ /* console.log('Updating source for feature overlay layer'); */
featureOverlayLayer.setVisible(false);
- source.setUrls(urls);
+ featureOverlayLayer.setSource(source);
}
- featureOverlayLayer.setStyle(function(feature, resolution) {
+ featureOverlayLayer.setStyle(function(feature) {
if (feature.getId() === id && feature.getProperties().layer === layer) {
return featureOverlayStyle;
- } else {
- return undefined;
}
});
featureOverlayLayer.setVisible(true);
featureOverlayLayer.changed();
};
+ /* highlight the feature at index overlayAttrIdx within the CGI reply list */
const refreshPopover = function() {
const attr = overlayAttributes[overlayAttrIdx];
- updateFeatureOverlayLayer(attr.layer_group, attr.layer, attr.ogc_fid);
+ highlightFeature(attr.layer_group, attr.layer, attr.ogc_fid);
pageNum.innerHTML = (overlayAttrIdx + 1).toString();
const content = formatFeaturePropertiesToHTML(attr);
popover.tip.getElementsByClassName('popover-body')[0].replaceChildren(content);
};
+ /* go back/forward in the overlayAttributes list */
const onClickPageChange = function(event, offset) {
const btn = event.target;
- if (btn.classList.contains('disabled') || popover === null || popover.tip === null) {
+ if (btn.classList.contains('disabled') || popover?.tip == null) {
return;
}
if (overlayAttrIdx + offset < 0 || overlayAttrIdx + offset > overlayAttributes.length - 1) {
- return;
+ return; /* out of range */
}
overlayAttrIdx += offset;
@@ -3653,6 +5366,7 @@ const [vectorLayers, featureOverlayLayer] = (function() {
setTimeout(function() { btn.blur() }, 100);
};
+ /* control buttons */
const btnPrev = document.createElement('button');
btnPrev.classList.add('popover-button', 'popover-button-prev');
btnPrev.setAttribute('type', 'button');
@@ -3677,8 +5391,8 @@ const [vectorLayers, featureOverlayLayer] = (function() {
const btnExpandTitle = 'Förstora';
const btnExpandTitle2 = 'Förminska';
btnExpand.setAttribute('aria-label', btnExpand.title);
- btnExpand.onclick = function(event) {
- if (popover === null || popover.tip === null) {
+ btnExpand.onclick = function() { /* maximize or reduce the popover */
+ if (popover?.tip == null) {
return;
}
if (!popover.tip.classList.contains('popover-maximized')) {
@@ -3700,119 +5414,30 @@ const [vectorLayers, featureOverlayLayer] = (function() {
btnClose.setAttribute('type', 'button');
btnClose.title = 'Stäng';
btnClose.setAttribute('aria-label', btnClose.title);
- btnClose.onclick = function(event) {
- featureOverlayLayer.setVisible(false);
- featureOverlayLayer.changed();
- if (popover !== null) {
- popover.dispose();
- }
- };
+ btnClose.onclick = disposePopover;
header.appendChild(btnPrev);
header.appendChild(btnNext);
header.appendChild(btnExpand);
header.appendChild(btnClose);
- const formatFeaturePropertiesToHTML = function(properties) {
- /* turn the properties into a fine table */
- const table = document.createElement('table');
- table.classList.add('table', 'table-sm', 'table-borderless', 'table-hover');
-
- const tbody = document.createElement('tbody');
- table.appendChild(tbody);
-
- const def = layers[properties.layer_group + '_' + properties.layer];
- def.popover.forEach(function([desc, key, opts]) {
- let v = properties[key];
- if (opts === undefined) {
- opts = {};
- }
- if (opts.fn !== undefined) {
- if (opts.fn === 'length') {
- if (v < 1000) {
- opts.unit = 'm';
- } else {
- v /= 1000.;
- v = Math.round(v*100) / 100.;
- opts.unit = 'km';
- }
- } else if (opts.fn === 'area') {
- if (v < 10000) {
- opts.unit = 'm²';
- } else if (v < 10000 * 10000) {
- v /= 10000.;
- opts.unit = 'ha';
- } else {
- v /= 1000000.;
- opts.unit = 'km²';
- }
- v = Math.round(v*100) / 100.;
- } else {
- v = opts.fn(v);
- }
- }
- if (v === undefined || v === null) {
- v = document.createTextNode('');
- } else if (!(v instanceof HTMLElement)) {
- if (typeof(v) === 'number' && opts.unit !== undefined) {
- v = v.toLocaleString('sv-SE');
- } else if (typeof(v) === 'boolean') {
- v = v ? 'Ja' : 'Nej';
- }
- if (opts.unit !== undefined && v !== '') {
- v += '\u202F' + opts.unit;
- }
- v = document.createTextNode(v);
- }
-
- const tr = document.createElement('tr');
- tbody.appendChild(tr);
-
- const td1 = document.createElement('td');
- tr.appendChild(td1);
- const textDesc = document.createTextNode(desc);
- td1.appendChild(textDesc);
-
- const td2 = document.createElement('td');
- tr.appendChild(td2);
- td2.appendChild(v);
- if (opts.classes !== undefined) {
- opts.classes.forEach((c) => td2.classList.add(c));
- }
- });
-
- const content = document.createElement('div');
- if (def.popoverTitle !== undefined) {
- const h = document.createElement('h6');
- content.appendChild(h);
- const textNode = document.createTextNode(def.popoverTitle);
- h.appendChild(textNode);
+ const measureButton = document.getElementById('measure-button')
+ .getElementsByTagName('button')[0];
+ MAP.on('singleclick', function(event) {
+ if (measureButton.getAttribute('aria-expanded') === 'true') {
+ /* skip popover while measuring */
+ return;
}
-
- content.appendChild(table);
- return content;
- };
-
- const container0 = map.getViewport().getElementsByClassName('ol-overlaycontainer-stopevent')[0];
- map.on('singleclick', function(event) {
- /* clear the overlay list */
- featureOverlayLayer.setVisible(false);
- featureOverlayLayer.changed();
- overlayAttributes = [];
- overlayAttrIdx = 0;
+ disposeFeatureOverlay();
/* dispose any pre-existing popover if not in detached mode */
- popover = Popover.getInstance(popup);
- if (popover !== null) {
- const popoverTip = popover.tip;
- if (popoverTip !== null && !popoverTip.classList.contains('popover-detached')) {
- popover.dispose();
- }
+ popover = Popover.getInstance(popupOverlay.element);
+ if (popover?.tip != null && !popover.tip.classList.contains('popover-detached')) {
+ popover.dispose();
}
- const size = map.getSize();
- if (size[0] < 576 || size[1] < 576) {
- return;
+ if (window.innerWidth < 200) {
+ return; /* skip popover if the map is too small */
}
/* unclear how many feature we'll find, don't render prev/next buttons for now */
@@ -3821,45 +5446,47 @@ const [vectorLayers, featureOverlayLayer] = (function() {
btnNext.classList.add('d-none', 'disabled');
/* never start in maximized mode */
- if (popover !== null && popover.tip !== null) {
+ if (popover?.tip != null) {
popover.tip.classList.remove('popover-maximized');
}
btnExpand.classList.replace('popover-button-reduce', 'popover-button-expand');
btnExpand.title = btnExpandTitle;
btnExpand.setAttribute('aria-label', btnExpand.title);
- const fetch_body = []
- map.forEachFeatureAtPixel(event.pixel, function(feature, layer) {
+ const fetch_body = [];
+ event.map.forEachFeatureAtPixel(event.pixel, function(feature, layer) {
const layerGroup = layer.get('layerGroup');
const layerName = feature.getProperties().layer;
- const def = layers[layerGroup + '_' + layerName];
- if (def !== undefined && def.popover !== undefined) {
+ mapSources[layerGroup] ??= layer.getSource();
+ const def = layerName != null ? layers[layerGroup]?.[layerName] : null;
+ if (def?.fields == null) {
/* skip layers which didn't opt-in for popover */
- if (!fetch_body.length) {
- document.body.classList.add('inprogress');
- if (popover !== null && popover.tip !== null) {
- popover.tip.classList.add('inprogress');
- }
- }
- fetch_body.push({
- layer_group: layerGroup,
- layer: layerName,
- fid: feature.getId(),
- });
- if (fetch_body.length >= 100) {
- return true; /* enough matches already, stop detection here */
- }
+ return false;
+ }
+ if (fetch_body.length === 0) {
+ /* first feature in the list, mark cursor and detached popover as in-progress */
+ document.body.classList.add('inprogress');
+ popover?.tip?.classList?.add?.('inprogress');
+ }
+ fetch_body.push({
+ layer_group: layerGroup,
+ layer: layerName,
+ fid: feature.getId() ?? -1,
+ });
+ if (fetch_body.length >= 100) {
+ return true; /* enough matches already, stop detection here */
}
}, {
hitTolerance: 5,
checkWrapped: false,
- layerFilter: (l) => l !== null && l.get('layerGroup') !== undefined,
+ layerFilter: (lyr) => lyr.get('layerGroup') != null,
});
if (fetch_body.length === 0) {
- if (popover !== null) {
- /* dispose pre-detached popover */
- popover.dispose();
+ /* no feature at pixel (or only within layers which didn't opt-in for popover) */
+ if (popover?.tip != null) {
+ /* dispose pre-detached popover */
+ popover.dispose();
}
return;
}
@@ -3868,18 +5495,26 @@ const [vectorLayers, featureOverlayLayer] = (function() {
method: 'POST',
body: JSON.stringify(fetch_body),
headers: {
- 'Content-Type': 'application/json; charset=UTF-8'
- }
- }).then((resp) => resp.json())
+ 'Content-Type': 'application/json; charset=UTF-8',
+ },
+ })
+ .then(function(resp) {
+ if (resp.status === 200) {
+ return resp.json();
+ } else {
+ throw new Error(`${resp.url} [${resp.status}]`);
+ }
+ })
.then(function(data) {
/* the data is received from the CGI in the order it was sent */
/* TODO optimizations on the CGI would break the above assumption, so the
* decoded JSON response would need to be reordered to match fetch_body */
- overlayAttributes = data
+ overlayAttributes = data;
if (overlayAttributes.length === 0) {
- if (popover !== null) {
- /* dispose pre-detached popover */
- popover.dispose();
+ /* couldn't fetch any attribute for feature(s) at pixel */
+ if (popover?.tip != null) {
+ /* dispose pre-detached popover */
+ popover.dispose();
}
return;
}
@@ -3891,14 +5526,14 @@ const [vectorLayers, featureOverlayLayer] = (function() {
btnPrev.classList.remove('d-none');
pageNode.classList.remove('d-none');
}
- if (popover === null || popover.tip === null) {
+ if (popover?.tip == null) {
/* create a new popover (we're not already showing one in detached mode) */
pageNum.innerHTML = (overlayAttrIdx + 1).toString();
popupOverlay.setPosition(event.coordinate);
const attr = overlayAttributes[0];
- updateFeatureOverlayLayer(attr.layer_group, attr.layer, attr.ogc_fid);
- popover = new Popover(popup, {
+ highlightFeature(attr.layer_group, attr.layer, attr.ogc_fid);
+ popover = new Popover(popupOverlay.element, {
template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div>' +
'<div class="popover-header"></div><div class="popover-body"></div></div>',
title: header,
@@ -3906,7 +5541,7 @@ const [vectorLayers, featureOverlayLayer] = (function() {
html: true,
placement: 'right',
fallbackPlacements: ['right', 'left', 'bottom', 'top'],
- container: container0,
+ container: CONTAINER_STOPEVENT,
});
popover.show();
}
@@ -3916,8 +5551,552 @@ const [vectorLayers, featureOverlayLayer] = (function() {
popover.tip.classList.remove('inprogress');
}
})
+ .catch(function(e) {
+ console.log(e);
+ })
.finally(function() {
+ /* remove in-progress marking on the cursor */
document.body.classList.remove('inprogress');
});
});
-}());
+
+ return disposePopover;
+})();
+
+/* age filter dialog */
+const ageFilterSetActive = (function() {
+ const panel = document.getElementById('age-filter-modal');
+
+ let updateHelpTextTimeoutID = null;
+ const updateHelpTextState = Object.seal({ value: null });
+ const dialog_setup = (function() {
+ const dialog = document.createElement('div');
+ dialog.classList.add('modal-dialog', 'modal-dialog-centered');
+ panel.appendChild(dialog);
+
+ const content = document.createElement('div');
+ content.classList.add('modal-content');
+ dialog.appendChild(content);
+
+ const header = document.createElement('div');
+ header.classList.add('modal-header');
+ content.appendChild(header);
+
+ const title = document.createElement('div');
+ title.classList.add('h4', 'm-0');
+ header.appendChild(title);
+ title.innerHTML = 'Filtrera objekt efter ålder';
+
+ const btn_close = document.createElement('button');
+ btn_close.classList.add('btn-close');
+ btn_close.type = 'button';
+ btn_close.title = 'Stäng';
+ btn_close.setAttribute('aria-label', btn_close.title);
+ btn_close.setAttribute('data-bs-dismiss', 'modal');
+ header.appendChild(btn_close);
+
+ const body = document.createElement('div');
+ body.classList.add('modal-body');
+ content.appendChild(body);
+
+ const p = document.createElement('p');
+ p.classList.add('small', 'text-muted', 'age-filter-infotext');
+ body.appendChild(p);
+ p.appendChild(document.createTextNode('Här kan du t.ex. filtrera bort gamla objekt. ' +
+ 'I nulaget gäller filtret endast objekt inom '));
+ ['mineralrättigheter', 'skogsbruk', 'vindbruk'].forEach(function(l, idx, arr) {
+ if (idx > 0) {
+ p.appendChild(document.createTextNode(idx < arr.length-1 ? ', ' : ' eller '));
+ }
+ const i = document.createElement('i');
+ i.innerHTML = l;
+ p.appendChild(i);
+ });
+ p.appendChild(document.createTextNode(' skikter.'));
+
+ const form = document.createElement('form');
+ form.action = '#';
+ form.id = 'age-filter-form';
+ body.appendChild(form);
+
+ const btn_group = document.createElement('div');
+ btn_group.classList.add('btn-group');
+ btn_group.setAttribute('role', 'group');
+ btn_group.setAttribute('aria-label', 'Välj filtreringstyp');
+ form.appendChild(btn_group);
+
+ const type_choices = Object.fromEntries([
+ { id: 'relative', text: 'Åldersgräns', desc: 'som är' },
+ { id: 'interval', text: 'Datumintervall', desc: 'med datum' },
+ ].map(function(x) {
+ const radio = document.createElement('input');
+ radio.classList.add('btn-check');
+ radio.type = 'radio';
+ radio.name = 'age-filter-type';
+ radio.id = 'age-filter-type-' + x.id;
+ radio.required = true;
+ radio.setAttribute('aria-expanded', 'false');
+ radio.setAttribute('autocomplete', 'off');
+ btn_group.appendChild(radio);
+
+ const lbl = document.createElement('label');
+ lbl.classList.add('btn', 'btn-primary');
+ lbl.setAttribute('for', radio.id);
+ lbl.appendChild(document.createTextNode(x.text));
+ btn_group.appendChild(lbl);
+
+ const div = document.createElement('div');
+ div.classList.add('d-none', 'age-filter-settings');
+ div.setAttribute('aria-hidden', 'true');
+ form.appendChild(div);
+
+ const p = document.createElement('p');
+ p.classList.add('age-filter-settings-desc');
+ div.appendChild(p);
+ const t = document.createTextNode('Visa endast objekt ' + x.desc + ':');
+ p.appendChild(t);
+
+ return [x.id, { radio: radio, div: div } ];
+ }));
+
+ Object.values(type_choices).forEach(function(type_choice) {
+ type_choice.radio.onclick = function(event) {
+ const radio = event.target;
+ radio.checked = true;
+ radio.setAttribute('aria-expanded', 'true');
+
+ if (type_choice === type_choices.relative) {
+ type_choice._update_helptext();
+ }
+
+ Object.values(type_choices).forEach(function(x) {
+ const isSame = radio.isEqualNode(x.radio);
+ if (isSame) {
+ x.div.classList.remove('d-none');
+ x.div.setAttribute('aria-hidden', 'false');
+ } else {
+ x.div.classList.add('d-none');
+ x.div.setAttribute('aria-hidden', 'true');
+ x.radio.checked = false;
+ x.radio.setAttribute('aria-expanded', 'false');
+ }
+ const inputs = x.div.getElementsByTagName('input');
+ for (let i = 0; i < inputs.length; i++) {
+ inputs[i].required = isSame;
+ }
+ });
+ };
+ });
+
+ const create_select = function(parentNode, select_label, options) {
+ const select = document.createElement('select');
+ select.classList.add('form-select');
+ select.setAttribute('aria-label', select_label);
+ parentNode.appendChild(select);
+ return [
+ select,
+ Object.fromEntries(options.map(function(x) {
+ const option = document.createElement('option');
+ option.appendChild(document.createTextNode(x.text));
+ option.value = x.id;
+ select.appendChild(option);
+ return [x.id, option];
+ }))
+ ];
+ };
+
+ const format_date = function(d, s) { /* YYYY-MM-DD or YYYYMMDD */
+ return d.getFullYear() .toString().padStart(4, '0') + (s ?? '-')
+ + (d.getMonth()+1).toString().padStart(2, '0') + (s ?? '-')
+ + d.getDate() .toString().padStart(2, '0');
+ };
+ const parse_date = (m) => new Date( /* YYYY-MM-DD */
+ parseInt(m.slice(0,4), 10),
+ parseInt(m.slice(5,7), 10)-1,
+ parseInt(m.slice(8,10),10),
+ 12 /* use 12:00:00.0 like for URL parameters */
+ );
+
+ (function(type_choice) {
+ const div = document.createElement('div');
+ div.classList.add('input-group');
+ type_choice.div.appendChild(div);
+
+ type_choice.operator = create_select(div, 'Under eller över åldersgränsen', [
+ { id: '<=', text: 'Nyare' },
+ { id: '>=', text: 'Äldre' },
+ ]);
+
+ const span = document.createElement('span');
+ span.classList.add('input-group-text');
+ div.appendChild(span);
+ span.innerHTML = 'än';
+
+ const input_quantity = type_choice.quantity = document.createElement('input');
+ input_quantity.classList.add('form-control');
+ input_quantity.type = 'number';
+ input_quantity.min = 0;
+ input_quantity.max = 65535;
+ input_quantity.placeholder = 'Antal';
+ input_quantity.setAttribute('aria-label', input_quantity.placeholder);
+ div.appendChild(input_quantity);
+
+ type_choice.unit = create_select(div, 'Enhet', [
+ { id: 'd', text: 'dagar' },
+ { id: 'w', text: 'veckor' },
+ { id: 'm', text: 'månader' },
+ { id: 'y', text: 'år' },
+ ]);
+
+ const p = document.createElement('p');
+ p.classList.add('small', 'text-muted', 'invisible');
+ type_choice.div.appendChild(p);
+ p.appendChild(document.createTextNode('Det vill säga med datum '));
+ const span_date = document.createElement('span');
+ p.appendChild(span_date);
+ p.appendChild(document.createTextNode(' eller '));
+ const span_direction = document.createElement('span');
+ p.appendChild(span_direction);
+ p.appendChild(document.createTextNode('.'));
+
+ type_choice._update_helptext = function() {
+ const d = ageFilterSettings.getRelativeDate(
+ parseInt(type_choice.quantity.value, 10),
+ type_choice.unit[0].value
+ );
+ const operator = type_choice.operator[0].value;
+ if (d == null || operator === '') {
+ p.classList.add('invisible');
+ return null;
+ } else {
+ /* update help text */
+ span_date.innerHTML = format_date(d);
+ span_direction.innerHTML = { '>=':'tidigare', '<=':'senare' }[operator];
+ p.classList.remove('invisible');
+ return d;
+ }
+ };
+
+ type_choice.operator[0].onchange = input_quantity.onchange
+ = type_choice.unit[0].onchange
+ = function() {
+ const d = type_choice._update_helptext();
+ if (d != null) {
+ /* propagate to interval tab */
+ const operator = type_choice.operator[0].value;
+ const [d1, d2] = { '>=': [new Date(1900, 0, 1, 12), d], /* between 1900-01-01 and d */
+ '<=': [d, new Date()], /* between d and today */
+ }[operator];
+ type_choices.interval.from.value = format_date(d1);
+ type_choices.interval.to.value = format_date(d2);
+ }
+ };
+ })(type_choices.relative);
+
+ (function(type_choice) {
+ const div = document.createElement('div');
+ div.classList.add('input-group');
+ type_choice.div.appendChild(div);
+
+ const span1 = document.createElement('span');
+ span1.classList.add('input-group-text');
+ span1.innerHTML = 'Mellan';
+ span1.id = 'age-filter-interval-from';
+ div.appendChild(span1);
+
+ const date1 = type_choice.from = document.createElement('input');
+ date1.classList.add('form-control');
+ date1.type = 'date';
+ date1.setAttribute('aria-label', 'Intervallstart');
+ date1.setAttribute('aria-describedby', span1.id);
+ div.appendChild(date1);
+
+ const span2 = document.createElement('span');
+ span2.classList.add('input-group-text');
+ span2.innerHTML = 'och';
+ span2.id = 'age-filter-interval-to';
+ div.appendChild(span2);
+
+ const date2 = type_choice.to = document.createElement('input');
+ date2.classList.add('form-control');
+ date2.type = 'date';
+ date2.setAttribute('aria-label', 'Intervallstop');
+ date2.setAttribute('aria-describedby', span2.id);
+ div.appendChild(date2);
+
+ /* propagate to relative tab by trying to preserve the operator and unit */
+ const propagate_to_relative = function() {
+ let d;
+ switch (type_choices.relative.operator[0].value) {
+ case '<=':
+ d = parse_date(date1.value);
+ break;
+ case '>=':
+ d = parse_date(date2.value);
+ break;
+ }
+ const delta = (new Date().getTime() - d.getTime()) / 86_400_000;
+ let v;
+ switch (type_choices.relative.unit[0].value) {
+ case 'd':
+ v = delta;
+ break;
+ case 'w':
+ v = delta/7;
+ break;
+ case 'm':
+ v = delta*12/365.25;
+ break;
+ case 'y':
+ v = delta/365.25;
+ break;
+ }
+ if (v != null) {
+ type_choices.relative.quantity.value = Math.round(v);
+ type_choices.relative._update_helptext();
+ }
+ };
+
+ /* make sure that from_date ≤ to_date */
+ date1.onchange = function() {
+ if (date1.value !== '' && (date2.value === '' || date1.value > date2.value)) {
+ date2.value = date1.value;
+ }
+ propagate_to_relative();
+ };
+ date2.onchange = function() {
+ if (date2.value !== '' && (date1.value === '' || date1.value > date2.value)) {
+ date1.value = date2.value;
+ }
+ propagate_to_relative();
+ };
+ })(type_choices.interval);
+
+ const show_unknown_age = (function() {
+ const div = document.createElement('div');
+ div.classList.add('form-check', 'form-switch');
+ form.appendChild(div);
+
+ const checkbox = document.createElement('input');
+ checkbox.classList.add('form-check-input');
+ checkbox.type = 'checkbox';
+ checkbox.id = 'age-filter-show-unknown';
+ checkbox.setAttribute('role', 'switch');
+ checkbox.checked = ageFilterSettings.show_unknown;
+ div.appendChild(checkbox);
+
+ const lbl = document.createElement('label');
+ lbl.classList.add('form-check-label');
+ lbl.setAttribute('for', checkbox.id);
+ lbl.innerHTML = 'Visa även objekt med okänd datum.';
+ div.appendChild(lbl);
+
+ return checkbox;
+ })();
+
+ const footer = document.createElement('div');
+ footer.classList.add('modal-footer');
+ content.appendChild(footer);
+
+ const btn_cancel = document.createElement('button');
+ btn_cancel.classList.add('btn', 'btn-secondary');
+ btn_cancel.type = 'button';
+ btn_cancel.innerHTML = 'Återställ';
+ footer.appendChild(btn_cancel);
+
+ const btn_apply = document.createElement('input');
+ btn_apply.classList.add('btn', 'btn-primary');
+ btn_apply.type = 'submit';
+ btn_apply.value = 'Filter';
+ btn_apply.setAttribute('form', form.id);
+ footer.appendChild(btn_apply);
+
+ btn_cancel.onclick = function() {
+ /* deactivate deactivate the filter but preserve its settings */
+ ageFilterSettings.active = false;
+ const params = new URLSearchParams(window.location.hash.substring(1));
+ params.delete('age-filter');
+ params.delete('show-unknown-age');
+ location.hash = '#' + params.toString();
+
+ modal.hide();
+ };
+
+ form.onsubmit = function(event) {
+ event.preventDefault();
+ const [filter_type, filter_settings] = Object.entries(type_choices).filter( (x) => x[1].radio.checked )[0];
+ let param;
+ ageFilterSettings._min_ts = ageFilterSettings._max_ts = null;
+ switch (filter_type) {
+ case 'relative': {
+ /* save relative value so it can be autoshifted later */
+ const operator = ageFilterSettings.operator = filter_settings.operator[0].value;
+ ageFilterSettings.quantity = parseInt(filter_settings.quantity.value, 10);
+ ageFilterSettings.unit = filter_settings.unit[0].value;
+ param = {'<=':'-', '>=':''}[operator];
+ param += ageFilterSettings.quantity.toString() + ageFilterSettings.unit;
+ break;
+ }
+ case 'interval': {
+ const date1 = ageFilterSettings.from = parse_date(filter_settings.from.value);
+ const date2 = ageFilterSettings.to = parse_date(filter_settings.to.value);
+ param = format_date(date1, '') + '-' + format_date(date2, '');
+ break;
+ }
+ default:
+ return;
+ }
+ ageFilterSettings.type = filter_type;
+ ageFilterSettings.show_unknown = show_unknown_age.checked;
+ ageFilterSettings.active = true;
+
+ const params = new URLSearchParams(window.location.hash.substring(1));
+ params.set('age-filter', param);
+ params.set('show-unknown-age', show_unknown_age.checked ? '1' : '0');
+ location.hash = '#' + params.toString();
+
+ modal.hide();
+ };
+
+ const updateHelpTextTimeout = function cb() {
+ const type_choice = type_choices.relative;
+ const [ms, b] = getDelay(updateHelpTextState);
+ if (type_choice.radio.checked && b) {
+ type_choice._update_helptext();
+ }
+ updateHelpTextTimeoutID = setTimeout(cb, ms);
+ };
+
+ /* Now that all elements have been added to the form, click the radio
+ * button to show the relevant <div>, mark its fields as required and
+ * fills them. The function is run whenever the modal is shown. */
+ return function() {
+ const type_choice = type_choices[ageFilterSettings.type];
+ type_choice.radio.click();
+
+ switch (ageFilterSettings.type) {
+ case 'relative': {
+ Object.entries(type_choice.operator[1]).map(function([id, option]) {
+ option.selected = id === ageFilterSettings.operator;
+ });
+ type_choice.quantity.value = ageFilterSettings.quantity.toString();
+ Object.entries(type_choice.unit[1]).map(function([id, option]) {
+ option.selected = id === ageFilterSettings.unit;
+ });
+ type_choice.quantity.dispatchEvent(new Event('change')); /* propagate to absolute */
+ break;
+ }
+ case 'interval': {
+ type_choice.from.value = format_date(ageFilterSettings.from);
+ type_choice.to.value = format_date(ageFilterSettings.to);
+ type_choice.from.dispatchEvent(new Event('change')); /* propagate to relative */
+ break;
+ }
+ }
+
+ if (updateHelpTextTimeoutID == null) {
+ const ms = getDelay(updateHelpTextState)[0];
+ updateHelpTextTimeoutID = setTimeout(updateHelpTextTimeout, ms);
+ }
+ };
+ })();
+
+ /* Retun the number of milliseconds left for the next full 15min at the
+ * wall clock, along with a boolean indicating whether we just passed
+ * midnight.
+ * Keep using setTimeout(,15min) since unlike setInterval() it allows us to avoid clock
+ * skews by adjusting delays */
+ const getDelay = function(state) {
+ const i = 900, d = new Date(); /* at *:00,15,30,45:00.50 */
+ const sec = (d.getHours()*60 + d.getMinutes())*60 + d.getSeconds();
+ /* add 50ms to ensure formatting the date doesn't end up on the previous period */
+ const ms = (Math.floor(sec/i)*i + i - sec)*1000 - d.getMilliseconds() + 50;
+ const v = state.value, v2 = d.getDate();
+ const b = v == null || v !== v2;
+ if (b) {
+ state.value = v2;
+ }
+ return [ms, b];
+ };
+
+ const modal = new Modal(panel, {
+ backdrop: false,
+ });
+
+ const backdrop = document.getElementById('age-filter-modal-backdrop');
+ backdrop.onclick = function() {
+ modal.hide();
+ };
+
+ const btn = document.getElementById('age-filter-button').getElementsByTagName('button')[0];
+ if (ageFilterSettings.active) {
+ btn.classList.replace('btn-light', 'btn-dark');
+ }
+ panel.addEventListener('show.bs.modal', function() {
+ backdrop.classList.add('modal-backdrop', 'show');
+ btn.setAttribute('aria-expanded', 'true');
+ btn.classList.replace('btn-light', 'btn-dark');
+ });
+ panel.addEventListener('hide.bs.modal', function() {
+ /* XXX workaround for https://github.com/twbs/bootstrap/issues/41005#issuecomment-2585390544 */
+ const activeElement = document.activeElement;
+ if (activeElement instanceof HTMLElement) {
+ activeElement.blur();
+ }
+ });
+ panel.addEventListener('hidden.bs.modal', function() {
+ if (!ageFilterSettings.active) {
+ btn.classList.replace('btn-dark', 'btn-light');
+ }
+ if (updateHelpTextTimeoutID != null) {
+ clearTimeout(updateHelpTextTimeoutID);
+ updateHelpTextState.value = null;
+ updateHelpTextTimeoutID = null;
+ }
+ btn.setAttribute('aria-expanded', 'false');
+ backdrop.classList.remove('modal-backdrop', 'show');
+ });
+
+ btn.onclick = function() {
+ disposePopover();
+ dialog_setup();
+ modal.show();
+ };
+
+ return (function() { /* setter for ageFilterSettings.active */
+ let timeoutID = null;
+ const state = { value: null };
+ const fun = function cb() {
+ const [ms, b] = getDelay(state);
+ if (b && ageFilterSettings.type === 'relative') {
+ ageFilterSettings.setupMinMax();
+ changed();
+ }
+ timeoutID = setTimeout(cb, ms);
+ };
+ const changed = function() {
+ Object.values(mapLayers)
+ .filter((lyr) => lyr?.get('canFilterByAge'))
+ .forEach((lyr) => lyr.changed());
+ };
+ const setter = function(active) {
+ if (active) {
+ ageFilterSettings.setupMinMax();
+ }
+ ageFilterSettings._active = active;
+ changed();
+ if (active && ageFilterSettings.type === 'relative') {
+ if (timeoutID == null) {
+ timeoutID = setTimeout(fun, getDelay(state)[0]);
+ }
+ } else if (timeoutID != null) {
+ clearTimeout(timeoutID);
+ state.value = null;
+ timeoutID = null;
+ }
+ };
+ /* initial activation from param URL */
+ setter(ageFilterSettings._active);
+ return setter;
+ })();
+})();
diff --git a/package-lock.json b/package-lock.json
index 73f9e67..3ab4cba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,19 +9,24 @@
"version": "0.0.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
+ "@fontsource/inter": "^5.2.8",
"bootstrap": "5.3.x",
"bootstrap-icons": "1.13.x",
- "ol": "10.5.x",
- "proj4": "2.17.x"
+ "ol": "10.6.x",
+ "proj4": "2.19.x"
},
"devDependencies": {
- "vite": "^5.4.19"
+ "@eslint/css": "^0.9.0",
+ "@eslint/js": "^9.29.0",
+ "eslint": "^9.37.0",
+ "globals": "^16.4.0",
+ "vite": "^7.1.9"
}
},
"node_modules/@esbuild/aix-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
- "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz",
+ "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==",
"cpu": [
"ppc64"
],
@@ -31,13 +36,13 @@
"aix"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
- "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz",
+ "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==",
"cpu": [
"arm"
],
@@ -47,13 +52,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
- "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz",
+ "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==",
"cpu": [
"arm64"
],
@@ -63,13 +68,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
- "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz",
+ "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==",
"cpu": [
"x64"
],
@@ -79,13 +84,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
- "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz",
+ "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==",
"cpu": [
"arm64"
],
@@ -95,13 +100,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
- "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz",
+ "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==",
"cpu": [
"x64"
],
@@ -111,13 +116,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
- "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==",
"cpu": [
"arm64"
],
@@ -127,13 +132,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
- "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz",
+ "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==",
"cpu": [
"x64"
],
@@ -143,13 +148,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
- "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz",
+ "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==",
"cpu": [
"arm"
],
@@ -159,13 +164,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
- "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz",
+ "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==",
"cpu": [
"arm64"
],
@@ -175,13 +180,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
- "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz",
+ "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==",
"cpu": [
"ia32"
],
@@ -191,13 +196,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
- "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz",
+ "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==",
"cpu": [
"loong64"
],
@@ -207,13 +212,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
- "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz",
+ "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==",
"cpu": [
"mips64el"
],
@@ -223,13 +228,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
- "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz",
+ "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==",
"cpu": [
"ppc64"
],
@@ -239,13 +244,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
- "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz",
+ "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==",
"cpu": [
"riscv64"
],
@@ -255,13 +260,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
- "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz",
+ "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==",
"cpu": [
"s390x"
],
@@ -271,13 +276,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
- "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz",
+ "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==",
"cpu": [
"x64"
],
@@ -287,13 +292,29 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
- "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz",
+ "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==",
"cpu": [
"x64"
],
@@ -303,13 +324,29 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz",
+ "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
- "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz",
+ "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==",
"cpu": [
"x64"
],
@@ -319,13 +356,29 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz",
+ "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
- "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz",
+ "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==",
"cpu": [
"x64"
],
@@ -335,13 +388,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
- "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz",
+ "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==",
"cpu": [
"arm64"
],
@@ -351,13 +404,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
- "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz",
+ "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==",
"cpu": [
"ia32"
],
@@ -367,13 +420,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
- "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz",
+ "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==",
"cpu": [
"x64"
],
@@ -383,13 +436,266 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
+ "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+ "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz",
+ "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.16.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers/node_modules/@eslint/core": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
+ "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.14.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
+ "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/css": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@eslint/css/-/css-0.9.0.tgz",
+ "integrity": "sha512-fq8hYnjipdzVDSU/bXqv7qlvdjDA27Nq7DhQXzlPElLlVon3lnKovIM/6HaUrq1bz7EPgRobr+vOhpeM6z0X4w==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.14.0",
+ "@eslint/css-tree": "^3.6.1",
+ "@eslint/plugin-kit": "^0.3.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/css-tree": {
+ "version": "3.6.6",
+ "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.6.tgz",
+ "integrity": "sha512-C3YiJMY9OZyZ/3vEMFWJIesdGaRY6DmIYvmtyxMT934CbrOKqRs+Iw7NWSRlJQEaK4dPYy2lZ2y1zkaj8z0p5A==",
+ "dev": true,
+ "dependencies": {
+ "mdn-data": "2.23.0",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz",
+ "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
+ "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.15.2",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
+ "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@fontsource/inter": {
+ "version": "5.2.8",
+ "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz",
+ "integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==",
+ "funding": {
+ "url": "https://github.com/sponsors/ayuhito"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
"node_modules/@petamoriken/float16": {
- "version": "3.9.2",
- "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.2.tgz",
- "integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog=="
+ "version": "3.9.3",
+ "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.9.3.tgz",
+ "integrity": "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="
},
"node_modules/@popperjs/core": {
"version": "2.11.8",
@@ -402,9 +708,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
- "integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
+ "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
"cpu": [
"arm"
],
@@ -415,9 +721,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
- "integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
+ "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
"cpu": [
"arm64"
],
@@ -428,9 +734,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
- "integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
+ "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
"cpu": [
"arm64"
],
@@ -441,9 +747,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
- "integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
+ "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
"cpu": [
"x64"
],
@@ -454,9 +760,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
- "integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
+ "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
"cpu": [
"arm64"
],
@@ -467,9 +773,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
- "integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
+ "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
"cpu": [
"x64"
],
@@ -480,9 +786,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
- "integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
+ "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
"cpu": [
"arm"
],
@@ -493,9 +799,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
- "integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
+ "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
"cpu": [
"arm"
],
@@ -506,9 +812,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
- "integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
+ "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
"cpu": [
"arm64"
],
@@ -519,9 +825,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
- "integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
+ "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
"cpu": [
"arm64"
],
@@ -531,10 +837,10 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
- "integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
+ "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
"cpu": [
"loong64"
],
@@ -544,10 +850,10 @@
"linux"
]
},
- "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
- "integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
+ "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
"cpu": [
"ppc64"
],
@@ -558,9 +864,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
- "integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
+ "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
"cpu": [
"riscv64"
],
@@ -571,9 +877,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
- "integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
+ "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
"cpu": [
"riscv64"
],
@@ -584,9 +890,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
- "integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
+ "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
"cpu": [
"s390x"
],
@@ -597,9 +903,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
- "integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
+ "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==",
"cpu": [
"x64"
],
@@ -610,9 +916,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
- "integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz",
+ "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==",
"cpu": [
"x64"
],
@@ -622,10 +928,23 @@
"linux"
]
},
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
+ "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
- "integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
+ "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
"cpu": [
"arm64"
],
@@ -636,9 +955,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
- "integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
+ "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
"cpu": [
"ia32"
],
@@ -648,10 +967,23 @@
"win32"
]
},
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
+ "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
- "integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
+ "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
"cpu": [
"x64"
],
@@ -662,9 +994,15 @@
]
},
"node_modules/@types/estree": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
- "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true
},
"node_modules/@types/rbush": {
@@ -672,10 +1010,74 @@
"resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz",
"integrity": "sha512-+N+2H39P8X+Hy1I5mC6awlTX54k3FhiUmvt7HWzGJZvF+syUAAxP/stwppS8JE84YHqFgRMv6fCy31202CMFxQ=="
},
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
"node_modules/bootstrap": {
- "version": "5.3.6",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
- "integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
+ "version": "5.3.8",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz",
+ "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==",
"funding": [
{
"type": "github",
@@ -705,49 +1107,414 @@
}
]
},
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
"node_modules/earcut": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz",
- "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw=="
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
+ "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="
},
"node_modules/esbuild": {
- "version": "0.21.5",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
- "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "version": "0.25.10",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
+ "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/aix-ppc64": "0.21.5",
- "@esbuild/android-arm": "0.21.5",
- "@esbuild/android-arm64": "0.21.5",
- "@esbuild/android-x64": "0.21.5",
- "@esbuild/darwin-arm64": "0.21.5",
- "@esbuild/darwin-x64": "0.21.5",
- "@esbuild/freebsd-arm64": "0.21.5",
- "@esbuild/freebsd-x64": "0.21.5",
- "@esbuild/linux-arm": "0.21.5",
- "@esbuild/linux-arm64": "0.21.5",
- "@esbuild/linux-ia32": "0.21.5",
- "@esbuild/linux-loong64": "0.21.5",
- "@esbuild/linux-mips64el": "0.21.5",
- "@esbuild/linux-ppc64": "0.21.5",
- "@esbuild/linux-riscv64": "0.21.5",
- "@esbuild/linux-s390x": "0.21.5",
- "@esbuild/linux-x64": "0.21.5",
- "@esbuild/netbsd-x64": "0.21.5",
- "@esbuild/openbsd-x64": "0.21.5",
- "@esbuild/sunos-x64": "0.21.5",
- "@esbuild/win32-arm64": "0.21.5",
- "@esbuild/win32-ia32": "0.21.5",
- "@esbuild/win32-x64": "0.21.5"
+ "@esbuild/aix-ppc64": "0.25.10",
+ "@esbuild/android-arm": "0.25.10",
+ "@esbuild/android-arm64": "0.25.10",
+ "@esbuild/android-x64": "0.25.10",
+ "@esbuild/darwin-arm64": "0.25.10",
+ "@esbuild/darwin-x64": "0.25.10",
+ "@esbuild/freebsd-arm64": "0.25.10",
+ "@esbuild/freebsd-x64": "0.25.10",
+ "@esbuild/linux-arm": "0.25.10",
+ "@esbuild/linux-arm64": "0.25.10",
+ "@esbuild/linux-ia32": "0.25.10",
+ "@esbuild/linux-loong64": "0.25.10",
+ "@esbuild/linux-mips64el": "0.25.10",
+ "@esbuild/linux-ppc64": "0.25.10",
+ "@esbuild/linux-riscv64": "0.25.10",
+ "@esbuild/linux-s390x": "0.25.10",
+ "@esbuild/linux-x64": "0.25.10",
+ "@esbuild/netbsd-arm64": "0.25.10",
+ "@esbuild/netbsd-x64": "0.25.10",
+ "@esbuild/openbsd-arm64": "0.25.10",
+ "@esbuild/openbsd-x64": "0.25.10",
+ "@esbuild/openharmony-arm64": "0.25.10",
+ "@esbuild/sunos-x64": "0.25.10",
+ "@esbuild/win32-arm64": "0.25.10",
+ "@esbuild/win32-ia32": "0.25.10",
+ "@esbuild/win32-x64": "0.25.10"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.37.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz",
+ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.8.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.4.0",
+ "@eslint/core": "^0.16.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.37.0",
+ "@eslint/plugin-kit": "^0.4.0",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/@eslint/core": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz",
+ "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/eslint/node_modules/@eslint/plugin-kit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz",
+ "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==",
+ "dev": true,
+ "dependencies": {
+ "@eslint/core": "^0.16.0",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
}
},
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -762,11 +1529,6 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/geographiclib-geodesic": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/geographiclib-geodesic/-/geographiclib-geodesic-2.1.1.tgz",
- "integrity": "sha512-lkd8EUkPSByobWu9BPMHTdYA5AUZxOa8McmUNtBE9KrvUJEvSADnN6gTDmhXbi6NzdA16LtWLpSxLE/lIIRhyA=="
- },
"node_modules/geotiff": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.1.3.tgz",
@@ -785,16 +1547,207 @@
"node": ">=10.19"
}
},
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.4.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz",
+ "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
"node_modules/lerc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz",
"integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww=="
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/mdn-data": {
+ "version": "2.23.0",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz",
+ "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==",
+ "dev": true
+ },
"node_modules/mgrs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mgrs/-/mgrs-1.0.0.tgz",
"integrity": "sha512-awNbTOqCxK1DBGjalK3xqWIstBZgN6fxsMSiXLs9/spqWkF2pAhb2rrYCFSsr1/tT7PhcDGjZndG8SWYn0byYA=="
},
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -813,10 +1766,16 @@
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
"node_modules/ol": {
- "version": "10.5.0",
- "resolved": "https://registry.npmjs.org/ol/-/ol-10.5.0.tgz",
- "integrity": "sha512-nHFx8gkGmvYImsa7iKkwUnZidd5gn1XbMZd9GNOorvm9orjW9gQvT3Naw/MjIasVJ3cB9EJUdCGR2EFAulMHsQ==",
+ "version": "10.6.1",
+ "resolved": "https://registry.npmjs.org/ol/-/ol-10.6.1.tgz",
+ "integrity": "sha512-xp174YOwPeLj7c7/8TCIEHQ4d41tgTDDhdv6SqNdySsql5/MaFJEJkjlsYcvOPt7xA6vrum/QG4UdJ0iCGT1cg==",
"dependencies": {
"@types/rbush": "4.0.0",
"earcut": "^3.0.0",
@@ -829,16 +1788,93 @@
"url": "https://opencollective.com/openlayers"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/pako": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse-headers": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz",
"integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A=="
},
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/pbf": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.1.tgz",
@@ -856,10 +1892,22 @@
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/postcss": {
- "version": "8.5.3",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
- "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
@@ -876,7 +1924,7 @@
}
],
"dependencies": {
- "nanoid": "^3.3.8",
+ "nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
@@ -884,14 +1932,25 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/proj4": {
- "version": "2.17.0",
- "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.17.0.tgz",
- "integrity": "sha512-BqVoruVAOUgkw5U9Ns76+E2nHZG0Y42tbkC+0BpyqjhwPIai29hoivyQoyelEKFSfaV3zkR3NqPRD0EwPM4Wug==",
+ "version": "2.19.10",
+ "resolved": "https://registry.npmjs.org/proj4/-/proj4-2.19.10.tgz",
+ "integrity": "sha512-uL6/C6kA8+ncJAEDmUeV8PmNJcTlRLDZZa4/87CzRpb8My4p+Ame4LhC4G3H/77z2icVqcu3nNL9h5buSdnY+g==",
"dependencies": {
- "geographiclib-geodesic": "^2.1.1",
"mgrs": "1.0.0",
"wkt-parser": "^1.5.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ahocevar"
}
},
"node_modules/protocol-buffers-schema": {
@@ -899,6 +1958,15 @@
"resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz",
"integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw=="
},
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/quick-lru": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz",
@@ -923,6 +1991,15 @@
"quickselect": "^3.0.0"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/resolve-protobuf-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
@@ -932,12 +2009,12 @@
}
},
"node_modules/rollup": {
- "version": "4.41.1",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
- "integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
+ "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
"dev": true,
"dependencies": {
- "@types/estree": "1.0.7"
+ "@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -947,29 +2024,52 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.41.1",
- "@rollup/rollup-android-arm64": "4.41.1",
- "@rollup/rollup-darwin-arm64": "4.41.1",
- "@rollup/rollup-darwin-x64": "4.41.1",
- "@rollup/rollup-freebsd-arm64": "4.41.1",
- "@rollup/rollup-freebsd-x64": "4.41.1",
- "@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
- "@rollup/rollup-linux-arm-musleabihf": "4.41.1",
- "@rollup/rollup-linux-arm64-gnu": "4.41.1",
- "@rollup/rollup-linux-arm64-musl": "4.41.1",
- "@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
- "@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
- "@rollup/rollup-linux-riscv64-gnu": "4.41.1",
- "@rollup/rollup-linux-riscv64-musl": "4.41.1",
- "@rollup/rollup-linux-s390x-gnu": "4.41.1",
- "@rollup/rollup-linux-x64-gnu": "4.41.1",
- "@rollup/rollup-linux-x64-musl": "4.41.1",
- "@rollup/rollup-win32-arm64-msvc": "4.41.1",
- "@rollup/rollup-win32-ia32-msvc": "4.41.1",
- "@rollup/rollup-win32-x64-msvc": "4.41.1",
+ "@rollup/rollup-android-arm-eabi": "4.52.4",
+ "@rollup/rollup-android-arm64": "4.52.4",
+ "@rollup/rollup-darwin-arm64": "4.52.4",
+ "@rollup/rollup-darwin-x64": "4.52.4",
+ "@rollup/rollup-freebsd-arm64": "4.52.4",
+ "@rollup/rollup-freebsd-x64": "4.52.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.52.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.52.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.52.4",
+ "@rollup/rollup-linux-arm64-musl": "4.52.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.52.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.52.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.52.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.52.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.52.4",
+ "@rollup/rollup-linux-x64-gnu": "4.52.4",
+ "@rollup/rollup-linux-x64-musl": "4.52.4",
+ "@rollup/rollup-openharmony-arm64": "4.52.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.52.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.52.4",
+ "@rollup/rollup-win32-x64-gnu": "4.52.4",
+ "@rollup/rollup-win32-x64-msvc": "4.52.4",
"fsevents": "~2.3.2"
}
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -979,21 +2079,85 @@
"node": ">=0.10.0"
}
},
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "dev": true,
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/vite": {
- "version": "5.4.19",
- "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
- "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
+ "version": "7.1.9",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.9.tgz",
+ "integrity": "sha512-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg==",
"dev": true,
"dependencies": {
- "esbuild": "^0.21.3",
- "postcss": "^8.4.43",
- "rollup": "^4.20.0"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
- "node": "^18.0.0 || >=20.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -1002,19 +2166,25 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
- "@types/node": "^18.0.0 || >=20.0.0",
- "less": "*",
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
"lightningcss": "^1.21.0",
- "sass": "*",
- "sass-embedded": "*",
- "stylus": "*",
- "sugarss": "*",
- "terser": "^5.4.0"
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
+ "jiti": {
+ "optional": true
+ },
"less": {
"optional": true
},
@@ -1035,6 +2205,12 @@
},
"terser": {
"optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
}
}
},
@@ -1043,16 +2219,52 @@
"resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.5.0.tgz",
"integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw=="
},
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/wkt-parser": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/wkt-parser/-/wkt-parser-1.5.2.tgz",
"integrity": "sha512-1ZUiV1FTwSiSrgWzV9KXJuOF2BVW91KY/mau04BhnmgOdroRQea7Q0s5TVqwGLm0D2tZwObd/tBYXW49sSxp3Q=="
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/xml-utils": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.10.2.tgz",
"integrity": "sha512-RqM+2o1RYs6T8+3DzDSoTRAUfrvaejbVHcp3+thnAtDKo8LskR+HomLajEy5UjTz24rpka7AxVBRR3g2wTUkJA=="
},
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/zstddec": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.1.0.tgz",
diff --git a/package.json b/package.json
index ab316e6..33a40a8 100644
--- a/package.json
+++ b/package.json
@@ -3,17 +3,23 @@
"version": "0.0.1",
"license": "AGPL-3.0-or-later",
"scripts": {
+ "lint": "npx eslint --color --format stylish",
"start": "vite",
"build": "vite build",
"serve": "vite preview"
},
"devDependencies": {
- "vite": "^5.4.19"
+ "@eslint/css": "^0.9.0",
+ "@eslint/js": "^9.29.0",
+ "eslint": "^9.37.0",
+ "globals": "^16.4.0",
+ "vite": "^7.1.9"
},
"dependencies": {
+ "@fontsource/inter": "^5.2.8",
"bootstrap": "5.3.x",
"bootstrap-icons": "1.13.x",
- "ol": "10.5.x",
- "proj4": "2.17.x"
+ "ol": "10.6.x",
+ "proj4": "2.19.x"
}
}
diff --git a/style.css b/style.css
index 6a1b1eb..8bb4033 100644
--- a/style.css
+++ b/style.css
@@ -1,6 +1,7 @@
@import "~bootstrap/dist/css/bootstrap.css";
@import "~bootstrap-icons/font/bootstrap-icons.css";
@import "~ol/ol.css";
+@import "~fontsource/inter/latin-400.css";
html, body {
margin: 0;
@@ -154,9 +155,9 @@ body.inprogress {
.ol-control {
background: none;
border: none;
- outline: none;
+ outline: none; /* eslint-disable-line css/use-baseline */
}
-.ol-control button.btn {
+.ol-control button.btn, #help-body button.btn.help-button {
--bs-btn-padding-x: 0.5rem;
--bs-btn-padding-y: 0.5rem;
display: block;
@@ -178,7 +179,7 @@ body.inprogress {
color: var(--bs-btn-color);
background-color: var(--bs-btn-bg);
border-color: var(--bs-btn-border-color);
- outline: none;
+ outline: none; /* eslint-disable-line css/use-baseline */
}
.ol-control button.btn:hover,
.ol-control button.btn:focus-visible {
@@ -186,7 +187,7 @@ body.inprogress {
color: var(--bs-btn-hover-color);
background-color: var(--bs-btn-hover-bg);
border-color: var(--bs-btn-hover-border-color);
- outline: none;
+ outline: none; /* eslint-disable-line css/use-baseline */
}
.btn-light, .btn-dark {
--bs-btn-border-color: var(--bs-btn-hover-border-color);
@@ -194,24 +195,36 @@ body.inprogress {
#map-control-container[aria-hidden="true"],
#layer-selection-panel[aria-hidden="true"],
-#map-legend-panel[aria-hidden="true"] {
+#map-legend-panel[aria-hidden="true"],
+#measure-panel[aria-hidden="true"] {
display: none;
}
#layer-selection-panel,
-#map-legend-panel {
+#map-legend-panel,
+#measure-panel {
position: relative;
- /*min-width: min-content;*/
- /*max-width: 35%;*/
- /*width: revert;*/
- max-width: 360px;
margin-left: 0;
margin-right: var(--map-menu-spacing);
font-size: medium;
z-index: 11;
+ height: max-content;
+ max-height: 100%;
pointer-events: auto;
--bs-modal-color: var(--bs-body-color);
--bs-modal-padding: .75rem;
}
+#layer-selection-panel {
+ /*min-width: min-content;*/
+ /*max-width: 35%;*/
+ /*width: revert;*/
+ max-width: 360px;
+}
+#map-legend-panel {
+ max-width: 410px;
+}
+#measure-panel {
+ max-width: 15em;
+}
@keyframes fade-in {
from {
opacity: 0;
@@ -220,8 +233,14 @@ body.inprogress {
opacity: 1;
}
}
+#map-legend-panel .modal-body {
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text; /* eslint-disable-line css/use-baseline */
+}
#layer-selection-panel[aria-hidden="false"],
-#map-legend-panel[aria-hidden="false"] {
+#map-legend-panel[aria-hidden="false"],
+#measure-panel[aria-hidden="false"] {
animation: fade-in .25s ease-in-out both;
display: block;
}
@@ -243,17 +262,25 @@ body.inprogress {
display: none;
}
#layer-selection-panel,
- #map-legend-panel {
+ #map-legend-panel,
+ #measure-panel {
margin: var(--map-menu-spacing) 0 0 auto;
}
}
+#map-legend-button.disabled button {
+ color: rgb(from var(--bs-btn-color) r g b / calc(alpha*.25));
+ pointer-events: none;
+}
@media screen and (max-width: 200px) {
#layer-selection-button,
#map-legend-button,
+ #measure-button,
+ #age-filter-button,
#export-to-image,
#layer-selection-panel,
#map-legend-panel,
+ #measure-panel,
#map-menu .ol-full-screen {
display: none;
}
@@ -272,23 +299,150 @@ body.inprogress {
--bs-btn-padding-y: 0.4rem;
}
}
-#modal-info {
- pointer-events: auto;
+#info-modal, #help-modal {
+ /* close the modal when clicking the backdrop */
+ pointer-events: none;
-webkit-user-select: text;
-moz-user-select: text;
- user-select: text;
+ user-select: text; /* eslint-disable-line css/use-baseline */
+ --bs-modal-header-padding: 1rem;
+ --modal-info-padding-x: var(--bs-modal-header-padding);
+ --modal-info-padding-y: .5rem;
+ --modal-info-bg-light: rgba(0, 0, 0, .08);
+}
+#help-modal {
+ --modal-info-padding-x: 1rem;
+ --modal-info-padding-y: 1rem;
}
-#modal-info .modal-body ul > li {
- padding: 0.05rem 0;
+#info-modal .modal-header,
+#help-modal .modal-header {
+ padding: var(--modal-info-padding-y) var(--modal-info-padding-x);
}
-#modal-info .modal-body a {
+#info-modal .list-group-item,
+#info-modal {
+ --bs-list-group-border-width: 1px;
+}
+#info-accordion {
+ --bs-accordion-active-bg: var(--bs-accordion-bg);
+ --bs-accordion-active-color: var(--bs-body-color);
+ --bs-accordion-btn-padding-x: 0;
+ --bs-accordion-btn-padding-y: .025rem;
+ --bs-accordion-btn-focus-box-shadow: none;
+ --bs-accordion-body-padding-x: 0;
+ --bs-accordion-body-padding-y: 0;
+ --bs-accordion-btn-active-bg: var(--modal-info-bg-light);
+ padding: 0 var(--modal-info-padding-x);
+}
+#info-modal .accordion-item {
+ border: none;
+}
+#info-modal .accordion-header > .accordion-button[aria-expanded="false"]:hover {
+ background-color: rgb(from var(--modal-info-bg-light) r g b / calc(alpha*.4));
+}
+#info-modal .accordion-header > .accordion-button[aria-expanded="true"] {
+ background-color: var(--bs-accordion-btn-active-bg);
+}
+#info-modal ul.list-group > li.list-group-item {
+ padding: .3rem 0;
+ border: none;
+}
+#info-modal ul.list-group > li.list-group-item:last-child {
+ padding-bottom: var(--modal-info-padding-y);
+}
+#info-body > ul.list-group {
+ margin-top: var(--modal-info-padding-y);
+ border-top: var(--bs-list-group-border-width) solid var(--modal-info-bg-light);
+ padding: 0 var(--modal-info-padding-x);
+}
+#info-modal ul.list-group > li.list-group-item:not(:first-child) {
+ border-top: var(--bs-list-group-border-width) solid var(--modal-info-bg-light);
+}
+#info-accordion > .accordion-item:not(:last-child) ul.list-group > li.list-group-item:last-child {
+ border-bottom: var(--bs-list-group-border-width) solid var(--modal-info-bg-light);
+}
+#info-modal .modal-body ul.list-group > li.list-group-item:not(.text-muted):hover {
+ background-color: rgb(from var(--modal-info-bg-light) r g b / calc(alpha*.4));
+}
+#info-body {
+ padding: var(--modal-info-padding-y) 0;
+}
+#info-body > ul.list-group > li.list-group-item p,
+#info-modal .accordion-body ul.list-group > li.list-group-item p {
+ margin: 0;
+}
+#info-body > ul.list-group > li.list-group-item h6,
+#info-modal .accordion-body ul.list-group > li.list-group-item h6 {
+ margin: .05rem 0 .15rem 0;
+ font-size: 1.15rem;
+}
+#info-modal .modal-body a {
color: inherit;
text-decoration: none;
}
-#modal-info .modal-body a:hover {
+#info-modal .modal-body a:hover {
opacity: .8;
text-decoration: underline;
}
+#info-modal .modal-body .info-credits {
+ border-top: var(--bs-list-group-border-width) solid var(--modal-info-bg-light);
+ padding: var(--modal-info-padding-y) var(--modal-info-padding-x) 0 var(--modal-info-padding-x);
+ margin: 0;
+}
+#help-body {
+ padding: var(--modal-info-padding-y) var(--modal-info-padding-x);
+ hyphens: auto; /* eslint-disable-line css/use-baseline */
+}
+#help-body .popover-header {
+ -webkit-user-select: inherit;
+ -moz-user-select: inherit;
+ user-select: inherit; /* eslint-disable-line css/use-baseline */
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+#help-body .popover-header .popover-button {
+ height: var(--bs-body-font-size);
+ width: var(--bs-body-font-size);
+ padding: 0;
+ border: none;
+ vertical-align: middle;
+}
+#help-body button.btn.help-button {
+ --bs-btn-padding-x: .1rem;
+ --bs-btn-padding-y: .1rem;
+ width: 1.75rem;
+ height: 1.75rem;
+ display: inline-block;
+ font-size: var(--bs-body-font-size);
+ font-weight: var(--bs-body-font-weight);
+ line-height: var(--bs-body-line-height);
+ margin: 0;
+}
+#help-body .help-button {
+ pointer-events: none;
+}
+#help-body .accordion .accordion-button {
+ display: inline;
+ padding: 0;
+ opacity: .75;
+}
+#help-body .accordion .accordion-button::after {
+ display: inline-block;
+ margin: 0;
+ height: 1rem;
+ vertical-align: middle;
+}
+#help-body .help-button-description > button.btn.help-button {
+ margin-right: .5rem;
+}
+#help-body p:last-child {
+ margin-bottom: 0;
+}
+@media (min-width: 992px) {
+ #help-body {
+ text-align: justify;
+ }
+}
.ol-overlaycontainer-stopevent .modal-backdrop.show {
pointer-events: auto;
@@ -337,18 +491,146 @@ body.inprogress {
#layer-selection-panel .accordion-item .form-check > .form-check-label {
display: inline;
}
+#map-legend-panel .list-group-flush > .list-group-item {
+ border: none;
+ padding: 0;
+}
+#map-legend-panel .modal-body > ul.list-group > li.list-group-item:not(.d-none) ~ li.list-group-item:not(.d-none) {
+ border-top: var(--bs-list-group-border-width) solid var(--bs-list-group-border-color);
+ margin-top: .5rem;
+ padding-top: .5rem;
+}
+#map-legend-panel .list-group .list-group-item.h4,
+#map-legend-panel .list-group .list-group-item.h5,
+#map-legend-panel .list-group .list-group-item.h6 {
+ margin: 0 0 .25rem 0;
+}
+#map-legend-panel .list-group .list-group-item:not(.d-none) ~ .list-group-item.h5:not(.d-none) {
+ margin-top: .125rem;
+}
+#map-legend-panel .list-group .list-group-item.h4 {
+ font-size: 1.3rem;
+}
+#map-legend-panel .list-group .list-group-item.h5 {
+ font-size: 1.05rem;
+}
+#map-legend-panel .list-group .list-group-item.h6 {
+ font-size: 1rem;
+}
+#map-legend-panel .list-group .list-group-item .map-legend-symbol {
+ /* 24px */
+ height: calc(var(--bs-body-line-height) * var(--bs-body-font-size)) !important;
+}
+#map-legend-panel .list-group .list-group-item .map-legend-symbol > * {
+ width: 32px;
+ margin-right: .5rem;
+}
+#map-legend-panel .list-group .list-group-item .map-legend-symbol img {
+ margin-left: auto;
+ margin-right: auto;
+ display: block;
+}
+#map-legend-panel .list-group .list-group-item:not(.h4,.h5,.h6):hover {
+ background-color: var(--bs-list-group-action-hover-bg);
+}
+
+#age-filter-modal .modal-body .btn-group {
+ width: 100%;
+}
+#age-filter-modal .age-filter-infotext {
+ margin: 0 0 var(--bs-modal-padding) 0;
+}
+#age-filter-form .age-filter-settings .age-filter-settings-desc {
+ margin: var(--bs-modal-padding) 0;
+}
+#age-filter-form .age-filter-settings {
+ margin: 0 0 var(--bs-modal-padding) 0;
+}
+#age-filter-form .age-filter-settings p.text-muted {
+ margin: 0;
+}
+#age-filter-modal p {
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text; /* eslint-disable-line css/use-baseline */
+}
+#measure-panel .modal-header {
+ border-bottom: none;
+ padding: var(--bs-modal-padding);
+}
+#measure-panel .modal-header .h5 {
+ font-size: 1.25rem;
+ margin: 0;
+}
+#measure-panel .modal-body {
+ padding-top: 0;
+}
+#measure-panel .modal-body .btn-group > .btn.btn-lg {
+ --bs-btn-padding-y: .375rem; /* revert to non-large size */
+}
+#age-filter-modal .btn-group > .btn-check:not(:checked) + label.btn:hover,
+#measure-panel .btn-group > .btn-check:not(:checked) + label.btn:hover {
+ background: color-mix(in srgb, var(--bs-btn-bg) 35%, var(--bs-btn-hover-bg)); /* eslint-disable-line css/use-baseline */
+ color: var(--bs-btn-hover-color);
+}
+#measure-panel .measure-value {
+ font-family: Inter;
+ font-variant-numeric: tabular-nums;
+ --measure-value-padding: .75rem;
+ --measure-value-border-width: 3px;
+ --measure-value-font-size: calc(var(--bs-body-font-size) * 1.5);
+ --bs-border-opacity: .75;
+ border-width: var(--measure-value-border-width);
+ border-style: solid;
+ border-color: rgb(from var(--bs-secondary) r g b / .75);
+ border-radius: var(--bs-border-radius-lg);
+ background: rgb(from var(--bs-secondary-bg-subtle) r g b / calc(alpha*.30));
+ height: calc(var(--bs-body-line-height) * var(--bs-body-font-size) * var(--measure-value-font-size)
+ + 2*var(--measure-value-padding) + 2*var(--measure-value-border-width));
+ padding: var(--measure-value-padding) var(--measure-value-padding);
+ margin: calc(var(--measure-value-padding)*1.5) 0;
+ display: flex;
+ justify-content: flex-end; /* overflow on the left */
+ overflow: hidden;
+ -webkit-user-select: text;
+ -moz-user-select: text;
+ user-select: text; /* eslint-disable-line css/use-baseline */
+}
+#measure-panel .measure-value :not(.measure-unit) {
+ font-size: var(--measure-value-font-size);
+ font-weight: 400;
+}
+#measure-panel .measure-value .measure-unit:before {
+ font-size: calc(.5*(var(--measure-value-font-size) + var(--bs-body-font-size)));
+ content: '\00A0'; /* U+00A0 NO-BREAK SPACE */
+}
+#measure-panel .measure-value .measure-unit {
+ font-size: var(--bs-body-font-size);
+ line-height: calc(var(--bs-body-line-height) * var(--measure-value-font-size));
+}
+#measure-panel .btn-group {
+ width: 100%;
+}
.popover {
--bs-popover-header-padding-x: .75rem;
--bs-popover-header-padding-y: .5rem;
- --bs-popover-body-padding-x: .45rem;
+ --bs-popover-body-offset-x: .3rem;
+ --bs-popover-body-padding-x: calc(var(--bs-popover-header-padding-x) - var(--bs-popover-body-offset-x));
--bs-popover-body-padding-y: .5rem;
--bs-popover-header-bg: var(--bs-popover-bg);
--bs-popover-zindex: 1000;
- --bs-popover-max-width: 450px;
+ --bs-popover-max-width: min(450px, 90vw);
width: var(--bs-popover-max-width);
pointer-events: auto;
}
+.popover.popover-maximized {
+ --bs-popover-header-padding-x: 1rem;
+ --bs-popover-header-padding-y: 1rem;
+ --bs-popover-body-padding-y: 1rem;
+ --bs-popover-body-offset-x: .5rem;
+ --bs-popover-font-size: 1rem;
+}
.popover.inprogress > .popover-header,
.popover.inprogress > .popover-body {
filter: opacity(70%);
@@ -357,42 +639,63 @@ body.inprogress {
.popover-header {
-webkit-user-select: none;
-moz-user-select: none;
- user-select: none;
+ user-select: none; /* eslint-disable-line css/use-baseline */
--bs-heading-color: var(--bs-popover-header-color);
}
+.popover-header h5, .popover-header h6 {
+ line-height: var(--bs-body-line-height);
+ vertical-align: middle;
+ margin: 0;
+}
.popover-body {
-webkit-user-select: text;
-moz-user-select: text;
- user-select: text;
+ user-select: text; /* eslint-disable-line css/use-baseline */
max-height: 40vh;
overflow: auto auto;
}
+.popover.popover-maximized .popover-body {
+ max-height: calc(100% - 2*var(--bs-popover-header-padding-y) - var(--bs-body-line-height)*var(--bs-popover-font-size) - var(--bs-popover-border-width));
+}
.popover-body h6, .popover-body h5 {
- margin-bottom: .25rem;
+ margin-bottom: var(--bs-popover-body-padding-y);
margin-left: calc(var(--bs-popover-header-padding-x) - var(--bs-popover-body-padding-x));
margin-right: calc(var(--bs-popover-header-padding-x) - var(--bs-popover-body-padding-x));
}
+.popover.popover-maximized .popover-body h5,
+.popover.popover-maximized .popover-body h6 {
+ font-size: calc(1.2 * var(--bs-popover-font-size));
+}
.popover-body .table {
--bs-table-bg: none;
- --bs-table-hover-bg: rgba(0, 0, 0, 0.04);
+ --bs-table-hover-bg: rgba(0, 0, 0, .04);
+ --bs-table-bg-padding-y: .1rem;
margin: 0;
}
-.popover-body table > tbody > tr > td:first-child {
+.popover.popover-maximized .popover-body .table {
+ --bs-table-hover-bg: none;
+ --bs-table-bg-padding-y: .5rem;
+}
+.popover.popover-maximized .popover-body .table tr:nth-of-type(2n+1) {
+ background: rgba(0, 0, 0, .05);
+}
+.popover-body table > tbody > tr > th[scope="row"] {
font-style: italic;
- width: 9.75em;
padding-right: .75em;
+ font-weight: normal;
}
+.popover .popover-body table > tbody > tr > th[scope="row"] {
+ width: 35%;
+}
+.popover-body table > tbody > tr > th[scope="row"],
.popover-body table > tbody > tr > td {
- padding: 0.1rem 0.3rem;
+ padding: var(--bs-table-bg-padding-y) var(--bs-popover-body-offset-x);
}
.feature-attr-mrr-license-id, .feature-attr-dnr, .feature-objid, .feature-orgnr {
font-family: var(--bs-font-monospace);
word-wrap: break-word;
}
-.popover-header h6 {
- margin: 0;
-}
/* inspired from bootstrap's .btn-close */
.popover-header .popover-button {
box-sizing: content-box;
@@ -410,7 +713,7 @@ body.inprogress {
opacity: .75;
}
.popover-header .popover-button:focus {
- outline: 0;
+ outline: 0; /* eslint-disable-line css/use-baseline */
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
opacity: 1;
}
diff --git a/vite.config.js b/vite.config.js
index 68076e9..2e513f9 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,3 +1,6 @@
+/* eslint-disable */
+import path from 'path';
+
export default {
build: {
sourcemap: true,
@@ -18,9 +21,10 @@ export default {
},
resolve: {
alias: {
- "~bootstrap": "node_modules/bootstrap",
- "~bootstrap-icons": "node_modules/bootstrap-icons",
- "~ol": "node_modules/ol",
+ "~bootstrap": path.resolve(__dirname, "node_modules/bootstrap"),
+ "~bootstrap-icons": path.resolve(__dirname, "node_modules/bootstrap-icons"),
+ "~ol": path.resolve(__dirname, "node_modules/ol"),
+ "~fontsource": path.resolve(__dirname, "node_modules/@fontsource"),
}
},
server: {