view client/src/store/fairway.js @ 1237:74562dc29e10

refactored drawtool The map interactions (ol/interaction/Draw) were previously always removed and re-created. Now there are created and added to the map once and their active flag is used to toggle the tools. Results in cleaner code and easier separation of the buttons in the future.
author Markus Kottlaender <markus@intevation.de>
date Tue, 20 Nov 2018 13:03:24 +0100
parents 48ae4458710d
children bc55ffaeb639
line wrap: on
line source

/*
 * 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 by via donau 
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 * 
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Markus Kottländer <markuks.kottlaender@intevation.de>
 */
import Vue from "vue";
import { HTTP } from "../application/lib/http";
import { prepareProfile } from "../application/lib/geo";
import LineString from "ol/geom/LineString.js";
import { generateFeatureRequest } from "../application/lib/geo.js";
import { getLength } from "ol/sphere.js";
import { calculateFairwayCoordinates } from "../application/lib/geo.js";
import { displayError } from "../application/lib/errors.js";

const DEMOLEVEL = 149.345;
const DEMODATA = 2.5;

export default {
  namespaced: true,
  state: {
    additionalSurvey: "",
    totalLength: 0,
    minAlt: 0,
    maxAlt: 0,
    currentProfile: {},
    waterLevels: [{ year: "2016", level: DEMOLEVEL, color: "#005DFF" }],
    selectedWaterLevel: DEMOLEVEL,
    fairwayCoordinates: [],
    startPoint: null,
    endPoint: null,
    previousCuts: []
  },
  getters: {
    length: state => {
      return state.totalLength;
    },
    additionalSurvey: state => {
      return state.additionalSurvey;
    }
  },
  mutations: {
    setAdditionalSurvey: (state, additionalSurvey) => {
      state.additionalSurvey = additionalSurvey;
    },
    setSelectedWaterLevel: (state, level) => {
      state.selectedWaterLevel = level;
    },
    profileLoaded: (state, answer) => {
      const { response, surveyDate } = answer;
      const { data } = response;
      const coordinates = data.geometry.coordinates;
      if (!coordinates) return;
      const startPoint = state.startPoint;
      const endPoint = state.endPoint;
      const geoJSON = data;
      const result = prepareProfile({ geoJSON, startPoint, endPoint });
      // Use Vue.set() to make new object properties rective
      // https://vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
      Vue.set(state.currentProfile, surveyDate, result.points);
      if (!state.minAlt || state.minAlt > result.minAlt) {
        state.minAlt = result.minAlt;
      }
      if (!state.maxAlt || state.maxAlt < result.maxAlt) {
        state.maxAlt = result.maxAlt;
      }
      if (!state.totalLength || state.totalLength < result.lengthPolyLine) {
        state.totalLength = result.lengthPolyLine;
      }
    },
    setStartPoint: (state, start) => {
      state.startPoint = start;
    },
    setEndPoint: (state, end) => {
      state.endPoint = end;
    },
    setFairwayCoordinates: (state, coordinates) => {
      state.fairwayCoordinates = coordinates;
    },
    clearCurrentProfile: state => {
      state.additionalSurvey = "";
      state.currentProfile = {};
      state.minAlt = null;
      state.maxAlt = null;
      state.totalLength = null;
      state.fairwayCoordinates = [];
      state.startPoint = null;
      state.endPoint = null;
    },
    previousCuts: (state, previousCuts) => {
      state.previousCuts = previousCuts;
    }
  },
  actions: {
    clearSelection({ commit, dispatch, rootGetters, rootState }) {
      dispatch("bottlenecks/setSelectedBottleneck", null, { root: true });
      commit("clearCurrentProfile");
      commit("application/showSplitscreen", false, { root: true });
      rootState.map.cutTool.setActive(false);
      rootGetters["map/getLayerByName"]("Cut Tool")
        .data.getSource()
        .clear();
    },
    loadProfile({ commit, state }, survey) {
      return new Promise((resolve, reject) => {
        const profileLine = new LineString([state.startPoint, state.endPoint]);
        const geoJSON = generateFeatureRequest(
          profileLine,
          survey.bottleneck_id,
          survey.date_info
        );
        HTTP.post("/cross", geoJSON, {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            commit("profileLoaded", {
              response: response,
              surveyDate: survey.date_info
            });
            resolve(response);
          })
          .catch(error => {
            reject(error);
          });
      });
    },
    cut({ commit, dispatch, rootState, rootGetters }, cut) {
      const length = getLength(cut.getGeometry());
      commit(
        "map/setCurrentMeasurement",
        {
          quantity: "Length",
          unitSymbol: "m",
          value: Math.round(length * 10) / 10
        },
        { root: true }
      );

      // if a survey has been selected, request a profile
      // TODO an improvement could be to check if the line intersects
      // with the bottleneck area's polygon before trying the server request
      if (rootState.bottlenecks.selectedSurvey) {
        commit("clearCurrentProfile");
        const inputLineString = cut.getGeometry().clone();
        inputLineString.transform("EPSG:3857", "EPSG:4326");
        const [start, end] = inputLineString
          .getCoordinates()
          .map(coords => coords.map(coord => parseFloat(coord.toFixed(8))));
        commit("setStartPoint", start);
        commit("setEndPoint", end);
        const profileLine = new LineString([start, end]);
        dispatch("loadProfile", rootState.bottlenecks.selectedSurvey)
          .then(() => {
            rootGetters["map/getLayerByName"]("Fairway Dimensions")
              .data.getSource()
              .forEachFeatureIntersectingExtent(
                // need to use EPSG:3857 which is the proj of vectorSource
                profileLine
                  .clone()
                  .transform("EPSG:4326", "EPSG:3857")
                  .getExtent(),
                feature => {
                  // transform back to prepare for usage
                  var intersectingPolygon = feature
                    .getGeometry()
                    .clone()
                    .transform("EPSG:3857", "EPSG:4326");
                  const fairwayCoordinates = calculateFairwayCoordinates(
                    profileLine,
                    intersectingPolygon,
                    DEMODATA
                  );
                  commit("setFairwayCoordinates", fairwayCoordinates);
                }
              );
          })
          .then(() => {
            commit("application/showSplitscreen", true, { root: true });
          })
          .catch(error => {
            const { status, data } = error.response;
            displayError({
              title: "Backend Error",
              message: `${status}: ${data.message || data}`
            });
          });
      }
    },
    previousCuts({ commit, rootState }) {
      const previousCuts =
        JSON.parse(localStorage.getItem("previousCuts")) || [];
      commit(
        "previousCuts",
        previousCuts.filter(cut => {
          return (
            cut.bottleneckName === rootState.bottlenecks.selectedBottleneck
          );
        })
      );
    }
  }
};