diff artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java @ 9486:ce13a2f07290

pdf exports added for fixation+bundu (waterlevel)
author gernotbelger
date Mon, 17 Sep 2018 19:07:57 +0200
parents dd05a5eef210
children aa23225fd85f
line wrap: on
line diff
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Mon Sep 17 16:00:00 2018 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Mon Sep 17 19:07:57 2018 +0200
@@ -24,7 +24,6 @@
 import org.apache.log4j.Logger;
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallMeta;
-import org.dive4elements.artifacts.common.utils.Config;
 import org.dive4elements.river.artifacts.AbstractFixBunduArtifact;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
@@ -32,8 +31,15 @@
 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
 import org.dive4elements.river.artifacts.access.IsOfficialAccess;
 import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.artifacts.common.DefaultCalculationResults;
+import org.dive4elements.river.artifacts.common.ExportContextPDF;
+import org.dive4elements.river.artifacts.common.GeneralResultType;
+import org.dive4elements.river.artifacts.common.JasperDesigner;
+import org.dive4elements.river.artifacts.common.JasperReporter;
+import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.ConstantWQKms;
+import org.dive4elements.river.artifacts.model.DischargeTables;
 import org.dive4elements.river.artifacts.model.Segment;
 import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
 import org.dive4elements.river.artifacts.model.WQCKms;
@@ -41,6 +47,9 @@
 import org.dive4elements.river.artifacts.model.WQKmsResult;
 import org.dive4elements.river.artifacts.model.WstLine;
 import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
+import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
+import org.dive4elements.river.model.DischargeTable;
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.RiverUtils;
@@ -49,9 +58,6 @@
 import au.com.bytecode.opencsv.CSVWriter;
 import gnu.trove.TDoubleArrayList;
 import net.sf.jasperreports.engine.JRException;
-import net.sf.jasperreports.engine.JasperExportManager;
-import net.sf.jasperreports.engine.JasperFillManager;
-import net.sf.jasperreports.engine.JasperPrint;
 
 /**
  * Generates different output formats (wst, csv, pdf) of data that resulted from
@@ -129,7 +135,6 @@
     public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = "außerhalb des gewählten Bezugspegels";
 
     protected static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
-    private static final String JASPER_FILE = "export.waterlevel.pdf.file";
 
     /** The storage that contains all WQKms objects that are calculated. */
     public List<WQKms[]> data;
@@ -137,6 +142,8 @@
     /** The storage that contains official fixings if available. */
     public List<WQKms> officalFixings;
 
+    private final Map<String, Double> gaugeQ_W_Map = new HashMap<>();
+
     public WaterlevelExporter() {
         this.data = new ArrayList<>();
     }
@@ -308,7 +315,7 @@
         }
     }
 
-    protected final boolean isQ() {
+    private final boolean isQ() {
         final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
         return mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
     }
@@ -571,6 +578,16 @@
         return colDesc == null ? "" : colDesc;
     }
 
+    private List<Segment> getSegments(final D4EArtifact flys) {
+        if (flys instanceof AbstractFixBunduArtifact) {
+            // Get W/Q input per gauge for this case.
+            final FixRealizingAccess fixAccess = new FixRealizingAccess(flys);
+            return fixAccess.getSegments();
+
+        }
+        return null;
+    }
+
     /**
      * Write "rows" of csv data from wqkms with writer.
      */
@@ -596,8 +613,6 @@
                                                   // (it seems to)
         String desc = "";
         final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
-        List<Segment> segments = null;
-        boolean isFixRealize = false;
 
         final double a = gauge.getRange().getA().doubleValue();
         final double b = gauge.getRange().getB().doubleValue();
@@ -605,15 +620,11 @@
 
         desc = getDesc(wqkms, isQ);
 
-        if (flys instanceof AbstractFixBunduArtifact) {
-            // Get W/Q input per gauge for this case.
-            final FixRealizingAccess fixAccess = new FixRealizingAccess(flys);
-            segments = fixAccess.getSegments();
-            if (segments != null && !segments.isEmpty()) {
-                isFixRealize = true;
-            }
+        boolean isFixRealize = false;
+        final List<Segment> segments = getSegments(flys);
+        if (segments != null && !segments.isEmpty()) {
+            isFixRealize = true;
         }
-
         if (atGauge) { // "At gauge" needs more output.
 
             // Kms tend to be close together so caching the last sector
@@ -757,27 +768,176 @@
         }
     }
 
-    public void doWritePdf(final WKmsJRDataSource source, final String jasperFile) {
-
-        final String confPath = Config.getConfigDirectory().toString();
+    @Override
+    protected void writePDF(final OutputStream out) {
+        log.debug("write PDF");
 
-        final Map parameters = new HashMap();
-        parameters.put("ReportTitle", "Exported Data");
+        final boolean isQ = isQ();
+        final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();
+        final String jasperFile = !isQ ? "/jasper/templates/waterlevel_new.jrxml" : "/jasper/templates/fix_waterlevel.jrxml";
+
+        ((D4EArtifact) this.master).getData("calculation.mode");
+        if ((this.master instanceof WINFOArtifact)) {
+            addMetaData(source, "calc.surface.curve"); // Wasserspiegellage
+        } else if
+
+        (this.master instanceof AbstractFixBunduArtifact) {
+            addMetaData(source, ((AbstractFixBunduArtifact) this.master).getCalculationModeString());// "calculation.vollmer"); // ausgelagerte
+                                                                                                     // Wasserspiegellage
+            // else
+            // addMetaData(source, "calculation.analysis"); // Fixierungsanalyse
+        }
+
         try {
-            final JasperPrint print = JasperFillManager.fillReport(confPath + jasperFile, parameters, source);
-            JasperExportManager.exportReportToPdfStream(print, this.out);
+            final List<String[]> sorted = getRows(); // Custom Result could be nice, too...
+            for (final String[] list : sorted) {
+                source.addData(list);
+            }
+
+            final JasperReporter reporter = new JasperReporter();
+
+            final JasperDesigner d = reporter.addReport(jasperFile, source);
+            d.removeColumn("delete"); // I don't want to mess with getRows(), so I prefer deleting the unwanted row directly in the report.
+
+            reporter.exportPDF(this.out);
         }
         catch (final JRException je) {
             log.warn("Error generating PDF Report!", je);
         }
+
     }
 
-    @Override
-    protected void writePDF(final OutputStream out) {
-        log.debug("write PDF");
-        final WKmsJRDataSource source = createJRData();
-        final String jasperFile = Resources.getMsg(this.context.getMeta(), JASPER_FILE, "/jasper/waterlevel_en.jasper");
-        doWritePdf(source, jasperFile);
+    private List<String[]> getRows() {
+        final List<String[]> list = new ArrayList<>();
+        final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
+        final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
+        final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
+
+        Double first = Double.NaN;
+        Double last = Double.NaN;
+
+        for (final WQKms[] tmp : this.data) {
+            for (final WQKms wqkms : tmp) {
+                list.addAll(getRows2(wqkms, atGauge, isQ));
+                final double[] firstLast = wqkms.getFirstLastKM();
+                if (first.isNaN()) {
+                    /* Initialize */
+                    first = firstLast[0];
+                    last = firstLast[1];
+                }
+                if (firstLast[0] > firstLast[1]) {
+                    /*
+                     * Calculating upstream we assert that it is
+                     * impossible that the direction changes during this
+                     * loop
+                     */
+                    first = Math.max(first, firstLast[0]);
+                    last = Math.min(last, firstLast[1]);
+                } else if (firstLast[0] < firstLast[1]) {
+                    first = Math.min(first, firstLast[0]);
+                    last = Math.max(last, firstLast[1]);
+                } else {
+                    first = last = firstLast[0];
+                }
+            }
+        }
+
+        /* Append the official fixing at the bottom */
+        for (final WQKms wqkms : this.officalFixings) {
+            list.addAll(getRows2(filterWQKms(wqkms, first, last), atGauge, isQ));
+        }
+        return list;
+    }
+
+    private List<String[]> getRows2(final WQKms wqkms, final boolean atGauge, final boolean isQ) {
+        log.debug("WaterlevelExporter.addWKmsData"); // OLD CODE :-/
+
+        final List<String[]> list = new ArrayList<>();
+        // Skip constant data.
+        if (wqkms instanceof ConstantWQKms) {
+            return null;
+        }
+
+        final NumberFormat kmf = getKmFormatter();
+        final NumberFormat wf = getWFormatter();
+        final NumberFormat qf = getQFormatter();
+
+        final int size = wqkms.size();
+        double[] result = new double[3];
+
+        final D4EArtifact flys = (D4EArtifact) this.master;
+        final RangeAccess rangeAccess = new RangeAccess(flys);
+
+        final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());
+
+        final String gaugeName = gauge.getName();
+        String desc = "";
+        final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
+
+        final double a = gauge.getRange().getA().doubleValue();
+        final double b = gauge.getRange().getB().doubleValue();
+        final WaterlevelDescriptionBuilder wldb = new WaterlevelDescriptionBuilder(flys, this.context);
+
+        desc = wldb.getDesc(wqkms);// class getDesc(wqkms, isQ);
+
+        Segment lastSegment = null;
+        final Gauge lastGauge = null;
+        final List<Segment> segments = getSegments(flys);
+
+        final NumberFormat nf = Formatter.getFormatter(this.context.getMeta(), 0, 0);
+
+        for (int i = 0; i < size; ++i) {
+            result = wqkms.get(i, result);
+            final double km = result[2];
+
+            if (segments != null) {
+                final Segment found = lastSegment != null && lastSegment.inside(km) ? lastSegment : findSegment(km, segments);
+
+                if (found != null) {
+                    desc = nf.format(found.getValues()[0]);
+                }
+                lastSegment = found;
+            }
+
+            final double q = result[1];
+            final String waterlevel = this.getWaterlevel(q, gauge); // THIS IS NEW (and makes common super method
+                                                                    // difficult)
+            if (atGauge) {
+                list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc,
+                        RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
+            } else {
+                list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc,
+                        RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
+            }
+
+        }
+
+        return list;
+    }
+
+    protected final void addMetaData(final MetaAndTableJRDataSource source, final String calculation) {
+        final D4EArtifact flys = (D4EArtifact) this.master;
+        final String user = CalculationUtils.findArtifactUser(this.context, flys);
+        final RangeAccess ra = new RangeAccess(flys);
+        final RiverInfo ri = new RiverInfo(ra.getRiver());
+
+        final DefaultCalculationResults results = new DefaultCalculationResults(msg(calculation), user, ri, ra.getRange());
+        final ExportContextPDF contextPdf = new ExportContextPDF(this.context, results);
+        contextPdf.addJRMetaDataDefaults(source);
+        contextPdf.addJRMetaDataForModules(source);
+
+        /* column headings */
+        contextPdf.addJRMetadata(source, "station_header", GeneralResultType.station);
+        contextPdf.addJRMetadata(source, "fix_w", msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { ri.getWstUnit() }));
+        contextPdf.addJRMetadata(source, "w_at_gauge_header", msg("fix.export.csv.w_at_gauge"));
+        contextPdf.addJRMetadata(source, "fix_q", msg(CSV_Q_HEADER));
+        contextPdf.addJRMetadata(source, "waterlevel_name_header", msg("common.export.csv.header.mainvalue_label"));
+        contextPdf.addJRMetadata(source, "location_header", msg("common.export.csv.header.location"));
+
+        contextPdf.addJRMetadata(source, "w_at_gauge_header_2", msg("export.waterlevel.csv.header.w.desc"));
+
+        contextPdf.addJRMetadata(source, "location_header", msg("common.export.csv.header.location"));
+        contextPdf.addJRMetadata(source, "gauge_header", msg("common.export.csv.header.gauge"));
 
     }
 
