view client/src/components/Maplayer.vue @ 2491:7247eb03e7c0 critical-bottlenecks

merged default into critical-bottlenecks branch
author Markus Kottlaender <markus@intevation.de>
date Mon, 04 Mar 2019 08:32:05 +0100
parents ae1987c5beb3 960550ccca55
children 89c439721db2
line wrap: on
line source

<template>
  <div id="map" :class="mapStyle"></div>
</template>

<style lang="scss" scoped>
.nocursor {
  cursor: none;
}

.mapsplit {
  height: 50vh;
}

.mapfull {
  height: 100vh;
}
</style>

<script>
/* This is Free Software under GNU Affero General Public License v >= 3.0
 * without warranty, see README.md and license for details.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 * License-Filename: LICENSES/AGPL-3.0.txt
 *
 * Copyright (C) 2018, 2019 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * * Thomas Junk <thomas.junk@intevation.de>
 * * Bernhard E. Reiter <bernhard.reiter@intevation.de>
 */
import { HTTP } from "@/lib/http";
import { mapGetters, mapState } from "vuex";
import "ol/ol.css";
import { Map, View } from "ol";
import { WFS, GeoJSON } from "ol/format.js";
import { equalTo } from "ol/format/filter.js";
import { Stroke, Style, Fill } from "ol/style.js";
import { displayError } from "@/lib/errors.js";
import { LAYERS } from "@/store/map.js";

