Need help rescale annotation...

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
smahamkl
Posts: 6
Joined: Fri Jul 03, 2009 12:23 am

Need help rescale annotation...

Post by smahamkl » Mon Jul 06, 2009 11:50 pm

Hi,
I am trying to create a small stock chart tool using JFreeChart in which I am using the annotations capabilities that can be used to draw a Line, Text or other shapes on the chart. Also the great thing about these annotations are that they are rescaled appropriately when the chart is mouse zoomed in or out.

In the process I have had to create my own Custom annotation to draw an ARC annotation which is rendering fine on the chart. But the thing is that the arc is not getting scaled properly when the chart is zoomed in or out. What am I missing here? I have used the code XYLineAnnotation as the basis and followed the same struts like extending my XYArcAnnotations from XYAbstractAnnotation and implementing XYAnnotation interface overriding draw, equals, and other serialization methods. Can anyone suggest me what am I doing wrong?

thanks
SM

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

Re: Need help rescale annotation...

Post by david.gilbert » Tue Jul 07, 2009 3:27 pm

If you are having trouble debugging your custom annotation class *with* the source code, imagine how much trouble we'll have debugging it *without* the source code.
David Gilbert
JFreeChart Project Leader

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

smahamkl
Posts: 6
Joined: Fri Jul 03, 2009 12:23 am

Re: Need help rescale annotation...

Post by smahamkl » Wed Jul 08, 2009 12:49 am

My mistake sorry...here's the source code for my custom annotation...

Code: Select all

import org.jfree.chart.annotations.AbstractXYAnnotation;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.ui.RectangleEdge;
import org.jfree.chart.plot.PlotOrientation;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Arc2D;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.util.PublicCloneable;
import java.io.Serializable;
import java.awt.BasicStroke;
import java.awt.Stroke;
import java.awt.Color;
import java.awt.Paint;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PaintUtilities;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import org.jfree.io.SerialUtilities;

public class XYArcAnnotation extends AbstractXYAnnotation implements XYAnnotation, Cloneable, PublicCloneable,
        Serializable {

    private double x;
    private double y;
    private double radius;
    private transient Stroke stroke;
    private transient Paint paint;

    public XYArcAnnotation(double inx, double iny, double rad) {
        this.x = inx;
        this.y = iny;
        radius = rad;
        stroke = new BasicStroke(1.0f);
        paint = Color.black;
    }

    public XYArcAnnotation(double inx, double iny, double rad, Stroke stroke, Paint paint) {
        this.x = inx;
        this.y = iny;
        radius = rad;
        this.stroke = stroke;
        this.paint = paint;
    }

    @Override
    public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis,
            int rendererIndex, PlotRenderingInfo info) {
        PlotOrientation orientation = plot.getOrientation();
        RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(plot.getDomainAxisLocation(), orientation);
        RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(plot.getRangeAxisLocation(), orientation);

        double xx = domainAxis.valueToJava2D(x, dataArea, domainEdge);
        double yy = rangeAxis.valueToJava2D(y, dataArea, rangeEdge);

        if (orientation == PlotOrientation.HORIZONTAL) {
            double temp = xx;
            xx = yy;
            yy = temp;
        }

        Arc2D.Double a1 = new Arc2D.Double();
        a1.setArcByCenter(xx, yy, radius, 145, 250, Arc2D.OPEN);
        g2.setPaint(this.paint);
        g2.setStroke(this.stroke);
        g2.draw(a1);

    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (!super.equals(obj)) {
            return false;
        }
        if (!(obj instanceof XYArcAnnotation)) {
            return false;
        }
        XYArcAnnotation that = (XYArcAnnotation) obj;
        if (this.x != that.x) {
            return false;
        }
        if (this.y != that.y) {
            return false;
        }
        if (this.radius != that.radius) {
            return false;
        }
        if (!PaintUtilities.equal(this.paint, that.paint)) {
            return false;
        }
        if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
            return false;
        }

        return true;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        temp = Double.doubleToLongBits(this.x);
        result = (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(this.y);
        result = 29 * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(this.radius);
        result = 29 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    private void writeObject(ObjectOutputStream stream) throws IOException {

        stream.defaultWriteObject();
        SerialUtilities.writePaint(this.paint, stream);
        SerialUtilities.writeStroke(this.stroke, stream);

    }

    private void readObject(ObjectInputStream stream)
            throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        this.paint = SerialUtilities.readPaint(stream);
        this.stroke = SerialUtilities.readStroke(stream);
    }
}
thanks,

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

