teichmann@8033: /* Copyright (C) 2014 by Bundesanstalt für Gewässerkunde teichmann@8033: * Software engineering by Intevation GmbH teichmann@8033: * teichmann@8033: * This file is Free Software under the GNU AGPL (>=v3) teichmann@8033: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@8033: * documentation coming with Dive4Elements River for details. teichmann@8033: */ teichmann@8033: package org.dive4elements.river.artifacts.model.minfo; teichmann@8033: teichmann@8033: import java.io.Serializable; teichmann@8033: import java.util.ArrayList; teichmann@8034: import java.util.Collection; teichmann@8033: import java.util.Date; teichmann@8033: import java.util.List; teichmann@8033: import java.util.TreeMap; teichmann@8033: teichmann@8033: import org.dive4elements.river.utils.EpsilonComparator; teichmann@8033: teichmann@8033: public class SedimentLoadData implements Serializable teichmann@8033: { teichmann@8033: public static final double STATION_EPS = 0.0001; teichmann@8033: teichmann@8033: public static final int GF_COARSE = 0; teichmann@8033: public static final int GF_FINE_MIDDLE = 1; teichmann@8033: public static final int GF_SAND = 2; teichmann@8033: public static final int GF_SUSP_SAND = 3; teichmann@8033: public static final int GF_SUSP_SAND_BED = 4; teichmann@8033: public static final int GF_SUSP_SEDIMENT = 5; teichmann@8033: public static final int GF_MAX = 5; teichmann@8033: teichmann@8033: public static final int grainFractionIndex(String name) { teichmann@8033: if ("coarse".equals(name)) return GF_COARSE; teichmann@8033: if ("fine_middle".equals(name)) return GF_FINE_MIDDLE; teichmann@8033: if ("sand".equals(name)) return GF_SAND; teichmann@8033: if ("susp_sand".equals(name)) return GF_SUSP_SAND; teichmann@8033: if ("susp_sand_bed".equals(name)) return GF_SUSP_SAND_BED; teichmann@8033: if ("suspended_sediment".equals(name)) return GF_SUSP_SEDIMENT; teichmann@8033: return -1; teichmann@8033: } teichmann@8033: teichmann@8033: public static class Value implements Serializable { teichmann@8033: teichmann@8033: private double value; teichmann@8033: teichmann@8033: private Load load; teichmann@8033: teichmann@8033: public Value() { teichmann@8033: } teichmann@8033: teichmann@8033: public Value(Load load, double value) { teichmann@8033: this.load = load; teichmann@8033: this.value = value; teichmann@8033: } teichmann@8033: teichmann@8033: public double getValue() { teichmann@8033: return value; teichmann@8033: } teichmann@8033: teichmann@8033: public Load getLoad() { teichmann@8033: return load; teichmann@8033: } teichmann@8033: } // class Value teichmann@8033: teichmann@8033: teichmann@8033: public static class Load implements Serializable { teichmann@8033: teichmann@8033: private int id; teichmann@8033: teichmann@8033: private String description; teichmann@8033: teichmann@8033: private Date startTime; teichmann@8033: private Date stopTime; teichmann@8033: teichmann@8033: public Load() { teichmann@8033: } teichmann@8033: teichmann@8033: public Load(int id, String description, Date startTime, Date stopTime) { teichmann@8033: this.id = id; teichmann@8033: this.description = description; teichmann@8033: this.startTime = startTime; teichmann@8033: this.stopTime = stopTime; teichmann@8033: } teichmann@8033: teichmann@8033: public int getId() { teichmann@8033: return id; teichmann@8033: } teichmann@8033: teichmann@8033: public String getDescription() { teichmann@8033: return description; teichmann@8033: } teichmann@8033: teichmann@8033: public Date getStartTime() { teichmann@8033: return startTime; teichmann@8033: } teichmann@8033: teichmann@8033: public Date getStopTime() { teichmann@8033: return stopTime; teichmann@8033: } teichmann@8033: teichmann@8033: public boolean isEpoch() { teichmann@8033: return startTime != null && stopTime != null; teichmann@8033: } teichmann@8033: } // class SedimentLoad teichmann@8033: teichmann@8033: public static class Station implements Serializable { teichmann@8033: teichmann@8033: public static final int BED_LOAD = 0; teichmann@8033: public static final int SUSPENDED = 1; teichmann@8033: teichmann@8033: public interface Visitor { teichmann@8033: boolean accept(Station station); teichmann@8033: boolean accept(int grainFraction); teichmann@8033: boolean visit(Value value, int grainFraction); teichmann@8033: } teichmann@8033: teichmann@8033: private double station; teichmann@8033: teichmann@8033: private int type; teichmann@8033: teichmann@8033: private List> grainFractions; teichmann@8033: teichmann@8034: private Station next; teichmann@8034: private Station prev; teichmann@8034: teichmann@8033: public Station() { teichmann@8033: this(BED_LOAD, 0.0); teichmann@8033: } teichmann@8033: teichmann@8033: public Station(int type, double station) { teichmann@8033: grainFractions = new ArrayList>(GF_MAX+1); teichmann@8033: for (int i = 0; i < GF_MAX+1; ++i) { teichmann@8033: grainFractions.add(new ArrayList()); teichmann@8033: } teichmann@8033: this.type = type; teichmann@8033: this.station = station; teichmann@8033: } teichmann@8033: teichmann@8033: public double getStation() { teichmann@8033: return station; teichmann@8033: } teichmann@8033: teichmann@8033: public int getType() { teichmann@8033: return type; teichmann@8033: } teichmann@8033: teichmann@8034: public void setNext(Station next) { teichmann@8034: this.next = next; teichmann@8034: } teichmann@8034: teichmann@8034: public Station getNext() { teichmann@8034: return next; teichmann@8034: } teichmann@8034: teichmann@8034: public void setPrev(Station prev) { teichmann@8034: this.prev = next; teichmann@8034: } teichmann@8034: teichmann@8034: public Station getPrev() { teichmann@8034: return prev; teichmann@8034: } teichmann@8034: teichmann@8033: public void addValue(int grainFraction, Value value) { teichmann@8033: grainFractions.get(grainFraction).add(value); teichmann@8033: } teichmann@8033: teichmann@8033: public boolean hasGrainFraction(String grainFraction) { teichmann@8033: return hasGrainFraction(grainFractionIndex(grainFraction)); teichmann@8033: } teichmann@8033: teichmann@8033: public boolean hasGrainFraction(int grainFraction) { teichmann@8033: List values = grainFractions.get(grainFraction); teichmann@8033: return !values.isEmpty(); teichmann@8033: } teichmann@8033: teichmann@8033: public void visit(Visitor visitor) { teichmann@8033: if (!visitor.accept(this)) { teichmann@8033: return; teichmann@8033: } teichmann@8033: teichmann@8033: for (int i = 0; i < GF_MAX+1; ++i) { teichmann@8033: List values = grainFractions.get(i); teichmann@8033: if (values.isEmpty() || !visitor.accept(i)) { teichmann@8033: continue; teichmann@8033: } teichmann@8033: for (Value value: values) { teichmann@8033: if (!visitor.visit(value, i)) { teichmann@8033: break; teichmann@8033: } teichmann@8033: } teichmann@8033: } teichmann@8033: } teichmann@8033: teichmann@8033: public boolean inside(double a, double b) { teichmann@8033: return station >= a && station <= b; teichmann@8033: } teichmann@8034: teichmann@8034: public double findValueByLoadId(int id) { teichmann@8034: for (List values: grainFractions) { teichmann@8034: double value = findValueByLoadId(values, id); teichmann@8034: if (!Double.isNaN(value)) { teichmann@8034: return value; teichmann@8034: } teichmann@8034: } teichmann@8034: return Double.NaN; teichmann@8034: } teichmann@8034: teichmann@8034: private static final double findValueByLoadId( teichmann@8034: List values, teichmann@8034: int id teichmann@8034: ) { teichmann@8034: // List is ordered by station id -> binary search. teichmann@8034: int lo = 0, hi = values.size()-1; teichmann@8034: while (lo <= hi) { teichmann@8034: int mid = (lo + hi)/2; teichmann@8034: Value v = values.get(mid); teichmann@8034: int xid = v.getLoad().getId(); teichmann@8034: if (xid < id) hi = mid-1; teichmann@8034: else if (xid > id) lo = mid+1; teichmann@8034: else return v.getValue(); teichmann@8034: } teichmann@8034: teichmann@8034: return Double.NaN; teichmann@8034: } teichmann@8033: } // class Station teichmann@8033: teichmann@8033: teichmann@8034: private List> stations; teichmann@8033: teichmann@8033: public SedimentLoadData() { teichmann@8033: } teichmann@8033: teichmann@8034: public SedimentLoadData(Collection stations) { teichmann@8034: setStations(stations); teichmann@8033: } teichmann@8033: teichmann@8034: public void setStations(Collection stations) { teichmann@8034: TreeMap> same = teichmann@8034: new TreeMap>(EpsilonComparator.CMP); teichmann@8034: for (Station station: stations) { teichmann@8034: Double key = station.getStation(); teichmann@8034: List sts = same.get(key); teichmann@8034: if (sts == null) { teichmann@8034: sts = new ArrayList(2); teichmann@8034: same.put(key, sts); teichmann@8034: } teichmann@8034: sts.add(station); teichmann@8034: } teichmann@8034: this.stations = new ArrayList>(same.values()); teichmann@8034: wireNeighbors(); teichmann@8034: } teichmann@8033: teichmann@8034: private void wireNeighbors() { teichmann@8034: for (int i = 0, N = stations.size()-1; i < N; ++i) { teichmann@8034: for (Station current: stations.get(i)) { teichmann@8034: int type = current.getType(); teichmann@8034: NEXT: for (int j = i+1; j < N; ++j) { teichmann@8034: for (Station next: stations.get(j)) { teichmann@8034: if (next.getType() == type) { teichmann@8034: current.setNext(next); teichmann@8034: next.setPrev(current); teichmann@8034: break NEXT; teichmann@8034: } teichmann@8034: } teichmann@8034: } teichmann@8034: } teichmann@8033: } teichmann@8033: } teichmann@8035: teichmann@8035: public interface Visitor { teichmann@8035: void visit(List stations); teichmann@8035: } teichmann@8035: teichmann@8035: private void recursiveFindStations( teichmann@8035: double a, double b, teichmann@8035: int lo, int hi, teichmann@8035: Visitor visitor teichmann@8035: ) { teichmann@8035: while (lo >= hi) { teichmann@8035: int mid = (lo+hi)/2; teichmann@8035: List sts = stations.get(mid); teichmann@8035: double station = sts.get(0).getStation(); teichmann@8035: if (station < a) { teichmann@8035: hi = mid-1; teichmann@8035: } else if (station > b) { teichmann@8035: lo = mid+1; teichmann@8035: } else { teichmann@8035: recursiveFindStations(a, b, lo, mid-1, visitor); teichmann@8035: visitor.visit(sts); teichmann@8035: lo = mid+1; teichmann@8035: } teichmann@8035: } teichmann@8035: } teichmann@8035: teichmann@8035: public void findStations(double a, double b, Visitor visitor) { teichmann@8035: if (a > b) { teichmann@8035: double t = a; a = b; b = t; teichmann@8035: } teichmann@8035: recursiveFindStations(a, b, 0, stations.size()-1, visitor); teichmann@8035: } teichmann@8035: teichmann@8035: public List> findStations(double a, double b) { teichmann@8035: final List> result = new ArrayList>(); teichmann@8035: findStations(a, b, new Visitor() { teichmann@8035: @Override teichmann@8035: public void visit(List stations) { teichmann@8035: result.add(stations); teichmann@8035: } teichmann@8035: }); teichmann@8035: return result; teichmann@8035: } teichmann@8033: } teichmann@8033: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :