comparison pkg/controllers/bottlenecks.go @ 4343:63c25eb9c07c

FA: be optimistic about missing data. According to clarification, missing data has to be interpreted as the best case, this is, because the services do not provide data for bottlenecks, which are not considered a limitating factor on the water way at a given time.
author Sascha Wilde <wilde@intevation.de>
date Fri, 06 Sep 2019 17:22:42 +0200
parents f543f9d4a0b5
children 6ac94171a994
comparison
equal deleted inserted replaced
4342:6a1fef54d49f 4343:63c25eb9c07c
8 // – Österreichische Wasserstraßen-Gesellschaft mbH 8 // – Österreichische Wasserstraßen-Gesellschaft mbH
9 // Software engineering by Intevation GmbH 9 // Software engineering by Intevation GmbH
10 // 10 //
11 // Author(s): 11 // Author(s):
12 // * Sascha L. Teichmann <sascha.teichmann@intevation.de> 12 // * Sascha L. Teichmann <sascha.teichmann@intevation.de>
13 // * Sascha Wilde <wilde@intevation.de>
13 14
14 package controllers 15 package controllers
15 16
16 import ( 17 import (
17 "context" 18 "context"
129 log.Printf("warn: unknown limitation '%s'. default to 'depth'\n", limiting) 130 log.Printf("warn: unknown limitation '%s'. default to 'depth'\n", limiting)
130 return (*availMeasurement).getDepth 131 return (*availMeasurement).getDepth
131 } 132 }
132 } 133 }
133 134
135 // According to clarification, it has to be assumed, that at times
136 // with no data, the best case (which by convention is the highest
137 // class created by classify()) should be assumed. That is due to the
138 // fact, that at times where bottlenecks are ot a limiting factor on
139 // the waterway, services don't brovide any data for the bottleneck in
140 // question.
141 //
142 // FIXME: A potentional improvement could be to intercest the time
143 // ranges with the time ranges where bottlenecks were "active" (this
144 // _might_ be derivable fromt the validity periods in the bottleneck
145 // data. So it _might_ be possible do detect actual missing data (BN
146 // valid, but no data from FA service). Anyway, this is left out for
147 // now, as many clarification regarding the base assumtions would bee
148 // needed and the results still might be unrelyable.
149 func optimisticPadClassification(
150 from, to time.Time,
151 classified []time.Duration,
152 ) []time.Duration {
153 var actualDuration time.Duration
154 maxDuration := to.Sub(from)
155
156 for i := 0; i < len(classified); i++ {
157 actualDuration += classified[i]
158 }
159
160 delta := maxDuration - actualDuration
161 if delta > 0 {
162 log.Printf("info: time interval: (%v - %v)\n", from, to)
163 log.Printf("info: found only data for %.2f hours, padding by %.2f hours\n",
164 actualDuration.Hours(), delta.Hours())
165 classified[len(classified)-1] = classified[len(classified)-1] + delta
166 }
167
168 return classified
169 }
170
134 func (measurements availMeasurements) classify( 171 func (measurements availMeasurements) classify(
135 from, to time.Time, 172 from, to time.Time,
136 breaks []float64, 173 breaks []float64,
137 access func(*availMeasurement) float64, 174 access func(*availMeasurement) float64,
138 ) []time.Duration { 175 ) []time.Duration {
156 // All values before from can be ignored. 193 // All values before from can be ignored.
157 return !measurements[i].when.Before(from) 194 return !measurements[i].when.Before(from)
158 }) 195 })
159 196
160 if idx >= len(measurements) { 197 if idx >= len(measurements) {
161 return result 198 return optimisticPadClassification(from, to, result)
162 } 199 }
163 200
164 // Be safe for interpolation. 201 // Be safe for interpolation.
165 if idx > 0 { 202 if idx > 0 {
166 idx-- 203 idx--
171 for i := 0; i < len(measurements)-1; i++ { 208 for i := 0; i < len(measurements)-1; i++ {
172 p1 := &measurements[i] 209 p1 := &measurements[i]
173 p2 := &measurements[i+1] 210 p2 := &measurements[i+1]
174 211
175 if p1.when.After(to) { 212 if p1.when.After(to) {
176 return result 213 return optimisticPadClassification(from, to, result)
177 } 214 }
178 215
179 if p2.when.Before(from) { 216 if p2.when.Before(from) {
180 continue 217 continue
181 } 218 }
212 start, end = maxTime(start, lo), minTime(end, hi) 249 start, end = maxTime(start, lo), minTime(end, hi)
213 result[j] += end.Sub(start) 250 result[j] += end.Sub(start)
214 } 251 }
215 } 252 }
216 253
217 return result 254 return optimisticPadClassification(from, to, result)
218 } 255 }
219 256
220 func orderTime(a, b time.Time) (time.Time, time.Time) { 257 func orderTime(a, b time.Time) (time.Time, time.Time) {
221 if a.Before(b) { 258 if a.Before(b) {
222 return a, b 259 return a, b
480 return 517 return
481 } 518 }
482 519
483 interval := intervals[mode](from, to) 520 interval := intervals[mode](from, to)
484 521
522 now := time.Now()
485 for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() { 523 for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() {
524 // Don't interpolate for the future
525 if now.Sub(pto) < 0 {
526 pto = now
527 }
528
486 lnwl := ms.classify( 529 lnwl := ms.classify(
487 pfrom, pto, 530 pfrom, pto,
488 ldcRefs, 531 ldcRefs,
489 (*availMeasurement).getValue, 532 (*availMeasurement).getValue,
490 ) 533 )
636 log.Printf("info: interval: %.2f [h]\n", ms[len(ms)-1].when.Sub(ms[0].when).Hours()) 679 log.Printf("info: interval: %.2f [h]\n", ms[len(ms)-1].when.Sub(ms[0].when).Hours())
637 } 680 }
638 681
639 interval := intervals[mode](from, to) 682 interval := intervals[mode](from, to)
640 683
684 now := time.Now()
641 for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() { 685 for pfrom, pto, label := interval(); label != ""; pfrom, pto, label = interval() {
686 // Don't interpolate for the future
687 if now.Sub(pto) < 0 {
688 pto = now
689 }
642 690
643 ldc := ms.classify( 691 ldc := ms.classify(
644 pfrom, pto, 692 pfrom, pto,
645 ldcRefs, 693 ldcRefs,
646 access, 694 access,