gernotbelger@9145: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde gernotbelger@9145: * Software engineering by gernotbelger@9145: * Björnsen Beratende Ingenieure GmbH gernotbelger@9145: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt gernotbelger@9145: * gernotbelger@9145: * This file is Free Software under the GNU AGPL (>=v3) gernotbelger@9145: * and comes with ABSOLUTELY NO WARRANTY! Check out the gernotbelger@9145: * documentation coming with Dive4Elements River for details. gernotbelger@9145: */ gernotbelger@9145: package org.dive4elements.river.artifacts.sinfo.flood_duration; gernotbelger@9145: gernotbelger@9145: import java.util.ArrayList; mschaefer@9266: import java.util.Collection; mschaefer@9176: import java.util.HashMap; mschaefer@9202: import java.util.List; mschaefer@9176: import java.util.Map; mschaefer@9202: import java.util.Set; gernotbelger@9145: gernotbelger@9145: import org.apache.commons.lang.math.DoubleRange; gernotbelger@9150: import org.dive4elements.artifacts.CallContext; mschaefer@9202: import org.dive4elements.river.artifacts.WINFOArtifact; mschaefer@9202: import org.dive4elements.river.artifacts.access.ComputationRangeAccess; gernotbelger@9145: import org.dive4elements.river.artifacts.common.GeneralResultType; gernotbelger@9145: import org.dive4elements.river.artifacts.common.ResultRow; gernotbelger@9150: import org.dive4elements.river.artifacts.model.Calculation; mschaefer@9202: import org.dive4elements.river.artifacts.model.Calculation.Problem; mschaefer@9202: import org.dive4elements.river.artifacts.model.CalculationResult; mschaefer@9252: import org.dive4elements.river.artifacts.model.WQDay; mschaefer@9202: import org.dive4elements.river.artifacts.model.WQKms; mschaefer@9269: import org.dive4elements.river.artifacts.model.WstValueTable; mschaefer@9269: import org.dive4elements.river.artifacts.model.WstValueTable.QPosition; mschaefer@9269: import org.dive4elements.river.artifacts.model.WstValueTableFactory; gernotbelger@9499: import org.dive4elements.river.artifacts.model.river.RiverInfoProvider; mschaefer@9259: import org.dive4elements.river.artifacts.resources.Resources; mschaefer@9176: import org.dive4elements.river.artifacts.sinfo.common.GaugeDurationValuesFinder; gernotbelger@9145: import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; mschaefer@9176: import org.dive4elements.river.artifacts.sinfo.flood_duration.RiversideRadioChoice.RiversideChoiceKey; gernotbelger@9205: import org.dive4elements.river.exports.WaterlevelDescriptionBuilder; mschaefer@9257: import org.dive4elements.river.jfree.StickyAxisAnnotation; mschaefer@9257: import org.dive4elements.river.jfree.StickyAxisAnnotation.SimpleAxis; mschaefer@9176: import org.dive4elements.river.model.Attribute.AttributeKey; mschaefer@9176: import org.dive4elements.river.model.Gauge; mschaefer@9176: import org.dive4elements.river.model.sinfo.InfrastructureValue; gernotbelger@9145: mschaefer@9202: import gnu.trove.TDoubleArrayList; mschaefer@9202: gernotbelger@9145: /** mschaefer@9176: * Calculation of the result rows of the flood duration of the infrastructures in a river km range mschaefer@9176: * and selected main value durations mschaefer@9176: * mschaefer@9176: * @author Matthias Schäfer gernotbelger@9145: */ gernotbelger@9145: final class FloodDurationCalculator { gernotbelger@9145: mschaefer@9259: private final List rows = new ArrayList<>(); gernotbelger@9145: gernotbelger@9145: private final RiverInfoProvider riverInfoProvider; mschaefer@9528: private RiverInfoProvider riverInfoProvider2; gernotbelger@9145: gernotbelger@9150: private final CallContext context; gernotbelger@9145: gernotbelger@9150: public FloodDurationCalculator(final CallContext context, final RiverInfoProvider riverInfoProvider) { gernotbelger@9150: this.context = context; gernotbelger@9145: this.riverInfoProvider = riverInfoProvider; mschaefer@9528: this.riverInfoProvider2 = null; gernotbelger@9145: } gernotbelger@9145: mschaefer@9176: /** mschaefer@9202: * Calculate the infrastructures flood duration result rows mschaefer@9176: */ gernotbelger@9492: public void execute(final Calculation problems, final String label, final DoubleRange calcRange, final RiversideChoiceKey riverside, final boolean withWspl, gernotbelger@9492: final WINFOArtifact winfo, final FloodDurationCalculationResults results) { gernotbelger@9145: mschaefer@9202: // Find all gauges of the calc range, and create the duration finders mschaefer@9176: final Map durFinders = new HashMap<>(); mschaefer@9528: for (final Gauge gauge : this.riverInfoProvider.getGauges()) { mschaefer@9176: durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); mschaefer@9176: } gernotbelger@9150: mschaefer@9202: // Find all infrastructures within the calc range mschaefer@9202: final AttributeKey bankKey = riverside.getAttributeKey(); mschaefer@9202: final List infras = InfrastructureValue.getValues(this.riverInfoProvider.getRiver(), calcRange.getMinimumDouble(), mschaefer@9202: calcRange.getMaximumDouble(), bankKey); gernotbelger@9150: mschaefer@9202: // Merge all stations (range/step, borders of gauge ranges, infrastructures) mschaefer@9202: final Map allStations = new HashMap<>(); mschaefer@9202: final Map secondBank = new HashMap<>(); // any second infrastructure in case of both-banks-option mschaefer@9236: addRangeStations(allStations, winfo); mschaefer@9202: addGaugeLimits(allStations, durFinders.keySet(), calcRange.getMinimumDouble(), calcRange.getMaximumDouble()); mschaefer@9202: addInfrastructures(allStations, secondBank, infras); mschaefer@9202: final double[] stationsSorted = sortStations(allStations.keySet()); gernotbelger@9150: mschaefer@9286: // Calculate W and Q for all stations and the selected discharge states/waterlevels mschaefer@9398: final WQKms[] wqkmsArray = calculateWsts(winfo, withWspl, stationsSorted, problems); mschaefer@9528: // final WaterlevelData waterlevel = new WaterlevelData(wqkmsArray[0], -1, false, true); mschaefer@9528: // this.riverInfoProvider2 = this.riverInfoProvider.forWaterlevel(waterlevel); mschaefer@9528: this.riverInfoProvider2 = this.riverInfoProvider.forReferenceRange(calcRange, false); mschaefer@9528: // this.riverInfoProvider2.cleanupGaugesAtStart(calcRange); mschaefer@9202: mschaefer@9286: // Determine discharge state labels of the waterlevels mschaefer@9286: updateWstLabels(wqkmsArray, winfo, problems); mschaefer@9202: mschaefer@9286: final Map> gaugeWstDurations = new HashMap<>(); mschaefer@9398: if (withWspl) mschaefer@9398: calcGaugeWstDurations(winfo, new ArrayList<>(durFinders.keySet()), gaugeWstDurations, durFinders); mschaefer@9286: mschaefer@9286: // Load base wst table (river).wst mschaefer@9286: // (should be in cache since already used in calculateWaterlevels (winfo.computeWaterlevelData) mschaefer@9528: final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider2.getRiver()); mschaefer@9202: mschaefer@9286: // Create the result rows, and calculate and add the flood durations etc. mschaefer@9202: for (int i = 0; i <= stationsSorted.length - 1; i++) { mschaefer@9528: final Gauge gauge = this.riverInfoProvider2.getGauge(stationsSorted[i], true); mschaefer@9398: final ResultRow row = createRow(stationsSorted[i], wqkmsArray, gaugeWstDurations.get(gauge), i); mschaefer@9202: if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) mschaefer@9269: calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wst, durFinders); mschaefer@9202: this.rows.add(row); mschaefer@9202: if (secondBank.containsKey(stationsSorted[i])) { mschaefer@9202: final ResultRow row2 = ResultRow.create(row); mschaefer@9269: calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wst, durFinders); mschaefer@9202: this.rows.add(row2); mschaefer@9202: } mschaefer@9202: } mschaefer@9269: mschaefer@9286: // Get the labels of the selected waterlevels mschaefer@9286: final String[] wstLabels = new String[wqkmsArray.length]; mschaefer@9286: for (int i = 0; i <= wqkmsArray.length - 1; i++) mschaefer@9286: wstLabels[i] = wqkmsArray[i].getName(); mschaefer@9269: gernotbelger@9492: results.addResult(new FloodDurationCalculationResult(label, wstLabels, this.rows, withWspl), problems); mschaefer@9252: } mschaefer@9202: mschaefer@9257: /** mschaefer@9266: * Calculates the duration curve for a station mschaefer@9286: * (other than the version 3.2.1 W-Info Dauerlinie the wst column positions mschaefer@9286: * are taken from the Q values of the gauge's Q-D-table) mschaefer@9257: */ mschaefer@9266: public WQDay calcWQDays(final Calculation problems, final double station, final WINFOArtifact winfo) { mschaefer@9252: mschaefer@9534: final CalculationResult res = winfo.getDurationCurveData(); mschaefer@9534: final WQDay wqday = (WQDay) res.getData(); mschaefer@9534: if (wqday == null) mschaefer@9534: return null; mschaefer@9534: mschaefer@9534: final int[] odays = new int[wqday.size()]; mschaefer@9534: for (int i = 0; i <= odays.length - 1; i++) mschaefer@9594: odays[i] = 365 - wqday.getDay(i); // TODO Eigentlich 365.25, ist aber mit getDay als int sinnlos mschaefer@9534: return new WQDay(odays, wqday.getWs(), wqday.getQs()); gernotbelger@9145: } gernotbelger@9145: mschaefer@9176: /** mschaefer@9269: * Calculates the data for the Q main value lines in the duration curve chart mschaefer@9257: */ mschaefer@9269: public List calcMainValueQAnnotations(final Calculation problems, final double station, final FloodDurationCalculationResult result) { mschaefer@9257: mschaefer@9269: // Search the station in the previously calculated result rows and terminate if no infrastructure row found mschaefer@9269: double station1 = station; mschaefer@9269: if (Double.isNaN(station)) { mschaefer@9298: for (final ResultRow row : result.getAllRows()) { mschaefer@9269: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9269: break; mschaefer@9269: } mschaefer@9269: } mschaefer@9376: final List stationRows = searchStation(station1, AttributeKey.NONE, result.getAllRows()); mschaefer@9297: if (stationRows.isEmpty()) { mschaefer@9269: return new ArrayList<>(); mschaefer@9269: } mschaefer@9269: final ResultRow row = stationRows.get(0); mschaefer@9269: final List annotations = new ArrayList<>(); mschaefer@9269: final List wqds = (List) row.getValue(SInfoResultType.customMultiRowColWaterlevel); mschaefer@9269: for (final DurationWaterlevel wqd : wqds) { mschaefer@9269: final String label = wqd.getBezeichnung().startsWith("W=") ? "Q(" + wqd.getBezeichnung() + ")" : wqd.getBezeichnung(); mschaefer@9269: final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) wqd.getDischarge(), SimpleAxis.Y_AXIS, mschaefer@9257: FloodDurationCurveGenerator.YAXIS.Q.idx); mschaefer@9357: annotation.setHitPoint((float) wqd.getFloodDurDaysPerYear()); mschaefer@9269: annotations.add(annotation); mschaefer@9269: } mschaefer@9269: return annotations; mschaefer@9269: } mschaefer@9269: mschaefer@9269: /** mschaefer@9269: * Calculates the data for the W main value lines in the duration curve chart mschaefer@9269: */ mschaefer@9269: public List calcMainValueWAnnotations(final Calculation problems, final double station, final FloodDurationCalculationResult result) { mschaefer@9269: mschaefer@9269: // Search the station in the previously calculated result rows and terminate if no infrastructure row found mschaefer@9269: double station1 = station; mschaefer@9269: if (Double.isNaN(station)) { mschaefer@9298: for (final ResultRow row : result.getAllRows()) { mschaefer@9269: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9269: break; mschaefer@9269: } mschaefer@9269: } mschaefer@9376: final List stationRows = searchStation(station1, AttributeKey.NONE, result.getAllRows()); mschaefer@9297: if (stationRows.isEmpty()) { mschaefer@9269: return new ArrayList<>(); mschaefer@9269: } mschaefer@9269: final List annotations = new ArrayList<>(); mschaefer@9269: final ResultRow row = stationRows.get(0); mschaefer@9269: final List wqds = (List) row.getValue(SInfoResultType.customMultiRowColWaterlevel); mschaefer@9269: for (final DurationWaterlevel wqd : wqds) { mschaefer@9269: final String label = !wqd.getBezeichnung().startsWith("W=") ? "W(" + wqd.getBezeichnung() + ")" : wqd.getBezeichnung(); mschaefer@9269: final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) wqd.getWaterlevel(), SimpleAxis.Y_AXIS, mschaefer@9257: FloodDurationCurveGenerator.YAXIS.W.idx); mschaefer@9357: annotation.setHitPoint((float) wqd.getFloodDurDaysPerYear()); mschaefer@9269: annotations.add(annotation); mschaefer@9257: } mschaefer@9269: return annotations; mschaefer@9257: } mschaefer@9257: mschaefer@9257: /** mschaefer@9398: * Find and return the W or Q annotation(s) of a station and a riverside in a previously calculated result mschaefer@9257: */ mschaefer@9376: public List calcInfrastructureAnnotations(final Calculation problems, final double station, final AttributeKey riverside, mschaefer@9376: final boolean isW, final FloodDurationCalculationResult result) { mschaefer@9257: mschaefer@9266: // Search the station in the previously calculated result rows and terminate if no infrastructure row found mschaefer@9266: double station1 = station; mschaefer@9266: if (Double.isNaN(station)) { mschaefer@9266: for (final ResultRow row : result.getRows()) { mschaefer@9376: if (row.getValue(SInfoResultType.riverside) == riverside) { mschaefer@9376: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9376: break; mschaefer@9376: } mschaefer@9266: } mschaefer@9266: } mschaefer@9376: final List stationRows = searchStation(station1, riverside, result.getRows()); mschaefer@9266: if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) { mschaefer@9269: return new ArrayList<>(); mschaefer@9257: } mschaefer@9259: // Same way as in MainValueWFacet and ..QFacet mschaefer@9266: final List annotations = new ArrayList<>(); mschaefer@9266: for (final ResultRow row : stationRows) { mschaefer@9376: if (isW) mschaefer@9376: annotations.add(calcInfrastructureWAnnotation(row)); mschaefer@9376: else mschaefer@9376: annotations.add(calcInfrastructureQAnnotation(row)); mschaefer@9266: } mschaefer@9266: return annotations; mschaefer@9266: } mschaefer@9266: mschaefer@9266: /** mschaefer@9266: * Searches the one or two rows of a station in a result rows collection mschaefer@9266: */ mschaefer@9376: private List searchStation(final double station, final AttributeKey riverside, final Collection rows) { mschaefer@9266: final List found = new ArrayList<>(); mschaefer@9266: for (final ResultRow row : rows) { mschaefer@9266: if (row.getDoubleValue(GeneralResultType.station) > station + 0.0001) mschaefer@9266: break; mschaefer@9376: else if ((row.getDoubleValue(GeneralResultType.station) > station - 0.0001) mschaefer@9376: && ((riverside == AttributeKey.NONE) || (row.getValue(SInfoResultType.riverside) == riverside))) mschaefer@9266: found.add(row); mschaefer@9266: } mschaefer@9266: return found; mschaefer@9266: } mschaefer@9266: mschaefer@9266: /** mschaefer@9266: * Calculates the Q annotation lines of an infrastructure mschaefer@9266: */ mschaefer@9269: private StickyAxisAnnotation calcInfrastructureQAnnotation(final ResultRow row) { mschaefer@9259: final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure", mschaefer@9259: "sinfo.chart.flood_duration.curve.infrastructure", gernotbelger@9312: SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) + ", " gernotbelger@9312: + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside))); gernotbelger@9312: final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.floodDischarge), SimpleAxis.Y_AXIS, gernotbelger@9312: FloodDurationCurveGenerator.YAXIS.Q.idx); mschaefer@9266: annotation.setHitPoint((float) row.getDoubleValue(SInfoResultType.floodDuration)); mschaefer@9266: return annotation; mschaefer@9266: } mschaefer@9266: mschaefer@9266: /** mschaefer@9266: * Calculates the W annotation lines of an infrastructure mschaefer@9266: */ mschaefer@9269: private StickyAxisAnnotation calcInfrastructureWAnnotation(final ResultRow row) { mschaefer@9266: final String label = Resources.getMsg(this.context.getMeta(), "sinfo.chart.flood_duration.curve.infrastructure", mschaefer@9266: "sinfo.chart.flood_duration.curve.infrastructure", gernotbelger@9312: SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) + ", " gernotbelger@9312: + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside))); mschaefer@9266: final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.infrastructureHeight), mschaefer@9259: SimpleAxis.Y_AXIS, FloodDurationCurveGenerator.YAXIS.W.idx); mschaefer@9266: annotation.setHitPoint((float) row.getDoubleValue(SInfoResultType.floodDuration)); mschaefer@9266: return annotation; mschaefer@9257: } mschaefer@9257: mschaefer@9257: /** mschaefer@9202: * Adds to a stations map all stations corresponding to the active range and step mschaefer@9176: */ mschaefer@9202: private void addRangeStations(final Map allStations, final WINFOArtifact winfo) { mschaefer@9202: for (final double station : new ComputationRangeAccess(winfo).getKms()) mschaefer@9202: allStations.put(Double.valueOf(station), null); mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Adds to a stations map all range limits of the gauges within the calc range mschaefer@9202: */ mschaefer@9202: private void addGaugeLimits(final Map allStations, final Set gauges, final double fromKm, final double toKm) { mschaefer@9202: for (final Gauge gauge : gauges) { mschaefer@9202: final Double kmA = Double.valueOf(gauge.getRange().getA().doubleValue()); mschaefer@9202: final Double kmB = Double.valueOf(gauge.getRange().getB().doubleValue()); mschaefer@9202: if (kmA > fromKm - 0.0001) mschaefer@9202: allStations.put(kmA, null); mschaefer@9202: if (kmB < toKm + 0.0001) mschaefer@9202: allStations.put(kmB, null); mschaefer@9202: } mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Adds to a stations map all (first) infrastructures of a station, and the second, if any, to another map mschaefer@9202: */ mschaefer@9202: private void addInfrastructures(final Map allStations, final Map secondBank, mschaefer@9202: final List infrastructures) { mschaefer@9202: for (final InfrastructureValue infrastructure : infrastructures) { mschaefer@9202: final Double station = infrastructure.getStation(); mschaefer@9202: if (!allStations.containsKey(station) || !(allStations.get(station) instanceof InfrastructureValue)) mschaefer@9202: allStations.put(station, infrastructure); mschaefer@9202: else mschaefer@9202: secondBank.put(station, infrastructure); mschaefer@9202: } mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Returns a double array with a sorted stations set mschaefer@9202: */ mschaefer@9202: private double[] sortStations(final Set stations) { mschaefer@9202: final TDoubleArrayList sorted = new TDoubleArrayList(); mschaefer@9202: for (final Double station : stations) mschaefer@9202: sorted.add(station.doubleValue()); mschaefer@9202: sorted.sort(); mschaefer@9202: return sorted.toNativeArray(); mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Calculates an array of w-q-longitudinal sections for all artifact W/Q options mschaefer@9202: */ mschaefer@9398: private WQKms[] calculateWsts(final WINFOArtifact winfo, final boolean withWspl, final double[] stations, final Calculation problems) { mschaefer@9286: // First run may take long, further runs are faster since WstValueTable is in cache then mschaefer@9202: // (So funktioniert computeWaterlevelData wohl: mschaefer@9229: // Es sucht die Spalte(n) zum Bezugspegel-Q in der W-Q-Tabelle ({river}.wst in Wst etc.), mschaefer@9229: // interpoliert die horizontale Tabellenposition (Q) und dann die vertikale Tabellenposition der station; mschaefer@9202: // das ergibt das W einer station für einen Abflusszustand; mschaefer@9202: // bei Vorgabe eines Pegel-W wird vorher anhand der W-Q-Tabelle des Pegels ({gauge}.at in DischargeTable) das Q mschaefer@9202: // interpoliert; mschaefer@9229: // bei Vorgabe eines W auf freier Strecke wird wohl vorher noch die .wst-Interpolation eingesetzt, um das Q zu bekommen. mschaefer@9229: mschaefer@9398: if (!withWspl) mschaefer@9398: return new WQKms[] {}; mschaefer@9398: mschaefer@9286: final CalculationResult wstsData = winfo.computeWaterlevelData(stations); mschaefer@9202: mschaefer@9202: /* copy all problems */ mschaefer@9286: final Calculation winfoProblems = wstsData.getReport(); mschaefer@9202: final List problems2 = winfoProblems.getProblems(); mschaefer@9202: if (problems2 != null) { mschaefer@9202: for (final Problem problem : problems2) { mschaefer@9202: problems.addProblem(problem); mschaefer@9202: } mschaefer@9202: } mschaefer@9286: return (WQKms[]) wstsData.getData(); mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9259: * Determines the waterlevel/discharge state labels for the selected Q or W values and sets them in the WQKms array mschaefer@9202: */ mschaefer@9286: private void updateWstLabels(final WQKms[] wqkmsArray, final WINFOArtifact winfo, final Calculation problems) { gernotbelger@9205: mschaefer@9229: for (int i = 0; i <= wqkmsArray.length - 1; i++) mschaefer@9259: wqkmsArray[i].setName(buildWQDescription(wqkmsArray[i], winfo)); mschaefer@9259: } mschaefer@9259: mschaefer@9259: /** mschaefer@9286: * Builds the description label of a waterlevel mschaefer@9259: */ mschaefer@9259: private String buildWQDescription(final WQKms wqkms, final WINFOArtifact winfo) { mschaefer@9286: mschaefer@9259: final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context); mschaefer@9286: // TODO Zwischen numerischem Q-Wert und Dauerzahl-Hauptwert (0..364) unterscheiden mschaefer@9259: final String description = descBuilder.getDesc(wqkms); mschaefer@9259: if (!description.isEmpty() && Character.isDigit(description.charAt(0))) { mschaefer@9259: if (winfo.isQ()) mschaefer@9259: return "Q=" + description; mschaefer@9259: else mschaefer@9259: return "W=" + description; gernotbelger@9312: } else mschaefer@9259: return description; mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9286: * Calculates the flood durations of the Qs of the waterlevels/discharge states for a map of gauges mschaefer@9286: */ mschaefer@9286: private void calcGaugeWstDurations(final WINFOArtifact winfo, final List gauges, final Map> gaugeWstDurations, mschaefer@9286: final Map durFinders) { mschaefer@9286: mschaefer@9286: final double[] gaugeKms = new double[gauges.size()]; mschaefer@9286: for (int i = 0; i <= gauges.size() - 1; i++) { mschaefer@9286: gaugeKms[i] = gauges.get(i).getStation().doubleValue(); mschaefer@9286: gaugeWstDurations.put(gauges.get(i), new ArrayList()); mschaefer@9286: } mschaefer@9286: final CalculationResult wstsData = winfo.computeWaterlevelData(gaugeKms); mschaefer@9286: final WQKms[] wsts = (WQKms[]) wstsData.getData(); mschaefer@9286: for (int i = 0; i <= gauges.size() - 1; i++) { mschaefer@9286: final GaugeDurationValuesFinder durFinder = durFinders.get(gauges.get(i)); mschaefer@9286: for (int j = 0; j <= wsts.length - 1; j++) { mschaefer@9286: final double d = durFinder.getDuration(wsts[j].getQ(i)); mschaefer@9286: gaugeWstDurations.get(gauges.get(i)).add(Double.valueOf(underflowDaysToOverflowDays(d))); mschaefer@9286: } mschaefer@9286: } mschaefer@9286: } mschaefer@9286: mschaefer@9286: /** mschaefer@9398: * Create a result row for a station, and add w-q-values as selected mschaefer@9202: */ mschaefer@9398: private ResultRow createRow(final Double station, final WQKms[] wqkmsArray, final List gaugeDurations, final int kmIndex) { gernotbelger@9145: gernotbelger@9145: final ResultRow row = ResultRow.create(); mschaefer@9202: row.putValue(GeneralResultType.station, station); mschaefer@9202: row.putValue(SInfoResultType.infrastructuretype, null); // is replaced later for an infrastructure mschaefer@9202: row.putValue(SInfoResultType.floodDuration, Double.NaN); // is replaced later for an infrastructure gernotbelger@9205: mschaefer@9528: final String gaugeLabel = this.riverInfoProvider2.findGauge(station); gernotbelger@9318: row.putValue(GeneralResultType.gaugeLabel, gaugeLabel); gernotbelger@9205: mschaefer@9528: final String location = this.riverInfoProvider2.getLocation(station); gernotbelger@9312: row.putValue(GeneralResultType.location, location); gernotbelger@9145: mschaefer@9286: final List wsts = new ArrayList<>(wqkmsArray.length); gernotbelger@9205: mschaefer@9286: for (int i = 0; i <= wqkmsArray.length - 1; i++) { mschaefer@9357: final DurationWaterlevel dw = new DurationWaterlevel(wqkmsArray[i].getW(kmIndex), gaugeDurations.get(i), wqkmsArray[i].getQ(kmIndex), mschaefer@9286: wqkmsArray[i].getName()); mschaefer@9286: wsts.add(dw); mschaefer@9202: } mschaefer@9286: row.putValue(SInfoResultType.customMultiRowColWaterlevel, wsts); gernotbelger@9205: mschaefer@9202: return row; mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Calculate the result row fields for one infrastructure mschaefer@9202: */ mschaefer@9269: private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue infrastructure, final WstValueTable wst, gernotbelger@9205: final Map durFinders) { mschaefer@9202: mschaefer@9269: // Interpolate the infrastructure height in the wst table to get the corresponding Q mschaefer@9269: final Calculation problems = new Calculation(); mschaefer@9269: final double[] qs = wst.findQsForW(infrastructure.getStation().doubleValue(), infrastructure.getHeight().doubleValue(), problems); mschaefer@9269: // TODO Fehlerbehandlung (kein Q gefunden) mschaefer@9269: final double q = (qs.length >= 1) ? qs[0] : Double.NaN; mschaefer@9308: // Set the result row mschaefer@9308: row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey()); mschaefer@9308: row.putValue(SInfoResultType.floodDischarge, q); mschaefer@9308: row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); mschaefer@9308: row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); mschaefer@9286: // Determine the relative column position of the Q of the infrastructure height mschaefer@9269: final QPosition qPos = wst.getQPosition(infrastructure.getStation().doubleValue(), q); mschaefer@9308: if (qPos == null) mschaefer@9308: return; mschaefer@9269: // Get the Q for the found column position for the station of the gauge mschaefer@9269: final double qGauge = wst.getQ(qPos, gauge.getStation().doubleValue()); mschaefer@9269: // Interpolate the Q-D-table of the gauge mschaefer@9269: final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(qGauge)); mschaefer@9308: // Set D in the result row mschaefer@9202: row.putValue(SInfoResultType.floodDuration, dur); mschaefer@9202: } gernotbelger@9145: mschaefer@9202: /** mschaefer@9202: * Translates underflow duration into overflow duration mschaefer@9202: */ mschaefer@9202: private double underflowDaysToOverflowDays(final double underflowDays) { mschaefer@9594: return 365.25 - underflowDays; gernotbelger@9145: } gernotbelger@9145: }