From 9e73f2603ab78b2406e13b9676f14f4ef2138744 Mon Sep 17 00:00:00 2001 From: "D. Berge" Date: Tue, 12 Sep 2023 11:20:50 +0200 Subject: [PATCH] Implement user layers on map view. The user layers are defined in the project configuration under `imports.map.layers`. Multiple layers may be defined and each layer may consist of one or more GeoJSON files. Files are retrieved via the /files/ API endpoint. --- lib/www/client/source/src/views/Map.vue | 170 +++++++++++++++--------- 1 file changed, 110 insertions(+), 60 deletions(-) diff --git a/lib/www/client/source/src/views/Map.vue b/lib/www/client/source/src/views/Map.vue index ea251d6..993e34b 100644 --- a/lib/www/client/source/src/views/Map.vue +++ b/lib/www/client/source/src/views/Map.vue @@ -267,59 +267,6 @@ const layers = { opacity: 0.5 } } - }), - - "Background": L.geoJSON(null,{ - style (feature) { - const style = { - stroke: undefined, - color: "grey", - weight: 2, - opacity: 0.5, - lineCap: undefined, - lineJoin: undefined, - dashArray: undefined, - dashOffset: undefined, - fill: undefined, - fillColor: "lightgrey", - fillOpacity: 0.5, - fillRule: undefined - }; - - for (let key in style) { - switch (key) { - case "color": - style[key] = feature.properties?.colour ?? feature.properties?.color ?? style[key]; - break; - case "fillColor": - style[key] = feature.properties?.fillColour ?? feature.properties?.fillColor ?? style[key]; - break; - default: - style[key] = feature.properties?.[key] ?? style[key]; - } - - if (typeof style[key] === "undefined") { - delete style[key]; - } - } - - return style; - - return Object.fromEntries(Object.entries(style).map( ([key, defaultValue]) => { - function value () { - switch (key) { - case "color": - return feature.properties?.colour ?? feature.properties?.color ?? defaultValue; - case "fillColor": - return feature.properties?.fillColour ?? feature.properties?.fillColor ?? defaultValue; - default: - return feature.properties?.[key] ?? defaultValue; - } - } - - return [key, value()]; - })); - } }) }; @@ -425,16 +372,11 @@ export default { ? `/project/${this.$route.params.project}/gis/final/line` : `/project/${this.$route.params.project}/gis/final/point?${query.toString()}`; } - }, - { - layer: layers["Background"], - url: (query = "") => { - return `/project/${this.$route.params.project}/gis/layer/Background`; - } } ], labels: {}, - hashMarker: null + hashMarker: null, + references: {} }; }, @@ -739,6 +681,108 @@ export default { this.labels = labelSet; }, + + removeUserLayers () { + map.eachLayer( layer => { + if (layer.options.userLayer === true) { + console.log("Removing", layer); + layer.eachLayer( sublayer => { + const idx = this.layerRefreshConfig.findIndex(i => i.layer == layer); + if (idx != -1) { + this.layerRefreshConfig.splice(idx, 1); + } + }); + map.removeLayer(layer); + this.references.layerControl.removeLayer(layer); + } + }); + }, + + async addUserLayers (userLayers) { + + const options = { + userLayer: true, + style (feature) { + const style = { + stroke: undefined, + color: "grey", + weight: 2, + opacity: 0.5, + lineCap: undefined, + lineJoin: undefined, + dashArray: undefined, + dashOffset: undefined, + fill: undefined, + fillColor: "lightgrey", + fillOpacity: 0.5, + fillRule: undefined + }; + + for (let key in style) { + switch (key) { + case "color": + style[key] = feature.properties?.colour ?? feature.properties?.color ?? style[key]; + break; + case "fillColor": + style[key] = feature.properties?.fillColour ?? feature.properties?.fillColor ?? style[key]; + break; + default: + style[key] = feature.properties?.[key] ?? style[key]; + } + + if (typeof style[key] === "undefined") { + delete style[key]; + } + } + + return style; + } + }; + + const userLayerGroups = {}; + userLayers.forEach(layer => { + if (!(layer.name in userLayerGroups)) { + userLayerGroups[layer.name] = []; + } + userLayerGroups[layer.name].push(layer); + }); + + for (let userLayerName in userLayerGroups) { + const userLayerGroup = userLayerGroups[userLayerName]; + + const layer = L.featureGroup(null, {userLayer: true, userLayerGroup: true, userLayerName}); + userLayerGroup.forEach(l => { + const sublayer = L.geoJSON(null, {...options, userLayerName}); + layer.addLayer(sublayer); + sublayer.on('add', ({target}) => { + this.refreshLayers([target]) + }); + + const refreshConfig = { + layer: sublayer, + url: (query = "") => { + return `/files/${l.path}`; + } + }; + + this.layerRefreshConfig.push(refreshConfig); + }); + + layer.on('add', ({target}) => { + this.refreshLayers(target.getLayers()) + }); + this.references.layerControl.addOverlay(layer, `${userLayerName}`); + } + }, + + async fetchUserLayers () { + const url = `/project/${this.$route.params.project}/gis/layer`; + const userLayers = await this.api([url]) || []; + + this.removeUserLayers(); + this.addUserLayers(userLayers); + }, + ...mapActions(["api"]) }, @@ -818,6 +862,9 @@ export default { const layerControl = L.control.layers(tileMaps, layers).addTo(map); const scaleControl = L.control.scale().addTo(map); + this.references.layerControl = layerControl; + this.references.scaleControl = scaleControl; + if (init.position) { map.setView(init.position.slice(1), init.position[0]); } else { @@ -845,10 +892,13 @@ export default { map.on('layeradd', this.updateURL); map.on('layerremove', this.updateURL); + this.layerRefreshConfig.forEach( l => { l.layer.on('add', ({target}) => this.refreshLayers([target])); }); + this.fetchUserLayers(); + if (init.position) { this.refreshLayers(); } else {