changeset 8033:5e3f4b4fcb28

New way to loaded sediment loads from database and cache it. The data structure a way more straight forward. TODO: Implement calculation on this basis.
author Sascha L. Teichmann <teichmann@intevation.de>
date Tue, 15 Jul 2014 12:47:52 +0200
parents fd3a24336e6a
children b6e7cfcabf2c
files artifacts/doc/conf/cache.xml artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadData.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadDataFactory.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java
diffstat 5 files changed, 386 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/cache.xml	Mon Jul 14 15:36:44 2014 +0200
+++ b/artifacts/doc/conf/cache.xml	Tue Jul 15 12:47:52 2014 +0200
@@ -161,6 +161,12 @@
            timeToLiveSeconds="14400"
        />
 
+   <!-- This on is used to cache sediment loads of rivers. -->
+    <cache name="sediment-load-data"
+           maxElementsInMemory="3"
+           timeToLiveSeconds="86400"
+       />
+
 
     <!-- This one is used for the cross section lookup
          Because of lazy fetching and relatively big amount of data, disabled
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadData.java	Tue Jul 15 12:47:52 2014 +0200
@@ -0,0 +1,207 @@
+/* Copyright (C) 2014 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.NavigableMap;
+import java.util.TreeMap;
+
+import org.dive4elements.river.utils.EpsilonComparator;
+
+public class SedimentLoadData implements Serializable
+{
+    public static final double STATION_EPS = 0.0001;
+
+    public static final int GF_COARSE        = 0;
+    public static final int GF_FINE_MIDDLE   = 1;
+    public static final int GF_SAND          = 2;
+    public static final int GF_SUSP_SAND     = 3;
+    public static final int GF_SUSP_SAND_BED = 4;
+    public static final int GF_SUSP_SEDIMENT = 5;
+    public static final int GF_MAX           = 5;
+
+    public static final int grainFractionIndex(String name) {
+        if ("coarse".equals(name))             return GF_COARSE;
+        if ("fine_middle".equals(name))        return GF_FINE_MIDDLE;
+        if ("sand".equals(name))               return GF_SAND;
+        if ("susp_sand".equals(name))          return GF_SUSP_SAND;
+        if ("susp_sand_bed".equals(name))      return GF_SUSP_SAND_BED;
+        if ("suspended_sediment".equals(name)) return GF_SUSP_SEDIMENT;
+        return -1;
+    }
+
+    public static class Value implements Serializable {
+
+        private double value;
+
+        private Load load;
+
+        public Value() {
+        }
+
+        public Value(Load load, double value) {
+            this.load = load;
+            this.value = value;
+        }
+
+        public double getValue() {
+            return value;
+        }
+
+        public Load getLoad() {
+            return load;
+        }
+    } // class Value
+
+
+    public static class Load implements Serializable {
+
+        private int id;
+
+        private String description;
+
+        private Date startTime;
+        private Date stopTime;
+
+        public Load() {
+        }
+
+        public Load(int id, String description, Date startTime, Date stopTime) {
+            this.id          = id;
+            this.description = description;
+            this.startTime   = startTime;
+            this.stopTime    = stopTime;
+        }
+
+        public int getId() {
+            return id;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public Date getStartTime() {
+            return startTime;
+        }
+
+        public Date getStopTime() {
+            return stopTime;
+        }
+
+        public boolean isEpoch() {
+            return startTime != null && stopTime != null;
+        }
+    } // class SedimentLoad
+
+    public static class Station implements Serializable {
+
+        public static final int BED_LOAD  = 0;
+        public static final int SUSPENDED = 1;
+
+        public interface Visitor {
+            boolean accept(Station station);
+            boolean accept(int grainFraction);
+            boolean visit(Value value, int grainFraction);
+        }
+
+        private double station;
+
+        private int type;
+
+        private List<List<Value>> grainFractions;
+
+        public Station() {
+            this(BED_LOAD, 0.0);
+        }
+
+        public Station(int type, double station) {
+            grainFractions = new ArrayList<List<Value>>(GF_MAX+1);
+            for (int i = 0; i < GF_MAX+1; ++i) {
+                grainFractions.add(new ArrayList<Value>());
+            }
+            this.type = type;
+            this.station = station;
+        }
+
+        public double getStation() {
+            return station;
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public void addValue(int grainFraction, Value value) {
+            grainFractions.get(grainFraction).add(value);
+        }
+
+        public boolean hasGrainFraction(String grainFraction) {
+            return hasGrainFraction(grainFractionIndex(grainFraction));
+        }
+
+        public boolean hasGrainFraction(int grainFraction) {
+            List<Value> values = grainFractions.get(grainFraction);
+            return !values.isEmpty();
+        }
+
+        public void visit(Visitor visitor) {
+            if (!visitor.accept(this)) {
+                return;
+            }
+
+            for (int i = 0; i < GF_MAX+1; ++i) {
+                List<Value> values = grainFractions.get(i);
+                if (values.isEmpty() || !visitor.accept(i)) {
+                    continue;
+                }
+                for (Value value: values) {
+                    if (!visitor.visit(value, i)) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        public boolean inside(double a, double b) {
+            return station >= a && station <= b;
+        }
+    } // class Station
+
+
+    private TreeMap<Double, List<Station>> stations;
+
+    public SedimentLoadData() {
+        stations = new TreeMap<Double, List<Station>>(EpsilonComparator.CMP);
+    }
+
+    public void addStation(Station station) {
+        Double key = station.getStation();
+        List<Station> sts = stations.get(key);
+        if (sts == null) {
+            sts = new ArrayList<Station>(2);
+            stations.put(key, sts);
+        }
+        sts.add(station);
+    }
+
+
+    public NavigableMap<Double, List<Station>> range(double from, double to) {
+
+        if (from > to) {
+            double t = from; from = to; to = t;
+        }
+
+        return stations.subMap(from, true, to, true);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadDataFactory.java	Tue Jul 15 12:47:52 2014 +0200
@@ -0,0 +1,169 @@
+/* Copyright (C) 2014 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.artifacts.model.minfo;
+
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.river.artifacts.cache.CacheFactory;
+import org.dive4elements.river.backend.SessionHolder;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+import org.hibernate.type.StandardBasicTypes;
+
+public class SedimentLoadDataFactory
+{
+    private static Logger log = Logger.getLogger(SedimentLoadDataFactory.class);
+
+    public static final String CACHE_NAME = "sediment-load-data";
+
+    public static final String SUSPENDED_STRING = "Schwebstoff";
+
+    public static final String SQL_LOAD_RIVER_SEDIMENT_LOADS =
+        "SELECT " +
+          "sl.id AS sl_id, " +
+          "sl.description AS sl_description, " +
+          "ti.start_time AS ti_start_time, " +
+          "ti.stop_time AS ti_stoptime, " +
+          "slv.value AS slv_value, " +
+          "gf.name AS gf_name, " +
+          "ms.id AS ms_id, " +
+          "ms.station AS ms_station, " +
+          "ms.measurement_type AS ms_type " +
+        "FROM sediment_load_values slv " +
+          "JOIN sediment_load sl ON slv.sediment_load_id = sl.id " +
+          "JOIN time_intervals ti ON sl.time_interval_id = ti.id " +
+          "JOIN grain_fraction gf ON sl.grain_fraction_id = gf.id " +
+          "JOIN measurement_station ms ON slv.measurement_station_id = ms.id " +
+          "JOIN rivers r ON ms.river_id = r.id " +
+        "WHERE r.name = :river " +
+        "ORDER BY sl.id";
+
+    private SedimentLoadDataFactory() {
+    }
+
+    public SedimentLoadData getSedimentLoadData(String river) {
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug(
+                "Looking for sediment load data for river '" + river + "'");
+        }
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            if (debug) {
+                log.debug("Cache not configured.");
+            }
+            return getUncached(river);
+        }
+
+        String key = "sediment-load-" + river;
+
+        Element element = cache.get(key);
+
+        if (element != null) {
+            if (debug) {
+                log.debug("Sediment load data found in cache");
+            }
+            return (SedimentLoadData)element.getValue();
+        }
+
+        SedimentLoadData sedimentLoad = getUncached(river);
+
+        if (sedimentLoad != null) {
+            if (debug) {
+                log.debug("Store sediment load data in cache.");
+            }
+            cache.put(new Element(key, sedimentLoad));
+        }
+
+        return sedimentLoad;
+    }
+
+    public SedimentLoadData getUncached(String river) {
+
+        Session session = SessionHolder.HOLDER.get();
+
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_LOAD_RIVER_SEDIMENT_LOADS)
+            .addScalar("sl_id",          StandardBasicTypes.INTEGER)
+            .addScalar("sl_description", StandardBasicTypes.STRING)
+            .addScalar("ti_start_time",  StandardBasicTypes.TIMESTAMP)
+            .addScalar("ti_stop_time",   StandardBasicTypes.TIMESTAMP)
+            .addScalar("slv_value",      StandardBasicTypes.DOUBLE)
+            .addScalar("gf_name",        StandardBasicTypes.STRING)
+            .addScalar("ms_id",          StandardBasicTypes.INTEGER)
+            .addScalar("ms_station",     StandardBasicTypes.DOUBLE)
+            .addScalar("ms_type",        StandardBasicTypes.STRING);
+
+        sqlQuery.setString("river", river);
+
+        SedimentLoadData.Load load = null;
+        int grainFractionIndex = -1;
+
+        HashMap<Integer, SedimentLoadData.Station> id2station
+            = new HashMap<Integer, SedimentLoadData.Station>();
+
+        for (Iterator iter = sqlQuery.iterate(); iter.hasNext();) {
+            Object [] row = (Object [])iter.next();
+
+            Integer   sl_id            = (Integer)row[0];
+            String    sl_description   = (String)row[1];
+            Timestamp ti_start_time    = (Timestamp)row[2];
+            Timestamp ti_stop_time     = (Timestamp)row[3];
+            Double    slv_value        = (Double)row[4];
+            String    gf_name          = (String)row[5];
+            Integer   ms_id            = (Integer)row[6];
+            Double    ms_station       = (Double)row[7];
+            String    ms_type          = (String)row[8];
+
+            if (load == null || load.getId() != sl_id) {
+                load = new SedimentLoadData.Load(
+                    sl_id, sl_description, ti_start_time, ti_stop_time);
+
+                // Grain fractions only change when a new sediment load starts.
+                grainFractionIndex =
+                    SedimentLoadData.grainFractionIndex(gf_name);
+
+                if (grainFractionIndex == -1) {
+                    log.error("Unknown grain fraction type: " + gf_name);
+                    break;
+                }
+            }
+
+            SedimentLoadData.Station station = id2station.get(ms_id);
+            if (station == null) {
+                int type = ms_type.equalsIgnoreCase(SUSPENDED_STRING)
+                    ? SedimentLoadData.Station.SUSPENDED
+                    : SedimentLoadData.Station.BED_LOAD;
+
+                station = new SedimentLoadData.Station(type, ms_station);
+                id2station.put(ms_id, station);
+            }
+
+            station.addValue(
+                grainFractionIndex,
+                new SedimentLoadData.Value(load, slv_value));
+        }
+
+        SedimentLoadData sld = new SedimentLoadData();
+
+        for (SedimentLoadData.Station station: id2station.values()) {
+            sld.addStation(station);
+        }
+
+        return sld;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java	Mon Jul 14 15:36:44 2014 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java	Tue Jul 15 12:47:52 2014 +0200
@@ -176,6 +176,7 @@
                     values[0][i*3+2] = endValue;
                     values[1][i*3+2] = kmLoad.getValue();
                     matchFound = true;
+                    break;
                 }
             }
             // Store points without match for later assessment.
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java	Mon Jul 14 15:36:44 2014 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java	Tue Jul 15 12:47:52 2014 +0200
@@ -243,7 +243,7 @@
         SedimentLoadLSData[] values =
             getSedimentLoadsUncached(river, type, startKm, endKm);
 
-        if (values != null && key != null) {
+        if (values != null) {
             log.debug("Store static sediment load values in cache.");
             element = new Element(key, values);
             cache.put(element);
@@ -296,7 +296,7 @@
             syear,
             eyear);
 
-        if (values != null && key != null) {
+        if (values != null) {
             log.debug("Store sediment loads in cache.");
             element = new Element(key, values);
             cache.put(element);
@@ -316,7 +316,7 @@
         SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLE_TIMES_BY_ID)
                 .addScalar("starttime", StandardBasicTypes.DATE)
                 .addScalar("stoptime", StandardBasicTypes.DATE);
-        sqlQuery.setDouble("id", id);
+        sqlQuery.setInteger("id", id);
 
         List<Object[]> results = sqlQuery.list();
         Object[] row = results.get(0);

http://dive4elements.wald.intevation.org