view artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensity.java @ 7460:2733b4aae74b

flys/issue1625: Changed default sediment density factor from 1.8 to 1.9 . The value can be changed by adusting conf.xml (/artifact-databae/options/sediment-density-factor/text()).
author Sascha L. Teichmann <teichmann@intevation.de>
date Tue, 29 Oct 2013 19:46:41 +0100
parents 2facd4066a28
children 52c364813cb1
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.apache.log4j.Logger;

import org.dive4elements.artifacts.common.utils.Config;


/** Sediment Densities for multiple years. */
public class SedimentDensity implements Serializable
{
    private static final Logger logger = Logger
        .getLogger(SedimentDensity.class);

    public static final double DEFAULT_SEDIMNET_DENSITY_FACTOR = 1.9;

    public static String SEDIMENT_DENSITY_FACTOR_XPATH =
        "/artifact-database/options/sediment-density-factor/text()";

    public static final double SEDIMNET_DENSITY_FACTOR =
        getSedimentDensityFactor();

    private Map<Integer, List<SedimentDensityValue>> densities;
    private List<Integer> years;


    /** Figures out the sediment density factor from global config. */
    private static final double getSedimentDensityFactor() {

        double factor = DEFAULT_SEDIMNET_DENSITY_FACTOR;

        String factorString =
            Config.getStringXPath(SEDIMENT_DENSITY_FACTOR_XPATH);

        if (factorString != null) {
            try {
                factor = Double.parseDouble(factorString.trim());
            }
            catch (NumberFormatException nfe) {
                logger.error(nfe);
            }
        }

        logger.info("Sedmiment density factor: " + factor);

        return factor;
    }

    public SedimentDensity() {
        this.densities = new HashMap<Integer, List<SedimentDensityValue>>();
        this.years = new ArrayList<Integer>();
    }

    public Map<Integer, List<SedimentDensityValue>> getDensities() {
        return densities;
    }

    public void setDensities(Map<Integer, List<SedimentDensityValue>> densities) {
        this.densities = densities;
    }

    public void addDensity(double km, double density, int year) {
        logger.debug("adding " + year);
        if (this.densities.containsKey(year)) {
            List<SedimentDensityValue> list = this.densities.get(year);
            list.add(new SedimentDensityValue(km, density, year));
        }
        else {
            List<SedimentDensityValue> list =
                new ArrayList<SedimentDensityValue>();
            list.add(new SedimentDensityValue(km, density, year));
            densities.put(year, list);
        }
        if (!this.years.contains(new Integer(year))) {
            logger.debug("new year");
            years.add(new Integer(year));
        }
    }

    public List<Integer> getYears() {
        return years;
    }

    public void setYears(List<Integer> years) {
        this.years = years;
    }

    /**
     * Get the density at year.
     * Measured densities are valid until the next measurement.
     * If no measurement was found 1.8 is returned.
     */
    public double getDensity(double km, int year) {
        Collections.sort(this.years);
        if (this.years.size() == 1 && years.get(0) <= year) {
            logger.debug("get density from year " + year + " at km " + km);
            return getDensityAtKm(densities.get(years.get(0)), km);
        }
        if (this.years.size() > 1) {
            for (int i = 0, I = years.size()-1; i < I; i++) {
                int y1 = years.get(i);
                int y2 = years.get(i + 1);
                if (year >= y1 && year < y2) {
                    return getDensityAtKm(densities.get(y1), km);
                }
                else if (year >= y2 && i == years.size() -1) {
                    return getDensityAtKm(densities.get(y2), km);
                }
            }
        }
        return SEDIMNET_DENSITY_FACTOR;
    }

    /** Get (sorted) map of km to density of all years. */
    public double[][] getAllDensities()
    {
        TreeMap<Double, Double> map = new TreeMap<Double,Double>();
        for (int year: years) {
            for (SedimentDensityValue sdv: densities.get(year)) {
                map.put(sdv.getKm(), sdv.getDensity());
            }
        }
        double[][] points = new double[2][map.keySet().size()];
        int i = 0;
        for (Map.Entry<Double, Double> kmDens: map.entrySet()) {
            points[0][i] = kmDens.getKey();
            points[1][i] = kmDens.getValue();
            i++;
        }

        return points;
    }

    /** Get points  km,density (sorted by km), for a given year. */
    public double[][] getDensities(int year)
    {
        TreeMap<Double, Double> map = new TreeMap<Double,Double>();
        for (SedimentDensityValue sdv: densities.get(year)) {
            map.put(sdv.getKm(), sdv.getDensity());
        }
        double[][] points = new double[2][map.keySet().size()];
        int i = 0;
        for (Map.Entry<Double, Double> kmDens: map.entrySet()) {
            points[0][i] = kmDens.getKey();
            points[1][i] = kmDens.getValue();
            i++;
        }

        return points;
    }

    /** Get value at km, interpolated. */
    private double getDensityAtKm(
        List<SedimentDensityValue> values,
        double km
    ) {
        SedimentDensityValue prev = null;
        SedimentDensityValue next = null;
        for (SedimentDensityValue sdv: values) {
            logger.debug("year: " + sdv.getYear());
            if (Math.abs(sdv.getKm() - km) < 0.00001) {
                return prev.getDensity();
            }
            if (sdv.getKm() > km) {
                next = sdv;
                break;
            }
            prev = sdv;
        }
        return spline(prev, next, km);
    }

    /** Linearly interpolate between density values. */
    private static double spline(
        SedimentDensityValue prev,
        SedimentDensityValue next,
        double km
    ) {
        if (prev == null && next == null) {
            logger.warn("prev and next are null -> NaN");
            return Double.NaN;
        }

        if (prev == null) return next.getDensity();
        if (next == null) return prev.getDensity();

        // XXX: This is no spline interpolation!
        double lower = prev.getKm();
        double upper = next.getKm();
        double upperDensity = next.getDensity();
        double lowerDensity = prev.getDensity();

        double m = (upperDensity - lowerDensity)/(upper - lower);
        double b = lowerDensity - (m * lower);
        return m * km + b;
    }


    /** If multiple values for same year and station are found,
     * build and store average, dismiss multiple values. */
    public void cleanUp() {
        Set<Integer> keys = densities.keySet();
        // Walk over years
        for (Integer key : keys) {
            List<SedimentDensityValue> list = densities.get(key);
            if (list.size() == 0) {
                return;
            }
            List<SedimentDensityValue> cleaned =
                new ArrayList<SedimentDensityValue>();
            double prevkm = list.get(0).getKm();
            int counter = 0;
            double sum = 0d;
            for (SedimentDensityValue value : list) {
                // Apparently we can assume that values are ordered by km.
                if (value.getKm() == prevkm) {
                    sum += value.getDensity();
                    counter++;
                }
                else {
                    cleaned.add(new SedimentDensityValue(
                        prevkm,
                        sum / counter,
                        value.getYear()));
                    sum = value.getDensity();
                    counter = 1;
                }
                prevkm = value.getKm();
            }
            this.densities.put(key, cleaned);
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :

http://dive4elements.wald.intevation.org