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 }

http://dive4elements.wald.intevation.org