changeset 4362:89d2d9309a38

Merge in fa-round-in-backend branch
author Bernhard Reiter <bernhard@intevation.de>
date Mon, 09 Sep 2019 17:25:28 +0200
parents 90b72e811efd (current diff) 8a26225b6133 (diff)
children d6439e7c8b1c
files
diffstat 4 files changed, 119 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/fairway/AvailableFairwayDepth.vue	Mon Sep 09 17:13:37 2019 +0200
+++ b/client/src/components/fairway/AvailableFairwayDepth.vue	Mon Sep 09 17:25:28 2019 +0200
@@ -93,15 +93,6 @@
 import { FREQUENCIES } from "@/store/fairwayavailability";
 import { defaultDiagramTemplate } from "@/lib/DefaultDiagramTemplate";
 
-// FIXME This is a rounding methods that shows that we have fractions,
-// because we are coming from hours.  Users will understand the underlying
-// math better and we can see if this is wanted.
-// With the backend just giving us the summarized hours, we cannot do
-// a classification of each day into a category.
-// (The name of the function is kept to keep the diff more readable and
-// should changed if this is more clarified.)
-const hoursInDays = x => Math.round((x * 10) / 24) / 10;
-
 export default {
   mixins: [diagram, pdfgen, templateLoader],
   components: {
@@ -475,19 +466,15 @@
             .querySelector(".diagram-container")
             .getBoundingClientRect().left;
           d3.select("#tooltip")
-            .text(hoursInDays(d.height))
+            .text(d.height)
             .attr("y", y - 10)
             .attr("x", d3.event.pageX - dy);
           //d3.event.pageX gives coordinates relative to SVG
           //dy gives offset of svg on page
         })
-        .attr("y", d => {
-          return (
-            2 * yScale(0) - yScale(hoursInDays(d.translateY)) + this.paddingTop
-          );
-        })
+        .attr("y", d => 2 * yScale(0) - yScale(d.translateY) + this.paddingTop)
         .attr("height", d => {
-          return yScale(0) - yScale(hoursInDays(d.height));
+          return yScale(0) - yScale(d.height);
         })
         .attr("x", ldcOffset + spaceBetween / 2)
         .attr("width", widthPerItem - ldcOffset - spaceBetween)
@@ -500,19 +487,19 @@
           .selectAll("g.bars")
           .data(d => d.lowerLevels)
           .enter()
-          .filter(d => hoursInDays(d.height) > 0)
+          .filter(d => d.height > 0)
           .insert("text")
           .attr("y", d => {
             return (
               2 * yScale(0) -
-              yScale(hoursInDays(d.translateY)) +
+              yScale(d.translateY) +
               this.paddingTop +
-              (yScale(0) - yScale(hoursInDays(d.height))) +
+              (yScale(0) - yScale(d.height)) +
               (yScale(0) - yScale(1.9)) //instead o alignment-baseline hanging
             );
           })
           .attr("x", widthPerItem / 2)
-          .text(d => hoursInDays(d.height))
+          .text(d => d.height)
           // does not work with svg2pdf .attr("alignment-baseline", "hanging")
           .attr("text-anchor", "middle")
           .attr("font-size", "8")
@@ -520,7 +507,7 @@
       }
     },
     fnheight({ name, yScale }) {
-      return d => yScale(0) - yScale(hoursInDays(d[name]));
+      return d => yScale(0) - yScale(d[name]);
     },
     drawLDC({ everyBar, yScale, widthPerItem, spaceBetween, ldcOffset }) {
       const height = this.fnheight({ name: "ldc", yScale });
@@ -540,7 +527,7 @@
             .querySelector(".diagram-container")
             .getBoundingClientRect().left;
           d3.select("#tooltip")
-            .text(hoursInDays(d.ldc))
+            .text(d.ldc)
             .attr("y", y - 50)
             .attr("x", d3.event.pageX - dy);
           //d3.event.pageX gives coordinates relative to SVG
@@ -558,11 +545,11 @@
         .attr("id", "ldc");
       if (this.showNumbers) {
         everyBar
-          .filter(d => hoursInDays(d.ldc) > 0)
+          .filter(d => d.ldc > 0)
           .append("text")
           .attr("y", yScale(0.5)) // some distance from the bar
           .attr("x", spaceBetween / 2)
-          .text(d => hoursInDays(d.ldc))
+          .text(d => d.ldc)
           .attr("text-anchor", "left")
           .attr("font-size", "8")
           .attr(
@@ -596,7 +583,7 @@
             .querySelector(".diagram-container")
             .getBoundingClientRect().left;
           d3.select("#tooltip")
-            .text(hoursInDays(d.highestLevel))
+            .text(d.highestLevel)
             .attr("y", y - 50)
             .attr("x", d3.event.pageX - dy);
           //d3.event.pageX gives coordinates relative to SVG
@@ -613,11 +600,11 @@
         .attr("fill", this.$options.COLORS.HIGHEST);
       if (this.showNumbers) {
         everyBar
-          .filter(d => hoursInDays(d.highestLevel) > 0)
+          .filter(d => d.highestLevel > 0)
           .append("text")
           .attr("y", yScale(0.5)) // some distance from the bar
           .attr("x", widthPerItem / 2)
-          .text(d => hoursInDays(d.highestLevel))
+          .text(d => d.highestLevel)
           .attr("text-anchor", "middle")
           .attr("font-size", "8")
           .attr(
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkg/common/round.go	Mon Sep 09 17:25:28 2019 +0200
@@ -0,0 +1,71 @@
+// 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):
+//  * Sascha Wilde <wilde@sha-bang.de>
+
+package common
+
+import (
+	"math"
+	"sort"
+	"time"
+)
+
+type Rest struct {
+	Key  int
+	Rest float64
+}
+
+// Simple sum preserving rounding method:
+func SumPreservingRound(arr []float64) []int {
+	var (
+		sum   float64
+		rests []Rest
+	)
+	result := make([]int, len(arr))
+
+	// floor all values
+	for i, v := range arr {
+		sum += v
+		result[i] = int(v)
+		rests = append(rests, Rest{Key: i, Rest: v - float64(result[i])})
+	}
+
+	// find the difference in summs
+	var newSum int
+	for _, v := range result {
+		newSum += v
+	}
+	delta := int(math.Round(sum)) - newSum
+
+	// spread delta over values with highest rest
+	sort.Slice(rests, func(i, j int) bool {
+		return rests[i].Rest > rests[j].Rest
+	})
+	for _, v := range rests {
+		if delta <= 0 {
+			break
+		}
+		result[v.Key]++
+		delta--
+	}
+
+	return result
+}
+
+// Round anarray of Duratons to full days
+func RoundToFullDays(durations []time.Duration) []int {
+	days := make([]float64, len(durations))
+	for i, v := range durations {
+		days[i] = v.Hours() / 24
+	}
+	return SumPreservingRound(days)
+}
--- a/pkg/controllers/bottlenecks.go	Mon Sep 09 17:13:37 2019 +0200
+++ b/pkg/controllers/bottlenecks.go	Mon Sep 09 17:25:28 2019 +0200
@@ -503,8 +503,8 @@
 
 	record := make([]string, 1+2+len(breaks)+1)
 	record[0] = "#time"
-	record[1] = fmt.Sprintf("# < LDC (%.1f) [h]", ldcRefs[0])
-	record[2] = fmt.Sprintf("# >= LDC (%.1f) [h]", ldcRefs[0])
+	record[1] = fmt.Sprintf("# < LDC (%.1f) [%%]", ldcRefs[0])
+	record[2] = fmt.Sprintf("# >= LDC (%.1f) [%%]", ldcRefs[0])
 	for i, v := range breaks {
 		if i == 0 {
 			record[3] = fmt.Sprintf("#d < %.1f [%%]", v)
@@ -653,13 +653,13 @@
 	// label, ldc, classes
 	record := make([]string, 1+2+len(breaks)+1)
 	record[0] = "#time"
-	record[1] = fmt.Sprintf("# < LDC (%.1f) [h]", ldcRefs[0])
-	record[2] = fmt.Sprintf("# >= LDC (%.1f) [h]", ldcRefs[0])
+	record[1] = fmt.Sprintf("# < LDC (%.1f) [d]", ldcRefs[0])
+	record[2] = fmt.Sprintf("# >= LDC (%.1f) [d]", ldcRefs[0])
 	for i, v := range breaks {
 		if i == 0 {
-			record[3] = fmt.Sprintf("# < %.1f [h]", v)
+			record[3] = fmt.Sprintf("# < %.1f [d]", v)
 		}
-		record[i+4] = fmt.Sprintf("# >= %.1f [h]", v)
+		record[i+4] = fmt.Sprintf("# >= %.1f [d]", v)
 	}
 
 	if err := out.Write(record); err != nil {
@@ -701,13 +701,17 @@
 			access,
 		)
 
+		// Round to full days
+		ldcRounded := common.RoundToFullDays(ldc)
+		rangesRounded := common.RoundToFullDays(ranges)
+
 		record[0] = label
-		for i, v := range ldc {
-			record[i+1] = fmt.Sprintf("%.3f", v.Hours())
+		for i, v := range ldcRounded {
+			record[i+1] = fmt.Sprintf("%d", v)
 		}
 
-		for i, d := range ranges {
-			record[3+i] = fmt.Sprintf("%.3f", d.Hours())
+		for i, d := range rangesRounded {
+			record[3+i] = fmt.Sprintf("%d", d)
 		}
 
 		if err := out.Write(record); err != nil {
--- a/pkg/controllers/stretches.go	Mon Sep 09 17:13:37 2019 +0200
+++ b/pkg/controllers/stretches.go	Mon Sep 09 17:25:28 2019 +0200
@@ -27,6 +27,7 @@
 
 	"github.com/gorilla/mux"
 
+	"gemma.intevation.de/gemma/pkg/common"
 	"gemma.intevation.de/gemma/pkg/middleware"
 )
 
@@ -348,19 +349,19 @@
 	// label, lnwl, classes
 	record := make([]string, 1+2+len(breaks)+1)
 	record[0] = "# time"
-	record[1] = "# < LDC [h]"
-	record[2] = "# >= LDC [h]"
+	record[1] = "# < LDC [d]"
+	record[2] = "# >= LDC [d]"
 	for i, v := range breaks {
 		if useDepth && useWidth {
 			if i == 0 {
-				record[3] = "# < break_1 [h]"
+				record[3] = "# < break_1 [d]"
 			}
 			record[i+4] = fmt.Sprintf("# >= break_%d", i+1)
 		} else {
 			if i == 0 {
-				record[3] = fmt.Sprintf("# < %.1f [h]", v)
+				record[3] = fmt.Sprintf("# < %.1f [d]", v)
 			}
-			record[i+4] = fmt.Sprintf("# >= %.1f [h]", v)
+			record[i+4] = fmt.Sprintf("# >= %.1f [d]", v)
 		}
 	}
 
@@ -379,13 +380,23 @@
 	}
 
 	for _, r := range results {
-		record[0] = r.label
+		// Round to full days
 		for i, v := range r.ldc {
-			record[1+i] = fmt.Sprintf("%.3f", v.Hours()*scale)
+			r.ldc[i] = time.Duration(float64(v) * scale)
+		}
+		for i, v := range r.breaks {
+			r.breaks[i] = time.Duration(float64(v) * scale)
+		}
+		ldcRounded := common.RoundToFullDays(r.ldc)
+		rangesRounded := common.RoundToFullDays(r.breaks)
+
+		record[0] = r.label
+		for i, v := range ldcRounded {
+			record[1+i] = fmt.Sprintf("%d", v)
 		}
 
-		for i, d := range r.breaks {
-			record[3+i] = fmt.Sprintf("%.3f", d.Hours()*scale)
+		for i, d := range rangesRounded {
+			record[3+i] = fmt.Sprintf("%d", d)
 		}
 
 		if err := out.Write(record); err != nil {