comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/GaugeMainValueFinder.java @ 9202:b4402594213b

More work on calculations and output for S-Info flood duration workflow (chart types 1 and 2)
author mschaefer
date Mon, 02 Jul 2018 07:33:53 +0200
parents
children b38be7ea53e2
comparison
equal deleted inserted replaced
9201:491e1a434457 9202:b4402594213b
1 /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde
2 * Software engineering by
3 * Björnsen Beratende Ingenieure GmbH
4 * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt
5 *
6 * This file is Free Software under the GNU AGPL (>=v3)
7 * and comes with ABSOLUTELY NO WARRANTY! Check out the
8 * documentation coming with Dive4Elements River for details.
9 */
10 package org.dive4elements.river.artifacts.sinfo.common;
11
12 import java.util.Map.Entry;
13 import java.util.NavigableMap;
14 import java.util.TreeMap;
15
16 import org.dive4elements.river.artifacts.model.Calculation;
17 import org.dive4elements.river.model.Gauge;
18 import org.dive4elements.river.model.MainValue;
19 import org.dive4elements.river.model.MainValueType.MainValueTypeKey;
20
21 /**
22 * Loading the main values of a gauge to find relative positions of a value and build a corresponding zone name
23 *
24 * @author Matthias Schäfer
25 *
26 */
27 public final class GaugeMainValueFinder {
28
29 /***** FIELDS *****/
30
31 // private static Logger log = Logger.getLogger(GaugeMainValueNameFinder.class);
32
33 private final Gauge gauge;
34
35 private Calculation problems;
36
37 private final NavigableMap<Double, MainValue> mainValues;
38
39 private final MainValueTypeKey keyType;
40
41 private final String approxPrefix = "ca."; // "\u2248" geht wohl nicht
42
43 private Entry<Double, MainValue> foundCeiling;
44
45 private Entry<Double, MainValue> foundFloor;
46
47 private double foundRelativeDistance;
48
49
50 /***** CONSTRUCTORS *****/
51
52 private GaugeMainValueFinder(final MainValueTypeKey keyType, final Gauge gauge, final Calculation problems) {
53 this.gauge = gauge;
54 this.problems = problems;
55 this.keyType = keyType;
56 this.mainValues = new TreeMap<>();
57 for (final MainValue mainValue : MainValue.getValuesOfGaugeAndType(gauge, keyType))
58 this.mainValues.put(Double.valueOf(mainValue.getValue().doubleValue()), mainValue);
59 if (this.mainValues.isEmpty() && (this.problems != null)) {
60 this.problems.addProblem("gauge_main_values.missing", gauge.getName());
61 // Report only once
62 this.problems = null;
63 }
64 }
65
66
67 /***** METHODS *****/
68
69 /**
70 * Loads the the main values table of a type and a gauge (GAUGE.sta)
71 *
72 * @return The main values finder of a type and a gauge, or null
73 */
74 public static GaugeMainValueFinder loadValues(final MainValueTypeKey type, final Gauge gauge, final Calculation problems) {
75 return new GaugeMainValueFinder(type, gauge, problems);
76 }
77
78 /**
79 * If this provider may return valid data at all.
80 */
81 public boolean isValid() {
82 return (this.mainValues != null);
83 }
84
85 /**
86 * Searches the main value zone for a value, and returns a textual description of the zone
87 * (name for an exact match, circa expression for +/- 10% match, less-than/between/greater-than expression otherwise)
88 */
89 public String findZoneName(final double value) {
90 if (!this.findValue(value))
91 return "";
92
93 // Clearly below or just (max. 10%) below lowest named value
94 if (this.foundFloor == null) {
95 if (Double.isInfinite(this.foundRelativeDistance))
96 return "<" + this.foundCeiling.getValue().getMainValue().getName();
97 else
98 return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
99 }
100
101 // Clearly above or just (max. 10%) above highest named value
102 if (this.foundCeiling == null) {
103 if (Double.isInfinite(this.foundRelativeDistance))
104 return ">" + this.foundFloor.getValue().getMainValue().getName();
105 else
106 return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
107 }
108
109 // Exact match
110 if (this.mainValues.containsKey(Double.valueOf(value)))
111 return this.mainValues.get(Double.valueOf(value)).getMainValue().getName();
112
113 // Near (10%) one of the borders of a zone interval, or clearly within a zone
114 if (this.foundRelativeDistance <= 0.001)
115 return this.foundFloor.getValue().getMainValue().getName();
116 else if (this.foundRelativeDistance <= 0.1)
117 return this.approxPrefix + this.foundFloor.getValue().getMainValue().getName();
118 else if (this.foundRelativeDistance >= 0.9)
119 return this.approxPrefix + this.foundCeiling.getValue().getMainValue().getName();
120 else
121 return this.foundFloor.getValue().getMainValue().getName() + "-" + this.foundCeiling.getValue().getMainValue().getName();
122 }
123
124 /**
125 * Searches the main value zone for a value, and returns the zone name for an exact match, the nomatchReturn otherwise
126 */
127 public String findExactZoneName(final double value, final String noMatchReturn) {
128 this.findValue(value);
129 if ((this.foundFloor != null) && (this.foundFloor.getKey() == this.foundCeiling.getKey()))
130 return this.foundFloor.getValue().getMainValue().getName();
131 else
132 return noMatchReturn;
133 }
134
135 /**
136 * Searches the interval of a main value and its relative distance from the lower value
137 */
138 public boolean findValue(final double value) {
139 this.foundFloor = null;
140 this.foundCeiling = null;
141 this.foundRelativeDistance = Double.NaN;
142 if (!this.isValid())
143 return false;
144 if (Double.isNaN(value))
145 return false;
146
147 // Clearly below or just (max. 10%) below lowest named value
148 this.foundFloor = this.mainValues.floorEntry(Double.valueOf(value));
149 if (this.foundFloor == null) {
150 this.foundCeiling = this.mainValues.firstEntry();
151 if (value >= this.mainValues.firstKey().doubleValue() * 0.9) {
152 this.foundRelativeDistance = 0.9;
153 return true;
154 }
155 else {
156 this.foundRelativeDistance = Double.NEGATIVE_INFINITY;
157 return false;
158 }
159 }
160
161 // Clearly above or just (max. 10%) above highest named value
162 this.foundCeiling = this.mainValues.ceilingEntry(Double.valueOf(value));
163 if (this.foundCeiling == null) {
164 if (value <= this.mainValues.lastKey().doubleValue() * 1.1) {
165 this.foundRelativeDistance = 0.1;
166 return true;
167 }
168 else {
169 this.foundRelativeDistance = Double.POSITIVE_INFINITY;
170 return false;
171 }
172 }
173
174 // Exact match or within an interval
175 if (this.foundCeiling.getKey() == this.foundFloor.getKey())
176 this.foundRelativeDistance = 0.0;
177 else
178 this.foundRelativeDistance = (value - this.foundFloor.getKey().doubleValue())
179 / (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue());
180 return true;
181 }
182
183 /**
184 * Floor value of the last findValue
185 */
186 public MainValue getFoundFloorValue() {
187 if (this.foundFloor != null)
188 return this.foundFloor.getValue();
189 else
190 return null;
191 }
192
193 /**
194 * Ceiling value of the last findValue
195 */
196 public MainValue getFoundCeilingValue() {
197 if (this.foundCeiling != null)
198 return this.foundCeiling.getValue();
199 else
200 return null;
201 }
202
203 /**
204 * Relative distance of the last findValue
205 */
206 public double getFoundRelativeDistance() {
207 return this.getFoundRelativeDistance();
208 }
209
210 /**
211 * Searches a pair of zone names and return the a value within the interval by a relative distance, or NaN
212 */
213 public double findValue(final String floorZone, final String ceilingZone, final double relativeDistance) {
214 this.foundFloor = null;
215 this.foundCeiling = null;
216 this.foundRelativeDistance = relativeDistance;
217 for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
218 if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(floorZone)) {
219 this.foundFloor = mainValue;
220 break;
221 }
222 }
223 if (this.foundFloor == null)
224 return Double.NaN;
225 if (floorZone.equalsIgnoreCase(ceilingZone))
226 return this.foundFloor.getKey().doubleValue();
227 for (final Entry<Double, MainValue> mainValue : this.mainValues.entrySet()) {
228 if (mainValue.getValue().getMainValue().getName().equalsIgnoreCase(ceilingZone)) {
229 this.foundCeiling = mainValue;
230 break;
231 }
232 }
233 if (this.foundCeiling == null)
234 return Double.NaN;
235 else
236 return (this.foundCeiling.getKey().doubleValue() - this.foundFloor.getKey().doubleValue()) * this.foundRelativeDistance
237 + this.foundFloor.getKey().doubleValue();
238 }
239 }

http://dive4elements.wald.intevation.org