# HG changeset patch # User Ingo Weinzierl # Date 1328870732 0 # Node ID f55984858952b92b6ac429d5b7accb01f7631555 # Parent 73b15736cb8023f512b26a684f5fbc3119a2649f Enabled zooming for timeseries charts. flys-artifacts/trunk@4026 c6561f87-3c4e-4783-a992-168aeb5c3f6f diff -r 73b15736cb80 -r f55984858952 flys-artifacts/ChangeLog --- a/flys-artifacts/ChangeLog Fri Feb 10 10:43:29 2012 +0000 +++ b/flys-artifacts/ChangeLog Fri Feb 10 10:45:32 2012 +0000 @@ -1,3 +1,11 @@ +2012-02-10 Ingo Weinzierl + + * src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: Write + correct min and max values for date axes into the info document. + + * src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java: + Enabled zooming for timeseries charts. + 2012-02-10 Ingo Weinzierl * src/main/java/de/intevation/flys/jfree/Bounds.java, diff -r 73b15736cb80 -r f55984858952 flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java --- a/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java Fri Feb 10 10:43:29 2012 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java Fri Feb 10 10:45:32 2012 +0000 @@ -24,6 +24,8 @@ import de.intevation.artifacts.common.utils.XMLUtils; import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator; +import de.intevation.flys.jfree.Bounds; + /** * This class helps generating chart info documents. @@ -204,13 +206,20 @@ Date from = axis.getMinimumDate(); Date to = axis.getMaximumDate(); + Bounds bounds = null; + if (type.equals("range")) { + bounds = generator.getYBounds(pos); + } + else { + bounds = generator.getXBounds(pos); + } + cr.addAttr(e, "axistype", "date", true); cr.addAttr(e, "from", String.valueOf(from.getTime()), true); cr.addAttr(e, "to", String.valueOf(to.getTime()), true); - // TODO Get correct min/max - cr.addAttr(e, "min", String.valueOf(from.getTime()), true); - cr.addAttr(e, "max", String.valueOf(to.getTime()), true); + cr.addAttr(e, "min", bounds.getLower().toString(), true); + cr.addAttr(e, "max", bounds.getUpper().toString(), true); return e; } diff -r 73b15736cb80 -r f55984858952 flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java --- a/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java Fri Feb 10 10:43:29 2012 +0000 +++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/TimeseriesChartGenerator.java Fri Feb 10 10:45:32 2012 +0000 @@ -11,6 +11,7 @@ import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.data.Range; @@ -18,6 +19,10 @@ import org.jfree.data.general.Series; import org.jfree.data.xy.XYDataset; +import de.intevation.flys.jfree.Bounds; +import de.intevation.flys.jfree.DoubleBounds; +import de.intevation.flys.jfree.TimeBounds; + /** * @author Ingo Weinzierl @@ -106,8 +111,9 @@ protected void mergeRanges(TimeSeriesCollection dataset) { logger.debug("Range after merging: " + range); - Range[] xyRanges = ChartHelper.getRanges(dataset); - range = Range.combine(range, xyRanges[1]); + Bounds[] xyRanges = ChartHelper.getBounds(dataset); + + // TODO COMBINE BOUNDS! logger.debug("Range after merging: " + range); } @@ -120,9 +126,12 @@ Logger.getLogger(TimeseriesChartGenerator.class); - protected Map xRanges; + public static final int AXIS_SPACE = 5; - protected Map yRanges; + + protected Map xRanges; + + protected Map yRanges; @@ -132,8 +141,8 @@ public TimeseriesChartGenerator() { super(); - xRanges = new HashMap(); - yRanges = new HashMap(); + xRanges = new HashMap(); + yRanges = new HashMap(); } @@ -161,6 +170,8 @@ addSubtitles(chart); addDatasets(plot); + adaptZoom(plot); + return chart; } @@ -171,15 +182,31 @@ } - @Override - protected void setXRange(int axis, Range range) { - xRanges.put(Integer.valueOf(axis), range); + // TODO DECLARE IN UPPER CLASS AND ADD OVERRIDE ANNOTATION + protected Bounds getXRange(int axis) { + return xRanges.get(Integer.valueOf(axis)); } @Override + // TODO setXRange should always await a Bounds instance! + // TODO SHOULD BE REMOVED WHEN DEFINED IN UPPER CLASS + protected void setXRange(int axis, Range range) { + // do nothing here, we will use setXRange(int, Bounds) now + } + + + @Override + // TODO setYRange should always await a Bounds instance! protected void setYRange(int axis, Range range) { - yRanges.put(Integer.valueOf(axis), range); + if (range == null) { + logger.warn("Range is null!"); + return; + } + + setYBounds(Integer.valueOf(axis), new DoubleBounds( + range.getLowerBound(), + range.getUpperBound())); } @@ -195,6 +222,37 @@ } + // TODO THIS SHOULD BE DONE IN AN UPPER CLASS! + @Override + public void addAxisDataset(XYDataset dataset, int idx, boolean visible) { + if (dataset == null || idx < 0) { + return; + } + + AxisDataset axisDataset = getAxisDataset(idx); + + Bounds[] bounds = ChartHelper.getBounds((TimeSeriesCollection)dataset); + + if (bounds == null) { + logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx); + return; + } + + if (visible) { + if (logger.isDebugEnabled()) { + logger.debug("Add new AxisDataset at index: " + idx); + logger.debug("X extent: " + bounds[0]); + logger.debug("Y extent: " + bounds[1]); + } + + axisDataset.addDataset(dataset); + } + + combineXRanges(bounds[0], 0); + combineYRanges(bounds[1], idx); + } + + /** * Effect: extend range of x axis to include given limits. * @param range the given ("minimal") range. @@ -202,40 +260,237 @@ */ @Override protected void combineXRanges(Range range, int index) { - if (range != null) { - Range old = xRanges.get(index); + throw new RuntimeException( + "TimeseriesChartGenerator.combineXRanges is not implemented!"); + } + + + protected void combineXRanges(Bounds bounds, int index) { + if (bounds != null) { + Bounds old = getXRange(index); if (old != null) { - range = Range.combine(old, range); + bounds = bounds.combine(old); } - xRanges.put(index, range); + setXBounds(index, bounds); } } + protected void combineYRanges(Bounds bounds, int index) { + if (bounds != null) { + Bounds old = getYBounds(index); + + if (old != null) { + bounds = bounds.combine(old); + } + + setYBounds(index, bounds); + } + } + + + // TODO REPLACE THIS METHOD WITH getBoundsForAxis(index) + @Override + public Range[] getRangesForAxis(int index) { + // TODO + Bounds[] bounds = getBoundsForAxis(index); + + return new Range[] { + new Range( + bounds[0].getLower().doubleValue(), + bounds[0].getUpper().doubleValue()), + new Range( + bounds[1].getLower().doubleValue(), + bounds[1].getUpper().doubleValue()) + }; + } + + @Override - public Range[] getRangesForAxis(int index) { - logger.debug("Return ranges for axis at: " + index); + public Bounds getXBounds(int axis) { + return xRanges.get(axis); + } - Range rx = xRanges.get(Integer.valueOf(0)); - Range ry = yRanges.get(Integer.valueOf(index)); + + @Override + protected void setXBounds(int axis, Bounds bounds) { + xRanges.put(axis, bounds); + } + + + @Override + public Bounds getYBounds(int axis) { + return yRanges.get(axis); + } + + + @Override + protected void setYBounds(int axis, Bounds bounds) { + yRanges.put(axis, bounds); + } + + + public Bounds[] getBoundsForAxis(int index) { + logger.debug("Return x and y bounds for axis at: " + index); + + Bounds rx = getXBounds(Integer.valueOf(index)); + Bounds ry = getYBounds(Integer.valueOf(index)); if (rx == null) { logger.warn("Range for x axis not set." + " Using default values: 0 - 1."); - rx = new Range(0, 1); - } - if (ry == null) { - logger.warn("Range for y" + index + - " axis not set. Using default values: 0 - 1."); - ry = new Range(0, 1); + rx = new TimeBounds(0l, 1l); } - logger.debug("X Range is: " + rx); - logger.debug("Y Range is: " + ry); + if (ry == null) { + logger.warn("Range for y axis not set." + + " Using default values: 0 - 1."); + ry = new DoubleBounds(0l, 1l); + } - return new Range[] {rx, ry}; + logger.debug("X Bounds at index " + index + " is: " + rx); + logger.debug("Y Bounds at index " + index + " is: " + ry); + + return new Bounds[] {rx, ry}; + } + + + public Bounds getDomainAxisRange() { + String[] ranges = getDomainAxisRangeFromRequest(); + + if (ranges == null || ranges.length < 2) { + logger.debug("No zoom range for domain axis specified."); + return null; + } + + if (ranges[0] == null || ranges[1] == null) { + logger.warn("Invalid ranges for domain axis specified!"); + return null; + } + + try { + double lower = Double.parseDouble(ranges[0]); + double upper = Double.parseDouble(ranges[1]); + + return new DoubleBounds(lower, upper); + } + catch (NumberFormatException nfe) { + logger.warn("Invalid ranges for domain axis specified: " + nfe); + } + + return null; + } + + + public Bounds getValueAxisRange() { + String[] ranges = getValueAxisRangeFromRequest(); + + if (ranges == null || ranges.length < 2) { + logger.debug("No zoom range for domain axis specified."); + return null; + } + + if (ranges[0] == null || ranges[1] == null) { + logger.warn("Invalid ranges for domain axis specified!"); + return null; + } + + try { + double lower = Double.parseDouble(ranges[0]); + double upper = Double.parseDouble(ranges[1]); + + return new DoubleBounds(lower, upper); + } + catch (NumberFormatException nfe) { + logger.warn("Invalid ranges for domain axis specified: " + nfe); + } + + return null; + } + + + protected void adaptZoom(XYPlot plot) { + logger.debug("Adapt zoom of Timeseries chart."); + + zoomX(plot, plot.getDomainAxis(), getXRange(0), getDomainAxisRange()); + + Bounds valueAxisBounds = getValueAxisRange(); + + for (int j = 0, n = plot.getRangeAxisCount(); j < n; j++) { + zoomY( + plot, + plot.getRangeAxis(j), + getYBounds(j), + valueAxisBounds); + } + } + + + protected void zoomX( + XYPlot plot, + ValueAxis axis, + Bounds total, + Bounds user + ) { + if (logger.isDebugEnabled()) { + logger.debug("== Zoom X axis =="); + logger.debug(" Total axis range : " + total); + logger.debug(" User defined range: " + user); + } + + if (user != null) { + long min = total.getLower().longValue(); + long max = total.getUpper().longValue(); + long diff = max > min ? max - min : min - max; + + long newMin = (long) Math.round(min + user.getLower().doubleValue() * diff); + long newMax = (long) Math.round(min + user.getUpper().doubleValue() * diff); + + TimeBounds newBounds = new TimeBounds(newMin, newMax); + + logger.debug(" Zoom axis to: " + newBounds); + + newBounds.applyBounds(axis, AXIS_SPACE); + } + else { + logger.debug("No user specified zoom values found!"); + total.applyBounds(axis, AXIS_SPACE); + } + } + + + protected void zoomY( + XYPlot plot, + ValueAxis axis, + Bounds total, + Bounds user + ) { + if (logger.isDebugEnabled()) { + logger.debug("== Zoom Y axis =="); + logger.debug(" Total axis range : " + total); + logger.debug(" User defined range: " + user); + } + + if (user != null) { + double min = total.getLower().doubleValue(); + double max = total.getUpper().doubleValue(); + double diff = max > min ? max - min : min - max; + + double newMin = min + user.getLower().doubleValue() * diff; + double newMax = min + user.getUpper().doubleValue() * diff; + + DoubleBounds newBounds = new DoubleBounds(newMin, newMax); + + logger.debug(" Zoom axis to: " + newBounds); + + newBounds.applyBounds(axis, AXIS_SPACE); + } + else { + logger.debug("No user specified zoom values found!"); + total.applyBounds(axis, AXIS_SPACE); + } } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :