ingo@2233: package de.intevation.flys.exports;
ingo@2233:
christian@3278: import de.intevation.artifactdatabase.state.ArtifactAndFacet;
christian@3278: import de.intevation.flys.jfree.Bounds;
christian@3278: import de.intevation.flys.jfree.CollisionFreeXYTextAnnotation;
christian@3278: import de.intevation.flys.jfree.DoubleBounds;
christian@3278: import de.intevation.flys.jfree.FLYSAnnotation;
christian@3278: import de.intevation.flys.jfree.StyledTimeSeries;
christian@3278: import de.intevation.flys.jfree.TimeBounds;
christian@3278:
sascha@3139: import java.awt.Color;
sascha@3139: import java.awt.Font;
sascha@3139: import java.util.ArrayList;
sascha@3139: import java.util.Date;
sascha@3139: import java.util.HashMap;
sascha@3139: import java.util.List;
sascha@3139: import java.util.Map;
sascha@3139:
felix@3622: import javax.swing.ImageIcon;
felix@3622:
sascha@3139: import org.apache.log4j.Logger;
sascha@3139: import org.jfree.chart.ChartFactory;
sascha@3139: import org.jfree.chart.JFreeChart;
felix@3622: import org.jfree.chart.annotations.XYAnnotation;
felix@3622: import org.jfree.chart.annotations.XYImageAnnotation;
sascha@3139: import org.jfree.chart.annotations.XYTextAnnotation;
sascha@3139: import org.jfree.chart.axis.ValueAxis;
sascha@3139: import org.jfree.chart.plot.Marker;
sascha@3139: import org.jfree.chart.plot.XYPlot;
sascha@3139: import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
sascha@3139: import org.jfree.data.Range;
sascha@3139: import org.jfree.data.general.Series;
sascha@3139: import org.jfree.data.time.Day;
sascha@3139: import org.jfree.data.time.TimeSeries;
sascha@3139: import org.jfree.data.time.TimeSeriesCollection;
sascha@3139: 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;
christian@3242: import org.w3c.dom.Document;
sascha@3139:
ingo@2233: /**
ingo@2233: * @author Ingo Weinzierl
ingo@2233: */
ingo@2233: public abstract class TimeseriesChartGenerator extends ChartGenerator {
ingo@2233:
ingo@2238:
ingo@2238: /**
ingo@2238: * Inner class TimeseriesAxisDataset stores TimeSeriesCollection.
ingo@2238: */
ingo@2238: public class TimeseriesAxisDataset implements AxisDataset {
ingo@2238:
ingo@2238: protected int axisSymbol;
ingo@2238:
ingo@2238: protected List datasets;
ingo@2238:
ingo@2238: protected Range range;
ingo@2238:
ingo@2238: protected int plotAxisIndex;
ingo@2238:
ingo@2238: public TimeseriesAxisDataset(int axisSymbol) {
ingo@2238: this.axisSymbol = axisSymbol;
ingo@2238: this.datasets = new ArrayList();
ingo@2238: }
ingo@2238:
ingo@2238:
ingo@2238: @Override
ingo@2238: public void addDataset(XYDataset dataset) {
ingo@2238: if (!(dataset instanceof TimeSeriesCollection)) {
ingo@2238: logger.warn("Skip non TimeSeriesCollection dataset.");
ingo@2238: return;
ingo@2238: }
ingo@2238:
ingo@2238: TimeSeriesCollection tsc = (TimeSeriesCollection) dataset;
ingo@2238:
ingo@2238: datasets.add(tsc);
ingo@2238: mergeRanges(tsc);
ingo@2238: }
ingo@2238:
ingo@2238:
ingo@2238: @Override
ingo@2242: public XYDataset[] getDatasets() {
christian@3278: return datasets.toArray(new XYDataset[datasets.size()]);
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2242: @Override
ingo@2238: public boolean isEmpty() {
ingo@2238: return datasets.isEmpty();
ingo@2238: }
ingo@2238:
ingo@2238:
ingo@2242: @Override
ingo@2242: public void setRange(Range range) {
ingo@2242: this.range = range;
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2242: @Override
ingo@2242: public Range getRange() {
ingo@2242: return range;
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2242: @Override
ingo@2242: public void setPlotAxisIndex(int plotAxisIndex) {
ingo@2242: this.plotAxisIndex = plotAxisIndex;
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2242: @Override
ingo@2242: public int getPlotAxisIndex() {
ingo@2242: return plotAxisIndex;
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2242: @Override
ingo@2242: public boolean isArea(XYDataset dataset) {
ingo@2242: logger.warn("This AxisDataset doesn't support Areas yet!");
ingo@2242: return false;
ingo@2242: }
ingo@2242:
ingo@2242:
ingo@2238: protected void mergeRanges(TimeSeriesCollection dataset) {
raimund@3236: logger.debug("Range before merging: " + range);
raimund@3236: Range subRange = null;
ingo@2238:
raimund@3236: // Determine min/max of range axis.
raimund@3236: for (int i = 0; i < dataset.getSeriesCount(); i++) {
raimund@3610: if (dataset.getSeries(i).getItemCount() == 0) {
raimund@3610: continue;
raimund@3610: }
raimund@3610: double min = Double.MAX_VALUE;
raimund@3610: double max = -Double.MAX_VALUE;
raimund@3236: TimeSeries series = dataset.getSeries(i);
raimund@3236: for (int j = 0; j < series.getItemCount(); j++) {
raimund@3236: double tmp = series.getValue(j).doubleValue();
raimund@3236: min = tmp < min ? tmp : min;
raimund@3236: max = tmp > max ? tmp : max;
raimund@3236: }
raimund@3236: if (subRange != null) {
raimund@3236: subRange = new Range(
raimund@3236: min < subRange.getLowerBound() ?
raimund@3236: min : subRange.getLowerBound(),
raimund@3236: max > subRange.getUpperBound() ?
raimund@3236: max : subRange.getUpperBound());
raimund@3236: }
raimund@3236: else {
raimund@3236: subRange = new Range(min, max);
raimund@3236: }
raimund@3236: }
ingo@2400:
raimund@3236: // Avoid merging NaNs, as they take min/max place forever.
raimund@3236: if (subRange == null ||
raimund@3236: Double.isNaN(subRange.getLowerBound()) ||
raimund@3236: Double.isNaN(subRange.getUpperBound())) {
raimund@3236: return;
raimund@3236: }
raimund@3236: if (range == null) {
raimund@3236: range = subRange;
raimund@3236: return;
raimund@3236: }
raimund@3236: range = Range.combine(range, subRange);
ingo@2238: }
ingo@2238:
ingo@2238: } // end of TimeseriesAxisDataset class
ingo@2238:
raimund@3130: protected List domainMarker;
raimund@3130:
raimund@3130: protected List valueMarker;
raimund@3130:
raimund@3134: protected Map attributes;
raimund@3134:
raimund@3130: protected boolean domainZeroLineVisible;
ingo@2238:
ingo@2233: private static final Logger logger =
ingo@2233: Logger.getLogger(TimeseriesChartGenerator.class);
ingo@2233:
ingo@2400: public static final int AXIS_SPACE = 5;
ingo@2238:
ingo@2587: protected Map xBounds;
ingo@2400:
ingo@2587: protected Map yBounds;
ingo@2242:
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() {
ingo@2233: logger.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@2554: 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@2553: adjustPlot(plot);
ingo@2238: addDatasets(plot);
ingo@2586: adjustAxes(plot);
raimund@3130: addDomainAxisMarker(plot);
raimund@3130: addValueAxisMarker(plot);
ingo@2400: adaptZoom(plot);
ingo@2400:
raimund@3134: applySeriesAttributes(plot);
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) {
felix@3622: logger.debug("No logo to show chosen");
felix@3622: return;
felix@3622: }
felix@3622:
felix@3622: 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@3623: */
felix@3622: if (logo.equals("Intevation")) {
felix@3622: imageIcon = new ImageIcon("/home/felix/Downloads/intevation_logo_120.png");
felix@3622: }
felix@3622: else { // TODO else if ...
felix@3622: imageIcon = new ImageIcon("/home/felix/Downloads/bfg_logo.gif");
felix@3622: }
felix@3622:
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 {
felix@3622: logger.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 {
felix@3622: logger.debug("Unknown place-h value: " + placeh);
felix@3622: }
felix@3622:
felix@3622: logger.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 =
felix@3622: new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor);
felix@3622: plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND);
felix@3622: }
felix@3622:
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: /**
ingo@2238: * This method creates new instances of TimeseriesAxisDataset.
ingo@2238: *
ingo@2238: * @param idx The symbol for the new TimeseriesAxisDataset.
ingo@2238: */
ingo@2238: @Override
ingo@2238: protected AxisDataset createAxisDataset(int idx) {
ingo@2238: logger.debug("Create a new AxisDataset for index: " + idx);
ingo@2238: return new TimeseriesAxisDataset(idx);
ingo@2238: }
ingo@2238:
ingo@2238:
ingo@2400: @Override
ingo@2587: protected void combineXBounds(Bounds bounds, int index) {
ingo@2400: if (bounds != null) {
ingo@2587: Bounds old = getXBounds(index);
ingo@2238:
ingo@2238: if (old != null) {
ingo@2400: bounds = bounds.combine(old);
ingo@2238: }
ingo@2238:
ingo@2400: setXBounds(index, bounds);
ingo@2238: }
ingo@2238: }
ingo@2261:
ingo@2261:
ingo@2587: @Override
ingo@2587: protected void combineYBounds(Bounds bounds, int index) {
ingo@2400: if (bounds != null) {
ingo@2400: Bounds old = getYBounds(index);
ingo@2400:
ingo@2400: if (old != null) {
ingo@2400: bounds = bounds.combine(old);
ingo@2400: }
ingo@2400:
ingo@2400: setYBounds(index, bounds);
ingo@2400: }
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index)
ingo@2400: @Override
ingo@2400: public Range[] getRangesForAxis(int index) {
ingo@2400: // TODO
ingo@2400: Bounds[] bounds = getBoundsForAxis(index);
ingo@2400:
ingo@2400: return new Range[] {
ingo@2400: new Range(
ingo@2400: bounds[0].getLower().doubleValue(),
ingo@2400: bounds[0].getUpper().doubleValue()),
ingo@2400: new Range(
ingo@2400: bounds[1].getLower().doubleValue(),
ingo@2400: bounds[1].getUpper().doubleValue())
ingo@2400: };
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2261: @Override
ingo@2400: public Bounds getXBounds(int axis) {
ingo@2587: return xBounds.get(axis);
ingo@2400: }
ingo@2261:
ingo@2400:
ingo@2400: @Override
ingo@2400: protected void setXBounds(int axis, Bounds bounds) {
ingo@2587: xBounds.put(axis, bounds);
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: @Override
ingo@2400: public Bounds getYBounds(int axis) {
ingo@2587: return yBounds.get(axis);
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: @Override
ingo@2400: protected void setYBounds(int axis, Bounds bounds) {
ingo@2587: if (bounds != null) {
ingo@2587: yBounds.put(axis, bounds);
ingo@2587: }
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: public Bounds[] getBoundsForAxis(int index) {
ingo@2400: logger.debug("Return x and y bounds for axis at: " + index);
ingo@2400:
ingo@2400: Bounds rx = getXBounds(Integer.valueOf(index));
ingo@2400: Bounds ry = getYBounds(Integer.valueOf(index));
ingo@2261:
ingo@2261: if (rx == null) {
ingo@2261: logger.warn("Range for x axis not set." +
ingo@2261: " Using default values: 0 - 1.");
ingo@2400: rx = new TimeBounds(0l, 1l);
ingo@2261: }
ingo@2261:
ingo@2400: if (ry == null) {
ingo@2400: logger.warn("Range for y axis not set." +
ingo@2400: " Using default values: 0 - 1.");
ingo@2400: ry = new DoubleBounds(0l, 1l);
ingo@2400: }
ingo@2261:
ingo@2400: logger.debug("X Bounds at index " + index + " is: " + rx);
ingo@2400: logger.debug("Y Bounds at index " + index + " is: " + ry);
ingo@2400:
ingo@2400: return new Bounds[] {rx, ry};
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: public Bounds getDomainAxisRange() {
ingo@2400: String[] ranges = getDomainAxisRangeFromRequest();
ingo@2400:
ingo@2400: if (ranges == null || ranges.length < 2) {
ingo@2400: logger.debug("No zoom range for domain axis specified.");
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400: if (ranges[0] == null || ranges[1] == null) {
ingo@2400: logger.warn("Invalid ranges for domain axis specified!");
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400: try {
ingo@2400: double lower = Double.parseDouble(ranges[0]);
ingo@2400: double upper = Double.parseDouble(ranges[1]);
ingo@2400:
ingo@2400: return new DoubleBounds(lower, upper);
ingo@2400: }
ingo@2400: catch (NumberFormatException nfe) {
ingo@2400: logger.warn("Invalid ranges for domain axis specified: " + nfe);
ingo@2400: }
ingo@2400:
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: public Bounds getValueAxisRange() {
ingo@2400: String[] ranges = getValueAxisRangeFromRequest();
ingo@2400:
ingo@2400: if (ranges == null || ranges.length < 2) {
ingo@2400: logger.debug("No zoom range for domain axis specified.");
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400: if (ranges[0] == null || ranges[1] == null) {
ingo@2400: logger.warn("Invalid ranges for domain axis specified!");
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400: try {
ingo@2400: double lower = Double.parseDouble(ranges[0]);
ingo@2400: double upper = Double.parseDouble(ranges[1]);
ingo@2400:
ingo@2400: return new DoubleBounds(lower, upper);
ingo@2400: }
ingo@2400: catch (NumberFormatException nfe) {
ingo@2400: logger.warn("Invalid ranges for domain axis specified: " + nfe);
ingo@2400: }
ingo@2400:
ingo@2400: return null;
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: protected void adaptZoom(XYPlot plot) {
ingo@2400: logger.debug("Adapt zoom of Timeseries chart.");
ingo@2400:
ingo@2587: zoomX(plot, plot.getDomainAxis(), getXBounds(0), getDomainAxisRange());
ingo@2400:
ingo@2400: Bounds valueAxisBounds = getValueAxisRange();
ingo@2400:
ingo@2400: for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) {
ingo@2400: zoomY(
ingo@2400: plot,
ingo@2400: plot.getRangeAxis(j),
ingo@2400: getYBounds(j),
ingo@2400: valueAxisBounds);
ingo@2400: }
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: protected void zoomX(
ingo@2400: XYPlot plot,
ingo@2400: ValueAxis axis,
ingo@2400: Bounds total,
ingo@2400: Bounds user
ingo@2400: ) {
ingo@2400: if (logger.isDebugEnabled()) {
ingo@2400: logger.debug("== Zoom X axis ==");
ingo@2400: logger.debug(" Total axis range : " + total);
ingo@2400: logger.debug(" User defined range: " + user);
ingo@2400: }
ingo@2400:
ingo@2400: if (user != null) {
ingo@2400: long min = total.getLower().longValue();
ingo@2400: long max = total.getUpper().longValue();
ingo@2400: long diff = max > min ? max - min : min - max;
ingo@2400:
christian@3278: long newMin = Math.round(min + user.getLower().doubleValue() * diff);
christian@3278: long newMax = Math.round(min + user.getUpper().doubleValue() * diff);
ingo@2400:
ingo@2400: TimeBounds newBounds = new TimeBounds(newMin, newMax);
ingo@2400:
ingo@2400: logger.debug(" Zoom axis to: " + newBounds);
ingo@2400:
ingo@2400: newBounds.applyBounds(axis, AXIS_SPACE);
ingo@2400: }
ingo@2400: else {
ingo@2400: logger.debug("No user specified zoom values found!");
sascha@3140: if (total != null && axis != null) {
sascha@3140: total.applyBounds(axis, AXIS_SPACE);
sascha@3140: }
ingo@2400: }
ingo@2400: }
ingo@2400:
ingo@2400:
ingo@2400: protected void zoomY(
ingo@2400: XYPlot plot,
ingo@2400: ValueAxis axis,
ingo@2400: Bounds total,
ingo@2400: Bounds user
ingo@2400: ) {
ingo@2400: if (logger.isDebugEnabled()) {
ingo@2400: logger.debug("== Zoom Y axis ==");
ingo@2400: logger.debug(" Total axis range : " + total);
ingo@2400: logger.debug(" User defined range: " + user);
ingo@2400: }
ingo@2400:
ingo@2400: if (user != null) {
ingo@2400: double min = total.getLower().doubleValue();
ingo@2400: double max = total.getUpper().doubleValue();
ingo@2400: double diff = max > min ? max - min : min - max;
ingo@2400:
ingo@2400: double newMin = min + user.getLower().doubleValue() * diff;
ingo@2400: double newMax = min + user.getUpper().doubleValue() * diff;
ingo@2400:
ingo@2400: DoubleBounds newBounds = new DoubleBounds(newMin, newMax);
ingo@2400:
ingo@2400: logger.debug(" Zoom axis to: " + newBounds);
ingo@2400:
ingo@2400: newBounds.applyBounds(axis, AXIS_SPACE);
ingo@2400: }
ingo@2400: else {
ingo@2400: logger.debug("No user specified zoom values found!");
sascha@3140: if (total != null && axis != null) {
sascha@3140: total.applyBounds(axis, AXIS_SPACE);
sascha@3140: }
ingo@2400: }
ingo@2261: }
ingo@2586:
ingo@2586:
ingo@2586: /**
ingo@2586: * Adjusts the axes of a plot. This method sets the labelFont of the
ingo@2586: * X axis.
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:
raimund@2633: /**
raimund@2633: * Do Points out.
raimund@2633: */
raimund@2633: protected void doPoints(
raimund@2633: Object o,
raimund@2633: ArtifactAndFacet aandf,
raimund@2633: Document 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();
raimund@2633: 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);
raimund@2633: double x = array.getDouble(0);
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: }
raimund@2633: long l = (new Double(x)).longValue();
raimund@2633: Date date = new Date(l);
raimund@2633: Day day = new Day(date);
raimund@2633: series.add(day, y, false);
raimund@2633: names.put(day, name);
raimund@2633: }
raimund@2633: }
raimund@2633: catch(JSONException e){
raimund@2633: logger.error("Could not decode json.");
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));
raimund@2633: }
raimund@2633: FLYSAnnotation annotations =
raimund@2633: new FLYSAnnotation(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) {
raimund@3130: logger.debug("domainmarkers: " + domainMarker.size());
raimund@3130: for (Marker marker: domainMarker) {
raimund@3130: logger.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) {
raimund@3130: logger.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:
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: }
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();
raimund@3134: if (attributes.containsKey(key)) {
raimund@3134: if (attributes.get(key).equals("interpolate")) {
raimund@3134: XYLineAndShapeRenderer renderer =
raimund@3134: series.getStyle().getRenderer();
raimund@3134: renderer.setSeriesPaint(
raimund@3134: j,
raimund@3134: renderer.getSeriesFillPaint(j));
raimund@3134: renderer.setSeriesShapesFilled(j, false);
raimund@3168: }
raimund@3168: }
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 :