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 :

http://dive4elements.wald.intevation.org