Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java @ 3814:8083f6384023
merged flys-artifacts/pre2.6-2012-01-04
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:56 +0200 |
parents | 6b0ae0f2cae6 |
children | 59af81364eb1 |
comparison
equal
deleted
inserted
replaced
1491:2a00f4849738 | 3814:8083f6384023 |
---|---|
1 package de.intevation.flys.artifacts.model; | |
2 | |
3 import java.util.List; | |
4 import java.util.Map; | |
5 import java.util.HashMap; | |
6 import java.util.Arrays; | |
7 import java.util.Comparator; | |
8 | |
9 import java.io.Serializable; | |
10 | |
11 import org.hibernate.Session; | |
12 import org.hibernate.Query; | |
13 | |
14 import org.apache.log4j.Logger; | |
15 | |
16 import de.intevation.flys.backend.SessionHolder; | |
17 import de.intevation.flys.model.Gauge; | |
18 import de.intevation.flys.model.DischargeTable; | |
19 import de.intevation.flys.model.DischargeTableValue; | |
20 | |
21 public class DischargeTables | |
22 implements Serializable | |
23 { | |
24 private static Logger log = Logger.getLogger(DischargeTables.class); | |
25 | |
26 public static final double DEFAULT_SCALE = 100.0; | |
27 | |
28 public static final int MASTER = 0; | |
29 | |
30 protected List<String> gaugeNames; | |
31 | |
32 protected String riverName; | |
33 | |
34 protected double scale; | |
35 | |
36 protected int kind; | |
37 | |
38 protected Map<String, double [][]> values; | |
39 | |
40 public DischargeTables() { | |
41 } | |
42 | |
43 public DischargeTables(String riverName, String gaugeName) { | |
44 this(riverName, gaugeName, MASTER); | |
45 } | |
46 | |
47 public DischargeTables(String riverName, String gaugeName, int kind) { | |
48 this(riverName, new String [] { gaugeName }, kind); | |
49 } | |
50 | |
51 public DischargeTables(String riverName, String [] gaugeNames) { | |
52 this(riverName, gaugeNames, MASTER); | |
53 } | |
54 | |
55 public DischargeTables(String riverName, String [] gaugeNames, int kind) { | |
56 this(riverName, Arrays.asList(gaugeNames), kind); | |
57 } | |
58 | |
59 public DischargeTables( | |
60 String riverName, | |
61 List<String> gaugeNames, | |
62 int kind | |
63 ) { | |
64 scale = Double.NaN; | |
65 this.kind = kind; | |
66 this.riverName = riverName; | |
67 this.gaugeNames = gaugeNames; | |
68 } | |
69 | |
70 public double [][] getFirstTable() { | |
71 return getFirstTable(DEFAULT_SCALE); | |
72 } | |
73 | |
74 public double [][] getFirstTable(double scale) { | |
75 Map<String, double [][]> values = getValues(scale); | |
76 for (double [][] table: values.values()) { | |
77 return table; | |
78 } | |
79 return null; | |
80 } | |
81 | |
82 public Map<String, double [][]> getValues() { | |
83 return getValues(DEFAULT_SCALE); | |
84 } | |
85 | |
86 public Map<String, double [][]> getValues(double scale) { | |
87 if (values == null || scale != this.scale) { | |
88 values = loadValues(scale); | |
89 this.scale = scale; | |
90 } | |
91 return values; | |
92 } | |
93 | |
94 /** | |
95 * Returns mapping of gauge name to values. | |
96 */ | |
97 protected Map<String, double [][]> loadValues(double scale) { | |
98 Map<String, double [][]> values = new HashMap<String, double [][]>(); | |
99 | |
100 Session session = SessionHolder.HOLDER.get(); | |
101 | |
102 Query gaugeQuery = session.createQuery( | |
103 "from Gauge where name=:gauge and river.name=:river"); | |
104 gaugeQuery.setParameter("river", riverName); | |
105 | |
106 for (String gaugeName: gaugeNames) { | |
107 gaugeQuery.setParameter("gauge", gaugeName); | |
108 List<Gauge> gauges = gaugeQuery.list(); | |
109 if (gauges.isEmpty()) { | |
110 log.warn( | |
111 "no gauge '"+gaugeName+"' at river '"+riverName+"'"); | |
112 continue; | |
113 } | |
114 Gauge gauge = gauges.get(0); | |
115 | |
116 List<DischargeTable> tables = gauge.getDischargeTables(); | |
117 | |
118 if (tables.isEmpty()) { | |
119 log.warn( | |
120 "no discharge table for gauge '" + gaugeName + "'"); | |
121 continue; | |
122 } | |
123 | |
124 // TODO: Filter by time interval | |
125 DischargeTable table = tables.get(0); | |
126 | |
127 List<DischargeTableValue> dtvs = | |
128 table.getDischargeTableValues(); | |
129 | |
130 final double [][] vs = new double[2][dtvs.size()]; | |
131 | |
132 boolean qSorted = true; | |
133 | |
134 double lastQ = -Double.MAX_VALUE; | |
135 | |
136 int idx = 0; | |
137 for (DischargeTableValue dtv: dtvs) { | |
138 double q = dtv.getQ().doubleValue(); | |
139 vs[0][idx] = q * scale; | |
140 vs[1][idx] = dtv.getW().doubleValue() * scale; | |
141 ++idx; | |
142 | |
143 if (qSorted && lastQ > q) { | |
144 qSorted = false; | |
145 } | |
146 lastQ = q; | |
147 } | |
148 | |
149 if (!qSorted) { | |
150 log.debug("need to sort by q values"); | |
151 // TODO: Do this db level. | |
152 // XXX: This is so ugly :-( | |
153 Integer [] indices = new Integer[vs[0].length]; | |
154 for (int i = 0; i < indices.length; ++i) { | |
155 indices[i] = i; | |
156 } | |
157 | |
158 Arrays.sort(indices, new Comparator<Integer>() { | |
159 public int compare(Integer a, Integer b) { | |
160 double va = vs[1][a]; | |
161 double vb = vs[1][b]; | |
162 double d = va - vb; | |
163 if (d < 0.0) return -1; | |
164 if (d > 0.0) return +1; | |
165 return 0; | |
166 } | |
167 }); | |
168 | |
169 double [][] vs2 = new double[2][vs[0].length]; | |
170 for (int i = 0; i < indices.length; ++i) { | |
171 vs2[0][i] = vs[0][indices[i]]; | |
172 vs2[1][i] = vs[1][indices[i]]; | |
173 } | |
174 values.put(gaugeName, vs2); | |
175 } | |
176 else { | |
177 values.put(gaugeName, vs); | |
178 } | |
179 | |
180 } | |
181 | |
182 return values; | |
183 } | |
184 | |
185 public static double getQForW(double [][] values, double w) { | |
186 | |
187 boolean debug = log.isDebugEnabled(); | |
188 | |
189 if (debug) { | |
190 log.debug("calculating getQForW(" + w + ")"); | |
191 } | |
192 | |
193 int index = Arrays.binarySearch(values[1], w); | |
194 if (index >= 0) { | |
195 return values[0][index]; | |
196 } | |
197 | |
198 index = -index - 1; // insert position | |
199 | |
200 if (index < 1 || index >= values[0].length) { | |
201 // do not extraploate | |
202 if (debug) { | |
203 log.debug("we do not extrapolate: NaN"); | |
204 } | |
205 return Double.NaN; | |
206 } | |
207 | |
208 double w1 = values[1][index-1]; | |
209 double w2 = values[1][index ]; | |
210 double q1 = values[0][index-1]; | |
211 double q2 = values[0][index ]; | |
212 | |
213 // q1 = m*w1 + b | |
214 // q2 = m*w2 + b | |
215 // q2 - q1 = m*(w2 - w1) | |
216 // m = (q2 - q1)/(w2 - w1) # w2 != w1 | |
217 // b = q1 - m*w1 | |
218 | |
219 double q; | |
220 if (w1 == w2) { | |
221 q = 0.5*(q1 + q2); | |
222 if (debug) { | |
223 log.debug("same w1 and w2: " + w1); | |
224 } | |
225 } | |
226 else { | |
227 double m = (q2 - q1)/(w2 - w1); | |
228 double b = q1 - m*w1; | |
229 q = w*m + b; | |
230 } | |
231 if (debug) { | |
232 log.debug("Q(" + w + ") = " + q); | |
233 } | |
234 return q; | |
235 } | |
236 } | |
237 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |