DateAxis zooming and plotting issues

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Post by paradoxoff » Thu Apr 03, 2008 1:12 pm

Hi,
I have run your code. Maybe I have misunderstood your "bug 2" because zooming works for me as expected. If you select with the zoom rectangle a range with no data points then you won´t see any data points after zooming. The fact that the tick marks for the y-axis are changing upon zooming is that they are calculated automatically unless you set them to a defined value as you have done for the x-axis.

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Fri Apr 04, 2008 12:01 am

RichardWest....

Thanks for the response.

The bug that remains a problem for me (bug 2) is not really a "show stopper" (since the user can easily recover from it) but it does provide a result that will certainly irritate the user every time they encounter it. This isn't a subtle problem, but rather one that unexpectedly makes all the user's data disappear from the chart!

The other bug (bug 1) - even though the problem was resolved for my purposes - I still documented this in the code since this might affect others who do not want to, or can't, use the "fix" I used. Besides, the "fix" is not really a fix (calling "setXPosition( TimePeriodAnchor.START )" ) since the other anchor positions result in the bug occuring ... unless you chronologically sort all your data.




paradoxoff....

Thanks for looking at this.

The problem is not with zooming by outlining a rectangle with the mouse.

The problem occurs when you drag the mouse left-to-right along the x-axis (dragging over where the hours are listed along the axis).

It would be great if I could disable zooming via dragging-along-the-axis but still allow zooming via a rectangle, but this isn't implemented. Maybe if I have the time at some point, I will look into overriding applicable methods so I can selectively disable this myself. Unless, of course, there is something else I could do to fix this (i.e., is there a way to play with the Y-axis parameters so that auto-scaling is still in place, but where the indicated bug with zooming would be fixed?).

It seems to me that if you zoom by dragging along the x-axis, that the y-axis settings should remain unchanged. This would be equivalent to outlining a zoom rectangle that enclosed the entire visible y-axis along with your start and end points along the x-axis.

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Post by paradoxoff » Fri Apr 04, 2008 7:40 am

Code: Select all

ChartPanel.setRangeZoomable(false);
You have set this explicitly to true in your code. Even without that explicit setting, it will be true for an XYPlot.
Make sure to set this flag to false after you call ChartPanel.setMouseZoomable(true)!

edit: sorry I just read your post a liitle more careful. The solution above will not work when you want to decide "on the fly" along which axis the zoom should be applied. I just checked the source of ChartPanel.java, method mouseReleased(MouseEvent me). There are two pairs of variables that indicate whether zooming should be performed along the x and y direction. hZoom and vZoom just care about whether zooming is possible in principle by checking rangeZoomable/domainZoomabale, zoomTrigger1 and zoomTrigger2 also care about whether the mouse movement during dragging along the respective direction exceeds the threshold defined by zoomTriggerDistance. For the definition of the zooming rectangle, which in turn defines the range of the axis after zooming, only hZoom and vZoom are used. I inserted

Code: Select all

hZoom = hZoom && zoomTrigger1;
vZoom = vZoom && zoomTrigger2;
between

Code: Select all

boolean zoomTrigger1 = ....;
boolean zoomTrigger2 = ....;
and

Code: Select all

if (zoomTrigger1 || zoomTrigger2) {....
and now it is working as you probably need. You just have to make sure that a sufficiently large zoomTriggerDistance is defined.
[/i]
Regards, paradoxoff

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Fri Apr 04, 2008 8:02 pm

I appreciate your efforts at resolving this problem, but....

I am in a large organization that puts very stringent controls over the support libraries we use. It is a matter of configuration control. We cannot make any modifications to these libraries, unless we are able to override methods in our own code so the original libaries are not changed.

I looked at your suggestions and unfortunately (for me) it does not look like I can override the applicable method since private references are being made in the code.

Perhaps your suggestions, if verified and approved, could be incorporated into a future release of JFreeChart?

Meanwhile, if you have any additional suggestions ... things I could do without needing to regenerate a customized JFreeChart jar ... then this would be greatly appreciated.

Thanks for your efforts!

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Fri Apr 04, 2008 10:27 pm

I took a much closer look at the ChartPanel source code and your suggested fix.

It looks like it will certainly work in most cases, however, as you mentioned, "zoomTriggerDistance" (which has a public setter method, so I could easily set this number) must be large enough so the user doesn't inadvertently trigger a zoom along the other axis while drag-zooming along one axis. Unfortunately, this single variable is used for BOTH x and y axis zooming and applies whether the user drag-zooms along an axis or zooms by outlining a rectangle on the chart.

So, while your solution is certainly much better than the current situation, a safer approach would be to detect if the user has dragged the cursor along one of the axes, and then set the local boolean zoom flag (hZoom or vZoom) to false for the other axis. I realize this alternate solution would add a little more complexity to the mouse listener methods, but it would not be dependent upon "zoomTriggerDistance" being large enough, and you wouldn't have to worry about the possible negative repercussions that setting this variable larger might have (you may not be able to increase this number enough to guarantee axis-zooming will always work as desired and still have your application work as desired when trying to zoom with the zoom rectangle - though, I'll agree that for most cases this should work just fine).

