Mercurial > dive4elements > gnv-client
diff gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java @ 335:e964a3d8f7bc
Some Refactoring work done.
Moved Transition to State
gnv-artifacts/trunk@401 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Tim Englich <tim.englich@intevation.de> |
---|---|
date | Tue, 08 Dec 2009 08:39:03 +0000 |
parents | |
children | bb1afbbd93e4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java Tue Dec 08 08:39:03 2009 +0000 @@ -0,0 +1,835 @@ +/** + * + */ +package de.intevation.gnv.state.timeseries; + +import java.io.IOException; +import java.io.File; +import java.io.OutputStream; +import java.io.FileOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Vector; + +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.log4j.Logger; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import org.jfree.chart.ChartTheme; + +import au.com.bytecode.opencsv.CSVWriter; +import de.intevation.artifactdatabase.Config; +import de.intevation.artifactdatabase.XMLUtils; +import de.intevation.artifacts.CallMeta; +import de.intevation.artifacts.PreferredLocale; +import de.intevation.gnv.artifacts.ressource.RessourceFactory; +import de.intevation.gnv.chart.Chart; +import de.intevation.gnv.chart.ChartLabels; +import de.intevation.gnv.chart.TimeSeriesChart; +import de.intevation.gnv.chart.XMLChartTheme; +import de.intevation.gnv.chart.exception.TechnicalChartException; +import de.intevation.gnv.exports.ChartExportHelper; +import de.intevation.gnv.exports.DefaultExport; +import de.intevation.gnv.exports.DefaultDataCollector; +import de.intevation.gnv.exports.SimpleOdvDataCollector; +import de.intevation.gnv.exports.DefaultProfile; +import de.intevation.gnv.exports.Export.Profile; +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.state.InputData; +import de.intevation.gnv.state.OutputStateBase; +import de.intevation.gnv.state.describedata.KeyValueDescibeData; +import de.intevation.gnv.state.describedata.NamedCollection; +import de.intevation.gnv.state.exception.StateException; +import de.intevation.gnv.statistics.Statistic; +import de.intevation.gnv.statistics.StatisticSet; +import de.intevation.gnv.statistics.Statistics; +import de.intevation.gnv.statistics.TimeseriesStatistics; +import de.intevation.gnv.statistics.exception.StatisticsException; +import de.intevation.gnv.timeseries.gap.DefaultTimeGap; +import de.intevation.gnv.timeseries.gap.TimeGap; +import de.intevation.gnv.utils.ArtifactXMLUtilities; + + +/** + * @author Tim Englich <tim.englich@intevation.de> + * + */ +public class TimeSeriesOutputState extends OutputStateBase { + + protected static final boolean CACHE_CHART = + Boolean.parseBoolean(System.getProperty("cache.chart", "false")); + + protected static final boolean PDF_FORMAT_LANDSCAPE = + Boolean.parseBoolean(System.getProperty("export.pdf.landscape","true")); + + protected static final String[] IMG_EXPORT_FORMAT = { + "PNG", "JPEG", "GIF" + }; + + /** + * The UID of this Class + */ + private static final long serialVersionUID = 4178407570503098858L; + + /** + * the logger, used to log exceptions and additonaly information + */ + private static Logger log = Logger + .getLogger(TimeSeriesOutputState.class); + + private static List<TimeGap> timeGapDefinitions = null; + + protected String domainLable = "Zeit [UTC]"; + + protected String featureValuesName = "featureid"; + protected String parameterValuesName = "parameterid"; + protected String measuremenValueName = "measurementid"; + protected String dateValueName = "dateid"; + + public static final String [] TIMESERIES_CSV_PROFILE_NAMES = { + "XORDINATE", + "YORDINATE", + "GROUP1", + "GROUP2", + "GROUP3" + }; + + public static final String [] TIMESERIES_ODV_PROFILE_NAMES = { + "CRUISE", + "STATION", + "TYPE", + "SHAPE", + "BOTDEPTH", + "DEPTH", + "TIMEVALUE", + "DATAVALUE", + "PARAMETER" + }; + + + public static final String [] ODV_COLUMN_HEADER = { + "Cruise", + "Station", + "Type", + "Longitude [deegrees_east]", + "Latitude [deegrees_north]", + "Bot. Depth [m]", + "Depth [m]", + "Date/Time", + "Value", + "Parameterid" + }; + + /** + * Profile for exporting data to cvs + */ + public static final Profile TIMESERIES_CSV_PROFILE = + new DefaultProfile( + null, + ',', + '"', + '"', + "CSV", + "ISO-8859-1"); + + /** + * Profile for exporting data to odv + * TODO Change TIMESERIES_PROFILE_NAMES, which belong to CSV exports + */ + public static final Profile TIMESERIES_ODV_PROFILE = + new DefaultProfile( + ODV_COLUMN_HEADER, + '\t', + CSVWriter.NO_QUOTE_CHARACTER, + CSVWriter.NO_ESCAPE_CHARACTER, + "ODV", + "ISO-8859-1"); + + /** + * Constructor + */ + public TimeSeriesOutputState() { + super(); + } + + /** + * @see de.intevation.gnv.transition.OutputTransition#out(java.lang.String, + * java.util.Collection, java.io.OutputStream, java.lang.String, + * de.intevation.artifacts.CallMeta) + */ + public void out( + Document format, + Collection<InputData> inputData, + OutputStream outputStream, + String uuid, + CallMeta callMeta + ) throws StateException + { + log.debug("TimeSeriesOutputTransition.out"); + + String outputMode = Config.getStringXPath( + format, + "action/out/@name" + ); + String mimeType = Config.getStringXPath( + format, + "action/out/mime-type/@value" + ); + + try { + if (outputMode.equalsIgnoreCase("chart")) { + log.debug("Chart will be generated."); + int chartWidth = 600; + int chartHeight = 400; + try { + if (inputData != null) { + Iterator<InputData> it = inputData.iterator(); + while (it.hasNext()) { + InputData ip = it.next(); + if (ip.getName().equalsIgnoreCase("width")) { + chartWidth = Integer.parseInt(ip.getValue()); + } else if (ip.getName().equalsIgnoreCase("height")) { + chartHeight = Integer.parseInt(ip.getValue()); + } + } + } + } catch (NumberFormatException e) { + log.error(e, e); + throw new StateException(e); + } + + PreferredLocale[] locales = callMeta.getLanguages(); + Locale[] serverLocales = + RessourceFactory.getInstance().getLocales(); + Locale locale = + callMeta.getPreferredLocale(serverLocales); + + log.debug( + "Best locale - regarding intersection of server and " + + "browser locales - is " + locale.toString() + ); + + Collection parameters = this.getCleanedParameters(uuid); + Collection measurements = this.getMeasurements(uuid); + Collection dates = this.getDates(uuid); + + ChartLabels chartLables = new ChartLabels( + createChartTitle(locale, uuid), + createChartSubtitle(locale, uuid), + domainLable + ); + + String exportFormat = getExportFormat(mimeType); + + // TODO Remove this and parse input data + boolean linesVisible = true; + boolean shapesVisible = true; + + this.createChart( + outputStream, + parameters, + measurements, + dates, + chartLables, + uuid, + exportFormat, + locale, + chartWidth, + chartHeight, + linesVisible, + shapesVisible + ); + } + else if (outputMode.equalsIgnoreCase("pdf")) { + log.debug("Output mode == pdf"); + + Locale[] serverLocales = + RessourceFactory.getInstance().getLocales(); + Locale locale = + callMeta.getPreferredLocale(serverLocales); + + // TODO Remove this and parse input data + boolean linesVisible = true; + boolean shapesVisible = true; + + log.debug( + "Best locale - regarding intersection of server and " + + "browser locales - is " + locale.toString() + ); + + createPDF( + outputStream, + getCleanedParameters(uuid), + getMeasurements(uuid), + getDates(uuid), + new ChartLabels( + createChartTitle(locale, uuid), + createChartSubtitle(locale, uuid), + domainLable), + uuid, + "A4", + true, + locale, + linesVisible, + shapesVisible + ); + } + else if (outputMode.equalsIgnoreCase("svg")) { + log.debug("Output mode == svg"); + int width = 600; + int height = 400; + + // TODO Remove this and parse input data + boolean linesVisible = true; + boolean shapesVisible = true; + + Locale[] serverLocales = + RessourceFactory.getInstance().getLocales(); + Locale locale = + callMeta.getPreferredLocale(serverLocales); + + log.debug( + "Best locale - regarding intersection of server and " + + "browser locales - is " + locale.toString() + ); + + createSVG( + outputStream, + getCleanedParameters(uuid), + getMeasurements(uuid), + getDates(uuid), + new ChartLabels( + createChartTitle(locale, uuid), + createChartSubtitle(locale, uuid), + domainLable + ), + uuid, + locale, + width, + height, + linesVisible, + shapesVisible + ); + } + else if (outputMode.equalsIgnoreCase("csv")) { + log.debug("CSV-File will be generated."); + Collection<Result> chartResult = this.getChartResult(uuid); + this.createCSV(outputStream, chartResult); + } else if (outputMode.equalsIgnoreCase("statistics")) { + log.debug("Statistics will be generated."); + Statistics s = getStatisticsGenerator(); + Collection<Result> chartResult = this.getChartResult(uuid); + Collection<KeyValueDescibeData> parameters = + this.getParameters(uuid); + Collection<KeyValueDescibeData> measurements = + this.getMeasurements(uuid); + Collection<KeyValueDescibeData> dates = + this.getDates(uuid); + Collection<StatisticSet> statistics = + s.calculateStatistics(chartResult, + parameters, + measurements, + dates); + Document doc = this.writeStatistics2XML(statistics); + this.writeDocument2OutputStream(doc, outputStream); + } else if (outputMode.equalsIgnoreCase("odv")) { + + Collection<Result> odvResult = this.getODVResult(uuid); + this.createODV(outputStream, odvResult); + } + } catch (IOException e) { + log.error(e, e); + throw new StateException(e); + } catch (TechnicalChartException e) { + log.error(e, e); + throw new StateException(e); + } catch (StatisticsException e) { + log.error(e, e); + throw new StateException(e); + } + } + + + protected String getExportFormat(String mime) { + for(int i = 0; i < IMG_EXPORT_FORMAT.length; i++) { + if (mime.trim().toUpperCase().indexOf(IMG_EXPORT_FORMAT[i]) > 0) + return IMG_EXPORT_FORMAT[i]; + } + + // no format found relating to mimeType, default export as PNG + return IMG_EXPORT_FORMAT[0]; + } + + + protected Collection getCleanedParameters(Collection parameters) { + Iterator iter = parameters.iterator(); + Collection parameter = new Vector(parameters); + while (iter.hasNext()) { + KeyValueDescibeData data = (KeyValueDescibeData)iter.next(); + if (!data.isSelected()) + parameter.remove(data); + } + + return parameter; + } + + + protected Collection getCleanedParameters(String uuid) { + return getCleanedParameters(getParameters(uuid)); + } + + + /** + * @param outputStream + * @param chartResult + * @throws UnsupportedEncodingException + * @throws IOException + * @throws StateException + */ + protected void createCSV(OutputStream outputStream, + Collection<Result> chartResult) + throws UnsupportedEncodingException, + IOException, + StateException { + DefaultExport export = new DefaultExport(new DefaultDataCollector( + TIMESERIES_CSV_PROFILE_NAMES)); + + export.create(TIMESERIES_CSV_PROFILE, outputStream, chartResult); + } + + /** + * TODO Result is not used at the moment. Change result with correct data. + */ + protected void createODV(OutputStream outputStream, Collection result) + throws IOException, StateException { + + DefaultExport export = new DefaultExport(new SimpleOdvDataCollector( + TIMESERIES_ODV_PROFILE_NAMES)); + + if (result == null) + log.error("#################### RESULT == NULL #################"); + export.create(TIMESERIES_ODV_PROFILE, outputStream, result); + } + + /** + * @return + */ + protected Statistics getStatisticsGenerator() { + Statistics s = new TimeseriesStatistics(); + return s; + } + + protected void writeDocument2OutputStream(Document document, OutputStream os) { + + try { + TransformerFactory transformerFactory = TransformerFactory + .newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(os); + transformer.transform(source, result); + } catch (TransformerConfigurationException e) { + log.error(e, e); + } catch (TransformerFactoryConfigurationError e) { + log.error(e, e); + } catch (TransformerException e) { + log.error(e, e); + } + } + + protected Document writeStatistics2XML( Collection<StatisticSet> statistic) { + ArtifactXMLUtilities xmlUtilities = new ArtifactXMLUtilities(); + Document doc = XMLUtils.newDocument(); + if (statistic != null) { + Node statisticResults = xmlUtilities.createArtifactElement(doc, + "statistics"); + doc.appendChild(statisticResults); + Iterator<StatisticSet> it = statistic.iterator(); + while (it.hasNext()) { + StatisticSet set = it.next(); + Element setElement = xmlUtilities.createArtifactElement(doc, + "statistic"); + setElement.setAttribute("name", set.getName()); + + Iterator<Statistic> sit = set.getStatistics().iterator(); + while (sit.hasNext()){ + Statistic s = sit.next(); + Element result = xmlUtilities.createArtifactElement(doc, + "statistic-value"); + result.setAttribute("name", s.getKey()); + result.setAttribute("value", s.getStringValue()); + setElement.appendChild(result); + } + statisticResults.appendChild(setElement); + } + + } + return doc; + } + + + protected String getSelectedFeatureName(String uuid) { + Collection values = getCollection(featureValuesName, uuid); + + if (values != null) { + Iterator it = values.iterator(); + + while (it.hasNext()) { + KeyValueDescibeData data = (KeyValueDescibeData) it.next(); + + if (data.isSelected()) { + return data.getValue(); + } + } + } + return null; + } + + + /** + * @param outputStream + * @param parameters + * @param measurements + * @param timeSeriesName + * @param chartStyle + * @param chartLables + * @throws IOException + * @throws TechnicalChartException + */ + protected void createChart( + OutputStream outputStream, + Collection parameters, + Collection measurements, + Collection dates, + ChartLabels chartLables, + String uuid, + String exportFormat, + Locale locale, + int width, + int height, + boolean linesVisible, + boolean shapesVisible + ) + throws IOException, TechnicalChartException + { + log.debug("Create chart."); + Chart chart = getChart( + chartLables, + parameters, + measurements, + dates, + getChartResult(uuid), + locale, // Locale + uuid, + linesVisible, + shapesVisible + ); + + if (chart == null) { + log.error("Could not initialize chart."); + return; + } + + log.debug( + "export chart as " + exportFormat + + " in " + width + "x" + height + ); + + ChartExportHelper.exportImage( + outputStream, + chart.generateChart(), + exportFormat, + width, + height + ); + } + + + protected void createPDF( + OutputStream outputStream, + Collection parameters, + Collection measurements, + Collection dates, + ChartLabels chartLables, + String uuid, + String exportFormat, + boolean landscape, + Locale locale, + boolean linesVisible, + boolean shapesVisible + ) { + Chart chart = getChart( + chartLables, + parameters, + measurements, + dates, + getChartResult(uuid), + locale, + uuid, + linesVisible, + shapesVisible + ); + + if (chart == null) { + log.error("Could not initialize chart."); + return; + } + + ChartExportHelper.exportPDF( + outputStream, + chart.generateChart(), + "A4", + PDF_FORMAT_LANDSCAPE, + 50F, 50F, 50F, 50F + ); + + try { + OutputStream toFile = new FileOutputStream("/vol1/home/iweinzierl/tmp/test.svg"); + ChartExportHelper.exportSVG( + toFile, + chart.generateChart(), + null, + 600, 400 + ); + toFile.flush(); + toFile.close(); + } + catch(Exception e) { log.debug("ERROR WHLILE TEST."); } + } + + + protected void createSVG( + OutputStream outputStream, + Collection parameters, + Collection measurements, + Collection dates, + ChartLabels chartLables, + String uuid, + Locale locale, + int width, + int height, + boolean linesVisible, + boolean shapesVisible + ) { + Chart chart = getChart( + chartLables, + parameters, + measurements, + dates, + getChartResult(uuid), + locale, + uuid, + linesVisible, + shapesVisible + ); + + if (chart == null) { + log.error("Could not initialize chart."); + return; + } + + ChartExportHelper.exportSVG( + outputStream, + chart.generateChart(), + null, + 600, 400 + ); + + log.debug("svg export finished."); + } + + + protected Chart getChart( + ChartLabels chartLables, + Collection parameters, + Collection measurements, + Collection dates, + Collection result, + Locale locale, + String uuid, + boolean linesVisible, + boolean shapesVisible + ) { + Chart chart = null; + + if (CACHE_CHART) { + log.info("Try to get timeseries chart from cache."); + chart = (Chart) getChartFromCache(uuid); + } + + if (chart != null) + return chart; + + log.info("Chart not in cache yet."); + chart = new TimeSeriesChart( + chartLables, + createStyle(), + parameters, + measurements, + dates, + result, + timeGapDefinitions, + locale, + linesVisible, + shapesVisible + ); + chart.generateChart(); + + if (CACHE_CHART) { + log.info("Put chart into cache."); + purifyChart(chart, uuid); + } + + return chart; + } + + protected ChartTheme createStyle() { + XMLChartTheme theme = null; + + Document template = Config.getChartTemplate(); + String name = Config.getStringXPath( + template, + "theme/name/@value" + ); + + theme = new XMLChartTheme(name); + theme.applyXMLConfiguration(template); + + return theme; + } + + + protected String createChartTitle(Locale locale, String uuid) { + return getFisName(locale); + + } + + + protected String createChartSubtitle(Locale locale, String uuid) { + return getSelectedFeatureName(uuid); + } + + + protected String getFisName(Locale locale) { + String returnValue = ""; + InputData input = inputData.get("fisname"); + + if (input != null) { + String value = input.getValue(); + + returnValue = RessourceFactory.getInstance().getRessource( + locale, + value, + value + ); + } + return returnValue; + } + + + protected Collection<KeyValueDescibeData> getParameters(String uuid) { + return this.getCollection(parameterValuesName, uuid); + } + + protected Collection<KeyValueDescibeData> getMeasurements(String uuid) { + return this.getCollection(measuremenValueName, uuid); + } + protected Collection<KeyValueDescibeData> getDates(String uuid) { + return this.getCollection(dateValueName,uuid); + } + + @Override + public void setup(Node configuration) { + super.setup(configuration); + String featureNameValue = Config.getStringXPath(configuration, + "value-names/value-name[@name='feature']/@value"); + if (featureNameValue != null) { + this.featureValuesName = featureNameValue; + } + String parameterNameValue = Config.getStringXPath(configuration, + "value-names/value-name[@name='parameter']/@value"); + if (parameterNameValue != null) { + this.parameterValuesName = parameterNameValue; + } + String measurementNameValue = Config.getStringXPath(configuration, + "value-names/value-name[@name='measurement']/@value"); + if (measurementNameValue != null) { + this.measuremenValueName = measurementNameValue; + } + + String dateNameValue = Config.getStringXPath(configuration, + "value-names/value-name[@name='date']/@value"); + if (dateNameValue != null) { + this.dateValueName = dateNameValue; + } + if (timeGapDefinitions == null){ + Element gapDefinition = (Element)Config.getNodeXPath(configuration, + "time-gap-definition"); + synchronized (this.getClass()) { + if (gapDefinition != null){ + String link = gapDefinition.getAttribute("xlink:href"); + if (link != null ){ + String absolutFileName = Config.replaceConfigDir(link); + gapDefinition = (Element)new ArtifactXMLUtilities(). + readConfiguration(absolutFileName); + } + + NodeList gapDefinitions = Config.getNodeSetXPath(gapDefinition, + "/time-gaps/time-gap"); + if (gapDefinition != null){ + timeGapDefinitions = new ArrayList<TimeGap>(gapDefinitions. + getLength()); + for (int i = 0; i < gapDefinitions.getLength(); i++){ + Element gapNode = (Element)gapDefinitions.item(i); + String unit = gapNode.getAttribute("unit"); + int key = Integer.parseInt(gapNode.getAttribute("key")); + int value = Integer.parseInt(gapNode.getAttribute("gap")); + log.info("Add new Timegap: "+key+" "+value+" "+ unit); + timeGapDefinitions.add(new DefaultTimeGap(unit, + key, + value)); + } + } + + } + } + } + } + + /** + * @param collectionName + * @return + */ + protected Collection<KeyValueDescibeData> getCollection( + String collectionName, + String uuid) { + Iterator<Object> it = this.getDescibeData(uuid).iterator(); + while (it.hasNext()) { + + Object o = it.next(); + + if (o instanceof NamedCollection<?>) { + NamedCollection<KeyValueDescibeData> nc = (NamedCollection<KeyValueDescibeData>) o; + if (nc.getName().equals(collectionName)) { + return nc; + } + } + } + return null; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :