Mercurial > dive4elements > river
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 } |