paradoxoff
Posts: 1634
Joined: Sat Feb 17, 2007 1:51 pm

Post by paradoxoff » Sun Apr 06, 2008 10:03 am

If the original libraries have to stay as they are, use the brute force approach: Apply the changes to the ChartPanel source, save it under a new name and use this new class for your purpose. It is of course highly inefficient to copy 2.5 k lines just to change less than 10, but if nothing else works....
Concerning the use of zoomTriggerDistance to check whether the user wants to zoom or not: it will always be a tightrope walk to distinguish between "user has moved the mouse only a little along one direction to zoom in closely" and "user has moved the mouse only a little along a direction to not zoom at all". If the zoomTriggerDistance is too big, the attempted zooming in a small region (by creating a zoomRectangle where one of the dimensions is small) will not work and be interpreted as "just zoom along the other direction", if the zoomTriggerDistance is too small, then mouse movements will mostly be interpreted as zoom request, and zooming only along one direction will require very careful mouse movements.
If you want to check whether the mouse has been dragged along one axis, you will probably again have to distinguish between these two scenarios "close zooming" or "no zooming" and finally introduce another "zoomTriggerDistance" with all the required fine tuning. All in all, it is clearly not a simple task to guess what the user wants.

Regards, paradoxoff

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Apr 07, 2008 3:24 pm

Sorry for my inattention...I'm looking through this now.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Mon Apr 07, 2008 5:14 pm

jpmaia wrote:After further fiddling and testing, here is a summary of existing problems (running under version 1.0.9 of JFreeChart):

1. When auto range is turned on, either 'setXPosition(START)' must be called, or the time series must be sorted chronologically, in order for the time axis range to be correctly determined and displayed. If either is not done, the time axis range will go from "00:00" to the time in the last data point in the series. This also applies if there are multiple unsorted series - the time axis range will end with the latest time among the last-point times in all the series.
I've tracked this down to a bug in the TimePeriodValues.updateBounds() method. A fix is now in Subversion for inclusion in the 1.0.10 release.

Still reading and trying to understand the other issues...
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Mon Apr 07, 2008 11:07 pm

Dave ... thank you for looking into these problems.


paradoxoff....

The current implementation does not apparently distinguish between mouse actions *within* the chart area (where the points are plotted) and mouse actions along either axis.

My alternate suggestion includes making this distinction. Once you make this distinction, you no longer need to worry about "zoomTriggerDistance" nor introduce another variable like it.

It seems to me that zooming (with both axes turned on) should *always* work like this:

1. If you draw a zoom rectangle, the chart zooms to that area ... as it does now.

2. If you drag-zoom along either axis, then ONLY the axis dragged along should be zoomed. The other axis should keep it's current zoom level. This currently does not happen ... instead, both axes are zoomed, and the unfortunate result (in my case) is that the other axis is zoomed to a scale that results in NO data being visible in the chart.

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Mon Apr 21, 2008 6:22 pm

Bumping this post so it doesn't get lost.... David, have you had any further opportunity to look at the remaining zoom problem, and the possible solution that has been suggested?


Thanks.

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Mon May 05, 2008 10:32 pm

Bumping so this doesn't get lost.

I'm not bumping too often am I?

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Tue May 06, 2008 1:32 pm

No, thanks for the reminder.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Wed May 07, 2008 1:35 pm

Looking at the zooming issue, I think it is an accident (and bug) that the zoom can be triggered by dragging along the axis only...that wasn't the intention (even though I like the idea that dragging within the axis region only would zoom just that axis - I might try to implement that later).

Here is a fix that seems to work for me (I'm going to test it a little more before committing it to Subversion):

Code: Select all

Index: C:/workspace/jfreechart-1.0.x-svn/source/org/jfree/chart/ChartPanel.java
===================================================================
--- C:/workspace/jfreechart-1.0.x-svn/source/org/jfree/chart/ChartPanel.java	(revision 891)
+++ C:/workspace/jfreechart-1.0.x-svn/source/org/jfree/chart/ChartPanel.java	(working copy)
@@ -2,7 +2,7 @@
  * JFreeChart : a free chart library for the Java(tm) platform
  * ===========================================================
  *
- * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
+ * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
  *
  * Project Info:  http://www.jfree.org/jfreechart/index.html
  *
@@ -27,7 +27,7 @@
  * ---------------
  * ChartPanel.java
  * ---------------
- * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
+ * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
  *
  * Original Author:  David Gilbert (for Object Refinery Limited);
  * Contributor(s):   Andrzej Porebski;
@@ -136,6 +136,8 @@
  *               buffer (DG);
  * 25-Oct-2007 : Added default directory attribute (DG);
  * 07-Nov-2007 : Fixed (rare) bug in refreshing off-screen image (DG);
+ * 07-May-2008 : Fixed bug in zooming that triggered zoom for a rectangle
+ *               outside of the data area (DG);
  *
  */
 
@@ -339,7 +341,7 @@
      * click).  This is a point on the screen, not the chart (which may have
      * been scaled up or down to fit the panel).
      */
