gernotbelger@8951: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@8951: * Software engineering by gernotbelger@8951: * Björnsen Beratende Ingenieure GmbH gernotbelger@8951: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@8951: * gernotbelger@8951: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@8951: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@8951: * documentation coming with Dive4Elements River for details. gernotbelger@8951: */ gernotbelger@8951: package org.dive4elements.river.artifacts.sinfo.flowdepthdev; gernotbelger@8951: gernotbelger@8951: import java.util.ArrayList; gernotbelger@8951: import java.util.Collection; gernotbelger@8964: import java.util.TreeSet; gernotbelger@8951: gernotbelger@8951: import org.apache.commons.lang.math.DoubleRange; gernotbelger@8951: import org.dive4elements.artifacts.CallContext; gernotbelger@8997: import org.dive4elements.river.artifacts.common.GeneralResultType; gernotbelger@8997: import org.dive4elements.river.artifacts.common.ResultRow; gernotbelger@8951: import org.dive4elements.river.artifacts.model.Calculation; gernotbelger@8951: import org.dive4elements.river.artifacts.model.CalculationResult; gernotbelger@8951: import org.dive4elements.river.artifacts.model.WKms; gernotbelger@8951: import org.dive4elements.river.artifacts.resources.Resources; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.SINFOArtifact; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.flowdepth.FlowDepthUtils; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.flowdepth.WstSoundingIdPair; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.tkhcalculation.WaterlevelValuesFinder; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.util.BedHeightInfo; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; gernotbelger@8951: import org.dive4elements.river.artifacts.sinfo.util.WstInfo; gernotbelger@8951: import org.dive4elements.river.artifacts.states.WaterlevelData; gernotbelger@8951: import org.dive4elements.river.artifacts.states.WaterlevelFetcher; gernotbelger@8951: import org.dive4elements.river.model.River; gernotbelger@8951: gernotbelger@8951: /** gernotbelger@8951: * @author Gernot Belger gernotbelger@8951: */ gernotbelger@8951: final class FlowDepthDevelopmentCalculation { gernotbelger@8951: gernotbelger@8951: private final CallContext context; gernotbelger@8951: gernotbelger@8951: public FlowDepthDevelopmentCalculation(final CallContext context) { gernotbelger@8951: this.context = context; gernotbelger@8951: } gernotbelger@8951: gernotbelger@8951: public CalculationResult calculate(final SINFOArtifact sinfo) { gernotbelger@8951: gernotbelger@8951: final String user = CalculationUtils.findArtifactUser(this.context, sinfo); gernotbelger@8951: gernotbelger@8951: /* access input data */ gernotbelger@8951: final FlowDepthDevelopmentAccess access = new FlowDepthDevelopmentAccess(sinfo); gernotbelger@8951: final River river = access.getRiver(); gernotbelger@8951: final RiverInfo riverInfo = new RiverInfo(river); gernotbelger@8951: gernotbelger@8951: final WstSoundingIdPair currentPair = access.getCurrentPair(); gernotbelger@8951: final WstSoundingIdPair histPair = access.getHistoricalPair(); gernotbelger@8951: gernotbelger@8951: final DoubleRange calcRange = access.getRange(); gernotbelger@8951: gernotbelger@8951: /* calculate results for each diff pair */ gernotbelger@8951: final Calculation problems = new Calculation(); gernotbelger@8951: gernotbelger@8951: final RiverInfoProvider infoProvider = RiverInfoProvider.forRange(this.context, river, calcRange); gernotbelger@8951: gernotbelger@8951: final String calcModeLabel = Resources.getMsg(this.context.getMeta(), sinfo.getCalculationMode().name()); gernotbelger@8951: gernotbelger@8951: final FlowDepthDevelopmentCalculationResults results = new FlowDepthDevelopmentCalculationResults(calcModeLabel, user, riverInfo, calcRange); gernotbelger@8951: gernotbelger@8951: final FlowDepthDevelopmentCalculationResult result = calculateResult(calcRange, currentPair, histPair, problems, infoProvider); gernotbelger@8980: results.addResult(result, problems); gernotbelger@8951: gernotbelger@8951: return new CalculationResult(results, problems); gernotbelger@8951: } gernotbelger@8951: gernotbelger@8951: private FlowDepthDevelopmentCalculationResult calculateResult(final DoubleRange calcRange, final WstSoundingIdPair currentPair, gernotbelger@8951: final WstSoundingIdPair histPair, final Calculation problems, final RiverInfoProvider infoProvider) { gernotbelger@8951: gernotbelger@8951: /* access real input data from database */ gernotbelger@8964: final WaterlevelData currentWaterlevel = loadWaterlevel(currentPair, calcRange, problems); gernotbelger@8951: if (currentWaterlevel == null) gernotbelger@8951: return null; gernotbelger@8951: gernotbelger@8964: final WaterlevelData historicalWaterlevel = loadWaterlevel(histPair, calcRange, problems); gernotbelger@8951: if (historicalWaterlevel == null) gernotbelger@8951: return null; gernotbelger@8951: gernotbelger@8951: final BedHeightsFinder currentSounding = loadBedHeight(currentPair, calcRange, problems); gernotbelger@8951: if (currentSounding == null) gernotbelger@8951: return null; gernotbelger@8951: gernotbelger@8951: final BedHeightsFinder historicalSounding = loadBedHeight(histPair, calcRange, problems); gernotbelger@8951: if (historicalSounding == null) gernotbelger@8951: return null; gernotbelger@8951: gernotbelger@8951: final BedHeightInfo currentSoundingInfo = currentSounding.getInfo(); gernotbelger@8951: final BedHeightInfo historicalSoundingInfo = historicalSounding.getInfo(); gernotbelger@8951: gernotbelger@8951: final int currentWstYear = currentWaterlevel.getYear(); gernotbelger@8951: final int historicalWstYear = historicalWaterlevel.getYear(); gernotbelger@8951: final int currentSoundingYear = currentSoundingInfo.getYear(); gernotbelger@8951: final int historicalSoundingYear = historicalSoundingInfo.getYear(); gernotbelger@8951: gernotbelger@8978: if (currentWstYear < 0) { gernotbelger@8978: problems.addProblem("flowdepthdevelopmentcalculation.missingCurrentYear", currentWaterlevel.getName()); gernotbelger@8978: return null; gernotbelger@8978: } gernotbelger@8978: gernotbelger@8978: if (historicalWstYear < 0) { gernotbelger@8978: problems.addProblem("flowdepthdevelopmentcalculation.missingHistoricalYear", historicalWaterlevel.getName()); gernotbelger@8978: return null; gernotbelger@8978: } gernotbelger@8978: gernotbelger@8980: FlowDepthUtils.checkYearDifference(Resources.getMsg(this.context.getMeta(), "flowdepthdevelopmentcalculation.yearDifferenceCurrent"), currentWstYear, gernotbelger@8980: currentSoundingYear, problems); gernotbelger@8980: FlowDepthUtils.checkYearDifference(Resources.getMsg(this.context.getMeta(), "flowdepthdevelopmentcalculation.yearDifferenceHistorical"), gernotbelger@8980: historicalWstYear, gernotbelger@8980: historicalSoundingYear, problems); gernotbelger@8951: gernotbelger@8951: /* re-determine the reference gauge, in the same way as the WaterlevelArtifact would do it */ gernotbelger@8951: final RiverInfoProvider currentRiverInfoProvider = infoProvider.forWaterlevel(currentWaterlevel); gernotbelger@8951: final RiverInfoProvider histRiverInfoProvider = infoProvider.forWaterlevel(historicalWaterlevel); gernotbelger@8951: gernotbelger@8951: final WstInfo currentWstInfo = new WstInfo(currentWaterlevel.getName(), currentWstYear, currentRiverInfoProvider.getReferenceGauge()); gernotbelger@8951: final WstInfo historicalWstInfo = new WstInfo(historicalWaterlevel.getName(), historicalWstYear, histRiverInfoProvider.getReferenceGauge()); gernotbelger@8951: gernotbelger@8951: final WKms currentWkms = currentWaterlevel.getWkms(); gernotbelger@8964: final WaterlevelValuesFinder currentWstProvider = WaterlevelValuesFinder.fromKms(problems, currentWkms); gernotbelger@8951: gernotbelger@8951: final WKms historicalWkms = historicalWaterlevel.getWkms(); gernotbelger@8964: final WaterlevelValuesFinder historicalWstProvider = WaterlevelValuesFinder.fromKms(problems, historicalWkms); gernotbelger@8951: gernotbelger@8951: final int currentMeanYear = (currentWstYear + currentSoundingYear) / 2; gernotbelger@8951: final int historcialMeanYear = (historicalWstYear + historicalSoundingYear) / 2; gernotbelger@8951: gernotbelger@8951: final double diffYear = currentMeanYear - historcialMeanYear; gernotbelger@8951: gernotbelger@8951: /* real calculation loop */ gernotbelger@8997: final Collection< ResultRow> rows = new ArrayList<>(); gernotbelger@8951: gernotbelger@8964: final Collection stations = determineCalculationSteps(currentSounding, historicalSounding); gernotbelger@8964: for (final double station : stations) { gernotbelger@8951: if (calcRange.containsDouble(station)) { gernotbelger@8951: gernotbelger@8951: final double currentWst = currentWstProvider.getWaterlevel(station); gernotbelger@8951: final double currentBedHeight = currentSounding.getMeanBedHeight(station); gernotbelger@8951: gernotbelger@8951: final double historicalWst = historicalWstProvider.getWaterlevel(station); gernotbelger@8951: final double historicalBedHeight = historicalSounding.getMeanBedHeight(station); gernotbelger@8951: gernotbelger@8978: /* ignore invalid lines */ gernotbelger@8978: if (Double.isNaN(currentWst) || Double.isNaN(currentBedHeight) || Double.isNaN(historicalWst) || Double.isNaN(historicalBedHeight)) gernotbelger@8978: continue; gernotbelger@8978: gernotbelger@8951: final double diffWst = (currentWst - historicalWst) * 100; gernotbelger@8951: final double diffBedHeight = (currentBedHeight - historicalBedHeight) * 100; gernotbelger@8951: gernotbelger@8951: final double flowDepthDevelopment = diffWst - diffBedHeight; gernotbelger@8951: gernotbelger@8951: final double flowDepthDevelopmentPerYear = flowDepthDevelopment / diffYear; gernotbelger@8951: gernotbelger@8951: final double currentFlowDepth = currentWst - currentBedHeight; gernotbelger@8951: final double historicalFlowDepth = historicalWst - historicalBedHeight; gernotbelger@8951: gernotbelger@8951: // REMARK: access the location once only during calculation gernotbelger@8951: final String location = currentRiverInfoProvider.getLocation(station); gernotbelger@8951: gernotbelger@8997: final ResultRow row = ResultRow.create().// gernotbelger@8997: putValue(GeneralResultType.station, station). // gernotbelger@8951: putValue(SInfoResultType.flowdepthDevelopment, flowDepthDevelopment). // gernotbelger@8951: putValue(SInfoResultType.flowdepthDevelopmentPerYear, flowDepthDevelopmentPerYear). // gernotbelger@8951: putValue(SInfoResultType.waterlevelDifference, diffWst). // gernotbelger@8951: putValue(SInfoResultType.bedHeightDifference, diffBedHeight). // gernotbelger@8951: putValue(SInfoResultType.flowdepthCurrent, currentFlowDepth). // gernotbelger@8951: putValue(SInfoResultType.flowdepthHistorical, historicalFlowDepth). // gernotbelger@8951: putValue(SInfoResultType.location, location); gernotbelger@8951: rows.add(row); gernotbelger@8951: } gernotbelger@8951: } gernotbelger@8951: gernotbelger@8953: final String label = buildLabel(currentWaterlevel, currentSoundingInfo, historicalWaterlevel, historicalSoundingInfo); gernotbelger@8953: gernotbelger@8953: return new FlowDepthDevelopmentCalculationResult(label, currentWstInfo, historicalWstInfo, currentSoundingInfo, historicalSoundingInfo, gernotbelger@8951: rows); gernotbelger@8951: } gernotbelger@8951: gernotbelger@8964: /** gernotbelger@8964: * Calculation steps are simply the union of all stations of all involved bed-height datasets gernotbelger@8964: */ gernotbelger@8964: private Collection determineCalculationSteps(final BedHeightsFinder currentSounding, final BedHeightsFinder historicalSounding) { gernotbelger@8964: gernotbelger@8964: final Collection allStations = new TreeSet<>(); gernotbelger@8964: gernotbelger@8964: allStations.addAll(currentSounding.getStations()); gernotbelger@8964: allStations.addAll(historicalSounding.getStations()); gernotbelger@8964: gernotbelger@8964: return allStations; gernotbelger@8964: } gernotbelger@8964: gernotbelger@8953: private String buildLabel(final WaterlevelData currentWaterlevel, final BedHeightInfo currentSounding, final WaterlevelData historicalWaterlevel, gernotbelger@8953: final BedHeightInfo historicalSounding) { gernotbelger@8953: gernotbelger@8953: return new StringBuilder(). // gernotbelger@8953: append(currentWaterlevel.getName()). // gernotbelger@8953: append('/'). // gernotbelger@8953: append(historicalWaterlevel.getName()). // gernotbelger@8953: append(" - "). // gernotbelger@8953: append(currentSounding.getDescription()). // gernotbelger@8953: append('/'). // gernotbelger@8953: append(historicalSounding.getDescription()). // gernotbelger@8953: toString(); gernotbelger@8953: } gernotbelger@8953: gernotbelger@8951: /* REMARK: fetch ALL wst kms, because we need to determine the original reference gauge */ gernotbelger@8964: private WaterlevelData loadWaterlevel(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) { gernotbelger@8951: final String wstId = pair.getWstId(); gernotbelger@8964: return new WaterlevelFetcher().findWaterlevel(this.context, wstId, calcRange, problems); gernotbelger@8951: } gernotbelger@8951: gernotbelger@8951: private BedHeightsFinder loadBedHeight(final WstSoundingIdPair pair, final DoubleRange calcRange, final Calculation problems) { gernotbelger@8951: final String soundingId = pair.getSoundingId(); gernotbelger@8951: return BedHeightsFinder.forId(this.context, soundingId, calcRange, problems); gernotbelger@8951: } gernotbelger@8951: }