diff artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BezugswstCalculation.java @ 9446:e60584f2a531

Added bundu bzws calculation for missing volumes (masses still not yet ready) and results1/2/3
author mschaefer
date Tue, 21 Aug 2018 18:19:35 +0200
parents ecadc9ed0ba0
children d32b11d585cd
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BezugswstCalculation.java	Tue Aug 21 14:16:15 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/bundu/bezugswst/BezugswstCalculation.java	Tue Aug 21 18:19:35 2018 +0200
@@ -17,6 +17,7 @@
 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
 import org.dive4elements.river.artifacts.bundu.BUNDUArtifact;
 import org.dive4elements.river.artifacts.bundu.BunduResultType;
+import org.dive4elements.river.artifacts.common.AbstractResultType;
 import org.dive4elements.river.artifacts.common.GeneralResultType;
 import org.dive4elements.river.artifacts.common.ResultRow;
 import org.dive4elements.river.artifacts.model.Calculation;
@@ -42,11 +43,24 @@
 
     // private static Logger log = Logger.getLogger(BezugswstCalculation.class);
 
+    /**
+     * Additional depth (m) to compute the excavation volume
+     */
+    private final static double EXCAVATION_DEPTH = 0.2; // REMARK Sollte von außen einstellbar sein
+
+    /**
+     * Excavation costs (euro) per cubic meter
+     */
+    private final static double EXPENSE_PER_CBM = 12.0; // REMARK Sollte von außen einstellbar sein
+
     private final CallContext context;
 
+    private final List<ResultRow> rows;
+
 
     public BezugswstCalculation(final CallContext context) {
         this.context = context;
+        this.rows = new ArrayList<>();
     }
 
 
@@ -94,87 +108,40 @@
         if (channelFinder == null)
             return new CalculationResult(results, problems);
 
-        // Calculate the result rows
-        final List<ResultRow> rows = new ArrayList<>();
+        // Compute the result rows
         for (int i = 0; i <= wqkms.size() - 1; i++) {
-            rows.add(createRow(wqkms.getKm(i), wqkms.getW(i), wqkms.getQ(i), bedHeightsFinder, channelFinder, riverInfoProvider, wstInfo));
+            this.rows.add(createRow(wqkms.getKm(i), wqkms.getW(i), wqkms.getQ(i), bedHeightsFinder, channelFinder, riverInfoProvider, wstInfo));
+        }
+
+        // Compute the missing volumes
+        if (access.isCalculateMissingValue()) {
+            computeMissingVolumes(access.getMissingVolFrom().doubleValue(), access.getMissingVolTo().doubleValue(), problems);
+            // TODO Lagerungsdichte holen/berechnen (density) und Massen berechnen
         }
 
         // Add the result to the results collection
         final WaterlevelDescriptionBuilder descBuilder = new WaterlevelDescriptionBuilder(winfo, this.context);
         final String qtext = descBuilder.getMetadataQ();
