aboutsummaryrefslogtreecommitdiffstats
path: root/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'main.js')
-rw-r--r--main.js8019
1 files changed, 5099 insertions, 2920 deletions
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;
+ })();
+})();