gernotbelger@8946: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@8946: * Software engineering by gernotbelger@8946: * Björnsen Beratende Ingenieure GmbH gernotbelger@8946: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@8946: * gernotbelger@8946: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@8946: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@8946: * documentation coming with Dive4Elements River for details. gernotbelger@8946: */ gernotbelger@8946: package org.dive4elements.river.artifacts.sinfo.flowdepthminmax; gernotbelger@8946: gernotbelger@8946: import java.util.ArrayList; gernotbelger@8946: import java.util.Collection; gernotbelger@8946: gernotbelger@8946: import org.apache.commons.lang.math.DoubleRange; gernotbelger@8946: import org.dive4elements.artifacts.CallContext; gernotbelger@8946: import org.dive4elements.river.artifacts.model.Calculation; gernotbelger@8946: import org.dive4elements.river.artifacts.model.CalculationResult; gernotbelger@8946: import org.dive4elements.river.artifacts.model.WKms; gernotbelger@8946: import org.dive4elements.river.artifacts.resources.Resources; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthUtils; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.flowdepthminmax.FlowDepthMinMaxAccess.MinMaxIdPair; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.tkhcalculation.DischargeValuesFinder; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.tkhcalculation.WaterlevelValuesFinder; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; gernotbelger@8946: import org.dive4elements.river.artifacts.sinfo.util.WstInfo; gernotbelger@8946: import org.dive4elements.river.artifacts.states.WaterlevelData; gernotbelger@8946: import org.dive4elements.river.artifacts.states.WaterlevelFetcher; gernotbelger@8946: import org.dive4elements.river.model.River; gernotbelger@8946: gernotbelger@8946: /** gernotbelger@8946: * @author Gernot Belger gernotbelger@8946: */ gernotbelger@8946: final class FlowDepthMinMaxCalculation { gernotbelger@8946: gernotbelger@8946: private final CallContext context; gernotbelger@8946: gernotbelger@8946: public FlowDepthMinMaxCalculation(final CallContext context) { gernotbelger@8946: this.context = context; gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: public CalculationResult calculate(final SINFOArtifact sinfo) { gernotbelger@8946: gernotbelger@8946: final String user = CalculationUtils.findArtifactUser(this.context, sinfo); gernotbelger@8946: gernotbelger@8946: /* access input data */ gernotbelger@8946: final FlowDepthMinMaxAccess access = new FlowDepthMinMaxAccess(sinfo); gernotbelger@8946: final River river = access.getRiver(); gernotbelger@8946: final RiverInfo riverInfo = new RiverInfo(river); gernotbelger@8946: gernotbelger@8946: final Collection minMaxPairs = access.getMinMaxPairs(); gernotbelger@8946: gernotbelger@8946: final DoubleRange calcRange = access.getRange(); gernotbelger@8946: gernotbelger@8946: /* calculate results for each diff pair */ gernotbelger@8946: final Calculation problems = new Calculation(); gernotbelger@8946: gernotbelger@8946: final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange); gernotbelger@8946: gernotbelger@8946: final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); gernotbelger@8946: gernotbelger@8946: final FlowDepthMinMaxCalculationResults results = new FlowDepthMinMaxCalculationResults(calcModeLabel, user, riverInfo, calcRange); gernotbelger@8946: gernotbelger@8946: for (final MinMaxIdPair minMaxPair : minMaxPairs) { gernotbelger@8946: final FlowDepthMinMaxCalculationResult result = calculateResult(calcRange, minMaxPair, problems, infoProvider); gernotbelger@8946: if (result != null) gernotbelger@8946: results.addResult(result); gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: return new CalculationResult(results, problems); gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: /** gernotbelger@8946: * Calculates one W-MSH differences pair. gernotbelger@8946: * gernotbelger@8946: * @param infoProvider gernotbelger@8946: */ gernotbelger@8946: private FlowDepthMinMaxCalculationResult calculateResult(final DoubleRange calcRange, final MinMaxIdPair minMaxPair, final Calculation problems, gernotbelger@8946: final RiverInfoProvider infoProvider) { gernotbelger@8946: gernotbelger@8946: /* access real input data from database */ gernotbelger@8946: final String wstId = minMaxPair.getWstId(); gernotbelger@8946: final String minSoundingId = minMaxPair.getMinSoundingId(); gernotbelger@8946: final String maxSoundingId = minMaxPair.getMinSoundingId(); gernotbelger@8946: gernotbelger@8946: final BedHeightsFinder minBedHeight = minSoundingId == null ? null : BedHeightsFinder.forId(this.context, minSoundingId, calcRange, problems); gernotbelger@8946: final BedHeightsFinder maxBedHeight = maxSoundingId == null ? null : BedHeightsFinder.forId(this.context, maxSoundingId, calcRange, problems); gernotbelger@8946: if (minBedHeight == null && maxBedHeight == null) gernotbelger@8946: return null; gernotbelger@8946: gernotbelger@8946: /* REMARK: fetch ALL wst kms, because we want to determine the original reference gauge */ gernotbelger@8946: final WaterlevelData waterlevel = new WaterlevelFetcher().findWaterlevel(this.context, wstId, Double.NaN, Double.NaN, problems); gernotbelger@8946: if (waterlevel == null) gernotbelger@8946: return null; gernotbelger@8946: gernotbelger@8946: final String label = createLabel(waterlevel, minBedHeight, maxBedHeight); gernotbelger@8946: gernotbelger@8946: final WKms wstKms = waterlevel.getWkms(); gernotbelger@8946: gernotbelger@8946: final int soundingYear = checkSoundingYear(minBedHeight, maxBedHeight, problems); gernotbelger@8946: FlowDepthUtils.checkYearDifference(label, waterlevel, soundingYear, problems); gernotbelger@8946: // FIXME gernotbelger@8946: // checkWaterlevelDiscretisation(wstKms, calcRange, problems); gernotbelger@8946: // TODO: prüfen, ob sohlhöhen die calcRange abdecken/überschneiden gernotbelger@8946: gernotbelger@8946: /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ gernotbelger@8946: final RiverInfoProvider riverInfoProvider = infoProvider.forWaterlevel(waterlevel); gernotbelger@8946: gernotbelger@8946: final int wspYear = waterlevel.getYear(); gernotbelger@8946: final WstInfo wstInfo = new WstInfo(waterlevel.getName(), wspYear, riverInfoProvider.getReferenceGauge()); gernotbelger@8946: gernotbelger@8946: final WaterlevelValuesFinder waterlevelProvider = WaterlevelValuesFinder.fromKms(wstKms); gernotbelger@8946: final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms); gernotbelger@8946: gernotbelger@8946: final String waterlevelLabel = waterlevel.getName(); gernotbelger@8946: final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight); gernotbelger@8946: gernotbelger@8946: /* real calculation loop */ gernotbelger@8946: final Collection rows = new ArrayList<>(); gernotbelger@8946: gernotbelger@8946: // FIXME: determine what is the spatial discretisation that we will use... gernotbelger@8946: final double[] allKms = wstKms.allKms().toNativeArray(); gernotbelger@8946: for (final double station : allKms) { gernotbelger@8946: if (calcRange.containsDouble(station)) { gernotbelger@8946: gernotbelger@8946: final double wst = waterlevelProvider.getWaterlevel(station); gernotbelger@8946: final double discharge = dischargeProvider.getDischarge(station); gernotbelger@8946: gernotbelger@8946: final double minBedHeightValue = minBedHeight == null ? Double.NaN : minBedHeight.getMeanBedHeight(station); gernotbelger@8946: final double maxBedHeightValue = maxBedHeight == null ? Double.NaN : maxBedHeight.getMeanBedHeight(station); gernotbelger@8946: gernotbelger@8946: final double minFlowDepth = wst - minBedHeightValue; gernotbelger@8946: final double maxFlowDepth = wst - maxBedHeightValue; gernotbelger@8946: gernotbelger@8946: // FIXME: unclear what is meant here... gernotbelger@8946: final double meanBedHeight = Double.NaN; gernotbelger@8946: gernotbelger@8946: // REMARK: access the location once only during calculation gernotbelger@8946: final String location = riverInfoProvider.getLocation(station); gernotbelger@8946: gernotbelger@8946: // REMARK: access the gauge once only during calculation gernotbelger@8946: final String gaugeLabel = riverInfoProvider.findGauge(station); gernotbelger@8946: gernotbelger@8946: rows.add(new FlowDepthMinMaxRow(station, minFlowDepth, maxFlowDepth, wst, discharge, waterlevelLabel, gaugeLabel, meanBedHeight, soundingLabel, gernotbelger@8946: location)); gernotbelger@8946: } gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: final BedHeightInfo minBedHeightInfo = minBedHeight == null ? null : minBedHeight.getInfo(); gernotbelger@8946: final BedHeightInfo maxBedHeightInfo = maxBedHeight == null ? null : maxBedHeight.getInfo(); gernotbelger@8946: return new FlowDepthMinMaxCalculationResult(label, wstInfo, minBedHeightInfo, maxBedHeightInfo, rows); gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: private String buildSoundingLabel(final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight) { gernotbelger@8946: gernotbelger@8946: if (minBedHeight == null) gernotbelger@8946: return maxBedHeight.getInfo().getDescription(); gernotbelger@8946: gernotbelger@8946: if (maxBedHeight == null) gernotbelger@8946: return minBedHeight.getInfo().getDescription(); gernotbelger@8946: gernotbelger@8946: return String.format("%s / %s", minBedHeight.getInfo().getDescription(), maxBedHeight.getInfo().getDescription()); gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: private String createLabel(final WaterlevelData waterlevel, final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight) { gernotbelger@8946: gernotbelger@8946: final StringBuilder buffer = new StringBuilder(waterlevel.getName()); gernotbelger@8946: gernotbelger@8946: if (minBedHeight != null) gernotbelger@8946: buffer.append(" - "). // gernotbelger@8946: append(minBedHeight.getInfo().getDescription()); gernotbelger@8946: gernotbelger@8946: if (maxBedHeight != null) gernotbelger@8946: buffer.append(" - "). // gernotbelger@8946: append(maxBedHeight.getInfo().getDescription()); gernotbelger@8946: gernotbelger@8946: return buffer.toString(); gernotbelger@8946: } gernotbelger@8946: gernotbelger@8946: private int checkSoundingYear(final BedHeightsFinder minBedHeight, final BedHeightsFinder maxBedHeight, final Calculation problems) { gernotbelger@8946: gernotbelger@8946: if (maxBedHeight == null) gernotbelger@8946: return minBedHeight.getInfo().getYear(); gernotbelger@8946: gernotbelger@8946: if (minBedHeight == null) gernotbelger@8946: return maxBedHeight.getInfo().getYear(); gernotbelger@8946: gernotbelger@8946: final int minYear = minBedHeight.getInfo().getYear(); gernotbelger@8946: final int maxYear = minBedHeight.getInfo().getYear(); gernotbelger@8946: gernotbelger@8946: if (minYear != maxYear) gernotbelger@8946: problems.addProblem("sinfo.flowdepthminmaxcalculation.soundingyear.different"); gernotbelger@8946: gernotbelger@8946: return minYear; gernotbelger@8946: } gernotbelger@8946: }