view client/src/components/map/Map.vue @ 5560:f2204f91d286

Join the log lines of imports to the log exports to recover data from them. Used in SR export to extract information that where in the meta json but now are only found in the log.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 09 Feb 2022 18:34:40 +0100
parents fb3e127bccb4
children 84d01a536bec
line wrap: on
line source

<template>
  <div
    :id="'map-' + paneId"
    :class="['map', { nocursor: this.hasActiveInteractions }]"
  >
    <Zoom :map="map" />
  </div>
</template>

<style lang="sass" scoped>
.map
  width: 100%
  height: 100%
  background-color: #eee
  background-image: linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%, #e8e8e8), linear-gradient(45deg, #e8e8e8 25%, transparent 25%, transparent 75%, #e8e8e8 75%, #e8e8e8)
  background-size: 20px 20px
  background-position: 0 0, 10px 10px

  &.nocursor
    cursor: none
</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>
 */

/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "_" }]*/

import { HTTP } from "@/lib/http";
import { mapState } from "vuex";
import { Map, View } from "ol";
import { Stroke, Style, Fill } from "ol/style";
import { displayError } from "@/lib/errors";
import { pane } from "@/lib/mixins";
import { layerFactory } from "@/components/layers/layers";
import { ImageWMS as ImageSource } from "ol/source";
import { styles } from "@/components/layers/styles";
import "ol/ol.css";

