Logarithmic axes are being used for both the domain and
range axes of an XYPlot in a ChartPanel. Zooming in in
any form causes the plot to zoom to an unidentifiable
location.
Corey Himes
2004-01-20
Corey Himes
2004-01-20
Logged In: YES
user_id=951857
I have traced the problem to LogarithmicAxis. zoomRange
needs to be overridden in LogarithmicAxis. I have attached a
corrected version of zoomRange.
David Gilbert
2004-01-28
David Gilbert
2004-01-28
Logged In: YES
user_id=112975
Thanks for the report. I will look at your solution...
Regards,
Dave Gilbert
JFreeChart Project Leader
deverhel
2004-05-26
Logged In: YES
user_id=1050162
I have try the patch ,
it work well for positive values,
but if the axis allow negative values the problems
are still present.
I suggest to insert a switchingPow10 method that invert the
switchingLog10, and use it instead the Math.pow in the
method zoomRange:
(and also in the java2DToValue method):
Below I wrote the methods proposed to be inserted/overwritten
in the LogarithmicAxis
------------------------------------------------------------
protected double switchedPow10(double val) {
return (!this.allowNegativesFlag && val < 1.0 && val
> 0.0) ?
Math.pow(10.0,val) :
adjustedPow10(val);
}
public double adjustedPow10(double val) {
boolean negFlag = (val < 0.0);
if (negFlag) {
val = -val ; // if negative then set
flag and make positive
}
double res=Math.pow(10,val);
if (val < 1.0) {
res=(Math.pow(10,val+1.0)-10.0)/9.0; //invert adjustLog10
}
return negFlag ? -res : res;
}
public double java2DToValue(double java2DValue, Rectangle2D
plotArea, RectangleEdge edge) {
Range range = getRange();
double axisMin = switchedLog10(range.getLowerBound());
double axisMax = switchedLog10(range.getUpperBound());
double plotMin = 0.0;
double plotMax = 0.0;
if (RectangleEdge.isTopOrBottom(edge)) {
plotMin = plotArea.getMinX();
plotMax = plotArea.getMaxX();
}
else if (RectangleEdge.isLeftOrRight(edge)) {
plotMin = plotArea.getMaxY();
plotMax = plotArea.getMinY();
}
if (isInverted()) {
return switchedPow10(axisMax - ((java2DValue -
plotMin) /
(plotMax - plotMin)) * (axisMax - axisMin));
} else {
return switchedPow10( axisMin + ((java2DValue -
plotMin) /
(plotMax -
plotMin)) * (axisMax - axisMin));
}
}
public void zoomRange(double lowerPercent, double
upperPercent)
{
double startLog = switchedLog10(getRange().getLowerBound());
double lengthLog = switchedLog10(getRange().getUpperBound())
- startLog;
Range adjusted = null;
if(isInverted()){
adjusted = new Range( switchedPow10( startLog +
lengthLog * (1 -
lowerPercent)),switchedPow10(startLog
+ lengthLog * (1 +
upperPercent)));
} else{
adjusted = new Range(switchedPow10( startLog
+ lengthLog * lowerPercent),
switchedPow10( startLog + lengthLog * upperPercent));
}
setRange(adjusted);
}
Vincent
2005-03-03
Logged In: YES
user_id=1231559
Hi!
The 2nd patch doesn't work if the axis is inverted.
cheers
Vincent
Nobody/Anonymous
2005-07-21
Logged In: NO
Why is this bug still open? It was reported over a year ago
and in my opinion it is a serious problem that you cannot do
mouse zooming in an XYPlot with a logarithmic range axis. It
seems quite easy to fix, so why not give it a little attention.
Thanks.
Roger Shaw
2005-09-14
Logged In: YES
user_id=712400
Problem still exists in RC1 - Please fix!
berth
2006-03-15
Logged In: YES
user_id=1031412
Here is a fix, unit tested for JFreechart 1.0.1. I have made
a subclass of LogarithmicAxis that does zoom in correctly. I
have used the patches suggested here, the main problem was
that switchedLog10 was not really an inverse of
switchedLog10, you have to use this.smallLogFlag for
switching, just like switchedLog10 does.
Unit test follows in next comment.
Please note that you'll have to put this class into another
package.
---------------
package com.decodon.visualization.jrefinery.chart;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.data.Range;
import org.jfree.ui.RectangleEdge;
import java.awt.geom.Rectangle2D;
/**
* This is a patched version of
org.jfree.chart.axis.LogarithmicAxis, according to
*
http://sourceforge.net/tracker/index.php?func=detail&aid=880597&group_id=15494&atid=115494
* It fixes a problem with zooming when logarithmic axes are
used. Tested with JFreechart 1.0.1.
*
* @author berth (at) decodon dot com
*/
public class LogarithmicAxisZoomPatched extends
LogarithmicAxis {
/**
* Creates a new instance of LogarithmicAxisZoomPatched
* @param label the axis label.
*/
public LogarithmicAxisZoomPatched(String label) {
super(label);
}
public double adjustedPow10\(double val\) \{ boolean negFlag = \(val < 0.0\); if \(negFlag\) \{ val = -val; // if negative then set flag and make positive \} double res = Math.pow\(10, val\); if \(val < 1.0\) \{ res = \(Math.pow\(10, val + 1.0\) - 10.0\) / 9.0; //invert
adjustLog10
}
return negFlag ? \(-res\) : res; \} /\*\* \* Converts a coordinate in Java2D space to the
corresponding data
* value, assuming that the axis runs along one edge of the
specified
* plotArea.
*
* @param java2DValue the coordinate in Java2D space.
* @param plotArea the area in which the data is plotted.
* @param edge the axis location.
*
* @return The data value.
*/
public double java2DToValue(double java2DValue, Rectangle2D
plotArea,
RectangleEdge edge) {
Range range = getRange();
double axisMin = switchedLog10(range.getLowerBound());
double axisMax = switchedLog10(range.getUpperBound());
double plotMin = 0.0; double plotMax = 0.0; if \(RectangleEdge.isTopOrBottom\(edge\)\) \{ plotMin = plotArea.getMinX\(\); plotMax = plotArea.getMaxX\(\); \} else if \(RectangleEdge.isLeftOrRight\(edge\)\) \{ plotMin = plotArea.getMaxY\(\); plotMax = plotArea.getMinY\(\); \} if \(isInverted\(\)\) \{ return switchedPow10\(axisMax - \(\(\(java2DValue - plotMin\) / \(plotMax - plotMin\)\) \*
(axisMax -
axisMin)));
} else {
return switchedPow10(axisMin +
(((java2DValue - plotMin) / (plotMax - plotMin)) *
(axisMax -
axisMin)));
}
}
/\*\* \* Returns the log10 value, depending on if values between
0 and
* 1 are being plotted. If negative values are not allowed and
* the lower bound is between 0 and 10 then a normal log is
* returned; otherwise the returned value is adjusted if the
* given value is less than 10.
*
* Note: I've made this a public method to be able to
access it in a unit test
*
* @param val the value.
* @return log<sub>10</sub>(val).
*/
public double switchedLog10(double val) {
double retValue;
retValue = super.switchedLog10\(val\); return retValue; \} /\*\* Inverse function of switchedLog10 \*/ public double switchedPow10\(double val\) \{ return this.smallLogFlag ? Math.pow\(10.0, val\) :
adjustedPow10(val);
}
/\*\* \* Zooms in on the current range. \* \* @param lowerPercent the new lower bound. \* @param upperPercent the new upper bound. \*/ public void zoomRange\(double lowerPercent, double
upperPercent) {
double startLog = switchedLog10(getRange().getLowerBound());
double lengthLog = switchedLog10(getRange().getUpperBound()) -
startLog;
Range adjusted = null;
if \(isInverted\(\)\) \{ adjusted = new Range\(switchedPow10\(startLog + \(lengthLog \* \(1 - lowerPercent\)\)\), switchedPow10\(startLog + \(lengthLog \* \(1 +
upperPercent))));
} else {
adjusted = new Range(switchedPow10(startLog +
(lengthLog * lowerPercent)),
switchedPow10(startLog + (lengthLog * upperPercent)));
}
setRange\(adjusted\); \}
}
berth
2006-03-15
Logged In: YES
user_id=1031412
Here is the unit test for the fix below.
Please note that you'll have to put this class into another
package.
---------------
/*
* LogarithmicAxisZoomPatchedTest.java
* JUnit based test
*
* Created on 14. März 2006, 15:58
*/
package com.decodon.visualization.jrefinery.chart;
import junit.framework.*;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.data.Range;
import org.jfree.ui.RectangleEdge;
import java.awt.geom.Rectangle2D;
/** Unit tests for LogarithmicAxisZoomPatched. This test
covers the zoom in functionality.
* TODO: tests with Margins > 0, tests with negative axis
values (I guess that's possible in JFreeChart)
* @author berth (at) decodon dot com
*/
public class LogarithmicAxisZoomPatchedTest extends TestCase {
/** Tolerance for floating point comparisons */
public static double EPSILON = 0.000001;
LogarithmicAxisZoomPatched axis = null;
public LogarithmicAxisZoomPatchedTest\(String testName\) \{ super\(testName\); \} protected void setUp\(\) throws Exception \{ axis = new LogarithmicAxisZoomPatched\("Value \(log\)"\); axis.setAllowNegativesFlag\(false\); axis.setLog10TickLabelsFlag\(false\); axis.setLowerMargin\(0.0\); axis.setUpperMargin\(0.0\); axis.setLowerBound\(0.2\); axis.setUpperBound\(100.0\); \} public static Test suite\(\) \{ TestSuite suite = new
TestSuite(LogarithmicAxisZoomPatchedTest.class);
return suite; \} /\*\* Test if adjustedLog10 and adjustedPow10 are inverses of
each other */
public void testAdjustedLog10() {
checkLogPowRoundTrip(20);
checkLogPowRoundTrip(10);
checkLogPowRoundTrip(5);
checkLogPowRoundTrip(2);
checkLogPowRoundTrip(1);
checkLogPowRoundTrip(0.5);
checkLogPowRoundTrip(0.2);
checkLogPowRoundTrip(0.0001);
}
private void checkLogPowRoundTrip\(double value\) \{ assertEquals\("log\(pow\(x\)\) = x", value, axis.adjustedLog10\(axis.adjustedPow10\(value\)\), EPSILON\); assertEquals\("pow\(log\(x\)\) = x", value, axis.adjustedPow10\(axis.adjustedLog10\(value\)\), EPSILON\); \} /\*\* Test if switchedLog10 and switchedPow10 are inverses of
each other */
public void testSwitchedLog10() {
assertFalse("Axis should not allow negative values",
axis.getAllowNegativesFlag());
assertEquals\(Math.log\(0.5\) / LogarithmicAxis.LOG10\_VALUE,
// log10(0.5) = -0.30102999566398114
axis.switchedLog10(0.5), EPSILON);
checkSwitchedLogPowRoundTrip\(20\); checkSwitchedLogPowRoundTrip\(10\); checkSwitchedLogPowRoundTrip\(5\); checkSwitchedLogPowRoundTrip\(2\); checkSwitchedLogPowRoundTrip\(1\); checkSwitchedLogPowRoundTrip\(0.5\); checkSwitchedLogPowRoundTrip\(0.2\); checkSwitchedLogPowRoundTrip\(0.0001\); \} private void checkSwitchedLogPowRoundTrip\(double value\) \{ assertEquals\("log\(pow\(x\)\) = x", value, axis.switchedLog10\(axis.switchedPow10\(value\)\), EPSILON\); assertEquals\("pow\(log\(x\)\) = x", value, axis.switchedPow10\(axis.switchedLog10\(value\)\), EPSILON\); \} /\*\* \* Test of java2DToValue method, of class
com.decodon.visualization.jrefinery.chart.LogarithmicAxisZoomPatched.
*/
public void testJava2DToValue() {
Rectangle2D plotArea = new Rectangle2D.Double(22, 33, 500,
500);
RectangleEdge edge = RectangleEdge.BOTTOM;
// set axis bounds to be both greater than 1 axis.setRange\(10, 20\); checkPointsToValue\(edge, plotArea\); // check for bounds interval that includes 1 axis.setRange\(0.5, 10\); checkPointsToValue\(edge, plotArea\); // check for bounds interval that includes 1 axis.setRange\(0.2, 20\); checkPointsToValue\(edge, plotArea\); // check for both bounds smaller than 1 axis.setRange\(0.2, 0.7\); checkPointsToValue\(edge, plotArea\); \} /\*\* \* Test of valueToJava2D method, of class
com.decodon.visualization.jrefinery.chart.LogarithmicAxisZoomPatched.
*/
public void testValueToJava2D() {
Rectangle2D plotArea = new Rectangle2D.Double(22, 33, 500,
500);
RectangleEdge edge = RectangleEdge.BOTTOM;
// set axis bounds to be both greater than 1 axis.setRange\(10, 20\); checkPointsToJava2D\(edge, plotArea\); // check for bounds interval that includes 1 axis.setRange\(0.5, 10\); checkPointsToJava2D\(edge, plotArea\); // check for bounds interval that includes 1 axis.setRange\(0.2, 20\); checkPointsToJava2D\(edge, plotArea\); // check for both bounds smaller than 1 axis.setRange\(0.2, 0.7\); checkPointsToJava2D\(edge, plotArea\); \} private void checkPointsToJava2D\(final RectangleEdge edge, final Rectangle2D plotArea\) \{ assertEquals\("Left most point on the axis should be
beginning of range.",
plotArea.getX(),
axis.valueToJava2D(axis.getLowerBound(), plotArea, edge),
EPSILON);
assertEquals("Right most point on the axis should be end
of range.",
plotArea.getX() + plotArea.getWidth(),
axis.valueToJava2D(axis.getUpperBound(), plotArea, edge),
EPSILON);
assertEquals("Center point on the axis should geometric
mean of the bounds.",
plotArea.getX() + (plotArea.getWidth() / 2),
axis.valueToJava2D(Math.sqrt(
axis.getLowerBound() * axis.getUpperBound()), plotArea,
edge),
EPSILON);
}
/\*\* Check the translation java2D to value for left, right,
and center point of the */
private void checkPointsToValue(RectangleEdge edge,
Rectangle2D plotArea) {
assertEquals("Right most point on the axis should be end
of range.",
axis.getUpperBound(),
axis.java2DToValue(plotArea.getX() + plotArea.getWidth(),
plotArea,
edge), EPSILON);
assertEquals\("Left most point on the axis should be
beginning of range.",
axis.getLowerBound(),
axis.java2DToValue(plotArea.getX(), plotArea, edge),
EPSILON);
assertEquals\("Center point on the axis should geometric
mean of the bounds.",
Math.sqrt(axis.getUpperBound() * axis.getLowerBound()),
axis.java2DToValue(plotArea.getX() + (plotArea.getWidth()
/ 2),
plotArea, edge), EPSILON);
}
public static void main\(String\[\] args\) \{
junit.textui.TestRunner.run(LogarithmicAxisZoomPatchedTest.class);
}
}
Kurt
2006-03-28
Logged In: YES
user_id=1448929
Did over-riding zoomRange work for anyone else? It didn't
work for me.
Does LogarithmicAxisZoomPatched work? Has anyone tried
it?
Kurt
berth
2006-03-31
Logged In: YES
user_id=1031412
Over-riding zoomRange did not work for me, so I fixed it in
LogarithmicAxisZoomPatched. LogarithmicAxisZoomPatched works
for me, obviously :-) The problem is that jfreechart
underwent some API (and implementation) changes in the
pre-1.0 versions. So maybe the zoomRange fix worked in
jfreechart 0.9.x.
Sergei Ivanov
2007-02-28
Logged In: YES
user_id=1606022
Originator: NO
I have just tested berth's modifications against 1.0.4 and they are working just fine. There was a problem with inverted axes, which is fixed in the new version of zoomRange(), submitted as patch #1671069.
@David: could you please integrate the complete patch into the latest codebase?
https://sourceforge.net/tracker/index.php?func=detail&aid=1671069&group_id=15494&atid=315494
David Gilbert
2007-03-02
David Gilbert
2007-03-02
Logged In: YES
user_id=112975
Originator: NO
Hi Sergei,
Thanks for your work on this. I committed your patch from #1671069, and added the unit test. This will be included in the 1.0.5 release.
Regards,
Dave Gilbert
JFreeChart Project Leader