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; 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.RiverInfoProvider; 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; 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; gernotbelger@9145: } gernotbelger@9145: mschaefer@9176: /** mschaefer@9202: * Calculate the infrastructures flood duration result rows mschaefer@9176: */ mschaefer@9252: public void execute(final Calculation problems, final String label, final DoubleRange calcRange, mschaefer@9252: final RiversideChoiceKey riverside, 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@9202: Gauge firstGauge = null; mschaefer@9176: for (final Gauge gauge : this.riverInfoProvider.getRiver().determineGauges(calcRange.getMinimumDouble(), calcRange.getMaximumDouble())) { mschaefer@9176: durFinders.put(gauge, GaugeDurationValuesFinder.loadValues(gauge, problems)); mschaefer@9202: if (firstGauge == null) mschaefer@9202: firstGauge = gauge; 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@9202: // Calculate W and Q for all stations and the selected discharge states mschaefer@9269: // TODO Geht das schneller, wenn man WstValueTable statt WINFOArtifact.computeWaterlevelData nutzt? mschaefer@9202: final WQKms[] wqkmsArray = calculateWaterlevels(winfo, stationsSorted, problems); mschaefer@9202: mschaefer@9202: // Determine discharge state labels of the main values mschaefer@9259: updateMainValueLabels(wqkmsArray, winfo, problems); mschaefer@9202: mschaefer@9269: // Load base wst table (river).wst - first run takes long time, then it's cached mschaefer@9269: final WstValueTable wst = WstValueTableFactory.getTable(this.riverInfoProvider.getRiver()); mschaefer@9202: mschaefer@9202: // Calculate the durations and create the result rows mschaefer@9202: for (int i = 0; i <= stationsSorted.length - 1; i++) { mschaefer@9202: final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); mschaefer@9259: final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.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@9269: //// Create a finder for Q in the {river}.wst km-w-q table mschaefer@9269: // final WQBaseTableFinder wqFinder = WQBaseTableFinder.loadValues(this.riverInfoProvider.getRiver(), mschaefer@9269: //// calcRange.getMinimumDouble(), mschaefer@9269: // calcRange.getMaximumDouble(), problems); mschaefer@9269: // mschaefer@9269: //// Calculate the durations and create the result rows mschaefer@9269: // for (int i = 0; i <= stationsSorted.length - 1; i++) { mschaefer@9269: // final Gauge gauge = this.riverInfoProvider.getGauge(stationsSorted[i], true); mschaefer@9269: // final ResultRow row = createRow(stationsSorted[i], gauge, firstGauge, wqkmsArray, durFinders.get(gauge), i); mschaefer@9269: // if (allStations.containsKey(stationsSorted[i]) && (allStations.get(stationsSorted[i]) != null)) mschaefer@9269: // calculateInfrastructure(row, gauge, allStations.get(stationsSorted[i]), wqFinder, durFinders); mschaefer@9269: // this.rows.add(row); mschaefer@9269: // if (secondBank.containsKey(stationsSorted[i])) { mschaefer@9269: // final ResultRow row2 = ResultRow.create(row); mschaefer@9269: // calculateInfrastructure(row2, gauge, secondBank.get(stationsSorted[i]), wqFinder, durFinders); mschaefer@9269: // this.rows.add(row2); mschaefer@9269: // } mschaefer@9269: // } mschaefer@9269: mschaefer@9259: final String[] mainValueLabels = new String[wqkmsArray.length]; mschaefer@9259: for (int i = 0; i <= wqkmsArray.length - 1; i++) mschaefer@9259: mainValueLabels[i] = wqkmsArray[i].getName(); mschaefer@9252: results.addResult(new FloodDurationCalculationResult(label, mainValueLabels, this.rows), problems); mschaefer@9252: } mschaefer@9202: mschaefer@9257: /** mschaefer@9266: * Calculates the duration curve for a station mschaefer@9257: */ mschaefer@9266: public WQDay calcWQDays(final Calculation problems, final double station, final WINFOArtifact winfo) { mschaefer@9252: mschaefer@9257: // Same processing as in W-Info DurationCurveState mschaefer@9252: winfo.addStringData("ld_locations", Double.toString(station)); mschaefer@9252: final CalculationResult res = winfo.getDurationCurveData(); mschaefer@9252: final WQDay underflow = (WQDay) res.getData(); mschaefer@9252: // Transform underflow days into overflow days and re-sort mschaefer@9252: final int[] days = new int[underflow.getWs().length]; mschaefer@9252: final double[] ws = new double[days.length]; mschaefer@9252: final double[] qs = new double[days.length]; mschaefer@9252: for (int i = 0, j = days.length - 1; i <= days.length - 1; i++, j--) { mschaefer@9252: days[j] = 365 - underflow.getDay(i); mschaefer@9252: ws[j] = underflow.getW(i); mschaefer@9252: qs[j] = underflow.getQ(i); mschaefer@9252: } mschaefer@9266: return new WQDay(days, ws, qs); 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@9269: for (final ResultRow row : result.getRows()) { mschaefer@9269: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9269: break; mschaefer@9269: } mschaefer@9269: } mschaefer@9269: final List stationRows = searchStation(station1, result.getRows()); mschaefer@9269: if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) { 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@9269: annotation.setHitPoint(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@9269: for (final ResultRow row : result.getRows()) { mschaefer@9269: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9269: break; mschaefer@9269: } mschaefer@9269: } mschaefer@9269: final List stationRows = searchStation(station1, result.getRows()); mschaefer@9269: if (stationRows.isEmpty() || (stationRows.get(0).getValue(SInfoResultType.infrastructuretype) == null)) { 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@9269: annotation.setHitPoint(wqd.getFloodDurDaysPerYear()); mschaefer@9269: annotations.add(annotation); mschaefer@9257: } mschaefer@9269: return annotations; mschaefer@9257: } mschaefer@9257: mschaefer@9257: /** mschaefer@9259: * Calculate the data for the W and Q lines in the duration curve chart for the infrastructure height and add to result mschaefer@9259: * collection mschaefer@9257: */ mschaefer@9266: public List calcInfrastructureAnnotations(final Calculation problems, final double station, mschaefer@9266: 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@9266: station1 = row.getDoubleValue(GeneralResultType.station); mschaefer@9266: break; mschaefer@9266: } mschaefer@9266: } mschaefer@9266: final List stationRows = searchStation(station1, 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@9269: annotations.add(calcInfrastructureWAnnotation(row)); mschaefer@9269: 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@9266: private List searchStation(final double station, 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@9266: else if (row.getDoubleValue(GeneralResultType.station) > station - 0.0001) 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", mschaefer@9259: SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) mschaefer@9259: + ", " + SInfoResultType.riverside.exportValue(this.context, row.getValue(SInfoResultType.riverside))); mschaefer@9266: final StickyAxisAnnotation annotation = new StickyAxisAnnotation(label, (float) row.getDoubleValue(SInfoResultType.floodDischarge), mschaefer@9259: SimpleAxis.Y_AXIS, 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", mschaefer@9266: SInfoResultType.infrastructuretype.exportValue(this.context, row.getValue(SInfoResultType.infrastructuretype)) mschaefer@9266: + ", " + 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@9202: private WQKms[] calculateWaterlevels(final WINFOArtifact winfo, final double[] stations, final Calculation problems) { mschaefer@9202: // REMARK aus TkhCalculation - move to WinfoArtifactWrapper? mschaefer@9202: // TODO das ist ziemlich langsam - durch den WQBaseTableFinder ersetzen? (vorher W-Optionen in Q umrechnen) 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@9202: final CalculationResult waterlevelData = winfo.computeWaterlevelData(stations); mschaefer@9202: mschaefer@9202: /* copy all problems */ mschaefer@9202: final Calculation winfoProblems = waterlevelData.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@9202: return (WQKms[]) waterlevelData.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@9259: private void updateMainValueLabels(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@9259: * mschaefer@9259: * @param wqkms mschaefer@9259: * @param descBuilder mschaefer@9259: * @return mschaefer@9259: */ mschaefer@9259: private String buildWQDescription(final WQKms wqkms, final WINFOArtifact winfo) { mschaefer@9259: final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context); 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; mschaefer@9259: } mschaefer@9259: else mschaefer@9259: return description; mschaefer@9202: } mschaefer@9202: mschaefer@9202: /** mschaefer@9202: * Create a result row for a station and its gauge, and add w-q-values as selected mschaefer@9202: */ mschaefer@9259: private ResultRow createRow(final Double station, final Gauge gauge, final Gauge firstGauge, final WQKms[] wqkmsArray, mschaefer@9259: final GaugeDurationValuesFinder durationFinder, 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@9229: final String gaugeLabel = this.riverInfoProvider.findGauge(station, (gauge == firstGauge)); gernotbelger@9205: row.putValue(SInfoResultType.gaugeLabel, gaugeLabel); gernotbelger@9205: mschaefer@9202: final String location = this.riverInfoProvider.getLocation(station); mschaefer@9202: row.putValue(SInfoResultType.location, location); gernotbelger@9145: gernotbelger@9205: final List waterlevels = new ArrayList<>(wqkmsArray.length); gernotbelger@9205: mschaefer@9259: for (final WQKms wqKms : wqkmsArray) { mschaefer@9259: assert (wqKms.getKm(kmIndex) == station.doubleValue()); gernotbelger@9205: mschaefer@9259: final int overflowDays = (int) Math.round(underflowDaysToOverflowDays(durationFinder.getDuration(wqKms.getQ(kmIndex)))); gernotbelger@9205: mschaefer@9259: final DurationWaterlevel dw = new DurationWaterlevel(wqKms.getW(kmIndex), overflowDays, wqKms.getQ(kmIndex), wqKms.getName()); gernotbelger@9205: waterlevels.add(dw); mschaefer@9202: } gernotbelger@9205: row.putValue(SInfoResultType.customMultiRowColWaterlevel, waterlevels); gernotbelger@9205: mschaefer@9202: return row; mschaefer@9202: } mschaefer@9202: mschaefer@9269: /// ** mschaefer@9269: // * Calculate the result row fields for one infrastructure - gives wrong duration numbers where Q changes within the mschaefer@9269: /// gauge range mschaefer@9269: // */ mschaefer@9269: // private void calculateInfrastructure(final ResultRow row, final Gauge gauge, final InfrastructureValue mschaefer@9269: /// infrastructure, final WQBaseTableFinder wqFinder, mschaefer@9269: // final Map durFinders) { mschaefer@9269: // mschaefer@9269: // final double q = wqFinder.getDischarge(infrastructure.getStation(), infrastructure.getHeight()); mschaefer@9269: // final double qOut = Double.isInfinite(q) ? Double.NaN : q; mschaefer@9269: // final double dur = underflowDaysToOverflowDays(durFinders.get(gauge).getDuration(q)); mschaefer@9269: // row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey()); mschaefer@9269: // row.putValue(SInfoResultType.floodDuration, dur); mschaefer@9269: // row.putValue(SInfoResultType.floodDischarge, qOut); mschaefer@9269: // row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); mschaefer@9269: // row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); mschaefer@9269: // } mschaefer@9269: 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@9176: final double qOut = Double.isInfinite(q) ? Double.NaN : q; mschaefer@9269: // Determine the relative column position of the Q mschaefer@9269: final QPosition qPos = wst.getQPosition(infrastructure.getStation().doubleValue(), q); 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@9269: // Set result row mschaefer@9229: row.putValue(SInfoResultType.riverside, infrastructure.getAttributeKey()); mschaefer@9202: row.putValue(SInfoResultType.floodDuration, dur); mschaefer@9176: row.putValue(SInfoResultType.floodDischarge, qOut); mschaefer@9176: row.putValue(SInfoResultType.infrastructureHeight, infrastructure.getHeight()); mschaefer@9176: row.putValue(SInfoResultType.infrastructuretype, infrastructure.getInfrastructure().getType().getName()); 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@9202: return 365 - underflowDays; gernotbelger@9145: } gernotbelger@9145: }