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@8036: import java.util.Collections; teichmann@8044: import java.util.Comparator; teichmann@8033: import java.util.Date; teichmann@8033: import java.util.List; teichmann@8033: import java.util.TreeMap; teichmann@8123: import java.util.TreeSet; teichmann@8033: teichmann@8186: import org.dive4elements.river.backend.utils.EpsilonComparator; teichmann@8033: teichmann@8033: public class SedimentLoadData implements Serializable teichmann@8033: { tom@8177: public static final int GF_UNKNOWN = -1; tom@8177: public static final int GF_COARSE = 0; tom@8177: public static final int GF_FINE_MIDDLE = 1; tom@8177: public static final int GF_SAND = 2; tom@8177: public static final int GF_SUSP_SAND = 3; tom@8177: public static final int GF_SUSP_SAND_BED = 4; tom@8177: public static final int GF_SUSP_SEDIMENT = 5; tom@8177: public static final int GF_TOTAL = 6; tom@8177: public static final int GF_BED_LOAD = 7; tom@8177: public static final int GF_BED_LOAD_SUSP_SAND = 8; tom@8185: public static final int GF_MAX = 8; teichmann@8033: teichmann@8048: public static final int [] MEASUREMENT_STATION_GF = { tom@8177: /* GF_COARSE */ Station.BED_LOAD, tom@8177: /* GF_FINE_MIDDLE */ Station.BED_LOAD, tom@8177: /* GF_SAND */ Station.BED_LOAD, tom@8177: /* GF_SUSP_SAND */ Station.BED_LOAD, tom@8177: /* GF_SUSP_SAND_BED */ Station.BED_LOAD, tom@8177: /* GF_SUSP_SEDIMENT */ Station.SUSPENDED, tom@8177: /* GF_TOTAL */ Station.BED_LOAD|Station.SUSPENDED, tom@8177: /* GF_BED_LOAD */ Station.BED_LOAD, tom@8177: /* GF_BED_LOAD_SUSP_SAND */ Station.BED_LOAD teichmann@8048: }; teichmann@8048: teichmann@8048: public static final int measurementStationType(int grainFraction) { teichmann@8048: return grainFraction < 0 || grainFraction >= MEASUREMENT_STATION_GF.length teichmann@8048: ? Station.UNKNOWN teichmann@8048: : MEASUREMENT_STATION_GF[grainFraction]; teichmann@8048: } teichmann@8048: 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@8041: if ("total".equals(name)) return GF_TOTAL; teichmann@8041: if ("bed_load".equals(name)) return GF_BED_LOAD; tom@8178: if ("bed_load_susp_sand".equals(name)) return GF_BED_LOAD_SUSP_SAND; teichmann@8039: return GF_UNKNOWN; teichmann@8033: } teichmann@8033: teichmann@8036: public interface Visitor { teichmann@8038: void visit(Station station); teichmann@8036: } teichmann@8036: teichmann@8033: public static class Value implements Serializable { teichmann@8033: teichmann@8036: public interface Filter { teichmann@8036: boolean accept(Value value); teichmann@8036: } teichmann@8036: teichmann@8047: public interface Visitor { teichmann@8047: void visit(Value value); teichmann@8047: } teichmann@8047: 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@8058: private int kind; 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@8058: public Load( tom@8185: int id, teichmann@8058: int kind, teichmann@8058: String description, teichmann@8058: Date startTime, teichmann@8058: Date stopTime teichmann@8058: ) { teichmann@8033: this.id = id; teichmann@8058: this.kind = kind; 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@8058: public int getKind() { teichmann@8058: return kind; teichmann@8058: } teichmann@8058: 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: } tom@8185: } // class Load teichmann@8033: teichmann@8033: public static class Station implements Serializable { teichmann@8033: teichmann@8048: public static final int UNKNOWN = 0; teichmann@8044: public static final int BED_LOAD = 1; teichmann@8044: public static final int SUSPENDED = 2; 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@8036: grainFractions.add(null); teichmann@8033: } teichmann@8033: this.type = type; teichmann@8033: this.station = station; teichmann@8033: } teichmann@8033: teichmann@8123: public void allLoads(Collection loads) { teichmann@8123: for (List values: grainFractions) { teichmann@8123: if (values != null) { teichmann@8123: for (Value value: values) { teichmann@8123: loads.add(value.getLoad()); teichmann@8123: } teichmann@8123: } teichmann@8123: } teichmann@8123: } teichmann@8123: 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@8044: public boolean isType(int type) { teichmann@8050: return (this.type & type) != 0; teichmann@8044: } teichmann@8044: 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@8036: public Station getNext(boolean isKMUp) { teichmann@8036: return isKMUp ? getNext() : getPrev(); teichmann@8036: } teichmann@8036: teichmann@8036: public Station getPrev(boolean isKMUp) { teichmann@8036: return isKMUp ? getPrev() : getNext(); teichmann@8036: } teichmann@8036: teichmann@8044: public void merge(Station other) { teichmann@8044: this.type |= other.type; teichmann@8044: for (int i = 0, N = grainFractions.size(); i < N; ++i) { teichmann@8044: grainFractions.set(i, teichmann@8044: mergeValues(grainFractions.get(i), other.grainFractions.get(i))); teichmann@8044: } teichmann@8038: } teichmann@8038: teichmann@8044: private static final Comparator ID_CMP = new Comparator() { teichmann@8044: @Override teichmann@8044: public int compare(Value a, Value b) { teichmann@8044: return a.getLoad().getId() - b.getLoad().getId(); teichmann@8044: } teichmann@8044: }; teichmann@8044: teichmann@8044: private static List mergeValues(List a, List b) { teichmann@8044: if (a == null) return b; teichmann@8044: if (b == null) return a; teichmann@8044: a.addAll(b); teichmann@8044: // re-establish id order. teichmann@8044: Collections.sort(a, ID_CMP); teichmann@8044: return a; teichmann@8044: } teichmann@8044: teichmann@8044: public Station nextByType(int type, boolean isKMUp) { teichmann@8044: for (Station curr = this; curr != null; curr = curr.getNext(isKMUp)) { teichmann@8044: if (curr.isType(type)) { teichmann@8044: return curr; teichmann@8044: } teichmann@8044: } teichmann@8044: return null; teichmann@8044: } teichmann@8044: teichmann@8044: public Station prevByType(int type, boolean isKMUp) { teichmann@8044: for (Station curr = this; curr != null; curr = curr.getPrev(isKMUp)) { teichmann@8044: if (curr.isType(type)) { teichmann@8044: return curr; teichmann@8044: } teichmann@8044: } teichmann@8044: return null; teichmann@8038: } teichmann@8038: teichmann@8033: public void addValue(int grainFraction, Value value) { teichmann@8036: List values = grainFractions.get(grainFraction); teichmann@8036: if (values == null) { teichmann@8036: values = new ArrayList(); teichmann@8036: grainFractions.set(grainFraction, values); teichmann@8036: } teichmann@8036: values.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@8036: return values != null && !values.isEmpty(); teichmann@8033: } teichmann@8033: teichmann@8047: public void filterGrainFraction( teichmann@8047: int grainFraction, teichmann@8047: Value.Filter filter, teichmann@8047: Value.Visitor visitor teichmann@8047: ) { teichmann@8036: List values = grainFractions.get(grainFraction); teichmann@8047: if (values != null && !values.isEmpty()) { teichmann@8047: for (Value value: values) { teichmann@8047: if (filter.accept(value)) { teichmann@8047: visitor.visit(value); teichmann@8047: } teichmann@8047: } teichmann@8033: } teichmann@8047: } teichmann@8047: teichmann@8047: public List filterGrainFraction(int grainFraction, Value.Filter filter) { teichmann@8047: final List result = new ArrayList(); teichmann@8047: filterGrainFraction(grainFraction, filter, new Value.Visitor() { teichmann@8047: @Override teichmann@8047: public void visit(Value value) { teichmann@8036: result.add(value); teichmann@8033: } teichmann@8047: }); teichmann@8036: return result; 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@8036: if (values == null) { teichmann@8036: return Double.NaN; teichmann@8036: } 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(); andre@8100: if (xid > id) hi = mid-1; andre@8100: 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@8040: private Station [] 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: andre@8092: public Station[] getStations() { andre@8092: return stations; andre@8092: } andre@8092: teichmann@8034: public void setStations(Collection stations) { teichmann@8038: TreeMap same = teichmann@8038: new TreeMap(EpsilonComparator.CMP); teichmann@8038: teichmann@8034: for (Station station: stations) { teichmann@8034: Double key = station.getStation(); teichmann@8038: Station st = same.get(key); teichmann@8038: if (st == null) { teichmann@8038: same.put(key, station); teichmann@8038: } else { teichmann@8044: st.merge(station); teichmann@8034: } teichmann@8034: } teichmann@8040: this.stations = new Station[same.size()]; teichmann@8040: int i = 0; teichmann@8040: for (Station station: same.values()) { teichmann@8040: this.stations[i++] = station; teichmann@8040: } teichmann@8034: wireNeighbors(); teichmann@8034: } teichmann@8033: teichmann@8034: private void wireNeighbors() { teichmann@8044: for (int i = 1; i < stations.length; ++i) { teichmann@8044: stations[i-1].setNext(stations[i]); teichmann@8044: stations[i].setPrev(stations[i-1]); teichmann@8033: } teichmann@8033: } 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@8139: while (lo <= hi) { teichmann@8139: int mid = (lo+hi)/2; teichmann@8139: Station st = stations[mid]; teichmann@8139: double station = st.getStation(); teichmann@8139: if (station < a) { teichmann@8139: lo = mid+1; teichmann@8139: } else if (station > b) { teichmann@8139: hi = mid-1; teichmann@8139: } else { teichmann@8139: recursiveFindStations(a, b, lo, mid-1, visitor); teichmann@8139: visitor.visit(st); teichmann@8139: lo = mid+1; teichmann@8139: } teichmann@8035: } teichmann@8035: } teichmann@8035: teichmann@8123: public static final Comparator LOAD_ID_CMP = new Comparator() { teichmann@8123: @Override teichmann@8123: public int compare(Load a, Load b) { teichmann@8123: return a.getId() - b.getId(); teichmann@8123: } teichmann@8123: }; teichmann@8123: teichmann@8123: public Collection findLoads(double a, double b) { teichmann@8140: final TreeSet loads = new TreeSet(LOAD_ID_CMP); teichmann@8123: teichmann@8123: findStations(a, b, new Visitor() { teichmann@8123: @Override teichmann@8123: public void visit(Station station) { teichmann@8123: station.allLoads(loads); teichmann@8123: } teichmann@8123: }); teichmann@8123: teichmann@8123: return loads; teichmann@8123: } teichmann@8123: 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@8040: recursiveFindStations(a, b, 0, stations.length-1, visitor); teichmann@8035: } teichmann@8035: teichmann@8038: public List findStations(double a, double b) { teichmann@8038: final List result = new ArrayList(); teichmann@8035: findStations(a, b, new Visitor() { teichmann@8035: @Override teichmann@8038: public void visit(Station station) { teichmann@8038: result.add(station); 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 :