-    private Point zoomPoint = null;
+    private Point2D zoomPoint = null;
 
     /** The zoom rectangle (selected by the user with the mouse). */
     private transient Rectangle2D zoomRectangle = null;
@@ -1170,16 +1172,16 @@
      * Applies any scaling that is in effect for the chart drawing to the
      * given rectangle.
      *
-     * @param rect  the rectangle.
+     * @param rect  the rectangle (<code>null</code> not permitted).
      *
      * @return A new scaled rectangle.
      */
     public Rectangle2D scale(Rectangle2D rect) {
         Insets insets = getInsets();
         double x = rect.getX() * getScaleX() + insets.left;
-        double y = rect.getY() * this.getScaleY() + insets.top;
-        double w = rect.getWidth() * this.getScaleX();
-        double h = rect.getHeight() * this.getScaleY();
+        double y = rect.getY() * getScaleY() + insets.top;
+        double w = rect.getWidth() * getScaleX();
+        double h = rect.getHeight() * getScaleY();
         return new Rectangle2D.Double(x, y, w, h);
     }
 
@@ -1532,12 +1534,10 @@
      *
      * @return A point within the rectangle.
      */
-    private Point getPointInRectangle(int x, int y, Rectangle2D area) {
-        x = (int) Math.max(Math.ceil(area.getMinX()), Math.min(x,
-                Math.floor(area.getMaxX())));
-        y = (int) Math.max(Math.ceil(area.getMinY()), Math.min(y,
-                Math.floor(area.getMaxY())));
-        return new Point(x, y);
+    private Point2D getPointInRectangle(int x, int y, Rectangle2D area) {
+        double xx = Math.max(area.getMinX(), Math.min(x, area.getMaxX()));
+        double yy = Math.max(area.getMinY(), Math.min(y, area.getMaxY()));
+        return new Point2D.Double(xx, yy);
     }
 
     /**
@@ -1635,6 +1635,8 @@
                     Rectangle2D screenDataArea = getScreenDataArea(
                             (int) this.zoomPoint.getX(),
                             (int) this.zoomPoint.getY());
+                    double maxX = screenDataArea.getMaxX();
+                    double maxY = screenDataArea.getMaxY();
                     // for mouseReleased event, (horizontalZoom || verticalZoom)
                     // will be true, so we can just test for either being false;
                     // otherwise both are true
@@ -1642,8 +1644,7 @@
                         x = this.zoomPoint.getX();
                         y = screenDataArea.getMinY();
                         w = Math.min(this.zoomRectangle.getWidth(),
-                                screenDataArea.getMaxX()
-                                - this.zoomPoint.getX());
+                                maxX - this.zoomPoint.getX());
                         h = screenDataArea.getHeight();
                     }
                     else if (!hZoom) {
@@ -1651,18 +1652,15 @@
                         y = this.zoomPoint.getY();
                         w = screenDataArea.getWidth();
                         h = Math.min(this.zoomRectangle.getHeight(),
-                                screenDataArea.getMaxY()
-                                - this.zoomPoint.getY());
+                                maxY - this.zoomPoint.getY());
                     }
                     else {
                         x = this.zoomPoint.getX();
                         y = this.zoomPoint.getY();
                         w = Math.min(this.zoomRectangle.getWidth(),
-                                screenDataArea.getMaxX()
-                                - this.zoomPoint.getX());
+                                maxX - this.zoomPoint.getX());
                         h = Math.min(this.zoomRectangle.getHeight(),
-                                screenDataArea.getMaxY()
-                                - this.zoomPoint.getY());
+                                maxY - this.zoomPoint.getY());
                     }
                     Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h);
                     zoom(zoomArea);
@@ -1924,7 +1922,8 @@
         if (p instanceof Zoomable) {
             Zoomable z = (Zoomable) p;
             // we need to guard against this.zoomPoint being null
-            Point zp = (this.zoomPoint != null ? this.zoomPoint : new Point());
+            Point2D zp = (this.zoomPoint != null
+            		? this.zoomPoint : new Point());
             z.zoomDomainAxes(0.0, this.info.getPlotInfo(), zp);
         }
     }
@@ -1937,7 +1936,8 @@
         if (p instanceof Zoomable) {
             Zoomable z = (Zoomable) p;
             // we need to guard against this.zoomPoint being null
-            Point zp = (this.zoomPoint != null ? this.zoomPoint : new Point());
+            Point2D zp = (this.zoomPoint != null
+            		? this.zoomPoint : new Point());
             z.zoomRangeAxes(0.0, this.info.getPlotInfo(), zp);
         }
     }
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

jpmaia
Posts: 53
Joined: Fri Feb 22, 2008 10:44 pm

Post by jpmaia » Wed May 07, 2008 9:36 pm

Thank you for figuring out a fix.

Will this go in the 1.0.10 version, or a later version?

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Thu May 08, 2008 8:10 am

I'd like to get this into 1.0.10. First I want to do a little more testing to make sure nothing is broken by this change.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

Locked