view client/src/fairway/Fairwayprofile.vue @ 1063:7ec2133c6404

client: add area measurement. simpify code * Add a third draw mode which can only be activated when no morphology is selected and we are already in LineString mode. It adds an area calculation. Because the Polygon drawMode ends on a double click, there needs to be an extra callback for this to run identify so that the area calculation is shown all times. * Add Bernhard as author to some files and also simplify copyright note. * Remove DRAWMODES in the code to simplify as this is just one indirection used once in stores/application.js. * Use mapState instead mapGetters to get the drawMode at all places to save some code lines.
author Bernhard Reiter <bernhard@intevation.de>
date Thu, 25 Oct 2018 23:16:53 +0200
parents d5d72756645a
children 82ae9cb56982
line wrap: on
line source

<template>
    <div class="profiledisplay d-flex flex-row">
        <div class="fairwayprofile"></div>
        <div class="additionalsurveys d-flex flex-column">
            <small class="label">Available Additional Surveys</small>
            <select v-model="additionalSurvey" @change="selectAdditionalSurveyData">
                <option value="">None</option>
                <option
                    v-for="survey in additionalSurveys"
                    :key="survey.date_info"
                >{{survey.date_info}}</option>
            </select>
            <small class="mt-2">
                <b>Start:</b>
                <br>
                Lat: {{ startPoint[1] }}
                <br>
                Lon: {{ startPoint[0] }}
                <br>
                <b>End:</b>
                <br>
                Lat: {{ endPoint[1] }}
                <br>
                Lon: {{ endPoint[0] }}
                <br>
            </small>
        </div>
    </div>
</template>

<style scoped lang="scss">
.label {
  margin-bottom: $small-offset;
}
.waterlevelselection {
  margin-top: $large-offset;
  margin-right: $large-offset;
}

.additionalsurveys {
  width: 300px;
  margin-top: $large-offset;
  margin-bottom: auto;
  margin-right: $large-offset;
  margin-left: auto;
}

.additionalsurveys input {
  margin-right: $small-offset;
}

.profiledisplay {
  width: 100vw;
}

.fairwayprofile {
  background-color: white;
  margin-left: auto;
  margin-right: $offset;
  margin-top: auto;
  margin-bottom: auto;
}
</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>
 */
import * as d3 from "d3";
import { mapState } from "vuex";
import { displayInfo } from "../application/lib/errors.js";

const GROUND_COLOR = "#4A2F06";

