diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTableFactory.java	Thu Apr 25 15:23:37 2013 +0200
@@ -0,0 +1,520 @@
+package org.dive4elements.river.artifacts.model;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+
+import org.dive4elements.river.backend.SessionHolder;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.river.model.River;
+import org.dive4elements.river.model.Wst;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+/**
+ * Creates WstValueTable s from database.
+ * WstValueTable s are used to interpolate given w/q/km values.
+ */
+public class WstValueTableFactory
+{
+    private static Logger log = Logger.getLogger(WstValueTableFactory.class);
+
+    public static final int DEFAULT_KIND = 0;
+
+    // TODO: put this into a property file
+
+    public static final String HQL_WST =
+        "from Wst where river=:river and kind=:kind";
+
+    public static final String SQL_SELECT_NAMES_POS =
+        "SELECT position, name FROM wst_columns " +
+        "WHERE wst_id = :wst_id ORDER BY position";
+
+    /** Select Qs for wst (view sorted by column). */
+    public static final String SQL_SELECT_QS =
+        "SELECT column_pos, q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id";
+
+    // (sorted by km)
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w, column_pos FROM wst_w_values " +
+        "WHERE wst_id = :wst_id";
+
+    /** Statement to query qranges of a single column. */
+    public static final String SQL_SELECT_QS_AT_COL =
+        "SELECT q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+    // (sorted by km)
+    public static final String SQL_SELECT_WS_AT_COL =
+        "SELECT km, w FROM wst_w_values " +
+        "WHERE wst_id = :wst_id AND column_pos = :column_pos";
+
+
+    private WstValueTableFactory() {
+    }
+
+
+    public static WstValueTable getTable(River river) {
+        return getTable(river, DEFAULT_KIND);
+    }
+
+
+    /**
+     * Get WstValueTable to interpolate values of a given Wst.
+     */
+    public static WstValueTable getTable(int wst_id) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            // "-1" is the symbolic river-id for "no river, but wst_id".
+            cacheKey = new WstValueTableCacheKey(-1, wst_id);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got specific wst value table from cache");
+                return (WstValueTable) element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        // Fetch data for one column only.
+
+        WstValueTable.Column [] columns = loadColumns(session, wst_id);
+        int [] map = loadQRangesMap(session, columns, wst_id);
+        List<WstValueTable.Row> rows = loadRows(session, wst_id, columns.length, map);
+
+        WstValueTable valueTable = new WstValueTable(columns, rows);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("Store wst value table in cache");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+    /**
+     * Get Table for a specific column of a wst.
+     */
+    public static WstValueTable getWstColumnTable(int wst_id, int col_pos) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            // A negaitve/negative number is the symbolic 'river-id' for
+            // "no river and kind but wst_id and colpos".
+            cacheKey = new WstValueTableCacheKey(-wst_id, -col_pos);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("Got specific wst value table from cache");
+                return (WstValueTable) element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        // Fetch data for one column only.
+
+        WstValueTable.Column [] columns = loadColumn(session, wst_id, col_pos);
+        loadQRanges(session, columns, wst_id, col_pos);
+        List<WstValueTable.Row> rows = loadRowsOneColumn(session, wst_id, col_pos);
+
+        WstValueTable valueTable = new WstValueTable(columns, rows);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("Store wst value table in cache (wst: "
+                + wst_id + "/ col: " + col_pos + ")");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+
+    /**
+     * Get table for first wst of given kind at given river.
+     */
+    public static WstValueTable getTable(River river, int kind) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new WstValueTableCacheKey(river.getId(), kind);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("got wst value table from cache");
+                return (WstValueTable)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WstValueTable valueTable = getTableUncached(river, kind);
+
+        if (valueTable != null && cacheKey != null) {
+            log.debug("store wst value table in cache");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+    public static WstValueTable getTableUncached(River river) {
+        return getTableUncached(river, DEFAULT_KIND);
+    }
+
+    public static WstValueTable getTableUncached(River river, int kind) {
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Wst wst = loadWst(session, river, kind);
+
+        if (wst == null) {
+            return null;
+        }
+
+        WstValueTable.Column [] columns = loadColumns(session, wst);
+
+        int map [] = loadQRangesMap(session, columns, wst);
+
+        List<WstValueTable.Row> rows =
+            loadRows(session, wst.getId(), columns.length, map);
+
+        return new WstValueTable(columns, rows);
+    }
+
+    /**
+     * @param kind Kind of wst.
+     */
+    protected static Wst loadWst(Session session, River river, int kind) {
+        Query query = session.createQuery(HQL_WST);
+        query.setParameter("river", river);
+        query.setInteger("kind",    kind);
+
+        List<Wst> wsts = query.list();
+
+        // TODO Multiple wsts can match, why return just the first one?
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
+
+
+    /**
+     * Load rows with a single columns result.
+     *
+     * @param session    session to use for querying db.
+     * @param wstId     id of wst (in db).
+     * @param column_pos the column_pos (within the db) of the wst_value_table
+     *                   of which the values shall be fetched.
+     *
+     * @return resultant rows.
+     */
+    protected static List<WstValueTable.Row> loadRowsOneColumn(
+        Session session,
+        int     wstId,
+        int     column_pos
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS_AT_COL)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("w",  StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wstId);
+        sqlQuery.setInteger("column_pos", column_pos);
+
+        List<Object []> results = sqlQuery.list();
+
+        double [] ws = null;
+
+        List<WstValueTable.Row> rows =
+            new ArrayList<WstValueTable.Row>(results.size());
+
+        // Walk over rows.
+        for (Object [] result: results) {
+            ws = new double[1];
+            WstValueTable.Row row =
+                new WstValueTable.Row((Double) result[0], ws);
+            rows.add(row);
+
+            Double w = (Double) result[1];
+            ws[0] = w != null ? w : Double.NaN;
+        }
+
+        return rows;
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        int     wst_id,
+        int     numColumns
+    ) {
+        return loadRows(session, wst_id, numColumns, null);
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        int     wst_id,
+        int     numColumns,
+        int []  map
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km",         StandardBasicTypes.DOUBLE)
+            .addScalar("w",          StandardBasicTypes.DOUBLE)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> results = sqlQuery.list();
+
+        int lastColumn = Integer.MAX_VALUE;
+        double [] ws = null;
+
+        ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
+
+        for (Object [] result: results) {
+            int column = (Integer)result[2];
+            if (column < lastColumn) {
+                ws = new double[numColumns];
+                Arrays.fill(ws, Double.NaN);
+                WstValueTable.Row row =
+                    new WstValueTable.Row((Double)result[0], ws);
+                rows.add(row);
+            }
+            Double w = (Double)result[1];
+            int index = map != null ? map[column] : column;
+            ws[index] = w != null ? w : Double.NaN;
+            lastColumn = column;
+        }
+
+        rows.trimToSize();
+        return rows;
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        Wst     wst,
+        int     numColumns
+    ) {
+        return loadRows(session, wst.getId(), numColumns);
+    }
+
+
+    protected static WstValueTable.Column [] loadColumn(
+        Session session,
+        int wst_id,
+        int col_pos
+    ) {
+        return new WstValueTable.Column [] {
+            new WstValueTable.Column(WKmsFactory.getWKmsName(col_pos, wst_id))};
+    }
+
+
+    /**
+     * Get columns from wst-id.
+     */
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        int wst_id
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
+            .addScalar("position",   StandardBasicTypes.INTEGER)
+            .addScalar("name",       StandardBasicTypes.STRING);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> columnNames = sqlQuery.list();
+
+        WstValueTable.Column [] columns =
+            new WstValueTable.Column[columnNames.size()];
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new WstValueTable.Column(
+                (String)columnNames.get(i)[1]);
+        }
+        return columns;
+    }
+
+    /**
+     * Get columns from Wst.
+     */
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        Wst     wst
+    ) {
+        return loadColumns(session, wst.getId());
+    }
+
+
+    /**
+     * Build a QRange-Tree.
+     */
+    protected static void loadQRanges(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        int                     wst_id,
+        int                     column_pos
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS_AT_COL)
+            .addScalar("q", StandardBasicTypes.DOUBLE)
+            .addScalar("a", StandardBasicTypes.DOUBLE)
+            .addScalar("b", StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id",     wst_id);
+        sqlQuery.setInteger("column_pos", column_pos);
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int qSize = qRanges.size();
+
+        QRangeTree qRangeTree = new QRangeTree(
+            qRanges, QRangeTree.WITHOUT_COLUMN, 0, qSize);
+        columns[0].setQRangeTree(qRangeTree);
+    }
+
+    protected static int [] loadQRangesMap(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        int                     wst_id
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER)
+            .addScalar("q",          StandardBasicTypes.DOUBLE)
+            .addScalar("a",          StandardBasicTypes.DOUBLE)
+            .addScalar("b",          StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wst_id);
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int     start      = -1;
+        int     Q          = qRanges.size();
+        Integer lastColumn = null;
+
+        for (int i = 0; i < Q; ++i) {
+            Object [] qRange = qRanges.get(i);
+            Integer columnId = (Integer)qRange[0];
+            if (lastColumn == null) {
+                lastColumn = columnId;
+                start = i;
+            }
+            else if (!lastColumn.equals(columnId)) {
+                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
+                columns[lastColumn].setQRangeTree(qRangeTree);
+                lastColumn = columnId;
+                start = i;
+            }
+        }
+
+        if (start != -1) {
+            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
+            columns[lastColumn].setQRangeTree(qRangeTree);
+        }
+
+        return sortColumnsByAverageQ(columns);
+
+        /* This is debug code to visualize the q ranges trees
+
+        java.io.PrintWriter out = null;
+        try {
+            out = new java.io.PrintWriter(
+                new java.io.FileWriter(
+                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
+
+            out.println("graph \"Q ranges trees\" {");
+
+            for (int i = 0; i < columns.length; ++i) {
+                QRangeTree tree = columns[i].getQRangeTree();
+                out.println(tree.toGraph());
+            }
+
+            out.println("}");
+
+            out.flush();
+        }
+        catch (java.io.IOException ioe) {
+            log.error(ioe);
+        }
+        finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+        */
+    }
+
+    private static final class QIndex implements Comparable<QIndex> {
+        double q;
+        int    index;
+
+        QIndex(double q, int index) {
+            this.q     = q;
+            this.index = index;
+        }
+
+        @Override
+        public int compareTo(QIndex other) {
+            double diff = q - other.q;
+            if (diff < 0d) return -1;
+            if (diff > 0d) return +1;
+            return 0;
+        }
+    } // class QIndex
+
+    /** Ensure that the q colums are sorted in ascending order. */
+    protected static int [] sortColumnsByAverageQ(WstValueTable.Column [] columns) {
+        QIndex [] order = new QIndex[columns.length];
+        for (int i = 0; i < order.length; ++i) {
+            QRangeTree tree = columns[i].getQRangeTree();
+            double avg = tree.averageQ();
+            double max = tree.maxQ();
+            double q   = (avg+max)*0.5d;
+            order[i] = new QIndex(q, i);
+        }
+        Arrays.sort(order);
+        WstValueTable.Column [] copy = new WstValueTable.Column[order.length];
+        int [] map = new int[order.length];
+        for (int i = 0; i < copy.length; ++i) {
+            copy[i] = columns[order[i].index];
+            map[order[i].index] = i;
+        }
+        System.arraycopy(copy, 0, columns, 0, order.length);
+        return map;
+    }
+
+    protected static int [] loadQRangesMap(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        Wst                     wst
+    ) {
+        return loadQRangesMap(session, columns, wst.getId());
+    }
+
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org