/*********************************************************************** * Copyright © 2024 Guilhem Moulin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . **********************************************************************/ import Map from 'ol/Map.js'; import View from 'ol/View.js'; import TileLayer from 'ol/layer/Tile.js'; import WMTS from 'ol/source/WMTS.js'; import WMTSTileGrid from 'ol/tilegrid/WMTS.js'; import Attribution from 'ol/control/Attribution.js'; import FullScreen from 'ol/control/FullScreen.js'; import ScaleLine from 'ol/control/ScaleLine.js'; import Zoom from 'ol/control/Zoom.js'; import ZoomSlider from 'ol/control/ZoomSlider.js'; import proj4 from 'proj4'; import {get as getProjection} from 'ol/proj.js'; import {register as registerProjection} from 'ol/proj/proj4.js'; import './style.css'; 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'); /* Lantmäteriet uses a tile-scheme where the origin (top-left corner) is * at N8500000 E-1200000 (SWEREF99 TM), where each tile is 256x256 * pixels, and where the resolution at level 0 is 4096m per pixel * (each side is 1048.576km long). * * https://www.lantmateriet.se/globalassets/geodata/geodatatjanster/tb_twk_visning_cache_v1.1.0.pdf * https://www.lantmateriet.se/globalassets/geodata/geodatatjanster/tb_twk_visning-oversiktlig_v1.0.3.pdf * * We set the extent to a 4x4 tiles square at level 2 (1024px = * 1048.576km per 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]; /* 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 violate its current terms of use (as * of January 2024 it's not CC0 open data). See * * https://www.lantmateriet.se/sv/om-lantmateriet/Rattsinformation/upphovsratt-och-publicering-av-lantmateriets-geografiska-information/ * https://www.lantmateriet.se/sv/kartor/vara-karttjanster/min-karta/#anchor-2 * https://help.locusmap.eu/topic/support-for-swedish-lantmateriets-min-karta-wms * * More precise background maps might be available in the future * as open data, though: * * https://www.lantmateriet.se/sv/om-lantmateriet/press/nyheter/lantmateriets-arbete-mot-oppna-data-i-full-gang/ */ const baseMapSource = new WMTS({ // XXX the 'layer' parameter should be passed in the options // dictionary (like style and version), but there is no setLayer() // method to switch from/to the toned down map url: 'https://minkarta.lantmateriet.se/map/topowebbcache?layer=topowebb', 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_cache_v1.1.0.pdf tileSize: 256, origin: [-1200000, 8500000], resolutions: [4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, .5], matrixIds: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], }), projection: projection, wrapX: false, crossOrigin: 'anonymous', attributions: '© Lantmäteriet', }); const map = new Map({ controls: [ new Zoom({ zoomInTipLabel: 'Zooma in', zoomInLabel: (function() { const label = document.createElement('i'); label.classList.add('bi', 'bi-plus'); return label; })(), zoomOutTipLabel: 'Zooma ut', zoomOutLabel: (function() { const label = document.createElement('i'); label.classList.add('bi', 'bi-dash'); return label; })(), }), new ZoomSlider({ }), new ScaleLine({ units: 'metric', minWidth: 150, }), new Attribution({ collapsible: true, collapsed: true, tipLabel: 'Information', label: (function() { const label = document.createElement('i'); label.classList.add('bi', 'bi-info'); return label; })(), }), ], 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, 4, 2, 1, .5], constrainResolution: false, }), layers: [ new TileLayer({ source: baseMapSource }), ], target: document.getElementById('map'), }); /* add the menu to the viewport */ const menu = document.createElement('div'); menu.classList.add('d-none'); menu.id = 'map-menu'; map.getViewport().appendChild(menu); /* layer selection button */ (function() { const div = document.createElement('div'); menu.append(div); div.classList.add('ol-unselectable', 'ol-control'); const btn = document.createElement('button'); div.appendChild(btn); btn.type = 'button'; btn.ariaExpanded = 'false'; btn.title = 'Byt kartlager'; const i = document.createElement('i'); btn.appendChild(i); i.classList.add('bi', 'bi-stack'); btn.onclick = function(event) { const panel = document.getElementById('layer-selection-panel'); if (btn.ariaExpanded === 'true') { panel.classList.add('d-none'); btn.ariaExpanded = 'false'; } else { panel.classList.remove('d-none'); btn.ariaExpanded = 'true'; } }; })(); /* fullscreen control */ (function() { const label = document.createElement('i'); label.classList.add('bi', 'bi-fullscreen'); const labelActive = document.createElement('i'); labelActive.classList.add('bi', 'bi-fullscreen-exit'); map.addControl(new FullScreen({ label: label, labelActive: labelActive, tipLabel: 'Växla helskärmsläge', keys: true, target: menu, })); })(); menu.classList.remove('d-none'); document.getElementById('layer-topowebb_nedtonad').onchange = function(event) { const layer = event.target.checked ? 'topowebb_nedtonad' : 'topowebb'; baseMapSource.setUrl('https://minkarta.lantmateriet.se/map/topowebbcache?LAYER=' + layer); };