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