teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.exports; ingo@2233: teichmann@5831: import org.dive4elements.artifactdatabase.state.ArtifactAndFacet; teichmann@5831: import org.dive4elements.river.artifacts.resources.Resources; teichmann@5831: import org.dive4elements.river.jfree.Bounds; teichmann@5831: import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation; teichmann@5831: import org.dive4elements.river.jfree.DoubleBounds; teichmann@5864: import org.dive4elements.river.jfree.RiverAnnotation; teichmann@5831: import org.dive4elements.river.jfree.StyledTimeSeries; teichmann@5831: import org.dive4elements.river.jfree.TimeBounds; aheinecke@7034: import org.dive4elements.river.jfree.AxisDataset; teichmann@6905: import org.dive4elements.river.themes.ThemeDocument; christian@3278: ingo@2238: import java.awt.Color; sascha@3139: import java.awt.Font; clins@3984: import java.text.DateFormat; clins@3984: import java.text.ParseException; ingo@2238: import java.util.ArrayList; sascha@3139: import java.util.Date; ingo@2238: import java.util.HashMap; christian@4044: import java.util.Iterator; ingo@2238: import java.util.List; ingo@2238: import java.util.Map; ingo@2233: felix@3622: import javax.swing.ImageIcon; felix@3622: ingo@2233: import org.apache.log4j.Logger; ingo@2238: import org.jfree.chart.ChartFactory; ingo@2233: import org.jfree.chart.JFreeChart; christian@4044: import org.jfree.chart.LegendItem; christian@4044: import org.jfree.chart.LegendItemCollection; felix@3622: import org.jfree.chart.annotations.XYAnnotation; felix@3622: import org.jfree.chart.annotations.XYImageAnnotation; sascha@3139: import org.jfree.chart.annotations.XYTextAnnotation; ingo@2330: import org.jfree.chart.axis.ValueAxis; sascha@3139: import org.jfree.chart.plot.Marker; ingo@2238: import org.jfree.chart.plot.XYPlot; sascha@3139: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; ingo@2238: import org.jfree.data.Range; ingo@2242: import org.jfree.data.general.Series; teichmann@7904: import org.jfree.data.time.FixedMillisecond; sascha@3139: import org.jfree.data.time.TimeSeries; sascha@3139: import org.jfree.data.time.TimeSeriesCollection; ingo@2238: import org.jfree.data.xy.XYDataset; sascha@3139: import org.jfree.ui.Layer; sascha@3139: import org.json.JSONArray; sascha@3139: import org.json.JSONException; ingo@2233: ingo@2233: /** felix@6795: * Generator for diagrams with time on x axis. ingo@2233: * @author Ingo Weinzierl ingo@2233: */ felix@6795: public abstract class TimeseriesChartGenerator felix@6795: extends ChartGenerator { ingo@2238: raimund@3130: protected List domainMarker; ingo@2238: raimund@3130: protected List valueMarker; raimund@3130: raimund@3134: protected Map attributes; raimund@3134: raimund@3130: protected boolean domainZeroLineVisible; ingo@2238: teichmann@8202: private static final Logger log = ingo@2233: Logger.getLogger(TimeseriesChartGenerator.class); ingo@2233: ingo@2330: public static final int AXIS_SPACE = 5; ingo@2238: ingo@2587: protected Map xBounds; ingo@2330: ingo@2587: protected Map yBounds; ingo@2238: ingo@2238: ingo@2238: /** ingo@2238: * The default constructor that initializes internal datastructures. ingo@2238: */ ingo@2238: public TimeseriesChartGenerator() { ingo@2238: super(); ingo@2238: ingo@2587: xBounds = new HashMap(); ingo@2587: yBounds = new HashMap(); raimund@3130: domainMarker = new ArrayList(); raimund@3130: valueMarker = new ArrayList(); raimund@3134: attributes = new HashMap(); ingo@2238: } ingo@2238: ingo@2233: ingo@2233: ingo@2233: @Override ingo@2234: public JFreeChart generateChart() { teichmann@8202: log.info("Generate Timeseries Chart."); ingo@2233: ingo@2238: JFreeChart chart = ChartFactory.createTimeSeriesChart( ingo@2238: getChartTitle(), ingo@2238: getXAxisLabel(), ingo@2238: getYAxisLabel(0), ingo@2238: null, ingo@2422: isLegendVisible(), ingo@2238: false, ingo@2238: false); ingo@2238: ingo@2238: XYPlot plot = (XYPlot) chart.getPlot(); ingo@2238: ingo@2238: chart.setBackgroundPaint(Color.WHITE); ingo@2238: plot.setBackgroundPaint(Color.WHITE); ingo@2238: ingo@2238: addSubtitles(chart); ingo@2422: adjustPlot(plot); ingo@2238: addDatasets(plot); ingo@2586: adjustAxes(plot); raimund@3130: addDomainAxisMarker(plot); raimund@3130: addValueAxisMarker(plot); ingo@2330: adaptZoom(plot); ingo@2330: raimund@3134: applySeriesAttributes(plot); christian@4021: felix@7595: consumeAxisSettings(plot); felix@7595: raimund@2633: addAnnotationsToRenderer(plot); felix@3622: addLogo(plot); felix@3185: aggregateLegendEntries(plot); ingo@2238: return chart; ingo@2238: } ingo@2238: ingo@2238: felix@3622: /** felix@3622: * Return left most data points x value (on first axis). felix@3622: * Shortcut, especially to be overridden in (LS) charts where felix@3622: * axis could be inverted. felix@3622: */ felix@3622: protected double getLeftX() { felix@3622: return (Long)getXBounds(0).getLower(); felix@3622: } felix@3622: felix@3622: felix@3622: /** felix@3622: * Return right most data points x value (on first axis). felix@3622: * Shortcut, especially to be overridden in (LS) charts where felix@3622: * axis could be inverted. felix@3622: */ felix@3622: protected double getRightX() { felix@3622: return (Long)getXBounds(0).getUpper(); felix@3622: } felix@3622: felix@3622: felix@3622: /** felix@3622: * Add a logo as background annotation to plot. felix@3622: * Copy from XYChartGenerator. felix@3622: */ felix@3622: protected void addLogo(XYPlot plot) { felix@3622: String logo = showLogo(); felix@3622: if (logo == null) { teichmann@8202: log.debug("No logo to show chosen"); felix@3622: return; felix@3622: } felix@3622: sascha@3633: ImageIcon imageIcon = null; felix@3622: if (logo.equals("none")) { felix@3622: return; felix@3622: } felix@3623: /* felix@3623: If you want to add images, remember to change code in these places: felix@3623: flys-artifacts: felix@3623: XYChartGenerator.java felix@3623: Timeseries*Generator.java and felix@3623: in the flys-client projects Chart*Propert*Editor.java. felix@3638: Also, these images have to be put in felix@3638: flys-artifacts/src/main/resources/images/ felix@3638: flys-client/src/main/webapp/images/ felix@3623: */ raimund@3637: java.net.URL imageURL; felix@3622: if (logo.equals("Intevation")) { raimund@3637: imageURL = XYChartGenerator.class.getResource("/images/intevation.png"); felix@3622: } felix@3622: else { // TODO else if ... raimund@3637: imageURL = XYChartGenerator.class.getResource("/images/bfg_logo.gif"); felix@3622: } raimund@3637: imageIcon = new ImageIcon(imageURL); felix@3622: double xPos = 0d, yPos = 0d; felix@3622: felix@3622: String placeh = logoHPlace(); felix@3622: String placev = logoVPlace(); felix@3622: felix@3622: if (placev == null || placev.equals("none")) { felix@3622: placev = "top"; felix@3622: } felix@3622: if (placev.equals("top")) { felix@3622: yPos = (Double)getYBounds(0).getUpper(); felix@3622: } felix@3622: else if (placev.equals("bottom")) { felix@3622: yPos = (Double)getYBounds(0).getLower(); felix@3622: } felix@3622: else if (placev.equals("center")) { felix@3622: yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d; felix@3622: } felix@3622: else { teichmann@8202: log.debug("Unknown place-v value: " + placev); felix@3622: } felix@3622: felix@3622: if (placeh == null || placeh.equals("none")) { felix@3622: placeh = "center"; felix@3622: } felix@3622: if (placeh.equals("left")) { felix@3622: xPos = getLeftX(); felix@3622: } felix@3622: else if (placeh.equals("right")) { felix@3622: xPos = getRightX(); felix@3622: } felix@3622: else if (placeh.equals("center")) { felix@3632: xPos = ((Long)getXBounds(0).getUpper() + (Long)getXBounds(0).getLower())/2d; felix@3622: } felix@3622: else { teichmann@8202: log.debug("Unknown place-h value: " + placeh); felix@3622: } felix@3622: teichmann@8202: log.debug("logo position: " + xPos + "/" + yPos); felix@3622: felix@3622: org.jfree.ui.RectangleAnchor anchor felix@3622: = org.jfree.ui.RectangleAnchor.TOP; felix@3622: if (placev.equals("top")) { felix@3622: if (placeh.equals("left")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT; felix@3622: } felix@3622: else if (placeh.equals("right")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT; felix@3622: } felix@3622: else if (placeh.equals("center")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.TOP; felix@3622: } felix@3622: } felix@3622: else if (placev.equals("bottom")) { felix@3622: if (placeh.equals("left")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT; felix@3622: } felix@3622: else if (placeh.equals("right")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT; felix@3622: } felix@3622: else if (placeh.equals("center")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.BOTTOM; felix@3622: } felix@3622: } felix@3622: else if (placev.equals("center")) { felix@3622: if (placeh.equals("left")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.LEFT; felix@3622: } felix@3622: else if (placeh.equals("right")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.RIGHT; felix@3622: } felix@3622: else if (placeh.equals("center")) { felix@3622: anchor = org.jfree.ui.RectangleAnchor.CENTER; felix@3622: } felix@3622: } felix@3622: felix@3622: XYAnnotation xyannotation = sascha@3633: new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor); sascha@3633: plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND); felix@3622: } felix@3622: felix@7595: /** felix@7595: * This method zooms the plot to the specified ranges in the attribute felix@7595: * document or to the ranges specified by the min/max values in the felix@7595: * datasets. Note: We determine the range manually if no zoom ranges felix@7595: * are given, because JFreeCharts auto-zoom adds a margin to the left and felix@7595: * right of the data area. felix@7595: * felix@7595: * Copy of implementation in XYChartGenerator. felix@7595: * felix@7595: * @param plot The XYPlot. felix@7595: */ felix@7595: protected void consumeAxisSettings(XYPlot plot) { teichmann@8202: log.debug("Zoom to specified ranges."); felix@7595: felix@7595: Bounds xrange = getDomainAxisRange(); felix@7595: Bounds yrange = getValueAxisRange(); felix@7595: felix@7595: ValueAxis xAxis = plot.getDomainAxis(); felix@7595: felix@7595: Range fixedXRange = getRangeForAxisFromSettings("X"); felix@7595: if (fixedXRange != null) { felix@7595: xAxis.setRange(fixedXRange); felix@7595: } felix@7595: else { felix@7595: zoomX(plot, xAxis, getXBounds(0), xrange); felix@7595: } felix@7595: felix@7595: for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) { felix@7595: ValueAxis yaxis = plot.getRangeAxis(i); felix@7595: felix@7595: if (yaxis instanceof IdentifiableNumberAxis) { felix@7595: IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis; felix@7595: felix@7595: Range fixedRange = getRangeForAxisFromSettings(idAxis.getId()); felix@7595: if (fixedRange != null) { felix@7595: yaxis.setRange(fixedRange); felix@7595: continue; felix@7595: } felix@7595: } felix@7595: felix@7595: if (yaxis == null) { teichmann@8202: log.debug("Zoom problem: no Y Axis for index: " + i); felix@7595: continue; felix@7595: } felix@7595: teichmann@8202: log.debug("Prepare zoom settings for y axis at index: " + i); felix@7595: zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange); felix@7595: } felix@7595: } felix@7595: felix@3622: ingo@2242: @Override ingo@2242: protected Series getSeriesOf(XYDataset dataset, int idx) { ingo@2242: return ((TimeSeriesCollection) dataset).getSeries(idx); ingo@2242: } ingo@2242: ingo@2242: ingo@2238: /** aheinecke@7034: * This method creates new instances of AxisDataset. ingo@2238: * aheinecke@7034: * @param idx The symbol for the new AxisDataset. ingo@2238: */ ingo@2238: @Override ingo@2238: protected AxisDataset createAxisDataset(int idx) { teichmann@8202: log.debug("Create a new AxisDataset for index: " + idx); aheinecke@7034: return new AxisDataset(idx); ingo@2238: } ingo@2238: ingo@2238: ingo@2330: @Override ingo@2587: protected void combineXBounds(Bounds bounds, int index) { ingo@2330: if (bounds != null) { ingo@2587: Bounds old = getXBounds(index); ingo@2238: ingo@2238: if (old != null) { ingo@2330: bounds = bounds.combine(old); ingo@2238: } ingo@2238: ingo@2330: setXBounds(index, bounds); ingo@2238: } ingo@2238: } ingo@2261: ingo@2261: ingo@2587: @Override ingo@2587: protected void combineYBounds(Bounds bounds, int index) { ingo@2330: if (bounds != null) { ingo@2330: Bounds old = getYBounds(index); ingo@2330: ingo@2330: if (old != null) { ingo@2330: bounds = bounds.combine(old); ingo@2330: } ingo@2330: ingo@2330: setYBounds(index, bounds); ingo@2330: } ingo@2330: } ingo@2330: ingo@2330: ingo@2330: // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index) ingo@2330: @Override ingo@2330: public Range[] getRangesForAxis(int index) { ingo@2330: // TODO ingo@2330: Bounds[] bounds = getBoundsForAxis(index); ingo@2330: ingo@2330: return new Range[] { ingo@2330: new Range( ingo@2330: bounds[0].getLower().doubleValue(), ingo@2330: bounds[0].getUpper().doubleValue()), ingo@2330: new Range( ingo@2330: bounds[1].getLower().doubleValue(), ingo@2330: bounds[1].getUpper().doubleValue()) ingo@2330: }; ingo@2330: } ingo@2330: ingo@2330: ingo@2261: @Override ingo@2330: public Bounds getXBounds(int axis) { ingo@2587: return xBounds.get(axis); ingo@2330: } ingo@2261: ingo@2330: ingo@2330: @Override ingo@2330: protected void setXBounds(int axis, Bounds bounds) { ingo@2587: xBounds.put(axis, bounds); ingo@2330: } ingo@2330: ingo@2330: ingo@2330: @Override ingo@2330: public Bounds getYBounds(int axis) { ingo@2587: return yBounds.get(axis); ingo@2330: } ingo@2330: ingo@2330: ingo@2330: @Override ingo@2330: protected void setYBounds(int axis, Bounds bounds) { ingo@2587: if (bounds != null) { ingo@2587: yBounds.put(axis, bounds); ingo@2587: } ingo@2330: } ingo@2330: ingo@2330: ingo@2330: public Bounds[] getBoundsForAxis(int index) { teichmann@8202: log.debug("Return x and y bounds for axis at: " + index); ingo@2330: ingo@2330: Bounds rx = getXBounds(Integer.valueOf(index)); ingo@2330: Bounds ry = getYBounds(Integer.valueOf(index)); ingo@2261: ingo@2261: if (rx == null) { teichmann@8202: log.warn("Range for x axis not set." + ingo@2261: " Using default values: 0 - 1."); ingo@2330: rx = new TimeBounds(0l, 1l); ingo@2261: } ingo@2261: ingo@2330: if (ry == null) { teichmann@8202: log.warn("Range for y axis not set." + ingo@2330: " Using default values: 0 - 1."); ingo@2330: ry = new DoubleBounds(0l, 1l); ingo@2330: } ingo@2261: teichmann@8202: log.debug("X Bounds at index " + index + " is: " + rx); teichmann@8202: log.debug("Y Bounds at index " + index + " is: " + ry); ingo@2330: ingo@2330: return new Bounds[] {rx, ry}; ingo@2330: } ingo@2330: ingo@2330: ingo@3650: /** Get (zoom)values from request. */ ingo@2330: public Bounds getDomainAxisRange() { ingo@2330: String[] ranges = getDomainAxisRangeFromRequest(); ingo@2330: ingo@2330: if (ranges == null || ranges.length < 2) { teichmann@8202: log.debug("No zoom range for domain axis specified."); ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: if (ranges[0] == null || ranges[1] == null) { teichmann@8202: log.warn("Invalid ranges for domain axis specified!"); ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: try { ingo@2330: double lower = Double.parseDouble(ranges[0]); ingo@2330: double upper = Double.parseDouble(ranges[1]); ingo@2330: ingo@2330: return new DoubleBounds(lower, upper); ingo@2330: } ingo@2330: catch (NumberFormatException nfe) { teichmann@8202: log.warn("Invalid ranges for domain axis specified: " + nfe); ingo@2330: } ingo@2330: ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: ingo@2330: public Bounds getValueAxisRange() { ingo@2330: String[] ranges = getValueAxisRangeFromRequest(); ingo@2330: ingo@2330: if (ranges == null || ranges.length < 2) { teichmann@8202: log.debug("No zoom range for domain axis specified."); ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: if (ranges[0] == null || ranges[1] == null) { teichmann@8202: log.warn("Invalid ranges for domain axis specified!"); ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: try { ingo@2330: double lower = Double.parseDouble(ranges[0]); ingo@2330: double upper = Double.parseDouble(ranges[1]); ingo@2330: ingo@2330: return new DoubleBounds(lower, upper); ingo@2330: } ingo@2330: catch (NumberFormatException nfe) { teichmann@8202: log.warn("Invalid ranges for domain axis specified: " + nfe); ingo@2330: } ingo@2330: ingo@2330: return null; ingo@2330: } ingo@2330: ingo@2330: ingo@2330: protected void adaptZoom(XYPlot plot) { teichmann@8202: log.debug("Adapt zoom of Timeseries chart."); ingo@2330: ingo@2587: zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange()); ingo@2330: ingo@2330: Bounds valueAxisBounds = getValueAxisRange(); ingo@2330: ingo@2330: for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) { ingo@2330: zoomY( ingo@2330: plot, ingo@2330: plot.getRangeAxis(j), ingo@2330: getYBounds(j), ingo@2330: valueAxisBounds); ingo@2330: } ingo@2330: } ingo@2330: ingo@2330: ingo@3650: /** ingo@3650: * @param plot the plot. ingo@3650: * @param axis the value (x, time) axis of which to set bounds. ingo@3650: * @param total the current bounds (?). ingo@3650: */ ingo@2330: protected void zoomX( ingo@2330: XYPlot plot, ingo@2330: ValueAxis axis, ingo@3650: Bounds total,//we could equally nicely getXBounds(0) ingo@2330: Bounds user ingo@2330: ) { teichmann@8202: if (log.isDebugEnabled()) { teichmann@8202: log.debug("== Zoom X axis =="); teichmann@8202: log.debug(" Total axis range : " + total); teichmann@8202: log.debug(" User defined range: " + user); ingo@2330: } ingo@2330: ingo@2330: if (user != null) { ingo@2330: long min = total.getLower().longValue(); ingo@2330: long max = total.getUpper().longValue(); ingo@2330: long diff = max > min ? max - min : min - max; ingo@2330: christian@3278: long newMin = Math.round(min + user.getLower().doubleValue() * diff); christian@3278: long newMax = Math.round(min + user.getUpper().doubleValue() * diff); ingo@2330: ingo@2330: TimeBounds newBounds = new TimeBounds(newMin, newMax); ingo@2330: teichmann@8202: log.debug(" Zoom axis to: " + newBounds); ingo@2330: ingo@2330: newBounds.applyBounds(axis, AXIS_SPACE); ingo@2330: } ingo@2330: else { teichmann@8202: log.debug("No user specified zoom values found!"); sascha@3140: if (total != null && axis != null) { sascha@3140: total.applyBounds(axis, AXIS_SPACE); sascha@3140: } ingo@2330: } ingo@2330: } ingo@2330: ingo@2330: ingo@3650: /** ingo@3650: * @param user zoom values in percent. ingo@3650: */ ingo@2330: protected void zoomY( ingo@2330: XYPlot plot, ingo@2330: ValueAxis axis, ingo@2330: Bounds total, ingo@2330: Bounds user ingo@2330: ) { teichmann@8202: if (log.isDebugEnabled()) { teichmann@8202: log.debug("== Zoom Y axis =="); teichmann@8202: log.debug(" Total axis range : " + total); teichmann@8202: log.debug(" User defined range: " + user); ingo@2330: } ingo@2330: ingo@2330: if (user != null) { ingo@2330: double min = total.getLower().doubleValue(); ingo@2330: double max = total.getUpper().doubleValue(); ingo@2330: double diff = max > min ? max - min : min - max; ingo@2330: ingo@2330: double newMin = min + user.getLower().doubleValue() * diff; ingo@2330: double newMax = min + user.getUpper().doubleValue() * diff; ingo@2330: ingo@2330: DoubleBounds newBounds = new DoubleBounds(newMin, newMax); ingo@2330: teichmann@8202: log.debug(" Zoom axis to: " + newBounds); ingo@2330: ingo@2330: newBounds.applyBounds(axis, AXIS_SPACE); ingo@2330: } ingo@2330: else { teichmann@8202: log.debug("No user specified zoom values found!"); sascha@3140: if (total != null && axis != null) { sascha@3140: total.applyBounds(axis, AXIS_SPACE); sascha@3140: } ingo@2330: } ingo@2261: } ingo@2586: ingo@2586: ingo@2586: /** felix@7595: * Adjusts the (look of) axes of a plot. This method sets the labelFont of the ingo@2586: * X axis. sascha@3726: * ingo@3650: * (Duplicate in XYChartGenerator). ingo@2586: * ingo@2586: * @param plot The XYPlot of the chart. ingo@2586: */ ingo@2586: protected void adjustAxes(XYPlot plot) { ingo@2586: ValueAxis xaxis = plot.getDomainAxis(); ingo@2586: ingo@2586: ChartSettings chartSettings = getChartSettings(); ingo@2586: if (chartSettings == null) { ingo@2586: return; ingo@2586: } ingo@2586: ingo@2586: Font labelFont = new Font( ingo@2586: DEFAULT_FONT_NAME, ingo@2586: Font.BOLD, ingo@2586: getXAxisLabelFontSize()); ingo@2586: ingo@2586: xaxis.setLabelFont(labelFont); ingo@2590: xaxis.setTickLabelFont(labelFont); ingo@2586: } raimund@2633: raimund@2633: clins@3984: protected Date decodeXAxisValue(JSONArray array) throws JSONException, ParseException { clins@3984: try { clins@3984: double x = array.getDouble(0); clins@3984: long l = (new Double(x)).longValue(); clins@3984: return new Date(l); clins@3984: } clins@3984: catch(JSONException ex) { clins@3984: String str = array.getString(0); clins@3984: DateFormat df = DateFormat.getDateInstance( clins@3984: DateFormat.MEDIUM, Resources.getLocale(context.getMeta())); clins@3984: return df.parse(str); clins@3984: } clins@3984: } clins@3984: raimund@2633: /** raimund@2633: * Do Points out. raimund@2633: */ raimund@2633: protected void doPoints( raimund@2633: Object o, raimund@2633: ArtifactAndFacet aandf, teichmann@6905: ThemeDocument theme, raimund@2633: boolean visible, raimund@2633: int axisIndex raimund@2633: ) { raimund@2633: String seriesName = aandf.getFacetDescription(); raimund@2633: TimeSeries series = new StyledTimeSeries(seriesName, theme); raimund@2633: raimund@2633: // Add text annotations for single points. raimund@2633: List xy = new ArrayList(); teichmann@7904: HashMap names = new HashMap(); raimund@2633: raimund@2633: try { raimund@2633: JSONArray points = new JSONArray((String) o); sascha@3087: for (int i = 0, P = points.length(); i < P; i++) { raimund@2633: JSONArray array = points.getJSONArray(i); clins@3984: raimund@2633: double y = array.getDouble(1); raimund@2633: String name = array.getString(2); raimund@2633: boolean act = array.getBoolean(3); raimund@2633: if (!act) { raimund@2633: continue; raimund@2633: } clins@3984: clins@3984: Date date = decodeXAxisValue(array); teichmann@7904: long ms = date.getTime(); clins@3984: teichmann@7904: FixedMillisecond day = new FixedMillisecond(ms); teichmann@7904: while (names.containsKey(day)) { teichmann@7904: day = new FixedMillisecond(++ms); teichmann@7904: } raimund@2633: series.add(day, y, false); raimund@2633: names.put(day, name); raimund@2633: } raimund@2633: } clins@3984: catch(JSONException ex) { teichmann@8202: log.error("Could not decode json"); clins@3984: } clins@3984: catch(ParseException ex) { teichmann@8202: log.error("Could not parse date string"); raimund@2633: } raimund@2633: raimund@2633: TimeSeriesCollection tsc = new TimeSeriesCollection(); raimund@2633: tsc.addSeries(series); raimund@2633: // Add Annotations. sascha@3087: for (int i = 0, S = series.getItemCount(); i < S; i++) { raimund@2633: double x = tsc.getXValue(0, i); raimund@2633: double y = tsc.getYValue(0, i); raimund@2633: xy.add(new CollisionFreeXYTextAnnotation( raimund@2633: names.get(series.getTimePeriod(i)), x, y)); teichmann@8202: log.debug("doPoints(): x=" + x + " y=" + y); raimund@2633: } teichmann@5864: RiverAnnotation annotations = teichmann@5864: new RiverAnnotation(null, null, null, theme); raimund@2633: annotations.setTextAnnotations(xy); raimund@2633: raimund@2633: // Do not generate second legend entry. (null was passed for the aand before). raimund@2634: doAnnotations(annotations, null, theme, visible); raimund@2633: raimund@2633: addAxisDataset(tsc, axisIndex, visible); raimund@2633: } raimund@2633: raimund@3130: public void addDomainAxisMarker(XYPlot plot) { teichmann@8202: log.debug("domainmarkers: " + domainMarker.size()); raimund@3130: for (Marker marker: domainMarker) { teichmann@8202: log.debug("adding domain marker"); raimund@3130: plot.addDomainMarker(marker, Layer.BACKGROUND); raimund@3130: } raimund@3130: domainMarker.clear(); raimund@3130: } raimund@3130: raimund@3130: public void addValueAxisMarker(XYPlot plot) { raimund@3130: for (Marker marker: valueMarker) { teichmann@8202: log.debug("adding value marker.."); raimund@3130: plot.addRangeMarker(marker, Layer.BACKGROUND); raimund@3130: } raimund@3130: valueMarker.clear(); raimund@3130: } raimund@2633: raimund@3134: public void addAttribute(String seriesKey, String name) { raimund@3134: attributes.put(seriesKey, name); raimund@3134: } raimund@3134: christian@4044: private LegendItem getLegendItemFor(XYPlot plot, String interSeriesKey) { christian@4044: LegendItemCollection litems = plot.getLegendItems(); christian@4044: Iterator iter = litems.iterator(); christian@4044: while(iter.hasNext()) { christian@4044: LegendItem item = iter.next(); christian@4044: if(interSeriesKey.startsWith(item.getSeriesKey().toString())) { christian@4044: return item; christian@4044: } christian@4044: } christian@4044: return null; christian@4044: } christian@4044: raimund@3134: protected void applySeriesAttributes(XYPlot plot) { raimund@3134: int count = plot.getDatasetCount(); raimund@3134: for (int i = 0; i < count; i++) { raimund@3134: XYDataset data = plot.getDataset(i); sascha@3140: if (data == null) { sascha@3140: continue; sascha@3140: } christian@4044: raimund@3134: int seriesCount = data.getSeriesCount(); raimund@3134: for (int j = 0; j < seriesCount; j++) { raimund@3134: StyledTimeSeries series = raimund@3134: (StyledTimeSeries)getSeriesOf(data, j); raimund@3134: String key = series.getKey().toString(); christian@4044: raimund@3134: if (attributes.containsKey(key)) { christian@4021: // Interpolated points are drawn unfilled raimund@3134: if (attributes.get(key).equals("interpolate")) { raimund@3134: XYLineAndShapeRenderer renderer = christian@4044: series.getStyle().getRenderer(); raimund@3134: renderer.setSeriesPaint( raimund@3134: j, raimund@3134: renderer.getSeriesFillPaint(j)); raimund@3134: renderer.setSeriesShapesFilled(j, false); christian@4044: christian@4044: LegendItem legendItem = getLegendItemFor(plot, key); christian@4044: if(legendItem != null) { christian@4044: LegendItem interLegend = new LegendItem( christian@4044: legendItem.getLabel(), christian@4044: legendItem.getDescription(), christian@4044: legendItem.getToolTipText(), christian@4044: legendItem.getURLText(), christian@4044: legendItem.isShapeVisible(), christian@4044: legendItem.getShape(), christian@4044: false, // shapeFilled? christian@4044: legendItem.getFillPaint(), christian@4044: true, // shapeOutlineVisible? christian@4044: renderer.getSeriesFillPaint(j), christian@4044: legendItem.getOutlineStroke(), christian@4044: legendItem.isLineVisible(), christian@4044: legendItem.getLine(), christian@4044: legendItem.getLineStroke(), christian@4044: legendItem.getLinePaint() christian@4044: ); christian@4044: interLegend.setSeriesKey(series.getKey()); teichmann@8202: log.debug("applySeriesAttributes: draw unfilled legend item"); christian@4044: plot.getLegendItems().add(interLegend); christian@4044: } raimund@3168: } raimund@3168: } christian@4044: raimund@3168: if (attributes.containsKey(key)) { raimund@3168: if(attributes.get(key).equals("outline")) { raimund@3168: XYLineAndShapeRenderer renderer = raimund@3168: series.getStyle().getRenderer(); raimund@3168: renderer.setSeriesPaint( raimund@3168: j, raimund@3168: renderer.getSeriesFillPaint(j)); raimund@3134: renderer.setDrawOutlines(true); raimund@3134: } raimund@3134: } raimund@3134: } raimund@3134: } raimund@3134: } raimund@3134: raimund@2633: /** Two Ranges that span a rectangular area. */ raimund@2633: public static class Area { raimund@2633: protected Range xRange; raimund@2633: protected Range yRange; raimund@2633: raimund@2633: public Area(Range rangeX, Range rangeY) { raimund@2633: this.xRange = rangeX; raimund@2633: this.yRange = rangeY; raimund@2633: } raimund@2633: raimund@2633: public Area(ValueAxis axisX, ValueAxis axisY) { raimund@2633: this.xRange = axisX.getRange(); raimund@2633: this.yRange = axisY.getRange(); raimund@2633: } raimund@2633: raimund@2633: public double ofLeft(double percent) { raimund@2633: return xRange.getLowerBound() raimund@2633: + xRange.getLength() * percent; raimund@2633: } raimund@2633: raimund@2633: public double ofRight(double percent) { raimund@2633: return xRange.getUpperBound() raimund@2633: - xRange.getLength() * percent; raimund@2633: } raimund@2633: raimund@2633: public double ofGround(double percent) { raimund@2633: return yRange.getLowerBound() raimund@2633: + yRange.getLength() * percent; raimund@2633: } raimund@2633: raimund@2633: public double atTop() { raimund@2633: return yRange.getUpperBound(); raimund@2633: } raimund@2633: raimund@2633: public double atGround() { raimund@2633: return yRange.getLowerBound(); raimund@2633: } raimund@2633: raimund@2633: public double atRight() { raimund@2633: return xRange.getUpperBound(); raimund@2633: } raimund@2633: raimund@2633: public double atLeft() { raimund@2633: return xRange.getLowerBound(); raimund@2633: } raimund@2633: raimund@2633: public double above(double percent, double base) { raimund@2633: return base + yRange.getLength() * percent; raimund@2633: } raimund@2633: } raimund@2633: ingo@2233: } ingo@2233: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :