view artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java @ 6152:0587819960c3

Waterlevel differences & bed height differences: Add new model LinearInterpolated intented to unify the two very similiar calculations. The focus of the current implementation is correctness and not speed! The fact that the data sets more mostly sorted by station is not exploited. Doing so would improve performance significantly.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 02 Jun 2013 17:52:53 +0200
parents af13ceeba52a
children fbe2d4c2b2dd
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 org.dive4elements.artifactdatabase.state.Facet;

import org.dive4elements.artifacts.Artifact;
import org.dive4elements.artifacts.CallContext;

import org.dive4elements.river.artifacts.D4EArtifact;

import org.dive4elements.river.artifacts.model.CalculationResult;
import org.dive4elements.river.artifacts.model.DataFacet;
import org.dive4elements.river.artifacts.model.FacetTypes;

import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;

import org.dive4elements.river.model.MeasurementStation;

import org.dive4elements.river.utils.RiverUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.log4j.Logger;


/** Facet to access various sediment loads. */
public class SedimentLoadFacet
extends DataFacet
{
    /** Very own logger. */
    private static Logger logger = Logger.getLogger(SedimentLoadFacet.class);

    /** Used as tolerance value when fetching measurement stations. */
    private static double EPSILON = 1e-5;


    public SedimentLoadFacet() {
    }

    public SedimentLoadFacet(int idx, String name, String description,
        ComputeType type, String stateId, String hash) {
        super(idx, name, description, type, hash, stateId);
    }

    public Object getData(Artifact artifact, CallContext context) {
        logger.debug("Get data for sediment load at index: " + index);

        D4EArtifact flys = (D4EArtifact) artifact;

        CalculationResult res = (CalculationResult) flys.compute(context, hash,
            stateId, type, false);

        Object[] data =
            (SedimentLoadResult[]) res.getData(); // TODO CAST TO SPECIFIC CLASS

        List<MeasurementStation> allStations = RiverUtils.getRiver(flys).getMeasurementStations();
        SedimentLoadResult result = data != null && data.length > index ? (SedimentLoadResult)data[index] : null;
        if (result == null) {
            return null;
        }

        List<Double> sortedStarts = new ArrayList<Double>();
        // Filter stations according to type.
        List<MeasurementStation> stations = new ArrayList<MeasurementStation>();
        for (MeasurementStation station: allStations) {
            if (station.getRange() == null || station.getMeasurementType() == null) {
                continue;
            }
            if (FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
                && station.getMeasurementType().equals("Geschiebe")) {
                stations.add(station);
                sortedStarts.add(station.getStation());
            }
            else if (!FacetTypes.IS.SEDIMENT_LOAD_NO_FLOAT(this.getName())
                && station.getMeasurementType().equals("Schwebstoff")) {
                stations.add(station);
                sortedStarts.add(station.getStation());
            }
        }
        Collections.sort(sortedStarts);

        // Access data according to type.
        double[][] sd = getLoadData(result);

        // Sort by km.
        TreeMap<Double, Double> sortData = new TreeMap<Double,Double>();

        double[] km   = sd[0];
        double[] load = sd[1];

        for (int i = 0 ; i < km.length; i++) {
            sortData.put(km[i], load[i]);
        }

        double[][] values = new double[2][];
        values[0] = new double[km.length*3];
        values[1] = new double[km.length*3];

        List<double[]> kmWithoutStation = new ArrayList<double[]>();

        // Find station via its station (km).
        // TODO use a binarySearch instead of linear absdiff approach
        int i = 0;
        for (Map.Entry<Double, Double> entry: sortData.entrySet()) {
            boolean matchFound = false;
            // For now, ignore overlaps like (B> next A)
            for (MeasurementStation station: stations) {
                if (Math.abs(station.getStation() - entry.getKey()) < EPSILON ||
                    station.getRange().containsTolerant(entry.getKey())) {
                    // TODO: In rare cases, two matches can be found.
                    values[0][i*3] = station.getRange().getA().doubleValue() + EPSILON;
                    values[1][i*3] = entry.getValue();
                    values[0][i*3+1] = station.getRange().getB().doubleValue() - EPSILON;
                    values[1][i*3+1] = entry.getValue();
                    values[0][i*3+2] = station.getRange().getB().doubleValue();
                    values[1][i*3+2] = entry.getValue();
                    matchFound = true;
                }
            }
            // Store points without match for later assessment.
            if (!matchFound) {
                logger.warn("measurement without station ("+entry.getKey()+")!");
            }
            i++;
        }

        for (int x = 0; x < values[0].length-1; x++) {
            // Introduce gaps where no data in measurement station.
            if (Math.abs(values[0][x+1] - values[0][x]) > 3*EPSILON
                    && values[1][x+1] != values[1][x]) {
                values[0][x] = Double.NaN;
                values[1][x] = Double.NaN;
            }
        }

        return values;
    }


    /** Get data according to type of facet. */
    private double[][] getLoadData(SedimentLoadResult result) {
        if (getName().equals(FacetTypes.SEDIMENT_LOAD_SAND))
            return result.getSandData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_COARSE))
            return result.getCoarseData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_FINEMIDDLE))
            return result.getFineMiddleData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND))
            return result.getSuspSandData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SAND_BED))
            return result.getSuspSandBedData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_SUSP_SEDIMENT))
            return result.getSuspSedimentData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL_LOAD))
            return result.getTotalLoadData();
        else if (getName().equals(FacetTypes.SEDIMENT_LOAD_TOTAL))
            return result.getTotalData();
        else {
            logger.error("SedimentLoadFacet " + getName() + " cannot determine data type.");
            return null;
        }
    }

    /** Copy deeply. */
    @Override
    public Facet deepCopy() {
        SedimentLoadFacet copy = new SedimentLoadFacet();
        copy.set(this);
        copy.type = type;
        copy.hash = hash;
        copy.stateId = stateId;
        return copy;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org