# HG changeset patch # User mschaefer # Date 1533915106 -7200 # Node ID 439699ff9b2d977d479db7f71bd35e5b08cfdfaf # Parent 6174daaf5e5695731168a8fd982a48eb8151124e Added U-Info iota (prev. salix) calculation for historical scenario diff -r 6174daaf5e56 -r 439699ff9b2d artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Fri Aug 10 17:07:30 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/BedHeightsFinder.java Fri Aug 10 17:31:46 2018 +0200 @@ -102,7 +102,7 @@ * * @return null if no bed height with the given id exists. */ - private static BedHeightsFinder forId(final Calculation problems, final int id, final DoubleRange range) { + public static BedHeightsFinder forId(final Calculation problems, final int id, final DoubleRange range) { final BedHeight bedHeight = BedHeight.getBedHeightById(id); if (bedHeight == null) @@ -153,6 +153,12 @@ return this.values.keySet(); } + public DoubleRange getKmRange() { + if (this.values.isEmpty()) + return null; + return new DoubleRange(this.values.firstKey().doubleValue(), this.values.lastKey().doubleValue()); + } + public double getMeanBedHeight(final double km) { return interpolateBedHeights(km, BedHeightValueType.value); } diff -r 6174daaf5e56 -r 439699ff9b2d artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java Fri Aug 10 17:07:30 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/sinfo/tkhstate/DefaultBedHeights.java Fri Aug 10 17:31:46 2018 +0200 @@ -22,11 +22,11 @@ import org.dive4elements.river.model.River; /** - * This class knows how to find the default bed heights defined for tkh calculation + * This class knows how to find the default bed heights defined for tkh and other calculations * * @author Gernot Belger */ -final class DefaultBedHeights { +public final class DefaultBedHeights { private final River river; diff -r 6174daaf5e56 -r 439699ff9b2d artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java Fri Aug 10 17:07:30 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineAccess.java Fri Aug 10 17:31:46 2018 +0200 @@ -99,6 +99,18 @@ return super.getString("supraregional_table"); } + /** + * Database id of the selected sounding, or 0 + */ + public int getBedHeightId() { + final BedHeightAccess access = new BedHeightAccess(this.artifact); + final int[] ids = access.getBedHeightIDs(); + if ((ids != null) && (ids.length > 0)) { + return ids[0]; + } + return 0; + } + public BedHeight getBedHeight() { // TODO: make lazy? Aber achtung, falls der user zurückgeht und ne andere Peilung auswählt... final BedHeightAccess access = new BedHeightAccess(this.artifact); final int[] ids = access.getBedHeightIDs(); diff -r 6174daaf5e56 -r 439699ff9b2d artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java Fri Aug 10 17:07:30 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculation.java Fri Aug 10 17:31:46 2018 +0200 @@ -10,6 +10,7 @@ package org.dive4elements.river.artifacts.uinfo.salix; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.NavigableMap; import java.util.TreeMap; @@ -22,26 +23,39 @@ import org.dive4elements.river.artifacts.model.CalculationResult; import org.dive4elements.river.artifacts.resources.Resources; import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; +import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsFinder; +import org.dive4elements.river.artifacts.sinfo.tkhstate.BedHeightsUtils; +import org.dive4elements.river.artifacts.sinfo.tkhstate.DefaultBedHeights; import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils; import org.dive4elements.river.artifacts.sinfo.util.RiverInfo; import org.dive4elements.river.artifacts.uinfo.UINFOArtifact; import org.dive4elements.river.artifacts.uinfo.salix.SalixLineAccess.ScenarioType; +import org.dive4elements.river.model.BedHeight; import org.dive4elements.river.model.River; +import org.dive4elements.river.utils.Formatter; /** + * Calculation of a iota (former salix) longitudinal section, optionally with a delta scenario + * * @author Domenico Nardi Tironi + * @author Matthias Schäfer * */ final class SalixLineCalculation { private final CallContext context; + private Calculation problems; + public SalixLineCalculation(final CallContext context) { this.context = context; } + /** + * Calculates the iota longitudinal section and delta scenario of a uinfo artifact + */ public CalculationResult calculate(final UINFOArtifact uinfo) { - final Calculation problems = new Calculation(); + this.problems = new Calculation(); final String calcModeLabel = Resources.getMsg(this.context.getMeta(), uinfo.getCalculationMode().name()); final String user = CalculationUtils.findArtifactUser(this.context, uinfo); @@ -62,14 +76,14 @@ final SalixLineCalculator calculator = new SalixLineCalculator(riverInfoProvider); final NavigableMap> rangeScenarios = buildRangeScenarios(accessSalix); - calculator.execute(problems, uinfo, rangeScenarios, scenarioType, buildScenarioLabels(accessSalix), buildPartialRangeString(accessSalix), + calculator.execute(this.problems, uinfo, rangeScenarios, scenarioType, buildScenarioLabels(accessSalix), buildPartialRangeString(accessSalix), buildAdditionalString(accessSalix), results); - return new CalculationResult(results, problems); + return new CalculationResult(results, this.problems); } /** - * Build a map of delta-Ws by from-km for the selected scenario + * Builds a map of delta-Ws by from-km for the selected scenario */ private NavigableMap> buildRangeScenarios(final SalixLineAccess access) { final NavigableMap> rangeScenarios = new TreeMap<>(); @@ -78,8 +92,8 @@ access.getRegionalScenarioIntegers()); else if (access.getScenario() == ScenarioType.SUPRAREGIONAL) fillRangeScenarios(rangeScenarios, access.getSupraRegionalString()); - // TODO else if (access.getScenario().equals(ScenarioType.HISTORICAL.getKey())) - // historisches Szenario aus MSH etc. + else if (access.getScenario() == ScenarioType.HISTORICAL) + fillRangeScenarios(rangeScenarios, access, access.getFromPart().doubleValue(), access.getToPart().doubleValue(), access.getBedHeightId()); else fillRangeScenarios(rangeScenarios, access); @@ -87,23 +101,23 @@ } /** - * Fill a map of delta-Ws with only one 0-delta for the whole calc range (no scenario) + * Fills a map of delta-Ws with only one 0-delta for the whole calc range (no scenario) */ private void fillRangeScenarios(final NavigableMap> rangeScenarios, final RangeAccess calcRange) { final List nulls = new ArrayList<>(); - nulls.add(0.0); + nulls.add(null); rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls); } /** - * Fill a map of delta-Ws by km-range from the regional scenario input data + * Fills a map of delta-Ws by km-range from the regional scenario input data */ - private void fillRangeScenarios(final NavigableMap> rangeScenarios, final RangeAccess calcRange, final double partFrom, - final double partTo, final int[] deltaWs) { + private void fillRangeScenarios(final NavigableMap> rangeScenarios, final RangeAccess calcRange, + final double partFrom, final double partTo, final int[] deltaWs) { final List nulls = new ArrayList<>(); final List dwsm = new ArrayList<>(); for (int i = 0; i <= deltaWs.length - 1; i++) { - nulls.add(0.0); + nulls.add(null); dwsm.add(deltaWs[i] / 100.0); } rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls); @@ -112,20 +126,65 @@ } /** - * Fill a map of delta-Ws by km-range from the supraregional scenario input data + * Fills a map of delta-Ws by km-range from the supraregional scenario input data * (the zones input by the user cover the calc range completely) */ private void fillRangeScenarios(final NavigableMap> rangeScenarios, final String zones) { final List parts = SalixZone.parse(zones); for (final SalixZone part : parts) { final List dwsm = new ArrayList<>(); - dwsm.add(part.getDwsplValue() / 100.0); + if (part.getDwsplValue() == 0) + dwsm.add(null); + else + dwsm.add(part.getDwsplValue() / 100.0); rangeScenarios.put(Double.valueOf(part.getFromKm().doubleValue() - 0.0001), dwsm); } } /** - * Build the list of delta-w labels for the scenario type + * Fetches historical and reference bed heights and fills a map of delta-MSHs for all fetched stations in the calc range + */ + private void fillRangeScenarios(final NavigableMap> rangeScenarios, final RangeAccess calcRange, + final double partFrom, final double partTo, final int historicalBedHeightId) { + + // Find relevant default bed-heights + final River river = calcRange.getRiver(); + final List defaultBedHeights = new DefaultBedHeights(river).getBedHeights(this.problems); + if (defaultBedHeights.isEmpty()) + return; + final DoubleRange scenarioRange = new DoubleRange(partFrom, partTo); + final Collection allFinders = BedHeightsFinder.createTkhBedHeights(this.problems, scenarioRange, defaultBedHeights); + final Collection currentFinders = new ArrayList<>(allFinders); + + // Add historical bed-heights + final BedHeightsFinder historicalFinder = BedHeightsFinder.forId(this.problems, historicalBedHeightId, scenarioRange); + allFinders.add(historicalFinder); + final Collection stations = BedHeightsUtils.extractStationCollection(allFinders, true); + final List nulls = new ArrayList<>(); + nulls.add(null); + rangeScenarios.put(Double.valueOf(calcRange.getLowerKm() - 0.0001), nulls); + for (final Double station : stations) { + rangeScenarios.put(station, new ArrayList()); + rangeScenarios.get(station).add(Double.valueOf(bedHeightDifference(station.doubleValue(), currentFinders, historicalFinder))); + } + rangeScenarios.put(Double.valueOf(partTo + 0.0001), nulls); + } + + /** + * Gets the difference of a historical bed height against a current one for a station + */ + private double bedHeightDifference(final double station, final Collection currentFinders, final BedHeightsFinder historicalFinder) { + double currentMSH = Double.NaN; + for (final BedHeightsFinder bhf : currentFinders) { + currentMSH = bhf.getMeanBedHeight(station); + if (!Double.isNaN(currentMSH)) + break; + } + return Formatter.roundFlowDepth(historicalFinder.getMeanBedHeight(station)).subtract(Formatter.roundFlowDepth(currentMSH)).doubleValue(); + } + + /** + * Builds the list of delta-w labels for the scenario type */ private String[] buildScenarioLabels(final SalixLineAccess access) { final List labels = new ArrayList<>(); @@ -142,7 +201,7 @@ } /** - * Build the km range string for the scenario type + * Builds the km range string for the scenario type */ private String buildPartialRangeString(final SalixLineAccess access) { if ((access.getScenario() == ScenarioType.REGIONAL) || (access.getScenario() == ScenarioType.HISTORICAL)) { @@ -166,7 +225,7 @@ } /** - * Build the delta w or time string for the scenario type + * Builds the delta w or time string for the scenario type */ private String buildAdditionalString(final SalixLineAccess access) { if (access.getScenario() == ScenarioType.REGIONAL) { diff -r 6174daaf5e56 -r 439699ff9b2d artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java Fri Aug 10 17:07:30 2018 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/uinfo/salix/SalixLineCalculator.java Fri Aug 10 17:31:46 2018 +0200 @@ -139,7 +139,7 @@ final ResultRow row = ResultRow.create(); row.putValue(GeneralResultType.station, station); - // Find station's gauge + // Find station's gauge (obsolete version which calculates gauge-wise) // final Gauge gauge = this.riverInfoProvider.getGauge(station, true); // Interpolate mnw, mw, and mhw // final double mnw = interpolateW(station, this.gaugeMnwPos.get(gauge)); @@ -156,11 +156,11 @@ row.putValue(UInfoResultType.salix_mw_mnw, calcMwmnw(mw, mnw)); // Calc scenario values (always all scenario types set, Result variant extracts the fields needed) final List scenarios = new ArrayList<>(); - final double[] deltaws = getDeltaWs(station, rangeScenarios); - for (int i = 0; i <= deltaws.length - 1; i++) { - if (Math.abs(deltaws[i]) > 0.0001) { - final double salix = calcSalix(mhw, mw + deltaws[i]); - scenarios.add(new SalixScenario((int) (deltaws[i] * 100), salix)); + final List deltaws = getDeltaWs(station, rangeScenarios); + for (final Double deltaw : deltaws) { + if (deltaw != null) { + final double salix = calcSalix(mhw, mw + deltaw); + scenarios.add(new SalixScenario((int) (deltaw * 100), salix)); } else { scenarios.add(null); @@ -198,16 +198,15 @@ } /** - * Gets the station-specific list of delta-ws of the active scenario, at least with one 0 item in any case + * Gets the station-specific list of delta-ws of the active scenario, at least with one null item in any case */ - private double[] getDeltaWs(final double station, final NavigableMap> rangeScenarios) { + private List getDeltaWs(final double station, final NavigableMap> rangeScenarios) { final Entry> stationScenarios = rangeScenarios.floorEntry(station); - if (stationScenarios == null) - return new double[] { 0.0 }; - - final double[] deltaws = new double[stationScenarios.getValue().size()]; - for (int i = 0; i <= stationScenarios.getValue().size() - 1; i++) - deltaws[i] = stationScenarios.getValue().get(i); - return deltaws; + if (stationScenarios != null) { + return stationScenarios.getValue(); + } + final List noScen = new ArrayList<>(); + noScen.add(null); + return noScen; } } \ No newline at end of file