comparison 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
comparison
equal deleted inserted replaced
9485:dd05a5eef210 9486:ce13a2f07290
22 import java.util.regex.Pattern; 22 import java.util.regex.Pattern;
23 23
24 import org.apache.log4j.Logger; 24 import org.apache.log4j.Logger;
25 import org.dive4elements.artifacts.Artifact; 25 import org.dive4elements.artifacts.Artifact;
26 import org.dive4elements.artifacts.CallMeta; 26 import org.dive4elements.artifacts.CallMeta;
27 import org.dive4elements.artifacts.common.utils.Config;
28 import org.dive4elements.river.artifacts.AbstractFixBunduArtifact; 27 import org.dive4elements.river.artifacts.AbstractFixBunduArtifact;
29 import org.dive4elements.river.artifacts.D4EArtifact; 28 import org.dive4elements.river.artifacts.D4EArtifact;
30 import org.dive4elements.river.artifacts.StaticWQKmsArtifact; 29 import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
31 import org.dive4elements.river.artifacts.WINFOArtifact; 30 import org.dive4elements.river.artifacts.WINFOArtifact;
32 import org.dive4elements.river.artifacts.access.FixRealizingAccess; 31 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
33 import org.dive4elements.river.artifacts.access.IsOfficialAccess; 32 import org.dive4elements.river.artifacts.access.IsOfficialAccess;
34 import org.dive4elements.river.artifacts.access.RangeAccess; 33 import org.dive4elements.river.artifacts.access.RangeAccess;
34 import org.dive4elements.river.artifacts.common.DefaultCalculationResults;
35 import org.dive4elements.river.artifacts.common.ExportContextPDF;
36 import org.dive4elements.river.artifacts.common.GeneralResultType;
37 import org.dive4elements.river.artifacts.common.JasperDesigner;
38 import org.dive4elements.river.artifacts.common.JasperReporter;
39 import org.dive4elements.river.artifacts.common.MetaAndTableJRDataSource;
35 import org.dive4elements.river.artifacts.model.CalculationResult; 40 import org.dive4elements.river.artifacts.model.CalculationResult;
36 import org.dive4elements.river.artifacts.model.ConstantWQKms; 41 import org.dive4elements.river.artifacts.model.ConstantWQKms;
42 import org.dive4elements.river.artifacts.model.DischargeTables;
37 import org.dive4elements.river.artifacts.model.Segment; 43 import org.dive4elements.river.artifacts.model.Segment;
38 import org.dive4elements.river.artifacts.model.WKmsJRDataSource; 44 import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
39 import org.dive4elements.river.artifacts.model.WQCKms; 45 import org.dive4elements.river.artifacts.model.WQCKms;
40 import org.dive4elements.river.artifacts.model.WQKms; 46 import org.dive4elements.river.artifacts.model.WQKms;
41 import org.dive4elements.river.artifacts.model.WQKmsResult; 47 import org.dive4elements.river.artifacts.model.WQKmsResult;
42 import org.dive4elements.river.artifacts.model.WstLine; 48 import org.dive4elements.river.artifacts.model.WstLine;
43 import org.dive4elements.river.artifacts.resources.Resources; 49 import org.dive4elements.river.artifacts.resources.Resources;
50 import org.dive4elements.river.artifacts.sinfo.util.CalculationUtils;
51 import org.dive4elements.river.artifacts.sinfo.util.RiverInfo;
52 import org.dive4elements.river.model.DischargeTable;
44 import org.dive4elements.river.model.Gauge; 53 import org.dive4elements.river.model.Gauge;
45 import org.dive4elements.river.utils.Formatter; 54 import org.dive4elements.river.utils.Formatter;
46 import org.dive4elements.river.utils.RiverUtils; 55 import org.dive4elements.river.utils.RiverUtils;
47 import org.dive4elements.river.utils.RiverUtils.WQ_MODE; 56 import org.dive4elements.river.utils.RiverUtils.WQ_MODE;
48 57
49 import au.com.bytecode.opencsv.CSVWriter; 58 import au.com.bytecode.opencsv.CSVWriter;
50 import gnu.trove.TDoubleArrayList; 59 import gnu.trove.TDoubleArrayList;
51 import net.sf.jasperreports.engine.JRException; 60 import net.sf.jasperreports.engine.JRException;
52 import net.sf.jasperreports.engine.JasperExportManager;
53 import net.sf.jasperreports.engine.JasperFillManager;
54 import net.sf.jasperreports.engine.JasperPrint;
55 61
56 /** 62 /**
57 * Generates different output formats (wst, csv, pdf) of data that resulted from 63 * Generates different output formats (wst, csv, pdf) of data that resulted from
58 * a waterlevel computation. 64 * a waterlevel computation.
59 * 65 *
127 public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage"; 133 public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
128 public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel"; 134 public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel";
129 public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = "außerhalb des gewählten Bezugspegels"; 135 public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = "außerhalb des gewählten Bezugspegels";
130 136
131 protected static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode"; 137 protected static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
132 private static final String JASPER_FILE = "export.waterlevel.pdf.file";
133 138
134 /** The storage that contains all WQKms objects that are calculated. */ 139 /** The storage that contains all WQKms objects that are calculated. */
135 public List<WQKms[]> data; 140 public List<WQKms[]> data;
136 141
137 /** The storage that contains official fixings if available. */ 142 /** The storage that contains official fixings if available. */
138 public List<WQKms> officalFixings; 143 public List<WQKms> officalFixings;
144
145 private final Map<String, Double> gaugeQ_W_Map = new HashMap<>();
139 146
140 public WaterlevelExporter() { 147 public WaterlevelExporter() {
141 this.data = new ArrayList<>(); 148 this.data = new ArrayList<>();
142 } 149 }
143 150
306 } else { 313 } else {
307 return "Q=" + nf.format(rawValue); 314 return "Q=" + nf.format(rawValue);
308 } 315 }
309 } 316 }
310 317
311 protected final boolean isQ() { 318 private final boolean isQ() {
312 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master); 319 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
313 return mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; 320 return mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
314 } 321 }
315 322
316 @Override 323 @Override
569 } 576 }
570 577
571 return colDesc == null ? "" : colDesc; 578 return colDesc == null ? "" : colDesc;
572 } 579 }
573 580
581 private List<Segment> getSegments(final D4EArtifact flys) {
582 if (flys instanceof AbstractFixBunduArtifact) {
583 // Get W/Q input per gauge for this case.
584 final FixRealizingAccess fixAccess = new FixRealizingAccess(flys);
585 return fixAccess.getSegments();
586
587 }
588 return null;
589 }
590
574 /** 591 /**
575 * Write "rows" of csv data from wqkms with writer. 592 * Write "rows" of csv data from wqkms with writer.
576 */ 593 */
577 protected void wQKms2CSV(final CSVWriter writer, final WQKms wqkms, final boolean atGauge, final boolean isQ) { 594 protected void wQKms2CSV(final CSVWriter writer, final WQKms wqkms, final boolean atGauge, final boolean isQ) {
578 log.debug("WaterlevelExporter.wQKms2CSV"); 595 log.debug("WaterlevelExporter.wQKms2CSV");
594 611
595 final String gaugeName = gauge.getName(); // RANDOMLY CHRASHES HERE, gauge is randomly null (especially on heavy load) - on production system as well 612 final String gaugeName = gauge.getName(); // RANDOMLY CHRASHES HERE, gauge is randomly null (especially on heavy load) - on production system as well
596 // (it seems to) 613 // (it seems to)
597 String desc = ""; 614 String desc = "";
598 final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE); 615 final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
599 List<Segment> segments = null;
600 boolean isFixRealize = false;
601 616
602 final double a = gauge.getRange().getA().doubleValue(); 617 final double a = gauge.getRange().getA().doubleValue();
603 final double b = gauge.getRange().getB().doubleValue(); 618 final double b = gauge.getRange().getB().doubleValue();
604 final long startTime = System.currentTimeMillis(); 619 final long startTime = System.currentTimeMillis();
605 620
606 desc = getDesc(wqkms, isQ); 621 desc = getDesc(wqkms, isQ);
607 622
608 if (flys instanceof AbstractFixBunduArtifact) { 623 boolean isFixRealize = false;
609 // Get W/Q input per gauge for this case. 624 final List<Segment> segments = getSegments(flys);
610 final FixRealizingAccess fixAccess = new FixRealizingAccess(flys); 625 if (segments != null && !segments.isEmpty()) {
611 segments = fixAccess.getSegments(); 626 isFixRealize = true;
612 if (segments != null && !segments.isEmpty()) { 627 }
613 isFixRealize = true;
614 }
615 }
616
617 if (atGauge) { // "At gauge" needs more output. 628 if (atGauge) { // "At gauge" needs more output.
618 629
619 // Kms tend to be close together so caching the last sector 630 // Kms tend to be close together so caching the last sector
620 // is a good time saving heuristic. 631 // is a good time saving heuristic.
621 Segment lastSegment = null; 632 Segment lastSegment = null;
755 } else { 766 } else {
756 writer.addColumn(wqkms.getName()); 767 writer.addColumn(wqkms.getName());
757 } 768 }
758 } 769 }
759 770
760 public void doWritePdf(final WKmsJRDataSource source, final String jasperFile) {
761
762 final String confPath = Config.getConfigDirectory().toString();
763
764 final Map parameters = new HashMap();
765 parameters.put("ReportTitle", "Exported Data");
766 try {
767 final JasperPrint print = JasperFillManager.fillReport(confPath + jasperFile, parameters, source);
768 JasperExportManager.exportReportToPdfStream(print, this.out);
769 }
770 catch (final JRException je) {
771 log.warn("Error generating PDF Report!", je);
772 }
773 }
774
775 @Override 771 @Override
776 protected void writePDF(final OutputStream out) { 772 protected void writePDF(final OutputStream out) {
777 log.debug("write PDF"); 773 log.debug("write PDF");
778 final WKmsJRDataSource source = createJRData(); 774
779 final String jasperFile = Resources.getMsg(this.context.getMeta(), JASPER_FILE, "/jasper/waterlevel_en.jasper"); 775 final boolean isQ = isQ();
780 doWritePdf(source, jasperFile); 776 final MetaAndTableJRDataSource source = new MetaAndTableJRDataSource();
781 777 final String jasperFile = !isQ ? "/jasper/templates/waterlevel_new.jrxml" : "/jasper/templates/fix_waterlevel.jrxml";
782 } 778
783 779 ((D4EArtifact) this.master).getData("calculation.mode");
784 private WKmsJRDataSource createJRData() { 780 if ((this.master instanceof WINFOArtifact)) {
785 final WKmsJRDataSource source = new WKmsJRDataSource(); 781 addMetaData(source, "calc.surface.curve"); // Wasserspiegellage
786 782 } else if
783
784 (this.master instanceof AbstractFixBunduArtifact) {
785 addMetaData(source, ((AbstractFixBunduArtifact) this.master).getCalculationModeString());// "calculation.vollmer"); // ausgelagerte
786 // Wasserspiegellage
787 // else
788 // addMetaData(source, "calculation.analysis"); // Fixierungsanalyse
789 }
790
791 try {
792 final List<String[]> sorted = getRows(); // Custom Result could be nice, too...
793 for (final String[] list : sorted) {
794 source.addData(list);
795 }
796
797 final JasperReporter reporter = new JasperReporter();
798
799 final JasperDesigner d = reporter.addReport(jasperFile, source);
800 d.removeColumn("delete"); // I don't want to mess with getRows(), so I prefer deleting the unwanted row directly in the report.
801
802 reporter.exportPDF(this.out);
803 }
804 catch (final JRException je) {
805 log.warn("Error generating PDF Report!", je);
806 }
807
808 }
809
810 private List<String[]> getRows() {
811 final List<String[]> list = new ArrayList<>();
787 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master); 812 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
788 final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; 813 final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
789 final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; 814 final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
790 815
791 Double first = Double.NaN; 816 Double first = Double.NaN;
792 Double last = Double.NaN; 817 Double last = Double.NaN;
793 818
794 addMetaData(source);
795 for (final WQKms[] tmp : this.data) { 819 for (final WQKms[] tmp : this.data) {
796 for (final WQKms wqkms : tmp) { 820 for (final WQKms wqkms : tmp) {
797 addWKmsData(wqkms, atGauge, isQ, source); 821 list.addAll(getRows2(wqkms, atGauge, isQ));
798 final double[] firstLast = wqkms.getFirstLastKM(); 822 final double[] firstLast = wqkms.getFirstLastKM();
799 if (first.isNaN()) { 823 if (first.isNaN()) {
800 /* Initialize */ 824 /* Initialize */
801 first = firstLast[0]; 825 first = firstLast[0];
802 last = firstLast[1]; 826 last = firstLast[1];
818 } 842 }
819 } 843 }
820 844
821 /* Append the official fixing at the bottom */ 845 /* Append the official fixing at the bottom */
822 for (final WQKms wqkms : this.officalFixings) { 846 for (final WQKms wqkms : this.officalFixings) {
847 list.addAll(getRows2(filterWQKms(wqkms, first, last), atGauge, isQ));
848 }
849 return list;
850 }
851
852 private List<String[]> getRows2(final WQKms wqkms, final boolean atGauge, final boolean isQ) {
853 log.debug("WaterlevelExporter.addWKmsData"); // OLD CODE :-/
854
855 final List<String[]> list = new ArrayList<>();
856 // Skip constant data.
857 if (wqkms instanceof ConstantWQKms) {
858 return null;
859 }
860
861 final NumberFormat kmf = getKmFormatter();
862 final NumberFormat wf = getWFormatter();
863 final NumberFormat qf = getQFormatter();
864
865 final int size = wqkms.size();
866 double[] result = new double[3];
867
868 final D4EArtifact flys = (D4EArtifact) this.master;
869 final RangeAccess rangeAccess = new RangeAccess(flys);
870
871 final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());
872
873 final String gaugeName = gauge.getName();
874 String desc = "";
875 final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
876
877 final double a = gauge.getRange().getA().doubleValue();
878 final double b = gauge.getRange().getB().doubleValue();
879 final WaterlevelDescriptionBuilder wldb = new WaterlevelDescriptionBuilder(flys, this.context);
880
881 desc = wldb.getDesc(wqkms);// class getDesc(wqkms, isQ);
882
883 Segment lastSegment = null;
884 final Gauge lastGauge = null;
885 final List<Segment> segments = getSegments(flys);
886
887 final NumberFormat nf = Formatter.getFormatter(this.context.getMeta(), 0, 0);
888
889 for (int i = 0; i < size; ++i) {
890 result = wqkms.get(i, result);
891 final double km = result[2];
892
893 if (segments != null) {
894 final Segment found = lastSegment != null && lastSegment.inside(km) ? lastSegment : findSegment(km, segments);
895
896 if (found != null) {
897 desc = nf.format(found.getValues()[0]);
898 }
899 lastSegment = found;
900 }
901
902 final double q = result[1];
903 final String waterlevel = this.getWaterlevel(q, gauge); // THIS IS NEW (and makes common super method
904 // difficult)
905 if (atGauge) {
906 list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc,
907 RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
908 } else {
909 list.add(new String[] { kmf.format(result[2]), wf.format(result[0]), waterlevel, qf.format(RiverUtils.roundQ(result[1])), desc,
910 RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
911 }
912
913 }
914
915 return list;
916 }
917
918 protected final void addMetaData(final MetaAndTableJRDataSource source, final String calculation) {
919 final D4EArtifact flys = (D4EArtifact) this.master;
920 final String user = CalculationUtils.findArtifactUser(this.context, flys);
921 final RangeAccess ra = new RangeAccess(flys);
922 final RiverInfo ri = new RiverInfo(ra.getRiver());
923
924 final DefaultCalculationResults results = new DefaultCalculationResults(msg(calculation), user, ri, ra.getRange());
925 final ExportContextPDF contextPdf = new ExportContextPDF(this.context, results);
926 contextPdf.addJRMetaDataDefaults(source);
927 contextPdf.addJRMetaDataForModules(source);
928
929 /* column headings */
930 contextPdf.addJRMetadata(source, "station_header", GeneralResultType.station);
931 contextPdf.addJRMetadata(source, "fix_w", msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { ri.getWstUnit() }));
932 contextPdf.addJRMetadata(source, "w_at_gauge_header", msg("fix.export.csv.w_at_gauge"));
933 contextPdf.addJRMetadata(source, "fix_q", msg(CSV_Q_HEADER));
934 contextPdf.addJRMetadata(source, "waterlevel_name_header", msg("common.export.csv.header.mainvalue_label"));
935 contextPdf.addJRMetadata(source, "location_header", msg("common.export.csv.header.location"));
936
937 contextPdf.addJRMetadata(source, "w_at_gauge_header_2", msg("export.waterlevel.csv.header.w.desc"));
938
939 contextPdf.addJRMetadata(source, "location_header", msg("common.export.csv.header.location"));
940 contextPdf.addJRMetadata(source, "gauge_header", msg("common.export.csv.header.gauge"));
941
942 }
943
944 private WKmsJRDataSource createJRData() {
945 final WKmsJRDataSource source = new WKmsJRDataSource();
946
947 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
948 final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
949 final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
950
951 Double first = Double.NaN;
952 Double last = Double.NaN;
953
954 addMetaData(source);
955 for (final WQKms[] tmp : this.data) {
956 for (final WQKms wqkms : tmp) {
957 addWKmsData(wqkms, atGauge, isQ, source);
958 final double[] firstLast = wqkms.getFirstLastKM();
959 if (first.isNaN()) {
960 /* Initialize */
961 first = firstLast[0];
962 last = firstLast[1];
963 }
964 if (firstLast[0] > firstLast[1]) {
965 /*
966 * Calculating upstream we assert that it is
967 * impossible that the direction changes during this
968 * loop
969 */
970 first = Math.max(first, firstLast[0]);
971 last = Math.min(last, firstLast[1]);
972 } else if (firstLast[0] < firstLast[1]) {
973 first = Math.min(first, firstLast[0]);
974 last = Math.max(last, firstLast[1]);
975 } else {
976 first = last = firstLast[0];
977 }
978 }
979 }
980
981 /* Append the official fixing at the bottom */
982 for (final WQKms wqkms : this.officalFixings) {
823 addWKmsData(filterWQKms(wqkms, first, last), atGauge, isQ, source); 983 addWKmsData(filterWQKms(wqkms, first, last), atGauge, isQ, source);
824 } 984 }
825 return source; 985 return source;
826 } 986 }
827 987
899 if (log.isDebugEnabled()) { 1059 if (log.isDebugEnabled()) {
900 log.debug("Writing PDF data took " + (stopTime - startTime) / 1000f + " secs."); 1060 log.debug("Writing PDF data took " + (stopTime - startTime) / 1000f + " secs.");
901 } 1061 }
902 } 1062 }
903 1063
1064 protected final String getWaterlevel(final double discharge, final Gauge gauge) {
1065 final NumberFormat formatter = Formatter.getWaterlevelW(this.context);
1066 final Double waterlevel = this.getWforGaugeAndQ(gauge, discharge);
1067 if (waterlevel != null)
1068 return formatter.format(waterlevel);
1069 return "";
1070 }
1071
1072 private Double getWforGaugeAndQ(final Gauge gauge, final double q) {
1073
1074 final String key = gauge.getName() + String.valueOf(q);
1075 if (!this.gaugeQ_W_Map.containsKey(key)) {
1076
1077 final DischargeTable dt = gauge.fetchMasterDischargeTable();
1078 final double[][] table = DischargeTables.loadDischargeTableValues(dt);
1079
1080 final double[] qs = DischargeTables.getWsForQ(table, q);
1081
1082 // final DischargeTables dct = new DischargeTables(gauge.getRiver().getName(), gauge.getName());
1083 // final double[] qs = DischargeTables.getWsForQ(dct.getFirstTable(), q); // TODO: KLÄREN, welche Abflusstabelle
1084 // genommen werden soll!
1085 if (qs != null && qs.length > 0) {
1086 this.gaugeQ_W_Map.put(key, qs[0]);
1087 }
1088 }
1089 return this.gaugeQ_W_Map.get(key);
1090 }
1091
904 } 1092 }
905 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : 1093 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org