view client/src/store/fairwayavailability.js @ 4329:83eb99662a91

client: improve ability to test FW diagrams * Restructure code in fairwayavailability.js to seperate external data retrieval and adding the data into the store. Good side effects are that using a mutation for setting test data is easier and only one commit call is necessary, which means less clutter in the state history. * Adding an example how to use this for testing to docs/developers.md . * Bump copyright year for one file.
author Bernhard Reiter <bernhard@intevation.de>
date Wed, 04 Sep 2019 17:05:43 +0200
parents 9f03eb3817d6
children 47aa3be2a7de
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, 2019 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 */

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

import { HTTP } from "@/lib/http";
import {
  format,
  subYears,
  startOfMonth,
  endOfMonth,
  startOfYear,
  endOfYear,
  startOfQuarter,
  endOfQuarter
} from "date-fns";

const LIMITINGFACTORS = {
  WIDTH: "width",
  DEPTH: "depth"
};

const TYPES = {
  BOTTLENECK: "bottleneck",
  SECTION: "section",
  STRETCH: "stretch"
};

const FREQUENCIES = {
  MONTHLY: "monthly",
  QUARTERLY: "quarterly",
  YEARLY: "yearly"
};

const isoFormat = date => {
  return format(date, "YYYY-MM-DD");
};

const getIntervallBorders = (start, end, frequency) => {
  switch (frequency) {
    case FREQUENCIES.MONTHLY:
      return [isoFormat(startOfMonth(start)), isoFormat(endOfMonth(end))];
    case FREQUENCIES.YEARLY:
      return [isoFormat(startOfYear(start)), isoFormat(endOfYear(end))];
    case FREQUENCIES.QUARTERLY:
      return [isoFormat(startOfQuarter(start)), isoFormat(endOfQuarter(end))];
    default:
      throw new Error("Boom!");
  }
};

const init = () => {
  return {
    type: TYPES.BOTTLENECK,
    selectedFairwayAvailabilityFeature: null,
    to: isoFormat(new Date()),
    from: isoFormat(subYears(new Date(), 1)),
    frequency: FREQUENCIES.MONTHLY,
    limitingFactor: null,
    depthlimit1: 230,
    depthlimit2: 250,
    widthlimit1: null,
    widthlimit2: null,
    csv: null,
    fwData: null,
    fwLNWLData: null,
    fwLNWLOverviewData: [],
    legendLNWL: null,
    legend: null,
    LOS: 3
  };
};

/**
 * transformAFD
 * @param {string} csv
 *
 * takes the afd csv and transforms it to an intermediary format
 * for display of diagrams
 *
 * Incoming csv Format
 * #label,# <LDC ,# >= LDC [h],# < 230.00 [h],# >= 230.00 [h],# >= 250.00 [h]
 * 05-2019,215.500,0.000,0.000,215.500
 *
 * Format:
 * $LABEL, $LDC, $BELOWLIMIT1, $BETWEENLIMIT12, $ABOVELIMIT2^
 *
 * This format is assumed to be fix
 *
 */
const transformAFD = csv => {
  return csv.map(e => {
    const result = e.split(",");
    let label, _, ldc, lower, middle, highestLevel;
    let levelsWithSum;
    if (result.length == 6) {
      [label, _, ldc, lower, middle, highestLevel] = result;
      levelsWithSum = [
        {
          height: Number(lower),
          translateY: Number(middle)
        },
        {
          height: Number(middle),
          translateY: 0
        }
      ];
    } else {
      {
        [label, _, ldc, middle, highestLevel] = result;
        levelsWithSum = [
          {
            height: Number(middle),
            translateY: 0
          }
        ];
      }
    }
    return {
      label: label,
      ldc: ldc,
      highestLevel: highestLevel,
      lowerLevels: levelsWithSum
    };
  });
};

const fairwayavailability = {
  init,
  namespaced: true,
  state: init(),
  getters: {
    fwLNWLOverviewData: state => feature => {
      return state.fwLNWLOverviewData.find(
        d => d.feature.get("id") === feature.get("id")
      );
    }
  },
  mutations: {
    type: (state, type) => {
      state.type = type;
    },
    setLOS: (state, LOS) => {
      state.LOS = LOS;
    },
    setFrequency: (state, frequency) => {
      state.frequency = frequency;
    },
    setFrom: (state, from) => {
      state.from = from;
    },
    setTo: (state, to) => {
      state.to = to;
    },
    setDepthlimit1: (state, depthlimit1) => {
      state.depthlimit1 = depthlimit1;
    },
    setDepthlimit2: (state, depthlimit2) => {
      state.depthlimit2 = depthlimit2;
    },
    setWidthlimit1: (state, widthlimit1) => {
      state.widthlimit1 = widthlimit1;
    },
    setWidthlimit2: (state, widthlimit2) => {
      state.widthlimit2 = widthlimit2;
    },
    setSelectedFairwayAvailability: (state, feature) => {
      state.selectedFairwayAvailabilityFeature = feature;
    },
    setFwLNWLData: (state, fwLNWLData) => {
      state.fwLNWLData = fwLNWLData;
    },
    setCSV: (state, csv) => {
      state.csv = csv;
    },
    addFwLNWLOverviewData: (state, data) => {
      let existingIndex = state.fwLNWLOverviewData.findIndex(
        d => d.feature.get("id") === data.feature.get("id")
      );
      if (existingIndex !== -1)
        state.fwLNWLOverviewData.splice(existingIndex, 1);
      state.fwLNWLOverviewData.push(data);
    },
    setLegendLNWL: (state, headerLNWL) => {
      state.headerLNWL = headerLNWL;
    },
    // See docs/developers.md for an example how to directly
    // call this method for testing.
    setAvailableFairwayDepthData: (state, data) => {
      state.csv = data;
      const csv = data.split("\n").filter(x => x !== ""); //omit empty lines

      // setLegend
      const headerEntries = csv.shift().split(",");
      headerEntries.shift();
      headerEntries.shift();
      state.legend = headerEntries.map(x => {
        let entry = x.split("#")[1]; // split leading #
        entry = entry.replace("[h]", "").trim(); // omit unit
        return entry;
      });

      state.fwData = transformAFD(csv);
    }
  },
  actions: {
    loadAvailableFairwayDepth: ({ commit }, options) => {
      return new Promise((resolve, reject) => {
        const {
          feature,
          frequency,
          LOS,
          depthLimit1,
          depthLimit2,
          widthLimit1,
          widthLimit2,
          limitingFactor,
          type
        } = options;
        let { from, to } = options;
        let name = feature.hasOwnProperty("properties")
          ? feature.properties.name
          : feature.get("objnam");
        [from, to] = getIntervallBorders(from, to, frequency);
        let additionalParams = "";
        let endpoint = type;
        if (type === TYPES.BOTTLENECK) {
          if (limitingFactor === LIMITINGFACTORS.DEPTH)
            additionalParams = `&breaks=${depthLimit1},${depthLimit2}`;
          if (limitingFactor === LIMITINGFACTORS.WIDTH)
            additionalParams = `&breaks=${widthLimit1},${widthLimit2}`;
        } else if (type == TYPES.SECTION || type == TYPES.STRETCH) {
          additionalParams = `&depthbreaks=${depthLimit1},${depthLimit2}&widthbreaks=${widthLimit1},${widthLimit2}`;
        }
        const start = encodeURIComponent("00:00:00+00:00");
        const end = encodeURIComponent("23:59:59+00:00");
        const URL = `data/${endpoint}/fairway-depth/${encodeURIComponent(
          name
        )}?from=${from}T${start}&to=${to}T${end}&mode=${frequency}&los=${LOS}${additionalParams}`;
        HTTP.get(URL, {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            commit("setAvailableFairwayDepthData", response.data);
            resolve(response);
          })
          .catch(error => {
            reject(error);
          });
      });
    },
    loadAvailableFairwayDepthLNWL: (context, options) => {
      return new Promise((resolve, reject) => {
        const {
          feature,
          frequency,
          LOS,
          depthLimit1,
          depthLimit2,
          widthLimit1,
          widthLimit2,
          limitingFactor,
          type
        } = options;
        let { from, to } = options;
        let name = feature.hasOwnProperty("properties")
          ? feature.properties.name
          : feature.get("objnam");
        [from, to] = getIntervallBorders(from, to, frequency);
        const start = encodeURIComponent("00:00:00+00:00");
        const end = encodeURIComponent("23:59:59+00:00");
        let additionalParams = "";
        let endpoint = type || TYPES.BOTTLENECK;
        if (type === TYPES.BOTTLENECK) {
          if (limitingFactor === LIMITINGFACTORS.DEPTH)
            additionalParams = `&breaks=${depthLimit1},${depthLimit2}`;
          if (limitingFactor === LIMITINGFACTORS.WIDTH)
            additionalParams = `&breaks=${widthLimit1},${widthLimit2}`;
        } else if (type == TYPES.SECTION || type == TYPES.STRETCH) {
          additionalParams = `&depthbreaks=${depthLimit1},${depthLimit2}&widthbreaks=${widthLimit1},${widthLimit2}`;
        }
        const URL = `data/${endpoint}/availability/${encodeURIComponent(
          name
        )}?from=${from}T${start}&to=${to}T${end}&mode=${frequency}&los=${LOS}${additionalParams}`;
        HTTP.get(URL, {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            const { data } = response;
            resolve(data);
          })
          .catch(error => {
            reject(error);
          });
      });
    },
    loadAvailableFairwayDepthLNWLDiagram: ({ commit, dispatch }, options) => {
      dispatch("loadAvailableFairwayDepthLNWL", options).then(response => {
        commit("setCSV", response);
        let data = response.split("\n").filter(d => d);
        data.shift(); // remove header line
        data = data.map(d => {
          let columns = d.split(",");
          let result;
          if (columns.length === 6) {
            result = {
              date: columns[0],
              ldc: Number(columns[2]),
              below: Number(columns[3]),
              between: Number(columns[4]),
              above: Number(columns[5])
            };
          } else {
            result = {
              date: columns[0],
              ldc: Number(columns[2]),
              below: Number(columns[3]),
              between: null,
              above: Number(columns[4])
            };
          }
          return result;
        });
        commit("setFwLNWLData", data);
        return data;
      });
    },
    loadAvailableFairwayDepthLNWLForMap: ({ dispatch }, options) => {
      return dispatch("loadAvailableFairwayDepthLNWL", options).then(
        response => {
          let data = response.split("\n").filter(d => d);
          data.shift(); // remove header line
          data = data.map(d => {
            let columns = d.split(",");
            return {
              ldc: Number(columns[2]),
              below: Number(columns[3]),
              between: Number(columns[4]),
              above: Number(columns[5])
            };
          });
          return data[0];
        }
      );
    }
  }
};

export { LIMITINGFACTORS, FREQUENCIES, TYPES, fairwayavailability };