[PATCH 2 of 2] Add DiagramGenerator which should mainly replace xygenerator
Wald Commits
scm-commit at wald.intevation.org
Wed Sep 18 17:13:21 CEST 2013
# HG changeset patch
# User Andre Heinecke <aheinecke at intevation.de>
# Date 1379517197 -7200
# Branch generator-refactoring
# Node ID 6ab1464021ae6694911396dbb5859023506a1d11
# Parent 06a9a241faac9c2ca3b1bfba60d654c357692598
Add DiagramGenerator which should mainly replace xygenerator
diff -r 06a9a241faac -r 6ab1464021ae artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java Wed Sep 18 17:13:17 2013 +0200
@@ -0,0 +1,1016 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.annotations.XYAnnotation;
+import org.jfree.chart.annotations.XYImageAnnotation;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.axis.LogarithmicAxis;
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.general.Series;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.AnnotationHelper;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+/* TODO remove after hackish testing */
+import org.dive4elements.river.exports.process.Processor;
+import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
+import org.dive4elements.river.exports.process.BedDiffYearProcessor;
+import org.dive4elements.river.exports.process.BedheightProcessor;
+import org.dive4elements.river.exports.process.QOutProcessor;
+import org.dive4elements.river.exports.process.WOutProcessor;
+/* end TODO*/
+
+/**
+ * The main diagram creation class.
+ *
+ * This class is the glue between output processors and facets.
+ * The generator creates one diagram and calls the appropiate
+ * processors for the state and
+ *
+ * With respect to datasets, ranges and axis, there are following requirements:
+ * <ul>
+ * <li> First in, first drawn: "Early" datasets should be of lower Z-Oder
+ * than later ones (only works per-axis). </li>
+ * <li> Visible axis should initially show the range of all datasets that
+ * show data for this axis (even invisible ones). Motivation: Once
+ * a dataset (theme) has been activated, it should be on screen. </li>
+ * <li> There should always be a Y-Axis on the "left". </li>
+ * </ul>
+ */
+public abstract class DiagramGenerator extends ChartGenerator2 {
+
+ /** Enumerator over existing axes. */
+ @Override
+ protected abstract YAxisWalker getYAxisWalker();
+
+ public static final int AXIS_SPACE = 5;
+
+ /** The logger that is used in this generator. */
+ private static Logger logger = Logger.getLogger(DiagramGenerator.class);
+
+ protected List<Marker> domainMarkers = new ArrayList<Marker>();
+
+ protected List<Marker> valueMarkers = new ArrayList<Marker>();
+
+ /** The max X range to include all X values of all series for each axis. */
+ protected Map<Integer, Bounds> xBounds;
+
+ /** The max Y range to include all Y values of all series for each axis. */
+ protected Map<Integer, Bounds> yBounds;
+
+ /** Whether or not the plot is inverted (left-right). */
+ private boolean inverted;
+
+ public DiagramGenerator() {
+ super();
+
+ xBounds = new HashMap<Integer, Bounds>();
+ yBounds = new HashMap<Integer, Bounds>();
+ }
+
+
+ /**
+ * Generate the chart anew (including localized axis and all).
+ */
+ @Override
+ public JFreeChart generateChart() {
+ logger.debug("DiagramGenerator.generateChart");
+
+ JFreeChart chart = ChartFactory.createXYLineChart(
+ getChartTitle(),
+ getXAxisLabel(),
+ getYAxisLabel(0),
+ null,
+ PlotOrientation.VERTICAL,
+ isLegendVisible(),
+ false,
+ false);
+
+ XYPlot plot = (XYPlot) chart.getPlot();
+ ValueAxis axis = createXAxis(getXAxisLabel());
+ plot.setDomainAxis(axis);
+
+ chart.setBackgroundPaint(Color.WHITE);
+ plot.setBackgroundPaint(Color.WHITE);
+ addSubtitles(chart);
+ adjustPlot(plot);
+
+ //debugAxis(plot);
+
+ addDatasets(plot);
+
+ //debugDatasets(plot);
+
+ addMarkers(plot);
+
+ recoverEmptyPlot(plot);
+ preparePointRanges(plot);
+
+ //debugAxis(plot);
+
+ localizeAxes(plot);
+ adjustAxes(plot);
+ if (!(axis instanceof LogarithmicAxis)) {
+ // XXX:
+ // The auto zoom without a range tries
+ // to include 0 in a logarithmic axis
+ // which triggers a bug in jfreechart that causes
+ // the values to be drawn carthesian
+ autoZoom(plot);
+ }
+
+ //debugAxis(plot);
+
+ // These have to go after the autozoom.
+ AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
+ getChartSettings(), datasets);
+
+ // Add a logo (maybe).
+ addLogo(plot);
+
+ aggregateLegendEntries(plot);
+
+ return chart;
+ }
+
+
+ /**
+ * Return left most data points x value (on first axis).
+ * Shortcut, especially to be overridden in (LS) charts where
+ * axis could be inverted.
+ */
+ protected double getLeftX() {
+ return (Double)getXBounds(0).getLower();
+ }
+
+
+ /**
+ * Return right most data points x value (on first axis).
+ * Shortcut, especially to be overridden in (LS) charts where
+ * axis could be inverted.
+ */
+ protected double getRightX() {
+ return (Double)getXBounds(0).getUpper();
+ }
+
+
+ /** Add a logo as background annotation to plot. */
+ protected void addLogo(XYPlot plot) {
+ String logo = showLogo();
+ if (logo == null) {
+ logger.debug("No logo to show chosen");
+ return;
+ }
+
+ ImageIcon imageIcon = null;
+ if (logo.equals("none")) {
+ return;
+ }
+ /*
+ If you want to add images, remember to change code in these places:
+ flys-artifacts:
+ DiagramGenerator.java
+ Timeseries*Generator.java and
+ in the flys-client projects Chart*Propert*Editor.java.
+ Also, these images have to be put in
+ flys-artifacts/src/main/resources/images/
+ flys-client/src/main/webapp/images/
+ */
+ java.net.URL imageURL;
+ if (logo.equals("Intevation")) {
+ imageURL = DiagramGenerator.class.getResource("/images/intevation.png");
+ }
+ else { // TODO else if ...
+ imageURL = DiagramGenerator.class.getResource("/images/bfg_logo.gif");
+ }
+ imageIcon = new ImageIcon(imageURL);
+
+
+ double xPos = 0d, yPos = 0d;
+
+ String placeh = logoHPlace();
+ String placev = logoVPlace();
+
+ if (placev == null || placev.equals("none")) {
+ placev = "top";
+ }
+ if (placev.equals("top")) {
+ yPos = (Double)getYBounds(0).getUpper();
+ }
+ else if (placev.equals("bottom")) {
+ yPos = (Double)getYBounds(0).getLower();
+ }
+ else if (placev.equals("center")) {
+ yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d;
+ }
+ else {
+ logger.debug("Unknown place-v value: " + placev);
+ }
+
+ if (placeh == null || placeh.equals("none")) {
+ placeh = "center";
+ }
+ if (placeh.equals("left")) {
+ xPos = getLeftX();
+ }
+ else if (placeh.equals("right")) {
+ xPos = getRightX();
+ }
+ else if (placeh.equals("center")) {
+ xPos = ((Double)getXBounds(0).getUpper() + (Double)getXBounds(0).getLower())/2d;
+ }
+ else {
+ logger.debug("Unknown place-h value: " + placeh);
+ }
+
+ logger.debug("logo position: " + xPos + "/" + yPos);
+
+ org.jfree.ui.RectangleAnchor anchor
+ = org.jfree.ui.RectangleAnchor.TOP;
+ if (placev.equals("top")) {
+ if (placeh.equals("left")) {
+ anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT;
+ }
+ else if (placeh.equals("right")) {
+ anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT;
+ }
+ else if (placeh.equals("center")) {
+ anchor = org.jfree.ui.RectangleAnchor.TOP;
+ }
+ }
+ else if (placev.equals("bottom")) {
+ if (placeh.equals("left")) {
+ anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT;
+ }
+ else if (placeh.equals("right")) {
+ anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT;
+ }
+ else if (placeh.equals("center")) {
+ anchor = org.jfree.ui.RectangleAnchor.BOTTOM;
+ }
+ }
+ else if (placev.equals("center")) {
+ if (placeh.equals("left")) {
+ anchor = org.jfree.ui.RectangleAnchor.LEFT;
+ }
+ else if (placeh.equals("right")) {
+ anchor = org.jfree.ui.RectangleAnchor.RIGHT;
+ }
+ else if (placeh.equals("center")) {
+ anchor = org.jfree.ui.RectangleAnchor.CENTER;
+ }
+ }
+
+ XYAnnotation xyannotation =
+ new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor);
+ plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND);
+ }
+
+
+ protected NumberAxis createXAxis(String label) {
+ return new NumberAxis(label);
+ }
+
+
+ @Override
+ protected Series getSeriesOf(XYDataset dataset, int idx) {
+ return ((XYSeriesCollection) dataset).getSeries(idx);
+ }
+
+
+ @Override
+ protected AxisDataset createAxisDataset(int idx) {
+ logger.debug("Create new AxisDataset for index: " + idx);
+ return new AxisDataset(idx);
+ }
+
+
+ /**
+ * Put debug output about datasets.
+ */
+ public void debugDatasets(XYPlot plot) {
+ logger.debug("Number of datasets: " + plot.getDatasetCount());
+ for (int i = 0, P = plot.getDatasetCount(); i < P; i++) {
+ if (plot.getDataset(i) == null) {
+ logger.debug("Dataset #" + i + " is null");
+ continue;
+ }
+ logger.debug("Dataset #" + i + ":" + plot.getDataset(i));
+ XYSeriesCollection series = (XYSeriesCollection) plot.getDataset(i);
+ logger.debug("X-Extend of Dataset: " + series.getSeries(0).getMinX()
+ + " " + series.getSeries(0).getMaxX());
+ logger.debug("Y-Extend of Dataset: " + series.getSeries(0).getMinY()
+ + " " + series.getSeries(0).getMaxY());
+ }
+ }
+
+
+ /**
+ * Put debug output about axes.
+ */
+ public void debugAxis(XYPlot plot) {
+ logger.debug("...............");
+ for (int i = 0, P = plot.getRangeAxisCount(); i < P; i++) {
+ if (plot.getRangeAxis(i) == null)
+ logger.debug("Range-Axis #" + i + " == null");
+ else {
+ logger.debug("Range-Axis " + i + " != null [" +
+ plot.getRangeAxis(i).getRange().getLowerBound() +
+ " " + plot.getRangeAxis(i).getRange().getUpperBound() +
+ "]");
+ }
+ }
+ for (int i = 0, P = plot.getDomainAxisCount(); i < P; i++) {
+ if (plot.getDomainAxis(i) == null)
+ logger.debug("Domain-Axis #" + i + " == null");
+ else {
+ logger.debug("Domain-Axis " + i + " != null [" +
+ plot.getDomainAxis(i).getRange().getLowerBound() +
+ " " + plot.getDomainAxis(i).getRange().getUpperBound() +
+ "]");
+ }
+ }
+ logger.debug("...............");
+ }
+
+
+ /**
+ * Registers an area to be drawn.
+ * @param area Area to be drawn.
+ * @param index 'axis index'
+ * @param visible Whether or not to be visible (important for range calculations).
+ */
+ public void addAreaSeries(StyledAreaSeriesCollection area, int index, boolean visible) {
+ if (area == null) {
+ logger.warn("Cannot yet render above/under curve.");
+ return;
+ }
+
+ AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
+
+ if (visible) {
+ axisDataset.addArea(area);
+ }
+ else {
+ /* No range merging, for areas extending to infinity this
+ * causes problems. */
+ }
+ }
+
+
+ /**
+ * Add given series if visible, if not visible adjust ranges (such that
+ * all points in data would be plotted once visible).
+ * @param series the data series to include in plot.
+ * @param index ('symbolic') index of the series and of its axis.
+ * @param visible whether or not the data should be plotted.
+ */
+ public void addAxisSeries(XYSeries series, int index, boolean visible) {
+ if (series == null) {
+ return;
+ }
+
+ logger.debug("Y Range of XYSeries: " +
+ series.getMinY() + " | " + series.getMaxY());
+
+ addAxisDataset(new XYSeriesCollection(series), index, visible);
+
+ AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
+ }
+
+
+ /**
+ * Add the given vertical marker to the chart.
+ */
+ public void addDomainMarker(Marker marker) {
+ addDomainMarker(marker, true);
+ }
+
+
+ /**
+ * Add the given vertical marker to the chart.<b>Note:</b> the marker is
+ * added to the chart only if it is not null and if <i>visible</i> is true.
+ * @param marker The marker that should be added to the chart.
+ * @param visible The visibility of the marker.
+ */
+ public void addDomainMarker(Marker marker, boolean visible) {
+ if (visible && marker != null) {
+ domainMarkers.add(marker);
+ }
+ }
+
+
+ /**
+ * Add the given vertical marker to the chart.
+ */
+ public void addValueMarker(Marker marker) {
+ addValueMarker(marker, true);
+ }
+
+
+ /**
+ * Add the given horizontal marker to the chart.<b>Note:</b> the marker is
+ * added to the chart only if it is not null and if <i>visible</i> is true.
+ * @param marker The marker that should be added to the chart.
+ * @param visible The visibility of the marker.
+ */
+ public void addValueMarker(Marker marker, boolean visible) {
+ if (visible && marker != null) {
+ valueMarkers.add(marker);
+ }
+ }
+
+
+ protected void addMarkers(XYPlot plot) {
+ for(Marker marker : domainMarkers) {
+ plot.addDomainMarker(marker);
+ }
+ for(Marker marker : valueMarkers) {
+ plot.addRangeMarker(marker);
+ }
+ }
+
+
+ /**
+ * Effect: extend range of x axis to include given limits.
+ *
+ * @param bounds the given ("minimal") bounds.
+ * @param index index of axis to be merged.
+ */
+ @Override
+ protected void combineXBounds(Bounds bounds, int index) {
+ if (!(bounds instanceof DoubleBounds)) {
+ logger.warn("Unsupported Bounds type: " + bounds.getClass());
+ return;
+ }
+
+ DoubleBounds dBounds = (DoubleBounds) bounds;
+
+ if (dBounds == null
+ || Double.isNaN((Double) dBounds.getLower())
+ || Double.isNaN((Double) dBounds.getUpper())) {
+ return;
+ }
+
+ Bounds old = getXBounds(index);
+
+ if (old != null) {
+ dBounds = (DoubleBounds) dBounds.combine(old);
+ }
+
+ setXBounds(index, dBounds);
+ }
+
+
+ @Override
+ protected void combineYBounds(Bounds bounds, int index) {
+ if (!(bounds instanceof DoubleBounds)) {
+ logger.warn("Unsupported Bounds type: " + bounds.getClass());
+ return;
+ }
+
+ DoubleBounds dBounds = (DoubleBounds) bounds;
+
+ if (dBounds == null
+ || Double.isNaN((Double) dBounds.getLower())
+ || Double.isNaN((Double) dBounds.getUpper())) {
+ return;
+ }
+
+ Bounds old = getYBounds(index);
+
+ if (old != null) {
+ dBounds = (DoubleBounds) dBounds.combine(old);
+ }
+
+ setYBounds(index, dBounds);
+ }
+
+
+ /**
+ * If no data is visible, draw at least empty axis.
+ */
+ private void recoverEmptyPlot(XYPlot plot) {
+ if (plot.getRangeAxis() == null) {
+ logger.debug("debug: No range axis");
+ plot.setRangeAxis(createYAxis(0));
+ }
+ }
+
+
+ /**
+ * Expands X axes if only a point is shown.
+ */
+ private void preparePointRanges(XYPlot plot) {
+ for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+
+ Integer key = Integer.valueOf(i);
+ Bounds b = getXBounds(key);
+
+
+ if (b != null && b.getLower().equals(b.getUpper())) {
+ logger.debug("Check whether to expand a x axis.i ("+b.getLower() + "-" + b.getUpper()+")");
+ setXBounds(key, ChartHelper.expandBounds(b, 5));
+ }
+ }
+ }
+
+
+ /**
+ * This method zooms the plot to the specified ranges in the attribute
+ * document or to the ranges specified by the min/max values in the
+ * datasets. <b>Note:</b> We determine the range manually if no zoom ranges
+ * are given, because JFreeCharts auto-zoom adds a margin to the left and
+ * right of the data area.
+ *
+ * @param plot The XYPlot.
+ */
+ protected void autoZoom(XYPlot plot) {
+ logger.debug("Zoom to specified ranges.");
+
+ Range xrange = getDomainAxisRange();
+ Range yrange = getValueAxisRange();
+
+ ValueAxis xAxis = plot.getDomainAxis();
+
+ Range fixedXRange = getRangeForAxisFromSettings("X");
+ if (fixedXRange != null) {
+ xAxis.setRange(fixedXRange);
+ }
+ else {
+ zoomX(plot, xAxis, getXBounds(0), xrange);
+ }
+
+ for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+ ValueAxis yaxis = plot.getRangeAxis(i);
+
+ if (yaxis instanceof IdentifiableNumberAxis) {
+ IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis;
+
+ Range fixedRange = getRangeForAxisFromSettings(idAxis.getId());
+ if (fixedRange != null) {
+ yaxis.setRange(fixedRange);
+ continue;
+ }
+ }
+
+ if (yaxis == null) {
+ logger.debug("Zoom problem: no Y Axis for index: " + i);
+ continue;
+ }
+
+ logger.debug("Prepare zoom settings for y axis at index: " + i);
+ zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange);
+ }
+ }
+
+
+ protected Range getDomainAxisRange() {
+ String[] ranges = getDomainAxisRangeFromRequest();
+
+ if (ranges == null || ranges.length < 2) {
+ logger.debug("No zoom range for domain axis specified.");
+ return null;
+ }
+
+ if (ranges[0].length() > 0 && ranges[1].length() > 0) {
+ try {
+ double from = Double.parseDouble(ranges[0]);
+ double to = Double.parseDouble(ranges[1]);
+
+ if (from == 0 && to == 0) {
+ logger.debug("No range specified. Lower and upper X == 0");
+ return null;
+ }
+
+ if (from > to) {
+ double tmp = to;
+ to = from;
+ from = tmp;
+ }
+
+ return new Range(from, to);
+ }
+ catch (NumberFormatException nfe) {
+ logger.warn("Wrong values for domain axis range.");
+ }
+ }
+
+ return null;
+ }
+
+
+ protected Range getValueAxisRange() {
+ String[] ranges = getValueAxisRangeFromRequest();
+
+ if (ranges == null || ranges.length < 2) {
+ logger.debug("No range specified. Lower and upper Y == 0");
+ return null;
+ }
+
+ if (ranges[0].length() > 0 && ranges[1].length() > 0) {
+ try {
+ double from = Double.parseDouble(ranges[0]);
+ double to = Double.parseDouble(ranges[1]);
+
+ if (from == 0 && to == 0) {
+ logger.debug("No range specified. Lower and upper Y == 0");
+ return null;
+ }
+
+ return from > to
+ ? new Range(to, from)
+ : new Range(from, to);
+ }
+ catch (NumberFormatException nfe) {
+ logger.warn("Wrong values for value axis range.");
+ }
+ }
+
+ return null;
+ }
+
+
+ protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+ return zoom(plot, axis, bounds, x);
+ }
+
+
+ protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+ return zoom(plot, axis, bounds, x);
+ }
+
+
+ /**
+ * Zooms the x axis to the range specified in the attribute document.
+ *
+ * @param plot The XYPlot.
+ * @param axis The axis the shoud be modified.
+ * @param bounds The whole range specified by a dataset.
+ * @param x A user defined range (null permitted).
+ *
+ * @return true, if a zoom range was specified, otherwise false.
+ */
+ protected boolean zoom(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+
+ if (bounds == null) {
+ return false;
+ }
+
+ if (x != null) {
+ Bounds computed = calculateZoom(bounds, x);
+ computed.applyBounds(axis, AXIS_SPACE);
+
+ logger.debug("Zoom axis to: " + computed);
+
+ return true;
+ }
+
+ bounds.applyBounds(axis, AXIS_SPACE);
+ return false;
+ }
+
+ /**
+ * Calculates the start and end km for zoomed charts.
+ * @param bounds The given total bounds (unzoomed).
+ * @param range The range specifying the zoom.
+ *
+ * @return The start and end km for the zoomed chart.
+ */
+ protected Bounds calculateZoom(Bounds bounds, Range range) {
+ double min = bounds.getLower().doubleValue();
+ double max = bounds.getUpper().doubleValue();
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Minimum is: " + min);
+ logger.debug("Maximum is: " + max);
+ logger.debug("Lower zoom is: " + range.getLowerBound());
+ logger.debug("Upper zoom is: " + range.getUpperBound());
+ }
+
+ double diff = max > min ? max - min : min - max;
+
+ DoubleBounds computed = new DoubleBounds(
+ min + range.getLowerBound() * diff,
+ min + range.getUpperBound() * diff);
+ return computed;
+ }
+
+ /**
+ * Extract the minimum and maximum values for x and y axes
+ * which are stored in <i>xRanges</i> and <i>yRanges</i>.
+ *
+ * @param index The index of the y-Axis.
+ *
+ * @return a Range[] as follows: [x-Range, y-Range].
+ */
+ @Override
+ public Range[] getRangesForAxis(int index) {
+ logger.debug("getRangesForAxis " + index);
+
+ Bounds rx = getXBounds(Integer.valueOf(0));
+ Bounds ry = getYBounds(Integer.valueOf(index));
+
+ if (rx == null) {
+ logger.warn("Range for x axis not set." +
+ " Using default values: 0 - 1.");
+ rx = new DoubleBounds(0, 1);
+ }
+ if (ry == null) {
+ logger.warn("Range for y" + index +
+ " axis not set. Using default values: 0 - 1.");
+ ry = new DoubleBounds(0, 1);
+ }
+
+ return new Range[] {
+ new Range(rx.getLower().doubleValue(), rx.getUpper().doubleValue()),
+ new Range(ry.getLower().doubleValue(), ry.getUpper().doubleValue())
+ };
+ }
+
+
+ /** Get X (usually horizontal) extent for given axis. */
+ @Override
+ public Bounds getXBounds(int axis) {
+ return xBounds.get(axis);
+ }
+
+
+ /** Set X (usually horizontal) extent for given axis. */
+ @Override
+ protected void setXBounds(int axis, Bounds bounds) {
+ if (bounds.getLower() == bounds.getUpper()) {
+ xBounds.put(axis, ChartHelper.expandBounds(bounds, 5d));
+ }
+ else {
+ xBounds.put(axis, bounds);
+ }
+ }
+
+
+ /** Get Y (usually vertical) extent for given axis. */
+ @Override
+ public Bounds getYBounds(int axis) {
+ return yBounds.get(axis);
+ }
+
+
+ /** Set Y (usually vertical) extent for given axis. */
+ @Override
+ protected void setYBounds(int axis, Bounds bounds) {
+ yBounds.put(axis, bounds);
+ }
+
+
+ /**
+ * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
+ * X axis.
+ *
+ * (Duplicate in TimeseriesChartGenerator)
+ *
+ * @param plot The XYPlot of the chart.
+ */
+ protected void adjustAxes(XYPlot plot) {
+ ValueAxis xaxis = plot.getDomainAxis();
+
+ ChartSettings chartSettings = getChartSettings();
+ if (chartSettings == null) {
+ return;
+ }
+
+ Font labelFont = new Font(
+ DEFAULT_FONT_NAME,
+ Font.BOLD,
+ getXAxisLabelFontSize());
+
+ xaxis.setLabelFont(labelFont);
+ xaxis.setTickLabelFont(labelFont);
+ }
+
+
+ /**
+ * This method walks over all axes (domain and range) of <i>plot</i> and
+ * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for
+ * range axes.
+ *
+ * @param plot The XYPlot.
+ */
+ private void localizeAxes(XYPlot plot) {
+ for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+ ValueAxis axis = plot.getDomainAxis(i);
+
+ if (axis != null) {
+ localizeDomainAxis(axis);
+ }
+ else {
+ logger.warn("Domain axis at " + i + " is null.");
+ }
+ }
+
+ for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+ ValueAxis axis = plot.getRangeAxis(i);
+
+ if (axis != null) {
+ localizeRangeAxis(axis);
+ }
+ else {
+ logger.warn("Range axis at " + i + " is null.");
+ }
+ }
+ }
+
+
+ /**
+ * Overrides the NumberFormat with the NumberFormat for the current locale
+ * that is provided by getLocale().
+ *
+ * @param domainAxis The domain axis that needs localization.
+ */
+ protected void localizeDomainAxis(ValueAxis domainAxis) {
+ NumberFormat nf = NumberFormat.getInstance(getLocale());
+ ((NumberAxis) domainAxis).setNumberFormatOverride(nf);
+ }
+
+
+ /**
+ * Overrides the NumberFormat with the NumberFormat for the current locale
+ * that is provided by getLocale().
+ *
+ * @param rangeAxis The domain axis that needs localization.
+ */
+ protected void localizeRangeAxis(ValueAxis rangeAxis) {
+ NumberFormat nf = NumberFormat.getInstance(getLocale());
+ ((NumberAxis) rangeAxis).setNumberFormatOverride(nf);
+ }
+
+
+ /**
+ * Do Points out.
+ */
+ protected void doPoints(
+ Object o,
+ ArtifactAndFacet aandf,
+ ThemeDocument theme,
+ boolean visible,
+ int axisIndex
+ ) {
+ String seriesName = aandf.getFacetDescription();
+ XYSeries series = new StyledXYSeries(seriesName, theme);
+
+ // Add text annotations for single points.
+ List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+ try {
+ JSONArray points = new JSONArray((String) o);
+ for (int i = 0, P = points.length(); i < P; i++) {
+ JSONArray array = points.getJSONArray(i);
+ double x = array.getDouble(0);
+ double y = array.getDouble(1);
+ String name = array.getString(2);
+ boolean act = array.getBoolean(3);
+ if (!act) {
+ continue;
+ }
+ //logger.debug(" x " + x + " y " + y );
+ series.add(x, y, false);
+ xy.add(new CollisionFreeXYTextAnnotation(name, x, y));
+ }
+ }
+ catch(JSONException e){
+ logger.error("Could not decode json.");
+ }
+
+ RiverAnnotation annotation = new RiverAnnotation(null, null, null, theme);
+ annotation.setTextAnnotations(xy);
+
+ // Do not generate second legend entry. (null was passed for the aand before).
+ if (visible) {
+ annotations.add(annotation);
+ }
+// doAnnotations(annotations, null, theme, visible);
+ addAxisSeries(series, axisIndex, visible);
+ }
+
+
+ /**
+ * Create a hash from a legenditem.
+ * This hash can then be used to merge legend items labels.
+ * @return hash for given legenditem to identify mergeables.
+ */
+ public static String legendItemHash(LegendItem li) {
+ // TODO Do proper implementation. Ensure that only mergable sets are created.
+ // getFillPaint()
+ // getFillPaintTransformer()
+ // getLabel()
+ // getLine()
+ // getLinePaint()
+ // getLineStroke()
+ // getOutlinePaint()
+ // getOutlineStroke()
+ // Shape getShape()
+ // String getToolTipText()
+ // String getURLText()
+ // boolean isLineVisible()
+ // boolean isShapeFilled()
+ // boolean isShapeOutlineVisible()
+ // boolean isShapeVisible()
+ String hash = li.getLinePaint().toString();
+ String label = li.getLabel();
+ if (label.startsWith("W (") || label.startsWith("W(")) {
+ hash += "-W-";
+ }
+ else if (label.startsWith("Q(") || label.startsWith("Q (")) {
+ hash += "-Q-";
+ }
+
+ // WQ.java holds example of using regex Matcher/Pattern.
+
+ return hash;
+ }
+
+ /** True if x axis has been inverted. */
+ public boolean isInverted() {
+ return inverted;
+ }
+
+
+ /** Set to true if x axis has been inverted. */
+ public void setInverted(boolean inverted) {
+ this.inverted = inverted;
+ }
+
+ /** Add the acutal data to the diagram according to the processors.
+ * For every outable facets, this function is
+ * called and handles the data accordingly. */
+ @Override
+ public void doOut(ArtifactAndFacet bundle, ThemeDocument theme,
+ boolean visible)
+ {
+ String facetName = bundle.getFacetName();
+ Facet facet = bundle.getFacet();
+
+ /* A conservative security check */
+ if (facetName == null || facet == null) {
+ /* Can't happen,.. */
+ logger.error("doOut called with null facet.");
+ }
+
+ logger.debug("DoOut for facet: " + facetName);
+
+ /* TODO Here should the configured processors come into play */
+ List<Processor> processors = new ArrayList<Processor>();
+ processors.add(new WOutProcessor());
+ processors.add(new QOutProcessor());
+ processors.add(new BedheightProcessor());
+ processors.add(new BedDiffYearProcessor());
+ processors.add(new BedDiffHeightYearProcessor());
+
+ for (Processor pr: processors) {
+ if (pr.canHandle(facetName)) {
+ // pr.doOut(this, bundle, theme, visible, 0);
+ }
+ }
+ }
+
+
+}
More information about the Dive4elements-commits
mailing list