-        final BezugswstMainCalculationResult result = new BezugswstMainCalculationResult("bundu-bzws", rows, bedHeightsFinder.getInfo(), wstInfo,
+        final BezugswstMainCalculationResult result = new BezugswstMainCalculationResult("bundu-bzws", this.rows, bedHeightsFinder.getInfo(), wstInfo,
                 access.getFunction(), preprocessing, startYear, endYear, ud, qtext, wqkms, missingVolFrom, missingVolTo);
         results.addResult(result, problems);
 
-        // // missing volume calculation
-        // if (access.getMissingVolFrom() != null) {
-        // /// FIRST RESULT
-        // final List<ResultRow> listResult1 = new ArrayList<>();
-        // final ResultRow rowResult1 = ResultRow.create();
-        // rowResult1.putValue(BunduResultType.bezugswst, 45.15);
-        // rowResult1.putValue(GeneralResultType.dischargeQwithUnit, 890);
-        // rowResult1.putValue(GeneralResultType.waterlevelLabel, "GLQ");
-        // rowResult1.putValue(GeneralResultType.gaugeLabel, "Bonn");
-        //
-        // rowResult1.putValue(BunduResultType.sounding, "NIEDERRHEIN_QP-2002");
-        // rowResult1.putValue(BunduResultType.channelLowerEdge, 42.65);
-        // rowResult1.putValue(BunduResultType.channelMinDepth, 2.5);
-        // rowResult1.putValue(BunduResultType.hasMissingDepth, Resources.getMsg(meta, "true"));
-        // rowResult1.putValue(BunduResultType.missVolume, 2250);
-        // rowResult1.putValue(BunduResultType.missMass, 3897);
-        // rowResult1.putValue(BunduResultType.excavationVolume, 2475);
-        // rowResult1.putValue(BunduResultType.excavationCosts, 999.99);
-        // rowResult1.putValue(BunduResultType.channelWidth, 150);
-        // rowResult1.putValue(BunduResultType.density, 1732);
-        //
-        // rowResult1.putValue(GeneralResultType.location, "Spitzenlage");
-        // listResult1.add(rowResult1);
-        //
-        // final BezugswstMissVolCalculationResult1 r1 = new BezugswstMissVolCalculationResult1(
-        // Resources.getMsg(meta, "bundu.export.csv.title.bezugswst.result1"), listResult1);
-        // results.addResult(r1, null);
-        //
-        // // SECOND RESULT
-        // final List<ResultRow> listResult2 = new ArrayList<>();
-        // final ResultRow rowResult2 = ResultRow.create();
-        //
-        // rowResult2.putValue(GeneralResultType.station, 890);
-        // final List<String> fieldValues = new ArrayList<>();
-        // fieldValues.add("444 [m³] / 765 [t]");
-        // fieldValues.add("4.444 [m³] / 1.765 [t]");
-        // fieldValues.add("444 [m³] / 765 [t]");
-        // fieldValues.add("");
-        // fieldValues.add("");
-        // fieldValues.add("");
-        // fieldValues.add("");
-        // fieldValues.add("");
-        // fieldValues.add("");
-        // fieldValues.add("444 [m³] / 765 [t]");
-        // rowResult2.putValue(BunduResultType.fields, fieldValues);
-        // rowResult2.putValue(BunduResultType.meanBedheight, "9.444 [m³] / 8.765 [t]");
-        //
-        // listResult2.add(rowResult2);
-        //
-        // final BezugswstMissVolCalculationResult2 r2 = new BezugswstMissVolCalculationResult2(
-        // Resources.getMsg(meta, "bundu.export.csv.title.bezugswst.result2"), listResult2);
-        // results.addResult(r2, null);
-        //
-        // // Third RESULT
-        // final List<ResultRow> listResult3 = new ArrayList<>();
-        // final ResultRow rowResult3 = ResultRow.create();
-        //
-        // rowResult3.putValue(BunduResultType.stationForMiss, "650 - 651");
-        // rowResult3.putValue(BunduResultType.missVolume, 52950);
-        // rowResult3.putValue(BunduResultType.missMass, 91491);
-        //
-        // listResult3.add(rowResult3);
-        //
-        // final BezugswstMissVolCalculationResult3 r3 = new BezugswstMissVolCalculationResult3(
-        // Resources.getMsg(meta, "bundu.export.csv.title.bezugswst.result3"), listResult3);
-        // results.addResult(r3, null);
-        //
-        // }
+        // Create the missing volume results
+        if (access.getMissingVolFrom() != null) {
+            final String title1 = Resources.getMsg(this.context.getMeta(), "bundu.export.csv.title.bezugswst.result1");
+            final BezugswstMissVolCalculationResult1 r1 = new BezugswstMissVolCalculationResult1(title1, this.rows);
+            results.addResult(r1, null);
+
+            final String title2 = Resources.getMsg(this.context.getMeta(), "bundu.export.csv.title.bezugswst.result2");
+            final BezugswstMissVolCalculationResult2 r2 = new BezugswstMissVolCalculationResult2(title2, this.rows);
+            results.addResult(r2, null);
+
+            final String title3 = Resources.getMsg(this.context.getMeta(), "bundu.export.csv.title.bezugswst.result3");
+            final List<ResultRow> totalRows = new ArrayList<>();
+            totalRows.add(createTotalsRow(missingVolFrom.doubleValue(), missingVolTo.doubleValue(), problems));
+            final BezugswstMissVolCalculationResult3 r3 = new BezugswstMissVolCalculationResult3(title3, totalRows);
+            results.addResult(r3, null);
+        }
 
         return new CalculationResult(results, problems);
     }