export default {
  name: "fairwayprofile",
  props: [
    "width",
    "height",
    "xScale",
    "yScaleLeft",
    "yScaleRight",
    "margin",
    "totalLength",
    "waterLevels",
    "fairwayCoordinates",
    "selectedWaterLevel",
    "minAlt",
    "maxAlt",
    "additionalSurveys"
  ],
  computed: {
    ...mapState("fairwayprofile", [
      "startPoint",
      "endPoint",
      "currentProfile",
      "selectedMorph"
    ]),
    currentData() {
      const currentSurveyDate = this.selectedMorph.date_info;
      return this.currentProfile[currentSurveyDate];
    },
    additionalData() {
      return this.currentProfile[this.additionalSurvey];
    },
    waterColor() {
      const result = this.waterLevels.find(
        x => x.level === this.selectedWaterLevel
      );
      return result.color;
    }
  },
  data() {
    return {
      additionalSurvey: "",
      wait: false
    };
  },
  watch: {
    currentData() {
      this.drawDiagram();
    },
    width() {
      this.drawDiagram();
    },
    height() {
      this.drawDiagram();
    },
    waterLevels() {
      this.drawDiagram();
    },
    selectedWaterLevel() {
      this.drawDiagram();
    },
    fairwayCoordinates() {
      this.drawDiagram();
    }
  },
  methods: {
    selectAdditionalSurveyData() {
      if (!this.additionalSurvey || this.wait) return;
      //   this.$store
      //     .dispatch("fairwayprofile/loadProfile", this.additionalSurvey)
      //     .then(() => {
      //       this.wait = false;
      //       this.drawDiagram();
      //     })
      //     .catch(error => {
      //       this.wait = false;
      //       let status = "ERROR";
      //       let data = error;
      //       const response = error.response;
      //       if (response) {
      //         status = response.status;
      //         data = response.data;
      //       }
      //       displayError({
      //         title: "Backend Error",
      //         message: `${status}: ${data.message || data}`
      //       });
      //     });
      displayInfo({
        title: "Not Implemented",
        message: `Currently under development`
      });
    },
    drawDiagram() {
      const chartDiv = document.querySelector(".fairwayprofile");
      d3.select("svg").remove();
      let svg = d3.select(chartDiv).append("svg");
      svg.attr("width", this.width);
      svg.attr("height", this.height);
      const width = this.width - this.margin.right - 1.5 * this.margin.left;
      const height = this.height - this.margin.top - 2 * this.margin.bottom;
      const currentData = this.currentData;
      const additionalData = this.additionalData;
      const {
        xScale,
        yScaleRight,
        yScaleLeft,
        graph
      } = this.generateCoordinates(svg, height, width);
      this.drawWaterlevel({
        graph,
        xScale,
        yScaleRight,
        height,
        width
      });
      this.drawProfile({
        graph,
        xScale,
        yScaleRight,
        currentData,
        height,
        width,
        color: GROUND_COLOR,
        strokeColor: "black"
      });
      if (additionalData) {
        this.drawProfile({
          graph,
          xScale,
          yScaleRight,
          additionalData,
          height,
          width,
          color: "green",
          strokeColor: "green"
        });
      }
      this.drawLabels({
        graph,
        xScale,
        yScaleLeft,
        currentData,
        height,
        width
      });
      this.drawFairway({
        graph,
        xScale,
        yScaleRight,
        currentData,
        height,
        width
      });
    },
    drawFairway({ graph, xScale, yScaleRight }) {
      for (let coordinates of this.fairwayCoordinates) {
        const [startPoint, endPoint, depth] = coordinates;
        let fairwayArea = d3
          .area()
          .x(function(d) {
            return xScale(d.x);
          })
          .y0(yScaleRight(0))
          .y1(function(d) {
            return yScaleRight(d.y);
          });
        graph
          .append("path")
          .datum([{ x: startPoint, y: -depth }, { x: endPoint, y: -depth }])
          .attr("fill", "#002AFF")
          .attr("stroke-opacity", 0.65)
          .attr("fill-opacity", 0.65)
          .attr("stroke", "#FFD20D")
          .attr("d", fairwayArea);
      }
    },
    drawLabels({ graph, height }) {
      graph
        .append("text")
        .attr("transform", ["rotate(-90)"])
        .attr("y", this.width - 60)
        .attr("x", -(this.height - this.margin.top - this.margin.bottom) / 2)
        .attr("dy", "1em")
        .attr("fill", "black")
        .style("text-anchor", "middle")
        .text("Depth [m]");
      graph
        .append("text")
        .attr("y", 0 - this.margin.left)
        .attr("x", 0 - height / 4)
        .attr("dy", "1em")
        .attr("fill", "black")
        .style("text-anchor", "middle")
        .attr("transform", [
          "translate(" + this.width / 2 + "," + this.height + ")",
          "rotate(0)"
        ])
        .text("Width [m]");
    },
    generateCoordinates(svg, height, width) {
      let xScale = d3
        .scaleLinear()
        .domain(this.xScale)
        .rangeRound([0, width]);

      xScale.ticks(5);
      let yScaleLeft = d3
        .scaleLinear()
        .domain(this.yScaleLeft)
        .rangeRound([height, 0]);

      let yScaleRight = d3
        .scaleLinear()
        .domain(this.yScaleRight)
        .rangeRound([height, 0]);

      let xAxis = d3.axisBottom(xScale);
      let yAxis2 = d3.axisRight(yScaleRight);
      let graph = svg
        .append("g")
        .attr(
          "transform",
          "translate(" + this.margin.left + "," + this.margin.top + ")"
        );
      graph
        .append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis.ticks(5));
      graph
        .append("g")
        .attr("transform", "translate(" + width + ",0)")
        .call(yAxis2);
      return { xScale, yScaleLeft, yScaleRight, graph };
    },
    drawWaterlevel({ graph, xScale, yScaleRight, height }) {
      let waterArea = d3
        .area()
        .x(function(d) {
          return xScale(d.x);
        })
        .y0(height)
        .y1(function(d) {
          return yScaleRight(d.y);
        });
      graph
        .append("path")
        .datum([{ x: 0, y: 0 }, { x: this.totalLength, y: 0 }])
        .attr("fill", this.waterColor)
        .attr("stroke", this.waterColor)
        .attr("d", waterArea);
    },
    drawProfile({
      graph,
      xScale,
      yScaleRight,
      currentData,
      height,
      color,
      strokeColor
    }) {
      for (let part of currentData) {
        let profileLine = d3
          .line()
          .x(d => {
            return xScale(d.x);
          })
          .y(d => {
            return yScaleRight(-d.y);
          });
        let profileArea = d3
          .area()
          .x(function(d) {
            return xScale(d.x);
          })
          .y0(height)
          .y1(function(d) {
            return yScaleRight(-d.y);
          });
        graph
          .append("path")
          .datum(part)
          .attr("fill", color)
          .attr("stroke", color)
          .attr("stroke-width", 3)
          .attr("d", profileArea);
        graph
          .append("path")
          .datum(part)
          .attr("fill", "none")
          .attr("stroke", strokeColor)
          .attr("stroke-linejoin", "round")
          .attr("stroke-linecap", "round")
          .attr("stroke-width", 3)
          .attr("d", profileLine);
      }
    }
  },
  mounted() {
    this.drawDiagram();
  }
};
</script>