comparison backend/src/main/java/org/dive4elements/river/model/River.java @ 8971:50416a0df385

Importer for the Schifffahrt (S-INFO) and Oekologie (U-INFO) files
author mschaefer
date Tue, 03 Apr 2018 10:18:30 +0200
parents c4ce25093953
children 7c8d62867876
comparison
equal deleted inserted replaced
8970:da5dc7446652 8971:50416a0df385
5 * and comes with ABSOLUTELY NO WARRANTY! Check out the 5 * and comes with ABSOLUTELY NO WARRANTY! Check out the
6 * documentation coming with Dive4Elements River for details. 6 * documentation coming with Dive4Elements River for details.
7 */ 7 */
8 8
9 package org.dive4elements.river.model; 9 package org.dive4elements.river.model;
10
11 import org.dive4elements.river.backend.SessionHolder;
12 10
13 import java.io.Serializable; 11 import java.io.Serializable;
14 import java.math.BigDecimal; 12 import java.math.BigDecimal;
15 import java.math.MathContext; 13 import java.math.MathContext;
16 import java.util.Comparator; 14 import java.util.Comparator;
27 import javax.persistence.OneToMany; 25 import javax.persistence.OneToMany;
28 import javax.persistence.OneToOne; 26 import javax.persistence.OneToOne;
29 import javax.persistence.SequenceGenerator; 27 import javax.persistence.SequenceGenerator;
30 import javax.persistence.Table; 28 import javax.persistence.Table;
31 29
30 import org.apache.log4j.Logger;
31 import org.dive4elements.river.backend.SessionHolder;
32 import org.hibernate.Query; 32 import org.hibernate.Query;
33 import org.hibernate.Session; 33 import org.hibernate.Session;
34 import org.hibernate.annotations.Type; 34 import org.hibernate.annotations.Type;
35
36 import org.apache.log4j.Logger;
37 35
38 @Entity 36 @Entity
39 @Table(name = "rivers") 37 @Table(name = "rivers")
40 public class River 38 public class River
41 implements Serializable 39 implements Serializable
49 // Tolerance for determining whether we are at the station of a gauge 47 // Tolerance for determining whether we are at the station of a gauge
50 public static final double GAUGE_EPSILON = 0.1; 48 public static final double GAUGE_EPSILON = 0.1;
51 49
52 public static final Comparator<Double> KM_CMP = new Comparator<Double>() { 50 public static final Comparator<Double> KM_CMP = new Comparator<Double>() {
53 @Override 51 @Override
54 public int compare(Double a, Double b) { 52 public int compare(final Double a, final Double b) {
55 double diff = a - b; 53 final double diff = a - b;
56 if (diff < -EPSILON) return -1; 54 if (diff < -EPSILON) return -1;
57 if (diff > EPSILON) return +1; 55 if (diff > EPSILON) return +1;
58 return 0; 56 return 0;
59 } 57 }
60 }; 58 };
75 73
76 private SeddbName seddbName; 74 private SeddbName seddbName;
77 75
78 @Id 76 @Id
79 @SequenceGenerator( 77 @SequenceGenerator(
80 name = "SEQUENCE_RIVERS_ID_SEQ", 78 name = "SEQUENCE_RIVERS_ID_SEQ",
81 sequenceName = "RIVERS_ID_SEQ", 79 sequenceName = "RIVERS_ID_SEQ",
82 allocationSize = 1) 80 allocationSize = 1)
83 @GeneratedValue( 81 @GeneratedValue(
84 strategy = GenerationType.SEQUENCE, 82 strategy = GenerationType.SEQUENCE,
85 generator = "SEQUENCE_RIVERS_ID_SEQ") 83 generator = "SEQUENCE_RIVERS_ID_SEQ")
86 @Column(name = "id") 84 @Column(name = "id")
87 public Integer getId() { 85 public Integer getId() {
88 return id; 86 return this.id;
89 } 87 }
90 88
91 public void setId(Integer id) { 89 public void setId(final Integer id) {
92 this.id = id; 90 this.id = id;
93 } 91 }
94 92
95 @Column(name = "official_number") 93 @Column(name = "official_number")
96 public Long getOfficialNumber() { 94 public Long getOfficialNumber() {
97 return officialNumber; 95 return this.officialNumber;
98 } 96 }
99 97
100 public void setOfficialNumber(Long officialNumber) { 98 public void setOfficialNumber(final Long officialNumber) {
101 this.officialNumber = officialNumber; 99 this.officialNumber = officialNumber;
102 } 100 }
103 101
104 @Column(name = "name") 102 @Column(name = "name")
105 public String getName() { 103 public String getName() {
106 return name; 104 return this.name;
107 } 105 }
108 106
109 public void setName(String name) { 107 public void setName(final String name) {
110 this.name = name; 108 this.name = name;
111 } 109 }
112 110
113 @Type(type="numeric_boolean") 111 @Type(type="numeric_boolean")
114 @Column(name = "km_up") 112 @Column(name = "km_up")
115 public boolean getKmUp() { 113 public boolean getKmUp() {
116 return kmUp; 114 return this.kmUp;
117 } 115 }
118 116
119 public void setKmUp(boolean kmUp) { 117 public void setKmUp(final boolean kmUp) {
120 this.kmUp = kmUp; 118 this.kmUp = kmUp;
121 } 119 }
122 120
123 @Column(name = "model_uuid") 121 @Column(name = "model_uuid")
124 public String getModelUuid() { 122 public String getModelUuid() {
125 return this.modelUuid; 123 return this.modelUuid;
126 } 124 }
127 125
128 public void setModelUuid(String modelUuid) { 126 public void setModelUuid(final String modelUuid) {
129 this.modelUuid = modelUuid; 127 this.modelUuid = modelUuid;
130 } 128 }
131 129
132 public River() { 130 public River() {
133 } 131 }
134 132
135 public River(String name, Unit wstUnit, String modelUuid) { 133 public River(final String name, final Unit wstUnit, final String modelUuid) {
136 this.name = name; 134 this.name = name;
137 this.modelUuid = modelUuid; 135 this.modelUuid = modelUuid;
138 this.wstUnit = wstUnit; 136 this.wstUnit = wstUnit;
139 } 137 }
140 138
141 @OneToMany 139 @OneToMany
142 @JoinColumn(name="river_id") 140 @JoinColumn(name="river_id")
143 public List<Gauge> getGauges() { 141 public List<Gauge> getGauges() {
144 return gauges; 142 return this.gauges;
145 } 143 }
146 144
147 public void setGauges(List<Gauge> gauges) { 145 public void setGauges(final List<Gauge> gauges) {
148 this.gauges = gauges; 146 this.gauges = gauges;
149 } 147 }
150 148
151 @OneToOne 149 @OneToOne
152 @JoinColumn(name = "wst_unit_id" ) 150 @JoinColumn(name = "wst_unit_id" )
153 public Unit getWstUnit() { 151 public Unit getWstUnit() {
154 return wstUnit; 152 return this.wstUnit;
155 } 153 }
156 154
157 public void setWstUnit(Unit wstUnit) { 155 public void setWstUnit(final Unit wstUnit) {
158 this.wstUnit = wstUnit; 156 this.wstUnit = wstUnit;
159 } 157 }
160 158
161 159
162 /** 160 /**
166 * and might differ from "our" backend db name. 164 * and might differ from "our" backend db name.
167 * 165 *
168 * @return The name River in the seddb. 166 * @return The name River in the seddb.
169 */ 167 */
170 public String nameForSeddb() { 168 public String nameForSeddb() {
171 SeddbName alt = getSeddbName(); 169 final SeddbName alt = getSeddbName();
172 if (alt == null) { 170 if (alt == null) {
173 return getName(); 171 return getName();
174 } 172 }
175 return alt.getName(); 173 return alt.getName();
176 } 174 }
177 175
178 176
179 @OneToOne 177 @OneToOne
180 @JoinColumn(name = "seddb_name_id" ) 178 @JoinColumn(name = "seddb_name_id" )
181 public SeddbName getSeddbName() { 179 public SeddbName getSeddbName() {
182 return seddbName; 180 return this.seddbName;
183 } 181 }
184 182
185 public void setSeddbName(SeddbName name) { 183 public void setSeddbName(final SeddbName name) {
186 this.seddbName = name; 184 this.seddbName = name;
187 } 185 }
188 186
189 @Override 187 @Override
190 public String toString() { 188 public String toString() {
191 return name != null ? name : ""; 189 return this.name != null ? this.name : "";
192 } 190 }
193 191
194 192
195 /** 193 /**
196 * This method returns the gauges that intersect with <i>a</i> and 194 * This method returns the gauges that intersect with <i>a</i> and
200 * @param b An end point. 198 * @param b An end point.
201 * 199 *
202 * @return the intersecting gauges. 200 * @return the intersecting gauges.
203 */ 201 */
204 public List<Gauge> determineGauges(double a, double b) { 202 public List<Gauge> determineGauges(double a, double b) {
205 Session session = SessionHolder.HOLDER.get(); 203 final Session session = SessionHolder.HOLDER.get();
206 204
207 if (a > b) { double t = a; a = b; b = t; } 205 if (a > b) { final double t = a; a = b; b = t; }
208 206
209 Query query = session.createQuery( 207 final Query query = session.createQuery(
210 "from Gauge where river=:river " + 208 "from Gauge where river=:river " +
211 "and not " + 209 "and not " +
212 "((:b < least(range.a, range.b)) or" + 210 "((:b < least(range.a, range.b)) or" +
213 " (:a > greatest(range.a, range.b)))" + 211 " (:a > greatest(range.a, range.b)))" +
214 "order by a"); 212 "order by a");
215 query.setParameter("river", this); 213 query.setParameter("river", this);
216 query.setParameter("a", new BigDecimal(a, PRECISION)); 214 query.setParameter("a", new BigDecimal(a, PRECISION));
217 query.setParameter("b", new BigDecimal(b, PRECISION)); 215 query.setParameter("b", new BigDecimal(b, PRECISION));
218 216
219 return query.list(); 217 return query.list();
220 } 218 }
221 219
222 public Gauge maxOverlap(double a, double b) { 220 public Gauge maxOverlap(double a, double b) {
223 List<Gauge> gauges = determineGauges(a, b); 221 final List<Gauge> gauges = determineGauges(a, b);
224 if (gauges == null) { 222 if (gauges == null) {
225 return null; 223 return null;
226 } 224 }
227 225
228 if (a > b) { double t = a; a = b; b = t; } 226 if (a > b) { final double t = a; a = b; b = t; }
229 227
230 double max = -Double.MAX_VALUE; 228 double max = -Double.MAX_VALUE;
231 229
232 Gauge result = null; 230 Gauge result = null;
233 231
234 for (Gauge gauge: gauges) { 232 for (final Gauge gauge: gauges) {
235 Range r = gauge.getRange(); 233 final Range r = gauge.getRange();
236 double c = r.getA().doubleValue(); 234 double c = r.getA().doubleValue();
237 double d = r.getB().doubleValue(); 235 double d = r.getB().doubleValue();
238 236
239 if (c > d) { double t = c; c = d; d = t; } 237 if (c > d) { final double t = c; c = d; d = t; }
240 238
241 double start = c >= a ? c : a; 239 final double start = c >= a ? c : a;
242 double stop = d <= b ? d : b; 240 final double stop = d <= b ? d : b;
243 241
244 double length = stop - start; 242 final double length = stop - start;
245 243
246 if (length > max) { 244 if (length > max) {
247 max = length; 245 max = length;
248 result = gauge; 246 result = gauge;
249 } 247 }
250 } 248 }
251 249
252 return result; 250 return result;
253 } 251 }
254 252
255 public Gauge determineGaugeByName(String name) { 253 public Gauge determineGaugeByName(final String name) {
256 Session session = SessionHolder.HOLDER.get(); 254 final Session session = SessionHolder.HOLDER.get();
257 Query query = session.createQuery( 255 final Query query = session.createQuery(
258 "from Gauge where river=:river and name=:name"); 256 "from Gauge where river=:river and name=:name");
259 query.setParameter("river", this); 257 query.setParameter("river", this);
260 query.setParameter("name", name); 258 query.setParameter("name", name);
261 List<Gauge> gauges = query.list(); 259 final List<Gauge> gauges = query.list();
262 return gauges.isEmpty() ? null : gauges.get(0); 260 return gauges.isEmpty() ? null : gauges.get(0);
263 } 261 }
264 262
265 public Gauge determineGaugeByPosition(double p) { 263 public Gauge determineGaugeByPosition(final double p) {
266 // Per default, we prefer the gauge downstream 264 // Per default, we prefer the gauge downstream
267 return determineGaugeByPosition(p, getKmUp()); 265 return determineGaugeByPosition(p, getKmUp());
268 } 266 }
269 267
270 /** 268 /**
271 * @param p Station on this river for which the gauge is searched 269 * @param p Station on this river for which the gauge is searched
272 * @param kmLower At boundary of two gauge ranges, should gauge at lower 270 * @param kmLower At boundary of two gauge ranges, should gauge at lower
273 * km be returned? 271 * km be returned?
274 */ 272 */
275 public Gauge determineGaugeByPosition(double p, boolean kmLower) { 273 public Gauge determineGaugeByPosition(final double p, final boolean kmLower) {
276 Session session = SessionHolder.HOLDER.get(); 274 final Session session = SessionHolder.HOLDER.get();
277 Query query = session.createQuery( 275 final Query query = session.createQuery(
278 "from Gauge g where river=:river " + 276 "from Gauge g where river=:river " +
279 "and :p between " + 277 "and :p between " +
280 "least(g.range.a, g.range.b) and " + 278 "least(g.range.a, g.range.b) and " +
281 "greatest(g.range.a, g.range.b)"); 279 "greatest(g.range.a, g.range.b)");
282 query.setParameter("river", this); 280 query.setParameter("river", this);
283 query.setParameter("p", new BigDecimal(p, PRECISION)); 281 query.setParameter("p", new BigDecimal(p, PRECISION));
284 List<Gauge> gauges = query.list(); 282 final List<Gauge> gauges = query.list();
285 if (gauges.isEmpty()) { 283 if (gauges.isEmpty()) {
286 return null; 284 return null;
287 } 285 }
288 if (gauges.size() == 1) { 286 if (gauges.size() == 1) {
289 return gauges.get(0); 287 return gauges.get(0);
290 } 288 }
291 if (gauges.size() > 2) { 289 if (gauges.size() > 2) {
292 // TODO: database schema should prevent this. 290 // TODO: database schema should prevent this.
293 log.warn("More than two gauge ranges overlap km " + p + 291 log.warn("More than two gauge ranges overlap km " + p +
294 ". Returning arbitrary result."); 292 ". Returning arbitrary result.");
295 } 293 }
296 Gauge g0 = gauges.get(0); 294 final Gauge g0 = gauges.get(0);
297 Gauge g1 = gauges.get(1); 295 final Gauge g1 = gauges.get(1);
298 if (kmLower) { 296 if (kmLower) {
299 return 297 return
300 g0.getStation().doubleValue() < g1.getStation().doubleValue() 298 g0.getStation().doubleValue() < g1.getStation().doubleValue()
299 ? g0
300 : g1;
301 }
302 return g0.getStation().doubleValue() > g1.getStation().doubleValue()
301 ? g0 303 ? g0
302 : g1; 304 : g1;
303 }
304 return g0.getStation().doubleValue() > g1.getStation().doubleValue()
305 ? g0
306 : g1;
307 } 305 }
308 306
309 307
310 /** 308 /**
311 * @param s station at which the gauge is requested. 309 * @param s station at which the gauge is requested.
312 * @return Gauge within tolerance at given station. null if there is none. 310 * @return Gauge within tolerance at given station. null if there is none.
313 */ 311 */
314 public Gauge determineGaugeAtStation(double s) { 312 public Gauge determineGaugeAtStation(final double s) {
315 Session session = SessionHolder.HOLDER.get(); 313 final Session session = SessionHolder.HOLDER.get();
316 314
317 Query query = session.createQuery( 315 final Query query = session.createQuery(
318 "from Gauge where river.id=:river " + 316 "from Gauge where river.id=:river " +
319 "and station between :a and :b"); 317 "and station between :a and :b");
320 query.setParameter("river", getId()); 318 query.setParameter("river", getId());
321 query.setParameter("a", new BigDecimal(s - GAUGE_EPSILON)); 319 query.setParameter("a", new BigDecimal(s - GAUGE_EPSILON));
322 query.setParameter("b", new BigDecimal(s + GAUGE_EPSILON)); 320 query.setParameter("b", new BigDecimal(s + GAUGE_EPSILON));
323 321
324 List<Gauge> gauges = query.list(); 322 final List<Gauge> gauges = query.list();
325 if (gauges.size() > 1) { 323 if (gauges.size() > 1) {
326 log.warn("More than one gauge found at km " + s + 324 log.warn("More than one gauge found at km " + s +
327 " within +-" + GAUGE_EPSILON + 325 " within +-" + GAUGE_EPSILON +
328 ". Returning arbitrary result."); 326 ". Returning arbitrary result.");
329 } 327 }
330 return gauges.isEmpty() ? null : gauges.get(0); 328 return gauges.isEmpty() ? null : gauges.get(0);
331 } 329 }
332 330
333 public double[] determineMinMaxQ() { 331 public double[] determineMinMaxQ() {
334 Session session = SessionHolder.HOLDER.get(); 332 final Session session = SessionHolder.HOLDER.get();
335 333
336 Query query = session.createQuery( 334 final Query query = session.createQuery(
337 "select min(wqr.q) as min, max(wqr.q) as max " + 335 "select min(wqr.q) as min, max(wqr.q) as max " +
338 "from Wst as w " + 336 "from Wst as w " +
339 "join w.columns as wc " + 337 "join w.columns as wc " +
340 "join wc.columnQRanges as wcqr " + 338 "join wc.columnQRanges as wcqr " +
341 "join wcqr.wstQRange as wqr " + 339 "join wcqr.wstQRange as wqr " +
342 "where w.kind = 0 and river_id = :river"); 340 "where w.kind = 0 and river_id = :river");
343 341
344 query.setParameter("river", getId()); 342 query.setParameter("river", getId());
345 343
346 double minmax[] = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE }; 344 final double minmax[] = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE };
347 345
348 List<Object> results = query.list(); 346 final List<Object> results = query.list();
349 347
350 if (!results.isEmpty()) { 348 if (!results.isEmpty()) {
351 Object[] arr = (Object[]) results.get(0); 349 final Object[] arr = (Object[]) results.get(0);
352 BigDecimal minq = (BigDecimal)arr[0]; 350 final BigDecimal minq = (BigDecimal)arr[0];
353 BigDecimal maxq = (BigDecimal)arr[1]; 351 final BigDecimal maxq = (BigDecimal)arr[1];
354 minmax[0] = minq.doubleValue(); 352 minmax[0] = minq.doubleValue();
355 minmax[1] = maxq.doubleValue(); 353 minmax[1] = maxq.doubleValue();
356 } 354 }
357 355
358 return minmax; 356 return minmax;
360 358
361 /** 359 /**
362 * Determine reference gauge dependent on direction of calculation 360 * Determine reference gauge dependent on direction of calculation
363 * for a range calculation, otherwise dependent on flow direction. 361 * for a range calculation, otherwise dependent on flow direction.
364 */ 362 */
365 public Gauge determineRefGauge(double[] range, boolean isRange) { 363 public Gauge determineRefGauge(final double[] range, final boolean isRange) {
366 if (isRange) { 364 if (isRange) {
367 return determineGaugeByPosition( 365 return determineGaugeByPosition(
368 range[0], 366 range[0],
369 range[0] > range[1]); 367 range[0] > range[1]);
370 } 368 }
371 else { 369 else {
372 return determineGaugeByPosition(range[0]); 370 return determineGaugeByPosition(range[0]);
373 } 371 }
374 } 372 }
379 * distance. 377 * distance.
380 * 378 *
381 * @return the min and max distance of this river. 379 * @return the min and max distance of this river.
382 */ 380 */
383 public double[] determineMinMaxDistance() { 381 public double[] determineMinMaxDistance() {
384 Session session = SessionHolder.HOLDER.get(); 382 final Session session = SessionHolder.HOLDER.get();
385 383
386 Query query = session.createQuery( 384 final Query query = session.createQuery(
387 "select min(range.a), max(range.b) from Gauge " 385 "select min(range.a), max(range.b) from Gauge "
388 + "where river=:river " 386 + "where river=:river "
389 + "and range is not null"); 387 + "and range is not null");
390 query.setParameter("river", this); 388 query.setParameter("river", this);
391 389
392 List<Object[]> result = query.list(); 390 final List<Object[]> result = query.list();
393 391
394 if (!result.isEmpty()) { 392 if (!result.isEmpty()) {
395 Object[] minMax = result.get(0); 393 final Object[] minMax = result.get(0);
396 if (minMax[0] != null && minMax[1] != null) { 394 if (minMax[0] != null && minMax[1] != null) {
397 return new double[] { ((BigDecimal)minMax[0]).doubleValue(), 395 return new double[] { ((BigDecimal)minMax[0]).doubleValue(),
398 ((BigDecimal)minMax[1]).doubleValue() }; 396 ((BigDecimal)minMax[1]).doubleValue() };
399 } 397 }
400 } 398 }
401 399
402 return null; 400 return null;
403 } 401 }
404 402
405 public Map<Double, Double> queryGaugeDatumsKMs() { 403 public Map<Double, Double> queryGaugeDatumsKMs() {
406 List<Gauge> gauges = getGauges(); 404 final List<Gauge> gauges = getGauges();
407 Map<Double, Double> result = new TreeMap<Double, Double>(KM_CMP); 405 final Map<Double, Double> result = new TreeMap<>(KM_CMP);
408 406
409 for (Gauge gauge: gauges) { 407 for (final Gauge gauge: gauges) {
410 BigDecimal km = gauge.getStation(); 408 final BigDecimal km = gauge.getStation();
411 BigDecimal datum = gauge.getDatum(); 409 final BigDecimal datum = gauge.getDatum();
412 if (km != null && datum != null) { 410 if (km != null && datum != null) {
413 result.put(km.doubleValue(), datum.doubleValue()); 411 result.put(km.doubleValue(), datum.doubleValue());
414 } 412 }
415 } 413 }
416 414
417 return result; 415 return result;
418 } 416 }
419 417
418 /**
419 * Searches the gauges list of the river for a gauge number or a gauge name
420 */
421 public Gauge findGauge(final long number, final String name) {
422 for (final Gauge gauge : getGauges()) {
423 if (gauge.getOfficialNumber().longValue() == number)
424 return gauge;
425 if (gauge.getName().equalsIgnoreCase(name))
426 return gauge;
427 }
428 return null;
429 }
420 } 430 }
421 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : 431 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org