Mercurial > gemma
view client/src/store/map.js @ 2997:707e1bc24d93
yarn lint --fix
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Wed, 10 Apr 2019 15:22:35 +0200 |
parents | 888a3454c3ae |
children | 44493664d40e |
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 { HTTP } from "../lib/http"; import TileWMS from "ol/source/TileWMS"; import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer"; import OSM from "ol/source/OSM"; import Draw from "ol/interaction/Draw"; import { Icon, Stroke, Style, Fill, Text, Circle } from "ol/style"; import VectorSource from "ol/source/Vector"; import Point from "ol/geom/Point"; import { bbox as bboxStrategy } from "ol/loadingstrategy"; import { HTTP } from "@/lib/http"; import { fromLonLat } from "ol/proj"; import { getLength, getArea } from "ol/sphere"; import { unByKey } from "ol/Observable"; import { getCenter } from "ol/extent"; import { transformExtent } from "ol/proj"; import bbox from "@turf/bbox"; import app from "@/main"; const moveMap = ({ view, extent, zoom, preventZoomOut }) => { const currentZoom = view.getZoom(); zoom = zoom || currentZoom; view.fit(extent, { maxZoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom, duration: 700 }); }; const blue1 = new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, 0.8)", lineDash: [2, 4], lineCap: "round", width: 2 }), fill: new Fill({ color: "rgba(240, 230, 0, 0.2)" }) }); const blue2 = new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, 0.9)", lineDash: [3, 6], lineCap: "round", width: 2 }), fill: new Fill({ color: "rgba(240, 230, 0, 0.1)" }) }); const blue3 = new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, 1.0)", width: 2 }), fill: new Fill({ color: "rgba(255, 255, 255, 0.4)" }) }); const yellow1 = new Style({ stroke: new Stroke({ color: "rgba(230, 230, 10, .8)", width: 4 }), fill: new Fill({ color: "rgba(230, 230, 10, .3)" }) }); const yellow2 = new Style({ stroke: new Stroke({ color: "rgba(250, 200, 0, .8)", width: 2 }), fill: new Fill({ color: "rgba(250, 200, 10, .3)" }) }); const yellow3 = new Style({ stroke: new Stroke({ color: "rgba(250, 240, 10, .9)", width: 5 }), fill: new Fill({ color: "rgba(250, 240, 0, .7)" }) }); const red1 = new Style({ stroke: new Stroke({ color: "rgba(255, 0, 0, 1)", width: 4 }) }); const circleBlue = new Style({ image: new Circle({ radius: 5, fill: new Fill({ color: "rgba(255, 0, 0, 0.1)" }), stroke: new Stroke({ color: "blue", width: 1 }) }) }); const textFW1 = new Style({ text: new Text({ font: 'bold 12px "Open Sans", "sans-serif"', placement: "line", fill: new Fill({ color: "black" }), text: "LOS: 1" //, zIndex: 10 }) }); const textFW2 = new Style({ text: new Text({ font: 'bold 12px "Open Sans", "sans-serif"', placement: "line", fill: new Fill({ color: "black" }), text: "LOS: 2" //, zIndex: 10 }) }); const textFW3 = new Style({ text: new Text({ font: 'bold 12px "Open Sans", "sans-serif"', placement: "line", fill: new Fill({ color: "black" }), text: "LOS: 3" //, zIndex: 10 }) }); const stretchesStyle = feature => { let style = yellow2; if (feature.get("highlighted")) { style = yellow3; } return style; }; const fwd1Style = () => { return [blue1, textFW1]; }; const fwd2Style = () => { return [blue2, textFW2]; }; const fwd3Style = () => { return [blue3, textFW3]; }; const bottleneckStyle = () => { return yellow1; }; const bottleneckStatusStyle = (feature, resolution, isLegend) => { let styles = []; if ((feature.get("fa_critical") && resolution > 15) || isLegend) { let bnCenter = getCenter(feature.getGeometry().getExtent()); styles.push( new Style({ geometry: new Point(bnCenter), image: new Icon({ src: require("@/assets/marker-bottleneck-critical.png"), anchor: [0.5, 0.5], scale: isLegend ? 0.5 : 1 }) }) ); } if (feature.get("fa_critical") && !isLegend) { styles.push(red1); } return styles; }; const dmaStyle = (feature, resolution) => { if (resolution < 10) { var s = circleBlue; if (resolution < 6) { s.setText( new Text({ offsetY: 12, font: '10px "Open Sans", "sans-serif"', fill: new Fill({ color: "black" }), text: (feature.get("hectometre") / 10).toString() }) ); } return s; } else { return []; } }; const gaugeStyle = (feature, resolution, isLegend) => { return [ new Style({ image: new Icon({ src: require("@/assets/marker-gauge.png"), anchor: [0.5, isLegend ? 0.5 : 1], scale: isLegend ? 0.5 : 1 }), text: new Text({ font: '10px "Open Sans", "sans-serif"', offsetY: 8, fill: new Fill({ color: "white" }), text: feature.get("objname") }) }), new Style({ text: new Text({ font: '10px "Open Sans", "sans-serif"', offsetY: 7, offsetX: -1, fill: new Fill({ color: "black" }), text: feature.get("objname") }) }) ]; }; // initial state const init = () => { return { openLayersMap: null, initialLoad: true, extent: { lat: 6155376, lon: 1819178, zoom: 11 }, identifyTool: null, // event binding (singleclick, dblclick) identifiedFeatures: [], // map features identified by clicking on the map currentMeasurement: null, // distance or area from line-/polygon-/cutTool lineTool: null, // open layers interaction object (Draw) polygonTool: null, // open layers interaction object (Draw) cutTool: null, // open layers interaction object (Draw) isolinesLegendImgDataURL: "", differencesLegendImgDataURL: "", layers: { OPENSTREETMAP: new TileLayer({ label: "Open Streetmap", visible: true, source: new OSM() }), INLANDECDIS: new TileLayer({ label: "Inland ECDIS chart Danube", visible: true, source: new TileWMS({ preload: 1, url: "https://service.d4d-portal.info/wms/", crossOrigin: "anonymous", params: { LAYERS: "d4d", VERSION: "1.1.1", TILED: true } }) }), WATERWAYAREA: new VectorLayer({ label: "Waterway Area", visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: new Style({ stroke: new Stroke({ color: "rgba(0, 102, 0, 1)", width: 2 }) }) }), STRETCHES: new VectorLayer({ label: "Stretches", visible: false, source: new VectorSource({ strategy: bboxStrategy }), style: stretchesStyle }), FAIRWAYDIMENSIONSLOS1: new VectorLayer({ label: "LOS 1 Fairway Dimensions", visible: false, source: new VectorSource(), style: fwd1Style }), FAIRWAYDIMENSIONSLOS2: new VectorLayer({ label: "LOS 2 Fairway Dimensions", visible: false, source: new VectorSource(), style: fwd2Style }), FAIRWAYDIMENSIONSLOS3: new VectorLayer({ label: "LOS 3 Fairway Dimensions", visible: true, source: new VectorSource(), style: fwd3Style }), WATERWAYAXIS: new VectorLayer({ label: "Waterway Axis", visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, .5)", lineDash: [5, 5], width: 2 }) }), // TODO: Set layer in layertree active/inactive depending on // resolution. maxResolution: 5, minResolution: 0 }), WATERWAYPROFILES: new VectorLayer({ label: "Waterway Profiles", visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, .5)", lineDash: [5, 5], width: 2 }) }), maxResolution: 2.5, minResolution: 0 }), BOTTLENECKS: new VectorLayer({ label: "Bottlenecks", visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: bottleneckStyle }), BOTTLENECKISOLINE: new TileLayer({ label: "Bottleneck isolines", visible: false, source: new TileWMS({ preload: 0, projection: "EPSG:3857", url: window.location.origin + "/api/internal/wms", params: { LAYERS: "sounding_results_contour_lines_geoserver", VERSION: "1.1.1", TILED: true }, tileLoadFunction: function(tile, src) { // console.log("calling for", tile, src); HTTP.get(src, { headers: { "X-Gemma-Auth": localStorage.getItem("token") }, responseType: "blob" }).then(response => { tile.getImage().src = URL.createObjectURL(response.data); }); } // TODO tile.setState(TileState.ERROR); }) }), DIFFERENCES: new TileLayer({ label: "Bottleneck Differences", visible: false, source: new TileWMS({ preload: 0, projection: "EPSG:4326", url: window.location.origin + "/api/internal/wms", params: { LAYERS: "sounding_differences", VERSION: "1.1.1", TILED: true }, tileLoadFunction: function(tile, src) { // console.log("calling for", tile, src); HTTP.get(src, { headers: { "X-Gemma-Auth": localStorage.getItem("token") }, responseType: "blob" }).then(response => { tile.getImage().src = URL.createObjectURL(response.data); }); } // TODO tile.setState(TileState.ERROR); }) }), BOTTLENECKSTATUS: new VectorLayer({ label: "Critical Bottlenecks", forLegendStyle: { point: true, resolution: 16 }, visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: bottleneckStatusStyle }), DISTANCEMARKS: new VectorLayer({ label: "Distance marks", forLegendStyle: { point: true, resolution: 8 }, visible: false, source: new VectorSource({ strategy: bboxStrategy }) }), DISTANCEMARKSAXIS: new VectorLayer({ label: "Distance marks, Axis", forLegendStyle: { point: true, resolution: 8 }, visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: dmaStyle }), GAUGES: new VectorLayer({ label: "Gauges", forLegendStyle: { point: true, resolution: 8 }, visible: true, source: new VectorSource({ strategy: bboxStrategy }), style: gaugeStyle, maxResolution: 100, minResolution: 0 }), DRAWTOOL: new VectorLayer({ label: "Draw Tool", visible: true, source: new VectorSource({ wrapX: false }), style: function(feature) { // adapted from OpenLayer's LineString Arrow Example var geometry = feature.getGeometry(); var styles = [ // linestring new Style({ stroke: new Stroke({ color: "#369aca", width: 2 }) }) ]; if (geometry.getType() === "LineString") { geometry.forEachSegment(function(start, end) { var dx = end[0] - start[0]; var dy = end[1] - start[1]; var rotation = Math.atan2(dy, dx); // arrows styles.push( new Style({ geometry: new Point(end), image: new Icon({ // we need to make sure the image is loaded by Vue Loader src: require("@/assets/linestring_arrow.png"), // fiddling with the anchor's y value does not help to // position the image more centered on the line ending, as the // default line style seems to be slightly uncentered in the // anti-aliasing, but the image is not placed with subpixel // precision anchor: [0.75, 0.5], rotateWithView: true, rotation: -rotation }) }) ); }); } return styles; } }), CUTTOOL: new VectorLayer({ label: "Cut Tool", visible: true, source: new VectorSource({ wrapX: false }), style: function(feature) { // adapted from OpenLayer's LineString Arrow Example var geometry = feature.getGeometry(); var styles = [ // linestring new Style({ stroke: new Stroke({ color: "#333333", width: 2, lineDash: [7, 7] }) }) ]; if (geometry.getType() === "LineString") { geometry.forEachSegment(function(start, end) { var dx = end[0] - start[0]; var dy = end[1] - start[1]; var rotation = Math.atan2(dy, dx); // arrows styles.push( new Style({ geometry: new Point(end), image: new Icon({ // we need to make sure the image is loaded by Vue Loader src: require("@/assets/linestring_arrow_grey.png"), // fiddling with the anchor's y value does not help to // position the image more centered on the line ending, as the // default line style seems to be slightly uncentered in the // anti-aliasing, but the image is not placed with subpixel // precision anchor: [0.75, 0.5], rotateWithView: true, rotation: -rotation }) }) ); }); } return styles; } }) } }; }; export default { init, namespaced: true, state: init(), getters: { filteredIdentifiedFeatures: state => { return state.identifiedFeatures.filter(f => f.getId()); } }, mutations: { initialLoad: (state, initialLoad) => { state.initialLoad = initialLoad; }, extent: (state, extent) => { state.extent = extent; }, openLayersMap: (state, map) => { state.openLayersMap = map; }, identifyTool: (state, events) => { state.identifyTool = events; }, setIdentifiedFeatures: (state, identifiedFeatures) => { state.identifiedFeatures = identifiedFeatures; }, setCurrentMeasurement: (state, measurement) => { state.currentMeasurement = measurement; }, lineTool: (state, lineTool) => { state.lineTool = lineTool; }, polygonTool: (state, polygonTool) => { state.polygonTool = polygonTool; }, cutTool: (state, cutTool) => { state.cutTool = cutTool; }, moveToBoundingBox: (state, { boundingBox, zoom, preventZoomOut }) => { const extent = transformExtent(boundingBox, "EPSG:4326", "EPSG:3857"); let view = state.openLayersMap.getView(); moveMap({ view, extent, zoom, preventZoomOut }); }, moveToExtent: (state, { feature, zoom, preventZoomOut }) => { const boundingBox = bbox(feature.geometry); const extent = transformExtent(boundingBox, "EPSG:4326", "EPSG:3857"); let view = state.openLayersMap.getView(); moveMap({ view, extent, zoom, preventZoomOut }); }, moveMap: (state, { coordinates, zoom, preventZoomOut }) => { let view = state.openLayersMap.getView(); const currentZoom = view.getZoom(); zoom = zoom || currentZoom; view.animate({ zoom: preventZoomOut ? Math.max(zoom, currentZoom) : zoom, center: fromLonLat(coordinates, view.getProjection()), duration: 700 }); }, isolinesLegendImgDataURL: (state, isolinesLegendImgDataURL) => { state.isolinesLegendImgDataURL = isolinesLegendImgDataURL; }, differencesLegendImgDataURL: (state, differencesLegendImgDataURL) => { state.differencesLegendImgDataURL = differencesLegendImgDataURL; } }, actions: { openLayersMap({ commit, dispatch, state }, map) { const drawVectorSrc = state.layers.DRAWTOOL.getSource(); const cutVectorSrc = state.layers.CUTTOOL.getSource(); // init line tool const lineTool = new Draw({ source: drawVectorSrc, type: "LineString", maxPoints: 2 }); lineTool.setActive(false); lineTool.on("drawstart", () => { drawVectorSrc.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 }); polygonTool.setActive(false); polygonTool.on("drawstart", () => { drawVectorSrc.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, 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.setActive(false); cutTool.on("drawstart", () => { dispatch("disableIdentifyTool"); cutVectorSrc.clear(); }); cutTool.on("drawend", event => { commit("fairwayprofile/selectedCut", null, { root: true }); dispatch("fairwayprofile/cut", event.feature, { root: true }).then(() => // This setTimeout is an ugly workaround. If we would enable the // identifyTool here immediately then the click event from ending the // cut will trigger it. We don't want that. setTimeout(() => dispatch("enableIdentifyTool"), 1000) ); }); map.addInteraction(lineTool); map.addInteraction(cutTool); map.addInteraction(polygonTool); commit("lineTool", lineTool); commit("polygonTool", polygonTool); commit("cutTool", cutTool); commit("openLayersMap", map); }, disableIdentifyTool({ state }) { unByKey(state.identifyTool); state.identifyTool = null; }, enableIdentifyTool({ state, rootState, commit, dispatch }) { if (!state.identifyTool) { state.identifyTool = state.openLayersMap.on( ["singleclick", "dblclick"], event => { commit("setIdentifiedFeatures", []); // checking our WFS layers var features = state.openLayersMap.getFeaturesAtPixel(event.pixel, { hitTolerance: 7 }); if (features) { let identifiedFeatures = []; for (let feature of features) { let id = feature.getId(); // avoid identifying the same feature twice if ( identifiedFeatures.findIndex( f => f.getId() === feature.getId() ) === -1 ) { identifiedFeatures.push(feature); } // get selected bottleneck // RegExp.prototype.test() works with number, str and undefined if (/^bottlenecks/.test(id)) { if ( rootState.bottlenecks.selectedBottleneck != feature.get("objnam") ) { dispatch( "bottlenecks/setSelectedBottleneck", feature.get("objnam"), { root: true } ).then(() => { this.commit("bottlenecks/setFirstSurveySelected"); }); commit("moveMap", { coordinates: getCenter( feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326") .getExtent() ), zoom: 17, preventZoomOut: true }); } } // get selected gauge if (/^gauges/.test(id)) { if ( rootState.gauges.selectedGaugeISRS !== feature.get("isrs_code") ) { dispatch( "gauges/selectedGaugeISRS", feature.get("isrs_code"), { root: true } ); commit("moveMap", { coordinates: getCenter( feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326") .getExtent() ), zoom: null, preventZoomOut: true }); } } // get selected stretch if (/^stretches/.test(id)) { if (rootState.imports.selectedStretchId === feature.getId()) { commit("imports/selectedStretchId", null, { root: true }); } else { commit("imports/selectedStretchId", feature.getId(), { root: true }); commit("moveMap", { coordinates: getCenter( feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326") .getExtent() ), zoom: null, preventZoomOut: true }); } } } commit("setIdentifiedFeatures", identifiedFeatures); } // 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)); } } } */ // trying the GetFeatureInfo way for WMS var wmsSource = state.layers.INLANDECDIS.getSource(); var url = wmsSource.getGetFeatureInfoUrl( event.coordinate, 100 /* resolution */, "EPSG:3857", // { INFO_FORMAT: "application/vnd.ogc.gml" } // not allowed by d4d { INFO_FORMAT: "text/plain" } ); if (url) { // cannot directly query here because of SOP console.log("GetFeatureInfo url:", url); } } ); } } } };