/* for the sake of debugging */
/* eslint-disable no-console */
export default {
  name: "maplayer",
  data() {
    return {
      projection: "EPSG:3857"
    };
  },
  computed: {
    ...mapGetters("map", ["getLayerByName", "getVSourceByName"]),
    ...mapState("map", [
      "initialLoad",
      "extent",
      "layers",
      "openLayersMap",
      "lineTool",
      "polygonTool",
      "cutTool"
    ]),
    ...mapState("bottlenecks", ["selectedSurvey"]),
    ...mapState("application", ["showSplitscreen"]),
    mapStyle() {
      return {
        mapfull: !this.showSplitscreen,
        mapsplit: this.showSplitscreen,
        nocursor: this.hasActiveInteractions
      };
    },
    hasActiveInteractions() {
      return (
        (this.lineTool && this.lineTool.getActive()) ||
        (this.polygonTool && this.polygonTool.getActive()) ||
        (this.cutTool && this.cutTool.getActive())
      );
    }
  },
  methods: {
    buildVectorLoader(featureRequestOptions, endpoint, vectorSource) {
      // 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: a) the geometryName has to be given in featureRequestOptions,
      //          because we want to load depending on the bbox
      //  b) the VectorSource has to have the option strategy: bbox
      featureRequestOptions["outputFormat"] = "application/json";
      var loader = function(extent, resolution, projection) {
        featureRequestOptions["bbox"] = extent;
        featureRequestOptions["srsName"] = projection.getCode();
        var featureRequest = new WFS().writeGetFeature(featureRequestOptions);
        // DEBUG console.log(featureRequest);
        HTTP.post(
          endpoint,
          new XMLSerializer().serializeToString(featureRequest),
          {
            headers: {
              "X-Gemma-Auth": localStorage.getItem("token"),
              "Content-type": "text/xml; charset=UTF-8"
            }
          }
        )
          .then(response => {
            var features = new GeoJSON().readFeatures(
              JSON.stringify(response.data)
            );
            vectorSource.addFeatures(features);
            // console.log(
            //   "loaded",
            //   features.length,
            //   featureRequestOptions.featureTypes,
            //   "features"
            // );
            // DEBUG console.log("loaded ", features, "for", vectorSource);
            // eslint-disable-next-line
          })
          .catch(() => {
            vectorSource.removeLoadedExtent(extent);
          });
      };
      return loader;
    },
    updateBottleneckFilter(bottleneck_id, datestr) {
      console.log("updating filter with", bottleneck_id, datestr);
      const layer = this.getLayerByName(LAYERS.BOTTLENECKISOLINE);
      const wmsSrc = layer.data.getSource();
      const exists = bottleneck_id != "does_not_exist";

      if (exists) {
        wmsSrc.updateParams({
          cql_filter:
            "date_info='" +
            datestr +
            "' AND bottleneck_id='" +
            bottleneck_id +
            "'"
        });
      }
      layer.isVisible = exists;
      layer.data.setVisible(exists);
    }
  },
  watch: {
    showSplitscreen() {
      const map = this.openLayersMap;
      this.$nextTick(() => {
        map && map.updateSize();
      });
    },
    selectedSurvey(newSelectedSurvey) {
      if (newSelectedSurvey) {
        this.updateBottleneckFilter(
          newSelectedSurvey.bottleneck_id,
          newSelectedSurvey.date_info
        );
      } else {
        this.updateBottleneckFilter("does_not_exist", "1999-10-01");
      }
    }
  },
  mounted() {
    let map = new Map({
      layers: [...this.layers.map(x => x.data)],
      target: "map",
      controls: [],
      view: new View({
        center: [this.extent.lon, this.extent.lat],
        minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px
        zoom: this.extent.zoom,
        projection: this.projection
      })
    });
    map.on("moveend", event => {
      const center = event.map.getView().getCenter();
      this.$store.commit("map/extent", {
        lat: center[1],
        lon: center[0],
        zoom: event.map.getView().getZoom()
      });
    });
    this.$store.dispatch("map/openLayersMap", map);

    if (this.initialLoad) {
      this.$store.commit("map/initialLoad", false);
      var currentUser = this.$store.state.user.user;
      HTTP.get("/users/" + currentUser, {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-type": "text/xml; charset=UTF-8"
        }
      })
        .then(response => {
          this.$store.commit("map/moveToBoundingBox", {
            boundingBox: [
              response.data.extent.x1,
              response.data.extent.y1,
              response.data.extent.x2,
              response.data.extent.y2
            ],
            zoom: 17,
            preventZoomOut: true
          });
        })
        .catch(error => {
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    }

    // TODO make display of layers more dynamic, e.g. from a list

    // load different fairway dimension layers (level of service)
    [
      LAYERS.FAIRWAYDIMENSIONSLOS1,
      LAYERS.FAIRWAYDIMENSIONSLOS2,
      LAYERS.FAIRWAYDIMENSIONSLOS3
    ].forEach((los, i) => {
      // loading the full WFS layer without bboxStrategy
      var source = this.getVSourceByName(los);
      /*eslint-disable no-unused-vars */
      var loader = function(extent, resolution, projection) {
        var featureRequest = new WFS().writeGetFeature({
          srsName: "EPSG:3857",
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["fairway_dimensions"],
          outputFormat: "application/json",
          filter: equalTo("level_of_service", i + 1)
        });

        featureRequest["outputFormat"] = "application/json";
        // NOTE: loading the full fairway_dimensions makes sure
        //       that all are available for the intersection with the profile
        HTTP.post(
          "/internal/wfs",
          new XMLSerializer().serializeToString(featureRequest),
          {
            headers: {
              "X-Gemma-Auth": localStorage.getItem("token"),
              "Content-type": "text/xml; charset=UTF-8"
            }
          }
        ).then(response => {
          source.addFeatures(
            new GeoJSON().readFeatures(JSON.stringify(response.data))
          );
          // would scale to the extend of all resulting features
          // this.openLayersMap.getView().fit(vectorSrc.getExtent());
        });
      };

      layer = this.getLayerByName(los);
      layer.data.getSource().setLoader(loader);
      layer.data.setVisible(layer.isVisible);
    });

    // load following layers with bboxStrategy (using our request builder)
    var layer = null;

    layer = this.getLayerByName(LAYERS.WATERWAYAREA);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["waterway_area"],
          geometryName: "area"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.WATERWAYAXIS);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["waterway_axis"],
          geometryName: "wtwaxs"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.WATERWAYPROFILES);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["waterway_profiles"],
          geometryName: "geom"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.DISTANCEMARKS);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["distance_marks_ashore_geoserver"],
          geometryName: "geom"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.DISTANCEMARKSAXIS);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["distance_marks_geoserver"],
          geometryName: "geom"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.GAUGES);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["gauges_geoserver"],
          geometryName: "geom"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.STRETCHES);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["stretches_geoserver"],
          geometryName: "area"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);

    layer = this.getLayerByName(LAYERS.BOTTLENECKSTATUS);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["bottlenecks_geoserver"],
          geometryName: "area"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );

    layer = this.getLayerByName(LAYERS.BOTTLENECKS);
    layer.data.getSource().setLoader(
      this.buildVectorLoader(
        {
          featureNS: "gemma",
          featurePrefix: "gemma",
          featureTypes: ["bottlenecks_geoserver"],
          geometryName: "area"
        },
        "/internal/wfs",
        layer.data.getSource()
      )
    );
    layer.data.setVisible(layer.isVisible);
    HTTP.get("/system/style/Bottlenecks/stroke", {
      headers: { "X-Gemma-Auth": localStorage.getItem("token") }
    })
      .then(response => {
        let btlnStrokeC = response.data.code;
        HTTP.get("/system/style/Bottlenecks/fill", {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            let btlnFillC = response.data.code;
            var newStyle = new Style({
              stroke: new Stroke({
                color: btlnStrokeC,
                width: 4
              }),
              fill: new Fill({
                color: btlnFillC
              })
            });
            layer.data.setStyle(function(feature, resolution) {
              if (resolution <= 50) {
                return newStyle;
              }
              return null;
            });
          })
          .catch(error => {
            console.log(error);
          });
      })
      .catch(error => {
        console.log(error);
      });

    // so none is shown
    this.updateBottleneckFilter("does_not_exist", "1999-10-01");
    this.$store.dispatch("map/disableIdentifyTool");
    this.$store.dispatch("map/enableIdentifyTool");
  }
};
</script>