comparison artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java @ 9082:42c15e2f95fb

2.3.4.1.4 Wasserstand falsche Einheit FixAnalysis PDF start
author gernotbelger
date Fri, 25 May 2018 10:44:24 +0200
parents 5294114b1df4
children 740d65e4aa14
comparison
equal deleted inserted replaced
9081:a0feac6ea1ba 9082:42c15e2f95fb
11 import java.io.IOException; 11 import java.io.IOException;
12 import java.io.OutputStream; 12 import java.io.OutputStream;
13 import java.text.DateFormat; 13 import java.text.DateFormat;
14 import java.text.NumberFormat; 14 import java.text.NumberFormat;
15 import java.util.ArrayList; 15 import java.util.ArrayList;
16 import java.util.Map; 16 import java.util.Date;
17 import java.util.HashMap; 17 import java.util.HashMap;
18 import java.util.Date;
19 import java.util.List; 18 import java.util.List;
20 import java.util.Locale; 19 import java.util.Locale;
20 import java.util.Map;
21 import java.util.regex.Matcher; 21 import java.util.regex.Matcher;
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;
26 import org.dive4elements.artifacts.CallMeta;
27 import org.dive4elements.artifacts.common.utils.Config;
28 import org.dive4elements.river.artifacts.D4EArtifact;
29 import org.dive4elements.river.artifacts.FixationArtifact;
30 import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
31 import org.dive4elements.river.artifacts.WINFOArtifact;
32 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
33 import org.dive4elements.river.artifacts.access.IsOfficialAccess;
34 import org.dive4elements.river.artifacts.access.RangeAccess;
35 import org.dive4elements.river.artifacts.model.CalculationResult;
36 import org.dive4elements.river.artifacts.model.ConstantWQKms;
37 import org.dive4elements.river.artifacts.model.Segment;
38 import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
39 import org.dive4elements.river.artifacts.model.WQCKms;
40 import org.dive4elements.river.artifacts.model.WQKms;
41 import org.dive4elements.river.artifacts.model.WQKmsResult;
42 import org.dive4elements.river.artifacts.model.WstLine;
43 import org.dive4elements.river.artifacts.resources.Resources;
44 import org.dive4elements.river.model.Gauge;
45 import org.dive4elements.river.utils.Formatter;
46 import org.dive4elements.river.utils.RiverUtils;
47 import org.dive4elements.river.utils.RiverUtils.WQ_MODE;
25 48
26 import au.com.bytecode.opencsv.CSVWriter; 49 import au.com.bytecode.opencsv.CSVWriter;
27
28 import gnu.trove.TDoubleArrayList; 50 import gnu.trove.TDoubleArrayList;
29 51 import net.sf.jasperreports.engine.JRException;
30 import org.dive4elements.river.artifacts.model.ConstantWQKms;
31
32 import net.sf.jasperreports.engine.JasperExportManager; 52 import net.sf.jasperreports.engine.JasperExportManager;
33 import net.sf.jasperreports.engine.JasperFillManager; 53 import net.sf.jasperreports.engine.JasperFillManager;
34 import net.sf.jasperreports.engine.JasperPrint; 54 import net.sf.jasperreports.engine.JasperPrint;
35 import net.sf.jasperreports.engine.JRException;
36
37 import org.dive4elements.artifacts.Artifact;
38 import org.dive4elements.artifacts.CallMeta;
39 import org.dive4elements.artifacts.common.utils.Config;
40
41 import org.dive4elements.river.model.Gauge;
42
43 import org.dive4elements.river.artifacts.access.FixRealizingAccess;
44 import org.dive4elements.river.artifacts.access.IsOfficialAccess;
45 import org.dive4elements.river.artifacts.access.RangeAccess;
46 import org.dive4elements.river.artifacts.FixationArtifact;
47 import org.dive4elements.river.artifacts.D4EArtifact;
48 import org.dive4elements.river.artifacts.WINFOArtifact;
49 import org.dive4elements.river.artifacts.StaticWQKmsArtifact;
50 import org.dive4elements.river.artifacts.model.CalculationResult;
51 import org.dive4elements.river.artifacts.model.Segment;
52 import org.dive4elements.river.artifacts.model.WQCKms;
53 import org.dive4elements.river.artifacts.model.WQKms;
54 import org.dive4elements.river.artifacts.model.WstLine;
55 import org.dive4elements.river.artifacts.model.WKmsJRDataSource;
56 import org.dive4elements.river.artifacts.model.WQKmsResult;
57 import org.dive4elements.river.artifacts.resources.Resources;
58
59 import org.dive4elements.river.utils.RiverUtils;
60 import org.dive4elements.river.utils.RiverUtils.WQ_MODE;
61 import org.dive4elements.river.utils.Formatter;
62 55
63 /** 56 /**
64 * Generates different output formats (wst, csv, pdf) of data that resulted from 57 * Generates different output formats (wst, csv, pdf) of data that resulted from
65 * a waterlevel computation. 58 * a waterlevel computation.
66 * 59 *
67 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> 60 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
68 */ 61 */
69 public class WaterlevelExporter extends AbstractExporter { 62 public class WaterlevelExporter extends AbstractExporter {
70 63
71 /** The log used in this exporter.*/ 64 /** The log used in this exporter. */
72 private static Logger log = Logger.getLogger(WaterlevelExporter.class); 65 private static Logger log = Logger.getLogger(WaterlevelExporter.class);
73 66
74 public static final String FACET_WST = "wst"; 67 public static final String FACET_WST = "wst";
75 68
76 /* This should be the same as in the StaticWQKmsArtifact */ 69 /* This should be the same as in the StaticWQKmsArtifact */
77 public static final String STATICWQKMSNAME = "staticwqkms"; 70 public static final String STATICWQKMSNAME = "staticwqkms";
78 71
79 public static final String CSV_KM_HEADER = 72 public static final String CSV_KM_HEADER = "export.waterlevel.csv.header.km";
80 "export.waterlevel.csv.header.km"; 73
81 74 public static final String CSV_W_HEADER = "export.waterlevel.csv.header.w";
82 public static final String CSV_W_HEADER = 75
83 "export.waterlevel.csv.header.w"; 76 public static final String CSV_Q_HEADER = "export.waterlevel.csv.header.q";
84
85 public static final String CSV_Q_HEADER =
86 "export.waterlevel.csv.header.q";
87 77
88 /** 78 /**
89 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 79 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
90 */ 80 */
91 @Deprecated 81 @Deprecated
92 public static final String CSV_Q_DESC_HEADER = 82 public static final String CSV_Q_DESC_HEADER = "export.waterlevel.csv.header.q.desc";
93 "export.waterlevel.csv.header.q.desc";
94 83
95 /** 84 /**
96 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 85 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
97 */ 86 */
98 @Deprecated 87 @Deprecated
99 public static final String CSV_W_DESC_HEADER = 88 public static final String CSV_W_DESC_HEADER = "export.waterlevel.csv.header.w.desc";
100 "export.waterlevel.csv.header.w.desc"; 89
101 90 public static final String CSV_LOCATION_HEADER = "export.waterlevel.csv.header.location";
102 public static final String CSV_LOCATION_HEADER = 91
103 "export.waterlevel.csv.header.location"; 92 public static final String CSV_GAUGE_HEADER = "export.waterlevel.csv.header.gauge";
104 93
105 public static final String CSV_GAUGE_HEADER = 94 public static final String CSV_META_RESULT = "export.waterlevel.csv.meta.result";
106 "export.waterlevel.csv.header.gauge"; 95
107 96 public static final String CSV_META_CREATION = "export.waterlevel.csv.meta.creation";
108 public static final String CSV_META_RESULT = 97
109 "export.waterlevel.csv.meta.result"; 98 public static final String CSV_META_CALCULATIONBASE = "export.waterlevel.csv.meta.calculationbase";
110 99
111 public static final String CSV_META_CREATION = 100 public static final String CSV_META_RIVER = "export.waterlevel.csv.meta.river";
112 "export.waterlevel.csv.meta.creation"; 101
113 102 public static final String CSV_META_RANGE = "export.waterlevel.csv.meta.range";
114 public static final String CSV_META_CALCULATIONBASE = 103
115 "export.waterlevel.csv.meta.calculationbase"; 104 public static final String CSV_META_GAUGE = "export.waterlevel.csv.meta.gauge";
116 105
117 public static final String CSV_META_RIVER = 106 public static final String CSV_META_Q = "export.waterlevel.csv.meta.q";
118 "export.waterlevel.csv.meta.river"; 107
119 108 public static final String CSV_META_W = "export.waterlevel.csv.meta.w";
120 public static final String CSV_META_RANGE = 109
121 "export.waterlevel.csv.meta.range"; 110 public static final String CSV_NOT_IN_GAUGE_RANGE = "export.waterlevel.csv.not.in.gauge.range";
122 111
123 public static final String CSV_META_GAUGE = 112 public static final Pattern NUMBERS_PATTERN = Pattern.compile("\\D*(\\d++.\\d*)\\D*");
124 "export.waterlevel.csv.meta.gauge"; 113
125 114 public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
126 public static final String CSV_META_Q = 115 public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]";
127 "export.waterlevel.csv.meta.q"; 116 public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]";
128
129 public static final String CSV_META_W =
130 "export.waterlevel.csv.meta.w";
131
132 public static final String CSV_NOT_IN_GAUGE_RANGE =
133 "export.waterlevel.csv.not.in.gauge.range";
134
135 public static final Pattern NUMBERS_PATTERN =
136 Pattern.compile("\\D*(\\d++.\\d*)\\D*");
137
138 public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
139 public static final String DEFAULT_CSV_W_HEADER = "W [NN + m]";
140 public static final String DEFAULT_CSV_Q_HEADER = "Q [m\u00b3/s]";
141 /** 117 /**
142 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 118 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
143 */ 119 */
144 @Deprecated 120 @Deprecated
145 public static final String DEFAULT_CSV_Q_DESC_HEADER = "Bezeichnung"; 121 public static final String DEFAULT_CSV_Q_DESC_HEADER = "Bezeichnung";
146 /** 122 /**
147 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 123 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
148 */ 124 */
149 @Deprecated 125 @Deprecated
150 public static final String DEFAULT_CSV_W_DESC_HEADER = "W/Pegel [cm]"; 126 public static final String DEFAULT_CSV_W_DESC_HEADER = "W/Pegel [cm]";
151 public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage"; 127 public static final String DEFAULT_CSV_LOCATION_HEADER = "Lage";
152 public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel"; 128 public static final String DEFAULT_CSV_GAUGE_HEADER = "Bezugspegel";
153 public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = 129 public static final String DEFAULT_CSV_NOT_IN_GAUGE_RANGE = "außerhalb des gewählten Bezugspegels";
154 "außerhalb des gewählten Bezugspegels";
155 130
156 public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode"; 131 public static final String PDF_HEADER_MODE = "export.waterlevel.pdf.mode";
157 public static final String JASPER_FILE = "export.waterlevel.pdf.file"; 132 public static final String JASPER_FILE = "export.waterlevel.pdf.file";
158 133
159 /** The storage that contains all WQKms objects that are calculated.*/ 134 /** The storage that contains all WQKms objects that are calculated. */
160 protected List<WQKms[]> data; 135 protected List<WQKms[]> data;
161 136
162 /** The storage that contains official fixings if available.*/ 137 /** The storage that contains official fixings if available. */
163 protected List<WQKms> officalFixings; 138 protected List<WQKms> officalFixings;
164 139
165 public WaterlevelExporter() { 140 public WaterlevelExporter() {
166 data = new ArrayList<WQKms[]>(); 141 this.data = new ArrayList<>();
167 } 142 }
168 143
169 @Override 144 @Override
170 public void generate() 145 public void generate() throws IOException {
171 throws IOException
172 {
173 log.debug("WaterlevelExporter.generate"); 146 log.debug("WaterlevelExporter.generate");
174 147
175 /* Check for official fixings. They should also be included in the 148 /*
176 * export but only the calculation result is added with addData */ 149 * Check for official fixings. They should also be included in the
177 150 * export but only the calculation result is added with addData
178 officalFixings = new ArrayList<WQKms>(); 151 */
179 152
180 for (Artifact art: collection.getArtifactsByName( 153 this.officalFixings = new ArrayList<>();
181 STATICWQKMSNAME, context) 154
182 ) { 155 for (final Artifact art : this.collection.getArtifactsByName(STATICWQKMSNAME, this.context)) {
183 if (art instanceof StaticWQKmsArtifact) { 156 if (art instanceof StaticWQKmsArtifact) {
184 IsOfficialAccess access = 157 final IsOfficialAccess access = new IsOfficialAccess((D4EArtifact) art);
185 new IsOfficialAccess((D4EArtifact)art); 158 final StaticWQKmsArtifact sart = (StaticWQKmsArtifact) art;
186 StaticWQKmsArtifact sart = (StaticWQKmsArtifact) art;
187 if (!access.isOfficial()) { 159 if (!access.isOfficial()) {
188 continue; 160 continue;
189 } 161 }
190 162
191 /* Check that we add the data only once */ 163 /* Check that we add the data only once */
192 WQKms toAdd = sart.getWQKms(); 164 final WQKms toAdd = sart.getWQKms();
193 String newName = toAdd.getName(); 165 final String newName = toAdd.getName();
194 166
195 boolean exists = false; 167 boolean exists = false;
196 for (WQKms wqkm: officalFixings) { 168 for (final WQKms wqkm : this.officalFixings) {
197 /* The same official fixing could be in two 169 /*
198 artifacts/outs so let's deduplicate */ 170 * The same official fixing could be in two
171 * artifacts/outs so let's deduplicate
172 */
199 if (wqkm.getName().equals(newName)) { 173 if (wqkm.getName().equals(newName)) {
200 exists = true; 174 exists = true;
201 } 175 }
202 } 176 }
203 if (!exists) { 177 if (!exists) {
204 officalFixings.add(toAdd); 178 this.officalFixings.add(toAdd);
205 log.debug("Adding additional offical fixing: " + newName); 179 log.debug("Adding additional offical fixing: " + newName);
206 } 180 }
207 } 181 }
208 } 182 }
209 183
210 if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) { 184 if (this.facet != null && this.facet.equals(AbstractExporter.FACET_CSV)) {
211 generateCSV(); 185 generateCSV();
212 } 186 } else if (this.facet != null && this.facet.equals(FACET_WST)) {
213 else if (facet != null && facet.equals(FACET_WST)) {
214 generateWST(); 187 generateWST();
215 } 188 } else if (this.facet != null && this.facet.equals(AbstractExporter.FACET_PDF)) {
216 else if (facet != null && facet.equals(AbstractExporter.FACET_PDF)) {
217 generatePDF(); 189 generatePDF();
218 } 190 } else {
219 else {
220 throw new IOException("invalid facet for exporter"); 191 throw new IOException("invalid facet for exporter");
221 } 192 }
222 } 193 }
223
224 194
225 @Override 195 @Override
226 protected void addData(Object d) { 196 protected void addData(Object d) {
227 if (d instanceof CalculationResult) { 197 if (d instanceof CalculationResult) {
228 d = ((CalculationResult)d).getData(); 198 d = ((CalculationResult) d).getData();
229 if (d instanceof WQKms []) { 199 if (d instanceof WQKms[]) {
230 data.add((WQKms [])d); 200 this.data.add((WQKms[]) d);
231 } 201 } else if (d instanceof WQKmsResult) {
232 else if (d instanceof WQKmsResult) { 202 this.data.add(((WQKmsResult) d).getWQKms());
233 data.add(((WQKmsResult) d).getWQKms()); 203 }
234 } 204 }
235 } 205 }
236 }
237
238 206
239 /** 207 /**
240 * Prepare the column titles of waterlevel exports. 208 * Prepare the column titles of waterlevel exports.
241 * Titles in this export include the Q value. If a Q value matches a named 209 * Titles in this export include the Q value. If a Q value matches a named
242 * main value (as HQ100 or MNQ) this named main value should be used as 210 * main value (as HQ100 or MNQ) this named main value should be used as
243 * title. This method resets the name of the <i>wqkms</i> object if such 211 * title. This method resets the name of the <i>wqkms</i> object if such
244 * named main value fits to the chosen Q. 212 * named main value fits to the chosen Q.
245 * 213 *
246 * @param winfo A WINFO Artifact. 214 * @param winfo
247 * @param wqkms A WQKms object that should be prepared. 215 * A WINFO Artifact.
248 */ 216 * @param wqkms
249 protected String getColumnTitle(WINFOArtifact winfo, WQKms wqkms) { 217 * A WQKms object that should be prepared.
218 */
219 protected String getColumnTitle(final WINFOArtifact winfo, final WQKms wqkms) {
250 log.debug("WaterlevelExporter.getColumnTitle"); 220 log.debug("WaterlevelExporter.getColumnTitle");
251 221
252 String name = wqkms.getName(); 222 final String name = wqkms.getName();
253 223
254 log.debug("Name of WQKms = '" + name + "'"); 224 log.debug("Name of WQKms = '" + name + "'");
255 225
256 if (name.indexOf("W=") >= 0) { 226 if (name.indexOf("W=") >= 0) {
257 return name; 227 return name;
258 } 228 }
259 229
260 Matcher m = NUMBERS_PATTERN.matcher(name); 230 final Matcher m = NUMBERS_PATTERN.matcher(name);
261 231
262 if (m.matches()) { 232 if (m.matches()) {
263 String raw = m.group(1); 233 final String raw = m.group(1);
264 234
265 try { 235 try {
266 double v = Double.valueOf(raw); 236 final double v = Double.valueOf(raw);
267 237
268 String nmv = RiverUtils.getNamedMainValue(winfo, v); 238 String nmv = RiverUtils.getNamedMainValue(winfo, v);
269 239
270 if (nmv != null && nmv.length() > 0) { 240 if (nmv != null && nmv.length() > 0) {
271 nmv = RiverUtils.stripNamedMainValue(nmv); 241 nmv = RiverUtils.stripNamedMainValue(nmv);
272 nmv += "=" + String.valueOf(v); 242 nmv += "=" + String.valueOf(v);
273 log.debug("Set named main value '" + nmv + "'"); 243 log.debug("Set named main value '" + nmv + "'");
274 244
275 return nmv; 245 return nmv;
276 } 246 }
277 } 247 }
278 catch (NumberFormatException nfe) { 248 catch (final NumberFormatException nfe) {
279 // do nothing here 249 // do nothing here
280 } 250 }
281 } 251 }
282 252
283 return name; 253 return name;
284 } 254 }
285 255
286
287 /** 256 /**
288 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 257 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
289 */ 258 */
290 @Deprecated 259 @Deprecated
291 protected String getCSVRowTitle(WINFOArtifact winfo, WQKms wqkms) { 260 protected String getCSVRowTitle(final WINFOArtifact winfo, final WQKms wqkms) {
292 log.debug("WaterlevelExporter.prepareNamedValue"); 261 log.debug("WaterlevelExporter.prepareNamedValue");
293 262
294 String name = wqkms.getName(); 263 final String name = wqkms.getName();
295 264
296 log.debug("Name of WQKms = '" + name + "'"); 265 log.debug("Name of WQKms = '" + name + "'");
297 266
298 WQ_MODE wqmode = RiverUtils.getWQMode(winfo); 267 final WQ_MODE wqmode = RiverUtils.getWQMode(winfo);
299 268
300 if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) { 269 if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.QGAUGE) {
301 return localizeWQKms(winfo, wqkms); 270 return localizeWQKms(winfo, wqkms);
302 } 271 }
303 272
304 Double v = wqkms.getRawValue(); 273 final Double v = wqkms.getRawValue();
305 274
306 String nmv = RiverUtils.getNamedMainValue(winfo, v); 275 String nmv = RiverUtils.getNamedMainValue(winfo, v);
307 276
308 if (nmv != null && nmv.length() > 0) { 277 if (nmv != null && nmv.length() > 0) {
309 nmv = RiverUtils.stripNamedMainValue(nmv); 278 nmv = RiverUtils.stripNamedMainValue(nmv);
313 } 282 }
314 283
315 return localizeWQKms(winfo, wqkms); 284 return localizeWQKms(winfo, wqkms);
316 } 285 }
317 286
318
319 /** 287 /**
320 * Get a string like 'W=' or 'Q=' with a number following in localized 288 * Get a string like 'W=' or 'Q=' with a number following in localized
321 * format. 289 * format.
322 * 290 *
323 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 291 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
324 */ 292 */
325 @Deprecated 293 @Deprecated
326 protected String localizeWQKms(WINFOArtifact winfo, WQKms wqkms) { 294 protected String localizeWQKms(final WINFOArtifact winfo, final WQKms wqkms) {
327 WQ_MODE wqmode = RiverUtils.getWQMode(winfo); 295 final WQ_MODE wqmode = RiverUtils.getWQMode(winfo);
328 Double rawValue = wqkms.getRawValue(); 296 final Double rawValue = wqkms.getRawValue();
329 297
330 if (rawValue == null) { 298 if (rawValue == null) {
331 return wqkms.getName(); 299 return wqkms.getName();
332 } 300 }
333 301
334 NumberFormat nf = Formatter.getRawFormatter(context); 302 final NumberFormat nf = Formatter.getRawFormatter(this.context);
335 303
336 if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) { 304 if (wqmode == WQ_MODE.WFREE || wqmode == WQ_MODE.WGAUGE) {
337 return "W=" + nf.format(rawValue); 305 return "W=" + nf.format(rawValue);
338 } 306 } else {
339 else {
340 return "Q=" + nf.format(rawValue); 307 return "Q=" + nf.format(rawValue);
341 } 308 }
342 } 309 }
343 310
344
345 @Override 311 @Override
346 protected void writeCSVData(CSVWriter writer) { 312 protected void writeCSVData(final CSVWriter writer) {
347 log.info("WaterlevelExporter.writeData"); 313 log.info("WaterlevelExporter.writeData");
348 314
349 WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact)master); 315 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
350 boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; 316 final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
351 boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; 317 final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
352 RiverUtils.WQ_INPUT input 318 final RiverUtils.WQ_INPUT input = RiverUtils.getWQInputMode((D4EArtifact) this.master);
353 = RiverUtils.getWQInputMode((D4EArtifact)master);
354 319
355 writeCSVMeta(writer); 320 writeCSVMeta(writer);
356 writeCSVHeader(writer, atGauge, isQ); 321 writeCSVHeader(writer, atGauge, isQ);
357 322
358 Double first = Double.NaN; 323 Double first = Double.NaN;
359 Double last = Double.NaN; 324 Double last = Double.NaN;
360 325
361 for (WQKms[] tmp: data) { 326 for (final WQKms[] tmp : this.data) {
362 for (WQKms wqkms: tmp) { 327 for (final WQKms wqkms : tmp) {
363 wQKms2CSV(writer, wqkms, atGauge, isQ); 328 wQKms2CSV(writer, wqkms, atGauge, isQ);
364 double[] firstLast = wqkms.getFirstLastKM(); 329 final double[] firstLast = wqkms.getFirstLastKM();
365 if (first.isNaN()) { 330 if (first.isNaN()) {
366 /* Initialize */ 331 /* Initialize */
367 first = firstLast[0]; 332 first = firstLast[0];
368 last = firstLast[1]; 333 last = firstLast[1];
369 } 334 }
370 if (firstLast[0] > firstLast[1]) { 335 if (firstLast[0] > firstLast[1]) {
371 /* Calculating upstream we assert that it is 336 /*
337 * Calculating upstream we assert that it is
372 * impossible that the direction changes during this 338 * impossible that the direction changes during this
373 * loop */ 339 * loop
340 */
374 first = Math.max(first, firstLast[0]); 341 first = Math.max(first, firstLast[0]);
375 last = Math.min(last, firstLast[1]); 342 last = Math.min(last, firstLast[1]);
376 } else if (firstLast[0] < firstLast[1]) { 343 } else if (firstLast[0] < firstLast[1]) {
377 first = Math.min(first, firstLast[0]); 344 first = Math.min(first, firstLast[0]);
378 last = Math.max(last, firstLast[1]); 345 last = Math.max(last, firstLast[1]);
380 first = last = firstLast[0]; 347 first = last = firstLast[0];
381 } 348 }
382 } 349 }
383 } 350 }
384 /* Append the official fixing at the bottom */ 351 /* Append the official fixing at the bottom */
385 for (WQKms wqkms: officalFixings) { 352 for (final WQKms wqkms : this.officalFixings) {
386 wQKms2CSV(writer, filterWQKms(wqkms, first, last), atGauge, isQ); 353 wQKms2CSV(writer, filterWQKms(wqkms, first, last), atGauge, isQ);
387 } 354 }
388 } 355 }
389 356
390 357 /**
391 /** Filter a wqkms object to a distance. 358 * Filter a wqkms object to a distance.
392 * 359 *
393 * To handle upstream / downstream and to limit 360 * To handle upstream / downstream and to limit
394 * the officialFixings to the calculation distance 361 * the officialFixings to the calculation distance
395 * we create a new wqkms object here and fill it only 362 * we create a new wqkms object here and fill it only
396 * with the relevant data. 363 * with the relevant data.
397 * 364 *
398 * @param wqkms: The WQKms Object to filter 365 * @param wqkms:
399 * @param first: The fist kilometer of the range 366 * The WQKms Object to filter
400 * @param last: The last kilometer of the range 367 * @param first:
368 * The fist kilometer of the range
369 * @param last:
370 * The last kilometer of the range
401 * 371 *
402 * @return A new WQKms with the relevant data sorted by direction 372 * @return A new WQKms with the relevant data sorted by direction
403 */ 373 */
404 private WQKms filterWQKms(WQKms wqkms, Double first, Double last) { 374 private WQKms filterWQKms(final WQKms wqkms, final Double first, final Double last) {
405 if (first.isNaN() || last.isNaN()) { 375 if (first.isNaN() || last.isNaN()) {
406 log.warn("Filtering official fixing without valid first/last."); 376 log.warn("Filtering official fixing without valid first/last.");
407 return wqkms; 377 return wqkms;
408 } 378 }
409 int firstIdx = first > last ? wqkms.size() - 1 : 0; 379 final int firstIdx = first > last ? wqkms.size() - 1 : 0;
410 int lastIdx = first > last ? 0 : wqkms.size() -1; 380 final int lastIdx = first > last ? 0 : wqkms.size() - 1;
411 WQKms filtered = new WQKms (wqkms.size()); 381 final WQKms filtered = new WQKms(wqkms.size());
412 filtered.setName(wqkms.getName()); 382 filtered.setName(wqkms.getName());
413 double [] dp = new double [3]; 383 double[] dp = new double[3];
414 384
415 if (first > last) { 385 if (first > last) {
416 for (int i = wqkms.size() - 1; i >= 0; i--) { 386 for (int i = wqkms.size() - 1; i >= 0; i--) {
417 dp = wqkms.get(i, dp); 387 dp = wqkms.get(i, dp);
418 if (dp[2] <= first + 1E-5 && dp[2] > last - 1E-5) { 388 if (dp[2] <= first + 1E-5 && dp[2] > last - 1E-5) {
428 } 398 }
429 } 399 }
430 return filtered; 400 return filtered;
431 } 401 }
432 402
433 403 protected void writeCSVMeta(final CSVWriter writer) {
434 protected void writeCSVMeta(CSVWriter writer) {
435 log.info("WaterlevelExporter.writeCSVMeta"); 404 log.info("WaterlevelExporter.writeCSVMeta");
436 405
437 // TODO use Access instead of RiverUtils 406 // TODO use Access instead of RiverUtils
438 407
439 CallMeta meta = context.getMeta(); 408 final CallMeta meta = this.context.getMeta();
440 409
441 D4EArtifact flys = (D4EArtifact) master; 410 final D4EArtifact flys = (D4EArtifact) this.master;
442 411
443 writer.writeNext(new String[] { 412 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_RESULT, CSV_META_RESULT, new Object[] { RiverUtils.getRivername(flys) }) });
444 Resources.getMsg( 413
445 meta, 414 final Locale locale = Resources.getLocale(meta);
446 CSV_META_RESULT, 415 final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
447 CSV_META_RESULT, 416
448 new Object[] { RiverUtils.getRivername(flys) }) 417 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_CREATION, CSV_META_CREATION, new Object[] { df.format(new Date()) }) });
418
419 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_CALCULATIONBASE, CSV_META_CALCULATIONBASE, new Object[] { "" }) // TODO what is required
420 // at this place?
449 }); 421 });
450 422
451 Locale locale = Resources.getLocale(meta); 423 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_RIVER, CSV_META_RIVER, new Object[] { RiverUtils.getRivername(flys) }) });
452 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); 424
453 425 final RangeAccess rangeAccess = new RangeAccess(flys);
454 writer.writeNext(new String[] { 426 final double[] kms = rangeAccess.getKmRange();
455 Resources.getMsg( 427 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_RANGE, CSV_META_RANGE, new Object[] { kms[0], kms[kms.length - 1] }) });
456 meta, 428
457 CSV_META_CREATION, 429 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_GAUGE, CSV_META_GAUGE, new Object[] { RiverUtils.getGaugename(flys) }) });
458 CSV_META_CREATION,
459 new Object[] { df.format(new Date()) })
460 });
461
462 writer.writeNext(new String[] {
463 Resources.getMsg(
464 meta,
465 CSV_META_CALCULATIONBASE,
466 CSV_META_CALCULATIONBASE,
467 new Object[] { "" }) // TODO what is required at this place?
468 });
469
470 writer.writeNext(new String[] {
471 Resources.getMsg(
472 meta,
473 CSV_META_RIVER,
474 CSV_META_RIVER,
475 new Object[] { RiverUtils.getRivername(flys) })
476 });
477
478 RangeAccess rangeAccess = new RangeAccess(flys);
479 double[] kms = rangeAccess.getKmRange();
480 writer.writeNext(new String[] {
481 Resources.getMsg(
482 meta,
483 CSV_META_RANGE,
484 CSV_META_RANGE,
485 new Object[] { kms[0], kms[kms.length-1] })
486 });
487
488 writer.writeNext(new String[] {
489 Resources.getMsg(
490 meta,
491 CSV_META_GAUGE,
492 CSV_META_GAUGE,
493 new Object[] { RiverUtils.getGaugename(flys) })
494 });
495 430
496 // TODO: code extracted into WaterlevelDescriptionBuilder, should be used instead. 431 // TODO: code extracted into WaterlevelDescriptionBuilder, should be used instead.
497 RiverUtils.WQ_MODE wq = RiverUtils.getWQMode(flys); 432 final RiverUtils.WQ_MODE wq = RiverUtils.getWQMode(flys);
498 if (wq == RiverUtils.WQ_MODE.QFREE || wq == RiverUtils.WQ_MODE.QGAUGE) { 433 if (wq == RiverUtils.WQ_MODE.QFREE || wq == RiverUtils.WQ_MODE.QGAUGE) {
499 double[] qs = RiverUtils.getQs(flys); 434 final double[] qs = RiverUtils.getQs(flys);
500 RiverUtils.WQ_INPUT input = RiverUtils.getWQInputMode(flys); 435 final RiverUtils.WQ_INPUT input = RiverUtils.getWQInputMode(flys);
501 436
502 String data = ""; 437 String data = "";
503 438
504 if ((input == RiverUtils.WQ_INPUT.ADAPTED || 439 if ((input == RiverUtils.WQ_INPUT.ADAPTED || input == RiverUtils.WQ_INPUT.RANGE) && qs != null && qs.length > 0) {
505 input == RiverUtils.WQ_INPUT.RANGE) &&
506 qs != null && qs.length > 0)
507 {
508 data = String.valueOf(qs[0]); 440 data = String.valueOf(qs[0]);
509 data += " - " + String.valueOf(qs[qs.length-1]); 441 data += " - " + String.valueOf(qs[qs.length - 1]);
510 } 442 } else if (input == RiverUtils.WQ_INPUT.SINGLE && qs != null) {
511 else if (input == RiverUtils.WQ_INPUT.SINGLE && qs != null){
512 data = String.valueOf(qs[0]); 443 data = String.valueOf(qs[0]);
513 for (int i = 1; i < qs.length; i++) { 444 for (int i = 1; i < qs.length; i++) {
514 data += ", " + String.valueOf(qs[i]); 445 data += ", " + String.valueOf(qs[i]);
515 } 446 }
516 } 447 } else {
517 else {
518 log.warn("Could not determine Q range!"); 448 log.warn("Could not determine Q range!");
519 } 449 }
520 450
521 writer.writeNext(new String[] { 451 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_Q, CSV_META_Q, new Object[] { data }) });
522 Resources.getMsg( 452 } else {
523 meta, 453 final double[] ws = RiverUtils.getWs(flys);
524 CSV_META_Q,
525 CSV_META_Q,
526 new Object[] {data})
527 });
528 }
529 else {
530 double[] ws = RiverUtils.getWs(flys);
531 454
532 String lower = ""; 455 String lower = "";
533 String upper = ""; 456 String upper = "";
534 457
535 if (ws != null && ws.length > 0) { 458 if (ws != null && ws.length > 0) {
536 lower = String.valueOf(ws[0]); 459 lower = String.valueOf(ws[0]);
537 upper = String.valueOf(ws[ws.length-1]); 460 upper = String.valueOf(ws[ws.length - 1]);
538 } 461 } else {
539 else {
540 log.warn("Could not determine W range!"); 462 log.warn("Could not determine W range!");
541 } 463 }
542 464
543 writer.writeNext(new String[] { 465 writer.writeNext(new String[] { Resources.getMsg(meta, CSV_META_W, CSV_META_W, new Object[] { lower, upper }) });
544 Resources.getMsg(
545 meta,
546 CSV_META_W,
547 CSV_META_W,
548 new Object[] { lower, upper })
549 });
550 } 466 }
551 467
552 writer.writeNext(new String[] { "" }); 468 writer.writeNext(new String[] { "" });
553 } 469 }
554
555 470
556 /** 471 /**
557 * Write the header, with different headings depending on whether at a 472 * Write the header, with different headings depending on whether at a
558 * gauge or at a location. 473 * gauge or at a location.
559 */ 474 */
560 protected void writeCSVHeader( 475 protected void writeCSVHeader(final CSVWriter writer, final boolean atGauge, final boolean isQ) {
561 CSVWriter writer,
562 boolean atGauge,
563 boolean isQ
564 ) {
565 log.info("WaterlevelExporter.writeCSVHeader"); 476 log.info("WaterlevelExporter.writeCSVHeader");
566 477
567 String unit = RiverUtils.getRiver( 478 final String unit = RiverUtils.getRiver((D4EArtifact) this.master).getWstUnit().getName();
568 (D4EArtifact) master).getWstUnit().getName();
569 479
570 if (atGauge) { 480 if (atGauge) {
571 writer.writeNext(new String[] { 481 writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }),
572 msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER), 482 msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
573 msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }), 483
574 msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), 484 // FIXME: use WaterlevelDescriptionBuilder instead and also remove all this duplicate code.
575 485 (isQ ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER) : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)),
576 // FIXME: use WaterlevelDescriptionBuilder instead and also remove all this duplicate code. 486 msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER), msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER) });
577 (isQ 487 } else {
578 ? msg(CSV_Q_DESC_HEADER, DEFAULT_CSV_Q_DESC_HEADER) 488 writer.writeNext(new String[] { msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
579 : msg(CSV_W_DESC_HEADER, DEFAULT_CSV_W_DESC_HEADER)),
580 msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER),
581 msg(CSV_GAUGE_HEADER, DEFAULT_CSV_GAUGE_HEADER)
582 });
583 }
584 else {
585 writer.writeNext(new String[] {
586 msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
587 // TODO flys/issue1128 (unit per river) 489 // TODO flys/issue1128 (unit per river)
588 msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }), 490 msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER, new Object[] { unit }), msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
589 msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER), 491 msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER) });
590 msg(CSV_LOCATION_HEADER, DEFAULT_CSV_LOCATION_HEADER) 492 }
591 }); 493 }
592 }
593 }
594
595 494
596 /** Linearly search for gauge which is valid at km. */ 495 /** Linearly search for gauge which is valid at km. */
597 private static Gauge findGauge(double km, List<Gauge> gauges) { 496 private static Gauge findGauge(final double km, final List<Gauge> gauges) {
598 for (Gauge gauge: gauges) { 497 for (final Gauge gauge : gauges) {
599 if (gauge.getRange().contains(km)) { 498 if (gauge.getRange().contains(km)) {
600 return gauge; 499 return gauge;
601 } 500 }
602 } 501 }
603 return null; 502 return null;
604 } 503 }
605 504
606 private static Segment findSegment(double km, List<Segment> segments) { 505 private static Segment findSegment(final double km, final List<Segment> segments) {
607 for (Segment segment: segments) { 506 for (final Segment segment : segments) {
608 if (segment.inside(km)) { 507 if (segment.inside(km)) {
609 return segment; 508 return segment;
610 } 509 }
611 } 510 }
612 return null; 511 return null;
613 } 512 }
614 513
615 514 private void writeRow4(final CSVWriter writer, final double wqkm[], final D4EArtifact flys) {
616 private void writeRow4(CSVWriter writer, double wqkm[], D4EArtifact flys) { 515 final NumberFormat kmf = getKmFormatter();
617 NumberFormat kmf = getKmFormatter(); 516 final NumberFormat wf = getWFormatter();
618 NumberFormat wf = getWFormatter(); 517 final NumberFormat qf = getQFormatter();
619 NumberFormat qf = getQFormatter(); 518 writer.writeNext(new String[] { kmf.format(wqkm[2]), wf.format(wqkm[0]), qf.format(RiverUtils.roundQ(wqkm[1])),
620 writer.writeNext(new String[] { 519 RiverUtils.getLocationDescription(flys, wqkm[2]) });
621 kmf.format(wqkm[2]),
622 wf.format(wqkm[0]),
623 qf.format(RiverUtils.roundQ(wqkm[1])),
624 RiverUtils.getLocationDescription(flys, wqkm[2])
625 });
626 } 520 }
627 521
628 /** Write an csv-row at gauge location. */ 522 /** Write an csv-row at gauge location. */
629 private void writeRow6(CSVWriter writer, double wqkm[], String wOrQDesc, 523 private void writeRow6(final CSVWriter writer, final double wqkm[], final String wOrQDesc, final D4EArtifact flys, final String gaugeName) {
630 D4EArtifact flys, String gaugeName) { 524 final NumberFormat kmf = getKmFormatter();
631 NumberFormat kmf = getKmFormatter(); 525 final NumberFormat wf = getWFormatter();
632 NumberFormat wf = getWFormatter(); 526 final NumberFormat qf = getQFormatter();
633 NumberFormat qf = getQFormatter(); 527
634 528 writer.writeNext(new String[] { kmf.format(wqkm[2]), wf.format(wqkm[0]), qf.format(RiverUtils.roundQ(wqkm[1])), wOrQDesc,
635 writer.writeNext(new String[] { 529 RiverUtils.getLocationDescription(flys, wqkm[2]), gaugeName });
636 kmf.format(wqkm[2]),
637 wf.format(wqkm[0]),
638 qf.format(RiverUtils.roundQ(wqkm[1])),
639 wOrQDesc,
640 RiverUtils.getLocationDescription(flys, wqkm[2]),
641 gaugeName
642 });
643 } 530 }
644 531
645 /** 532 /**
646 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead. 533 * @deprecated Use {@link WaterlevelDescriptionBuilder} instead.
647 */ 534 */
648 @Deprecated 535 @Deprecated
649 private String getDesc(WQKms wqkms, boolean isQ) 536 private String getDesc(final WQKms wqkms, final boolean isQ) {
650 { 537 final D4EArtifact flys = (D4EArtifact) this.master;
651 D4EArtifact flys = (D4EArtifact) master;
652 String colDesc = ""; 538 String colDesc = "";
653 539
654 if (flys instanceof WINFOArtifact && isQ) { 540 if (flys instanceof WINFOArtifact && isQ) {
655 colDesc = getCSVRowTitle((WINFOArtifact)flys, wqkms); 541 colDesc = getCSVRowTitle((WINFOArtifact) flys, wqkms);
656 } 542 } else if (!isQ) {
657 else if (!isQ) { 543 final Double value = RiverUtils.getValueFromWQ(wqkms);
658 Double value = RiverUtils.getValueFromWQ(wqkms); 544 colDesc = (value != null) ? Formatter.getWaterlevelW(this.context).format(value) : null;
659 colDesc = (value != null) ?
660 Formatter.getWaterlevelW(context).format(value) : null;
661 } 545 }
662 546
663 if (flys instanceof WINFOArtifact) { 547 if (flys instanceof WINFOArtifact) {
664 if (wqkms != null && wqkms.getRawValue() != null) { 548 if (wqkms != null && wqkms.getRawValue() != null) {
665 WINFOArtifact winfo = (WINFOArtifact) flys; 549 final WINFOArtifact winfo = (WINFOArtifact) flys;
666 colDesc = RiverUtils.getNamedMainValue( 550 colDesc = RiverUtils.getNamedMainValue(winfo, wqkms.getRawValue());
667 winfo, wqkms.getRawValue());
668 // For 'W am Pegel' s 551 // For 'W am Pegel' s
669 if (colDesc == null) { 552 if (colDesc == null) {
670 Double value = RiverUtils.getValueFromWQ(wqkms); 553 final Double value = RiverUtils.getValueFromWQ(wqkms);
671 colDesc = (value != null) ? 554 colDesc = (value != null) ? Formatter.getWaterlevelW(this.context).format(value) : null;
672 Formatter.getWaterlevelW(context).format(value) : null;
673 } 555 }
674 } 556 }
675 } 557 }
676 if (colDesc != null) { 558 if (colDesc != null) {
677 /* Quick hack. Can be removed when database strings are 559 /*
678 * adapted or left in here as it should never be harmful. */ 560 * Quick hack. Can be removed when database strings are
561 * adapted or left in here as it should never be harmful.
562 */
679 colDesc = colDesc.replace("Amtl.Festlegung_", "Amtl. "); 563 colDesc = colDesc.replace("Amtl.Festlegung_", "Amtl. ");
680 } 564 }
681 565
682 return colDesc == null ? "" : colDesc; 566 return colDesc == null ? "" : colDesc;
683 } 567 }
684 568
685 /** 569 /**
686 * Write "rows" of csv data from wqkms with writer. 570 * Write "rows" of csv data from wqkms with writer.
687 */ 571 */
688 protected void wQKms2CSV( 572 protected void wQKms2CSV(final CSVWriter writer, final WQKms wqkms, final boolean atGauge, final boolean isQ) {
689 CSVWriter writer,
690 WQKms wqkms,
691 boolean atGauge,
692 boolean isQ
693 ) {
694 log.debug("WaterlevelExporter.wQKms2CSV"); 573 log.debug("WaterlevelExporter.wQKms2CSV");
695 574
696 // Skip constant data. 575 // Skip constant data.
697 if (wqkms instanceof ConstantWQKms) { 576 if (wqkms instanceof ConstantWQKms) {
698 return; 577 return;
699 } 578 }
700 579
701 NumberFormat kmf = getKmFormatter(); 580 final NumberFormat kmf = getKmFormatter();
702 NumberFormat wf = getWFormatter(); 581 final NumberFormat wf = getWFormatter();
703 NumberFormat qf = getQFormatter(); 582 final NumberFormat qf = getQFormatter();
704 583
705 int size = wqkms.size(); 584 final int size = wqkms.size();
706 double[] result = new double[3]; 585 double[] result = new double[3];
707 586
708 D4EArtifact flys = (D4EArtifact) master; 587 final D4EArtifact flys = (D4EArtifact) this.master;
709 RangeAccess rangeAccess = new RangeAccess(flys); 588 final RangeAccess rangeAccess = new RangeAccess(flys);
710 589
711 List<Gauge> gauges = RiverUtils.getGauges(flys); 590 final List<Gauge> gauges = RiverUtils.getGauges(flys);
712 591
713 Gauge gauge = rangeAccess.getRiver().determineRefGauge( 592 final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());
714 rangeAccess.getKmRange(), rangeAccess.isRange()); 593
715 594 final String gaugeName = gauge.getName();
716 String gaugeName = gauge.getName(); 595 String desc = "";
717 String desc = ""; 596 final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
718 String notinrange = msg(
719 CSV_NOT_IN_GAUGE_RANGE,
720 DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
721 List<Segment> segments = null; 597 List<Segment> segments = null;
722 boolean isFixRealize = false; 598 boolean isFixRealize = false;
723 599
724 double a = gauge.getRange().getA().doubleValue(); 600 final double a = gauge.getRange().getA().doubleValue();
725 double b = gauge.getRange().getB().doubleValue(); 601 final double b = gauge.getRange().getB().doubleValue();
726 long startTime = System.currentTimeMillis(); 602 final long startTime = System.currentTimeMillis();
727 603
728 desc = getDesc(wqkms, isQ); 604 desc = getDesc(wqkms, isQ);
729 605
730 if (flys instanceof FixationArtifact) { 606 if (flys instanceof FixationArtifact) {
731 // Get W/Q input per gauge for this case. 607 // Get W/Q input per gauge for this case.
732 FixRealizingAccess fixAccess = new FixRealizingAccess(flys); 608 final FixRealizingAccess fixAccess = new FixRealizingAccess(flys);
733 segments = fixAccess.getSegments(); 609 segments = fixAccess.getSegments();
734 if (segments != null && !segments.isEmpty()) { 610 if (segments != null && !segments.isEmpty()) {
735 isFixRealize = true; 611 isFixRealize = true;
736 } 612 }
737 } 613 }
739 if (atGauge) { // "At gauge" needs more output. 615 if (atGauge) { // "At gauge" needs more output.
740 616
741 // Kms tend to be close together so caching the last sector 617 // Kms tend to be close together so caching the last sector
742 // is a good time saving heuristic. 618 // is a good time saving heuristic.
743 Segment lastSegment = null; 619 Segment lastSegment = null;
744 Gauge lastGauge = null; 620 Gauge lastGauge = null;
745 621
746 NumberFormat nf = 622 final NumberFormat nf = Formatter.getFormatter(this.context.getMeta(), 0, 0);
747 Formatter.getFormatter(context.getMeta(), 0, 0);
748 623
749 for (int i = 0; i < size; ++i) { 624 for (int i = 0; i < size; ++i) {
750 result = wqkms.get(i, result); 625 result = wqkms.get(i, result);
751 double km = result[2]; 626 final double km = result[2];
752 627
753 if (segments != null) { 628 if (segments != null) {
754 Segment found = lastSegment != null 629 final Segment found = lastSegment != null && lastSegment.inside(km) ? lastSegment : findSegment(km, segments);
755 && lastSegment.inside(km)
756 ? lastSegment
757 : findSegment(km, segments);
758 630
759 if (found != null) { 631 if (found != null) {
760 desc = nf.format(found.getValues()[0]); 632 desc = nf.format(found.getValues()[0]);
761 } 633 }
762 lastSegment = found; 634 lastSegment = found;
763 } 635 }
764 636
765 String gaugeN; 637 String gaugeN;
766 if (isFixRealize) { 638 if (isFixRealize) {
767 Gauge found = lastGauge != null 639 final Gauge found = lastGauge != null && lastGauge.getRange().contains(km) ? lastGauge : findGauge(km, gauges);
768 && lastGauge.getRange().contains(km)
769 ? lastGauge
770 : findGauge(km, gauges);
771 640
772 gaugeN = found != null ? found.getName() : notinrange; 641 gaugeN = found != null ? found.getName() : notinrange;
773 lastGauge = found; 642 lastGauge = found;
774 } 643 } else {
775 else {
776 // TODO issue1114: Take correct gauge 644 // TODO issue1114: Take correct gauge
777 gaugeN = km >= a && km <= b 645 gaugeN = km >= a && km <= b ? gaugeName : notinrange;
778 ? gaugeName
779 : notinrange;
780 } 646 }
781 writeRow6(writer, result, desc, flys, gaugeN); 647 writeRow6(writer, result, desc, flys, gaugeN);
782 } 648 }
783 } 649 } else { // Not at gauge.
784 else { // Not at gauge.
785 for (int i = 0; i < size; ++i) { 650 for (int i = 0; i < size; ++i) {
786 result = wqkms.get(i, result); 651 result = wqkms.get(i, result);
787 writeRow4(writer, result, flys); 652 writeRow4(writer, result, flys);
788 } 653 }
789 } 654 }
790 655
791 long stopTime = System.currentTimeMillis(); 656 final long stopTime = System.currentTimeMillis();
792 657
793 if (log.isDebugEnabled()) { 658 if (log.isDebugEnabled()) {
794 log.debug("Writing CSV took " + 659 log.debug("Writing CSV took " + (stopTime - startTime) / 1000f + " secs.");
795 (float)(stopTime-startTime)/1000f + " secs."); 660 }
796 } 661 }
797 }
798
799 662
800 /** 663 /**
801 * Generates the output in WST format. 664 * Generates the output in WST format.
802 */ 665 */
803 protected void generateWST() 666 protected void generateWST() throws IOException {
804 throws IOException
805 {
806 log.info("WaterlevelExporter.generateWST"); 667 log.info("WaterlevelExporter.generateWST");
807 668
808 int cols = data.get(0).length + officalFixings.size(); 669 final int cols = this.data.get(0).length + this.officalFixings.size();
809 WstWriter writer = new WstWriter(cols); 670 final WstWriter writer = new WstWriter(cols);
810 671
811 writeWSTData(writer); 672 writeWSTData(writer);
812 673
813 writer.write(out); 674 writer.write(this.out);
814 } 675 }
815 676
816 677 protected void writeWSTData(final WstWriter writer) {
817 protected void writeWSTData(WstWriter writer) {
818 log.debug("WaterlevelExporter.writeWSTData"); 678 log.debug("WaterlevelExporter.writeWSTData");
819 679
820 double[] result = new double[4]; 680 double[] result = new double[4];
821 681
822 for (WQKms[] tmp: data) { 682 for (final WQKms[] tmp : this.data) {
823 for (WQKms wqkms: tmp) { 683 for (final WQKms wqkms : tmp) {
824 if (wqkms instanceof ConstantWQKms) { 684 if (wqkms instanceof ConstantWQKms) {
825 continue; 685 continue;
826 } 686 }
827 int size = wqkms != null ? wqkms.size() : 0; 687 final int size = wqkms != null ? wqkms.size() : 0;
828 688
829 addWSTColumn(writer, wqkms); 689 addWSTColumn(writer, wqkms);
830 690
831 for (int i = 0; i < size; i++) { 691 for (int i = 0; i < size; i++) {
832 result = wqkms.get(i, result); 692 result = wqkms.get(i, result);
848 708
849 // Append the official fixing interpolated to the calculation steps 709 // Append the official fixing interpolated to the calculation steps
850 // 710 //
851 // There was some confusion how to implement this. see flys/issue1620 711 // There was some confusion how to implement this. see flys/issue1620
852 // for details. 712 // for details.
853 for (WQKms wqkms: officalFixings) { 713 for (final WQKms wqkms : this.officalFixings) {
854 // To add some spaces here or to add them in the writer,.. 714 // To add some spaces here or to add them in the writer,..
855 writer.addColumn(getDesc(wqkms, true)); 715 writer.addColumn(getDesc(wqkms, true));
856 716
857 // Get all lines from the calculation 717 // Get all lines from the calculation
858 Map <Double, WstLine> calcLines = writer.getLines(); 718 final Map<Double, WstLine> calcLines = writer.getLines();
859 719
860 // All KM values where we have a point for 720 // All KM values where we have a point for
861 TDoubleArrayList officialKms = wqkms.allKms(); 721 final TDoubleArrayList officialKms = wqkms.allKms();
862 722
863 for (Map.Entry<Double, WstLine> entry : calcLines.entrySet()) { 723 for (final Map.Entry<Double, WstLine> entry : calcLines.entrySet()) {
864 // Bad for perfomance but the user can wait a bit for WST 724 // Bad for perfomance but the user can wait a bit for WST
865 // so lets not spend time optimizing too much,.. *hides* 725 // so lets not spend time optimizing too much,.. *hides*
866 double km = entry.getKey().doubleValue(); 726 final double km = entry.getKey().doubleValue();
867 int idx = officialKms.indexOf(km); 727 final int idx = officialKms.indexOf(km);
868 if (idx != -1) { 728 if (idx != -1) {
869 entry.getValue().add(wqkms.getW(idx), wqkms.getQ(idx)); 729 entry.getValue().add(wqkms.getW(idx), wqkms.getQ(idx));
870 } 730 }
871 } 731 }
872 } 732 }
873 } 733 }
874
875 734
876 /** 735 /**
877 * Register a new column at <i>writer</i>. The name / 736 * Register a new column at <i>writer</i>. The name /
878 * title of the column depends on the Q or W value of <i>wqkms</i>. If a Q 737 * title of the column depends on the Q or W value of <i>wqkms</i>. If a Q
879 * was selected and the Q fits to a named main value, the title is set to 738 * was selected and the Q fits to a named main value, the title is set to
880 * the named main value. Otherwise, the name returned by 739 * the named main value. Otherwise, the name returned by
881 * <i>WQKms.getName()</i> is set. 740 * <i>WQKms.getName()</i> is set.
882 * 741 *
883 * @param writer The WstWriter. 742 * @param writer
884 * @param wqkms The new WST column. 743 * The WstWriter.
885 */ 744 * @param wqkms
886 protected void addWSTColumn(WstWriter writer, WQKms wqkms) { 745 * The new WST column.
746 */
747 protected void addWSTColumn(final WstWriter writer, final WQKms wqkms) {
887 if (wqkms instanceof ConstantWQKms) { 748 if (wqkms instanceof ConstantWQKms) {
888 return; 749 return;
889 } 750 }
890 if (master instanceof WINFOArtifact) { 751 if (this.master instanceof WINFOArtifact) {
891 writer.addColumn(getColumnTitle((WINFOArtifact) master, wqkms)); 752 writer.addColumn(getColumnTitle((WINFOArtifact) this.master, wqkms));
892 } 753 } else {
893 else {
894 writer.addColumn(wqkms.getName()); 754 writer.addColumn(wqkms.getName());
895 } 755 }
896 } 756 }
897 757
898
899 @Override 758 @Override
900 protected void writePDF(OutputStream out) { 759 protected void writePDF(final OutputStream out) {
901 log.debug("write PDF"); 760 log.debug("write PDF");
902 WKmsJRDataSource source = createJRData(); 761 final WKmsJRDataSource source = createJRData();
903 762
904 String jasperFile = Resources.getMsg( 763 final String jasperFile = // "/jasper/waterlevel_en.jasper";
905 context.getMeta(), 764 Resources.getMsg(this.context.getMeta(), JASPER_FILE, "/jasper/waterlevel_en.jasper");
906 JASPER_FILE, 765 final String confPath = Config.getConfigDirectory().toString();
907 "/jasper/waterlevel_en.jasper"); 766
908 String confPath = Config.getConfigDirectory().toString(); 767 final Map parameters = new HashMap();
909
910
911 Map parameters = new HashMap();
912 parameters.put("ReportTitle", "Exported Data"); 768 parameters.put("ReportTitle", "Exported Data");
913 try { 769 try {
914 JasperPrint print = JasperFillManager.fillReport( 770 final JasperPrint print = JasperFillManager.fillReport(confPath + jasperFile, parameters, source);
915 confPath + jasperFile,
916 parameters,
917 source);
918 JasperExportManager.exportReportToPdfStream(print, out); 771 JasperExportManager.exportReportToPdfStream(print, out);
919 } 772 }
920 catch(JRException je) { 773 catch (final JRException je) {
921 log.warn("Error generating PDF Report!", je); 774 log.warn("Error generating PDF Report!", je);
922 } 775 }
923 } 776 }
924 777
925 protected WKmsJRDataSource createJRData() { 778 protected WKmsJRDataSource createJRData() {
926 WKmsJRDataSource source = new WKmsJRDataSource(); 779 final WKmsJRDataSource source = new WKmsJRDataSource();
927 780
928 WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact)master); 781 final WQ_MODE mode = RiverUtils.getWQMode((D4EArtifact) this.master);
929 boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE; 782 final boolean atGauge = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.WGAUGE;
930 boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE; 783 final boolean isQ = mode == WQ_MODE.QGAUGE || mode == WQ_MODE.QFREE;
931 784
932 Double first = Double.NaN; 785 Double first = Double.NaN;
933 Double last = Double.NaN; 786 Double last = Double.NaN;
934 787
935 addMetaData(source); 788 addMetaData(source);
936 for (WQKms[] tmp: data) { 789 for (final WQKms[] tmp : this.data) {
937 for (WQKms wqkms: tmp) { 790 for (final WQKms wqkms : tmp) {
938 addWKmsData(wqkms, atGauge, isQ, source); 791 addWKmsData(wqkms, atGauge, isQ, source);
939 double[] firstLast = wqkms.getFirstLastKM(); 792 final double[] firstLast = wqkms.getFirstLastKM();
940 if (first.isNaN()) { 793 if (first.isNaN()) {
941 /* Initialize */ 794 /* Initialize */
942 first = firstLast[0]; 795 first = firstLast[0];
943 last = firstLast[1]; 796 last = firstLast[1];
944 } 797 }
945 if (firstLast[0] > firstLast[1]) { 798 if (firstLast[0] > firstLast[1]) {
946 /* Calculating upstream we assert that it is 799 /*
800 * Calculating upstream we assert that it is
947 * impossible that the direction changes during this 801 * impossible that the direction changes during this
948 * loop */ 802 * loop
803 */
949 first = Math.max(first, firstLast[0]); 804 first = Math.max(first, firstLast[0]);
950 last = Math.min(last, firstLast[1]); 805 last = Math.min(last, firstLast[1]);
951 } else if (firstLast[0] < firstLast[1]) { 806 } else if (firstLast[0] < firstLast[1]) {
952 first = Math.min(first, firstLast[0]); 807 first = Math.min(first, firstLast[0]);
953 last = Math.max(last, firstLast[1]); 808 last = Math.max(last, firstLast[1]);
956 } 811 }
957 } 812 }
958 } 813 }
959 814
960 /* Append the official fixing at the bottom */ 815 /* Append the official fixing at the bottom */
961 for (WQKms wqkms: officalFixings) { 816 for (final WQKms wqkms : this.officalFixings) {
962 addWKmsData(filterWQKms(wqkms, first, last), atGauge, isQ, source); 817 addWKmsData(filterWQKms(wqkms, first, last), atGauge, isQ, source);
963 } 818 }
964 return source; 819 return source;
965 } 820 }
966 821
967 protected void addMetaData(WKmsJRDataSource source) { 822 protected void addMetaData(final WKmsJRDataSource source) {
968 CallMeta meta = context.getMeta(); 823 final CallMeta meta = this.context.getMeta();
969 824
970 D4EArtifact flys = (D4EArtifact) master; 825 final D4EArtifact flys = (D4EArtifact) this.master;
971 826
972 source.addMetaData ("river", RiverUtils.getRivername(flys)); 827 source.addMetaData("river", RiverUtils.getRivername(flys));
973 828
974 Locale locale = Resources.getLocale(meta); 829 final Locale locale = Resources.getLocale(meta);
975 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); 830 final DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
976 NumberFormat kmf = getKmFormatter(); 831 final NumberFormat kmf = getKmFormatter();
977 832
978 source.addMetaData("date", df.format(new Date())); 833 source.addMetaData("date", df.format(new Date()));
979 834
980 RangeAccess rangeAccess = new RangeAccess(flys); 835 final RangeAccess rangeAccess = new RangeAccess(flys);
981 double[] kms = rangeAccess.getKmRange(); 836 final double[] kms = rangeAccess.getKmRange();
982 source.addMetaData("range", 837 source.addMetaData("range", kmf.format(kms[0]) + " - " + kmf.format(kms[kms.length - 1]));
983 kmf.format(kms[0]) + " - " + kmf.format(kms[kms.length-1]));
984 838
985 source.addMetaData("gauge", RiverUtils.getGaugename(flys)); 839 source.addMetaData("gauge", RiverUtils.getGaugename(flys));
986 840
987 source.addMetaData("calculation", Resources.getMsg( 841 source.addMetaData("calculation", Resources.getMsg(locale, PDF_HEADER_MODE, "Waterlevel"));
988 locale, 842 }
989 PDF_HEADER_MODE, 843
990 "Waterlevel")); 844 protected void addWKmsData(final WQKms wqkms, final boolean atGauge, final boolean isQ, final WKmsJRDataSource source) {
991 }
992
993 protected void addWKmsData(
994 WQKms wqkms,
995 boolean atGauge,
996 boolean isQ,
997 WKmsJRDataSource source)
998 {
999 log.debug("WaterlevelExporter.addWKmsData"); 845 log.debug("WaterlevelExporter.addWKmsData");
1000 846
1001 // Skip constant data. 847 // Skip constant data.
1002 if (wqkms instanceof ConstantWQKms) { 848 if (wqkms instanceof ConstantWQKms) {
1003 return; 849 return;
1004 } 850 }
1005 851
1006 NumberFormat kmf = getKmFormatter(); 852 final NumberFormat kmf = getKmFormatter();
1007 NumberFormat wf = getWFormatter(); 853 final NumberFormat wf = getWFormatter();
1008 NumberFormat qf = getQFormatter(); 854 final NumberFormat qf = getQFormatter();
1009 855
1010 int size = wqkms.size(); 856 final int size = wqkms.size();
1011 double[] result = new double[3]; 857 double[] result = new double[3];
1012 858
1013 D4EArtifact flys = (D4EArtifact) master; 859 final D4EArtifact flys = (D4EArtifact) this.master;
1014 RangeAccess rangeAccess = new RangeAccess(flys); 860 final RangeAccess rangeAccess = new RangeAccess(flys);
1015 861
1016 Gauge gauge = rangeAccess.getRiver().determineRefGauge( 862 final Gauge gauge = rangeAccess.getRiver().determineRefGauge(rangeAccess.getKmRange(), rangeAccess.isRange());
1017 rangeAccess.getKmRange(), rangeAccess.isRange()); 863
1018 864 final String gaugeName = gauge.getName();
1019 String gaugeName = gauge.getName(); 865 String desc = "";
1020 String desc = ""; 866 final String notinrange = msg(CSV_NOT_IN_GAUGE_RANGE, DEFAULT_CSV_NOT_IN_GAUGE_RANGE);
1021 String notinrange = msg( 867
1022 CSV_NOT_IN_GAUGE_RANGE, 868 final double a = gauge.getRange().getA().doubleValue();
1023 DEFAULT_CSV_NOT_IN_GAUGE_RANGE); 869 final double b = gauge.getRange().getB().doubleValue();
1024
1025 double a = gauge.getRange().getA().doubleValue();
1026 double b = gauge.getRange().getB().doubleValue();
1027 870
1028 desc = getDesc(wqkms, isQ); 871 desc = getDesc(wqkms, isQ);
1029 long startTime = System.currentTimeMillis(); 872 final long startTime = System.currentTimeMillis();
1030 873
1031 for (int i = 0; i < size; i ++) { 874 for (int i = 0; i < size; i++) {
1032 result = wqkms.get(i, result); 875 result = wqkms.get(i, result);
1033 876
1034 if (atGauge) { 877 if (atGauge) {
1035 source.addData(new String[] { 878 source.addData(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(RiverUtils.roundQ(result[1])), desc,
1036 kmf.format(result[2]), 879 RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
1037 wf.format(result[0]), 880 } else {
1038 qf.format(RiverUtils.roundQ(result[1])), 881 source.addData(new String[] { kmf.format(result[2]), wf.format(result[0]), qf.format(RiverUtils.roundQ(result[1])), desc,
1039 desc, 882 RiverUtils.getLocationDescription(flys, result[2]), result[2] >= a && result[2] <= b ? gaugeName : notinrange });
1040 RiverUtils.getLocationDescription(flys, result[2]), 883 }
1041 result[2] >= a && result[2] <= b 884 }
1042 ? gaugeName 885
1043 : notinrange 886 final long stopTime = System.currentTimeMillis();
1044 });
1045 }
1046 else {
1047 source.addData(new String[] {
1048 kmf.format(result[2]),
1049 wf.format(result[0]),
1050 qf.format(RiverUtils.roundQ(result[1])),
1051 desc,
1052 RiverUtils.getLocationDescription(flys, result[2]),
1053 result[2] >= a && result[2] <= b
1054 ? gaugeName
1055 : notinrange
1056 });
1057 }
1058 }
1059
1060 long stopTime = System.currentTimeMillis();
1061 887
1062 if (log.isDebugEnabled()) { 888 if (log.isDebugEnabled()) {
1063 log.debug("Writing PDF data took " + 889 log.debug("Writing PDF data took " + (stopTime - startTime) / 1000f + " secs.");
1064 (float)(stopTime-startTime)/1000f + " secs.");
1065 } 890 }
1066 } 891 }
1067 } 892 }
1068 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : 893 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org