Mercurial > gemma
comparison pkg/controllers/bottlenecks.go @ 3076:e0daeb05bf50
Display Available Fairway Depths vs. LNWL: Completed backend (untested).
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Wed, 17 Apr 2019 15:04:28 +0200 |
parents | a661e9b8f3b6 |
children | c68cef0346b7 |
comparison
equal
deleted
inserted
replaced
3075:3cf7189fa93e | 3076:e0daeb05bf50 |
---|---|
79 ) | 79 ) |
80 ` | 80 ` |
81 ) | 81 ) |
82 | 82 |
83 type ( | 83 type ( |
84 availReferenceValue struct { | 84 referenceValue struct { |
85 level int | 85 level int |
86 value float64 | 86 value float64 |
87 } | 87 } |
88 | 88 |
89 availMeasurement struct { | 89 availMeasurement struct { |
94 ) | 94 ) |
95 | 95 |
96 func classifyAvailMeasurements( | 96 func classifyAvailMeasurements( |
97 from, to time.Time, | 97 from, to time.Time, |
98 measurements []availMeasurement, | 98 measurements []availMeasurement, |
99 classes []availReferenceValue, | 99 classes []referenceValue, |
100 access func(*availMeasurement) float64, | 100 access func(*availMeasurement) float64, |
101 ) []time.Duration { | 101 ) []time.Duration { |
102 | 102 |
103 type classValues struct { | 103 type classValues struct { |
104 when time.Time | 104 when time.Time |
201 } | 201 } |
202 | 202 |
203 return result | 203 return result |
204 } | 204 } |
205 | 205 |
206 func durationsToPercentage(from, to time.Time, classes []time.Duration) []float64 { | |
207 percents := make([]float64, len(classes)) | |
208 total := 100 / to.Sub(from).Seconds() | |
209 for i, v := range classes { | |
210 percents[i] = v.Seconds() * total | |
211 } | |
212 return percents | |
213 } | |
214 | |
206 func bottleneckAvailabilty( | 215 func bottleneckAvailabilty( |
207 _ interface{}, | 216 _ interface{}, |
208 req *http.Request, | 217 req *http.Request, |
209 conn *sql.Conn, | 218 conn *sql.Conn, |
210 ) (jr JSONResult, err error) { | 219 ) (jr JSONResult, err error) { |
216 Message: "Missing objnam of bottleneck", | 225 Message: "Missing objnam of bottleneck", |
217 } | 226 } |
218 return | 227 return |
219 } | 228 } |
220 | 229 |
230 var from, to time.Time | |
231 | |
232 if f := req.FormValue("from"); f != "" { | |
233 if from, err = time.Parse(common.TimeFormat, f); err != nil { | |
234 err = JSONError{ | |
235 Code: http.StatusBadRequest, | |
236 Message: fmt.Sprintf("Invalid time format for 'from' field: %v", err), | |
237 } | |
238 return | |
239 } | |
240 from = from.UTC() | |
241 } else { | |
242 from = time.Now().AddDate(-1, 0, 0).UTC() | |
243 } | |
244 | |
245 if t := req.FormValue("to"); t != "" { | |
246 if to, err = time.Parse(common.TimeFormat, t); err != nil { | |
247 err = JSONError{ | |
248 Code: http.StatusBadRequest, | |
249 Message: fmt.Sprintf("Invalid time format for 'from' field: %v", err), | |
250 } | |
251 return | |
252 } | |
253 to = to.UTC() | |
254 } else { | |
255 to = from.AddDate(1, 0, 0).UTC() | |
256 } | |
257 | |
258 if to.Before(from) { | |
259 to, from = from, to | |
260 } | |
261 | |
262 log.Printf("info: time interval: (%v - %v)\n", from, to) | |
263 | |
264 var los int | |
265 if l := req.FormValue("los"); l != "" { | |
266 if los, err = strconv.Atoi(l); err != nil { | |
267 err = JSONError{ | |
268 Code: http.StatusBadRequest, | |
269 Message: fmt.Sprintf("Invalid value for field 'los': %v", err), | |
270 } | |
271 return | |
272 } | |
273 } else { | |
274 los = 1 | |
275 } | |
276 | |
221 ctx := req.Context() | 277 ctx := req.Context() |
222 | 278 |
223 loadReferenceValues := func() ([]availReferenceValue, error) { | 279 loadLNWLReferenceValues := func() ([]referenceValue, error) { |
224 rows, err := conn.QueryContext(ctx, selectGaugeLevelsSQL, bn) | 280 rows, err := conn.QueryContext(ctx, selectGaugeLevelsSQL, bn) |
225 if err != nil { | 281 if err != nil { |
226 return nil, err | 282 return nil, err |
227 } | 283 } |
228 defer rows.Close() | 284 defer rows.Close() |
229 | 285 |
230 var levels []availReferenceValue | 286 var levels []referenceValue |
231 | 287 |
232 loop: | 288 loop: |
233 for rows.Next() { | 289 for rows.Next() { |
234 var what string | 290 var what string |
235 var value int | 291 var value int |
251 if levels[i].level == level { | 307 if levels[i].level == level { |
252 levels[i].value = float64(value) | 308 levels[i].value = float64(value) |
253 continue loop | 309 continue loop |
254 } | 310 } |
255 } | 311 } |
256 levels = append(levels, availReferenceValue{ | 312 levels = append(levels, referenceValue{ |
257 level: level, | 313 level: level, |
258 value: float64(value), | 314 value: float64(value), |
259 }) | 315 }) |
260 } | 316 } |
261 | 317 |
266 sort.Slice(levels, func(i, j int) bool { return levels[i].level < levels[j].level }) | 322 sort.Slice(levels, func(i, j int) bool { return levels[i].level < levels[j].level }) |
267 | 323 |
268 return levels, nil | 324 return levels, nil |
269 } | 325 } |
270 | 326 |
271 var refVals []availReferenceValue | 327 var lnwlRefs []referenceValue |
272 if refVals, err = loadReferenceValues(); err != nil { | 328 if lnwlRefs, err = loadLNWLReferenceValues(); err != nil { |
273 return | 329 return |
274 } | 330 } |
275 | 331 |
276 if len(refVals) == 0 { | 332 if len(lnwlRefs) == 0 { |
277 err = JSONError{ | 333 err = JSONError{ |
278 Code: http.StatusNotFound, | 334 Code: http.StatusNotFound, |
279 Message: "No gauge reference values found for bottleneck", | 335 Message: "No gauge reference values found for bottleneck", |
280 } | 336 } |
281 } | |
282 | |
283 var from, to time.Time | |
284 | |
285 if f := req.FormValue("from"); f != "" { | |
286 if from, err = time.Parse(common.TimeFormat, f); err != nil { | |
287 err = JSONError{ | |
288 Code: http.StatusBadRequest, | |
289 Message: fmt.Sprintf("Invalid time format for 'from' field: %v", err), | |
290 } | |
291 return | |
292 } | |
293 from = from.UTC() | |
294 } else { | |
295 from = time.Now().AddDate(-1, 0, 0).UTC() | |
296 } | |
297 | |
298 if t := req.FormValue("to"); t != "" { | |
299 if to, err = time.Parse(common.TimeFormat, t); err != nil { | |
300 err = JSONError{ | |
301 Code: http.StatusBadRequest, | |
302 Message: fmt.Sprintf("Invalid time format for 'from' field: %v", err), | |
303 } | |
304 return | |
305 } | |
306 to = to.UTC() | |
307 } else { | |
308 to = from.AddDate(1, 0, 0).UTC() | |
309 } | |
310 | |
311 if to.Before(from) { | |
312 to, from = from, to | |
313 } | |
314 | |
315 log.Printf("info: time interval: (%v - %v)\n", from, to) | |
316 | |
317 var los int | |
318 if l := req.FormValue("los"); l != "" { | |
319 if los, err = strconv.Atoi(l); err != nil { | |
320 err = JSONError{ | |
321 Code: http.StatusBadRequest, | |
322 Message: fmt.Sprintf("Invalid value for field 'los': %v", err), | |
323 } | |
324 return | |
325 } | |
326 } else { | |
327 los = 1 | |
328 } | 337 } |
329 | 338 |
330 loadDepthValues := func() ([]availMeasurement, error) { | 339 loadDepthValues := func() ([]availMeasurement, error) { |
331 | 340 |
332 rows, err := conn.QueryContext( | 341 rows, err := conn.QueryContext( |
353 | 362 |
354 return ms, nil | 363 return ms, nil |
355 } | 364 } |
356 | 365 |
357 var ms []availMeasurement | 366 var ms []availMeasurement |
358 | |
359 if ms, err = loadDepthValues(); err != nil { | 367 if ms, err = loadDepthValues(); err != nil { |
360 return | 368 return |
361 } | 369 } |
362 | 370 |
363 if len(ms) == 0 { | 371 if len(ms) == 0 { |
366 Message: "No available fairway depth values found", | 374 Message: "No available fairway depth values found", |
367 } | 375 } |
368 return | 376 return |
369 } | 377 } |
370 | 378 |
371 results := classifyAvailMeasurements( | 379 lnwl := classifyAvailMeasurements( |
372 from, to, | 380 from, to, |
373 ms, | 381 ms, |
374 refVals, | 382 lnwlRefs, |
375 func(m *availMeasurement) float64 { return float64(m.value) }, | 383 func(m *availMeasurement) float64 { return float64(m.value) }, |
376 ) | 384 ) |
377 | 385 |
378 classes := make([]float64, len(results)) | 386 afdRefs := []referenceValue{ |
379 total := 100 / to.Sub(from).Seconds() | 387 {0, 200}, |
380 for i, v := range results { | 388 {1, 230}, |
381 classes[i] = v.Seconds() * total | 389 {2, 250}, |
382 } | 390 } |
383 | 391 |
384 type outputLevel struct { | 392 afd := classifyAvailMeasurements( |
385 Level string `json:"level"` | 393 from, to, |
386 Value float64 `json:"value"` | 394 ms, |
395 afdRefs, | |
396 func(m *availMeasurement) float64 { return float64(m.depth) }, | |
397 ) | |
398 | |
399 lnwlPercents := durationsToPercentage(from, to, lnwl) | |
400 afdPercents := durationsToPercentage(from, to, afd) | |
401 | |
402 type lnwlOutput struct { | |
403 Level string `json:"level"` | |
404 Value float64 `json:"value"` | |
405 Percent float64 `json:"percent"` | |
406 } | |
407 | |
408 type afdOutput struct { | |
409 Value float64 `json:"value"` | |
410 Percent float64 `json:"percent"` | |
387 } | 411 } |
388 | 412 |
389 type output struct { | 413 type output struct { |
390 Levels []outputLevel `json:"levels"` | 414 LNWL []lnwlOutput `json:"lnwl"` |
391 Classes []float64 `json:"classes"` | 415 AFD []afdOutput `json:"afd"` |
392 } | 416 } |
393 | 417 |
394 out := output{Classes: classes} | 418 out := output{} |
395 | 419 |
396 for i := range refVals { | 420 for i := range lnwlRefs { |
397 var level string | 421 var level string |
398 switch refVals[i].level { | 422 switch lnwlRefs[i].level { |
399 case 0: | 423 case 0: |
400 level = "LDC" | 424 level = "LDC" |
401 case 1: | 425 case 1: |
402 level = "MW" | 426 level = "MW" |
403 case 2: | 427 case 2: |
404 level = "HDC" | 428 level = "HDC" |
405 } | 429 } |
406 out.Levels = append(out.Levels, outputLevel{ | 430 out.LNWL = append(out.LNWL, lnwlOutput{ |
407 Level: level, | 431 Level: level, |
408 Value: refVals[i].value, | 432 Value: lnwlRefs[i].value, |
433 Percent: lnwlPercents[i], | |
434 }) | |
435 } | |
436 | |
437 for i := range afdRefs { | |
438 out.AFD = append(out.AFD, afdOutput{ | |
439 Value: afdRefs[i].value, | |
440 Percent: afdPercents[i], | |
409 }) | 441 }) |
410 } | 442 } |
411 | 443 |
412 jr = JSONResult{Result: &out} | 444 jr = JSONResult{Result: &out} |
413 | 445 |