view client/src/components/fairway/AvailableFairwayDepth.vue @ 3184:1ba2a7d22fbb

available_fairway_depth: display selected feature
author Thomas Junk <thomas.junk@intevation.de>
date Tue, 07 May 2019 16:59:11 +0200
parents 429e28295902
children 505414dfe3e7
line wrap: on
line source

<template>
  <div class="d-flex flex-column flex-fill">
    <UIBoxHeader icon="chart-area" :title="title" :closeCallback="close" />
    <UISpinnerOverlay v-if="loading" />
    <div class="mt-3 d-flex flex-row">
      <div class="d-flex flex-column mr-auto">
        <div class="d-flex flex-row">
          <div class="mr-3 my-auto ml-auto">
            <select class="form-control mr-3">
              <option>Monthly</option>
              <option>Quaterly</option>
              <option>Yearly</option>
            </select>
          </div>
          <div class="d-flex flex-row mr-3">
            <label for="from" class="my-auto mr-3"
              ><translate>from</translate></label
            ><input class="form-control" type="date" />
          </div>
          <div class="d-flex flex-row mr-auto">
            <label for="to" class="my-auto mr-3"
              ><translate>to</translate></label
            ><input class="form-control" type="date" />
          </div>
        </div>
        <div :id="containerId" class="diagram-container"></div>
      </div>
    </div>
  </div>
</template>

<style></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 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 * Markus Kottländer <markus.kottlaender@intevation.de>
 */
import * as d3 from "d3";
import app from "@/main";
import { displayError } from "@/lib/errors";
import debounce from "debounce";
import { HTTP } from "@/lib/http";
import { diagram } from "@/lib/mixins";
import { mapState } from "vuex";

const MOCKDATA = `#label,# >= LDC [h],# < 200.00 [h],# >= 200.00 [h],# >= 230.00 [h],# >= 250.00 [h]
01-2019, 22.000,1.000, 4.000,6.000, 20.000
02-2019, 24.000,0.000,0.000,0.000, 23.000
03-2019, 30.000,0.000,0.000,0.000, 30.000
04-2019, 30.000,0.000,0.000,0.000, 30.000
05-2019, 30.000,0.000,0.000,0.000, 30.000`;

