Mercurial > dive4elements > river
view artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/flowdepthminmax/FlowDepthMinMaxCalculation.java @ 8980:b194fa64506a
SINFO - show results themes according to spec, either raw data or floating mean values.
Some improvements to error handling and handling of empty results.
author | gernotbelger |
---|---|
date | Thu, 05 Apr 2018 18:30:34 +0200 |
parents | b5600453bb8f |
children | 0adc6d04de95 |
line wrap: on
line source
/** 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.common.SInfoResultRow; import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; 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); results.addResult(result, problems); } 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(); // FIXME: bfg überzeugen dass man immer nur pärchen auswählen kann --> min/max id ist gleich! final String minSoundingId = minMaxPair.getMinSoundingId(); final String maxSoundingId = minMaxPair.getMaxSoundingId(); 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, calcRange, 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.getYear(), soundingYear, problems); /* 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(problems, wstKms); final DischargeValuesFinder dischargeProvider = DischargeValuesFinder.fromKms(wstKms); final String waterlevelLabel = waterlevel.getName(); final String soundingLabel = buildSoundingLabel(minBedHeight, maxBedHeight); /* real calculation loop */ final Collection<SInfoResultRow> rows = new ArrayList<>(); // FIXME: we use the stations of one of the bed heights atm, because we probably will later use only data from one bed heights datasets! final Collection<Double> stations = minBedHeight == null ? maxBedHeight.getStations() : minBedHeight.getStations(); for (final double station : stations) { 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... // FIXME: this will simply the bed height of 'the' bed height if we reduce this to simply using one sounding dataset 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); /* ignore invalid lines */ if (Double.isNaN(wst) || Double.isNaN(minBedHeightValue) || Double.isNaN(maxBedHeightValue)) continue; final SInfoResultRow row = SInfoResultRow.create().// putValue(SInfoResultType.station, station). // putValue(SInfoResultType.flowdepthmin, minFlowDepth). // putValue(SInfoResultType.flowdepthmax, maxFlowDepth). // putValue(SInfoResultType.waterlevel, wst). // putValue(SInfoResultType.discharge, discharge). // putValue(SInfoResultType.waterlevelLabel, waterlevelLabel). // putValue(SInfoResultType.gaugeLabel, gaugeLabel). // putValue(SInfoResultType.meanBedHeight, meanBedHeight). // putValue(SInfoResultType.soundingLabel, soundingLabel). // putValue(SInfoResultType.location, location); rows.add(row); } } 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; } }