Re: Need help rescale annotation...

Post by paradoxoff » Wed Jul 08, 2009 6:29 pm

You are not scaling your radius. It is always the same value, hence the arc will have the same size regardless of the the zoom.
I suggest to create an arc with the desired start and end angle and a size that looks ok when the zoom is not active. Then, when zoomed, scale not only the position of your arc to java2D space, but also the width and height.

smahamkl
Posts: 6
Joined: Fri Jul 03, 2009 12:23 am

Re: Need help rescale annotation...

Post by smahamkl » Wed Jul 08, 2009 7:00 pm

Hi,
Thanks for your reply. Can you provide some details of what you were suggesting? To rescale it properly, do I have override onzoom event or so of the chartpanel? If possible can you provide me with a sample or point me to the similar class in JFreeChart source.

thanks
SM

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

Re: Need help rescale annotation...

Post by paradoxoff » Thu Jul 09, 2009 9:17 pm

I am not sure what you want to achieve. If you do not scale the radius, it will always be the same (as the shapes drawn by an XYLineAndShapeRenderer).
If you want to scale the arc properly, the question is what is "properly".
You could define a width and height of the arc in data units and scale the width and height from data units to java2d units. You could check XYShapeAnnotation for a similar example. Better yet: use an XYShapeAnnotation and provide your Arc2D.Double (which is also a Shape) as argument in the constructor.

skunk
Posts: 1087
Joined: Thu Jun 02, 2005 10:14 pm
Location: Brisbane, Australia

Re: Need help rescale annotation...

Post by skunk » Fri Jul 10, 2009 1:07 pm

http://www.jfree.org/phpBB2/viewtopic.php?f=10&t=28511

You need to clearly explain what "properly" means

smahamkl
Posts: 6
Joined: Fri Jul 03, 2009 12:23 am

Re: Need help rescale annotation...

Post by smahamkl » Fri Jul 10, 2009 7:56 pm

Thanks for your reply paradoxoff. The problem with the XYShapeAnnotation is that if I am passing the same Arc2D.Double to the XYShapeAnnotation, I can only see a couple of lines on either side of the center point on the chart and nothing else. That was the reason why I went on to create my own annotation.

Coming back to the scaling problem, I think I found the solution. Thanks to you for pointing out that the radius should be changed with respect to the currently rendered chart panel area and it should be updated each time the panel area changes. I am achieving this by deleting the old arc and creating a new arc in the same x,y location with the radius being updated.

Please let me know if there is a better way of doing this?

thanks
SM

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

Re: Need help rescale annotation...

Post by paradoxoff » Sat Jul 11, 2009 10:23 am

smahamkl wrote: Please let me know if there is a better way of doing this?
It still depends on what you want to achieve. You have mentioned that you now "update the radius" but you have not described how you do this.
I see two general possibilities for any "graphical object" to be rendered on a chart:
- size constant in java2D space (example: the shapes rendered by an XYLineAndShapeRenderer)
- size constant in data space (example: an XYShapeAnnotation). Note that this requires the conversion of "data units" to "java2d units" and may turn something was a circle (or part of a circle) at one particular zoom range into very flat or very thin ellipses (or a part of it) if you zoom too far along the range or domain axis.
Creating a new arc for every rendering pass seems ok (it shouldn´t be too expensive, ressource-wise). Alternatively you could check whether

Code: Select all

RectangularShape.setFrame(double x, double y, double w, double h)
does what you want (note that Arc2D extends RectangularShape).

Locked