view artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadData.java @ 8098:09725b65955a

Add new and simplyfied SedimentLoadFacet The SedimentLoadFacet is intended to work with the Measurement stations. It uses the same mechanismn to access the Mesurement station values as the calculation does. SedimentLoadLS values need a different facet that will come soon.
author Andre Heinecke <andre.heinecke@intevation.de>
date Fri, 15 Aug 2014 18:27:19 +0200
parents 7d4be7a6a2b1
children 786bb4f87e8d
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 org.dive4elements.river.utils.EpsilonComparator;

public class SedimentLoadData implements Serializable
{
    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_SUSPENDED_LOAD =  8;
    public static final int GF_MAX            =  8;

    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_SUSPENDED_LOAD */ Station.BED_LOAD
    };

    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) {
        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;
        if ("total".equals(name))              return GF_TOTAL;
        if ("bed_load".equals(name))           return GF_BED_LOAD;
        if ("suspended_load".equals(name))     return GF_SUSPENDED_LOAD;
        return GF_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 String description;

        private Date startTime;
        private Date stopTime;

        public Load() {
        }

        public Load(
            int    id, 
            int    kind,
            String description,
            Date   startTime,
            Date   stopTime
        ) {
            this.id          = id;
            this.kind        = kind;
            this.description = description;
            this.startTime   = startTime;
            this.stopTime    = stopTime;
        }

        public int getId() {
            return id;
        }

        public int getKind() {
            return kind;
        }

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

        public int getType() {
            return type;
        }

        public boolean isType(int type) {
            return (this.type & type) != 0;
        }

        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 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 nextByType(int type, boolean isKMUp) {
            for (Station curr = this; curr != null; curr = curr.getNext(isKMUp)) {
                if (curr.isType(type)) {
                    return curr;
                }
            }
            return null;
        }

        public Station prevByType(int type, boolean isKMUp) {
            for (Station curr = this; curr != null; curr = curr.getPrev(isKMUp)) {
                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) {
        setStations(stations);
    }

    public Station[] getStations() {
        return 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.merge(station);
            }
        }
        this.stations = new Station[same.size()];
        int i = 0;
        for (Station station: same.values()) {
            this.stations[i++] = station;
        }
        wireNeighbors();
    }

    private void wireNeighbors() {
        for (int i = 1; i < stations.length; ++i) {
            stations[i-1].setNext(stations[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) {
                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.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 :

http://dive4elements.wald.intevation.org