diff pkg/controllers/bottlenecks.go @ 3367:ecb4baa2be1a

Simplified waterlevel classification.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 22 May 2019 10:47:04 +0200
parents 146bf3a1752c
children ac630f0f5dbf
line wrap: on
line diff
--- a/pkg/controllers/bottlenecks.go	Wed May 22 09:33:40 2019 +0200
+++ b/pkg/controllers/bottlenecks.go	Wed May 22 10:47:04 2019 +0200
@@ -124,130 +124,70 @@
 	return float64(measurement.value)
 }
 
-func (measurements availMeasurements) classifyAvailMeasurements(
+func (measurements availMeasurements) classify(
 	from, to time.Time,
-	classes []referenceValue,
+	breaks []referenceValue,
 	access func(*availMeasurement) float64,
 ) []time.Duration {
 
-	type classValues struct {
-		when time.Time
-		kind common.ValueRangeKind
-	}
-
-	//var invalid time.Duration
-	result := make([]time.Duration, len(classes)+1)
-
-	if len(measurements) == 0 ||
-		to.Before(measurements[0].when) ||
-		from.After(measurements[len(measurements)-1].when) {
-		return result
+	if len(breaks) == 0 {
+		return []time.Duration{}
 	}
 
-	cvs := make([]classValues, len(classes))
+	result := make([]time.Duration, len(breaks)+1)
+	classes := make([]float64, len(breaks)+2)
+	values := make([]time.Time, len(classes))
 
-	classify := func(v func(float64) (time.Time, common.ValueRangeKind)) {
-		for i := range classes {
-			cvs[i].when, cvs[i].kind = v(classes[i].value)
-		}
+	// Add sentinels
+	classes[0] = breaks[0].value - 9999
+	classes[len(classes)-1] = breaks[len(breaks)-1].value + 9999
+	for i := range breaks {
+		classes[i+1] = breaks[i].value
 	}
 
-	vbt := func(p1, p2 *availMeasurement) func(time.Time) (float64, common.ValueRangeKind) {
-		return common.InterpolateValueByTime(p1.when, access(p1), p2.when, access(p2))
-	}
-
-	// var total time.Duration
-
-pairs:
 	for i := 0; i < len(measurements)-1; i++ {
 		p1 := &measurements[i]
 		p2 := &measurements[i+1]
 
-		var start, end time.Time
-
-		switch {
-		case !p2.when.After(p1.when):
-			// Segment invalid
-			// log.Println("invalid")
-			continue pairs
+		if p1.when.After(to) {
+			return result
+		}
 
-		case p1.when.After(to) || p2.when.Before(from):
-			// Segment complete outside.
-			// log.Println("complete outside")
-			continue pairs
-
-		case (p1.when.Before(from) || p1.when.Equal(from)) && (to.Before(p2.when) || to.Equal(p2.when)):
-			// log.Println("(from, to) complete inside current interval")
-			v := vbt(p1, p2)
-			f, _ := v(from)
-			t, _ := v(to)
-			classify(common.InterpolateTimeByValue(from, f, to, t))
-			start, end = from, to
+		if p2.when.Before(from) {
+			continue
+		}
 
-		case (from.Before(p1.when) || from.Equal(p1.when)) && (p2.when.Before(to) || p2.when.Equal(to)):
-			//log.Println("current interval complete inside (from, to)")
-			classify(common.InterpolateTimeByValue(
-				p1.when, access(p1),
-				p2.when, access(p2),
-			))
-			start, end = p1.when, p2.when
+		f := common.InterpolateTime(
+			p1.when, access(p1),
+			p2.when, access(p2),
+		)
 
-		case p1.when.After(from):
-			// log.Println("p1 > from")
-			pt := minTime(to, p2.when)
-			t, _ := vbt(p1, p2)(pt)
-			classify(common.InterpolateTimeByValue(
-				p1.when, access(p1),
-				pt, t,
-			))
-			start, end = p1.when, pt
-
-		case p2.when.Before(to):
-			// log.Println("p2 < to")
-			pf := maxTime(from, p1.when)
-			f, _ := vbt(p1, p2)(pf)
-			classify(common.InterpolateTimeByValue(
-				pf, f,
-				p2.when, access(p2),
-			))
-			start, end = pf, p2.when
-
-		default:
-			log.Printf("warn: unexpected case. That should not happen. %v - %v, %v - %v\n",
-				p1.when, p2.when, from, to)
-			continue pairs
+		for j, c := range classes {
+			values[j] = f(c)
 		}
 
-		// total += end.Sub(start)
+		for j := 0; j < len(values)-1; j++ {
+			start, end := orderTime(values[j], values[j+1])
 
-		for i := len(cvs) - 1; i >= 0; i-- {
-			switch cvs[i].kind {
-			case common.ValueAbove:
-				result[i+1] += end.Sub(start)
-				continue pairs
+			lo, hi := maxTime(p1.when, from), minTime(p2.when, to)
 
-			case common.ValueInside:
-				// -> split
-				if access(p1) < classes[i].value {
-					// started below -> second part above
-					diff := absDuration(end.Sub(cvs[i].when))
-					result[i+1] += diff
-					end = cvs[i].when
-				} else {
-					// started above -> first part above
-					diff := absDuration(cvs[i].when.Sub(start))
-					result[i+1] += diff
-					start = cvs[i].when
-				}
+			if start.After(hi) || end.Before(lo) {
+				continue
 			}
+
+			start, end = maxTime(start, lo), minTime(end, hi)
+			result[j] += end.Sub(start)
 		}
-		result[0] += end.Sub(start)
 	}
 
-	// log.Printf("total all: %f\n", to.Sub(from).Hours())
-	// log.Printf("total: %f\n", total.Hours())
+	return result
+}
 
-	return result
+func orderTime(a, b time.Time) (time.Time, time.Time) {
+	if a.Before(b) {
+		return a, b
+	}
+	return b, a
 }
 
 func minTime(a, b time.Time) time.Time {
@@ -264,14 +204,6 @@
 	return b
 }
 
-func absDuration(x time.Duration) time.Duration {
-	if x < 0 {
-		log.Printf("warn: negative duration %v\n", x)
-		return -x
-	}
-	return x
-}
-
 func durationsToPercentage(from, to time.Time, classes []time.Duration) []float64 {
 	percents := make([]float64, len(classes))
 	total := 100 / to.Sub(from).Seconds()
@@ -353,6 +285,7 @@
 	case err != nil:
 		return nil, err
 	}
+	log.Printf("info: LDC = %.2f\n", value)
 	return []referenceValue{{0, value}}, nil
 }
 
@@ -486,13 +419,13 @@
 		return
 	}
 
-	lnwl := ms.classifyAvailMeasurements(
+	lnwl := ms.classify(
 		from, to,
 		lnwlRefs,
 		(*availMeasurement).getValue,
 	)
 
-	afd := ms.classifyAvailMeasurements(
+	afd := ms.classify(
 		from, to,
 		afdRefs,
 		(*availMeasurement).getDepth,
@@ -667,41 +600,29 @@
 		return
 	}
 
-	// log.Println(len(ms))
+	//log.Println(len(ms))
 	//for i := range ms {
 	//	log.Println(ms[i].when, ms[i].depth)
 	//}
 
-	interval := intervals[mode](from, to)
+	log.Printf("info: measurements: %d\n", len(ms))
+	if len(ms) > 1 {
+		log.Printf("info: first: %v\n", ms[0].when)
+		log.Printf("info: last: %v\n", ms[len(ms)-1].when)
+		log.Printf("info: interval: %.2f [h]\n", ms[len(ms)-1].when.Sub(ms[0].when).Hours())
+	}
 
-	//log.Printf("first: %v\n", ms[0].when)
-	//log.Printf("last: %v\n", ms[len(ms)-1].when)
+	interval := intervals[mode](from, to)
 
 	for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() {
 
-		samples := ms
-
-		/*
-			//log.Printf("pfrom: %v\n", pfrom)
-
-			// Find good starting point
-			idx := sort.Search(len(ms), func(i int) bool {
-				return !ms[i].when.Before(pfrom)
-			})
-			//log.Printf("%d\n", idx)
-			if idx > 0 {
-				idx--
-			}
-			samples := ms[idx:]
-		*/
-
-		ranges := samples.classifyAvailMeasurements(
+		ranges := ms.classify(
 			pfrom, pto,
 			afdRefs,
 			(*availMeasurement).getDepth,
 		)
 
-		ldc := samples.classifyAvailMeasurements(
+		ldc := ms.classify(
 			pfrom, pto,
 			ldcRefs,
 			(*availMeasurement).getDepth,