Mercurial > gemma
view client/src/map/Maplayer.vue @ 842:ee6f127f573c
client: add first prototype for bottleneck iso layer
* Add prototype with internal direct url and TOTOISOs markings.
author | Bernhard Reiter <bernhard@intevation.de> |
---|---|
date | Fri, 28 Sep 2018 11:27:07 +0200 |
parents | d9da1ea14abf |
children | d2aa972df342 |
line wrap: on
line source
<template> <div id="map" :class="mapStyle"></div> </template> <style lang="scss"> .mapsplit { height: 50vh; } .mapfull { height: 100vh; } </style> <script> import { HTTP } from "../application/lib/http"; import { mapGetters, mapState } from "vuex"; import "ol/ol.css"; import { Map, View } from "ol"; import Feature from "ol/Feature"; // import { bbox as bboxFilter } from "ol/format/filter.js"; import { WFS, GeoJSON } from "ol/format.js"; // import GeometryType from "ol/geom/GeometryType.js"; import LineString from "ol/geom/LineString.js"; import Draw from "ol/interaction/Draw.js"; import { Vector as VectorLayer } from "ol/layer.js"; import { Vector as VectorSource } from "ol/source.js"; import { getLength } from "ol/sphere.js"; import distance from "@turf/distance"; import { lineString as turfLineString, polygon as turfPolygon } from "@turf/helpers"; //import { lineIntersect as turfLineIntersect } from "@turf/line-intersect"; import lineIntersect from "@turf/line-intersect"; import { displayError } from "../application/lib/errors.js"; const DEMODATA = 2.5; export default { name: "maplayer", props: ["drawMode", "lat", "long", "zoom", "split"], data() { return { projection: "EPSG:3857", interaction: null, vectorLayer: null, vectorSource: null }; }, computed: { ...mapGetters("mapstore", ["layers", "getLayerByName"]), ...mapState("mapstore", ["openLayersMap", "selectedMorph"]), mapStyle() { return { mapfull: !this.split, mapsplit: this.split }; }, layerData() { const l = this.layers.map(x => { return x.data; }); return [...l, this.vectorLayer]; } }, methods: { createVectorSource() { this.vectorSource = new VectorSource({ wrapX: false }); }, createVectorLayer() { this.vectorLayer = new VectorLayer({ source: this.vectorSource }); }, removeCurrentInteraction() { this.openLayersMap.removeInteraction(this.interaction); this.interaction = null; }, createInteraction() { this.vectorSource.clear(); var draw = new Draw({ source: this.vectorSource, type: this.drawMode, maxPoints: 2 }); draw.on("drawstart", event => { this.vectorSource.clear(); this.$store.commit("mapstore/setCurrentMeasurement", null); event.feature.setId("drawn.1"); // unique id for new feature }); draw.on("drawend", this.drawEnd); return draw; }, drawEnd(event) { const length = getLength(event.feature.getGeometry()); this.$store.commit("mapstore/setCurrentMeasurement", length); // also place the a rounded length in a property, so identify can show it event.feature.set("length", Math.round(length * 10) / 10); // if a survey has been selected, request a profile // TODO an improvement could be to check if the line intersects // with the bottleneck area's polygon before trying the server request if (this.selectedMorph) { console.log("requesting profile for", this.selectedMorph); this.requestProfile(event, this.selectedMorph); } }, requestProfile(event, survey) { // survey has to have the properties bottleneck_id and date_info // prepare to send the first line seqment to the server as GeoJSON const inputLineString = event.feature.getGeometry().clone(); inputLineString.transform("EPSG:3857", "EPSG:4326"); const [start, end] = inputLineString.getCoordinates(); this.$store.commit("fairwayprofile/setStartPoint", start); this.$store.commit("fairwayprofile/setEndPoint", end); const profileLine = new LineString([start, end]); const feature = new Feature({ geometry: profileLine, bottleneck: survey.bottleneck_id, date: survey.date_info }); const geoJSON = new GeoJSON({ geometryName: "geometry" }).writeFeature( feature ); this.$store .dispatch("fairwayprofile/loadProfile", geoJSON) .then(() => { var vectorSource = this.getLayerByName( "Fairway Dimensions" ).data.getSource(); vectorSource.forEachFeatureIntersectingExtent( // need to use EPSG:3857 which is the proj of vectorSource profileLine .clone() .transform("EPSG:4326", "EPSG:3857") .getExtent(), feature => { // transform back to prepare for usage var intersectingPolygon = feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326"); this.addToFairwayRectangle( profileLine, intersectingPolygon, DEMODATA ); } ); this.$store.commit("application/openSplitScreen"); }) .catch(error => { const { status, data } = error.response; displayError({ title: "Backend Error", message: `${status}: ${data.message || data}` }); }); }, addToFairwayRectangle(profileLine, fairwayGeometry, depth) { // both geometries have to be in EPSG:4326 // uses turfjs distance() function let fairwayCoordinates = []; var line = turfLineString(profileLine.getCoordinates()); var polygon = turfPolygon(fairwayGeometry.getCoordinates()); var intersects = lineIntersect(line, polygon); var l = intersects.features.length; if (l % 2 != 0) { console.log("Ignoring fairway because profile only intersects once."); } else { for (let i = 0; i < l; i += 2) { let pStartPoint = profileLine.getCoordinates()[0]; let fStartPoint = intersects.features[i].geometry.coordinates; let fEndPoint = intersects.features[i + 1].geometry.coordinates; let opts = { units: "kilometers" }; fairwayCoordinates.push([ distance(pStartPoint, fStartPoint, opts) * 1000, distance(pStartPoint, fEndPoint, opts) * 1000, depth ]); } } this.$store.commit( "fairwayprofile/setFairwayCoordinates", fairwayCoordinates ); }, activateInteraction() { const interaction = this.createInteraction(this.drawMode); this.interaction = interaction; this.openLayersMap.addInteraction(interaction); }, activateIdentifyMode() { this.openLayersMap.on("singleclick", event => { // console.log("single click on map:", event); this.identify(event.coordinate, event.pixel); }); }, identify(coordinate, pixel) { this.$store.commit("mapstore/setIdentifiedFeatures", []); // checking our WFS layers var features = this.openLayersMap.getFeaturesAtPixel(pixel); this.$store.commit("mapstore/setIdentifiedFeatures", features); // 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 = this.getLayerByName( "Inland ECDIS chart Danube" ).data.getSource(); var url = wmsSource.getGetFeatureInfoUrl( 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); } }, buildVectorLoader(featureRequestOptions, endpoint, vectorSource) { // build a function to be used for VectorSource.setLoader() // make use of WFS().writeGetFeature to build the request // and use our HTTP library to actually do it // NOTE: a) the geometryName has to be given in featureRequestOptions, // because we want to load depending on the bbox // b) the VectorSource has to have the option strategy: bbox featureRequestOptions["outputFormat"] = "application/json"; var loader = function(extent, resolution, projection) { featureRequestOptions["bbox"] = extent; featureRequestOptions["srsName"] = projection.getCode(); var featureRequest = new WFS().writeGetFeature(featureRequestOptions); // DEBUG console.log(featureRequest); HTTP.post( endpoint, new XMLSerializer().serializeToString(featureRequest), { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } } ) .then(response => { var features = new GeoJSON().readFeatures( JSON.stringify(response.data) ); vectorSource.addFeatures(features); // console.log( // "loaded", // features.length, // featureRequestOptions.featureTypes, // "features" // ); // DEBUG console.log("loaded ", features, "for", vectorSource); // eslint-disable-next-line }) .catch(() => { vectorSource.removeLoadedExtent(extent); }); }; return loader; }, // TODOISO call if new survey is selected updateBottleneckFilter(datestr) { console.log(datestr); var wmsSrc = this.getLayerByName("Bottleneck isolines").data.getSource(); // TODOISO check if this works wmsSrc.updateParams({cql_filter: "date_info = '" + datestr + "'"}); } }, watch: { drawMode() { if (this.interaction) { this.removeCurrentInteraction(); } else { this.activateInteraction(); } }, split() { const map = this.openLayersMap; this.$nextTick(() => { map.updateSize(); }); } }, mounted() { this.createVectorSource(); this.createVectorLayer(); let map = new Map({ layers: this.layerData, target: "map", controls: [], view: new View({ center: [this.long, this.lat], zoom: this.zoom, projection: this.projection }) }); this.$store.commit("mapstore/setOpenLayersMap", map); // TODO make display of layers more dynamic, e.g. from a list // loading the full WFS layer, by not setting the loader function // and without bboxStrategy var featureRequest2 = new WFS().writeGetFeature({ srsName: "EPSG:3857", featureNS: "gemma", featurePrefix: "gemma", featureTypes: ["fairway_dimensions"], outputFormat: "application/json" }); // NOTE: loading the full fairway_dimensions makes sure // that all are available for the intersection with the profile HTTP.post( "/internal/wfs", new XMLSerializer().serializeToString(featureRequest2), { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } } ).then(response => { var features = new GeoJSON().readFeatures(JSON.stringify(response.data)); var vectorSrc = this.getLayerByName( "Fairway Dimensions" ).data.getSource(); vectorSrc.addFeatures(features); // would scale to the extend of all resulting features // this.openLayersMap.getView().fit(vectorSrc.getExtent()); }); // load following layers with bboxStrategy (using our request builder) var layer = null; layer = this.getLayerByName("Waterway Area"); layer.data.getSource().setLoader( this.buildVectorLoader( { featurePrefix: "ws-wamos", featureTypes: ["ienc_wtware"], geometryName: "geom" }, "/external/d4d", layer.data.getSource() ) ); layer = this.getLayerByName("Waterway Axis"); layer.data.getSource().setLoader( this.buildVectorLoader( { featurePrefix: "ws-wamos", featureTypes: ["ienc_wtwaxs"], geometryName: "geom" }, "/external/d4d", layer.data.getSource() ) ); layer = this.getLayerByName("Distance marks"); layer.data.getSource().setLoader( this.buildVectorLoader( { featurePrefix: "ws-wamos", featureTypes: ["ienc_dismar"], geometryName: "geom" //, /* restrict loading approximately to extend of danube in Austria */ // filter: bboxFilter("geom", [13.3, 48.0, 17.1, 48.6], "EPSG:4326") }, "/external/d4d", layer.data.getSource() ) ); layer.data.setVisible(layer.isVisible); layer = this.getLayerByName("Distance marks, Axis"); layer.data.getSource().setLoader( this.buildVectorLoader( { featureNS: "gemma", featurePrefix: "gemma", featureTypes: ["distance_marks_geoserver"], geometryName: "geom" }, "/internal/wfs", layer.data.getSource() ) ); layer = this.getLayerByName("Waterway Area, named"); layer.data.getSource().setLoader( this.buildVectorLoader( { featureNS: "gemma", featurePrefix: "gemma", featureTypes: ["hydro_seaare"], geometryName: "geom" }, "/external/d4d", layer.data.getSource() ) ); layer.data.setVisible(layer.isVisible); layer = this.getLayerByName("Bottlenecks"); layer.data.getSource().setLoader( this.buildVectorLoader( { featureNS: "gemma", featurePrefix: "gemma", featureTypes: ["bottlenecks"], geometryName: "area" }, "/internal/wfs", layer.data.getSource() ) ); // so none is shown // this.updateBottleneckFilter("1999-10-01"); // test date this.updateBottleneckFilter("2018-08-30"); this.activateIdentifyMode(); } }; </script>