mschaefer@9432: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde mschaefer@9432: * Software engineering by mschaefer@9432: * Björnsen Beratende Ingenieure GmbH mschaefer@9432: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt mschaefer@9432: * mschaefer@9432: * This file is Free Software under the GNU AGPL (>=v3) mschaefer@9432: * and comes with ABSOLUTELY NO WARRANTY! Check out the mschaefer@9432: * documentation coming with Dive4Elements River for details. mschaefer@9432: */ mschaefer@9432: mschaefer@9432: package org.dive4elements.river.artifacts.bundu.bezugswst; mschaefer@9432: mschaefer@9432: import java.util.Map.Entry; mschaefer@9432: import java.util.NavigableMap; mschaefer@9432: import java.util.TreeMap; mschaefer@9432: mschaefer@9432: import org.dive4elements.river.artifacts.math.Linear; mschaefer@9432: import org.dive4elements.river.artifacts.model.Calculation; mschaefer@9432: import org.dive4elements.river.model.River; mschaefer@9432: import org.dive4elements.river.model.sinfo.Channel; mschaefer@9432: import org.dive4elements.river.model.sinfo.ChannelValue; mschaefer@9432: mschaefer@9432: /** mschaefer@9432: * Provides channel depth and height of a river mschaefer@9432: * mschaefer@9432: * @author Matthias Schäfer mschaefer@9432: */ mschaefer@9432: public final class ChannelFinder { mschaefer@9432: mschaefer@9432: /***** TYPES *****/ mschaefer@9432: mschaefer@9432: public enum ChannelValueType { mschaefer@9432: depth { mschaefer@9432: @Override mschaefer@9432: public Double getValue(final ChannelValue channelValue) { mschaefer@9432: return channelValue.getDepth(); mschaefer@9432: } mschaefer@9432: }, mschaefer@9432: width { mschaefer@9432: @Override mschaefer@9432: public Double getValue(final ChannelValue channelValue) { mschaefer@9432: return channelValue.getWidth(); mschaefer@9432: } mschaefer@9432: }; mschaefer@9432: mschaefer@9432: public abstract Double getValue(final ChannelValue channelValue); mschaefer@9432: } mschaefer@9432: mschaefer@9432: /***** FIELDS *****/ mschaefer@9432: mschaefer@9432: // private static Logger log = Logger.getLogger(ChannelFinder.class); mschaefer@9432: mschaefer@9432: private static double MAX_DISTANCE_KM = 1; mschaefer@9432: mschaefer@9432: private final NavigableMap values = new TreeMap<>(); mschaefer@9432: mschaefer@9432: private Calculation problems; mschaefer@9432: mschaefer@9432: mschaefer@9432: /***** CONSTRUCTORS *****/ mschaefer@9432: mschaefer@9432: private ChannelFinder(final Calculation problems, final Channel channel) { mschaefer@9432: mschaefer@9432: this.problems = problems; mschaefer@9432: mschaefer@9432: for (final ChannelValue v : channel.getValues()) { mschaefer@9432: this.values.put(v.getStation(), v); mschaefer@9432: } mschaefer@9432: } mschaefer@9432: mschaefer@9432: /***** METHODS *****/ mschaefer@9432: mschaefer@9432: /** mschaefer@9432: * Loads the channel values for a river and year mschaefer@9432: * mschaefer@9432: * @return Whether the load has been successful mschaefer@9432: */ mschaefer@9432: public static ChannelFinder loadValues(final Calculation problems, final River river, final int year) { mschaefer@9432: final Channel channel = Channel.getSeries(river, year); mschaefer@9432: if (channel != null) mschaefer@9432: return new ChannelFinder(problems, channel); mschaefer@9432: mschaefer@9636: problems.addProblem("bundu.channelfinder.empty"); mschaefer@9432: return null; mschaefer@9432: } mschaefer@9432: mschaefer@9432: mschaefer@9432: /***** METHODS *****/ mschaefer@9432: mschaefer@9432: /** mschaefer@9432: * Searches the channel depth of a station mschaefer@9432: */ mschaefer@9432: public double getDepth(final double station) { mschaefer@9432: final double value = interpolateChannel(station, ChannelValueType.depth); mschaefer@9432: if (Double.isNaN(value)) mschaefer@9432: reportProblem(station); mschaefer@9432: return value; mschaefer@9432: } mschaefer@9432: mschaefer@9432: /** mschaefer@9432: * Searches the channel width of a station mschaefer@9432: */ mschaefer@9432: public double getWidth(final double station) { mschaefer@9432: final double value = interpolateChannel(station, ChannelValueType.width); mschaefer@9432: if (Double.isNaN(value)) mschaefer@9432: reportProblem(station); mschaefer@9432: return value; mschaefer@9432: } mschaefer@9432: mschaefer@9432: /** mschaefer@9432: * Searches and interpolates a channel value for a km mschaefer@9432: */ mschaefer@9432: private double interpolateChannel(final double km, final ChannelValueType type) { mschaefer@9432: mschaefer@9432: if (this.values.containsKey(km)) { mschaefer@9432: final Double value = type.getValue(this.values.get(km)); mschaefer@9432: return (value == null) ? Double.NaN : value.doubleValue(); mschaefer@9432: } mschaefer@9432: mschaefer@9432: final Entry floorEntry = this.values.floorEntry(km); mschaefer@9432: final Entry ceilingEntry = this.values.ceilingEntry(km); mschaefer@9432: mschaefer@9432: if ((floorEntry == null) || (ceilingEntry == null)) mschaefer@9432: return Double.NaN; mschaefer@9432: mschaefer@9432: final double floorKm = floorEntry.getKey().doubleValue(); mschaefer@9432: final double ceilKm = ceilingEntry.getKey().doubleValue(); mschaefer@9432: mschaefer@9432: /* report once if the interpolation distance exceeds 1000m */ mschaefer@9432: if ((Math.abs(floorKm - ceilKm) > MAX_DISTANCE_KM) && (this.problems != null)) { mschaefer@9432: this.problems.addProblem(km, "linearInterpolator.maxdistance", MAX_DISTANCE_KM * 1000); mschaefer@9432: this.problems = null; mschaefer@9432: return Double.NaN; mschaefer@9432: } mschaefer@9432: mschaefer@9432: final Double floorHeight = type.getValue(floorEntry.getValue()); mschaefer@9432: final Double ceilingHeight = type.getValue(ceilingEntry.getValue()); mschaefer@9432: mschaefer@9432: if (floorHeight == null || ceilingHeight == null) mschaefer@9432: return Double.NaN; mschaefer@9432: mschaefer@9432: return Linear.linear(km, floorKm, ceilKm, floorHeight, ceilingHeight); mschaefer@9432: } mschaefer@9432: mschaefer@9432: private void reportProblem(final double km) { mschaefer@9432: mschaefer@9432: if (this.problems == null) mschaefer@9432: return; mschaefer@9432: mschaefer@9432: this.problems.addProblem(km, "channelfinder.missing"); mschaefer@9432: mschaefer@9432: // report problem only once mschaefer@9432: this.problems = null; mschaefer@9432: } mschaefer@9432: }