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