view client/src/store/fairwayprofile.js @ 3207:ba7bc3740fb3

client: renamed store modules to better reflect their context
author Markus Kottlaender <markus@intevation.de>
date Wed, 08 May 2019 17:43:18 +0200
parents
children 30a47d9fc667
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";
import { generateFeatureRequest } from "@/lib/geo";
import { getLength } from "ol/sphere";
import { displayError } from "@/lib/errors";
import { featureToFairwayCoordinates } from "@/lib/geo";

// initial state
const init = () => {
  return {
    additionalSurvey: null,
    minAlt: 0,
    maxAlt: 0,
    currentProfile: {},
    referenceWaterLevel: null,
    waterLevels: {},
    selectedWaterLevel: "",
    fairwayData: [],
    startPoint: null,
    endPoint: null,
    previousCuts: [],
    profileLoading: false,
    selectedCut: null,
    differencesLoading: false
  };
};

export default {
  init,
  namespaced: true,
  state: init(),
  getters: {
    totalLength: state => {
      const keys = Object.keys(state.currentProfile);
      return keys.length
        ? Math.max(...keys.map(x => state.currentProfile[x].length))
        : 0;
    },
    additionalSurvey: state => {
      return state.additionalSurvey;
    }
  },
  mutations: {
    additionalSurvey: (state, additionalSurvey) => {
      state.additionalSurvey = additionalSurvey;
    },
    setSelectedWaterLevel: (state, level) => {
      state.selectedWaterLevel = state.waterLevels[level];
    },
    setDifferencesLoading: (state, value) => {
      state.differencesLoading = value;
    },
    profileLoaded: (state, answer) => {
      const { response, surveyDate } = answer;
      const { data } = response;
      const { waterlevel } = response.data.properties;
      const { value, when } = waterlevel;
      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
      const entry = {
        date: when,
        value: value
      };
      state.waterLevels = { [when]: entry };
      state.selectedWaterLevel = entry;
      Vue.set(state.currentProfile, surveyDate, {
        points: result.points,
        length: result.lengthPolyLine
      });
      if (!state.minAlt || state.minAlt > result.minAlt) {
        state.minAlt = result.minAlt;
      }
      if (!state.maxAlt || state.maxAlt < result.maxAlt) {
        state.maxAlt = result.maxAlt;
      }
    },
    setStartPoint: (state, start) => {
      state.startPoint = start;
    },
    setEndPoint: (state, end) => {
      state.endPoint = end;
    },
    addFairwayData: (state, coordinates) => {
      state.fairwayData.push(coordinates);
    },
    clearFairwayData: state => {
      state.fairwayData = [];
    },
    clearCurrentProfile: state => {
      state.currentProfile = {};
      state.minAlt = null;
      state.maxAlt = null;
      state.totalLength = null;
      state.fairwayData = [];
      state.startPoint = null;
      state.endPoint = null;
      state.referenceWaterLevel = null;
      state.waterLevels = {};
      state.selectedWaterLevel = "";
    },
    previousCuts: (state, previousCuts) => {
      state.previousCuts = previousCuts;
    },
    profileLoading: (state, loading) => {
      state.profileLoading = loading;
    },
    selectedCut: (state, cut) => {
      state.selectedCut = cut;
    }
  },
  actions: {
    clearCurrentProfile({ commit, rootState }) {
      commit("clearCurrentProfile");
      commit("map/cutToolEnabled", false, { root: true });
      rootState.map.openLayersMaps.forEach(m => {
        m.getLayer("CUTTOOL")
          .getSource()
          .clear();
      });
    },
    loadProfile({ commit, state }, survey) {
      if (state.startPoint && state.endPoint) {
        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 => {
              if (response.data.geometry.coordinates.length) {
                commit("profileLoaded", {
                  response: response,
                  surveyDate: survey.date_info
                });
                resolve(response);
              } else {
                commit("clearCurrentProfile");
                reject({
                  response: {
                    status: null,
                    data: "No intersection with sounding data."
                  }
                });
              }
            })
            .catch(error => reject(error));
        });
      }
    },
    cut({ commit, dispatch, state, rootState, rootGetters }, cut) {
      return new Promise(resolve => {
        const length = getLength(cut.getGeometry());
        commit(
          "map/setCurrentMeasurement",
          {
            quantity: "Length",
            unitSymbol: "m",
            value: Math.round(length * 10) / 10
          },
          { root: true }
        );
        commit("clearFairwayData");
        // 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) {
          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]);

          const profileLoaders = [
            dispatch("loadProfile", rootState.bottlenecks.selectedSurvey)
          ];
          if (state.additionalSurvey) {
            profileLoaders.push(
              dispatch("loadProfile", state.additionalSurvey)
            );
          }

          commit("profileLoading", true);
          Promise.all(profileLoaders)
            .then(() => {
              commit("map/cutToolEnabled", false, { root: true });
              const los3 = rootGetters["map/openLayersMap"]().getLayer(
                "FAIRWAYDIMENSIONSLOS3"
              );
              los3.getSource().forEachFeatureIntersectingExtent(
                profileLine
                  .clone()
                  .transform("EPSG:4326", "EPSG:3857")
                  .getExtent(),
                feature => {
                  const fairwayCoordinates = featureToFairwayCoordinates(
                    feature,
                    profileLine
                  );
                  let fairwayData = {
                    coordinates: fairwayCoordinates,
                    style: los3.getStyle()
                  };
                  if (fairwayCoordinates.length > 0) {
                    commit("addFairwayData", fairwayData);
                  }
                }
              );
              const los2 = rootGetters["map/openLayersMap"]().getLayer(
                "FAIRWAYDIMENSIONSLOS2"
              );
              los2.getSource().forEachFeatureIntersectingExtent(
                profileLine
                  .clone()
                  .transform("EPSG:4326", "EPSG:3857")
                  .getExtent(),
                feature => {
                  let fairwayCoordinates = featureToFairwayCoordinates(
                    feature,
                    profileLine
                  );
                  let fairwayData = {
                    coordinates: fairwayCoordinates,
                    style: los2.getStyle()
                  };
                  if (fairwayCoordinates.length > 0) {
                    commit("addFairwayData", fairwayData);
                  }
                }
              );
              const los1 = rootGetters["map/openLayersMap"]().getLayer(
                "FAIRWAYDIMENSIONSLOS1"
              );
              los1.getSource().forEachFeatureIntersectingExtent(
                profileLine
                  .clone()
                  .transform("EPSG:4326", "EPSG:3857")
                  .getExtent(),
                feature => {
                  const fairwayCoordinates = featureToFairwayCoordinates(
                    feature,
                    profileLine
                  );
                  let fairwayData = {
                    coordinates: fairwayCoordinates,
                    style: los1.getStyle()
                  };
                  if (fairwayCoordinates.length > 0) {
                    commit("addFairwayData", fairwayData);
                  }
                }
              );
              resolve();
            })
            .catch(error => {
              const { status, data } = error.response;
              displayError({
                title: "Backend Error",
                message: `${status ? status + ":" : ""} ${data.message || data}`
              });
            })
            .finally(() => {
              commit("application/paneRotate", 1, { root: true });
              if (state.additionalSurvey) {
                commit(
                  "application/paneSetup",
                  "COMPARESURVEYS_FAIRWAYPROFILE",
                  { root: true }
                );
              } else {
                commit("application/paneSetup", "FAIRWAYPROFILE", {
                  root: true
                });
              }
              commit("profileLoading", false);
            });
        }
      });
    },
    previousCuts({ commit, rootState }) {
      const previousCuts =
        JSON.parse(localStorage.getItem("previousCuts")) || [];
      commit(
        "previousCuts",
        previousCuts
          .filter(cut => {
            return (
              cut.bottleneckName === rootState.bottlenecks.selectedBottleneck
            );
          })
          .sort((a, b) => (a.timestamp < b.timestamp ? 1 : -1))
      );
    }
  }
};