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.html210
-rw-r--r--main.js8132
-rw-r--r--package-lock.json1764
-rw-r--r--package.json14
-rw-r--r--style.css376
-rw-r--r--vite.config.js10
10 files changed, 7197 insertions, 3342 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 165f47a..1522c6a 100644
--- a/index.html
+++ b/index.html
@@ -11,68 +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>.
+ <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
- <a href="https://sametinget.se" target="_blank">Sametinget</a>.
+
+ <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 4917e79..9840746 100644
--- a/main.js
+++ b/main.js
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright © 2024 Guilhem Moulin <info@guilhem.se>
+ * Copyright © 2024-2025 Guilhem Moulin <info@guilhem.se>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -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,9 +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 GeoJSON from 'ol/format/GeoJSON.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';
@@ -45,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';
@@ -52,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
@@ -70,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
@@ -88,83 +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 featureOverlaySource = new VectorSource();
+ 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 */
@@ -187,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 */
@@ -199,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');
@@ -236,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');
};
@@ -250,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');
- }
- };
+ btn.classList.add('btn', 'btn-light');
- 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');
+ 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 */
@@ -343,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() {
- featureOverlaySource.clear(true);
- 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);
@@ -368,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() {
- featureOverlaySource.clear(true);
- 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);
@@ -386,7 +375,7 @@ if (window.location === window.parent.location) {
if (exp !== undefined) {
exp.classList.remove('d-none');
}
- })
+ });
}
/* export/download button */
@@ -405,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);
@@ -436,2490 +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();
+ }
+ });
+
+ panel.addEventListener('hidden.bs.modal', function() {
+ btn.classList.replace('btn-dark', 'btn-light');
+ btn.setAttribute('aria-expanded', 'false');
+ backdrop.classList.remove('modal-backdrop', 'show');
+ });
+
+ btn.onclick = function() {
+ modal.show();
+ };
+
+ /* 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;
+ }
+ }
- btn.onclick = function(event) {
- modal.toggle();
+ return [panel, btn, modal];
};
-})();
-/* we're all set, show the control container now */
-container.setAttribute('aria-hidden', 'false');
+ /* info button */
+ (function() {
+ const [panel, btn, modal] = add_button({
+ id: 'info',
+ title: 'Källor och licensinformation',
+ bi: 'info-lg',
+ });
-view.on('change', function(event) {
- featureOverlaySource.clear(true);
- const popover = Popover.getInstance(popup);
- if (popover !== null) {
- popover.dispose();
- }
+ 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 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();
-});
+ 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);
+ }
-/* TODO: this should really be refactored… */
-const layers = {
- mrr_appr_ec: {
- popoverTitle: 'Bearbetningskoncession \u2013 beviljad',
- popover: [
- ['Namn', 'Name'],
- ['Koncessionsmineral', 'Mineral'],
- ['Ägare', 'Owner'],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Giltig från', 'ValidFrom'],
- ['Giltig till', 'ValidTo'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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', 'Applicant'],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Ansökningsdatum', 'ApplicationDate'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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', 'Owner'],
- ['Licence id', 'LicenceID', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Giltig från', 'ValidFrom'],
- ['Giltig till', 'ValidTo'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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', 'Applicant'],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Ansökningsdatum', 'ApplicationDate'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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', 'Owner'],
- ['Licence id', 'LicenceID', { classes: ['feature-attr-mrr-license-id'] }],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Giltig från', 'ValidFrom'],
- ['Giltig till', 'ValidTo'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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', 'Applicant'],
- ['Areal', 'GeomArea', { fn: 'area' }],
- ['Ansökningsdatum', 'ApplicationDate'],
- ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }],
- //['Kommun', 'Municipality'],
- //['Län', 'County'],
- ['Senast uppdaterad', 'LastUpdated'],
- ],
- 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: [<