comparison artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableFactory.java @ 5838:5aa05a7a34b7

Rename modules to more fitting names.
author Sascha L. Teichmann <teichmann@intevation.de>
date Thu, 25 Apr 2013 15:23:37 +0200
parents flys-artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableFactory.java@bd047b71ab37
children 4897a58c8746
comparison
equal deleted inserted replaced
5837:d9901a08d0a6 5838:5aa05a7a34b7
1 package org.dive4elements.river.artifacts.model;
2
3 import java.util.Arrays;
4 import java.util.List;
5 import java.util.ArrayList;
6
7 import net.sf.ehcache.Cache;
8 import net.sf.ehcache.Element;
9
10 import org.dive4elements.river.artifacts.cache.CacheFactory;
11
12 import org.dive4elements.river.backend.SessionHolder;
13
14 import org.apache.log4j.Logger;
15
16 import org.dive4elements.river.model.River;
17 import org.dive4elements.river.model.Wst;
18
19 import org.hibernate.Session;
20 import org.hibernate.Query;
21 import org.hibernate.SQLQuery;
22
23 import org.hibernate.type.StandardBasicTypes;
24
25 /**
26 * Creates WstValueTable s from database.
27 * WstValueTable s are used to interpolate given w/q/km values.
28 */
29 public class WstValueTableFactory
30 {
31 private static Logger log = Logger.getLogger(WstValueTableFactory.class);
32
33 public static final int DEFAULT_KIND = 0;
34
35 // TODO: put this into a property file
36
37 public static final String HQL_WST =
38 "from Wst where river=:river and kind=:kind";
39
40 public static final String SQL_SELECT_NAMES_POS =
41 "SELECT position, name FROM wst_columns " +
42 "WHERE wst_id = :wst_id ORDER BY position";
43
44 /** Select Qs for wst (view sorted by column). */
45 public static final String SQL_SELECT_QS =
46 "SELECT column_pos, q, a, b FROM wst_q_values " +
47 "WHERE wst_id = :wst_id";
48
49 // (sorted by km)
50 public static final String SQL_SELECT_WS =
51 "SELECT km, w, column_pos FROM wst_w_values " +
52 "WHERE wst_id = :wst_id";
53
54 /** Statement to query qranges of a single column. */
55 public static final String SQL_SELECT_QS_AT_COL =
56 "SELECT q, a, b FROM wst_q_values " +
57 "WHERE wst_id = :wst_id AND column_pos = :column_pos";
58
59 // (sorted by km)
60 public static final String SQL_SELECT_WS_AT_COL =
61 "SELECT km, w FROM wst_w_values " +
62 "WHERE wst_id = :wst_id AND column_pos = :column_pos";
63
64
65 private WstValueTableFactory() {
66 }
67
68
69 public static WstValueTable getTable(River river) {
70 return getTable(river, DEFAULT_KIND);
71 }
72
73
74 /**
75 * Get WstValueTable to interpolate values of a given Wst.
76 */
77 public static WstValueTable getTable(int wst_id) {
78
79 Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
80
81 WstValueTableCacheKey cacheKey;
82
83 if (cache != null) {
84 // "-1" is the symbolic river-id for "no river, but wst_id".
85 cacheKey = new WstValueTableCacheKey(-1, wst_id);
86 Element element = cache.get(cacheKey);
87 if (element != null) {
88 log.debug("Got specific wst value table from cache");
89 return (WstValueTable) element.getValue();
90 }
91 }
92 else {
93 cacheKey = null;
94 }
95
96 Session session = SessionHolder.HOLDER.get();
97
98 // Fetch data for one column only.
99
100 WstValueTable.Column [] columns = loadColumns(session, wst_id);
101 int [] map = loadQRangesMap(session, columns, wst_id);
102 List<WstValueTable.Row> rows = loadRows(session, wst_id, columns.length, map);
103
104 WstValueTable valueTable = new WstValueTable(columns, rows);
105
106 if (valueTable != null && cacheKey != null) {
107 log.debug("Store wst value table in cache");
108 Element element = new Element(cacheKey, valueTable);
109 cache.put(element);
110 }
111
112 return valueTable;
113 }
114
115 /**
116 * Get Table for a specific column of a wst.
117 */
118 public static WstValueTable getWstColumnTable(int wst_id, int col_pos) {
119
120 Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
121
122 WstValueTableCacheKey cacheKey;
123
124 if (cache != null) {
125 // A negaitve/negative number is the symbolic 'river-id' for
126 // "no river and kind but wst_id and colpos".
127 cacheKey = new WstValueTableCacheKey(-wst_id, -col_pos);
128 Element element = cache.get(cacheKey);
129 if (element != null) {
130 log.debug("Got specific wst value table from cache");
131 return (WstValueTable) element.getValue();
132 }
133 }
134 else {
135 cacheKey = null;
136 }
137
138 Session session = SessionHolder.HOLDER.get();
139
140 // Fetch data for one column only.
141
142 WstValueTable.Column [] columns = loadColumn(session, wst_id, col_pos);
143 loadQRanges(session, columns, wst_id, col_pos);
144 List<WstValueTable.Row> rows = loadRowsOneColumn(session, wst_id, col_pos);
145
146 WstValueTable valueTable = new WstValueTable(columns, rows);
147
148 if (valueTable != null && cacheKey != null) {
149 log.debug("Store wst value table in cache (wst: "
150 + wst_id + "/ col: " + col_pos + ")");
151 Element element = new Element(cacheKey, valueTable);
152 cache.put(element);
153 }
154
155 return valueTable;
156 }
157
158
159 /**
160 * Get table for first wst of given kind at given river.
161 */
162 public static WstValueTable getTable(River river, int kind) {
163
164 Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
165
166 WstValueTableCacheKey cacheKey;
167
168 if (cache != null) {
169 cacheKey = new WstValueTableCacheKey(river.getId(), kind);
170 Element element = cache.get(cacheKey);
171 if (element != null) {
172 log.debug("got wst value table from cache");
173 return (WstValueTable)element.getValue();
174 }
175 }
176 else {
177 cacheKey = null;
178 }
179
180 WstValueTable valueTable = getTableUncached(river, kind);
181
182 if (valueTable != null && cacheKey != null) {
183 log.debug("store wst value table in cache");
184 Element element = new Element(cacheKey, valueTable);
185 cache.put(element);
186 }
187
188 return valueTable;
189 }
190
191 public static WstValueTable getTableUncached(River river) {
192 return getTableUncached(river, DEFAULT_KIND);
193 }
194
195 public static WstValueTable getTableUncached(River river, int kind) {
196
197 Session session = SessionHolder.HOLDER.get();
198
199 Wst wst = loadWst(session, river, kind);
200
201 if (wst == null) {
202 return null;
203 }
204
205 WstValueTable.Column [] columns = loadColumns(session, wst);
206
207 int map [] = loadQRangesMap(session, columns, wst);
208
209 List<WstValueTable.Row> rows =
210 loadRows(session, wst.getId(), columns.length, map);
211
212 return new WstValueTable(columns, rows);
213 }
214
215 /**
216 * @param kind Kind of wst.
217 */
218 protected static Wst loadWst(Session session, River river, int kind) {
219 Query query = session.createQuery(HQL_WST);
220 query.setParameter("river", river);
221 query.setInteger("kind", kind);
222
223 List<Wst> wsts = query.list();
224
225 // TODO Multiple wsts can match, why return just the first one?
226 return wsts.isEmpty() ? null : wsts.get(0);
227 }
228
229
230 /**
231 * Load rows with a single columns result.
232 *
233 * @param session session to use for querying db.
234 * @param wstId id of wst (in db).
235 * @param column_pos the column_pos (within the db) of the wst_value_table
236 * of which the values shall be fetched.
237 *
238 * @return resultant rows.
239 */
240 protected static List<WstValueTable.Row> loadRowsOneColumn(
241 Session session,
242 int wstId,
243 int column_pos
244 ) {
245 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS_AT_COL)
246 .addScalar("km", StandardBasicTypes.DOUBLE)
247 .addScalar("w", StandardBasicTypes.DOUBLE);
248
249 sqlQuery.setInteger("wst_id", wstId);
250 sqlQuery.setInteger("column_pos", column_pos);
251
252 List<Object []> results = sqlQuery.list();
253
254 double [] ws = null;
255
256 List<WstValueTable.Row> rows =
257 new ArrayList<WstValueTable.Row>(results.size());
258
259 // Walk over rows.
260 for (Object [] result: results) {
261 ws = new double[1];
262 WstValueTable.Row row =
263 new WstValueTable.Row((Double) result[0], ws);
264 rows.add(row);
265
266 Double w = (Double) result[1];
267 ws[0] = w != null ? w : Double.NaN;
268 }
269
270 return rows;
271 }
272
273 protected static List<WstValueTable.Row> loadRows(
274 Session session,
275 int wst_id,
276 int numColumns
277 ) {
278 return loadRows(session, wst_id, numColumns, null);
279 }
280
281 protected static List<WstValueTable.Row> loadRows(
282 Session session,
283 int wst_id,
284 int numColumns,
285 int [] map
286 ) {
287 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
288 .addScalar("km", StandardBasicTypes.DOUBLE)
289 .addScalar("w", StandardBasicTypes.DOUBLE)
290 .addScalar("column_pos", StandardBasicTypes.INTEGER);
291
292 sqlQuery.setInteger("wst_id", wst_id);
293
294 List<Object []> results = sqlQuery.list();
295
296 int lastColumn = Integer.MAX_VALUE;
297 double [] ws = null;
298
299 ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
300
301 for (Object [] result: results) {
302 int column = (Integer)result[2];
303 if (column < lastColumn) {
304 ws = new double[numColumns];
305 Arrays.fill(ws, Double.NaN);
306 WstValueTable.Row row =
307 new WstValueTable.Row((Double)result[0], ws);
308 rows.add(row);
309 }
310 Double w = (Double)result[1];
311 int index = map != null ? map[column] : column;
312 ws[index] = w != null ? w : Double.NaN;
313 lastColumn = column;
314 }
315
316 rows.trimToSize();
317 return rows;
318 }
319
320 protected static List<WstValueTable.Row> loadRows(
321 Session session,
322 Wst wst,
323 int numColumns
324 ) {
325 return loadRows(session, wst.getId(), numColumns);
326 }
327
328
329 protected static WstValueTable.Column [] loadColumn(
330 Session session,
331 int wst_id,
332 int col_pos
333 ) {
334 return new WstValueTable.Column [] {
335 new WstValueTable.Column(WKmsFactory.getWKmsName(col_pos, wst_id))};
336 }
337
338
339 /**
340 * Get columns from wst-id.
341 */
342 protected static WstValueTable.Column [] loadColumns(
343 Session session,
344 int wst_id
345 ) {
346 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
347 .addScalar("position", StandardBasicTypes.INTEGER)
348 .addScalar("name", StandardBasicTypes.STRING);
349
350 sqlQuery.setInteger("wst_id", wst_id);
351
352 List<Object []> columnNames = sqlQuery.list();
353
354 WstValueTable.Column [] columns =
355 new WstValueTable.Column[columnNames.size()];
356
357 for (int i = 0; i < columns.length; ++i) {
358 columns[i] = new WstValueTable.Column(
359 (String)columnNames.get(i)[1]);
360 }
361 return columns;
362 }
363
364 /**
365 * Get columns from Wst.
366 */
367 protected static WstValueTable.Column [] loadColumns(
368 Session session,
369 Wst wst
370 ) {
371 return loadColumns(session, wst.getId());
372 }
373
374
375 /**
376 * Build a QRange-Tree.
377 */
378 protected static void loadQRanges(
379 Session session,
380 WstValueTable.Column [] columns,
381 int wst_id,
382 int column_pos
383 ) {
384 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS_AT_COL)
385 .addScalar("q", StandardBasicTypes.DOUBLE)
386 .addScalar("a", StandardBasicTypes.DOUBLE)
387 .addScalar("b", StandardBasicTypes.DOUBLE);
388
389 sqlQuery.setInteger("wst_id", wst_id);
390 sqlQuery.setInteger("column_pos", column_pos);
391
392 List<Object []> qRanges = sqlQuery.list();
393
394 int qSize = qRanges.size();
395
396 QRangeTree qRangeTree = new QRangeTree(
397 qRanges, QRangeTree.WITHOUT_COLUMN, 0, qSize);
398 columns[0].setQRangeTree(qRangeTree);
399 }
400
401 protected static int [] loadQRangesMap(
402 Session session,
403 WstValueTable.Column [] columns,
404 int wst_id
405 ) {
406 SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
407 .addScalar("column_pos", StandardBasicTypes.INTEGER)
408 .addScalar("q", StandardBasicTypes.DOUBLE)
409 .addScalar("a", StandardBasicTypes.DOUBLE)
410 .addScalar("b", StandardBasicTypes.DOUBLE);
411
412 sqlQuery.setInteger("wst_id", wst_id);
413
414 List<Object []> qRanges = sqlQuery.list();
415
416 int start = -1;
417 int Q = qRanges.size();
418 Integer lastColumn = null;
419
420 for (int i = 0; i < Q; ++i) {
421 Object [] qRange = qRanges.get(i);
422 Integer columnId = (Integer)qRange[0];
423 if (lastColumn == null) {
424 lastColumn = columnId;
425 start = i;
426 }
427 else if (!lastColumn.equals(columnId)) {
428 QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
429 columns[lastColumn].setQRangeTree(qRangeTree);
430 lastColumn = columnId;
431 start = i;
432 }
433 }
434
435 if (start != -1) {
436 QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
437 columns[lastColumn].setQRangeTree(qRangeTree);
438 }
439
440 return sortColumnsByAverageQ(columns);
441
442 /* This is debug code to visualize the q ranges trees
443
444 java.io.PrintWriter out = null;
445 try {
446 out = new java.io.PrintWriter(
447 new java.io.FileWriter(
448 "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
449
450 out.println("graph \"Q ranges trees\" {");
451
452 for (int i = 0; i < columns.length; ++i) {
453 QRangeTree tree = columns[i].getQRangeTree();
454 out.println(tree.toGraph());
455 }
456
457 out.println("}");
458
459 out.flush();
460 }
461 catch (java.io.IOException ioe) {
462 log.error(ioe);
463 }
464 finally {
465 if (out != null) {
466 out.close();
467 }
468 }
469 */
470 }
471
472 private static final class QIndex implements Comparable<QIndex> {
473 double q;
474 int index;
475
476 QIndex(double q, int index) {
477 this.q = q;
478 this.index = index;
479 }
480
481 @Override
482 public int compareTo(QIndex other) {
483 double diff = q - other.q;
484 if (diff < 0d) return -1;
485 if (diff > 0d) return +1;
486 return 0;
487 }
488 } // class QIndex
489
490 /** Ensure that the q colums are sorted in ascending order. */
491 protected static int [] sortColumnsByAverageQ(WstValueTable.Column [] columns) {
492 QIndex [] order = new QIndex[columns.length];
493 for (int i = 0; i < order.length; ++i) {
494 QRangeTree tree = columns[i].getQRangeTree();
495 double avg = tree.averageQ();
496 double max = tree.maxQ();
497 double q = (avg+max)*0.5d;
498 order[i] = new QIndex(q, i);
499 }
500 Arrays.sort(order);
501 WstValueTable.Column [] copy = new WstValueTable.Column[order.length];
502 int [] map = new int[order.length];
503 for (int i = 0; i < copy.length; ++i) {
504 copy[i] = columns[order[i].index];
505 map[order[i].index] = i;
506 }
507 System.arraycopy(copy, 0, columns, 0, order.length);
508 return map;
509 }
510
511 protected static int [] loadQRangesMap(
512 Session session,
513 WstValueTable.Column [] columns,
514 Wst wst
515 ) {
516 return loadQRangesMap(session, columns, wst.getId());
517 }
518
519 }
520 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org