view client/src/components/map/layers.js @ 3271:b74e9768231d

client: map layers: added support for vuex store in layer configuration
author Markus Kottlaender <markus@intevation.de>
date Wed, 15 May 2019 14:15:45 +0200
parents d7bc51fd50b0
children 80037790032d
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));
        }
        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() {
  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();
        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() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["bottlenecks_geoserver"],
              geometryName: "area"
            },
            source
          )
        );
        return new VectorLayer({
          id: "BOTTLENECKS",
          label: "Bottlenecks",
          visible: true,
          style: styles.bottleneck,
          source
        });
      })(),
      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() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["bottlenecks_geoserver"],
              geometryName: "area"
            },
            source
          )
        );
        return new VectorLayer({
          id: "BOTTLENECKSTATUS",
          label: "Critical Bottlenecks",
          forLegendStyle: { point: true, resolution: 16 },
          visible: true,
          zIndex: 1,
          style: styles.bottleneckStatus,
          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
    ]
  };
}