view client/src/components/map/layers.js @ 3457:870812d8f247

client: spuc05: implemented new data format (csv) for diagrams on map
author Markus Kottlaender <markus@intevation.de>
date Fri, 24 May 2019 15:32:47 +0200
parents 1559566662f5
children c5c7cc24fe72
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({ 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() {
        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
        });
      })(),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: ["bottlenecks_geoserver"],
              geometryName: "area"
            },
            source,
            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 new VectorLayer({
          id: "BOTTLENECKFAIRWAYAVAILABILITY",
          label: "Bottlenecks Fairway Availability",
          forLegendStyle: { point: true, resolution: 16 },
          visible: false,
          zIndex: 1,
          style: styles.bottleneckFairwayAvailability,
          source
        });
      })(),
      (function() {
        const source = new VectorSource({ strategy: bboxStrategy });
        source.setLoader(
          buildVectorLoader(
            {
              featureTypes: [
                "bottlenecks_geoserver",
                "gauges_geoserver",
                "stretches_geoserver",
                "sections_geoserver"
              ]
            },
            source,
            true
          )
        );
        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
    ]
  };
}