Mercurial > dive4elements > gnv-client
comparison gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java @ 1119:7c4f81f74c47
merged gnv-artifacts
author | Thomas Arendsen Hein <thomas@intevation.de> |
---|---|
date | Fri, 28 Sep 2012 12:14:00 +0200 |
parents | dec4257ad570 |
children |
comparison
equal
deleted
inserted
replaced
1027:fca4b5eb8d2f | 1119:7c4f81f74c47 |
---|---|
1 /* | |
2 * Copyright (c) 2010 by Intevation GmbH | |
3 * | |
4 * This program is free software under the LGPL (>=v2.1) | |
5 * Read the file LGPL.txt coming with the software for details | |
6 * or visit http://www.gnu.org/licenses/ if it does not exist. | |
7 */ | |
8 | |
9 package de.intevation.gnv.state.timeseries; | |
10 | |
11 import java.io.IOException; | |
12 import java.io.OutputStream; | |
13 import java.io.UnsupportedEncodingException; | |
14 | |
15 import java.text.DateFormat; | |
16 import java.text.NumberFormat; | |
17 import java.text.ParseException; | |
18 | |
19 import java.util.ArrayList; | |
20 import java.util.Collection; | |
21 import java.util.Date; | |
22 import java.util.HashMap; | |
23 import java.util.Iterator; | |
24 import java.util.List; | |
25 import java.util.Locale; | |
26 import java.util.Map; | |
27 import java.util.Vector; | |
28 | |
29 import org.apache.log4j.Logger; | |
30 import org.jfree.chart.ChartTheme; | |
31 import org.w3c.dom.Document; | |
32 import org.w3c.dom.Element; | |
33 import org.w3c.dom.Node; | |
34 import org.w3c.dom.NodeList; | |
35 | |
36 import au.com.bytecode.opencsv.CSVWriter; | |
37 import de.intevation.artifacts.common.utils.Config; | |
38 import de.intevation.artifacts.common.utils.XMLUtils; | |
39 import de.intevation.artifacts.ArtifactNamespaceContext; | |
40 import de.intevation.artifacts.CallContext; | |
41 import de.intevation.artifacts.CallMeta; | |
42 import de.intevation.gnv.artifacts.context.GNVArtifactContext; | |
43 import de.intevation.gnv.artifacts.ressource.RessourceFactory; | |
44 import de.intevation.gnv.chart.Chart; | |
45 import de.intevation.gnv.chart.ChartLabels; | |
46 import de.intevation.gnv.chart.DefaultHistogram; | |
47 import de.intevation.gnv.chart.TimeSeriesChart; | |
48 import de.intevation.gnv.chart.XMLChartTheme; | |
49 import de.intevation.gnv.chart.exception.TechnicalChartException; | |
50 import de.intevation.gnv.exports.ChartExportHelper; | |
51 import de.intevation.gnv.exports.DefaultDataCollector; | |
52 import de.intevation.gnv.exports.DefaultExport; | |
53 import de.intevation.gnv.exports.DefaultProfile; | |
54 import de.intevation.gnv.exports.Export; | |
55 import de.intevation.gnv.exports.ODVExport; | |
56 import de.intevation.gnv.exports.SimpleOdvDataCollector; | |
57 import de.intevation.gnv.exports.Export.Profile; | |
58 import de.intevation.gnv.geobackend.base.Result; | |
59 import de.intevation.gnv.histogram.HistogramHelper; | |
60 import de.intevation.gnv.state.InputData; | |
61 import de.intevation.gnv.state.OutputStateBase; | |
62 import de.intevation.gnv.state.State; | |
63 import de.intevation.gnv.state.describedata.DefaultKeyValueDescribeData; | |
64 import de.intevation.gnv.state.describedata.KeyValueDescibeData; | |
65 import de.intevation.gnv.state.describedata.MinMaxDescribeData; | |
66 import de.intevation.gnv.state.describedata.NamedArrayList; | |
67 import de.intevation.gnv.state.describedata.NamedCollection; | |
68 import de.intevation.gnv.state.exception.StateException; | |
69 import de.intevation.gnv.statistics.Statistic; | |
70 import de.intevation.gnv.statistics.StatisticSet; | |
71 import de.intevation.gnv.statistics.Statistics; | |
72 import de.intevation.gnv.statistics.TimeseriesStatistics; | |
73 import de.intevation.gnv.statistics.exception.StatisticsException; | |
74 import de.intevation.gnv.timeseries.gap.DefaultTimeGap; | |
75 import de.intevation.gnv.timeseries.gap.TimeGap; | |
76 import de.intevation.gnv.utils.ArtifactXMLUtilities; | |
77 | |
78 /** | |
79 * @author <a href="mailto:tim.englich@intevation.de">Tim Englich</a> | |
80 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> | |
81 */ | |
82 public class TimeSeriesOutputState extends OutputStateBase { | |
83 | |
84 /** | |
85 * A boolean property to enable caching of charts after they have been | |
86 * created. This property can be adjusted via system property 'cache.chart'. | |
87 * 'true' of 'false' are valid options - defaults to 'false'.<br> | |
88 * <b>NOTE:</b> If chart caching is enabled, charts aren't sensible to | |
89 * changes in the parameterization anymore, after a first chart has been | |
90 * created for an artifact. | |
91 */ | |
92 protected static final boolean CACHE_CHART = | |
93 Boolean.parseBoolean(System.getProperty("cache.chart", "false")); | |
94 | |
95 /** | |
96 * Supported image export formats. | |
97 */ | |
98 protected static final String[] IMG_EXPORT_FORMAT = { | |
99 "PNG", "JPEG", "GIF" | |
100 }; | |
101 | |
102 /** | |
103 * The UID of this Class | |
104 */ | |
105 private static final long serialVersionUID = 4178407570503098858L; | |
106 | |
107 /** | |
108 * the logger, used to log exceptions and additonaly information | |
109 */ | |
110 private static Logger log = Logger | |
111 .getLogger(TimeSeriesOutputState.class); | |
112 | |
113 protected List<TimeGap> timeGapDefinitions = null; | |
114 | |
115 /** | |
116 * Key in resource bundle the x-axis title is stored. | |
117 */ | |
118 protected String domainLable = "chart.timeseries.title.xaxis"; | |
119 | |
120 protected String featureValuesName = "featureid"; | |
121 protected String parameterValuesName = "parameterid"; | |
122 protected String measuremenValueName = "measurementid"; | |
123 protected String dateValueName = "dateid"; | |
124 protected String timeIntervalValueName = "timeinterval"; | |
125 | |
126 | |
127 /** | |
128 * Array used to specify the columns used in csv exports. | |
129 */ | |
130 public static final String [] TIMESERIES_CSV_PROFILE_COLUMNS = { | |
131 "XORDINATE", | |
132 "YORDINATE", | |
133 "GROUP1", | |
134 "GROUP2", | |
135 "GROUP3" | |
136 }; | |
137 | |
138 | |
139 /** | |
140 * Column labels used in csv exports. | |
141 */ | |
142 public static final String [] TIMESERIES_TIMESERIES_CSV_COLUMN_LABEL = { | |
143 "Date/Time", | |
144 "Value", | |
145 "ParameterID", | |
146 "MeasurementID", | |
147 "TimeseriesID" | |
148 }; | |
149 | |
150 | |
151 /** | |
152 * Array used to specify the column in csv exports on meshes. | |
153 */ | |
154 public static final String [] TIMESERIES_MESH_CSV_COLUMN_LABEL = { | |
155 "Date/Time", | |
156 "Value", | |
157 "ParameterID", | |
158 "FeatureID", | |
159 "MeshID" | |
160 }; | |
161 | |
162 | |
163 /** | |
164 * Array used to specify the columns in odv exports. | |
165 */ | |
166 public static final String [] TIMESERIES_ODV_PROFILE_NAMES = { | |
167 "CRUISE", | |
168 "STATION", | |
169 "TYPE", | |
170 "TIMEVALUE", | |
171 "SHAPE", | |
172 "BOTDEPTH", | |
173 "DEPTH", | |
174 "QF" | |
175 }; | |
176 | |
177 | |
178 /** | |
179 * Column labels used in odv exports. | |
180 */ | |
181 public static final String [] ODV_COLUMN_HEADER = { | |
182 "Cruise", | |
183 "Station", | |
184 "Type", | |
185 "yyyy-mm-dd hh:mm", | |
186 "Lon (°E)", | |
187 "Lat (°N)", | |
188 "Bot. Depth [m]", | |
189 "Depth [m]", | |
190 "QF" | |
191 }; | |
192 | |
193 | |
194 /** | |
195 * Profile for exporting data to odv | |
196 */ | |
197 public static final Profile TIMESERIES_ODV_PROFILE = | |
198 new DefaultProfile( | |
199 ODV_COLUMN_HEADER, | |
200 '\t', | |
201 CSVWriter.NO_QUOTE_CHARACTER, | |
202 CSVWriter.NO_ESCAPE_CHARACTER, | |
203 "ODV", | |
204 "ISO-8859-1"); | |
205 | |
206 | |
207 /** | |
208 * Constructor | |
209 */ | |
210 public TimeSeriesOutputState() { | |
211 super(); | |
212 } | |
213 | |
214 | |
215 /** | |
216 * Calls <code>getChartResult</code> which puts the data used for chart | |
217 * creation into cache. This redruces waiting periods after selecting an | |
218 * output type. | |
219 * | |
220 * @param uuid The UUID of the current artifact. | |
221 * @param context The CallContext object. | |
222 * @throws StateException if an error occured while fetching the data used | |
223 * for chart creation. | |
224 */ | |
225 @Override | |
226 public void initialize(String uuid, CallContext context) | |
227 throws StateException | |
228 { | |
229 getChartResult(uuid, context); | |
230 } | |
231 | |
232 | |
233 /** | |
234 * This out target has following modes:<br> | |
235 * <ol> | |
236 * <li>chart: Creates a chart displaying the data corresponding to the | |
237 * current parameterization. A chart has following export modes:<br> | |
238 * <ol> | |
239 * <li>img: Image representation of a chart.</li> | |
240 * <li>pdf: PDF representation of a chart.</li> | |
241 * <li>svg: SVG representation of a chart.</li> | |
242 * </ol> | |
243 * All selected parameters are drawn into a single chart.<br> | |
244 * </li> | |
245 * <li>histogram: Creates a histogram displaying the data corresponding to | |
246 * the current parameterization. A histogram has following export modes:<br> | |
247 * <ol> | |
248 * <li>img: Image representation of a histogram.</li> | |
249 * <li>pdf: PDF representation of a histogram.</li> | |
250 * <li>svg: SVG representation of a histogram.</li> | |
251 * </ol> | |
252 * A single histogram is created for each selected parameter.<br> | |
253 * </li> | |
254 * <li>statistic: Creates a statistic with important figures.</li> | |
255 * <li>csv: Creates a csv file.</li> | |
256 * <li>odv: Creates a odv file.</li> | |
257 * </ol> | |
258 * | |
259 * @param format Document which contains some export specific information. | |
260 * @param inputData Contains some meta information to adjust the export | |
261 * (e.g. width and height of a chart). | |
262 * @param outputStream The output stream used to return the export. | |
263 * @param uuid The UUID of the current artifact. | |
264 * @param callContext The CallContext object. | |
265 * @throws StateException if an error occured while creating the export | |
266 * object. | |
267 */ | |
268 public void out( | |
269 Document format, | |
270 Collection<InputData> inputData, | |
271 OutputStream outputStream, | |
272 String uuid, | |
273 CallContext callContext | |
274 ) throws StateException | |
275 { | |
276 log.debug("TimeSeriesOutputTransition.out"); | |
277 | |
278 String outputMode = XMLUtils.xpathString( | |
279 format, XPATH_OUTPUT_MODE, ArtifactNamespaceContext.INSTANCE); | |
280 | |
281 String mode = XMLUtils.xpathString( | |
282 format, XPATH_EXPORT_MODE, ArtifactNamespaceContext.INSTANCE); | |
283 | |
284 if (mode == null || mode.equals("")) { | |
285 mode = "img"; | |
286 } | |
287 | |
288 String mimeType = XMLUtils.xpathString( | |
289 format, XPATH_MIME_TYPE, ArtifactNamespaceContext.INSTANCE); | |
290 | |
291 CallMeta callMeta = callContext.getMeta(); | |
292 | |
293 int chartWidth = 600; | |
294 int chartHeight = 400; | |
295 boolean sVisible = false; | |
296 int binCount = 0; | |
297 int binWidth = 0; | |
298 | |
299 Map requestParameter = new HashMap(); | |
300 | |
301 // lines are always visible. if lines should be configurable we need a | |
302 // parameter in the user interface | |
303 boolean lVisible = true; | |
304 | |
305 try { | |
306 if (inputData != null) { | |
307 Iterator<InputData> it = inputData.iterator(); | |
308 while (it.hasNext()) { | |
309 InputData ip = it.next(); | |
310 String optionName = ip.getName().trim(); | |
311 requestParameter.put(optionName, ip.getValue()); | |
312 | |
313 if (optionName.equals("width")) { | |
314 chartWidth = Integer.parseInt(ip.getValue()); | |
315 } | |
316 else if (optionName.equals("height")) { | |
317 chartHeight = Integer.parseInt(ip.getValue()); | |
318 } | |
319 else if (optionName.equals("points")) { | |
320 sVisible = Boolean.parseBoolean(ip.getValue()); | |
321 } | |
322 } | |
323 } | |
324 } catch (NumberFormatException e) { | |
325 log.warn(e, e); | |
326 XMLUtils.toStream( | |
327 feedFailure("not.a.number"), | |
328 outputStream); | |
329 return; | |
330 } | |
331 | |
332 try { | |
333 Collection<KeyValueDescibeData> parameters = | |
334 getParameters(uuid); | |
335 Collection<KeyValueDescibeData> measurements = | |
336 getMeasurements(uuid); | |
337 Collection<KeyValueDescibeData> dates = | |
338 getDates(uuid); | |
339 | |
340 Locale[] serverLocales = | |
341 RessourceFactory.getInstance().getLocales(); | |
342 Locale locale = | |
343 callMeta.getPreferredLocale(serverLocales); | |
344 | |
345 ChartLabels chartLables = createChartLabels(locale, uuid); | |
346 | |
347 log.debug( | |
348 "Best locale - regarding intersection of server and " + | |
349 "browser locales - is " + locale.toString() | |
350 ); | |
351 | |
352 String exportFormat = getExportFormat(mimeType); | |
353 | |
354 // CHART | |
355 if (outputMode.equalsIgnoreCase("chart")) { | |
356 log.debug("Chart will be generated."); | |
357 | |
358 if (mode.equalsIgnoreCase("img")) { | |
359 createChart( | |
360 outputStream, | |
361 parameters, | |
362 measurements, | |
363 dates, | |
364 chartLables, | |
365 callContext, | |
366 uuid, | |
367 exportFormat, | |
368 locale, | |
369 chartWidth, | |
370 chartHeight, | |
371 lVisible, | |
372 sVisible, | |
373 callContext | |
374 ); | |
375 } | |
376 else if (mode.equalsIgnoreCase("pdf")) { | |
377 callContext.putContextValue("chart.width", chartWidth); | |
378 callContext.putContextValue("chart.height", chartHeight); | |
379 callContext.putContextValue("shapes.visible", sVisible); | |
380 callContext.putContextValue("lines.visible", lVisible); | |
381 callContext.putContextValue("locale", locale); | |
382 | |
383 createPDF( | |
384 outputStream, | |
385 parameters, | |
386 measurements, | |
387 dates, | |
388 chartLables, | |
389 uuid, | |
390 "A4", | |
391 callContext | |
392 ); | |
393 } | |
394 else if (mode.equalsIgnoreCase("svg")) { | |
395 createSVG( | |
396 outputStream, | |
397 getParameters(uuid), | |
398 getMeasurements(uuid), | |
399 getDates(uuid), | |
400 createChartLabels(locale, uuid), | |
401 uuid, | |
402 locale, | |
403 chartWidth, | |
404 chartHeight, | |
405 lVisible, | |
406 sVisible, | |
407 callContext | |
408 ); | |
409 } | |
410 } | |
411 // HISTOGRAM | |
412 else if (outputMode.equalsIgnoreCase("histogram")) { | |
413 requestParameter.put("locale", locale); | |
414 | |
415 Chart[] histograms = getHistograms( | |
416 uuid, | |
417 callContext, | |
418 parameters, | |
419 measurements, | |
420 dates, | |
421 requestParameter); | |
422 | |
423 if (mode.equalsIgnoreCase("img")) { | |
424 ChartExportHelper.exportHistograms( | |
425 outputStream, | |
426 histograms, | |
427 exportFormat, | |
428 chartWidth, | |
429 chartHeight | |
430 ); | |
431 } | |
432 else if (mode.equalsIgnoreCase("pdf")) { | |
433 callContext.putContextValue("chart.width", chartWidth); | |
434 callContext.putContextValue("chart.height", chartHeight); | |
435 | |
436 ChartExportHelper.exportHistogramsAsPDF( | |
437 outputStream, | |
438 histograms, | |
439 "A4", | |
440 50F, 50F, 50F, 50F, | |
441 callContext | |
442 ); | |
443 } | |
444 else if (mode.equalsIgnoreCase("svg")) { | |
445 ChartExportHelper.exportHistogramsAsSVG( | |
446 outputStream, | |
447 histograms, | |
448 null, | |
449 chartWidth, | |
450 chartHeight | |
451 ); | |
452 } | |
453 } | |
454 else if (outputMode.equalsIgnoreCase("csv")) { | |
455 log.debug("CSV-File will be generated."); | |
456 Object result = getChartResult(uuid, callContext); | |
457 if (result instanceof Collection) { | |
458 this.createCSV( | |
459 outputStream, | |
460 (Collection<Result>)result); | |
461 } | |
462 } else if (outputMode.equalsIgnoreCase("statistics")) { | |
463 log.debug("Statistics will be generated."); | |
464 | |
465 Collection<StatisticSet> statistics; | |
466 | |
467 Statistics s = getStatisticsGenerator(); | |
468 Object result = getChartResult(uuid, callContext); | |
469 | |
470 if (result != null && s != null) { | |
471 statistics = s.calculateStatistics( | |
472 result, | |
473 parameters, | |
474 measurements, | |
475 dates); | |
476 } | |
477 else { | |
478 statistics = new ArrayList<StatisticSet>(); | |
479 } | |
480 | |
481 Document doc = writeStatistics2XML(statistics, locale); | |
482 | |
483 XMLUtils.toStream(doc, outputStream); | |
484 | |
485 } | |
486 else if (outputMode.equalsIgnoreCase("odv")) { | |
487 createODV(outputStream, uuid, callContext); | |
488 } | |
489 } catch (IOException e) { | |
490 log.error(e, e); | |
491 throw new StateException(e); | |
492 } catch (TechnicalChartException e) { | |
493 log.error(e, e); | |
494 throw new StateException(e); | |
495 } catch (StatisticsException e) { | |
496 log.error(e, e); | |
497 throw new StateException(e); | |
498 } | |
499 } | |
500 | |
501 | |
502 protected Chart[] getHistograms( | |
503 String uuid, | |
504 CallContext callContext, | |
505 Collection<KeyValueDescibeData> parameters, | |
506 Collection<KeyValueDescibeData> measurements, | |
507 Collection<KeyValueDescibeData> dates, | |
508 Map requestParameter | |
509 ) { | |
510 Locale locale = (Locale) requestParameter.get("locale"); | |
511 | |
512 Collection results = (Collection) getChartResult(uuid, callContext); | |
513 ChartTheme theme = createStyle(callContext); | |
514 | |
515 Object[][] data = HistogramHelper.prepareHistogramData( | |
516 results, parameters, measurements, dates); | |
517 | |
518 int size = data.length; | |
519 Chart[] histograms = new Chart[size]; | |
520 | |
521 for (int i = 0; i < size; i++) { | |
522 ChartLabels labels = HistogramHelper.createHistogramLabels( | |
523 uuid, callContext, locale, data[i]); | |
524 | |
525 histograms[i] = new DefaultHistogram( | |
526 labels, data[i], theme, requestParameter); | |
527 } | |
528 | |
529 return histograms; | |
530 } | |
531 | |
532 /** | |
533 * @param outputStream | |
534 * @param uuid | |
535 * @throws IOException | |
536 * @throws StateException | |
537 */ | |
538 protected void createODV(OutputStream outputStream, | |
539 String uuid, | |
540 CallContext callContext) | |
541 throws IOException, | |
542 StateException { | |
543 Collection<Result> odvResult = this.getODVResult(uuid); | |
544 this.createODV(outputStream, odvResult,uuid); | |
545 } | |
546 | |
547 | |
548 /** | |
549 * Retrieves the export format (e.g. png, gif, jpeg). | |
550 * | |
551 * @param mime Export format specified by the incoming request. | |
552 * @return <i>mime</i> if it is supported - otherwise the first format in | |
553 * {@link #IMG_EXPORT_FORMAT}. | |
554 */ | |
555 protected String getExportFormat(String mime) { | |
556 for(int i = 0; i < IMG_EXPORT_FORMAT.length; i++) { | |
557 if (mime.trim().toUpperCase().indexOf(IMG_EXPORT_FORMAT[i]) > 0) | |
558 return IMG_EXPORT_FORMAT[i]; | |
559 } | |
560 | |
561 // no format found relating to mimeType, default export as PNG | |
562 return IMG_EXPORT_FORMAT[0]; | |
563 } | |
564 | |
565 | |
566 /** | |
567 * Returns a collection containing all selected KeyValueDescibeData objects | |
568 * of <i>parameters</i>. | |
569 * | |
570 * @param parameters A collection with KeyValueDescibeData objects. | |
571 * @return a collection cleaned from unselected objects. | |
572 * @deprecated | |
573 */ | |
574 protected Collection getCleanedParameters(Collection parameters) { | |
575 Iterator iter = parameters.iterator(); | |
576 Collection parameter = new Vector(parameters); | |
577 while (iter.hasNext()) { | |
578 KeyValueDescibeData data = (KeyValueDescibeData)iter.next(); | |
579 if (!data.isSelected()) | |
580 parameter.remove(data); | |
581 } | |
582 | |
583 return parameter; | |
584 } | |
585 | |
586 | |
587 /** | |
588 * Calls {@link #getCleanedParameters(java.util.Collection)} with the | |
589 * collection returned by {@link #getParameters(java.lang.String)}. | |
590 * | |
591 * @param uuid The UUID of the current artifact. | |
592 * @return a cleaned collection. | |
593 */ | |
594 protected Collection getCleanedParameters(String uuid) { | |
595 return getCleanedParameters(getParameters(uuid)); | |
596 } | |
597 | |
598 | |
599 /** | |
600 * Create a csv file representing the data corresponding to the current | |
601 * parameterization. | |
602 * | |
603 * @param out The output stream used to export the csv file. | |
604 * @param results The data used for csv file creation. | |
605 * @throws UnsupportedEncodingException if the encoding is not supported. | |
606 * @throws IOException if an error occured while writing to output stream. | |
607 * @throws StateException if an error occured while creating the csv file. | |
608 */ | |
609 protected void createCSV(OutputStream out, Collection<Result> results) | |
610 throws UnsupportedEncodingException, IOException, StateException | |
611 { | |
612 Iterator iter = results.iterator(); | |
613 Result res = iter.hasNext() ? (Result) iter.next() : null; | |
614 | |
615 if (res == null) | |
616 return; | |
617 | |
618 Profile profile = null; | |
619 int dataid = res.getInteger("DATAID").intValue(); | |
620 | |
621 // on meshes | |
622 if (dataid == 2) { | |
623 profile = new DefaultProfile( | |
624 TIMESERIES_MESH_CSV_COLUMN_LABEL, | |
625 ',', | |
626 '"', | |
627 '"', | |
628 "CSV", | |
629 "ISO-8859-1"); | |
630 } | |
631 | |
632 // on timeseries | |
633 else { | |
634 profile = new DefaultProfile( | |
635 TIMESERIES_TIMESERIES_CSV_COLUMN_LABEL, | |
636 ',', | |
637 '"', | |
638 '"', | |
639 "CSV", | |
640 "ISO-8859-1"); | |
641 } | |
642 | |
643 DefaultExport export = new DefaultExport( | |
644 new DefaultDataCollector(TIMESERIES_CSV_PROFILE_COLUMNS)); | |
645 export.create(profile, out, results); | |
646 } | |
647 | |
648 | |
649 /** | |
650 * Create an odv file representing the data corresponding to the current | |
651 * parameterization. | |
652 * | |
653 * @param outputStream The output stream used to export the odv file. | |
654 * @param result The data used for odv file creation. | |
655 * @param uuid The UUID of the current artifact. | |
656 * @throws IOException if an error occured while writing to output stream. | |
657 * @throws StateException if an error occured while creating the odv file. | |
658 */ | |
659 protected void createODV(OutputStream outputStream, | |
660 Collection result, | |
661 String uuid) | |
662 throws IOException, StateException { | |
663 | |
664 Export export = new ODVExport(new SimpleOdvDataCollector( | |
665 TIMESERIES_ODV_PROFILE_NAMES), | |
666 this.getParameters(uuid), | |
667 this.getStartTime()); | |
668 | |
669 if (result == null) | |
670 log.error("#################### RESULT == NULL #################"); | |
671 export.create(TIMESERIES_ODV_PROFILE, outputStream, result); | |
672 } | |
673 | |
674 /** | |
675 * Method that returns the Starttime of an TimeSeries or | |
676 * null if it is not a TimeSeries. | |
677 * @return the Starttime of an TimeSeries or null if it is not a TimeSeries. | |
678 */ | |
679 protected String getStartTime(){ | |
680 InputData data = inputData.get(this.timeIntervalValueName); | |
681 if (data != null){ | |
682 Object describeData = data.getObject(); | |
683 if (describeData instanceof MinMaxDescribeData){ | |
684 String value = ((MinMaxDescribeData)describeData) | |
685 .getMinValue().toString(); | |
686 return value.substring(0,value.lastIndexOf(':')); | |
687 } | |
688 return null; | |
689 }else{ | |
690 return null; | |
691 } | |
692 | |
693 } | |
694 | |
695 /** | |
696 * Returns the statistic generator. | |
697 * | |
698 * @return the statistic generator. | |
699 */ | |
700 protected Statistics getStatisticsGenerator() { | |
701 Statistics s = new TimeseriesStatistics(); | |
702 return s; | |
703 } | |
704 | |
705 | |
706 /** | |
707 * Writes the statistic into an xml document. | |
708 * | |
709 * @param statistic Statistic to be written to xml document. | |
710 * @return the xml document containing the statistic. | |
711 */ | |
712 protected Document writeStatistics2XML( | |
713 Collection<StatisticSet> statistic, Locale locale) | |
714 { | |
715 ArtifactXMLUtilities xmlUtilities = new ArtifactXMLUtilities(); | |
716 Document doc = XMLUtils.newDocument(); | |
717 | |
718 NumberFormat format = NumberFormat.getInstance(locale); | |
719 | |
720 if (statistic != null) { | |
721 Node statisticResults = ArtifactXMLUtilities.createArtifactElement(doc, | |
722 "statistics"); | |
723 doc.appendChild(statisticResults); | |
724 Iterator<StatisticSet> it = statistic.iterator(); | |
725 while (it.hasNext()) { | |
726 StatisticSet set = it.next(); | |
727 Element setElement = ArtifactXMLUtilities.createArtifactElement(doc, | |
728 "statistic"); | |
729 setElement.setAttribute("name", set.getName()); | |
730 | |
731 Iterator<Statistic> sit = set.getStatistics().iterator(); | |
732 while (sit.hasNext()){ | |
733 Statistic s = sit.next(); | |
734 Element result = ArtifactXMLUtilities.createArtifactElement(doc, | |
735 "statistic-value"); | |
736 result.setAttribute("name", s.getKey()); | |
737 result.setAttribute("value", format.format(s.getValue())); | |
738 setElement.appendChild(result); | |
739 } | |
740 statisticResults.appendChild(setElement); | |
741 } | |
742 | |
743 } | |
744 return doc; | |
745 } | |
746 | |
747 | |
748 /** | |
749 * Returns the name of the selected feature. | |
750 * | |
751 * @param uuid The UUID of the current artifact. | |
752 * @return the name of the selectef feature. | |
753 */ | |
754 protected String getSelectedFeatureName(String uuid) { | |
755 Collection values = getCollection(featureValuesName, uuid); | |
756 | |
757 if (values != null) { | |
758 Iterator it = values.iterator(); | |
759 | |
760 while (it.hasNext()) { | |
761 KeyValueDescibeData data = (KeyValueDescibeData) it.next(); | |
762 return data.getValue(); | |
763 } | |
764 | |
765 return ""; | |
766 } | |
767 return ""; | |
768 } | |
769 | |
770 | |
771 /** | |
772 * Creates a chart and writes it to <i>outputStream</i>. | |
773 * | |
774 * @param outputStream The stream used to write the chart to. | |
775 * @param parameters A collection with parameters. | |
776 * @param measurements A collection with measurements. | |
777 * @param dates A collection with dates. | |
778 * @param chartLables The labels used to decorate the chart. | |
779 * @param context The CallContext. | |
780 * @param uuid The UUID of the current artifact. | |
781 * @param exportFormat The format the chart used to be exported as. | |
782 * @param locale The Locale to specify the language. | |
783 * @param width The width of the chart. | |
784 * @param height The height of the chart. | |
785 * @param linesVisible A boolean property to determine the visibility of | |
786 * lines connecting two datapoins. | |
787 * @param shapesVisible A boolean property to determine the visibility of | |
788 * datapoints. | |
789 * @param callContext The CallContext object. | |
790 * @throws IOException if an error occured while writing to output stream. | |
791 * @throws TechnicalChartException if an error occured while chart creation. | |
792 */ | |
793 protected void createChart( | |
794 OutputStream outputStream, | |
795 Collection parameters, | |
796 Collection measurements, | |
797 Collection dates, | |
798 ChartLabels chartLables, | |
799 CallContext context, | |
800 String uuid, | |
801 String exportFormat, | |
802 Locale locale, | |
803 int width, | |
804 int height, | |
805 boolean linesVisible, | |
806 boolean shapesVisible, | |
807 CallContext callContext | |
808 ) | |
809 throws IOException, TechnicalChartException | |
810 { | |
811 log.debug("Create chart."); | |
812 Chart chart = getChart( | |
813 chartLables, | |
814 createStyle(context), | |
815 parameters, | |
816 measurements, | |
817 dates, | |
818 getChartResult(uuid, callContext), | |
819 locale, // Locale | |
820 uuid, | |
821 linesVisible, | |
822 shapesVisible, | |
823 callContext | |
824 ); | |
825 | |
826 if (chart == null) { | |
827 log.error("Could not initialize chart."); | |
828 return; | |
829 } | |
830 | |
831 log.debug( | |
832 "export chart as " + exportFormat + | |
833 " in " + width + "x" + height | |
834 ); | |
835 | |
836 ChartExportHelper.exportImage( | |
837 outputStream, | |
838 chart.generateChart(), | |
839 exportFormat, | |
840 width, | |
841 height | |
842 ); | |
843 } | |
844 | |
845 | |
846 /** | |
847 * Creates a chart and writes it as pdf to <i>outputStream</i>. | |
848 * | |
849 * @param outputStream The stream used to write the chart to. | |
850 * @param parameters A collection with parameters. | |
851 * @param measurements A collection with measurements. | |
852 * @param dates A collection with dates. | |
853 * @param chartLables The labels used to decorate the chart. | |
854 * @param uuid The UUID of the current artifact. | |
855 * @param exportFormat The format the chart used to be exported as. | |
856 * @param landscape A boolean property to determine the alignment of the | |
857 * chart. | |
858 * @param linesVisible A boolean property to determine the visibility of | |
859 * lines connecting two datapoins. | |
860 * @param shapesVisible A boolean property to determine the visibility of | |
861 * datapoints. | |
862 * @param locale The Locale to specify the language. | |
863 * @param context The CallContext object. | |
864 */ | |
865 protected void createPDF( | |
866 OutputStream outputStream, | |
867 Collection parameters, | |
868 Collection measurements, | |
869 Collection dates, | |
870 ChartLabels chartLables, | |
871 String uuid, | |
872 String exportFormat, | |
873 CallContext context | |
874 ) { | |
875 Chart chart = getChart( | |
876 chartLables, | |
877 createStyle(context), | |
878 parameters, | |
879 measurements, | |
880 dates, | |
881 getChartResult(uuid, context), | |
882 (Locale) context.getContextValue("locale"), | |
883 uuid, | |
884 Boolean.TRUE.equals(context.getContextValue("lines.visible")), | |
885 Boolean.TRUE.equals(context.getContextValue("shapes.visible")), | |
886 context | |
887 ); | |
888 | |
889 if (chart == null) { | |
890 log.error("Could not initialize chart."); | |
891 return; | |
892 } | |
893 | |
894 ChartExportHelper.exportPDF( | |
895 outputStream, | |
896 chart.generateChart(), | |
897 "A4", | |
898 50F, 50F, 50F, 50F, | |
899 context | |
900 ); | |
901 } | |
902 | |
903 | |
904 /** | |
905 * Creates a chart and writes it as svg to <i>outputStream</i>. | |
906 * | |
907 * @param outputStream The stream used to write the chart to. | |
908 * @param parameters A collection with parameters. | |
909 * @param measurements A collection with measurements. | |
910 * @param dates A collection with dates. | |
911 * @param chartLables The labels used to decorate the chart. | |
912 * @param uuid The UUID of the current artifact. | |
913 * @param locale The Locale to specify the language. | |
914 * @param width The width of the chart. | |
915 * @param height The height of the chart. | |
916 * @param linesVisible A boolean property to determine the visibility of | |
917 * lines connecting two datapoins. | |
918 * @param shapesVisible A boolean property to determine the visibility of | |
919 * datapoints. | |
920 * @param callContext The CallContext object. | |
921 */ | |
922 protected void createSVG( | |
923 OutputStream outputStream, | |
924 Collection parameters, | |
925 Collection measurements, | |
926 Collection dates, | |
927 ChartLabels chartLables, | |
928 String uuid, | |
929 Locale locale, | |
930 int width, | |
931 int height, | |
932 boolean linesVisible, | |
933 boolean shapesVisible, | |
934 CallContext callContext | |
935 ) { | |
936 Chart chart = getChart( | |
937 chartLables, | |
938 createStyle(callContext), | |
939 parameters, | |
940 measurements, | |
941 dates, | |
942 getChartResult(uuid, callContext), | |
943 locale, | |
944 uuid, | |
945 linesVisible, | |
946 shapesVisible, | |
947 callContext | |
948 ); | |
949 | |
950 if (chart == null) { | |
951 log.error("Could not initialize chart."); | |
952 return; | |
953 } | |
954 | |
955 ChartExportHelper.exportSVG( | |
956 outputStream, | |
957 chart.generateChart(), | |
958 null, | |
959 width, height | |
960 ); | |
961 | |
962 log.debug("svg export finished."); | |
963 } | |
964 | |
965 | |
966 /** | |
967 * This method creates a chart and returns it. In normal case, this is the | |
968 * only method to be overriden by subclasses to create other types of | |
969 * charts. | |
970 * | |
971 * @param chartLables Labels used to decorate the chart. | |
972 * @param theme The theme used to adjust the look of the chart. | |
973 * @param parameters A collection with parameters this chart contains. | |
974 * @param measurements A collection with measurement this chart contains. | |
975 * @param dates A collection with dates this chart contains. | |
976 * @param result The data collection used to be displayed in this chart. | |
977 * @param locale The Locale used to determine the language. | |
978 * @param uuid The uuid of the current artifact. | |
979 * @param linesVisible A boolean property to determine the visibility of | |
980 * lines connecting two points in a chart (not used in this chart type). | |
981 * @param shapesVisible A boolean property to determine the visiblity of | |
982 * datapoints in this chart (not used in this chart type). | |
983 * @param callContext The CallContext object. | |
984 * @return a timeseries chart. | |
985 */ | |
986 protected Chart getChart( | |
987 ChartLabels chartLables, | |
988 ChartTheme theme, | |
989 Collection parameters, | |
990 Collection measurements, | |
991 Collection dates, | |
992 Object result, | |
993 Locale locale, | |
994 String uuid, | |
995 boolean linesVisible, | |
996 boolean shapesVisible, | |
997 CallContext callContext | |
998 ) { | |
999 Chart chart = null; | |
1000 | |
1001 if (CACHE_CHART) { | |
1002 log.info("Try to get timeseries chart from cache."); | |
1003 chart = (Chart) getChartFromCache(uuid, callContext); | |
1004 } | |
1005 | |
1006 if (chart != null) | |
1007 return chart; | |
1008 | |
1009 log.info("Chart not in cache yet."); | |
1010 chart = new TimeSeriesChart( | |
1011 chartLables, | |
1012 theme, | |
1013 parameters, | |
1014 measurements, | |
1015 dates, | |
1016 (Collection)result, | |
1017 timeGapDefinitions, | |
1018 locale, | |
1019 linesVisible, | |
1020 shapesVisible | |
1021 ); | |
1022 chart.generateChart(); | |
1023 | |
1024 if (CACHE_CHART) { | |
1025 log.info("Put chart into cache."); | |
1026 purifyChart(chart, uuid); | |
1027 } | |
1028 | |
1029 return chart; | |
1030 } | |
1031 | |
1032 | |
1033 /** | |
1034 * Fetches the ChartTheme from <i>callContext</i> and returns it. | |
1035 * | |
1036 * @param callContext CallContext objects storing a chart theme. | |
1037 * @return a chart theme. | |
1038 */ | |
1039 protected ChartTheme createStyle(CallContext callContext) { | |
1040 log.debug("Fetch chart theme from global context"); | |
1041 | |
1042 GNVArtifactContext context = | |
1043 (GNVArtifactContext) callContext.globalContext(); | |
1044 | |
1045 XMLChartTheme theme = (XMLChartTheme) context.get( | |
1046 GNVArtifactContext.CHART_TEMPLATE_KEY); | |
1047 | |
1048 return theme; | |
1049 } | |
1050 | |
1051 /** | |
1052 * Creates a ChartLabels object storing different labels used to decorate a | |
1053 * chart. | |
1054 * | |
1055 * @param locale The Locale object to adjust the language of labels. | |
1056 * @param uuid The UUID of the current artifact. | |
1057 * @return the chart labels. | |
1058 */ | |
1059 protected ChartLabels createChartLabels(Locale locale, String uuid) { | |
1060 return new ChartLabels( | |
1061 createChartTitle(locale, uuid), | |
1062 createChartSubtitle(locale, uuid), | |
1063 RessourceFactory.getInstance().getRessource( | |
1064 locale, | |
1065 domainLable, | |
1066 domainLable | |
1067 ) | |
1068 ); | |
1069 } | |
1070 | |
1071 | |
1072 /** | |
1073 * Creates and returns the chart title. | |
1074 * | |
1075 * @param locale The Locale used to adjust the language of the title. | |
1076 * @param uuid The UUID of the current artifact. | |
1077 * @return the name of the selected fis. | |
1078 */ | |
1079 protected String createChartTitle(Locale locale, String uuid) { | |
1080 return getFisName(locale); | |
1081 | |
1082 } | |
1083 | |
1084 | |
1085 /** | |
1086 * Creates and returns the subtitle of a chart. The subtitle in this class | |
1087 * contains the station and the time interval. | |
1088 * | |
1089 * @param locale The Locale used to adjust the language of the subtitle. | |
1090 * @param uuid The UUID of the current artifact. | |
1091 * @return the selected feature. | |
1092 */ | |
1093 protected String createChartSubtitle(Locale locale, String uuid) { | |
1094 String subtitle = ""; | |
1095 subtitle += getSelectedFeatureName(uuid); | |
1096 | |
1097 InputData data = inputData.get(timeIntervalValueName); | |
1098 if (data != null){ | |
1099 Object describeData = data.getObject(); | |
1100 if (describeData instanceof MinMaxDescribeData) { | |
1101 MinMaxDescribeData tmp = (MinMaxDescribeData) describeData; | |
1102 | |
1103 DateFormat format = DateFormat.getDateTimeInstance( | |
1104 DateFormat.MEDIUM, DateFormat.SHORT, locale); | |
1105 | |
1106 String lDateStr = (String) tmp.getMinValue(); | |
1107 String rDateStr = (String) tmp.getMaxValue(); | |
1108 | |
1109 try { | |
1110 Date lDate = srcFormat.parse(lDateStr); | |
1111 Date rDate = srcFormat.parse(rDateStr); | |
1112 | |
1113 String interval = format.format(lDate); | |
1114 interval += " - "; | |
1115 interval += format.format(rDate); | |
1116 | |
1117 if (subtitle != null && subtitle.length() > 0) | |
1118 subtitle += ", "; | |
1119 | |
1120 subtitle += interval; | |
1121 } | |
1122 catch (ParseException pe) { | |
1123 log.error(pe, pe); | |
1124 } | |
1125 } | |
1126 } | |
1127 | |
1128 return subtitle; | |
1129 } | |
1130 | |
1131 | |
1132 /** | |
1133 * Returns the selected fis name. | |
1134 * | |
1135 * @param locale The Locale object used to adjust the language. | |
1136 * @return the name of the fis. | |
1137 */ | |
1138 protected String getFisName(Locale locale) { | |
1139 String returnValue = ""; | |
1140 InputData input = inputData.get("fisname"); | |
1141 | |
1142 if (input != null) { | |
1143 String value = input.getValue(); | |
1144 | |
1145 returnValue = RessourceFactory.getInstance().getRessource( | |
1146 locale, | |
1147 value, | |
1148 value | |
1149 ); | |
1150 } | |
1151 return returnValue; | |
1152 } | |
1153 | |
1154 | |
1155 /** | |
1156 * Returns the name of the selected object in the collection specified by | |
1157 * <i>key</i>. | |
1158 * | |
1159 * @param uuid The UUID of the current artifat. | |
1160 * @param key A key to specify a collection. | |
1161 * @return the name of the selected item. | |
1162 */ | |
1163 protected String getSelectedInputDataName(String uuid, String key) { | |
1164 Collection values = getCollection(key, uuid); | |
1165 | |
1166 if (values != null) { | |
1167 Iterator it = values.iterator(); | |
1168 | |
1169 while (it.hasNext()) { | |
1170 KeyValueDescibeData data = (KeyValueDescibeData) it.next(); | |
1171 | |
1172 if (data.isSelected()) { | |
1173 return data.getValue(); | |
1174 } | |
1175 } | |
1176 } | |
1177 return null; | |
1178 } | |
1179 | |
1180 | |
1181 /** | |
1182 * Returns a collection of selected parameters. | |
1183 * | |
1184 * @param uuid The UUID of the current artifact. | |
1185 * @return selected parameters. | |
1186 */ | |
1187 protected Collection<KeyValueDescibeData> getParameters(String uuid) { | |
1188 return this.getCollection(parameterValuesName, uuid); | |
1189 } | |
1190 | |
1191 /** | |
1192 * Returns a collection of selected measurements. | |
1193 * | |
1194 * @param uuid The UUID of the current artifact. | |
1195 * @return selected measurements. | |
1196 */ | |
1197 protected Collection<KeyValueDescibeData> getMeasurements(String uuid) { | |
1198 return this.getCollection(measuremenValueName, uuid); | |
1199 } | |
1200 | |
1201 /** | |
1202 * Returns a collection of selected dates. | |
1203 * | |
1204 * @param uuid The UUID of the current artifact. | |
1205 * @return selected dates. | |
1206 */ | |
1207 protected Collection<KeyValueDescibeData> getDates(String uuid) { | |
1208 return this.getCollection(dateValueName,uuid); | |
1209 } | |
1210 | |
1211 @Override | |
1212 public void setup(Node configuration) { | |
1213 super.setup(configuration); | |
1214 String featureNameValue = Config.getStringXPath(configuration, | |
1215 "value-names/value-name[@name='feature']/@value"); | |
1216 if (featureNameValue != null) { | |
1217 this.featureValuesName = featureNameValue; | |
1218 } | |
1219 String parameterNameValue = Config.getStringXPath(configuration, | |
1220 "value-names/value-name[@name='parameter']/@value"); | |
1221 if (parameterNameValue != null) { | |
1222 this.parameterValuesName = parameterNameValue; | |
1223 } | |
1224 String measurementNameValue = Config.getStringXPath(configuration, | |
1225 "value-names/value-name[@name='measurement']/@value"); | |
1226 if (measurementNameValue != null) { | |
1227 this.measuremenValueName = measurementNameValue; | |
1228 } | |
1229 String timeIntervalValue = Config.getStringXPath(configuration, | |
1230 "value-names/value-name[@name='timeinterval']/@value"); | |
1231 if (timeIntervalValue != null){ | |
1232 this.timeIntervalValueName = timeIntervalValue; | |
1233 } | |
1234 | |
1235 String dateNameValue = Config.getStringXPath(configuration, | |
1236 "value-names/value-name[@name='date']/@value"); | |
1237 if (dateNameValue != null) { | |
1238 this.dateValueName = dateNameValue; | |
1239 } | |
1240 if (timeGapDefinitions == null){ | |
1241 Element gapDefinition = (Element)Config.getNodeXPath(configuration, | |
1242 "time-gap-definition"); | |
1243 synchronized (this.getClass()) { | |
1244 if (gapDefinition != null){ | |
1245 String link = gapDefinition.getAttribute("xlink:href"); | |
1246 if (link != null ){ | |
1247 String absolutFileName = Config.replaceConfigDir(link); | |
1248 gapDefinition = (Element)new ArtifactXMLUtilities(). | |
1249 readConfiguration(absolutFileName); | |
1250 } | |
1251 | |
1252 NodeList gapDefinitions = Config.getNodeSetXPath(gapDefinition, | |
1253 "/time-gaps/time-gap"); | |
1254 if (gapDefinition != null){ | |
1255 timeGapDefinitions = new ArrayList<TimeGap>(gapDefinitions. | |
1256 getLength()); | |
1257 for (int i = 0; i < gapDefinitions.getLength(); i++){ | |
1258 Element gapNode = (Element)gapDefinitions.item(i); | |
1259 String unit = gapNode.getAttribute("unit"); | |
1260 int key = Integer.parseInt(gapNode.getAttribute("key")); | |
1261 int value = Integer.parseInt(gapNode.getAttribute("gap")); | |
1262 log.info("Add new Timegap: "+key+" "+value+" "+ unit); | |
1263 timeGapDefinitions.add(new DefaultTimeGap(unit, | |
1264 key, | |
1265 value)); | |
1266 } | |
1267 } | |
1268 } | |
1269 } | |
1270 } | |
1271 } | |
1272 | |
1273 | |
1274 /** | |
1275 * Creates a <code>NamedArrayList</code> storing some <code> | |
1276 * KeyValueDescibeData</code> objects found in the input data with the key | |
1277 * <i>collectionName</i>. | |
1278 * | |
1279 * @param collectionName String to specify the input data. | |
1280 * @param uuid The UUID of the current artifact. | |
1281 * @return a collection with values from input data. | |
1282 */ | |
1283 protected Collection<KeyValueDescibeData> getCollection( | |
1284 String collectionName, | |
1285 String uuid) | |
1286 { | |
1287 log.debug("Search for input data: " + collectionName); | |
1288 NamedCollection<KeyValueDescibeData> c = new NamedArrayList<KeyValueDescibeData>(collectionName); | |
1289 | |
1290 InputData data = null; | |
1291 State parent = this; | |
1292 do { | |
1293 data = parent.inputData().get(collectionName); | |
1294 | |
1295 if (data != null) { | |
1296 break; | |
1297 } | |
1298 } | |
1299 while ((parent = parent.getParent()) != null); | |
1300 | |
1301 if (data == null) { | |
1302 log.warn("No collection found with name: " + collectionName); | |
1303 return c; | |
1304 } | |
1305 | |
1306 String[] descs = data.getDescription(); | |
1307 String[] values = data.splitValue(); | |
1308 int size = values.length; | |
1309 | |
1310 for (int i = 0; i < size; i++){ | |
1311 c.add(new DefaultKeyValueDescribeData( | |
1312 values[i], descs[i], getID())); | |
1313 } | |
1314 | |
1315 return c; | |
1316 } | |
1317 } | |
1318 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : |