view client/src/store/fairway.js @ 1372:553aadd97087

new cross profile workflow (WIP) Needs fixing of some bugs and not so nice looks.
author Markus Kottlaender <markus@intevation.de>
date Tue, 27 Nov 2018 12:59:26 +0100
parents ca33ad696594
children fa7d647f8d77
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 "../lib/http";
import { prepareProfile } from "../lib/geo";
import LineString from "ol/geom/LineString.js";
import { generateFeatureRequest } from "../lib/geo.js";
import { getLength } from "ol/sphere.js";
import { calculateFairwayCoordinates } from "../lib/geo.js";
import { displayError } from "../lib/errors.js";

const DEMOLEVEL = 149.345;
const DEMODATA = 2.5;

// initial state
const init = () => {
  return {
    additionalSurvey: "",
    totalLength: 0,
    minAlt: 0,
    maxAlt: 0,
    currentProfile: {},
    waterLevels: [{ year: "2016", level: DEMOLEVEL, color: "#005DFF" }],
    selectedWaterLevel: DEMOLEVEL,
    fairwayCoordinates: [],
    startPoint: null,
    endPoint: null,
    previousCuts: [],
    profileLoading: false
  };
};

export default {
  init,
  namespaced: true,
  state: init(),
  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;
    },
    profileLoading: (state, loading) => {
      state.profileLoading = loading;
    }
  },
  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) {
      if (state.startPoint && state.endPoint) {
        return new Promise((resolve, reject) => {
          commit("profileLoading", true);
          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);
            })
            .finally(() => commit("profileLoading", false));
        });
      }
    },
    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(() => {
            rootState.map.cutTool.setActive(false);
            rootGetters["map/getVSourceByName"](
              "Fairway Dimensions"
            ).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
          );
        })
      );
    }
  }
};