@@ -206,6 +173,7 @@
     private ResultRow createRow(final double station, final double w, final double q, final BedHeightsFinder bedHeightsFinder,
             final ChannelFinder channelFinder, final RiverInfoProvider riverInfoProv, final WstInfo wstInfo) {
 
+        // Set W and Q
         final ResultRow row = ResultRow.create();
         row.putValue(GeneralResultType.station, station);
         row.putValue(BunduResultType.bezugswst, w);
@@ -213,6 +181,8 @@
         row.putValue(GeneralResultType.waterlevelLabel, wstInfo.getLabel());
         row.putValue(GeneralResultType.gaugeLabel, riverInfoProv.findGauge(station));
         row.putValue(GeneralResultType.location, riverInfoProv.getLocation(station));
+
+        // Set bed and channel bottom height
         final double msh = bedHeightsFinder.getMeanBedHeight(station);
         row.putValue(SInfoResultType.meanBedHeight, msh);
         if (!Double.isNaN(w) && !Double.isNaN(msh))
@@ -221,19 +191,180 @@
             row.putValue(SInfoResultType.flowdepth, Double.NaN);
         final double channelDepth = channelFinder.getDepth(station);
         row.putValue(BunduResultType.channelDepth, channelDepth);
+        double channelHeight;
         if (!Double.isNaN(w) && !Double.isNaN(channelDepth))
-            row.putValue(BunduResultType.channelLowerEdge, Formatter.roundFlowDepth(w).subtract(Formatter.roundFlowDepth(channelDepth)).doubleValue());
+            channelHeight = Formatter.roundFlowDepth(w).subtract(Formatter.roundFlowDepth(channelDepth)).doubleValue();
         else
-            row.putValue(BunduResultType.channelLowerEdge, Double.NaN);
+            channelHeight = Double.NaN;
+        row.putValue(BunduResultType.channelLowerEdge, channelHeight);
+        final double channelWidth = channelFinder.getWidth(station);
+        row.putValue(BunduResultType.channelWidth, channelWidth);
+
+        // Set field heights and missing heights
         final List<Double> fieldHeights = new ArrayList<>();
         final List<Double> fieldDepths = new ArrayList<>();
+        final List<Double> fieldMissDepths = new ArrayList<>();
+        final List<Double> fieldMissWidths = new ArrayList<>();
+        int missFieldCnt = 0;
         for (int i = BedHeightValueType.FIELD_FIRST_INDEX; i <= BedHeightValueType.FIELD_LAST_INDEX; i++) {
             final double h = bedHeightsFinder.getFieldHeight(station, i);
             fieldHeights.add(Double.valueOf(h));
-            fieldDepths.add(w - h);
+            fieldDepths.add(Double.valueOf(w - h));
+            if (h > channelHeight + 0.001) {
+                missFieldCnt++;
+                fieldMissDepths.add(Double.valueOf(h - channelHeight));
+                fieldMissWidths.add(Double.valueOf(channelWidth / BedHeightValueType.FIELD_LAST_INDEX));
+            }
+            else {
+                fieldMissDepths.add(Double.valueOf(0.0));
+                fieldMissWidths.add(Double.valueOf(0.0));
+            }
         }
+        row.putValue(BunduResultType.missDepthFields, fieldMissDepths);
+        row.putValue(BunduResultType.missWidthFields, fieldMissWidths);
+        row.putValue(BunduResultType.hasMissingDepth, (missFieldCnt >= 1));
         row.putValue(BunduResultType.bedHeightFields, fieldHeights);
         row.putValue(BunduResultType.depthFields, fieldDepths);
+
+        // Preset the missing volume fields with NaN
+        row.putValue(BunduResultType.excavationCosts, Double.NaN);
+        row.putValue(BunduResultType.excavationVolume, Double.NaN);
+        row.putValue(BunduResultType.missVolumeMeanBed, Double.NaN);
+        row.putValue(BunduResultType.missMassMeanBed, Double.NaN);
+        row.putValue(BunduResultType.missVolumeTotal, Double.NaN);
+        row.putValue(BunduResultType.missMassTotal, Double.NaN);
+        row.putValue(BunduResultType.density, Double.NaN);
+        row.putValue(BunduResultType.missStationRangeFrom, Double.NaN);
+        row.putValue(BunduResultType.missStationRangeTo, Double.NaN);
+
         return row;
     }
