Mercurial > dive4elements > river
comparison artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepth/BedQualityD50KmValueFinder.java @ 8898:89f3c5462a16
Implemented S-INFO Flowdepth TKH calculation
author | mschaefer |
---|---|
date | Thu, 22 Feb 2018 12:03:31 +0100 |
parents | |
children | 0a900d605d52 |
comparison
equal
deleted
inserted
replaced
8891:f431aec10d2c | 8898:89f3c5462a16 |
---|---|
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 | |
11 package org.dive4elements.river.artifacts.sinfo.flowdepth; | |
12 | |
13 import java.util.Date; | |
14 import java.util.List; | |
15 | |
16 import org.apache.commons.lang.math.DoubleRange; | |
17 import org.apache.commons.math.ArgumentOutsideDomainException; | |
18 import org.apache.commons.math.analysis.interpolation.LinearInterpolator; | |
19 import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction; | |
20 import org.apache.log4j.Logger; | |
21 import org.dive4elements.river.artifacts.model.DateRange; | |
22 import org.dive4elements.river.backend.SedDBSessionHolder; | |
23 import org.dive4elements.river.model.River; | |
24 import org.hibernate.SQLQuery; | |
25 import org.hibernate.Session; | |
26 import org.hibernate.type.StandardBasicTypes; | |
27 | |
28 /** | |
29 * Searchable sorted km array with parallel bed measurements value array and linear interpolation for km and d50 between the array elements. | |
30 * @author Matthias Schäfer | |
31 * | |
32 */ | |
33 public class BedQualityD50KmValueFinder { | |
34 | |
35 /***** INNER CLASSES *****/ | |
36 | |
37 /** | |
38 * A bed measurements aggregate with its d50 characteristic grain diameter | |
39 */ | |
40 private class D50Measurement { | |
41 private double km; | |
42 public double getKm() { | |
43 return km; | |
44 } | |
45 private Date mindate; | |
46 public Date getMinDate() { | |
47 return mindate; | |
48 } | |
49 private Date maxdate; | |
50 public Date getMaxDate() { | |
51 return maxdate; | |
52 } | |
53 private int cnt; | |
54 public int getCnt() { | |
55 return cnt; | |
56 } | |
57 private double mindepth; | |
58 public double getMinDepth() { | |
59 return mindepth; | |
60 } | |
61 private double maxdepth; | |
62 public double getMaxDepth() { | |
63 return maxdepth; | |
64 } | |
65 private double d50; | |
66 /** | |
67 * D50 in m | |
68 */ | |
69 public double getD50() { | |
70 return d50; | |
71 } | |
72 /** | |
73 * Parameter constructor | |
74 */ | |
75 public D50Measurement(double km, Date mindate, Date maxdate, int cnt, double mindepth, double maxdepth, double d50mm) { | |
76 this.km = km; | |
77 this.mindate = mindate; | |
78 this.maxdate = maxdate; | |
79 this.cnt = cnt; | |
80 this.mindepth = mindepth; | |
81 this.maxdepth = maxdepth; | |
82 this.d50 = d50mm / 1000; | |
83 } | |
84 | |
85 /** | |
86 * Query result row constructor | |
87 */ | |
88 public D50Measurement(Object[] tuple, String[] aliases) { | |
89 km = 0; | |
90 mindate = null; | |
91 maxdate = null; | |
92 cnt = 0; | |
93 mindepth = Double.NaN; | |
94 maxdepth = Double.NaN; | |
95 d50 = Double.NaN; | |
96 for (int i = 0; i < tuple.length; ++i) { | |
97 if (tuple[i] == null) | |
98 continue; | |
99 switch (aliases[i]) { | |
100 case "km": | |
101 km = ((Number) tuple[i]).doubleValue(); | |
102 break; | |
103 case "mindate": | |
104 mindate = (Date) tuple[i]; | |
105 break; | |
106 case "maxdate": | |
107 maxdate = (Date) tuple[i]; | |
108 break; | |
109 case "cnt": | |
110 cnt = ((Number) tuple[i]).intValue(); | |
111 break; | |
112 case "mindepth": | |
113 mindepth = ((Number) tuple[i]).doubleValue(); | |
114 break; | |
115 case "maxdepth": | |
116 maxdepth = ((Number) tuple[i]).doubleValue(); | |
117 break; | |
118 case "d50": | |
119 d50 = ((Number) tuple[i]).doubleValue() / 1000; // mm to m | |
120 break; | |
121 default: | |
122 break; | |
123 } | |
124 } | |
125 } | |
126 } | |
127 | |
128 /***** FIELDS *****/ | |
129 | |
130 /** | |
131 * Private log to use here. | |
132 */ | |
133 private static Logger log = Logger.getLogger(BedQualityD50KmValueFinder.class); | |
134 | |
135 /** | |
136 * Query that aggregates by km for a km range and a time period all sub layer bed measurements with their d50<br /> | |
137 * <br /> | |
138 * A km may have bed measurements for multiple dates, multiple distances from the river bank, and multiple depth layers. | |
139 * The query filters by km range, time period and layer (sub layer: below bed to max. 50 cm depth). | |
140 * Those measurements are then grouped by km, and the D50 aggregated as average value. | |
141 */ | |
142 private static final String SQL_BED_D50_SUBLAYER_MEASUREMENT = | |
143 "SELECT t.km, MIN(t.datum) AS mindate, MAX(t.datum) AS maxdate, COUNT(*) AS cnt," | |
144 + " MIN(p.tiefevon) AS mindepth, MAX(p.tiefebis) AS maxdepth, AVG(a.d50) AS d50" | |
145 + " FROM sohltest t INNER JOIN station s ON t.stationid = s.stationid" | |
146 + " INNER JOIN gewaesser g ON s.gewaesserid = g.gewaesserid" | |
147 + " INNER JOIN sohlprobe p ON t.sohltestid = p.sohltestid" | |
148 + " INNER JOIN siebanalyse a ON p.sohlprobeid = a.sohlprobeid" | |
149 + " WHERE (g.name = :name) AND (s.km BETWEEN :fromkm - 0.0001 AND :tokm + 0.0001)" | |
150 + " AND (p.tiefevon > 0.0) AND (p.tiefebis <= 0.5)" | |
151 + " AND (t.datum BETWEEN :fromdate AND :todate)" | |
152 + " GROUP BY t.km" | |
153 + " ORDER BY t.km"; | |
154 private static final String[] SQL_BED_D50_SELECT_ALIAS = {"km", "mindate", "maxdate", "cnt", "mindepth", "maxdepth", "d50"}; | |
155 | |
156 /** | |
157 * Real linear interpolator for kms and d50 values | |
158 */ | |
159 private PolynomialSplineFunction interpolator; | |
160 | |
161 /***** METHODS *****/ | |
162 | |
163 /** | |
164 * Returns the d50 value interpolated according to a km | |
165 * @throws ArgumentOutsideDomainException | |
166 */ | |
167 public double findD50(double km) throws ArgumentOutsideDomainException { | |
168 return interpolator.value(km); | |
169 } | |
170 | |
171 /** | |
172 * Loads the range of the river's kms with their associated values. | |
173 * @return Whether the load has been successful | |
174 */ | |
175 public boolean loadValues(final River river, final DoubleRange kmRange, final DateRange dateRange) { | |
176 log.debug(String.format("loadValues km %.3f - %.3f %tF - %tF", kmRange.getMinimumDouble(), kmRange.getMaximumDouble(), dateRange.getFrom(), dateRange.getTo())); | |
177 Session session = SedDBSessionHolder.HOLDER.get(); | |
178 SQLQuery sqlQuery = session.createSQLQuery(SQL_BED_D50_SUBLAYER_MEASUREMENT) | |
179 .addScalar("km", StandardBasicTypes.DOUBLE) | |
180 .addScalar("mindate", StandardBasicTypes.DATE) | |
181 .addScalar("maxdate", StandardBasicTypes.DATE) | |
182 .addScalar("cnt", StandardBasicTypes.INTEGER) | |
183 .addScalar("mindepth", StandardBasicTypes.DOUBLE) | |
184 .addScalar("maxdepth", StandardBasicTypes.DOUBLE) | |
185 .addScalar("d50", StandardBasicTypes.DOUBLE); | |
186 String seddbRiver = river.nameForSeddb(); | |
187 sqlQuery.setString("name", seddbRiver); | |
188 sqlQuery.setDouble("fromkm", kmRange.getMinimumDouble()); | |
189 sqlQuery.setDouble("tokm", kmRange.getMaximumDouble()); | |
190 sqlQuery.setDate("fromdate", dateRange.getFrom()); | |
191 sqlQuery.setDate("todate", dateRange.getTo()); | |
192 @SuppressWarnings("unchecked") | |
193 final List<Object[]> rows = sqlQuery.list(); | |
194 final double[] kms = new double[rows.size()]; | |
195 final double[] values = new double[rows.size()]; | |
196 D50Measurement measurement; | |
197 int i = -1; | |
198 for (Object[] row : rows) { | |
199 measurement = new D50Measurement(row, SQL_BED_D50_SELECT_ALIAS); | |
200 i++; | |
201 kms[i] = measurement.getKm(); | |
202 values[i] = measurement.getD50(); | |
203 log.debug(String.format("loadValues km %.3f d50(mm) %.1f count %d", kms[i], values[i], measurement.getCnt())); | |
204 } | |
205 interpolator = new LinearInterpolator().interpolate(kms, values); | |
206 return true; | |
207 } | |
208 | |
209 } |