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 :

http://dive4elements.wald.intevation.org