+
+    /**
+     * Computes the missing volumes in a km range
+     */
+    private void computeMissingVolumes(final double kmFrom, final double kmTo, final Calculation problems) {
+        // Search start km
+        int first = -1;
+        for (int j = 0; j <= this.rows.size() - 1; j++) {
+            if (this.rows.get(j).getDoubleValue(GeneralResultType.station) > kmFrom - 0.0001) {
+                first = j;
+                break;
+            }
+        }
+        if (first < 0)
+            return;
+        int last = this.rows.size() - 1;
+        int i = first;
+        while (i <= this.rows.size() - 1) {
+            if (this.rows.get(i).getDoubleValue(GeneralResultType.station) > kmTo + 0.0001)
+                break;
+            if (this.rows.get(i).getDoubleValue(GeneralResultType.station) > kmTo - 0.0001)
+                last = i;
+            final List<Double> areas = new ArrayList<>();
+            final List<Double> volumes = new ArrayList<>();
+            double vTotal = 0.0;
+            double vExcav = 0.0;
+            double expenses = 0.0;
+            for (int j = BedHeightValueType.FIELD_FIRST_INDEX; j <= BedHeightValueType.FIELD_LAST_INDEX; j++) {
+                if (getFieldValue(i, BunduResultType.missDepthFields, j) > 0.0001) {
+                    computeMissingVolume(volumes, areas, i, first, last, j);
+                    vTotal += volumes.get(j - 1);
+                    vExcav += volumes.get(j - 1) + areas.get(j - 1) * EXCAVATION_DEPTH;
+                    expenses += vExcav * EXPENSE_PER_CBM;
+                }
+                else {
+                    volumes.add(Double.valueOf(0.0));
+                    areas.add(Double.valueOf(0.0));
+                }
+            }
+            this.rows.get(i).putValue(BunduResultType.missVolumeFields, volumes);
+            this.rows.get(i).putValue(BunduResultType.missAreaFields, areas);
+            this.rows.get(i).putValue(BunduResultType.missVolumeTotal, vTotal);
+            this.rows.get(i).putValue(BunduResultType.excavationVolume, vExcav);
+            this.rows.get(i).putValue(BunduResultType.excavationCosts, expenses);
+            i++;
+        }
+    }
+
+    /**
+     * Computes the missing volume of a field of a km row
+     */
+    private void computeMissingVolume(final List<Double> volumes, final List<Double> areas, final int current, final int first, final int last,
+            final int field) {
+        final double areaCurr = missingArea(current, first, last, field);
+        final double areaPrev = missingArea(current - 1, first, last, field);
+        final double areaNext = missingArea(current + 1, first, last, field);
+        final double kmCurr = missingKm(current);
+        final double kmPrev = missingKm(current - 1);
+        final double kmNext = missingKm(current + 1);
+        if (Double.isNaN(kmPrev) || Double.isNaN(kmNext)) {
+            volumes.add(Double.valueOf(0.0));
+            areas.add(Double.valueOf(0.0));
+        }
+        else {
+            final double area1 = 0.5 * (areaCurr + areaPrev);
+            final double area2 = 0.5 * (areaCurr + areaNext);
+            volumes.add(Double.valueOf((Math.abs(kmCurr - kmPrev) * 0.5 * area1) + (Math.abs(kmNext - kmCurr) * 0.5 * area2)));
+            areas.add(Double.valueOf(area1 + area2));
+        }
+    }
+
+    /**
+     * Gets the missing area of a field and a row if in range, otherwise 0.0
+     */
+    private double missingArea(final int rowIndex, final int first, final int last, final int fieldIndex) {
+        if ((first <= rowIndex) && (rowIndex <= last))
+            return getFieldValue(rowIndex, BunduResultType.missDepthFields, fieldIndex)
+                    * getFieldValue(rowIndex, BunduResultType.missWidthFields, fieldIndex);
+        else
+            return 0.0;
+    }
+
+    /**
+     * Gets the km of a row if within range, otherwise NaN
+     */
+    private double missingKm(final int rowIndex) {
+        if ((0 <= rowIndex) && (rowIndex <= this.rows.size() - 1))
+            return this.rows.get(rowIndex).getDoubleValue(GeneralResultType.station);
+        else
+            return Double.NaN;
+    }
+
+    /**
+     * Gets a value of one of the field list types of a row
+     *
+     * @param rowIndex
+     * @param type
+     * @param fieldIndex
+     *            1-based field index
+     */
+    private double getFieldValue(final int rowIndex, final AbstractResultType type, final int fieldIndex) {
+        @SuppressWarnings("unchecked")
+        final List<Double> values = (List<Double>) this.rows.get(rowIndex).getValue(type);
+        return values.get(fieldIndex - 1);
+    }
+
+    /**
+     * Computes the volume and mass total of all rows with missing volumes
+     */
+    private ResultRow createTotalsRow(final double kmFrom, final double kmTo, final Calculation problems) {
+        // Search start km
+        double vTotal = 0.0;
+        double mTotal = 0.0;
+        for (final ResultRow row : this.rows) {
+            final double volume = row.getDoubleValue(BunduResultType.missVolumeMeanBed);
+            final double mass = row.getDoubleValue(BunduResultType.missMassMeanBed);
+            if (!Double.isNaN(volume) && !Double.isNaN(mass)) {
+                vTotal += volume;
+                mTotal += mass;
+            }
+        }
+        final ResultRow sumRow = ResultRow.create();
+        sumRow.putValue(BunduResultType.missStationRangeFrom, Double.valueOf(kmFrom));
+        sumRow.putValue(BunduResultType.missStationRangeTo, Double.valueOf(kmTo));
+        sumRow.putValue(BunduResultType.missVolumeTotal, vTotal);
+        sumRow.putValue(BunduResultType.missMassTotal, mTotal);
+        return sumRow;
+    }
 }
\ No newline at end of file

http://dive4elements.wald.intevation.org