Mercurial > gemma
view client/src/store/map.js @ 3430:6994602d2935
fairway availibilty: Implemented for streches and sections.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Thu, 23 May 2019 17:28:14 +0200 |
parents | c0f5f62343c9 |
children | 45eab8e9b580 |
line wrap: on
line source
/* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * * Bernhard Reiter <bernhard.reiter@intevation.de> * * Markus Kottländer <markus@intevation.de> * * Thomas Junk <thomas.junk@intevation.de> */ import Draw from "ol/interaction/Draw"; import { Stroke, Style, Fill, Circle } from "ol/style"; import { fromLonLat } from "ol/proj"; import { getLength, getArea } from "ol/sphere"; import { transformExtent } from "ol/proj"; import bbox from "@turf/bbox"; import app from "@/main"; import { HTTP } from "@/lib/http"; import Feature from "ol/Feature"; import Point from "ol/geom/Point"; import { Vector as VectorLayer } from "ol/layer"; import { toLonLat } from "ol/proj"; // initial state const init = () => { return { openLayersMaps: [], syncedMaps: [], syncedView: null, mapPopup: null, mapPopupEnabled: true, initialLoad: true, extent: { lat: 6155376, lon: 1819178, zoom: 11 }, identifiedFeatures: [], // map features identified by clicking on the map identifiedCoordinates: null, currentMeasurement: null, // distance or area from line-/polygon-/cutTool lineToolEnabled: false, polygonToolEnabled: false, cutToolEnabled: false, isolinesLegendImgDataURL: "", differencesLegendImgDataURL: "" }; }; export default { init, namespaced: true, state: init(), getters: { openLayersMap: state => id => { return state.openLayersMaps.find( map => map.getTarget() === "map-" + (id || "main") ); }, filteredIdentifiedFeatures: state => { return state.identifiedFeatures.filter(f => f.getId()); } }, mutations: { initialLoad: (state, initialLoad) => { state.initialLoad = initialLoad; }, extent: (state, extent) => { state.extent = extent; }, addOpenLayersMap: (state, map) => { state.openLayersMaps.push(map); }, removeOpenLayersMap: (state, map) => { let index = state.openLayersMaps.findIndex( m => m.getTarget() === map.getTarget() ); if (index !== -1) { state.openLayersMaps.splice(index, 1); } }, syncedMaps: (state, ids) => { state.syncedMaps = ids; }, syncedView: (state, view) => { state.syncedView = view; }, mapPopup: (state, popup) => { state.mapPopup = popup; }, mapPopupEnabled: (state, enabled) => { state.mapPopupEnabled = enabled; }, setIdentifiedFeatures: (state, identifiedFeatures) => { state.identifiedFeatures = identifiedFeatures; }, addIdentifiedFeatures: (state, identifiedFeatures) => { state.identifiedFeatures = state.identifiedFeatures.concat( identifiedFeatures ); }, identifiedCoordinates: (state, coordinates) => { state.identifiedCoordinates = coordinates; }, setCurrentMeasurement: (state, measurement) => { state.currentMeasurement = measurement; }, lineToolEnabled: (state, enabled) => { state.lineToolEnabled = enabled; state.openLayersMaps.forEach(m => { let tool = m .getInteractions() .getArray() .find(i => i.get("id") === "linetool"); if (tool) { tool.setActive(enabled); } }); }, polygonToolEnabled: (state, enabled) => { state.polygonToolEnabled = enabled; state.openLayersMaps.forEach(m => { let tool = m .getInteractions() .getArray() .find(i => i.get("id") === "polygontool"); if (tool) { tool.setActive(enabled); } }); }, cutToolEnabled: (state, enabled) => { state.cutToolEnabled = enabled; state.openLayersMaps.forEach(m => { let tool = m .getInteractions() .getArray() .find(i => i.get("id") === "cuttool"); if (tool) { tool.setActive(enabled); } }); }, isolinesLegendImgDataURL: (state, isolinesLegendImgDataURL) => { state.isolinesLegendImgDataURL = isolinesLegendImgDataURL; }, differencesLegendImgDataURL: (state, differencesLegendImgDataURL) => { state.differencesLegendImgDataURL = differencesLegendImgDataURL; } }, actions: { openLayersMap({ state, commit, dispatch }, map) { const drawVectorSrc = map.getLayer("DRAWTOOL").getSource(); const cutVectorSrc = map.getLayer("CUTTOOL").getSource(); // init line tool const lineTool = new Draw({ source: drawVectorSrc, type: "LineString", maxPoints: 2, stopClick: true }); lineTool.set("id", "linetool"); lineTool.setActive(false); lineTool.on("drawstart", () => { state.openLayersMaps.forEach(m => { m.getLayer("DRAWTOOL") .getSource() .clear(); }); commit("setCurrentMeasurement", null); }); lineTool.on("drawend", event => { commit("setCurrentMeasurement", { quantity: app.$gettext("Length"), unitSymbol: "m", value: Math.round(getLength(event.feature.getGeometry()) * 10) / 10 }); commit("application/showIdentify", true, { root: true }); }); // init polygon tool const polygonTool = new Draw({ source: drawVectorSrc, type: "Polygon", maxPoints: 50, stopClick: true }); polygonTool.set("id", "polygontool"); polygonTool.setActive(false); polygonTool.on("drawstart", () => { state.openLayersMaps.forEach(m => { m.getLayer("DRAWTOOL") .getSource() .clear(); }); commit("setCurrentMeasurement", null); }); polygonTool.on("drawend", event => { const areaSize = getArea(event.feature.getGeometry()); commit("setCurrentMeasurement", { quantity: app.$gettext("Area"), unitSymbol: areaSize > 100000 ? "km²" : "m²", value: areaSize > 100000 ? Math.round(areaSize / 1000) / 1000 // convert into 1 km² == 1000*1000 m² and round to 1000 m² : Math.round(areaSize) }); commit("application/showIdentify", true, { root: true }); }); // init cut tool const cutTool = new Draw({ source: cutVectorSrc, type: "LineString", maxPoints: 2, stopClick: true, style: new Style({ stroke: new Stroke({ color: "#444", width: 2, lineDash: [7, 7] }), image: new Circle({ fill: new Fill({ color: "#333" }), stroke: new Stroke({ color: "#fff", width: 1.5 }), radius: 6 }) }) }); cutTool.set("id", "cuttool"); cutTool.setActive(false); cutTool.on("drawstart", () => { state.openLayersMaps.forEach(m => { m.getLayer("CUTTOOL") .getSource() .clear(); }); }); cutTool.on("drawend", event => { commit("fairwayprofile/selectedCut", null, { root: true }); dispatch("fairwayprofile/cut", event.feature, { root: true }); }); map.addInteraction(lineTool); map.addInteraction(cutTool); map.addInteraction(polygonTool); // If there are multiple maps and you enable one of the draw tools, when // moving the mouse to another map, the cursor for the draw tool stays // visible in the first map, right next to the edge where the cursor left // the map. So here we disable all draw layers except the ones in the map // that the user currently hovering with the mouse. map.getTargetElement().addEventListener("mouseenter", () => { if ( state.lineToolEnabled || state.polygonToolEnabled || state.cutToolEnabled ) { state.openLayersMaps.forEach(m => { let lineTool = m .getInteractions() .getArray() .find(i => i.get("id") === "linetool"); let polygonTool = m .getInteractions() .getArray() .find(i => i.get("id") === "polygontool"); let cutTool = m .getInteractions() .getArray() .find(i => i.get("id") === "cuttool"); if (lineTool) lineTool.setActive(false); if (polygonTool) polygonTool.setActive(false); if (cutTool) cutTool.setActive(false); }); let lineTool = map .getInteractions() .getArray() .find(i => i.get("id") === "linetool"); let polygonTool = map .getInteractions() .getArray() .find(i => i.get("id") === "polygontool"); let cutTool = map .getInteractions() .getArray() .find(i => i.get("id") === "cuttool"); if (lineTool && state.lineToolEnabled) lineTool.setActive(true); if (polygonTool && state.polygonToolEnabled) polygonTool.setActive(true); if (cutTool && state.cutToolEnabled) cutTool.setActive(true); } }); commit("addOpenLayersMap", map); }, initIdentifyTool({ state, rootState, commit, dispatch }, map) { map.on(["singleclick", "dblclick"], event => { commit( "identifiedCoordinates", toLonLat(event.coordinate, map.getView().getProjection()) ); state.mapPopup.setPosition(undefined); commit("setIdentifiedFeatures", []); // checking our WFS layers var features = map.getFeaturesAtPixel(event.pixel, { hitTolerance: 7 }); if (features) { let all = []; let bottlenecks = []; let gauges = []; let stretches = []; let sections = []; for (let feature of features) { // avoid identifying the same feature twice if (all.findIndex(f => f.getId() === feature.getId()) === -1) all.push(feature); let id = feature.getId(); // RegExp.prototype.test() works with number, str and undefined // get selected bottleneck if (/^bottlenecks/.test(id)) bottlenecks.push(feature); // get selected gauge if (/^gauges/.test(id)) gauges.push(feature); // get selected stretch if (/^stretches/.test(id)) stretches.push(feature); // get selected section if (/^sections/.test(id)) sections.push(feature); } commit("setIdentifiedFeatures", all); // Decide whether we open a related dialog immediately or show the // popup with possible options first. // The following cases require a manual decision via the popup because // the targeted feature is not clear. if ( (bottlenecks.length || gauges.length > 1 || stretches.length > 1 || sections.length > 1 || (sections.length && stretches.length) || (gauges.length && sections.length) || (gauges.length && stretches.length)) && state.mapPopupEnabled ) { state.mapPopup.setMap(map); state.mapPopup.setPosition(event.coordinate); } // The following scenarios lead to a distinct action without popup. if ( gauges.length === 1 && !bottlenecks.length && !sections.length && !stretches.length ) { commit("application/showGauges", true, { root: true }); dispatch("gauges/selectedGaugeISRS", gauges[0].get("isrs_code"), { root: true }); } if ( stretches.length === 1 && !sections.length && !bottlenecks.length && !gauges.length ) { if (rootState.imports.selectedStretchId === stretches[0].getId()) { commit("imports/selectedStretchId", null, { root: true }); } else { commit("imports/selectedStretchId", stretches[0].getId(), { root: true }); commit("fairwayavailability/type", "stretches", { root: true }); commit("application/showFairwayDepth", true, { root: true }); dispatch("moveToFeauture", { feature: stretches[0], zoom: 17 }); } } if ( sections.length === 1 && !stretches.length && !bottlenecks.length && !gauges.length ) { if (rootState.imports.selectedSectionId === sections[0].getId()) { commit("imports/selectedSectionId", null, { root: true }); } else { commit("imports/selectedSectionId", sections[0].getId(), { root: true }); commit("fairwayavailability/type", "sections", { root: true }); commit("application/showFairwayDepth", true, { root: true }); dispatch("moveToFeauture", { feature: sections[0], zoom: 17 }); } } } // DEBUG output and example how to remove the GeometryName /* for (let feature of features) { console.log("Identified:", feature.getId()); for (let key of feature.getKeys()) { if (key != feature.getGeometryName()) { console.log(key, feature.get(key)); } } } */ let currentResolution = map.getView().getResolution(); var waterwayAxisSource = map.getLayer("WATERWAYAXIS").getSource(); var waxisUrl = waterwayAxisSource.getGetFeatureInfoUrl( event.coordinate, currentResolution /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "application/json" } ); if (waxisUrl) { // cannot directly query here because of SOP HTTP.get(waxisUrl, { headers: { "X-Gemma-Auth": localStorage.getItem("token") } }).then(response => { let features = response.data.features.map(f => { let feat = new Feature({ geometry: new Point(f.geometry.coordinates) }); feat.setId(f.id); feat.setProperties(f.properties); return feat; }); commit("addIdentifiedFeatures", features); }); } var waterwayAreaSource = map.getLayer("WATERWAYAREA").getSource(); var wareaUrl = waterwayAreaSource.getGetFeatureInfoUrl( event.coordinate, currentResolution /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "application/json" } ); if (wareaUrl) { HTTP.get(wareaUrl, { headers: { "X-Gemma-Auth": localStorage.getItem("token") } }).then(response => { let features = response.data.features.map(f => { let feat = new Feature({ geometry: new Point(f.geometry.coordinates) }); feat.setId(f.id); feat.setProperties(f.properties); return feat; }); commit("addIdentifiedFeatures", features); }); } var dmSource = map.getLayer("DISTANCEMARKS").getSource(); var dmUrl = dmSource.getGetFeatureInfoUrl( event.coordinate, currentResolution /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "application/json" } ); if (dmUrl) { HTTP.get(dmUrl + "&BUFFER=5", { headers: { "X-Gemma-Auth": localStorage.getItem("token") } }).then(response => { let features = response.data.features.map(f => { let feat = new Feature({ geometry: new Point(f.geometry.coordinates) }); feat.setId(f.id); feat.setProperties(f.properties); return feat; }); commit("addIdentifiedFeatures", features); }); } var dmaSource = map.getLayer("DISTANCEMARKSAXIS").getSource(); var dmaUrl = dmaSource.getGetFeatureInfoUrl( event.coordinate, currentResolution /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "application/json" } ); if (dmaUrl) { HTTP.get(dmaUrl + "&BUFFER=5", { headers: { "X-Gemma-Auth": localStorage.getItem("token") } }).then(response => { let features = response.data.features.map(f => { let feat = new Feature({ geometry: new Point(f.geometry.coordinates) }); feat.setId(f.id); feat.setProperties(f.properties); return feat; }); commit("addIdentifiedFeatures", features); }); } // trying the GetFeatureInfo way for WMS var iecdisSource = map.getLayer("INLANDECDIS").getSource(); var iecdisUrl = iecdisSource.getGetFeatureInfoUrl( event.coordinate, currentResolution /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "text/plain" } ); if (iecdisUrl) { // cannot directly query here because of SOP console.log("GetFeatureInfo url:", iecdisUrl); } }); }, refreshLayers({ state }) { state.openLayersMaps.forEach(map => { let layers = map.getLayers().getArray(); for (let i = 0; i < layers.length; i++) { let layer = layers[i]; if ( layer instanceof VectorLayer && layer.get("source").loader_.name != "VOID" ) { layer.getSource().clear(true); layer.getSource().refresh({ force: true }); } } }); }, moveToBoundingBox( { state }, { boundingBox, zoom, preventZoomOut, duration } ) { const extent = transformExtent(boundingBox, "EPSG:4326", "EPSG:3857"); const currentZoom = state.syncedView.getZoom(); zoom = zoom || currentZoom; state.syncedView.fit(extent, { maxZoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom, duration: duration || 700 }); }, moveToFeauture({ dispatch }, { feature, zoom, preventZoomOut }) { const boundingBox = feature.hasOwnProperty("geometry") ? bbox(feature.geometry) : feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326") .getExtent(); dispatch("moveToBoundingBox", { boundingBox, zoom, preventZoomOut }); }, moveMap({ state }, { coordinates, zoom, preventZoomOut }) { const currentZoom = state.syncedView.getZoom(); zoom = zoom || currentZoom; state.syncedView.animate({ zoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom, center: fromLonLat(coordinates, state.syncedView.getProjection()), duration: 700 }); } } };