Mercurial > gemma
view client/src/components/map/layers.js @ 3547:47c61ea894b1
client: map layers: share same vector source object for layers that use the same geoserver view (bottlenecks_geoserver)
The layers Bottlenecks, Critical Bottlenecks and Bottlenecks Fairway Availability used the exact same geoserver_view/data. Thus
the vector source object can be shared to avoid multiple identical requests. Unfortunately this does not work across maps. So each
map has it's own vector source object for the three bottleneck layers.
author | Markus Kottlaender <markus@intevation.de> |
---|---|
date | Fri, 31 May 2019 12:20:08 +0200 |
parents | a606d003730c |
children | f3102fa16a69 |
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 } from "ol/format/filter"; import { HTTP } from "@/lib/http"; import styles from "./styles"; import store from "@/store/index"; 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: "#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 function() { // Shared feature source for layers: // BOTTLENECKS, BOTTLENECKSTATUS and BOTTLENECKFAIRWAYAVAILABILITY // Reduces bottlenecks_geoserver requests and number of stored feature objects. 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; } ) ); return { get(id) { return this.config.find(l => l.get("id") === id); }, config: [ new TileLayer({ id: "OPENSTREETMAP", label: "Open Streetmap", visible: true, source: new OSM() }), new ImageLayer({ id: "INLANDECDIS", label: "Inland ECDIS chart Danube", visible: true, source: new ImageSource({ preload: 1, url: "https://service.d4d-portal.info/wms/", crossOrigin: "anonymous", params: { LAYERS: "d4d", VERSION: "1.1.1", TILED: true } }) }), 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() { const source = new VectorSource(); source.setLoader( buildVectorLoader( { featureTypes: ["fairway_dimensions"], filter: equalTo("level_of_service", 1) }, source, true ) ); 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(); source.setLoader( buildVectorLoader( { featureTypes: ["fairway_dimensions"], filter: equalTo("level_of_service", 2) }, source, true ) ); 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(); source.setLoader( buildVectorLoader( { featureTypes: ["fairway_dimensions"], filter: equalTo("level_of_service", 3) }, source, true ) ); 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: "BOTTLENECKS", label: "Bottlenecks", visible: true, style: styles.bottleneck, source: bottlenecksSource }); })(), new TileLayer({ id: "BOTTLENECKISOLINE", 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); }) }), 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 }, 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); }) }), (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: "Bottlenecks 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) { // look for nashSutcliffeOverview in store. If present and // not older than 15 min use it or fetch new data and store it. let data = store.getters["gauges/nashSutcliffeOverview"](f); if ( data && new Date().getTime() - data.createdAt.getTime() < 900000 ) { f.set("nsc_data", data.data); } else { data = await store.dispatch( "gauges/loadNashSutcliffeForOverview", f.get("isrs_code") ); if (data) { store.commit("gauges/addNashSutcliffeOverviewEntry", { feature: f, data, createdAt: new Date() }); f.set("nsc_data", data); } } } } ) ); 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 ] }; }