gernotbelger@8915: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@8915: * Software engineering by gernotbelger@8915: * Björnsen Beratende Ingenieure GmbH gernotbelger@8915: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@8915: * gernotbelger@8915: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@8915: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@8915: * documentation coming with Dive4Elements River for details. gernotbelger@8915: */ gernotbelger@8915: package org.dive4elements.river.artifacts.sinfo.tkhstate; gernotbelger@8915: gernotbelger@8915: import java.util.ArrayList; gernotbelger@8915: import java.util.Collection; gernotbelger@8915: import java.util.List; gernotbelger@8915: import java.util.Map.Entry; gernotbelger@8915: import java.util.NavigableMap; gernotbelger@8915: import java.util.TreeMap; gernotbelger@8915: gernotbelger@8915: import org.apache.commons.lang.math.DoubleRange; gernotbelger@8946: import org.dive4elements.artifacts.CallContext; gernotbelger@8946: import org.dive4elements.river.artifacts.BedHeightsArtifact; gernotbelger@8915: import org.dive4elements.river.artifacts.math.Linear; gernotbelger@8946: import org.dive4elements.river.artifacts.model.Calculation; gernotbelger@8915: import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; gernotbelger@8915: import org.dive4elements.river.model.BedHeight; gernotbelger@8915: import org.dive4elements.river.model.BedHeightValue; gernotbelger@8946: import org.dive4elements.river.utils.RiverUtils; gernotbelger@8915: gernotbelger@8915: /** gernotbelger@8915: * Provides bed heigts for vcarious calculations. gernotbelger@8915: * gernotbelger@8915: * @author Gernot Belger gernotbelger@8915: */ gernotbelger@8915: public final class BedHeightsFinder { gernotbelger@8915: gernotbelger@8915: private final BedHeightInfo info; gernotbelger@8915: gernotbelger@8915: private final NavigableMap values; gernotbelger@8915: gernotbelger@8915: /** gernotbelger@8942: * Create bed height finders from a collection of bed heights. gernotbelger@8915: */ gernotbelger@8942: public static Collection createTkhBedHeights(final DoubleRange range, final Collection bedHeights) { gernotbelger@8915: final List result = new ArrayList<>(bedHeights.size()); gernotbelger@8915: gernotbelger@8915: for (final BedHeight bedHeight : bedHeights) { gernotbelger@8942: final BedHeightsFinder finder = createBedHeights(bedHeight, range); gernotbelger@8942: result.add(finder); gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: return result; gernotbelger@8915: } gernotbelger@8915: gernotbelger@8946: public static BedHeightsFinder forId(final CallContext context, final String soundingId, final DoubleRange calcRange, final Calculation problems) { gernotbelger@8946: gernotbelger@8946: // REMARK: absolutely unbelievable.... gernotbelger@8946: // The way how bed-heights (and other data too) is accessed is different for nearly every calculation-type gernotbelger@8946: // throughout flys. gernotbelger@8946: // The knowledge on how to parse the datacage-ids is spread through the complete code-base... gernotbelger@8946: gernotbelger@8946: // We use here the way on how bed-heights are accessed by the BedDifferenceAccess/BedDifferenceCalculation, but gernotbelger@8946: // this is plain random gernotbelger@8946: final String[] parts = soundingId.split(";"); gernotbelger@8946: gernotbelger@8946: final BedHeightsArtifact artifact = (BedHeightsArtifact) RiverUtils.getArtifact(parts[0], context); gernotbelger@8946: gernotbelger@8946: final Integer bedheightId = artifact.getDataAsInteger("height_id"); gernotbelger@8946: gernotbelger@8946: // REMARK: this only works with type 'single'; unclear on how to distinguish from epoch data (or whatever the gernotbelger@8946: // other type means) gernotbelger@8946: // Luckily, the requirement is to only access 'single' data here. gernotbelger@8946: // final String bedheightType = artifact.getDataAsString("type"); gernotbelger@8946: gernotbelger@8946: // REMARK: BedDifferences uses this, but we also need the metadata of the BedHeight gernotbelger@8946: // REMARK: second absolutely awful thing: BedHeight is a hibernate binding class, accessing the database via gernotbelger@8946: // hibernate stuff gernotbelger@8946: // BedHeightFactory uses its own (direct) way of accessing the data, with its own implemented data classes. gernotbelger@8946: // return BedHeightFactory.getHeight(bedheightType, bedheightId, from, to); gernotbelger@8946: gernotbelger@8946: final BedHeightsFinder bedHeight = bedheightId == null ? null : BedHeightsFinder.forId(bedheightId, calcRange); gernotbelger@8946: if (bedHeight != null) gernotbelger@8946: return bedHeight; gernotbelger@8946: gernotbelger@8946: // FIXME: 10n gernotbelger@8946: problems.addProblem("Failed to access sounding with id '{0}'", soundingId); gernotbelger@8946: return null; gernotbelger@8946: } gernotbelger@8946: gernotbelger@8915: /** gernotbelger@8915: * Creates a {@link BedHeightsFinder} for a dataset from the database, specified by its id. gernotbelger@8915: * gernotbelger@8915: * @return null if no bed height with the given id exists. gernotbelger@8915: */ gernotbelger@8946: private static BedHeightsFinder forId(final int id, final DoubleRange range) { gernotbelger@8915: gernotbelger@8915: final BedHeight bedHeight = BedHeight.getBedHeightById(id); gernotbelger@8915: if (bedHeight == null) gernotbelger@8915: return null; gernotbelger@8915: gernotbelger@8915: return BedHeightsFinder.createBedHeights(bedHeight, range); gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: /** gernotbelger@8915: * Create a finder for a given bed height. gernotbelger@8915: * gernotbelger@8915: * @param range gernotbelger@8915: */ gernotbelger@8915: private static BedHeightsFinder createBedHeights(final BedHeight bedHeight, final DoubleRange range) { gernotbelger@8915: gernotbelger@8915: // FIXME: sort by station, but in what direction? gernotbelger@8915: // FIXME: using river.getKmUp()? gernotbelger@8915: final NavigableMap values = new TreeMap<>(); gernotbelger@8915: gernotbelger@8915: for (final BedHeightValue bedHeightValue : bedHeight.getValues()) { gernotbelger@8915: final Double station = bedHeightValue.getStation(); gernotbelger@8915: if (station != null && range.containsDouble(station)) { gernotbelger@8915: gernotbelger@8915: if (bedHeightValue.getHeight() != null) gernotbelger@8915: values.put(station, bedHeightValue); gernotbelger@8915: } gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: final BedHeightInfo info = BedHeightInfo.from(bedHeight); gernotbelger@8915: gernotbelger@8915: return new BedHeightsFinder(info, values); gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: private BedHeightsFinder(final BedHeightInfo info, final NavigableMap values) { gernotbelger@8915: this.info = info; gernotbelger@8915: this.values = values; gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: public BedHeightInfo getInfo() { gernotbelger@8915: return this.info; gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: public Collection getStations() { gernotbelger@8915: return this.values.keySet(); gernotbelger@8915: } gernotbelger@8915: gernotbelger@8915: public double getMeanBedHeight(final double km) { gernotbelger@8915: gernotbelger@8915: if (this.values.containsKey(km)) gernotbelger@8915: return this.values.get(km).getHeight(); gernotbelger@8915: gernotbelger@8915: final Entry floorEntry = this.values.floorEntry(km); gernotbelger@8915: final Entry ceilingEntry = this.values.ceilingEntry(km); gernotbelger@8915: gernotbelger@8915: if (floorEntry == null || ceilingEntry == null) gernotbelger@8915: return Double.NaN; gernotbelger@8915: gernotbelger@8915: final double floorKm = floorEntry.getKey(); gernotbelger@8915: final double floorHeight = floorEntry.getValue().getHeight(); gernotbelger@8915: final double ceilKm = ceilingEntry.getKey(); gernotbelger@8915: final double ceilHeight = ceilingEntry.getValue().getHeight(); gernotbelger@8915: gernotbelger@8915: // FIXME: check if we always want that... gernotbelger@8915: gernotbelger@8915: return Linear.linear(km, floorKm, ceilKm, floorHeight, ceilHeight); gernotbelger@8915: } gernotbelger@8915: }