# HG changeset patch # User Ingo Weinzierl # Date 1274277933 0 # Node ID 70653c29fc1d89b9d1170ae46cc52fa806e7008c # Parent 3be83ac4cfde3b487f304353fda8a41088ec76df Finished vector support for timeseriespoints (Issue27). gnv-artifacts/trunk@1112 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/ChangeLog --- a/gnv-artifacts/ChangeLog Wed May 19 06:09:10 2010 +0000 +++ b/gnv-artifacts/ChangeLog Wed May 19 14:05:33 2010 +0000 @@ -1,3 +1,30 @@ +2010-05-19 Ingo Weinzierl + + Issue27 - Vector data support for timeseriespoints finished. + + * doc/conf/products/timeseries/conf_timeseriespoint.xml: Adjusted the + relevant OutputState for timeseries with components of vector data. + + * src/main/java/de/intevation/gnv/utils/VectorDataProcessor.java: A helper + class that converts vector data into a useful container for our chart + classes. Our chart classes just use one single data value of each Result + object - but there is a data value for each component of a vector. So we + need to split Result object into many Result objects - one for each vector + component. + + * src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesVectorOutputState.java: + Inherits from TimeSeriesOutputStates. Currently, the only reason for this + class is to instantiate TimeSeriesVectorChart objects instead of + TimeSeriesChart objects. + + * src/main/java/de/intevation/gnv/chart/TimeSeriesVectorChart.java: This + chart type inherits from TimeSeriesChart. The only difference between + these two types is the way how to add datasets and how to create names for + series - most of the code is equal. + + * src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java: + Timegap definitions have protected access now. + 2010-05-19 Ingo Weinzierl * doc/conf/products/timeseries/conf_timeseriespoint.xml: Added the vector diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/doc/conf/products/timeseries/conf_timeseriespoint.xml --- a/gnv-artifacts/doc/conf/products/timeseries/conf_timeseriespoint.xml Wed May 19 06:09:10 2010 +0000 +++ b/gnv-artifacts/doc/conf/products/timeseries/conf_timeseriespoint.xml Wed May 19 14:05:33 2010 +0000 @@ -339,7 +339,7 @@ - + timeseries_chart_data_vector timeseries_odv_data_vector diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesVectorChart.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/chart/TimeSeriesVectorChart.java Wed May 19 14:05:33 2010 +0000 @@ -0,0 +1,142 @@ +package de.intevation.gnv.chart; + +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.geobackend.base.ResultDescriptor; + +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.Locale; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartTheme; +import org.jfree.chart.plot.XYPlot; + +import org.jfree.data.time.Minute; +import org.jfree.data.time.TimeSeries; +import org.jfree.data.time.TimeSeriesCollection; + + +/** + * This class is used to create timeseries charts that contain components of a + * vector parameter. The domain axis contains multiple date/time objects. + * + * @author Ingo Weinzierl + */ +public class TimeSeriesVectorChart extends TimeSeriesChart { + + private static Logger logger =Logger.getLogger(TimeSeriesVectorChart.class); + + public TimeSeriesVectorChart( + ChartLabels labels, + ChartTheme theme, + Collection parameters, + Collection measurements, + Collection dates, + Collection result, + Collection timeGaps, + Locale locale, + boolean linesVisible, + boolean shapesVisible + ) { + super(labels, theme, parameters, measurements, dates, result, + timeGaps, locale, linesVisible, shapesVisible); + } + + + @Override + protected void initData() { + logger.debug("init data for timeseries vector chart"); + + Iterator iter = resultSet.iterator(); + Result row = null; + String seriesName = null; + TimeSeries series = null; + + int idx = 0; + int startPos = 0; + int endPos = 0; + Date startDate = null; + Date endDate = null; + + ResultDescriptor rd = null; + int idxSeries = -1; + int idxX = -1; + int idxY = -1; + + Result[] results = + (Result[]) resultSet.toArray(new Result[resultSet.size()]); + + while (iter.hasNext()) { + row = (Result) iter.next(); + + if (rd == null) { + rd = row.getResultDescriptor(); + idxSeries = rd.getColumnIndex("SERIES"); + idxX = rd.getColumnIndex("XORDINATE"); + idxY = rd.getColumnIndex("YORDINATE"); + } + + // add current data to plot and prepare for next one + if (!row.getString(idxSeries).equals(seriesName)) { + logger.debug("prepare data/plot for next dataset"); + + if(series != null) { + // add gaps before adding series to chart + startDate = results[startPos].getDate(idxX); + endDate = results[endPos-1].getDate(idxX); + addGaps(results,series,startDate,endDate,startPos,endPos); + addSeries(series, seriesName, idx); + + startPos = endPos + 1; + } + + // prepare variables for next plot + seriesName = row.getString(idxSeries); + + logger.debug("next dataset is '" + seriesName + "'"); + series = new TimeSeries(seriesName, Minute.class); + } + + addValue(row, series); + storeMaxRange(ranges, row.getDouble(idxY), seriesName); + endPos++; + } + + if (startPos < results.length && endPos-1 < results.length) { + // add the last dataset if existing to plot and prepare its axis + startDate = results[startPos].getDate(idxX); + endDate = results[endPos-1].getDate(idxX); + addGaps(results, series, startDate, endDate, startPos, endPos); + addSeries(series, seriesName, idx); + } + + addDatasets(); + } + + + @Override + protected void addDatasets() { + XYPlot plot = chart.getXYPlot(); + int idx = 0; + + TimeSeriesCollection tsc = null; + Iterator iter = datasets.keySet().iterator(); + + while (iter.hasNext()) { + String key = (String) iter.next(); + tsc = (TimeSeriesCollection)datasets.get(key); + plot.setDataset(idx, tsc ); + logger.debug("Added " + key + " parameter to plot."); + prepareAxis(key, idx); + adjustRenderer( + idx++, + tsc.getSeriesCount(), + linesVisible, + shapesVisible + ); + } + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java --- a/gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java Wed May 19 06:09:10 2010 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesOutputState.java Wed May 19 14:05:33 2010 +0000 @@ -111,7 +111,7 @@ private static Logger log = Logger .getLogger(TimeSeriesOutputState.class); - private List timeGapDefinitions = null; + protected List timeGapDefinitions = null; /** * Key in resource bundle the x-axis title is stored. diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesVectorOutputState.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/state/timeseries/TimeSeriesVectorOutputState.java Wed May 19 14:05:33 2010 +0000 @@ -0,0 +1,88 @@ +package de.intevation.gnv.state.timeseries; + +import de.intevation.artifacts.CallContext; + +import de.intevation.gnv.chart.Chart; +import de.intevation.gnv.chart.ChartLabels; +import de.intevation.gnv.chart.TimeSeriesVectorChart; + +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.geobackend.base.query.QueryExecutor; +import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; +import de.intevation.gnv.geobackend.base.query.exception.QueryException; + +import de.intevation.gnv.utils.VectorDataProcessor; + +import java.util.Collection; +import java.util.Locale; + +import org.apache.log4j.Logger; + +import org.jfree.chart.ChartTheme; + +/** + * @author Ingo Weinzierl + */ +public class TimeSeriesVectorOutputState extends TimeSeriesOutputState { + + private static Logger logger = + Logger.getLogger(TimeSeriesVectorOutputState.class); + + public TimeSeriesVectorOutputState() { + } + + + @Override + protected Collection getData(String queryID) { + logger.debug("Fetch chart data from database and convert it."); + try { + String[] filter = generateFilterValuesFromInputData(); + QueryExecutor queryExecutor = + QueryExecutorFactory.getInstance().getQueryExecutor(); + + Collection res = queryExecutor.executeQuery(queryID,filter); + return VectorDataProcessor.process(res); + } + catch (RuntimeException e) { + logger.error(e, e); + } + catch (QueryException e) { + logger.error(e, e); + } + + return null; + } + + + @Override + protected Chart getChart( + ChartLabels chartLables, + ChartTheme theme, + Collection parameters, + Collection measurements, + Collection dates, + Object result, + Locale locale, + String uuid, + boolean linesVisible, + boolean shapesVisible, + CallContext callContext + ) { + Chart chart = new TimeSeriesVectorChart( + chartLables, + theme, + parameters, + measurements, + dates, + (Collection)result, + timeGapDefinitions, + locale, + linesVisible, + shapesVisible + ); + chart.generateChart(); + + return chart; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 : diff -r 3be83ac4cfde -r 70653c29fc1d gnv-artifacts/src/main/java/de/intevation/gnv/utils/VectorDataProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gnv-artifacts/src/main/java/de/intevation/gnv/utils/VectorDataProcessor.java Wed May 19 14:05:33 2010 +0000 @@ -0,0 +1,107 @@ +package de.intevation.gnv.utils; + +import de.intevation.gnv.geobackend.base.DefaultResult; +import de.intevation.gnv.geobackend.base.DefaultResultDescriptor; +import de.intevation.gnv.geobackend.base.Result; +import de.intevation.gnv.geobackend.base.ResultDescriptor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +/** + * @author Ingo Weinzierl + */ +public final class VectorDataProcessor { + + private static Logger logger = Logger.getLogger(VectorDataProcessor.class); + + public static final String[] COLUMNS = { + "YORDINATE", "XORDINATE", "GROUP1", "GROUP2", "GROUP3", "GAPID", "SERIES" + }; + + public static Collection process(Collection input) { + logger.info("Process vector data (" + input.size() + " items)"); + + ResultDescriptor outDescriptor = initResultDescriptor(); + ResultDescriptor inDescriptor = null; + int[] inIndices = null; + int columns = -1; + + Map series = new HashMap(); + + // for each result of the incoming result + for (Result result: input) { + if (inDescriptor == null || inIndices == null) { + inDescriptor = result.getResultDescriptor(); + inIndices = inDescriptor.getColumnIndices(COLUMNS); + columns = inDescriptor.getColumnCount(); + } + + // for each column of the incoming result object + for (int i = 0; i < columns; i++) { + String name = inDescriptor.getColumnName(i); + + if (!StringUtils.contains(COLUMNS, name)) { + List list = (List) series.get(name); + if (list == null) + list = new ArrayList(); + + Result out = new DefaultResult(outDescriptor); + Object value = result.getObject(i); + + // skipp results that have no data value + if (value == null) + continue; + + out.addColumnValue(0, value); + + // add meta data to result object + // TODO: Maybe we could do this one single time, because the + // TODO: meta data will not change + for (int j = 1; j < inIndices.length -1; j++) { + out.addColumnValue(j, result.getObject(inIndices[j])); + } + + // add the name of the component to distinguish between + // different series later in the chart creation + out.addColumnValue(inIndices.length-1, name); + + list.add(out); + series.put(name, list); + } + } + } + + // finally, we put all lists together + Iterator iter = series.values().iterator(); + List output = new ArrayList(); + while (iter.hasNext()) { + output.addAll((Collection)iter.next()); + } + + logger.info("Data processing created " + output.size() + " elements."); + return output; + } + + + public static ResultDescriptor initResultDescriptor() { + logger.debug("Init ResultDescriptor for outgoing results."); + + ResultDescriptor desc = new DefaultResultDescriptor(); + + for (String name: COLUMNS) { + desc.addColumn(name, "java.lang.String"); + } + + logger.debug("Outgoing ResultDescriptor has " + desc.getColumnCount() + + " columns"); + return desc; + } +} +// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :