teichmann@6385: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@6385: * Software engineering by Intevation GmbH teichmann@6385: * teichmann@6385: * This file is Free Software under the GNU AGPL (>=v3) teichmann@6385: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@6385: * documentation coming with Dive4Elements River for details. teichmann@6385: */ teichmann@6385: teichmann@6385: package org.dive4elements.river.artifacts.model; teichmann@6385: teichmann@6385: import java.util.ArrayList; teichmann@6385: import java.util.Collections; teichmann@6385: import java.util.HashMap; teichmann@6385: import java.util.List; teichmann@6385: import java.util.Map; teichmann@6385: teichmann@6385: import net.sf.ehcache.Cache; teichmann@6385: import net.sf.ehcache.Element; teichmann@6385: teichmann@6494: import org.apache.log4j.Logger; teichmann@6385: import org.dive4elements.river.artifacts.D4EArtifact; teichmann@6385: import org.dive4elements.river.artifacts.cache.CacheFactory; teichmann@6385: import org.dive4elements.river.model.Gauge; teichmann@6385: import org.dive4elements.river.model.MainValue; teichmann@6385: import org.dive4elements.river.model.NamedMainValue; teichmann@6385: import org.dive4elements.river.model.OfficialLine; teichmann@6385: import org.dive4elements.river.model.River; teichmann@6385: import org.dive4elements.river.model.Wst; teichmann@6385: import org.dive4elements.river.model.WstColumn; teichmann@6385: teichmann@6385: public class OfficialLineFinder teichmann@6385: { teichmann@6385: public static final String CACHE_NAME = "official-lines"; teichmann@6385: teichmann@6494: private static Logger log = Logger.getLogger(OfficialLineFinder.class); teichmann@6494: teichmann@6385: // We will only have one entry in this cache. teichmann@6385: public static final String CACHE_KEY = CACHE_NAME; teichmann@6385: teichmann@6385: public static final double EPSILON = 1e-4; teichmann@6385: teichmann@6385: teichmann@6385: public static class ValueRange extends Range { teichmann@6385: teichmann@6385: private double value; teichmann@6385: private int wstId; teichmann@6385: private int columnPos; teichmann@6397: private String name; teichmann@6385: teichmann@6385: public ValueRange( teichmann@6385: double start, teichmann@6385: double end, teichmann@6385: double value, teichmann@6385: int wstId, teichmann@6397: int columnPos, teichmann@6397: String name teichmann@6385: ) { teichmann@6385: super(start, end); teichmann@6385: this.value = value; teichmann@6385: this.wstId = wstId; teichmann@6385: this.columnPos = columnPos; teichmann@6397: this.name = name; teichmann@6385: } teichmann@6385: teichmann@6385: public boolean sameValue(double value) { teichmann@6385: return Math.abs(value - this.value) < EPSILON; teichmann@6385: } teichmann@6385: teichmann@6385: public int getWstId() { teichmann@6385: return wstId; teichmann@6385: } teichmann@6385: teichmann@6385: public int getColumnPos() { teichmann@6385: return columnPos; teichmann@6385: } teichmann@6397: teichmann@6397: public boolean intersectsValueRange(Range r) { teichmann@6397: return r.inside(value); teichmann@6397: } teichmann@6397: teichmann@6397: public String getName() { teichmann@6397: return name; teichmann@6397: } teichmann@6397: teichmann@6397: @Override teichmann@6397: public boolean equals(Object o) { teichmann@6397: if (!(o instanceof ValueRange)) { teichmann@6397: return false; teichmann@6397: } teichmann@6397: ValueRange r = (ValueRange)o; teichmann@6397: return wstId == r.wstId && columnPos == r.columnPos; teichmann@6397: } teichmann@6494: teichmann@6494: @Override teichmann@6494: public String toString() { teichmann@6494: return "[" + name + teichmann@6494: " value: " + value + teichmann@6494: " wstId: " + wstId + teichmann@6494: " pos: " + columnPos + "]"; teichmann@6494: } teichmann@6385: } teichmann@6385: teichmann@6385: public OfficialLineFinder() { teichmann@6385: } teichmann@6385: teichmann@6385: public static Map> getAll() { teichmann@6385: Cache cache = CacheFactory.getCache(CACHE_NAME); teichmann@6385: teichmann@6385: if (cache == null) { teichmann@6385: return getAllUncached(); teichmann@6385: } teichmann@6385: teichmann@6385: Element element = cache.get(CACHE_KEY); teichmann@6385: teichmann@6385: if (element != null) { teichmann@6385: return (Map>)element.getValue(); teichmann@6385: } teichmann@6385: teichmann@6385: Map> result = getAllUncached(); teichmann@6385: if (result != null) { teichmann@6385: cache.put(new Element(CACHE_KEY, result)); teichmann@6385: } teichmann@6385: return result; teichmann@6385: teichmann@6385: } teichmann@6385: teichmann@6385: public static Map> getAllUncached() { teichmann@6385: teichmann@6494: boolean debug = log.isDebugEnabled(); teichmann@6494: teichmann@6385: Map> rivers2officialLines = teichmann@6385: new HashMap>(); teichmann@6385: teichmann@6385: for (OfficialLine line: OfficialLine.fetchAllOfficalLines()) { teichmann@6494: NamedMainValue nmv = line.getNamedMainValue(); teichmann@6494: Integer mnvId = nmv.getId(); teichmann@6494: WstColumn wc = line.getWstColumn(); teichmann@6494: Wst wst = wc.getWst(); teichmann@6385: teichmann@6385: List ranges = new ArrayList(); teichmann@6385: teichmann@6385: River river = wst.getRiver(); teichmann@6385: List gauges = river.getGauges(); teichmann@6385: for (Gauge gauge: gauges) { teichmann@6385: List mainValues = gauge.getMainValues(); teichmann@6385: for (MainValue mainValue: mainValues) { teichmann@6494: NamedMainValue tnmv = mainValue.getMainValue(); teichmann@6494: if (tnmv.getId().equals(mnvId)) { teichmann@6385: // found gauge with this main value teichmann@6385: teichmann@6494: double from = gauge.getRange().getA().doubleValue(); teichmann@6494: double to = gauge.getRange().getA().doubleValue(); teichmann@6385: double value = mainValue.getValue().doubleValue(); teichmann@6385: int wstId = wst.getId(); teichmann@6385: int pos = wc.getPosition(); teichmann@6494: ValueRange range = new ValueRange( teichmann@6494: from, to, value, wstId, pos, nmv.getName()); teichmann@6494: teichmann@6494: if (debug) { teichmann@6494: log.debug( teichmann@6494: "river: " + river.getName() + teichmann@6494: " gauge: " + gauge.getName() + teichmann@6494: " ol: " + range); teichmann@6494: } teichmann@6385: ranges.add(range); teichmann@6385: break; teichmann@6385: } teichmann@6385: } teichmann@6385: } teichmann@6385: teichmann@6385: if (!ranges.isEmpty()) { teichmann@6494: String rname = river.getName(); teichmann@6494: List old = rivers2officialLines.get(rname); teichmann@6494: if (old != null) { teichmann@6494: old.addAll(ranges); teichmann@6494: } teichmann@6494: else { teichmann@6494: rivers2officialLines.put(rname, ranges); teichmann@6494: } teichmann@6385: } teichmann@6385: } teichmann@6385: teichmann@6385: return rivers2officialLines; teichmann@6385: } teichmann@6385: teichmann@6395: public static final Range MAX_RANGE = new Range(-Double.MAX_VALUE, +Double.MAX_VALUE); teichmann@6395: teichmann@6395: private static final String nn(String s) { teichmann@6395: return s != null ? s : ""; teichmann@6395: } teichmann@6395: teichmann@6395: public static Range extractRange(D4EArtifact artifact) { teichmann@6494: teichmann@6395: String mode = nn(artifact.getDataAsString("ld_mode")); teichmann@6395: String locations = nn(artifact.getDataAsString("ld_locations")); teichmann@6395: String from = nn(artifact.getDataAsString("ld_from")); teichmann@6395: String to = nn(artifact.getDataAsString("ld_to")); teichmann@6395: teichmann@6494: if (log.isDebugEnabled()) { teichmann@6494: log.debug("ld_mode: '" + mode + "'"); teichmann@6494: log.debug("ld_locations: '" + locations + "'"); teichmann@6494: log.debug("ld_from: '" + from + "'"); teichmann@6494: log.debug("ld_to: '" + to + "'"); teichmann@6494: } teichmann@6494: teichmann@6395: if (mode.equals("location")) { teichmann@6395: try { teichmann@6395: String loc = locations.replace(" ", ""); teichmann@6395: String[] split = loc.split(","); teichmann@6395: if (split.length < 1) { teichmann@6395: return MAX_RANGE; teichmann@6395: } teichmann@6395: double min = Double.parseDouble(split[0]); teichmann@6395: double max = min; teichmann@6395: for (int i = 1; i < split.length; ++i) { teichmann@6395: double v = Double.parseDouble(split[i]); teichmann@6395: if (v > max) max = v; teichmann@6395: if (v < min) min = v; teichmann@6395: } teichmann@6395: return new Range(min, max); teichmann@6395: } teichmann@6395: catch (NumberFormatException nfe) { teichmann@6395: return MAX_RANGE; teichmann@6395: } teichmann@6395: } teichmann@6395: try { teichmann@6395: return new Range(Double.parseDouble(from), Double.parseDouble(to)); teichmann@6395: } teichmann@6395: catch (NumberFormatException nfe) { teichmann@6395: return MAX_RANGE; teichmann@6395: } teichmann@6395: } teichmann@6395: teichmann@6395: private static List filterByRange(Range range, List ranges) { teichmann@6395: List list = new ArrayList(ranges.size()); teichmann@6395: for (ValueRange r: ranges) { teichmann@6395: if (r.intersects(range)) { teichmann@6395: list.add(r); teichmann@6395: } teichmann@6395: } teichmann@6395: return list; teichmann@6395: } teichmann@6395: teichmann@6397: private static List filterByQRange(Range range, List ranges) { teichmann@6397: List list = new ArrayList(ranges.size()); teichmann@6397: for (ValueRange r: ranges) { teichmann@6397: if (r.intersectsValueRange(range) && !list.contains(r)) { teichmann@6397: list.add(r); teichmann@6397: } teichmann@6397: } teichmann@6397: return list; teichmann@6397: } teichmann@6397: teichmann@6395: private static boolean isQ(D4EArtifact artifact) { teichmann@6395: Boolean b = artifact.getDataAsBoolean("wq_isq"); teichmann@6395: return b != null && b; teichmann@6395: } teichmann@6395: teichmann@6397: private static boolean isRange(D4EArtifact artifact) { teichmann@6397: Boolean b = artifact.getDataAsBoolean("wq_isrange"); teichmann@6397: return b != null && b; teichmann@6397: } teichmann@6397: teichmann@6397: public static final Range Q_OUT_OF_RANGE = new Range(-10000, -9999); teichmann@6397: teichmann@6397: private static Range singleQs(D4EArtifact artifact) { teichmann@6397: String singleData = nn(artifact.getDataAsString("wq_single")); teichmann@6397: double min = Double.MAX_VALUE; teichmann@6397: double max = -Double.MAX_VALUE; teichmann@6397: teichmann@6397: for (String value: singleData.split(" ")) { teichmann@6397: try { teichmann@6397: double x = Double.parseDouble(value); teichmann@6397: if (x < min) min = x; teichmann@6397: if (x > max) max = x; teichmann@6397: } teichmann@6397: catch (NumberFormatException nfe) { teichmann@6397: } teichmann@6397: } teichmann@6397: teichmann@6397: return min == Double.MAX_VALUE teichmann@6397: ? Q_OUT_OF_RANGE teichmann@6397: : new Range(min, max); teichmann@6397: teichmann@6397: } teichmann@6397: teichmann@6397: private static Range qRange(D4EArtifact artifact) { teichmann@6397: try { teichmann@6397: Double from = artifact.getDataAsDouble("wq_from"); teichmann@6397: Double to = artifact.getDataAsDouble("wq_to"); teichmann@6397: teichmann@6397: if (from == null || to == null) { teichmann@6397: return Q_OUT_OF_RANGE; teichmann@6397: } teichmann@6397: double f = from; teichmann@6397: double t = to; teichmann@6397: return new Range(Math.min(f, t), Math.max(f, t)); teichmann@6397: } teichmann@6397: catch (NumberFormatException nfe) { teichmann@6397: return Q_OUT_OF_RANGE; teichmann@6397: } teichmann@6397: } teichmann@6397: teichmann@6397: private static Range tripleQRange(D4EArtifact artifact) { teichmann@6397: String rangesData = nn(artifact.getDataAsString("wq_values")); teichmann@6397: teichmann@6397: double min = Double.MAX_VALUE; teichmann@6397: double max = -Double.MAX_VALUE; teichmann@6397: teichmann@6397: for (String range: rangesData.split(":")) { teichmann@6397: String [] parts = range.split(";"); teichmann@6397: if (parts.length < 3) { teichmann@6397: continue; teichmann@6397: } teichmann@6397: String [] values = parts[2].split(","); teichmann@6397: for (String value: values) { teichmann@6397: try { teichmann@6397: double x = Double.parseDouble(value); teichmann@6397: if (x < min) min = x; teichmann@6397: if (x > max) max = x; teichmann@6397: } teichmann@6397: catch (NumberFormatException nfe) { teichmann@6397: } teichmann@6397: } teichmann@6397: } teichmann@6397: teichmann@6397: return min == Double.MAX_VALUE teichmann@6397: ? Q_OUT_OF_RANGE teichmann@6397: : new Range(min, max); teichmann@6397: } teichmann@6397: teichmann@6397: public static List findOfficialLines(D4EArtifact artifact) { teichmann@6385: teichmann@6395: if (!isQ(artifact)) { // Only handle Q calculations teichmann@6397: return Collections.emptyList(); teichmann@6395: } teichmann@6395: teichmann@6385: Map> rivers2officialLines = getAll(); teichmann@6385: teichmann@6395: String riverName = nn(artifact.getDataAsString("river")); teichmann@6385: teichmann@6395: List ranges = rivers2officialLines.get(riverName); teichmann@6395: teichmann@6395: if (ranges == null) { teichmann@6397: return Collections.emptyList(); teichmann@6385: } teichmann@6494: boolean debug = log.isDebugEnabled(); teichmann@6494: teichmann@6494: if (debug) { teichmann@6494: log.debug("Before range filter:" + ranges); teichmann@6494: } teichmann@6385: teichmann@6395: ranges = filterByRange(extractRange(artifact), ranges); teichmann@6385: teichmann@6494: if (debug) { teichmann@6494: log.debug("After range filter:" + ranges); teichmann@6494: } teichmann@6494: teichmann@6385: if (ranges.isEmpty()) { teichmann@6397: return Collections.emptyList(); teichmann@6385: } teichmann@6385: teichmann@6397: Range qRange = isRange(artifact) teichmann@6397: ? qRange(artifact) teichmann@6397: : singleQs(artifact); teichmann@6395: teichmann@6397: if (qRange == Q_OUT_OF_RANGE) { teichmann@6397: qRange = tripleQRange(artifact); teichmann@6397: } teichmann@6385: teichmann@6494: if (debug) { teichmann@6494: log.debug("Q range filter: " + qRange); teichmann@6494: } teichmann@6494: teichmann@6494: ranges = filterByQRange(qRange, ranges); teichmann@6494: teichmann@6494: if (debug) { teichmann@6494: log.debug("After q range filter: " + ranges); teichmann@6494: } teichmann@6494: teichmann@6494: return ranges; teichmann@6385: } teichmann@6385: }