tim@335: package de.intevation.gnv.state.timeseries;
tim@335:
sascha@480: import java.io.IOException;
tim@335: import java.io.OutputStream;
tim@335: import java.io.UnsupportedEncodingException;
tim@335: import java.util.ArrayList;
tim@335: import java.util.Collection;
ingo@629: import java.util.HashMap;
tim@335: import java.util.Iterator;
tim@335: import java.util.List;
tim@335: import java.util.Locale;
ingo@629: import java.util.Map;
tim@335: import java.util.Vector;
tim@335:
tim@335: import org.apache.log4j.Logger;
sascha@480: import org.jfree.chart.ChartTheme;
tim@335: import org.w3c.dom.Document;
tim@335: import org.w3c.dom.Element;
tim@335: import org.w3c.dom.Node;
tim@335: import org.w3c.dom.NodeList;
tim@335:
tim@812: import au.com.bytecode.opencsv.CSVWriter;
tim@812: import de.intevation.artifactdatabase.Config;
tim@812: import de.intevation.artifactdatabase.XMLUtils;
tim@812: import de.intevation.artifacts.ArtifactNamespaceContext;
tim@812: import de.intevation.artifacts.CallContext;
tim@812: import de.intevation.artifacts.CallMeta;
tim@812: import de.intevation.gnv.artifacts.context.GNVArtifactContext;
tim@812: import de.intevation.gnv.artifacts.ressource.RessourceFactory;
tim@812: import de.intevation.gnv.chart.Chart;
tim@812: import de.intevation.gnv.chart.ChartLabels;
tim@812: import de.intevation.gnv.chart.DefaultHistogram;
tim@812: import de.intevation.gnv.chart.TimeSeriesChart;
tim@812: import de.intevation.gnv.chart.XMLChartTheme;
tim@812: import de.intevation.gnv.chart.exception.TechnicalChartException;
tim@812: import de.intevation.gnv.exports.ChartExportHelper;
tim@812: import de.intevation.gnv.exports.DefaultDataCollector;
tim@812: import de.intevation.gnv.exports.DefaultExport;
tim@812: import de.intevation.gnv.exports.DefaultProfile;
tim@812: import de.intevation.gnv.exports.Export;
tim@812: import de.intevation.gnv.exports.ODVExport;
tim@812: import de.intevation.gnv.exports.SimpleOdvDataCollector;
tim@812: import de.intevation.gnv.exports.Export.Profile;
tim@812: import de.intevation.gnv.geobackend.base.Result;
tim@812: import de.intevation.gnv.histogram.HistogramHelper;
tim@812: import de.intevation.gnv.state.InputData;
tim@812: import de.intevation.gnv.state.OutputStateBase;
tim@812: import de.intevation.gnv.state.describedata.DefaultKeyValueDescribeData;
tim@812: import de.intevation.gnv.state.describedata.KeyValueDescibeData;
tim@812: import de.intevation.gnv.state.describedata.MinMaxDescribeData;
tim@812: import de.intevation.gnv.state.describedata.NamedArrayList;
tim@812: import de.intevation.gnv.state.describedata.NamedCollection;
tim@812: import de.intevation.gnv.state.exception.StateException;
tim@812: import de.intevation.gnv.statistics.Statistic;
tim@812: import de.intevation.gnv.statistics.StatisticSet;
tim@812: import de.intevation.gnv.statistics.Statistics;
tim@812: import de.intevation.gnv.statistics.TimeseriesStatistics;
tim@812: import de.intevation.gnv.statistics.exception.StatisticsException;
tim@812: import de.intevation.gnv.timeseries.gap.DefaultTimeGap;
tim@812: import de.intevation.gnv.timeseries.gap.TimeGap;
tim@812: import de.intevation.gnv.utils.ArtifactXMLUtilities;
tim@812:
tim@335: /**
sascha@780: * @author Tim Englich
sascha@780: * @author Ingo Weinzierl
tim@335: */
tim@335: public class TimeSeriesOutputState extends OutputStateBase {
tim@335:
ingo@804: /**
ingo@804: * A boolean property to enable caching of charts after they have been
ingo@804: * created. This property can be adjusted via system property 'cache.chart'.
ingo@804: * 'true' of 'false' are valid options - defaults to 'false'.
ingo@804: * NOTE: If chart caching is enabled, charts aren't sensible to
ingo@804: * changes in the parameterization anymore, after a first chart has been
ingo@804: * created for an artifact.
ingo@804: */
tim@335: protected static final boolean CACHE_CHART =
tim@335: Boolean.parseBoolean(System.getProperty("cache.chart", "false"));
tim@335:
ingo@804: /**
ingo@804: * A field parsing a system property to adjust the alignment of pdf exports.
ingo@804: * The system property is 'export.pdf.landscape' and should be true or
ingo@804: * false. If this property is true, PDFs will be created in landscape
ingo@804: * format.
ingo@804: */
tim@335: protected static final boolean PDF_FORMAT_LANDSCAPE =
tim@335: Boolean.parseBoolean(System.getProperty("export.pdf.landscape","true"));
tim@335:
ingo@804: /**
ingo@804: * Supported image export formats.
ingo@804: */
tim@335: protected static final String[] IMG_EXPORT_FORMAT = {
tim@335: "PNG", "JPEG", "GIF"
tim@335: };
tim@335:
tim@335: /**
tim@335: * The UID of this Class
tim@335: */
tim@335: private static final long serialVersionUID = 4178407570503098858L;
tim@335:
tim@335: /**
tim@335: * the logger, used to log exceptions and additonaly information
tim@335: */
tim@335: private static Logger log = Logger
tim@335: .getLogger(TimeSeriesOutputState.class);
tim@335:
ingo@781: private List timeGapDefinitions = null;
tim@335:
ingo@804: /**
ingo@804: * Key in resource bundle the x-axis title is stored.
ingo@804: */
ingo@342: protected String domainLable = "chart.timeseries.title.xaxis";
tim@335:
tim@335: protected String featureValuesName = "featureid";
tim@335: protected String parameterValuesName = "parameterid";
tim@335: protected String measuremenValueName = "measurementid";
tim@335: protected String dateValueName = "dateid";
tim@812: protected String timeIntervalValueName = "timeinterval";
ingo@368:
ingo@804:
ingo@804: /**
ingo@804: * Array used to specify the columns used in csv exports.
ingo@804: */
ingo@368: public static final String [] TIMESERIES_CSV_PROFILE_COLUMNS = {
tim@335: "XORDINATE",
tim@335: "YORDINATE",
tim@335: "GROUP1",
tim@335: "GROUP2",
tim@335: "GROUP3"
tim@335: };
ingo@368:
ingo@368:
ingo@804: /**
ingo@804: * Column labels used in csv exports.
ingo@804: */
ingo@368: public static final String [] TIMESERIES_TIMESERIES_CSV_COLUMN_LABEL = {
ingo@368: "Date/Time",
ingo@368: "Value",
ingo@368: "ParameterID",
ingo@368: "MeasurementID",
ingo@368: "TimeseriesID"
ingo@368: };
ingo@368:
ingo@804:
ingo@804: /**
ingo@804: * Array used to specify the column in csv exports on meshes.
ingo@804: */
ingo@368: public static final String [] TIMESERIES_MESH_CSV_COLUMN_LABEL = {
ingo@368: "Date/Time",
ingo@368: "Value",
ingo@368: "ParameterID",
ingo@368: "FeatureID",
ingo@368: "MeshID"
ingo@368: };
ingo@368:
ingo@804:
ingo@804: /**
ingo@804: * Array used to specify the columns in odv exports.
ingo@804: */
tim@335: public static final String [] TIMESERIES_ODV_PROFILE_NAMES = {
tim@335: "CRUISE",
tim@335: "STATION",
tim@335: "TYPE",
tim@765: "TIMEVALUE",
tim@335: "SHAPE",
tim@335: "BOTDEPTH",
tim@335: "DEPTH",
tim@765: "QF"
tim@335: };
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Column labels used in odv exports.
ingo@804: */
tim@335: public static final String [] ODV_COLUMN_HEADER = {
tim@335: "Cruise",
tim@335: "Station",
tim@335: "Type",
tim@765: "yyyy-mm-dd hh:mm",
tim@765: "Lon (°E)",
tim@765: "Lat (°N)",
tim@335: "Bot. Depth [m]",
tim@335: "Depth [m]",
tim@765: "QF"
tim@335: };
tim@335:
sascha@805:
tim@335: /**
tim@335: * Profile for exporting data to odv
tim@335: */
tim@335: public static final Profile TIMESERIES_ODV_PROFILE =
tim@335: new DefaultProfile(
tim@335: ODV_COLUMN_HEADER,
tim@335: '\t',
tim@335: CSVWriter.NO_QUOTE_CHARACTER,
tim@335: CSVWriter.NO_ESCAPE_CHARACTER,
tim@335: "ODV",
tim@335: "ISO-8859-1");
tim@335:
tim@335: /**
tim@335: * Constructor
tim@335: */
tim@335: public TimeSeriesOutputState() {
tim@335: super();
tim@335: }
tim@335:
ingo@536:
ingo@804: /**
ingo@804: * Calls getChartResult
which puts the data used for chart
ingo@804: * creation into cache. This redruces waiting periods after selecting an
ingo@804: * output type.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param context The CallContext object.
ingo@804: * @throws StateException if an error occured while fetching the data used
ingo@804: * for chart creation.
ingo@804: */
ingo@536: @Override
ingo@536: public void initialize(String uuid, CallContext context)
ingo@536: throws StateException
ingo@536: {
ingo@536: getChartResult(uuid, context);
ingo@536: }
ingo@536:
ingo@536:
tim@335: /**
ingo@804: * This out target has following modes:
ingo@804: *
ingo@804: * - chart: Creates a chart displaying the data corresponding to the
ingo@804: * current parameterization. A chart has following export modes:
ingo@804: *
ingo@804: * - img: Image representation of a chart.
ingo@804: * - pdf: PDF representation of a chart.
ingo@804: * - svg: SVG representation of a chart.
ingo@804: *
ingo@804: * All selected parameters are drawn into a single chart.
ingo@804: *
ingo@804: * - histogram: Creates a histogram displaying the data corresponding to
ingo@804: * the current parameterization. A histogram has following export modes:
ingo@804: *
ingo@804: * - img: Image representation of a histogram.
ingo@804: * - pdf: PDF representation of a histogram.
ingo@804: * - svg: SVG representation of a histogram.
ingo@804: *
ingo@804: * A single histogram is created for each selected parameter.
ingo@804: *
ingo@804: * - statistic: Creates a statistic with important figures.
ingo@804: * - csv: Creates a csv file.
ingo@804: * - odv: Creates a odv file.
ingo@804: *
ingo@804: *
ingo@804: * @param format Document which contains some export specific information.
ingo@804: * @param inputData Contains some meta information to adjust the export
ingo@804: * (e.g. width and height of a chart).
ingo@804: * @param outputStream The output stream used to return the export.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param callContext The CallContext object.
ingo@804: * @throws StateException if an error occured while creating the export
ingo@804: * object.
tim@335: */
tim@335: public void out(
sascha@480: Document format,
tim@335: Collection inputData,
sascha@480: OutputStream outputStream,
sascha@480: String uuid,
sascha@480: CallContext callContext
tim@335: ) throws StateException
tim@335: {
tim@335: log.debug("TimeSeriesOutputTransition.out");
tim@335:
ingo@467: String outputMode = XMLUtils.xpathString(
ingo@467: format, XPATH_OUTPUT_MODE, ArtifactNamespaceContext.INSTANCE);
ingo@467:
ingo@639: String mode = XMLUtils.xpathString(
ingo@639: format, XPATH_EXPORT_MODE, ArtifactNamespaceContext.INSTANCE);
ingo@639:
ingo@639: if (mode == null || mode.equals("")) {
ingo@639: mode = "img";
ingo@639: }
ingo@639:
ingo@467: String mimeType = XMLUtils.xpathString(
ingo@467: format, XPATH_MIME_TYPE, ArtifactNamespaceContext.INSTANCE);
tim@335:
ingo@358: CallMeta callMeta = callContext.getMeta();
ingo@358:
ingo@502: int chartWidth = 600;
ingo@502: int chartHeight = 400;
ingo@502: boolean sVisible = false;
ingo@629: int binCount = 0;
ingo@629: int binWidth = 0;
ingo@629:
ingo@629: Map requestParameter = new HashMap();
ingo@502:
ingo@502: // lines are always visible. if lines should be configurable we need a
ingo@502: // parameter in the user interface
ingo@502: boolean lVisible = true;
ingo@502:
ingo@502: try {
ingo@502: if (inputData != null) {
ingo@502: Iterator it = inputData.iterator();
ingo@502: while (it.hasNext()) {
ingo@502: InputData ip = it.next();
ingo@502: String optionName = ip.getName().trim();
ingo@629: requestParameter.put(optionName, ip.getValue());
ingo@502:
ingo@502: if (optionName.equals("width")) {
ingo@502: chartWidth = Integer.parseInt(ip.getValue());
ingo@502: }
ingo@502: else if (optionName.equals("height")) {
ingo@502: chartHeight = Integer.parseInt(ip.getValue());
ingo@502: }
ingo@502: else if (optionName.equals("points")) {
ingo@502: sVisible = Boolean.parseBoolean(ip.getValue());
ingo@502: }
ingo@502: }
ingo@502: }
ingo@502: } catch (NumberFormatException e) {
ingo@725: log.warn(e, e);
ingo@725: XMLUtils.toStream(
ingo@725: feedFailure("not.a.number"),
ingo@725: outputStream);
ingo@725: return;
ingo@502: }
ingo@502:
tim@335: try {
ingo@639: Collection parameters =
ingo@639: getParameters(uuid);
ingo@639: Collection measurements =
ingo@639: getMeasurements(uuid);
ingo@639: Collection dates =
ingo@639: getDates(uuid);
ingo@639:
ingo@639: Locale[] serverLocales =
ingo@639: RessourceFactory.getInstance().getLocales();
ingo@639: Locale locale =
ingo@639: callMeta.getPreferredLocale(serverLocales);
ingo@639:
ingo@639: ChartLabels chartLables = createChartLabels(locale, uuid);
ingo@639:
ingo@639: log.debug(
ingo@639: "Best locale - regarding intersection of server and " +
ingo@639: "browser locales - is " + locale.toString()
ingo@639: );
ingo@639:
ingo@639: String exportFormat = getExportFormat(mimeType);
ingo@639:
ingo@639: // CHART
tim@335: if (outputMode.equalsIgnoreCase("chart")) {
tim@335: log.debug("Chart will be generated.");
tim@335:
ingo@639: if (mode.equalsIgnoreCase("img")) {
ingo@639: createChart(
ingo@639: outputStream,
ingo@639: parameters,
ingo@639: measurements,
ingo@639: dates,
ingo@639: chartLables,
ingo@639: callContext,
ingo@639: uuid,
ingo@639: exportFormat,
ingo@639: locale,
ingo@639: chartWidth,
ingo@639: chartHeight,
ingo@639: lVisible,
ingo@639: sVisible,
ingo@639: callContext
ingo@639: );
ingo@639: }
ingo@639: else if (mode.equalsIgnoreCase("pdf")) {
ingo@639: createPDF(
ingo@639: outputStream,
ingo@639: parameters,
ingo@639: measurements,
ingo@639: dates,
ingo@639: chartLables,
ingo@639: uuid,
ingo@639: "A4",
ingo@639: true,
ingo@639: lVisible,
ingo@639: sVisible,
ingo@639: locale,
ingo@639: callContext
ingo@639: );
ingo@639: }
ingo@639: else if (mode.equalsIgnoreCase("svg")) {
ingo@639: createSVG(
ingo@639: outputStream,
ingo@639: getParameters(uuid),
ingo@639: getMeasurements(uuid),
ingo@639: getDates(uuid),
ingo@639: createChartLabels(locale, uuid),
ingo@639: uuid,
ingo@639: locale,
ingo@639: chartWidth,
ingo@639: chartHeight,
ingo@639: lVisible,
ingo@639: sVisible,
ingo@639: callContext
ingo@639: );
ingo@639: }
ingo@639: }
ingo@639: // HISTOGRAM
ingo@639: else if (outputMode.equalsIgnoreCase("histogram")) {
ingo@639: Collection results = (Collection) getChartResult(uuid, callContext);
ingo@639: requestParameter.put("locale", locale);
tim@335:
ingo@617: Object[][] data = HistogramHelper.prepareHistogramData(
ingo@617: results, parameters, measurements, dates);
ingo@617:
ingo@617: int size = data.length;
ingo@617: Chart[] histograms = new Chart[size];
ingo@617:
ingo@617: for (int i = 0; i < size; i++) {
ingo@617: ChartLabels labels = createHistogramLabels(
ingo@617: uuid, callContext, data[i]);
ingo@617:
ingo@617: ChartTheme theme = createStyle(callContext);
ingo@617:
ingo@617: histograms[i] = new DefaultHistogram(
ingo@629: labels, data[i], theme, requestParameter);
ingo@617: }
ingo@617:
ingo@639: if (mode.equalsIgnoreCase("img")) {
ingo@639: ChartExportHelper.exportHistograms(
ingo@639: outputStream,
ingo@639: histograms,
ingo@639: exportFormat,
ingo@639: chartWidth,
ingo@639: chartHeight
ingo@639: );
ingo@639: }
ingo@639: else if (mode.equalsIgnoreCase("pdf")) {
ingo@640: ChartExportHelper.exportHistogramsAsPDF(
ingo@640: outputStream,
ingo@640: histograms,
ingo@640: "A4",
ingo@640: PDF_FORMAT_LANDSCAPE,
ingo@640: 50F, 50F, 50F, 50F
ingo@640: );
ingo@639: }
ingo@639: else if (mode.equalsIgnoreCase("svg")) {
ingo@639: ChartExportHelper.exportHistogramsAsSVG(
ingo@639: outputStream,
ingo@639: histograms,
ingo@639: null,
ingo@639: chartWidth,
ingo@639: chartHeight
ingo@639: );
ingo@639: }
tim@335: }
tim@335: else if (outputMode.equalsIgnoreCase("csv")) {
tim@335: log.debug("CSV-File will be generated.");
sascha@452: Object result = getChartResult(uuid, callContext);
sascha@452: if (result instanceof Collection) {
sascha@452: this.createCSV(
sascha@452: outputStream,
sascha@452: (Collection)result);
sascha@452: }
tim@335: } else if (outputMode.equalsIgnoreCase("statistics")) {
tim@335: log.debug("Statistics will be generated.");
sascha@446:
sascha@446: Collection statistics;
sascha@446:
sascha@454: Statistics s = getStatisticsGenerator();
sascha@454: Object result = getChartResult(uuid, callContext);
sascha@446:
sascha@454: if (result != null && s != null) {
sascha@446: statistics = s.calculateStatistics(
sascha@454: result,
sascha@446: parameters,
sascha@446: measurements,
sascha@446: dates);
sascha@446: }
sascha@446: else {
sascha@446: statistics = new ArrayList();
sascha@446: }
sascha@446:
sascha@454: Document doc = writeStatistics2XML(statistics);
sascha@454:
sascha@481: XMLUtils.toStream(doc, outputStream);
sascha@454:
ingo@617: }
ingo@617: else if (outputMode.equalsIgnoreCase("odv")) {
tim@830: createODV(outputStream, uuid, callContext);
tim@335: }
tim@335: } catch (IOException e) {
tim@335: log.error(e, e);
tim@335: throw new StateException(e);
tim@335: } catch (TechnicalChartException e) {
tim@335: log.error(e, e);
tim@335: throw new StateException(e);
tim@335: } catch (StatisticsException e) {
tim@335: log.error(e, e);
tim@335: throw new StateException(e);
tim@335: }
tim@335: }
tim@335:
tim@335:
ingo@804: /**
tim@830: * @param outputStream
tim@830: * @param uuid
tim@830: * @throws IOException
tim@830: * @throws StateException
tim@830: */
tim@830: protected void createODV(OutputStream outputStream,
tim@830: String uuid,
tim@830: CallContext callContext)
tim@830: throws IOException,
tim@830: StateException {
tim@830: Collection odvResult = this.getODVResult(uuid);
tim@830: this.createODV(outputStream, odvResult,uuid);
tim@830: }
tim@830:
tim@830:
tim@830: /**
ingo@804: * Retrieves the export format (e.g. png, gif, jpeg).
ingo@804: *
ingo@804: * @param mime Export format specified by the incoming request.
ingo@804: * @return mime if it is supported - otherwise the first format in
ingo@804: * {@link #IMG_EXPORT_FORMAT}.
ingo@804: */
tim@335: protected String getExportFormat(String mime) {
tim@335: for(int i = 0; i < IMG_EXPORT_FORMAT.length; i++) {
tim@335: if (mime.trim().toUpperCase().indexOf(IMG_EXPORT_FORMAT[i]) > 0)
tim@335: return IMG_EXPORT_FORMAT[i];
tim@335: }
tim@335:
tim@335: // no format found relating to mimeType, default export as PNG
tim@335: return IMG_EXPORT_FORMAT[0];
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Returns a collection containing all selected KeyValueDescibeData objects
ingo@804: * of parameters.
ingo@804: *
ingo@804: * @param parameters A collection with KeyValueDescibeData objects.
ingo@804: * @return a collection cleaned from unselected objects.
ingo@804: * @deprecated
ingo@804: */
tim@335: protected Collection getCleanedParameters(Collection parameters) {
tim@335: Iterator iter = parameters.iterator();
tim@335: Collection parameter = new Vector(parameters);
tim@335: while (iter.hasNext()) {
tim@335: KeyValueDescibeData data = (KeyValueDescibeData)iter.next();
tim@335: if (!data.isSelected())
tim@335: parameter.remove(data);
tim@335: }
tim@335:
tim@335: return parameter;
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Calls {@link #getCleanedParameters(java.util.Collection)} with the
ingo@804: * collection returned by {@link #getParameters(java.lang.String)}.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return a cleaned collection.
ingo@804: */
tim@335: protected Collection getCleanedParameters(String uuid) {
tim@335: return getCleanedParameters(getParameters(uuid));
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Create a csv file representing the data corresponding to the current
ingo@804: * parameterization.
ingo@804: *
ingo@804: * @param out The output stream used to export the csv file.
ingo@804: * @param results The data used for csv file creation.
ingo@804: * @throws UnsupportedEncodingException if the encoding is not supported.
ingo@804: * @throws IOException if an error occured while writing to output stream.
ingo@804: * @throws StateException if an error occured while creating the csv file.
ingo@804: */
ingo@368: protected void createCSV(OutputStream out, Collection results)
ingo@368: throws UnsupportedEncodingException, IOException, StateException
ingo@368: {
ingo@368: Iterator iter = results.iterator();
ingo@368: Result res = iter.hasNext() ? (Result) iter.next() : null;
tim@335:
ingo@368: if (res == null)
ingo@368: return;
ingo@368:
ingo@368: Profile profile = null;
ingo@368: int dataid = res.getInteger("DATAID").intValue();
ingo@368:
ingo@368: // on meshes
ingo@368: if (dataid == 2) {
ingo@368: profile = new DefaultProfile(
ingo@368: TIMESERIES_MESH_CSV_COLUMN_LABEL,
ingo@368: ',',
ingo@368: '"',
ingo@368: '"',
ingo@368: "CSV",
ingo@368: "ISO-8859-1");
ingo@368: }
ingo@368:
ingo@368: // on timeseries
ingo@368: else {
ingo@368: profile = new DefaultProfile(
ingo@368: TIMESERIES_TIMESERIES_CSV_COLUMN_LABEL,
ingo@368: ',',
ingo@368: '"',
ingo@368: '"',
ingo@368: "CSV",
ingo@368: "ISO-8859-1");
ingo@368: }
ingo@368:
ingo@368: DefaultExport export = new DefaultExport(
ingo@368: new DefaultDataCollector(TIMESERIES_CSV_PROFILE_COLUMNS));
ingo@368: export.create(profile, out, results);
tim@335: }
tim@335:
ingo@368:
tim@335: /**
ingo@804: * Create an odv file representing the data corresponding to the current
ingo@804: * parameterization.
sascha@805: *
ingo@804: * @param outputStream The output stream used to export the odv file.
ingo@804: * @param result The data used for odv file creation.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @throws IOException if an error occured while writing to output stream.
ingo@804: * @throws StateException if an error occured while creating the odv file.
tim@335: */
sascha@778: protected void createODV(OutputStream outputStream,
tim@765: Collection result,
tim@765: String uuid)
tim@335: throws IOException, StateException {
tim@335:
tim@765: Export export = new ODVExport(new SimpleOdvDataCollector(
tim@765: TIMESERIES_ODV_PROFILE_NAMES),
tim@812: this.getParameters(uuid),
tim@812: this.getStartTime());
tim@335:
tim@335: if (result == null)
tim@335: log.error("#################### RESULT == NULL #################");
tim@335: export.create(TIMESERIES_ODV_PROFILE, outputStream, result);
tim@335: }
tim@335:
tim@335: /**
tim@812: * Method that returns the Starttime of an TimeSeries or
tim@812: * null if it is not a TimeSeries.
tim@812: * @return the Starttime of an TimeSeries or null if it is not a TimeSeries.
tim@812: */
tim@812: protected String getStartTime(){
tim@812: InputData data = inputData.get(this.timeIntervalValueName);
tim@812: if (data != null){
tim@812: Object describeData = data.getObject();
tim@812: if (describeData instanceof MinMaxDescribeData){
tim@816: String value = ((MinMaxDescribeData)describeData)
tim@816: .getMinValue().toString();
tim@816: return value.substring(0,value.lastIndexOf(':'));
tim@812: }
tim@812: return null;
tim@812: }else{
tim@812: return null;
tim@812: }
tim@812:
tim@812: }
tim@812:
tim@812: /**
ingo@804: * Returns the statistic generator.
ingo@804: *
ingo@804: * @return the statistic generator.
tim@335: */
tim@335: protected Statistics getStatisticsGenerator() {
tim@335: Statistics s = new TimeseriesStatistics();
tim@335: return s;
tim@335: }
tim@335:
ingo@804:
ingo@804: /**
ingo@804: * Writes the statistic into an xml document.
ingo@804: *
ingo@804: * @param statistic Statistic to be written to xml document.
ingo@804: * @return the xml document containing the statistic.
ingo@804: */
tim@335: protected Document writeStatistics2XML( Collection statistic) {
tim@335: ArtifactXMLUtilities xmlUtilities = new ArtifactXMLUtilities();
tim@335: Document doc = XMLUtils.newDocument();
tim@335: if (statistic != null) {
ingo@804: Node statisticResults = ArtifactXMLUtilities.createArtifactElement(doc,
tim@335: "statistics");
tim@335: doc.appendChild(statisticResults);
tim@335: Iterator it = statistic.iterator();
tim@335: while (it.hasNext()) {
tim@335: StatisticSet set = it.next();
ingo@804: Element setElement = ArtifactXMLUtilities.createArtifactElement(doc,
tim@335: "statistic");
tim@335: setElement.setAttribute("name", set.getName());
sascha@778:
tim@335: Iterator sit = set.getStatistics().iterator();
tim@335: while (sit.hasNext()){
tim@335: Statistic s = sit.next();
ingo@804: Element result = ArtifactXMLUtilities.createArtifactElement(doc,
tim@335: "statistic-value");
tim@335: result.setAttribute("name", s.getKey());
tim@335: result.setAttribute("value", s.getStringValue());
tim@335: setElement.appendChild(result);
tim@335: }
tim@335: statisticResults.appendChild(setElement);
tim@335: }
tim@335:
tim@335: }
tim@335: return doc;
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Returns the name of the selected feature.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return the name of the selectef feature.
ingo@804: */
tim@335: protected String getSelectedFeatureName(String uuid) {
tim@335: Collection values = getCollection(featureValuesName, uuid);
tim@335:
tim@335: if (values != null) {
tim@335: Iterator it = values.iterator();
tim@335:
tim@335: while (it.hasNext()) {
tim@335: KeyValueDescibeData data = (KeyValueDescibeData) it.next();
ingo@610: return data.getValue();
ingo@610: }
tim@335:
ingo@610: return "";
tim@335: }
ingo@610: return "";
tim@335: }
tim@335:
tim@335:
tim@335: /**
ingo@804: * Creates a chart and writes it to outputStream.
ingo@804: *
ingo@804: * @param outputStream The stream used to write the chart to.
ingo@804: * @param parameters A collection with parameters.
ingo@804: * @param measurements A collection with measurements.
ingo@804: * @param dates A collection with dates.
ingo@804: * @param chartLables The labels used to decorate the chart.
ingo@804: * @param context The CallContext.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param exportFormat The format the chart used to be exported as.
ingo@804: * @param locale The Locale to specify the language.
ingo@804: * @param width The width of the chart.
ingo@804: * @param height The height of the chart.
ingo@804: * @param linesVisible A boolean property to determine the visibility of
ingo@804: * lines connecting two datapoins.
ingo@804: * @param shapesVisible A boolean property to determine the visibility of
ingo@804: * datapoints.
ingo@804: * @param callContext The CallContext object.
ingo@804: * @throws IOException if an error occured while writing to output stream.
ingo@804: * @throws TechnicalChartException if an error occured while chart creation.
tim@335: */
tim@335: protected void createChart(
tim@335: OutputStream outputStream,
tim@335: Collection parameters,
tim@335: Collection measurements,
tim@335: Collection dates,
tim@335: ChartLabels chartLables,
ingo@358: CallContext context,
tim@335: String uuid,
tim@335: String exportFormat,
tim@335: Locale locale,
tim@335: int width,
tim@335: int height,
tim@335: boolean linesVisible,
sascha@439: boolean shapesVisible,
sascha@439: CallContext callContext
tim@335: )
tim@335: throws IOException, TechnicalChartException
tim@335: {
tim@335: log.debug("Create chart.");
tim@335: Chart chart = getChart(
tim@335: chartLables,
ingo@358: createStyle(context),
tim@335: parameters,
tim@335: measurements,
tim@335: dates,
sascha@439: getChartResult(uuid, callContext),
tim@335: locale, // Locale
tim@335: uuid,
tim@335: linesVisible,
sascha@439: shapesVisible,
sascha@439: callContext
tim@335: );
tim@335:
tim@335: if (chart == null) {
tim@335: log.error("Could not initialize chart.");
tim@335: return;
tim@335: }
tim@335:
tim@335: log.debug(
tim@335: "export chart as " + exportFormat +
tim@335: " in " + width + "x" + height
tim@335: );
tim@335:
tim@335: ChartExportHelper.exportImage(
tim@335: outputStream,
tim@335: chart.generateChart(),
tim@335: exportFormat,
tim@335: width,
tim@335: height
tim@335: );
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Creates a chart and writes it as pdf to outputStream.
ingo@804: *
ingo@804: * @param outputStream The stream used to write the chart to.
ingo@804: * @param parameters A collection with parameters.
ingo@804: * @param measurements A collection with measurements.
ingo@804: * @param dates A collection with dates.
ingo@804: * @param chartLables The labels used to decorate the chart.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param exportFormat The format the chart used to be exported as.
ingo@804: * @param landscape A boolean property to determine the alignment of the
ingo@804: * chart.
ingo@804: * @param linesVisible A boolean property to determine the visibility of
ingo@804: * lines connecting two datapoins.
ingo@804: * @param shapesVisible A boolean property to determine the visibility of
ingo@804: * datapoints.
ingo@804: * @param locale The Locale to specify the language.
ingo@804: * @param context The CallContext object.
ingo@804: */
tim@335: protected void createPDF(
tim@335: OutputStream outputStream,
tim@335: Collection parameters,
tim@335: Collection measurements,
tim@335: Collection dates,
tim@335: ChartLabels chartLables,
tim@335: String uuid,
tim@335: String exportFormat,
tim@335: boolean landscape,
ingo@358: boolean linesVisible,
ingo@358: boolean shapesVisible,
tim@335: Locale locale,
ingo@358: CallContext context
tim@335: ) {
tim@335: Chart chart = getChart(
tim@335: chartLables,
ingo@358: createStyle(context),
tim@335: parameters,
tim@335: measurements,
tim@335: dates,
sascha@452: getChartResult(uuid, context),
tim@335: locale,
tim@335: uuid,
tim@335: linesVisible,
sascha@439: shapesVisible,
sascha@439: context
tim@335: );
tim@335:
tim@335: if (chart == null) {
tim@335: log.error("Could not initialize chart.");
tim@335: return;
tim@335: }
tim@335:
tim@335: ChartExportHelper.exportPDF(
tim@335: outputStream,
tim@335: chart.generateChart(),
tim@335: "A4",
tim@335: PDF_FORMAT_LANDSCAPE,
tim@335: 50F, 50F, 50F, 50F
tim@335: );
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Creates a chart and writes it as svg to outputStream.
ingo@804: *
ingo@804: * @param outputStream The stream used to write the chart to.
ingo@804: * @param parameters A collection with parameters.
ingo@804: * @param measurements A collection with measurements.
ingo@804: * @param dates A collection with dates.
ingo@804: * @param chartLables The labels used to decorate the chart.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param locale The Locale to specify the language.
ingo@804: * @param width The width of the chart.
ingo@804: * @param height The height of the chart.
ingo@804: * @param linesVisible A boolean property to determine the visibility of
ingo@804: * lines connecting two datapoins.
ingo@804: * @param shapesVisible A boolean property to determine the visibility of
ingo@804: * datapoints.
ingo@804: * @param callContext The CallContext object.
ingo@804: */
tim@335: protected void createSVG(
tim@335: OutputStream outputStream,
tim@335: Collection parameters,
tim@335: Collection measurements,
tim@335: Collection dates,
tim@335: ChartLabels chartLables,
tim@335: String uuid,
tim@335: Locale locale,
tim@335: int width,
tim@335: int height,
tim@335: boolean linesVisible,
ingo@358: boolean shapesVisible,
ingo@358: CallContext callContext
tim@335: ) {
tim@335: Chart chart = getChart(
tim@335: chartLables,
ingo@358: createStyle(callContext),
tim@335: parameters,
tim@335: measurements,
tim@335: dates,
sascha@446: getChartResult(uuid, callContext),
tim@335: locale,
tim@335: uuid,
tim@335: linesVisible,
sascha@439: shapesVisible,
sascha@439: callContext
tim@335: );
tim@335:
tim@335: if (chart == null) {
tim@335: log.error("Could not initialize chart.");
tim@335: return;
tim@335: }
tim@335:
tim@335: ChartExportHelper.exportSVG(
tim@335: outputStream,
tim@335: chart.generateChart(),
tim@335: null,
tim@335: 600, 400
tim@335: );
tim@335:
tim@335: log.debug("svg export finished.");
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * This method creates a chart and returns it. In normal case, this is the
ingo@804: * only method to be overriden by subclasses to create other types of
ingo@804: * charts.
ingo@804: *
ingo@804: * @param chartLables Labels used to decorate the chart.
ingo@804: * @param theme The theme used to adjust the look of the chart.
ingo@804: * @param parameters A collection with parameters this chart contains.
ingo@804: * @param measurements A collection with measurement this chart contains.
ingo@804: * @param dates A collection with dates this chart contains.
ingo@804: * @param result The data collection used to be displayed in this chart.
ingo@804: * @param locale The Locale used to determine the language.
ingo@804: * @param uuid The uuid of the current artifact.
ingo@804: * @param linesVisible A boolean property to determine the visibility of
ingo@804: * lines connecting two points in a chart (not used in this chart type).
ingo@804: * @param shapesVisible A boolean property to determine the visiblity of
ingo@804: * datapoints in this chart (not used in this chart type).
ingo@804: * @param callContext The CallContext object.
ingo@804: * @return a timeseries chart.
ingo@804: */
tim@335: protected Chart getChart(
tim@335: ChartLabels chartLables,
ingo@358: ChartTheme theme,
tim@335: Collection parameters,
tim@335: Collection measurements,
tim@335: Collection dates,
ingo@429: Object result,
tim@335: Locale locale,
tim@335: String uuid,
tim@335: boolean linesVisible,
sascha@439: boolean shapesVisible,
sascha@439: CallContext callContext
tim@335: ) {
tim@335: Chart chart = null;
tim@335:
tim@335: if (CACHE_CHART) {
tim@335: log.info("Try to get timeseries chart from cache.");
sascha@439: chart = (Chart) getChartFromCache(uuid, callContext);
tim@335: }
tim@335:
tim@335: if (chart != null)
tim@335: return chart;
tim@335:
tim@335: log.info("Chart not in cache yet.");
tim@335: chart = new TimeSeriesChart(
tim@335: chartLables,
ingo@358: theme,
tim@335: parameters,
tim@335: measurements,
tim@335: dates,
ingo@429: (Collection)result,
tim@335: timeGapDefinitions,
tim@335: locale,
tim@335: linesVisible,
tim@335: shapesVisible
tim@335: );
tim@335: chart.generateChart();
tim@335:
tim@335: if (CACHE_CHART) {
tim@335: log.info("Put chart into cache.");
tim@335: purifyChart(chart, uuid);
tim@335: }
tim@335:
tim@335: return chart;
tim@335: }
tim@335:
ingo@804:
ingo@804: /**
ingo@804: * Fetches the ChartTheme from callContext and returns it.
ingo@804: *
ingo@804: * @param callContext CallContext objects storing a chart theme.
ingo@804: * @return a chart theme.
ingo@804: */
ingo@358: protected ChartTheme createStyle(CallContext callContext) {
ingo@358: log.debug("Fetch chart theme from global context");
tim@335:
ingo@358: GNVArtifactContext context =
ingo@358: (GNVArtifactContext) callContext.globalContext();
ingo@358:
ingo@358: XMLChartTheme theme = (XMLChartTheme) context.get(
sascha@443: GNVArtifactContext.CHART_TEMPLATE_KEY);
tim@335:
tim@335: return theme;
tim@335: }
tim@335:
ingo@804: /**
ingo@804: * Creates a ChartLabels object storing different labels used to decorate a
ingo@804: * chart.
ingo@804: *
ingo@804: * @param locale The Locale object to adjust the language of labels.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return the chart labels.
ingo@804: */
ingo@492: protected ChartLabels createChartLabels(Locale locale, String uuid) {
ingo@492: return new ChartLabels(
ingo@492: createChartTitle(locale, uuid),
ingo@492: createChartSubtitle(locale, uuid),
ingo@492: RessourceFactory.getInstance().getRessource(
ingo@492: locale,
ingo@492: domainLable,
ingo@492: domainLable
ingo@492: )
ingo@492: );
ingo@492: }
ingo@492:
tim@335:
ingo@804: /**
ingo@804: * Creates and returns the chart title.
ingo@804: *
ingo@804: * @param locale The Locale used to adjust the language of the title.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return the name of the selected fis.
ingo@804: */
tim@335: protected String createChartTitle(Locale locale, String uuid) {
tim@335: return getFisName(locale);
tim@335:
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Creates and returns the subtitle of a chart.
ingo@804: *
ingo@804: * @param locale The Locale used to adjust the language of the subtitle.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return the selected feature.
ingo@804: */
tim@335: protected String createChartSubtitle(Locale locale, String uuid) {
tim@335: return getSelectedFeatureName(uuid);
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Creates and returns labels to decorate histograms.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @param context The CallContext object.
ingo@804: * @param data An array storing strings.
ingo@804: * @return A ChartLabels object with the 1st string in data as title.
ingo@804: */
ingo@617: protected ChartLabels createHistogramLabels(
ingo@617: String uuid, CallContext context, Object[] data)
ingo@617: {
ingo@617: return new ChartLabels((String) data[0], "", "");
ingo@617: }
ingo@617:
ingo@617:
ingo@804: /**
ingo@804: * Returns the selected fis name.
ingo@804: *
ingo@804: * @param locale The Locale object used to adjust the language.
ingo@804: * @return the name of the fis.
ingo@804: */
tim@335: protected String getFisName(Locale locale) {
tim@335: String returnValue = "";
tim@335: InputData input = inputData.get("fisname");
tim@335:
tim@335: if (input != null) {
tim@335: String value = input.getValue();
tim@335:
tim@335: returnValue = RessourceFactory.getInstance().getRessource(
tim@335: locale,
tim@335: value,
tim@335: value
tim@335: );
tim@335: }
tim@335: return returnValue;
tim@335: }
tim@335:
tim@335:
ingo@804: /**
ingo@804: * Returns the name of the selected object in the collection specified by
ingo@804: * key.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifat.
ingo@804: * @param key A key to specify a collection.
ingo@804: * @return the name of the selected item.
ingo@804: */
ingo@492: protected String getSelectedInputDataName(String uuid, String key) {
ingo@492: Collection values = getCollection(key, uuid);
ingo@492:
ingo@492: if (values != null) {
ingo@492: Iterator it = values.iterator();
ingo@492:
ingo@492: while (it.hasNext()) {
ingo@492: KeyValueDescibeData data = (KeyValueDescibeData) it.next();
ingo@492:
ingo@492: if (data.isSelected()) {
ingo@492: return data.getValue();
ingo@492: }
ingo@492: }
ingo@492: }
ingo@492: return null;
ingo@492: }
ingo@492:
ingo@492:
ingo@804: /**
ingo@804: * Returns a collection of selected parameters.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return selected parameters.
ingo@804: */
tim@335: protected Collection getParameters(String uuid) {
tim@335: return this.getCollection(parameterValuesName, uuid);
tim@335: }
tim@335:
ingo@804: /**
ingo@804: * Returns a collection of selected measurements.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return selected measurements.
ingo@804: */
tim@335: protected Collection getMeasurements(String uuid) {
tim@335: return this.getCollection(measuremenValueName, uuid);
tim@335: }
ingo@804:
ingo@804: /**
ingo@804: * Returns a collection of selected dates.
ingo@804: *
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return selected dates.
ingo@804: */
tim@335: protected Collection getDates(String uuid) {
tim@335: return this.getCollection(dateValueName,uuid);
tim@335: }
tim@335:
tim@335: @Override
tim@335: public void setup(Node configuration) {
tim@335: super.setup(configuration);
tim@335: String featureNameValue = Config.getStringXPath(configuration,
tim@335: "value-names/value-name[@name='feature']/@value");
tim@335: if (featureNameValue != null) {
tim@335: this.featureValuesName = featureNameValue;
tim@335: }
tim@335: String parameterNameValue = Config.getStringXPath(configuration,
tim@335: "value-names/value-name[@name='parameter']/@value");
tim@335: if (parameterNameValue != null) {
tim@335: this.parameterValuesName = parameterNameValue;
tim@335: }
tim@335: String measurementNameValue = Config.getStringXPath(configuration,
tim@335: "value-names/value-name[@name='measurement']/@value");
tim@335: if (measurementNameValue != null) {
tim@335: this.measuremenValueName = measurementNameValue;
tim@335: }
tim@812: String timeIntervalValue = Config.getStringXPath(configuration,
tim@812: "value-names/value-name[@name='timeinterval']/@value");
tim@812: if (timeIntervalValue != null){
tim@812: this.timeIntervalValueName = timeIntervalValue;
tim@812: }
sascha@778:
tim@335: String dateNameValue = Config.getStringXPath(configuration,
tim@335: "value-names/value-name[@name='date']/@value");
tim@335: if (dateNameValue != null) {
tim@335: this.dateValueName = dateNameValue;
tim@335: }
tim@335: if (timeGapDefinitions == null){
tim@335: Element gapDefinition = (Element)Config.getNodeXPath(configuration,
tim@335: "time-gap-definition");
tim@335: synchronized (this.getClass()) {
tim@335: if (gapDefinition != null){
tim@335: String link = gapDefinition.getAttribute("xlink:href");
tim@335: if (link != null ){
tim@335: String absolutFileName = Config.replaceConfigDir(link);
tim@335: gapDefinition = (Element)new ArtifactXMLUtilities().
tim@335: readConfiguration(absolutFileName);
tim@335: }
sascha@778:
sascha@778: NodeList gapDefinitions = Config.getNodeSetXPath(gapDefinition,
tim@335: "/time-gaps/time-gap");
tim@335: if (gapDefinition != null){
tim@335: timeGapDefinitions = new ArrayList(gapDefinitions.
tim@335: getLength());
tim@335: for (int i = 0; i < gapDefinitions.getLength(); i++){
tim@335: Element gapNode = (Element)gapDefinitions.item(i);
tim@335: String unit = gapNode.getAttribute("unit");
tim@335: int key = Integer.parseInt(gapNode.getAttribute("key"));
tim@335: int value = Integer.parseInt(gapNode.getAttribute("gap"));
tim@335: log.info("Add new Timegap: "+key+" "+value+" "+ unit);
sascha@778: timeGapDefinitions.add(new DefaultTimeGap(unit,
sascha@778: key,
tim@335: value));
tim@335: }
tim@335: }
tim@335: }
tim@335: }
tim@335: }
tim@335: }
ingo@610:
ingo@804:
tim@335: /**
ingo@804: * Creates a NamedArrayList
storing some
ingo@804: * KeyValueDescibeData
objects found in the input data with the key
ingo@804: * collectionName.
ingo@804: *
ingo@804: * @param collectionName String to specify the input data.
ingo@804: * @param uuid The UUID of the current artifact.
ingo@804: * @return a collection with values from input data.
tim@335: */
tim@335: protected Collection getCollection(
ingo@610: String collectionName,
ingo@610: String uuid)
ingo@610: {
ingo@610: NamedCollection c = new NamedArrayList(collectionName);
tim@335:
ingo@610: InputData data = inputData.get(collectionName);
ingo@610: if (data == null) {
ingo@610: log.warn("No collection found with name: " + collectionName);
ingo@610: return c;
tim@335: }
ingo@610:
ingo@615: String[] descs = data.getDescription();
ingo@615: String[] values = data.splitValue();
ingo@615: int size = values.length;
ingo@615:
ingo@615: for (int i = 0; i < size; i++){
ingo@615: c.add(new DefaultKeyValueDescribeData(
ingo@615: values[i], descs[i], getID()));
ingo@615: }
ingo@610:
ingo@610: return c;
tim@335: }
tim@335: }
tim@335: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :