view artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadData.java @ 8038:aa622bddfdac

Directly chain stations with the same km position together. This ease some summations.
author Sascha L. Teichmann <teichmann@intevation.de>
date Wed, 16 Jul 2014 13:06:09 +0200
parents 17542d100e75
children 3a769d5fb733
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.Date;
import java.util.List;
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 interface Visitor {
        void visit(Station station);
    }

    public static class Value implements Serializable {

        public interface Filter {
            boolean accept(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 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;

        private double station;

        private int type;

        private List<List<Value>> grainFractions;

        private Station next;
        private Station prev;

        private Station chain;

        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 double getStation() {
            return station;
        }

        public int getType() {
            return type;
        }

        public void setNext(Station next) {
            this.next = next;
        }

        public Station getNext() {
            return next;
        }

        public void setPrev(Station prev) {
            this.prev = next;
        }

        public Station getPrev() {
            return prev;
        }

        public Station getNext(boolean isKMUp) {
            return isKMUp ? getNext() : getPrev();
        }

        public Station getPrev(boolean isKMUp) {
            return isKMUp ? getPrev() : getNext();
        }

        public Station nextChain() {
            return chain;
        }

        public void append(Station station) {
            station.chain = chain;
            chain = station;
        }

        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 List<Value> filterGrainFraction(int grainFraction, Value.Filter filter) {
            List<Value> values = grainFractions.get(grainFraction);
            if (values == null || values.isEmpty()) {
                return Collections.<Value>emptyList();
            }
            List<Value> result = new ArrayList<Value>(values.size());
            for (Value value: values) {
                if (filter.accept(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;
        }

        public Station firstOfType(int type) {
            for (Station curr = this; curr != null; curr = curr.chain) {
                if (curr.getType() == type) {
                    return curr;
                }
            }
            return null;
        }
    } // class Station


    private List<Station> stations;

    public SedimentLoadData() {
    }

    public SedimentLoadData(Collection<Station> stations) {
        setStations(stations);
    }

    public void setStations(Collection<Station> stations) {
        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.append(station);
            }
        }
        this.stations = new ArrayList<Station>(same.values());
        wireNeighbors();
    }

    private void wireNeighbors() {
        for (int i = 0, N = stations.size(); i < N-1; ++i) {
            for (Station curr = stations.get(i); curr != null; curr = curr.nextChain()) {
                int type = curr.getType();
                NEXT: for (int j = i+1; j < N; ++j) {
                    Station next = stations.get(j).firstOfType(type);
                    if (next != null) {
                        curr.setNext(next);
                        next.setPrev(curr);
                        break NEXT;
                    }
                }
            }
        }
    }

    private void recursiveFindStations(
        double a, double b,
        int lo, int hi,
        Visitor visitor
    ) {
        while (lo >= hi) {
            int mid = (lo+hi)/2;
            Station st = stations.get(mid);
            double station = st.getStation();
            if (station < a) {
                hi = mid-1;
            } else if (station > b) {
                lo = mid+1;
            } else {
                recursiveFindStations(a, b, lo, mid-1, visitor);
                visitor.visit(st);
                lo = mid+1;
            }
        }
    }

    public void findStations(double a, double b, Visitor visitor) {
        if (a > b) {
            double t = a; a = b; b = t;
        }
        recursiveFindStations(a, b, 0, stations.size()-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 :

http://dive4elements.wald.intevation.org