export default {
  mixins: [diagram],
  data() {
    return {
      containerId: "availablefairwaydepth",
      loading: false,
      fwData: null,
      width: 1000,
      height: 600,
      paddingRight: 100,
      spaceBetween: 80,
      labelPaddingTop: 15,
      scalePaddingLeft: 50,
      paddingTop: 10,
      legend: "",
      diagram: null,
      yScale: null,
      barsWidth: 60,
      dimensions: null
    };
  },
  created() {
    window.addEventListener("resize", debounce(this.drawDiagram), 100);
  },
  mounted() {
    this.loadData();
  },
  computed: {
    ...mapState("diagram", ["selectedFairwayAvailabilityFeature"]),
    availability() {
      return this.plainAvailability;
    },
    title() {
      return `Available Fairway Depth: ${this.featureName}`;
    },
    featureName() {
      return this.selectedFairwayAvailabilityFeature.properties.name;
    }
  },
  methods: {
    close() {
      this.$store.commit("application/paneSetup", "DEFAULT");
    },
    prepareLegend(header) {
      const headerEntries = header.split(",");
      headerEntries.shift();
      return headerEntries.map(x => {
        return x.split("#")[1].trim();
      });
    },
    prepare(data) {
      const csv = data.split("\n");
      this.legend = this.prepareLegend(csv.shift());
      let transformed = csv.map(e => {
        const result = e.split(",");
        const label = result.shift();
        const ldc = result.shift();
        const highestLevel = result.pop();
        return {
          label: label,
          ldc: ldc,
          highestLevel: highestLevel,
          lowerLevels: result
        };
      });
      this.fwData = transformed;
    },
    loadData() {
      const URL = `/data/bottleneck/fairway-depth/${
        this.featureName
      }?from=2019-01-01T15:04:05%2b00:00&to=2019-05-02T15:04:05%2b07:00&mode=monthly`;
      HTTP.get(URL, {
        headers: { "X-Gemma-Auth": localStorage.getItem("token") }
      })
        .then(() => {
          // const { data } = response;
          this.prepare(MOCKDATA);
          this.drawDiagram();
        })
        .catch(error => {
          console.log(error);
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Backend Error"),
            message: `${status}: ${data.message || data}`
          });
        });
    },
    drawDiagram() {
      this.dimensions = this.getDimensions();
      this.yScale = d3
        .scaleLinear()
        .domain([-33, 33])
        .range([this.dimensions.mainHeight - 30, 0]);
      d3.select(".diagram-container svg").remove();
      this.generateDiagramContainer();
      this.drawBars();
      this.drawScaleLabel();
      this.drawScale();
    },
    generateDiagramContainer() {
      const diagram = d3
        .select(".diagram-container")
        .append("svg")
        .attr("width", this.dimensions.width)
        .attr("height", this.dimensions.mainHeight);
      this.diagram = diagram
        .append("g")
        .attr("transform", `translate(0 ${this.paddingTop})`);
    },
    drawBars() {
      const everyBar = this.diagram
        .selectAll("g")
        .data(this.fwData)
        .enter()
        .append("g")
        .attr("transform", (d, i) => {
          const dx = this.paddingRight + i * this.spaceBetween;
          return `translate(${dx})`;
        });
      this.drawSingleBars(everyBar);
      this.drawLabelPerBar(everyBar);
    },
    drawSingleBars(everyBar) {
      this.drawLDC(everyBar);
      this.drawHighestLevel(everyBar);
      this.drawLowerLevels(everyBar);
    },
    drawLowerLevels(everyBar) {
      everyBar
        .selectAll("g")
        .data(d => d.lowerLevels.reverse())
        .enter()
        .append("rect")
        .attr("y", this.yScale(0))
        .attr("height", d => {
          return this.yScale(0) - this.yScale(d);
        })
        .attr("width", this.barsWidth)
        .attr("fill", (d, i) => {
          return this.$options.COLORS.REST[i];
        });
    },
    fnheight(name) {
      return d => this.yScale(0) - this.yScale(d[name]);
    },
    drawLDC(everyBar) {
      const height = this.fnheight("ldc");
      everyBar
        .append("rect")
        .attr("y", this.yScale(0))
        .attr("height", height)
        .attr("width", this.barsWidth)
        .attr("transform", d => `translate(0 ${-1 * height(d)})`)
        .attr("fill", this.$options.COLORS.LDC);
    },
    drawHighestLevel(everyBar) {
      const height = this.fnheight("highestLevel");
      everyBar
        .append("rect")
        .attr("y", this.yScale(0))
        .attr("height", height)
        .attr("width", this.barsWidth)
        .attr("transform", d => `translate(0 ${-1 * height(d)})`)
        .attr("fill", this.$options.COLORS.HIGHEST);
    },
    drawLabelPerBar(everyBar) {
      everyBar
        .append("text")
        .text(d => d.label)
        .attr("y", this.yScale(0) + this.labelPaddingTop);
    },
    drawScaleLabel() {
      const center = this.dimensions.mainHeight / 2;
      this.diagram
        .append("text")
        .text(this.$options.LEGEND)
        .attr("text-anchor", "middle")
        .attr("x", 0)
        .attr("y", 0)
        .attr("dy", "1em")
        .attr("transform", `translate(0, ${center}), rotate(-90)`);
    },
    drawScale() {
      const yAxis = d3.axisLeft().scale(this.yScale);
      this.diagram
        .append("g")
        .attr("transform", `translate(${this.scalePaddingLeft})`)
        .call(yAxis);
    }
  },
  LEGEND: app.$gettext("sum of days / Summe der Tage"),
  COLORS: {
    LDC: "#59C6FF",
    HIGHEST: "#2D84B3",
    REST: ["#FF424F", "#FF737C", "#FF99A0"]
  }
};
</script>