Mercurial > gemma
view client/src/components/map/layers.js @ 4837:3b0ec9b56d7c
Merged
author | Sascha Wilde <wilde@intevation.de> |
---|---|
date | Fri, 08 Nov 2019 13:10:56 +0100 |
parents | 75d1126f7299 |
children | 5555b77b8c4e |
line wrap: on
line source
import TileWMS from "ol/source/TileWMS"; import { Tile as TileLayer, Vector as VectorLayer, Image as ImageLayer } from "ol/layer"; import { Icon, Stroke, Style } from "ol/style"; import VectorSource from "ol/source/Vector"; import { ImageWMS as ImageSource } from "ol/source"; import Point from "ol/geom/Point"; import { bbox as bboxStrategy } from "ol/loadingstrategy"; import { WFS, GeoJSON } from "ol/format"; import OSM from "ol/source/OSM"; import { equalTo, and as andFilter } from "ol/format/filter"; import { HTTP } from "@/lib/http"; import { styleFactory } from "./styles"; import store from "@/store/index"; export const buildVectorLoader = ( featureRequestOptions, vectorSource, bboxStrategyDisabled, featurePostProcessor ) => { // 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: the geometryName has to be given in featureRequestOptions if // bboxStrategy (default) is used featureRequestOptions.featureNS = "gemma"; featureRequestOptions.featurePrefix = "gemma"; featureRequestOptions.outputFormat = "application/json"; return (extent, resolution, projection) => { if (!bboxStrategyDisabled) { featureRequestOptions.bbox = extent; } featureRequestOptions.srsName = projection.getCode(); HTTP.post( "/internal/wfs", new XMLSerializer().serializeToString( new WFS().writeGetFeature(featureRequestOptions) ), { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } } ) .then(response => { const features = new GeoJSON().readFeatures( JSON.stringify(response.data) ); if (featurePostProcessor) { features.map(f => featurePostProcessor(f, store, features)); } vectorSource.addFeatures(features); }) .catch(() => { vectorSource.removeLoadedExtent(extent); }); }; }; // SHARED LAYERS: // DRAW- and CUTLAYER are shared across maps. E.g. you want to see the cross cut // arrow on both maps when comparing surveys. So we don't need to initialize a // new VectorLayer object for each map. Instead we use these two constants so // that all maps use the same object. const DRAWLAYER = new VectorLayer({ id: "DRAWTOOL", 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; } }); const CUTLAYER = new VectorLayer({ id: "CUTTOOL", 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: "#FFFFFF", width: 5, lineDash: [7, 7] }) }), new Style({ stroke: new Stroke({ color: "#333333", width: 3, 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; } }); let layerConfigs = {}; export const unsetLayerConfigs = function() { layerConfigs = {}; }; export const layerFactory = function(mapId) { const styles = styleFactory(mapId); // Shared feature source for layers: // BOTTLENECKS, BOTTLENECKSTATUS and BOTTLENECKFAIRWAYAVAILABILITY // Reduces bottlenecks_geoserver requests and number of stored feature objects. const FDREVIEWLAYER = new VectorLayer({ id: "FDREVIEWLAYER", label: "Review", visible: true, source: new VectorSource({ wrapX: false }), style: styles.sections }); const bottlenecksSource = new VectorSource({ strategy: bboxStrategy }); bottlenecksSource.setLoader( buildVectorLoader( { featureTypes: ["bottlenecks_geoserver"], geometryName: "area" }, bottlenecksSource, false, async (f, store) => { if (f.get("fa_critical")) { // look for fairway availability data in store. If present and // not older than 15 min use it or fetch new data and store it. let data = store.getters["fairwayavailability/fwLNWLOverviewData"](f); if ( data && new Date().getTime() - data.createdAt.getTime() < 900000 ) { f.set("fa_data", data.data); } else { let date = new Date(); data = await store.dispatch( "fairwayavailability/loadAvailableFairwayDepthLNWLForMap", { feature: f, from: date.toISOString().split("T")[0], to: date.toISOString().split("T")[0], frequency: "monthly", LOS: 3 } ); if (data) { store.commit("fairwayavailability/addFwLNWLOverviewData", { feature: f, data, createdAt: new Date() }); f.set("fa_data", data); } } } return f; } ) ); // either use existing config or create new one // important is only each map has its individual layer config // but we don't want to create new layer objects each time a store value // that is used here changes. const config = layerConfigs.hasOwnProperty(mapId) ? layerConfigs[mapId] : [ new TileLayer({ id: "OPENSTREETMAP", label: "Open Streetmap", visible: true, source: new OSM() }), new ImageLayer({ id: "INLANDECDIS", label: "Inland ECDIS chart Danube", visible: true, source: null }), new ImageLayer({ id: "WATERWAYAREA", label: "Waterway Area", maxResolution: 100, minResolution: 0, source: new ImageSource({ url: window.location.origin + "/api/internal/wms", params: { LAYERS: "waterway_area", VERSION: "1.1.1", TILED: true }, imageLoadFunction: function(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); }) }), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { featureTypes: ["stretches_geoserver"], geometryName: "area" }, source, true, (f, store) => { if (f.getId() === store.state.imports.selectedStretchId) { f.set("highlighted", true); } return f; } ) ); return new VectorLayer({ id: "STRETCHES", label: "Stretches", visible: false, style: styles.stretches, source }); })(), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { featureTypes: ["sections_geoserver"], geometryName: "area" }, source, true, (f, store) => { if (f.getId() === store.state.imports.selectedSectionId) { f.set("highlighted", true); } return f; } ) ); return new VectorLayer({ id: "SECTIONS", label: "Sections", visible: false, style: styles.sections, source }); })(), (function() { return new VectorLayer({ id: "BOTTLENECKS", label: "Bottlenecks", visible: true, style: styles.bottleneck, source: bottlenecksSource }); })(), new TileLayer({ id: "BOTTLENECKISOLINE", label: "Bottleneck morphology", visible: false, source: new TileWMS({ preload: 0, projection: "EPSG:3857", url: window.location.origin + "/api/internal/wms", params: { LAYERS: "sounding_results_areas_geoserver", VERSION: "1.1.1", TILED: true }, tileLoadFunction: function(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); }) }), new TileLayer({ id: "DIFFERENCES", label: "Bottleneck Differences", visible: false, source: new TileWMS({ preload: 0, projection: "EPSG:3857", url: window.location.origin + "/api/internal/wms", params: { LAYERS: "sounding_differences", VERSION: "1.1.1", TILED: true, CQL_FILTER: "id=" + store.state.fairwayprofile.currentDifference }, tileLoadFunction: function(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); }) }), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { geometryName: "area", featureTypes: ["fairway_dimensions"], filter: andFilter( equalTo("level_of_service", 1), equalTo("staging_done", true) ) }, source, false ) ); return new VectorLayer({ id: "FAIRWAYDIMENSIONSLOS1", label: "LOS 1 Fairway Dimensions", visible: false, style: styles.fwd1, maxResolution: 80, minResolution: 0, source }); })(), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { geometryName: "area", featureTypes: ["fairway_dimensions"], filter: andFilter( equalTo("level_of_service", 2), equalTo("staging_done", true) ) }, source, false ) ); return new VectorLayer({ id: "FAIRWAYDIMENSIONSLOS2", label: "LOS 2 Fairway Dimensions", visible: false, style: styles.fwd2, maxResolution: 80, minResolution: 0, source }); })(), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { geometryName: "area", featureTypes: ["fairway_dimensions"], filter: andFilter( equalTo("level_of_service", 3), equalTo("staging_done", true) ) }, source, false ) ); return new VectorLayer({ id: "FAIRWAYDIMENSIONSLOS3", label: "LOS 3 Fairway Dimensions", visible: true, style: styles.fwd3, maxResolution: 80, minResolution: 0, source }); })(), new ImageLayer({ id: "WATERWAYAXIS", label: "Waterway Axis", source: new ImageSource({ url: window.location.origin + "/api/internal/wms", params: { LAYERS: "waterway_axis", VERSION: "1.1.1", TILED: true }, imageLoadFunction: function(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); }) }), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { featureTypes: ["waterway_profiles"], geometryName: "geom" }, source ) ); return new VectorLayer({ id: "WATERWAYPROFILES", label: "Waterway Profiles", visible: true, style: new Style({ stroke: new Stroke({ color: "rgba(0, 0, 255, .5)", lineDash: [5, 5], width: 2 }) }), maxResolution: 2.5, minResolution: 0, source }); })(), (function() { return new VectorLayer({ id: "BOTTLENECKSTATUS", label: "Critical Bottlenecks", forLegendStyle: { point: true, resolution: 16 }, visible: true, zIndex: 1, style: styles.bottleneckStatus, source: bottlenecksSource }); })(), (function() { return new VectorLayer({ id: "BOTTLENECKFAIRWAYAVAILABILITY", label: "Bottleneck Fairway Availability", forLegendStyle: { point: true, resolution: 16 }, visible: false, zIndex: 1, style: styles.bottleneckFairwayAvailability, source: bottlenecksSource }); })(), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { featureTypes: [ "bottlenecks_geoserver", "gauges_geoserver", "stretches_geoserver", "sections_geoserver" ] }, source, true, // since we don't use bbox strategy, features will contain all features and we can use it // to find reference gauges for bottlenecks, yeah! async (f, store, features) => { // attach reference gauge to bottleneck if (f.getId().indexOf("bottlenecks") > -1) { f.set( "gauge_obj", features.find(feat => { return ( feat.getId().indexOf("gauges") > -1 && feat.get("objname") === f.get("gauge_objname") ); }) ); } // attach nsc data to gauge if (f.getId().indexOf("gauges") > -1) { store .dispatch( "gauges/getNashSutcliffeForISRS", f.get("isrs_code") ) .then(response => { f.set("nsc_data", response); }); } } ) ); return new VectorLayer({ id: "DATAAVAILABILITY", label: "Data Availability/Accuracy", forLegendStyle: { point: true, resolution: 16 }, visible: false, zIndex: 1, style: styles.dataAvailability, source }); })(), new ImageLayer({ id: "DISTANCEMARKS", label: "Distance Marks", maxResolution: 10, minResolution: 0, source: new ImageSource({ url: window.location.origin + "/api/internal/wms", params: { LAYERS: "distance_marks_ashore_geoserver", VERSION: "1.1.1", TILED: true }, imageLoadFunction: function(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); }) }), new ImageLayer({ id: "DISTANCEMARKSAXIS", label: "Distance Marks, Axis", source: new ImageSource({ url: window.location.origin + "/api/internal/wms", params: { LAYERS: "distance_marks_geoserver", VERSION: "1.1.1", TILED: true }, imageLoadFunction: function(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); }) }), (function() { const source = new VectorSource({ strategy: bboxStrategy }); source.setLoader( buildVectorLoader( { featureTypes: ["gauges_geoserver"], geometryName: "geom" }, source ) ); return new VectorLayer({ id: "GAUGES", label: "Gauges", forLegendStyle: { point: true, resolution: 8 }, visible: true, style: styles.gauge, maxResolution: 100, minResolution: 0, source }); })(), DRAWLAYER, CUTLAYER, FDREVIEWLAYER ]; layerConfigs[mapId] = config; return { get(id) { return config.find(l => l.get("id") === id); }, config }; };