export default {
  mixins: [pane],
  components: {
    Zoom: () => import("@/components/map/Zoom")
  },
  data() {
    return {
      map: null
    };
  },
  computed: {
    ...mapState("map", [
      "initialLoad",
      "extent",
      "syncedMaps",
      "syncedView",
      "ongoingRefresh"
    ]),
    ...mapState("bottlenecks", ["selectedSurvey"]),
    ...mapState("fairwayprofile", ["additionalSurvey"]),
    ...mapState("application", [
      "paneSetup",
      "paneRotate",
      "config",
      "ongoingPDFExport"
    ]),
    ...mapState("imports", ["selectedStretchId", "selectedSectionId"]),
    layers() {
      return layerFactory(this.paneId);
    },
    hasActiveInteractions() {
      return (
        this.map &&
        this.map
          .getInteractions()
          .getArray()
          .filter(
            i =>
              ["linetool", "polygontool", "cuttool"].includes(i.get("id")) &&
              i.getActive()
          ).length
      );
    }
  },
  watch: {
    ongoingRefresh() {
      if (this.ongoingRefresh) return;
      this.loadStyles();
    },
    paneSetup() {
      this.$nextTick(() => this.map.updateSize());
    },
    paneRotate() {
      this.$nextTick(() => this.map.updateSize());
    },
    syncedMaps(syncedMaps) {
      if (syncedMaps.includes(this.paneId) || this.paneId === "main") {
        this.map.setView(this.syncedView);
      } else {
        this.map.setView(
          new View({
            enableRotation: false,
            center: [this.extent.lon, this.extent.lat],
            minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px
            zoom: this.extent.zoom,
            projection: "EPSG:3857"
          })
        );
      }
    },
    selectedSurvey(survey) {
      if (this.paneId === "main") {
        if (survey) {
          this.updateBottleneckFilter(survey.bottleneck_id, survey.date_info);
        } else {
          this.updateBottleneckFilter("does_not_exist", "1999-10-01");
        }
      }
    },
    additionalSurvey(survey) {
      if (this.paneId === "compare-survey") {
        if (survey) {
          this.updateBottleneckFilter(survey.bottleneck_id, survey.date_info);
        } else {
          this.updateBottleneckFilter("does_not_exist", "1999-10-01");
        }
      }
    },
    selectedStretchId(id) {
      let stretch = this.layers.get("STRETCHES");
      stretch
        .getSource()
        .getFeatures()
        .forEach(f => {
          f.set("highlighted", false);
          f.setStyle(null);
          if (id === f.getId()) {
            f.set("highlighted", true);
            let highlight = new Style({
              fill: new Fill({
                color: this.colorLuminance(
                  stretch
                    .getStyle()
                    .getFill()
                    .getColor(),
                  0.3
                )
              }),
              stroke: new Stroke({
                color: this.colorLuminance(
                  stretch
                    .getStyle()
                    .getStroke()
                    .getColor(),
                  0.3
                ),
                width: 3
              })
            });
            f.setStyle(highlight);
          }
        });
    },
    selectedSectionId(id) {
      let section = this.layers.get("SECTIONS");
      section
        .getSource()
        .getFeatures()
        .forEach(f => {
          f.set("highlighted", false);
          if (id === f.getId()) {
            f.set("highlighted", true);
            let highlight = new Style({
              fill: new Fill({
                color: this.colorLuminance(
                  section
                    .getStyle()
                    .getFill()
                    .getColor(),
                  0.3
                )
              }),
              stroke: new Stroke({
                color: this.colorLuminance(
                  section
                    .getStyle()
                    .getStroke()
                    .getColor(),
                  0.3
                ),
                width: 4
              })
            });
            f.setStyle(highlight);
          }
        });
    }
  },
  methods: {
    colorLuminance(color, lum) {
      let [r, g, b, a] = color
        .substring(5, color.length - 1)
        .split(",")
        .map(e => Number(e));
      let [r1, g1, b1] = [r, g, b].map(e =>
        Math.round(Math.min(Math.max(0, e + e * lum), 255))
      );
      return `rgba(${r1},${g1},${b1},${a})`;
    },
    updateBottleneckFilter(bottleneck_id, datestr) {
      if (!bottleneck_id) return;
      const exists = bottleneck_id != "does_not_exist";
      if (exists) {
        this.layers
          .get("BOTTLENECKISOLINE")
          .getSource()
          .updateParams({
            cql_filter: `date_info='${datestr}' AND bottleneck_id='${bottleneck_id}'`
          });
      }
      this.layers.get("BOTTLENECKISOLINE").setVisible(exists);
    },
    initMap() {
      if (!this.syncedView) {
        this.$store.commit(
          "map/syncedView",
          new View({
            enableRotation: false,
            center: [this.extent.lon, this.extent.lat],
            minZoom: 5, // restrict zooming out to ~size of Europe for width 1000px
            zoom: this.extent.zoom,
            projection: "EPSG:3857"
          })
        );
      }

      // move to user specific default extent if map loads for the first time
      // checking initialLoad will be obsolete once we abandoned the separated admin context
      if (this.initialLoad) {
        this.$store.commit("map/initialLoad", false);
        var currentUser = this.$store.state.user.user;
        HTTP.get("/users/" + encodeURIComponent(`${currentUser}`), {
          headers: {
            "X-Gemma-Auth": localStorage.getItem("token"),
            "Content-type": "text/xml; charset=UTF-8"
          }
        })
          .then(response => {
            this.mountMap();
            this.$store.dispatch("map/moveToBoundingBox", {
              boundingBox: [
                response.data.extent.x1,
                response.data.extent.y1,
                response.data.extent.x2,
                response.data.extent.y2
              ],
              zoom: 17,
              preventZoomOut: true,
              duration: 0
            });
          })
          .catch(error => {
            this.mountMap();
            let message = "Backend not reachable";
            if (error.response) {
              const { status, data } = error.response;
              message = `${status}: ${data.message || data}`;
            }
            displayError({
              title: this.$gettext("Backend Error"),
              message: message
            });
          });
      } else {
        this.mountMap();
      }
    },
    mountMap() {
      const source = new ImageSource({
        preload: 1,
        url: this.config.ecdis_wms_url,
        crossOrigin: "anonymous",
        params: JSON.parse(this.config.ecdis_wms_params)
      });
      source.on("imageloaderror", _ => {
        if (this.ongoingPDFExport) {
          displayError({
            title: this.$gettext("Loading Error"),
            message: this.$gettext(
              "The ECDIS chart could not be loaded completely, the map might be corrupted. Please retry later"
            ),
            options: {
              timeout: 0,
              showProgressBar: false,
              closeOnClick: true,
              pauseOnHover: true,
              bodyMaxLength: 1024
            }
          });
        }
      });

      this.layers.get("INLANDECDIS").setSource(source);
      this.map = new Map({
        layers: this.layers.config,
        target: "map-" + this.paneId,
        controls: [],
        view:
          this.syncedMaps.includes(this.paneId) || this.paneId === "main"
            ? this.syncedView
            : new View({
                center: [this.extent.lon, this.extent.lat],
                minZoom: 5,
                zoom: this.extent.zoom,
                projection: "EPSG:3857"
              })
      });
      this.map.getLayer = id => this.layers.get(id);
      // store map position on every move
      // will be obsolete once we abandoned the separated admin context
      this.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", this.map);
      this.$store.dispatch("map/initIdentifyTool", this.map);
    },
    loadStyles() {
      const getRGBA = color => {
        return (
          "rgba(" +
          parseInt(color.slice(1, 3), 16) +
          ", " +
          parseInt(color.slice(3, 5), 16) +
          ", " +
          parseInt(color.slice(5, 7), 16) +
          ", " +
          (color.length > 7 ? parseInt(color.slice(7, 9), 16) / 255 : "1") +
          ")"
        );
      };
      // load configured bottleneck colors
      HTTP.get("/system/settings", {
        headers: { "X-Gemma-Auth": localStorage.getItem("token") }
      }).then(response => {
        let btlnStrokeC = getRGBA(response.data.bottlenecks_stroke),
          btlnFillC = getRGBA(response.data.bottlenecks_fill),
          strFillC = getRGBA(response.data.stretches_fill),
          strStrokeC = getRGBA(response.data.stretches_stroke),
          secStrokeC = getRGBA(response.data.sections_stroke),
          secFillC = getRGBA(response.data.sections_fill),
          fwd1StrokeC = getRGBA(response.data.fairwaydimensionslos1_stroke),
          fwd1FillC = getRGBA(response.data.fairwaydimensionslos1_fill),
          fwd2StrokeC = getRGBA(response.data.fairwaydimensionslos2_stroke),
          fwd2FillC = getRGBA(response.data.fairwaydimensionslos2_fill),
          fwd3StrokeC = getRGBA(response.data.fairwaydimensionslos3_stroke),
          fwd3FillC = getRGBA(response.data.fairwaydimensionslos3_fill),
          wwpStokeC = getRGBA(response.data.waterwayprofiles_stroke);
        let btlnStyle = new Style({
            stroke: new Stroke({
              color: btlnStrokeC,
              width: 4
            }),
            fill: new Fill({
              color: btlnFillC
            })
          }),
          strStyle = new Style({
            stroke: new Stroke({
              color: strStrokeC,
              width: 2
            }),
            fill: new Fill({
              color: strFillC
            })
          }),
          secStyle = new Style({
            stroke: new Stroke({
              color: secStrokeC,
              width: 5
            }),
            fill: new Fill({
              color: secFillC
            })
          }),
          fwd1Style = new Style({
            stroke: new Stroke({
              lineDash: [2, 4],
              lineCap: "round",
              color: fwd1StrokeC,
              width: 2
            }),
            fill: new Fill({
              color: fwd1FillC
            })
          }),
          fwd2Style = new Style({
            stroke: new Stroke({
              lineDash: [3, 6],
              lineCap: "round",
              color: fwd2StrokeC,
              width: 2
            }),
            fill: new Fill({
              color: fwd2FillC
            })
          }),
          fwd3Style = new Style({
            stroke: new Stroke({
              color: fwd3StrokeC,
              width: 2
            }),
            fill: new Fill({
              color: fwd3FillC
            })
          }),
          wwpStyle = new Style({
            stroke: new Stroke({
              color: wwpStokeC,
              lineDash: [5, 5],
              width: 2
            })
          });
        this.layers.get("WATERWAYPROFILES").setStyle(wwpStyle);
        this.layers
          .get("FAIRWAYDIMENSIONSLOS1")
          .setStyle(() => [fwd1Style, styles.textFW1]);
        this.layers
          .get("FAIRWAYDIMENSIONSLOS2")
          .setStyle(() => [fwd2Style, styles.textFW2]);
        this.layers
          .get("FAIRWAYDIMENSIONSLOS3")
          .setStyle(() => [fwd3Style, styles.textFW3]);
        this.layers.get("SECTIONS").setStyle(secStyle);
        this.layers.get("STRETCHES").setStyle(strStyle);
        this.layers.get("BOTTLENECKS").setStyle(btlnStyle);
        this.$store.commit("gauges/deleteNashSutcliffeCache");
        this.$store.dispatch("map/refreshLayers");
      });
    }
  },
  mounted() {
    // ToDo set path to correct endpoint in order to retrieve an OSM URL
    HTTP.get("/system/config", {
      headers: { "X-Gemma-Auth": localStorage.getItem("token") }
    })
      .then(response => {
        if (response.data["osm-url"]) {
          this.layers
            .get("OPENSTREETMAP")
            .getSource()
            .setUrl(response.data["osm-url"]);
        }
        this.initMap();

        if (this.selectedSurvey && this.paneId === "main") {
          this.updateBottleneckFilter(
            this.selectedSurvey.bottleneck_id,
            this.selectedSurvey.date_info
          );
        }
        if (this.additionalSurvey && this.paneId === "compare-survey") {
          this.updateBottleneckFilter(
            this.additionalSurvey.bottleneck_id,
            this.additionalSurvey.date_info
          );
        }
        this.loadStyles();
      })
      .catch(error => {
        let message = "Backend not reachable";
        if (error.response) {
          const { status, data } = error.response;
          message = `${status}: ${data.message || data}`;
        }
        displayError({
          title: this.$gettext("Backend Error"),
          message: message
        });
      });
  },
  destroyed() {
    this.$store.commit("map/removeOpenLayersMap", this.map);
  }
};
</script>