Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadData.java @ 8750:69b0a71b4845
(issue1448) Make sq time intervals distinct only for non epoch loads
Previously it was random if the loads choosen to be part of the
distinct sq time interval loads set were epoch loads or not.
This led to wrong results beeing removed in the isEpoch check
afterwards.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Fri, 19 Jun 2015 16:35:13 +0200 |
parents | 301a32bd7045 |
children | d0f2250b5521 |
line wrap: on
line source
/* 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.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.TreeMap; import java.util.TreeSet; import org.apache.log4j.Logger; import org.dive4elements.river.backend.utils.EpsilonComparator; public class SedimentLoadData implements Serializable { private static Logger log = Logger.getLogger(SedimentLoadData.class); public static final int GF_UNKNOWN = -1; 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_TOTAL = 6; public static final int GF_BED_LOAD = 7; public static final int GF_BED_LOAD_SUSP_SAND = 8; public static final int GF_SUSP_LOAD = 9; public static final int GF_MAX = 9; public static final String [] GF_NAMES = { "coarse", "fine_middle", "sand", "susp_sand", "susp_sand_bed", "suspended_sediment", "total", "bed_load", "bed_load_susp_sand", "suspended_load" }; public static final int [] MEASUREMENT_STATION_GF = { /* GF_COARSE */ Station.BED_LOAD, /* GF_FINE_MIDDLE */ Station.BED_LOAD, /* GF_SAND */ Station.BED_LOAD, /* GF_SUSP_SAND */ Station.BED_LOAD, /* GF_SUSP_SAND_BED */ Station.BED_LOAD, /* GF_SUSP_SEDIMENT */ Station.SUSPENDED, /* GF_TOTAL */ Station.BED_LOAD|Station.SUSPENDED, /* GF_BED_LOAD */ Station.BED_LOAD, /* GF_BED_LOAD_SUSP_SAND */ Station.BED_LOAD, /* GF_SUSP_LOAD */ Station.SUSPENDED }; public static final int measurementStationType(int grainFraction) { return grainFraction < 0 || grainFraction >= MEASUREMENT_STATION_GF.length ? Station.UNKNOWN : MEASUREMENT_STATION_GF[grainFraction]; } public static final int grainFractionIndex(String name) { for (int i = 0; i < GF_NAMES.length; ++i) { if (GF_NAMES[i].equals(name)) { return i; } } return GF_UNKNOWN; } public static final String grainFractionName(int index) { return index >= 0 && index < GF_NAMES.length ? GF_NAMES[index] : "unknown"; } public interface Visitor { void visit(Station station); } public static class Value implements Serializable { public interface Filter { boolean accept(Value value); } public interface Visitor { void visit(Value value); } 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 int kind; private Integer sqTiId; private String description; private Date startTime; private Date stopTime; private Date sqStartTime; private Date sqStopTime; public Load() { } public Load( int id, int kind, String description, Date startTime, Date stopTime, Integer sqTiId, Date sqStartTime, Date sqStopTime ) { this.id = id; this.kind = kind; this.description = description; this.startTime = startTime; this.stopTime = stopTime; this.sqStartTime = sqStartTime; this.sqStopTime = sqStopTime; this.sqTiId = sqTiId; } public int getId() { return id; } public Integer getSQRelationTimeIntervalId() { return sqTiId; } public int getKind() { return kind; } public String getDescription() { return description; } public Date getStartTime() { return startTime; } public Date getStopTime() { return stopTime; } public Date getSQStartTime() { return sqStartTime; } public Date getSQStopTime() { return sqStopTime; } public boolean isEpoch() { return startTime != null && stopTime != null; } } // class Load public static class Station implements Serializable { public static final int UNKNOWN = 0; public static final int BED_LOAD = 1; public static final int SUSPENDED = 2; private double station; private int type; private List<List<Value>> grainFractions; private Station prev; 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(null); } this.type = type; this.station = station; } public void allLoads(Collection<Load> loads) { for (List<Value> values: grainFractions) { if (values != null) { for (Value value: values) { loads.add(value.getLoad()); } } } } public void allNonEpochLoads(Collection<Load> loads) { for (List<Value> values: grainFractions) { if (values != null) { for (Value value: values) { Load load = value.getLoad(); if (load.isEpoch()) { continue; } loads.add(value.getLoad()); } } } } public void allOfficialLoads(Collection<Load> loads) { for (List<Value> values: grainFractions) { if (values != null) { for (Value value: values) { Load load = value.getLoad(); if (load.getKind() == 1) { loads.add(value.getLoad()); } } } } } public void allLoads(Collection<Load> loads, Integer sqRelationTimeInterval) { for (List<Value> values: grainFractions) { if (values == null) { continue; } for (Value value: values) { Load load = value.getLoad(); Integer sqId = load.getSQRelationTimeIntervalId(); if ((sqRelationTimeInterval == null) || sqId != null && sqId.equals(sqRelationTimeInterval)) { loads.add(load); } } } } public double getStation() { return station; } public int getType() { return type; } public boolean isType(int type) { return (this.type & type) != 0; } public void setPrev(Station prev) { this.prev = prev; } public Station getPrev() { return prev; } public void merge(Station other) { this.type |= other.type; for (int i = 0, N = grainFractions.size(); i < N; ++i) { grainFractions.set(i, mergeValues(grainFractions.get(i), other.grainFractions.get(i))); } } private static final Comparator<Value> ID_CMP = new Comparator<Value>() { @Override public int compare(Value a, Value b) { return a.getLoad().getId() - b.getLoad().getId(); } }; private static List<Value> mergeValues(List<Value> a, List<Value> b) { if (a == null) return b; if (b == null) return a; a.addAll(b); // re-establish id order. Collections.sort(a, ID_CMP); return a; } public Station prevByType(int type) { for (Station curr = this; curr != null; curr = curr.getPrev()) { if (curr.isType(type)) { return curr; } } return null; } public void addValue(int grainFraction, Value value) { List<Value> values = grainFractions.get(grainFraction); if (values == null) { values = new ArrayList<Value>(); grainFractions.set(grainFraction, values); } values.add(value); } public boolean hasGrainFraction(String grainFraction) { return hasGrainFraction(grainFractionIndex(grainFraction)); } public boolean hasGrainFraction(int grainFraction) { List<Value> values = grainFractions.get(grainFraction); return values != null && !values.isEmpty(); } public void filterGrainFraction( int grainFraction, Value.Filter filter, Value.Visitor visitor ) { List<Value> values = grainFractions.get(grainFraction); if (values != null && !values.isEmpty()) { for (Value value: values) { if (filter.accept(value)) { visitor.visit(value); } } } } public List<Value> filterGrainFraction(int grainFraction, Value.Filter filter) { final List<Value> result = new ArrayList<Value>(); filterGrainFraction(grainFraction, filter, new Value.Visitor() { @Override public void visit(Value value) { result.add(value); } }); return result; } public double findValueByLoadId(int id) { for (List<Value> values: grainFractions) { double value = findValueByLoadId(values, id); if (!Double.isNaN(value)) { return value; } } return Double.NaN; } private static final double findValueByLoadId( List<Value> values, int id ) { if (values == null) { return Double.NaN; } // List is ordered by station id -> binary search. int lo = 0, hi = values.size()-1; while (lo <= hi) { int mid = (lo + hi)/2; Value v = values.get(mid); int xid = v.getLoad().getId(); if (xid > id) hi = mid-1; else if (xid < id) lo = mid+1; else return v.getValue(); } return Double.NaN; } } // class Station private Station [] stations; public SedimentLoadData() { } public SedimentLoadData(Collection<Station> stations, boolean kmUp) { setStations(stations, kmUp); } public Station[] getStations() { return stations; } public void setStations(Collection<Station> stations, boolean kmUp) { TreeMap<Double, Station> same = new TreeMap<Double, Station>(EpsilonComparator.CMP); for (Station station: stations) { Double key = station.getStation(); Station st = same.get(key); if (st == null) { same.put(key, station); } else { st.merge(station); } } this.stations = new Station[same.size()]; int i = 0; for (Station station: same.values()) { this.stations[i++] = station; } wireNeighbors(kmUp); } private void wireNeighbors(boolean kmUp) { if (kmUp) { for (int i = stations.length - 1; i > 0; --i) { stations[i-1].setPrev(stations[i]); } } else { for (int i = 1; i < stations.length; ++i) { stations[i].setPrev(stations[i-1]); } } } private void recursiveFindStations( double a, double b, int lo, int hi, Visitor visitor ) { while (lo <= hi) { int mid = (lo+hi)/2; Station st = stations[mid]; double station = st.getStation(); if (station < a) { lo = mid+1; } else if (station > b) { hi = mid-1; } else { recursiveFindStations(a, b, lo, mid-1, visitor); visitor.visit(st); lo = mid+1; } } } public static final Comparator<Load> LOAD_ID_CMP = new Comparator<Load>() { @Override public int compare(Load a, Load b) { return a.getId() - b.getId(); } }; public static final Comparator<Load> LOAD_TI_CMP = new Comparator<Load>() { @Override public int compare(Load a, Load b) { Date a_start = a.getStartTime(); Date a_stop = a.getStopTime(); Date b_start = b.getStartTime(); Date b_stop = b.getStopTime(); if (a_start == null && b_start == null) { return 0; } else if (a_start != null) { return a_start.compareTo(b_start); } else if (a_stop != null) { return a_stop.compareTo(b_stop); } else { return 1; } } }; public static final Comparator<Load> LOAD_SQ_TI_CMP = new Comparator<Load>() { @Override public int compare(Load a, Load b) { Integer a_id = a.getSQRelationTimeIntervalId(); Integer b_id = b.getSQRelationTimeIntervalId(); if (a_id == null && b_id == null) { return 0; } if (a_id == null) { return -1; } if (b_id == null) { return 1; } return a_id - b_id; } }; /** Find all loads in the range a/b with the according sq_time_interval */ public Collection<Load> findLoads(double a, double b, final Integer sqRelationTimeInterval) { final TreeSet<Load> loads = new TreeSet<Load>(LOAD_ID_CMP); findStations(a, b, new Visitor() { @Override public void visit(Station station) { station.allLoads(loads, sqRelationTimeInterval); } }); return loads; } /** Find all official loads with a distinct time interval id */ public Collection<Load> findUniqueTiOfficialLoads(double a, double b) { final TreeSet<Load> loads = new TreeSet<Load>(LOAD_TI_CMP); findStations(a, b, new Visitor() { @Override public void visit(Station station) { station.allOfficialLoads(loads); } }); return loads; } /** Get a list of loads with unique sq_time_intervals. * * This is mainly a convenience function for the SedimentLoadInfoService. */ public Collection<Load> findDistinctSQTimeIntervalNonEpochLoads(double a, double b) { final TreeSet<Load> loads = new TreeSet<Load>(LOAD_SQ_TI_CMP); findStations(a, b, new Visitor() { @Override public void visit(Station station) { station.allNonEpochLoads(loads); } }); return loads; } public Collection<Load> findLoads(double a, double b) { final TreeSet<Load> loads = new TreeSet<Load>(LOAD_ID_CMP); findStations(a, b, new Visitor() { @Override public void visit(Station station) { station.allLoads(loads); } }); return loads; } public void findStations(double a, double b, Visitor visitor) { if (a > b) { double t = a; a = b; b = t; } recursiveFindStations(a, b, 0, stations.length-1, visitor); } public List<Station> findStations(double a, double b) { final List<Station> result = new ArrayList<Station>(); findStations(a, b, new Visitor() { @Override public void visit(Station station) { result.add(station); } }); return result; } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :