From 1c0748176997d2e915faae3c6fcf5634f4fb0582 Mon Sep 17 00:00:00 2001 From: Guilhem Moulin Date: Sun, 20 Oct 2024 00:19:56 +0200 Subject: Adjust to new backend. Account for changes in the tooling: - Use multiple vector layers and a metadata.json manifest; - Automatically reload the manifest after 6h so we don't miss updates; - Fetch feature properties from separate attribute files; and - Use the original feature geometry as overlay to avoid clipping. --- main.js | 968 +++++++++++++++++++++++++++++++++----------------------------- style.css | 4 + 2 files changed, 524 insertions(+), 448 deletions(-) diff --git a/main.js b/main.js index a711f2c..0cf51c9 100644 --- a/main.js +++ b/main.js @@ -36,6 +36,7 @@ import { createXYZ } from 'ol/tilegrid.js'; import VectorLayer from 'ol/layer/Vector.js'; import VectorSource from 'ol/source/Vector.js'; +import GeoJSON from 'ol/format/GeoJSON.js'; import CircleStyle from 'ol/style/Circle.js'; import Fill from 'ol/style/Fill.js'; @@ -503,6 +504,7 @@ view.on('change', function(event) { }); +/* TODO: this should really be refactored… */ const layers = { mrr_appr_ec: { popoverTitle: 'Bearbetningskoncession \u2013 beviljad', @@ -510,12 +512,13 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Ägare', 'Owner'], - ['Area', 'Area'], - ['Giltig från', 'Valid from'], - ['Giltig till', 'Valid to'], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Giltig från', 'ValidFrom'], + ['Giltig till', 'ValidTo'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -536,10 +539,12 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Sökande', 'Applicant'], - ['Area', 'Area'], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Ansökningsdatum', 'ApplicationDate'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -561,14 +566,14 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Ägare', 'Owner'], - ['Licence id', 'Licence id', { classes: ['feature-attr-mrr-license-id'] }], - ['Area', 'Area'], - ['Giltig från', 'Valid from'], - ['Giltig till', 'Valid to'], - ['Diary nr', 'Diary nr', { classes: ['feature-attr-dnr'] }], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Licence id', 'LicenceID', { classes: ['feature-attr-mrr-license-id'] }], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Giltig från', 'ValidFrom'], + ['Giltig till', 'ValidTo'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -589,12 +594,12 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Sökande', 'Applicant'], - ['Area', 'Area'], - ['Ansökningsdatum', 'Application date'], - ['Diary nr', 'Diary nr', { classes: ['feature-attr-dnr'] }], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Ansökningsdatum', 'ApplicationDate'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -616,14 +621,14 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Ägare', 'Owner'], - ['Licence id', 'Licence id', { classes: ['feature-attr-mrr-license-id'] }], - ['Area', 'Area'], - ['Giltig från', 'Valid from'], - ['Giltig till', 'Valid to'], - ['Diary nr', 'Diary nr', { classes: ['feature-attr-dnr'] }], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Licence id', 'LicenceID', { classes: ['feature-attr-mrr-license-id'] }], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Giltig från', 'ValidFrom'], + ['Giltig till', 'ValidTo'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -644,12 +649,12 @@ const layers = { ['Namn', 'Name'], ['Koncessionsmineral', 'Mineral'], ['Sökande', 'Applicant'], - ['Area', 'Area'], - ['Ansökningsdatum', 'Application date'], - ['Diary nr', 'Diary nr', { classes: ['feature-attr-dnr'] }], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Ansökningsdatum', 'ApplicationDate'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -669,10 +674,12 @@ const layers = { popoverTitle: 'Markanvisning till koncession', popover: [ ['Namn', 'Name'], - ['Area', 'Area'], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Beslutsdatum', 'DecisionDate'], + ['Diarienummer', 'DiaryNr', { classes: ['feature-attr-dnr'] }], + ['Areal', 'GeomArea', { fn: 'area' }], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -691,13 +698,14 @@ const layers = { popoverTitle: 'Gällande torvkoncession', popover: [ ['Namn', 'Name'], + //['Mineral', 'Mineral'], ['Ägare', 'Owner'], - ['Area', 'Area'], - ['Giltig från', 'Valid from'], - ['Giltig till', 'Valid to'], - ['Kommun', 'Municipality'], - ['Län', 'County'], - ['Senast uppdaterad', 'Last updated'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Giltig från', 'ValidFrom'], + ['Giltig till', 'ValidTo'], + //['Kommun', 'Municipality'], + //['Län', 'County'], + ['Senast uppdaterad', 'LastUpdated'], ], style: [0, .1, .5, .5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5].map(function(width, z) { return new Style({ @@ -713,11 +721,12 @@ const layers = { }), }, - svk_lines: { + svk_ledningar: { popoverTitle: 'Kraftledning (befintlig)', popover: [ - ['Förläggn', 'FÖRLÄGGN'], - ['Spänning', 'SPÄNNING', { unit: 'kV' }], + ['Förläggning', 'Placement'], + ['Spänning', 'Voltage', { unit: 'kV' }], + ['Ledlängd', 'GeomLength', { fn: 'length' }], ], style: [1, 1.5, 2, 2, 2, 2, 3, 4, 5, 6, 8, 10].map(function(width) { return new Style({ @@ -729,7 +738,7 @@ const layers = { }); }), }, - svk_pylons: { + svk_stolpar: { style: [undefined, undefined, undefined, undefined, undefined] .concat([3, 4, 5, 6, 8, 10, 15].map(function(radius) { return new Style({ @@ -743,12 +752,12 @@ const layers = { }); })), }, - svk_planned: { + svk_transmissionsnatsprojekt: { popoverTitle: 'Transmissionsnätsprojekt', popover: [ - ['Projektnamn', 'name'], - ['Spänning', 'voltage', { unit: 'kV' }], - ['Länk', 'url', { fn: function(v) { + ['Projektnamn', 'Name'], + ['Spänning', 'Voltage', { unit: 'kV' }], + ['Länk', 'Url', { fn: function(v) { const a = document.createElement('a'); a.href = v; a.target = '_blank'; @@ -769,7 +778,7 @@ const layers = { }); }), }, - svk_stations: { + svk_stationer: { style: [3, 4, 5, 6, 7, 8.5, 10].map(function(radius) { return new Style({ zIndex: 50, @@ -800,21 +809,22 @@ const layers = { vbk_area_current: { popoverTitle: 'Projekteringsområde för vindbruk', popover: [ - ['Projektnamn', 'PROJNAMN'], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Aktuella verk', 'ANTALVERK'], - ['Antal ej koordinatsatta verk', 'AntalejXY', { fn: (v) => v || 0 }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Planerad byggstart', 'PBYGGSTART'], - ['Planerat drifttagande', 'PDRIFT'], - ['Andringsansokan', 'Andringsansokan'], + ['Projektnamn', 'Projektnamn'], + ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }], + ['Aktuella verk', 'AntalVerk'], + ['Antal ej koordinatsatta verk', 'AntalEjXY'], + ['Areal', 'GeomArea', { fn: 'area' }], + ['Beräknad årsproduktion', 'Calprod', { unit: 'GWh' }], + ['Planerad byggstart', 'PlaneradByggstart'], + ['Planerat drifttagande', 'PlaneratDrift'], + ['Andringsansokan', 'AndringsansokanPagar'], ['Under Byggnation', 'UnderByggnation'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNR', { classes: ['feature-orgnr'] }], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Senast uppdaterat', 'ArendeStatusUppdaterat'], + ['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({ @@ -832,20 +842,21 @@ const layers = { vbk_area_notcurrent: { popoverTitle: 'Projekteringsområde för vindbruk \u2013 ej aktuell', popover: [ - ['Projektnamn', 'PROJNAMN'], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Aktuella verk', 'ANTALVERK'], - ['Antal ej koordinatsatta verk', 'AntalejXY', { fn: (v) => v || 0 }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Planerad byggstart', 'PBYGGSTART'], - ['Planerat drifttagande', 'PDRIFT'], - ['Andringsansokan', 'Andringsansokan'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNR', { classes: ['feature-orgnr'] }], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Senast uppdaterat', 'ArendeStatusUppdaterat'], + ['Projektnamn', 'Projektnamn'], + ['Områdes-ID', 'OmrID', { classes: ['feature-objid'] }], + ['Aktuella verk', 'AntalVerk'], + ['Antal ej koordinatsatta verk', 'AntalEjXY'], + ['Areal', 'GeomArea', { 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({ @@ -864,27 +875,27 @@ const layers = { vbk_station_completed: { popoverTitle: 'Vindkraftverk \u2013 uppfört', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Uppförandedatum', 'UPPFORT'], - ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand_TIDSBEGRANS_DAT'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -900,25 +911,25 @@ const layers = { vbk_station_processed: { popoverTitle: 'Vindkraftverk \u2013 handlagt', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -934,26 +945,26 @@ const layers = { vbk_station_approved: { popoverTitle: 'Vindkraftverk \u2013 beviljat', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand_TIDSBEGRANS_DAT'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -969,26 +980,26 @@ const layers = { 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', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand_TIDSBEGRANS_DAT'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -1004,26 +1015,26 @@ const layers = { vbk_station_rejected: { popoverTitle: 'Vindkraftverk \u2013 avslagit/nekat', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Miljöbalken tillstånd tidsbegränsning', 'MB_Tillstand_TIDSBEGRANS_DAT'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -1039,26 +1050,26 @@ const layers = { vbk_station_dismounted: { popoverTitle: 'Vindkraftverk \u2013 nedmonterat', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Uppförandedatum', 'UPPFORT'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -1074,25 +1085,25 @@ const layers = { vbk_station_appealed: { popoverTitle: 'Vindkraftverk \u2013 överklagat', popover: [ - ['Verk-ID', 'VERKID', { classes: ['feature-objid'] }], - ['Områdes-ID', 'OMRID', { classes: ['feature-objid'] }], - ['Projektnamn', 'PROJNAMN'], - ['Status', 'STATUS'], - ['Handlingstyp', 'HANDLINGSTYP'], - ['Totalhöjd', 'TOTALHOJD', { unit: 'm' }], - ['Navhöjd', 'NAVHOJD', { unit: 'm' }], - ['Rotordiameter', 'ROTDIAMETER', { unit: 'm' }], - ['Maxeffekt', 'MAXEFFEKT', { unit: 'MW' }], - ['Beräknad årsproduktion', 'CALPROD', { unit: 'GWh' }], - ['Fabrikat', 'FABRIKAT'], - ['Modell', 'MODELL'], - ['Organisationsnamn', 'ORGNAMN'], - ['Organisationsnummer', 'ORGNMR', { classes: ['feature-orgnr'] }], - ['Placering', 'PLACERING'], - ['Kommun', 'KOMNAMN'], - ['Län', 'LANSNAMN'], - ['Elområde', 'EL_NAMN'], - ['Datum för senaste uppdatering av verk', 'SenasteUppdatering'], + ['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({ @@ -1113,7 +1124,6 @@ const layers = { popoverTitle: 'Utförd avverkning', popover: [ ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }], - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], ['Registeringsår', 'ArendeAr'], ['Skogstyp', 'Skogstyp'], ['Areal anmält', 'AnmaldHa', { unit: 'ha' }], @@ -1123,9 +1133,9 @@ const layers = { ['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', 'Arealha', { unit: 'ha' }], + //['Kommun', 'Kommun'], + //['Län', 'Lan'], + ['Areal för ytan', 'GeomArea', { fn: 'area' }], ], style: [0, 0, 0, 0, 0, .5, .75, 1, 1, 1, 1, 1].map(function(width, z) { return new Style({ @@ -1147,7 +1157,6 @@ const layers = { popoverTitle: 'Avverkningsanmälansområde', popover: [ ['Ärendebeteckning', 'Beteckn', { classes: ['feature-objid'] }], - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], ['Inkom datum', 'Inkomdatum'], ['Registeringsår', 'ArendeAr'], ['Skogstyp', 'Skogstyp'], @@ -1156,9 +1165,10 @@ const layers = { ['Areal plantering', 'SkogsodlHa', { unit: 'ha', fn: (v) => v === 0 ? '' : v }], ['Avverkningssäsong', 'AvvSasong'], ['Avverkningstyp', 'Avverktyp'], - ['Kommun', 'Kommun'], - ['Län', 'Lan'], - ['Ärendestatus', 'ArendeStat'], + ['Ä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) { @@ -1201,14 +1211,11 @@ const layers = { }), }, - st_renbete: { + ren_betesomraden: { popoverTitle: 'Samebyarnas betesområde', popover: [ ['Sameby', 'NAMN'], ['Samebys typ', 'SAMEBY_TYP'], - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], - ['Sameby-ID', 'SAMEBY_ID', { classes: ['feature-objid'] }], - ['By-ID', 'BY_ID', { classes: ['feature-objid'] }], ['Signatur', 'SIGNATUR'], ['Aktualitet', 'AKTUALITET'], ], @@ -1227,24 +1234,20 @@ const layers = { }); }), }, - st_flyttled: { + 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'], - ['Sameby #1 Nr', 'BYNR1', { classes: ['feature-objid'], fn: (v) => v === 0 ? '' : v }], - ['Sameby #2 Nr', 'BYNR2', { classes: ['feature-objid'], fn: (v) => v === 0 ? '' : v }], - ['Sameby #3 Nr', 'BYNR3', { classes: ['feature-objid'], fn: (v) => v === 0 ? '' : v }], - ['Led-ID', 'LED_ID', { classes: ['feature-objid'], fn: (v) => v === 0 ? '' : v }], - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], ['Riksintresse', 'RIKSINTR'], ['Fast led', 'FAST_LED'], ['Aktualitet', 'AKTUALITET'], ['Signatur', 'SIGNATUR'], - ['Globalt ID', 'GlobalID', { classes: ['feature-objid'] }], + ['Ledlängd', 'GeomLength', { fn: 'length' }], ], style: [.75, 1, 1.5, 2, 3, 4, 5, 5, 6, 7, 8, 10].map(function(width, z) { return new Style({ @@ -1257,14 +1260,13 @@ const layers = { }); }), }, - st_riks_ren: { + ren_riks_ren: { popoverTitle: 'Riksintresse rennäring', popover: [ - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], ['Lagrum', 'LAGRUM'], ['Aktualitet', 'AKTUALITET'], ['Signatur', 'SIGNATUR'], - ['Globalt ID', 'GlobalID', { classes: ['feature-objid'] }], + ['Areal', 'GeomArea', { 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'); @@ -1293,10 +1295,9 @@ const layers = { }); }), }, - st_riks_ren_core: { + ren_omr_riks: { popoverTitle: '(Kärn)områden av riksintresse rennäring', popover: [ - ['Objekt-ID', 'OBJECTID', { classes: ['feature-objid'] }], ['Områdes-ID', 'OMR_NR', { classes: ['feature-objid'] }], ['Länk', 'LANK'], ['Årets runt', 'ARET_RUNT'], @@ -1304,9 +1305,7 @@ const layers = { ['Ansvarig', 'ANSVARIG'], ['Aktualitet', 'AKTUALITET'], ['Signatur', 'SIGNATUR'], - ['Globalt ID', 'GlobalID', { classes: ['feature-objid'] }], - ['Area', 'AREA_HA', { unit: 'ha' }], - ['Länskod', 'LANSKOD'], + ['Areal', 'GeomArea', { fn: 'area' }], ], style: [.5, .5, 1, 1, 1, 1.5, 1.5, 1.5, 2, 2, 2, 2].map(function(width, z) { return new Style({ @@ -1325,19 +1324,18 @@ const layers = { /* Documentation at * https://www.smhi.se/polopoly_fs/1.34541!/dammprod%202013_3%2C%20beskrivning%2C%20SVAR2012_2.pdf * */ - smhi_dam: { + misc_dammar: { popoverTitle: 'Damm', popover: [ - ['Dammenhetens namn', 'dnamn'], - ['Dammanläggningens namn', 'namn'], - ['Länsnr', 'lst_objid', { classes: ['feature-objid'] }], - ['Status', 'status', { fn: (v) => v === 0 ? '' : v === 1 ? 'Befintlig damm' : v === 2 ? 'Fd. damm' : v }], - ['Regleringstyp', 'regl_typ', { fn: (v) => v === 0 ? '' : v }], - ['Byggår', 'byggar', { fn: (v) => v === 0 ? '' : v }], - ['Dammhöjd', 'dammhojd', { unit: 'm', fn: (v) => v === 0 ? '' : v }], - ['Krönlängd', 'kron', { unit: 'm', fn: (v) => v === 0 ? '' : v }], - ['Fiskväg', 'fiskvag', { fn: (v) => - v === 0 ? '' : + ['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' : @@ -1348,11 +1346,10 @@ const layers = { v === 8 ? 'Okänd typ' : v === 9 ? 'Ingen' : v === 10 ? 'Annan' : - v }], - ['Huvudavrinningsområdesnummer', 'haro', { classes: ['feature-objid'] } ], - ['Vattendistrikt', 'RBD', { classes: ['feature-objid'] } ], - ['Verksamhet', 'verksmht', { fn: (v) => - v === 0 ? '' : + '' }], + ['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' : @@ -1361,13 +1358,12 @@ const layers = { v === 6 ? 'Spegeldamm' : v === 7 ? 'Historisk' : v === 8 ? 'Övrigt' : - v }], - ['Högsta dämningsgräns', 'dg', { unit: 'm', fn: (v) => v === 0 ? '' : v }], - ['Lägsta sänkningsgräns', 'sg', { unit: 'm', fn: (v) => v === 0 ? '' : v }], - ['Magasinsyta', 'my', { unit: 'km²', fn: (v) => v === 0 ? '' : v }], - ['Reglerbar volym', 'my', { unit: 'Mm³', fn: (v) => v === 0 ? '' : v }], - ['Kommentar', 'kommentar'], - ['Damm-ID', 'dammid', { classes: ['feature-objid'] }], + '' }], + ['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({ @@ -1386,11 +1382,11 @@ const layers = { }), }, - gigafactories: { + misc_gigafactories: { popoverTitle: 'Stor industrisatsning', popover: [ - ['Namn', 'name'], - ['Länk', 'url', { fn: function(v) { + ['Namn', 'Name'], + ['Länk', 'Url', { fn: function(v) { const a = document.createElement('a'); a.href = v; a.target = '_blank'; @@ -1436,25 +1432,25 @@ const layerHierarchy = [ children: [ { text: 'Kraftledningar (befintliga)', - layer: ['svk_lines', 'svk_pylons'], + layer: ['svk_ledningar', 'svk_stolpar'], }, { text: 'Stationer', - layer: 'svk_stations', + layer: 'svk_stationer', }, { text: 'Transmissionsnätsprojekt ', - layer: 'svk_planned', + layer: 'svk_transmissionsnatsprojekt', }, ], }, { text: 'Stora industrisatsningar', - layer: 'gigafactories', + layer: 'misc_gigafactories', }, { text: 'Dammar', - layer: 'smhi_dam', + layer: 'misc_dammar', }, { text: 'Mineralrättigheter', @@ -1579,53 +1575,24 @@ const layerHierarchy = [ children: [ { text: 'Betesområden', - layer: 'st_renbete', + layer: 'ren_betesomraden', }, { text: 'Flyttled', - layer: 'st_flyttled', + layer: 'ren_flyttled', }, { text: 'Riksintressen', - layer: 'st_riks_ren', + layer: 'ren_riks_ren', }, { text: '(Kärn)områden av riksintresse', - layer: 'st_riks_ren_core', + layer: 'ren_omr_riks', }, ] - } + }, ]; -const vectorSource = new VectorTile({ - url: '/tiles/a/{z}/{x}/{y}.pbf', - format: new MVT(), - projection: projection, - wrapX: false, - transition: 0, - tileGrid: createXYZ({ - extent: extent, - tileSize: 1024, - maxResolution: 1024, /* = 1048576/1024 */ - minZoom: 0, - maxZoom: 9, - }), -}); -const vectorSource2 = new VectorTile({ - url: '/tiles/b/{z}/{x}/{y}.pbf', - format: new MVT(), - projection: projection, - wrapX: false, - transition: 0, - tileGrid: createXYZ({ - extent: extent, - tileSize: 1024, - maxResolution: 1024, /* = 1048576/1024 */ - minZoom: 0, - maxZoom: 9, - }), -}); - const styles = (function() { const searchParams = new URLSearchParams(location.hash.substring(1)); const layersParams = searchParams.has('layers') ? searchParams.get('layers').split(' ') : []; @@ -1637,49 +1604,79 @@ const styles = (function() { }, {}); })(); -const vectorLayer = new VectorTileLayer({ - source: vectorSource, - /* 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: false, - style: function(feature, resolution) { - const style = styles[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]; - } - }, -}); -map.addLayer(vectorLayer); - -const vectorLayer2 = new VectorTileLayer({ - source: vectorSource2, - renderMode: 'hybrid', - declutter: false, - visible: false, - style: function(feature, resolution) { - const style = styles[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]; - } - }, +const vectorLayers = {}; +const tileGrid = createXYZ({ + extent: extent, + tileSize: 1024, + maxResolution: 1024, /* = 1048576/1024 */ + minZoom: 0, + maxZoom: 9, }); -map.addLayer(vectorLayer2); +function readLayerMap() { + fetch('/tiles/metadata.json', { + signal: AbortSignal.timeout(30000) + }).then((resp) => resp.json()) + .then((data) => { + const xyz = '/{z}/{x}/{y}.pbf'; + Object.entries(data.layermap).forEach(function([k, baseurl]) { + if (vectorLayers[k] !== undefined) { + const l = vectorLayers[k]; + if (l.baseurl !== baseurl) { + console.log(`Changing ${k}'s baseurl to ${baseurl} from ${l.baseurl}`); + l.baseurl = baseurl; + l.vectorLayer.getSource().setUrl(baseurl + xyz); + } + } else { + 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: baseurl + 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); + vectorLayers[k] = { + baseurl: baseurl, + vectorLayer: vectorLayer, + }; + } + }); + }); +}; + +/* re-read after 6h so we don't miss updates; the interval should never + * be higher than the Cache-Control value on the tiles and metadata */ +readLayerMap(); +setInterval(readLayerMap, 21600000); + /* layer selection panel */ (function() { @@ -1729,12 +1726,22 @@ map.addLayer(vectorLayer2); } }); }; - /* TODO refactor */ - const layerList1 = Object.keys(layers).filter((l) => !l.startsWith('sks_') && !l.startsWith('st_')); - const layerList2 = Object.keys(layers).filter((l) => l.startsWith('sks_') || l.startsWith('st_')); const fixLayerVisibility = function() { - vectorLayer .setVisible(layerList1.some((lyr) => styles[lyr] !== undefined)); - vectorLayer2.setVisible(layerList2.some((lyr) => styles[lyr] !== undefined)); + const result = {} + Object.keys(layers).forEach(function(lyr) { + const layerGroup = lyr.split('_', 2)[0]; + if (result[layerGroup] === undefined) { + result[layerGroup] = false; + } + result[layerGroup] ||= styles[lyr] !== undefined; + }); + Object.entries(result).forEach(function([lyr, visible]) { + const v = vectorLayers[lyr]; + if (v !== undefined) { + //console.log(lyr, visible); + v.vectorLayer.setVisible(visible); + } + }); }; const onClickFunction = function(layerList, event) { const searchParams = new URLSearchParams(location.hash.substring(1)); @@ -1755,8 +1762,12 @@ map.addLayer(vectorLayer2); } setIndeterminateAndChecked(layerHierarchy); fixLayerVisibility(); - vectorSource.changed(); - vectorSource2.changed(); + + layerList + .map((l) => l.split('_', 2)[0]) + .filter((v, i, arr) => arr.indexOf(v) === i) + .forEach((l) => vectorLayers[l].vectorLayer.getSource().changed()); + searchParams.set('layers', layersParams.join(' ')); location.hash = '#' + searchParams.toString(); }; @@ -1923,6 +1934,7 @@ map.addLayer(vectorLayer2); map.addLayer(new VectorLayer({ source: featureOverlaySource, + zIndex: 65535, style: new Style({ stroke: new Stroke({ color: 'rgba(0, 255, 255, .8)', @@ -2124,103 +2136,163 @@ map.addLayer(vectorLayer2); btnExpand.setAttribute('aria-label', btnExpand.title); map.forEachFeatureAtPixel(event.pixel, function(feature, layer) { - const properties = feature.getProperties(); - const def = layers[properties.layer]; + const layerGroup = layer.get('layerGroup'); + let properties = feature.getProperties(); + const def = layers[layerGroup + '_' + properties.layer]; if (def === undefined || def.popover === undefined) { /* skip layers which didn't opt-in for popover */ return; } - /* 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); - - def.popover.forEach(function([desc, key, opts]) { - let v = properties[key]; - if (opts === undefined) { - opts = {}; - } - if (opts.fn !== undefined) { - v = opts.fn(v); - } - if (opts.unit !== undefined && v !== undefined && v !== '') { - v += '\u202F' + opts.unit; - } - if (v === undefined) { - v = document.createTextNode(''); - } else if (!(v instanceof HTMLElement)) { - v = document.createTextNode(v); - } + /* get filename and index of the original feature from the FID */ + const fid_dv = new DataView(new ArrayBuffer(4), 0); + fid_dv.setUint32(0, feature.getId(), false); - const tr = document.createElement('tr'); - tbody.appendChild(tr); + const fid_idx = fid_dv.getUint16(2); + const buf = new Uint8Array(fid_dv.buffer.slice(0,2)) + const fid_p = buf.reduce((t, x) => t + x.toString(16).padStart(2, '0'), '') - 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); + if (features.length === 0) { + document.body.classList.add('progress'); } - // console.log(properties); - content.appendChild(table); - features.push({feature: feature, formattedContent: content}); - - pageCount.innerHTML = features.length.toString(); - if (features.length == 2) { - /* there are ≥2 features, render prev/pre buttons */ - btnNext.classList.remove('d-none', 'disabled'); - btnPrev.classList.remove('d-none'); - pageNode.classList.remove('d-none'); - } + const url = vectorLayers[layerGroup].baseurl + '/attr/' + fid_p + '.json'; + fetch(url) + .then((resp) => resp.json()) + .then((data) => { + document.body.classList.remove('progress'); + properties = data[fid_idx] + //console.log(properties); + + /* build new feature from the metadata */ + const geojson = { id: feature.getId() } + geojson.properties = properties + const geom = properties['geom'] + delete properties['geom'] + geojson.geometry = geom + geojson.type = 'Feature' + const feature2 = new GeoJSON().readFeature(geojson); + + /* 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); + + 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)); + } + }); - if (features.length > 1) { - /* we're already showing the first feature */ - return; - } + 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); + } + + content.appendChild(table); + features.push({feature: feature2, formattedContent: content}); + + pageCount.innerHTML = features.length.toString(); + if (features.length == 2) { + /* there are ≥2 features, render prev/pre buttons */ + btnNext.classList.remove('d-none', 'disabled'); + btnPrev.classList.remove('d-none'); + pageNode.classList.remove('d-none'); + } + + if (features.length > 1) { + /* we're already showing the first feature */ + return; + } + + if (popover === null || popover.tip === null) { + /* create a new popover if we're not already showing one */ + pageNum.innerHTML = (featureNum + 1).toString(); + popupOverlay.setPosition(event.coordinate); + + popover = new Popover(popup, { + template: '', + title: header, + content: content, + html: true, + placement: 'right', + fallbackPlacements: ['right', 'left', 'bottom', 'top'], + container: container0, + }); + popover.show(); + featureOverlaySource.addFeature(feature2); + } + else if (popover.tip.classList.contains('popover-detached')) { + refreshPopover(); + } + }) + .catch(function() { + document.body.classList.remove('progress'); + }); - if (popover === null || popover.tip === null) { - /* create a new popover if we're not already showing one */ - pageNum.innerHTML = (featureNum + 1).toString(); - popupOverlay.setPosition(event.coordinate); - - popover = new Popover(popup, { - template: '', - title: header, - content: content, - html: true, - placement: 'right', - fallbackPlacements: ['right', 'left', 'bottom', 'top'], - container: container0, - }); - popover.show(); - featureOverlaySource.addFeature(feature); - } - else if (popover.tip.classList.contains('popover-detached')) { - refreshPopover(); - } }, { hitTolerance: 5, checkWrapped: false, - layerFilter: (l) => l === vectorLayer || l === vectorLayer2, + layerFilter: (l) => l.get('layerGroup') !== undefined, }); if (features.length === 0 && popover !== null && popover.tip !== null) { diff --git a/style.css b/style.css index 2d7e723..88afbca 100644 --- a/style.css +++ b/style.css @@ -24,6 +24,10 @@ html, body { --map-container-padding: 1rem; } +body.progress { + cursor: wait; +} + #map-control-container { --map-menu-spacing: .5em; position: absolute; -- cgit v1.2.3