Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/common/WQBaseTableFinder.java @ 9176:1614cb14308f
Work on calculations for S-Info flood duration workflow
author | mschaefer |
---|---|
date | Mon, 25 Jun 2018 19:21:11 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9175:34dc0163ad2d | 9176:1614cb14308f |
---|---|
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.ArrayList; | |
13 import java.util.List; | |
14 import java.util.Map.Entry; | |
15 import java.util.NavigableMap; | |
16 import java.util.TreeMap; | |
17 | |
18 import org.apache.commons.math.FunctionEvaluationException; | |
19 import org.apache.commons.math.analysis.interpolation.LinearInterpolator; | |
20 import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; | |
21 import org.dive4elements.river.artifacts.math.Linear; | |
22 import org.dive4elements.river.artifacts.model.Calculation; | |
23 import org.dive4elements.river.backend.SessionHolder; | |
24 import org.dive4elements.river.model.River; | |
25 import org.hibernate.SQLQuery; | |
26 import org.hibernate.Session; | |
27 import org.hibernate.type.StandardBasicTypes; | |
28 | |
29 import gnu.trove.TDoubleArrayList; | |
30 | |
31 /** | |
32 * Loading and search/interpolation of a W/Q base table of a river | |
33 * | |
34 * @author Matthias Schäfer | |
35 * | |
36 */ | |
37 public final class WQBaseTableFinder { | |
38 | |
39 /***** FIELDS *****/ | |
40 | |
41 // private static Logger log = Logger.getLogger(WQTableFinder.class); | |
42 | |
43 private final River river; | |
44 | |
45 private Calculation problems; | |
46 | |
47 private final List<String> columnNames; | |
48 | |
49 private final NavigableMap<Double, PolynomialSplineFunction> kmWs; | |
50 | |
51 private final NavigableMap<Double, PolynomialSplineFunction> kmQs; | |
52 | |
53 private static final String SQLCOLUMNS = "SELECT wc.position AS colindex, wc.name AS qzone" | |
54 + " FROM wsts w" | |
55 + " INNER JOIN wst_columns wc ON w.id=wc.wst_id" | |
56 + " WHERE w.river_id = :river_id" | |
57 + " AND w.kind = 0" | |
58 + " ORDER BY wc.position ASC"; | |
59 | |
60 private static final String SQLMAIN = "SELECT wcv.position AS station, wc.position AS colindex, wcv.w, wqr.q" | |
61 + " FROM wsts w" | |
62 + " INNER JOIN wst_columns wc ON w.id=wc.wst_id" | |
63 + " INNER JOIN wst_column_values wcv ON wc.id=wcv.wst_column_id" | |
64 + " INNER JOIN wst_column_q_ranges wcqr ON wc.id=wcqr.wst_column_id" | |
65 + " INNER JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id=wqr.id" | |
66 + " INNER JOIN ranges r ON wqr.range_id=r.id AND wcv.position BETWEEN r.a AND r.b" | |
67 + " WHERE w.river_id = :river_id" | |
68 + " AND w.kind = 0" | |
69 + " AND r.river_id = :river_id" | |
70 + " AND wcv.position BETWEEN :kmfrom - 1 AND :kmto + 1" // some tolerance for start and end of list | |
71 + " ORDER BY wcv.position ASC, " | |
72 + " wc.position ASC"; | |
73 | |
74 | |
75 /***** CONSTRUCTORS *****/ | |
76 | |
77 private WQBaseTableFinder(final River river, final Calculation problems, final List<Object[]> colnames, final List<Object[]> rows) { | |
78 this.river = river; | |
79 this.problems = problems; | |
80 this.columnNames = new ArrayList<>(); | |
81 for (final Object[] colname : colnames) | |
82 this.columnNames.add(colname[1].toString()); | |
83 this.kmWs = new TreeMap<>(); | |
84 this.kmQs = new TreeMap<>(); | |
85 final TDoubleArrayList ws = new TDoubleArrayList(); | |
86 final TDoubleArrayList qs = new TDoubleArrayList(); | |
87 double km = Double.NaN; | |
88 for (int i = 0; i <= rows.size() - 1; i++) { | |
89 if (ws.isEmpty() || ((double) rows.get(i)[0] <= km + 0.0001)) { | |
90 if (ws.isEmpty()) | |
91 km = (double) rows.get(i)[0]; | |
92 ws.add((double) rows.get(i)[2]); | |
93 qs.add((double) rows.get(i)[3]); | |
94 } | |
95 if ((i == rows.size() - 1) || ((double) rows.get(i)[0] > km + 0.0001)) { | |
96 try { | |
97 this.kmWs.put(km, new LinearInterpolator().interpolate(ws.toNativeArray(), qs.toNativeArray())); | |
98 this.kmQs.put(km, new LinearInterpolator().interpolate(qs.toNativeArray(), ws.toNativeArray())); | |
99 } | |
100 catch (final Exception e) { | |
101 if (this.problems != null) { | |
102 this.problems.addProblem(km, "wq_base_data.missing"); | |
103 // Report only once | |
104 this.problems = null; | |
105 } | |
106 } | |
107 ws.clear(); | |
108 qs.clear(); | |
109 km = (double) rows.get(i)[0]; | |
110 ws.add((double) rows.get(i)[2]); | |
111 qs.add((double) rows.get(i)[3]); | |
112 } | |
113 } | |
114 } | |
115 | |
116 | |
117 /***** METHODS *****/ | |
118 | |
119 /** | |
120 * Loads the the W/Q tables of a km range of a river | |
121 * | |
122 * @return The W/Q table finder of the river, or null | |
123 */ | |
124 public static WQBaseTableFinder loadValues(final River river, final double fromKm, final double toKm, final Calculation problems) { | |
125 final Session session = SessionHolder.HOLDER.get(); | |
126 final SQLQuery colQuery = session.createSQLQuery(SQLCOLUMNS) | |
127 .addScalar("colindex", StandardBasicTypes.INTEGER) | |
128 .addScalar("qzone", StandardBasicTypes.STRING); | |
129 colQuery.setParameter("river_id", river.getId()); | |
130 final List<Object[]> colnames = colQuery.list(); | |
131 if ((colnames == null) || colnames.isEmpty()) { | |
132 problems.addProblem("wq_table.missing"); | |
133 return null; | |
134 } | |
135 final SQLQuery wqQuery = session.createSQLQuery(SQLMAIN) | |
136 .addScalar("station", StandardBasicTypes.DOUBLE) | |
137 .addScalar("colindex", StandardBasicTypes.INTEGER) | |
138 .addScalar("w", StandardBasicTypes.DOUBLE) | |
139 .addScalar("q", StandardBasicTypes.DOUBLE); | |
140 wqQuery.setParameter("river_id", river.getId()); | |
141 wqQuery.setParameter("kmfrom", fromKm); | |
142 wqQuery.setParameter("kmto", toKm); | |
143 final List<Object[]> rows = wqQuery.list(); | |
144 if ((rows != null) && !rows.isEmpty()) | |
145 return new WQBaseTableFinder(river, problems, colnames, rows); | |
146 else { | |
147 problems.addProblem("wq_table.missing"); | |
148 return null; | |
149 } | |
150 } | |
151 | |
152 /** | |
153 * If this provider may return valid data at all. | |
154 */ | |
155 public boolean isValid() { | |
156 return (this.kmWs != null); | |
157 } | |
158 | |
159 /** | |
160 * Discharge for a W | |
161 * | |
162 * @param station | |
163 * station to find or interpolate | |
164 * @param w | |
165 * W in m+NN or m+NHN | |
166 * @return Q, or NegInf for w less than all, or PosInf for w greater then all, or NaN in case of exception | |
167 */ | |
168 public double getDischarge(final double station, final double w) { | |
169 if (this.kmWs == null) | |
170 return Double.NaN; | |
171 final Entry<Double, PolynomialSplineFunction> lowerEntry = this.kmWs.floorEntry(Double.valueOf(station)); | |
172 final Entry<Double, PolynomialSplineFunction> upperEntry = this.kmWs.ceilingEntry(Double.valueOf(station)); | |
173 if ((lowerEntry == null) || (upperEntry == null)) | |
174 return Double.NaN; | |
175 try { | |
176 final double lowerQ = lowerEntry.getValue().value(w); | |
177 final double upperQ = upperEntry.getValue().value(w); | |
178 return Linear.linear(station, lowerEntry.getKey().doubleValue(), upperEntry.getKey().doubleValue(), lowerQ, upperQ); | |
179 } | |
180 catch (@SuppressWarnings("unused") final FunctionEvaluationException e) { | |
181 // ignore exception because this can/will happen regularly | |
182 return Double.NaN; | |
183 } | |
184 } | |
185 } |