@@ -901,5 +1061,33 @@
         }
     }
 
+    protected final String getWaterlevel(final double discharge, final Gauge gauge) {
+        final NumberFormat formatter = Formatter.getWaterlevelW(this.context);
+        final Double waterlevel = this.getWforGaugeAndQ(gauge, discharge);
+        if (waterlevel != null)
+            return formatter.format(waterlevel);
+        return "";
+    }
+
+    private Double getWforGaugeAndQ(final Gauge gauge, final double q) {
+
+        final String key = gauge.getName() + String.valueOf(q);
+        if (!this.gaugeQ_W_Map.containsKey(key)) {
+
+            final DischargeTable dt = gauge.fetchMasterDischargeTable();
+            final double[][] table = DischargeTables.loadDischargeTableValues(dt);
+
+            final double[] qs = DischargeTables.getWsForQ(table, q);
+
+            // final DischargeTables dct = new DischargeTables(gauge.getRiver().getName(), gauge.getName());
+            // final double[] qs = DischargeTables.getWsForQ(dct.getFirstTable(), q); // TODO: KLÄREN, welche Abflusstabelle
+            // genommen werden soll!
+            if (qs != null && qs.length > 0) {
+                this.gaugeQ_W_Map.put(key, qs[0]);
+            }
+        }
+        return this.gaugeQ_W_Map.get(key);
+    }
+
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org