view client/src/components/map/layers.js @ 3029:81c2e561fe03

Use distance marks an axis via WMS. Added legend and featureinfo.
author Raimund Renkert <raimund.renkert@intevation.de>
date Fri, 12 Apr 2019 12:49:43 +0200
parents 2e2a271c1026
children 3524bbc5067e
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 OSM from "ol/source/OSM";
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 { equalTo } from "ol/format/filter";
import { HTTP } from "@/lib/http";
import styles from "./styles";

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));
        }
        vectorSource.addFeatures(features);
      })
      .catch(() => {
        vectorSource.removeLoadedExtent(extent);
      });
  };
};

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 TileLayer({
        id: "INLANDECDIS",
        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 }
        })
      }),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["waterway_area"],
              geometryName: "area"
            },
            source
          )
        );
        return new VectorLayer({
          id: "WATERWAYAREA",
          label: "Waterway Area",
          visible: true,
          style: new Style({
            stroke: new Stroke({
              color: "rgba(0, 102, 0, 1)",
              width: 2
            })
          }),
          source
        });
      })(),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["stretches_geoserver"],
              geometryName: "area"
            },
            source,
            f => {
              if (f.getId() === this.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,
          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,
          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,
          source
        });
      })(),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["waterway_axis"],
              geometryName: "wtwaxs"
            },
            source
          )
        );
        return new VectorLayer({
          id: "WATERWAYAXIS",
          label: "Waterway Axis",
          visible: true,
          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,
          source
        });
      })(),
      (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: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);
        })
      }),
      (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,
          style: styles.bottleneckStatus,
          source
        });
      })(),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["distance_marks_ashore_geoserver"],
              geometryName: "geom"
            },
            source
          )
        );
        return new VectorLayer({
          id: "DISTANCEMARKS",
          label: "Distance marks",
          forLegendStyle: { point: true, resolution: 8 },
          visible: false,
          source
        });
      })(),
      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
        });
      })(),
      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;
        }
      }),
      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;
        }
      })
    ]
  };
}