Click on a line

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
Fredo
Posts: 3
Joined: Mon Feb 28, 2005 4:30 pm

Click on a line

Post by Fredo » Thu Mar 03, 2005 4:56 pm

Hi !

I created a XYLineChart and i would like that the user can click on a line (to display some data).

Can I do that (with a ChartMouseListener?)and if yes, how?

Thank you very much

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 Mar 03, 2005 6:16 pm

Add a ChartMouseListener to the ChartPanel, and it will return the ChartEntity for a mouse click. You should be able to determine from the entity which data item it relates to, and get the actual values by querying the dataset directly.
David Gilbert
JFreeChart Project Leader

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

Fredo
Posts: 3
Joined: Mon Feb 28, 2005 4:30 pm

Post by Fredo » Thu Mar 03, 2005 7:03 pm

I did a test :

Code: Select all

  public void chartMouseClicked(ChartMouseEvent e){
        
   ChartEntity ce = e.getEntity();
   System.out.println(ce);};
When I click on a point, it takes me "org.free.chart.entity.XYItemEntity@..." ok !
But when I click on a line, it takes me "null" ! So how I can take the ChartEntity corresponding to my line?

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 Mar 03, 2005 7:49 pm

There isn't one for the line...so you'll need to amend the renderer to insert one.
David Gilbert
JFreeChart Project Leader

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

Fredo
Posts: 3
Joined: Mon Feb 28, 2005 4:30 pm

Post by Fredo » Thu Mar 03, 2005 10:30 pm

Can you be more precise on the way of proceeding? i don't know by what start and how to create a LineEntity.

Thank you for your help.

Alexander
Posts: 24
Joined: Thu Feb 17, 2005 3:59 pm

Post by Alexander » Mon Mar 14, 2005 4:19 pm

Hi,

Did you get this working? I also need to be able to determine if a click was on a line.

TiA,

Alexander

Silent

Post by Silent » Tue Mar 15, 2005 2:16 pm

Hi :lol:

I need a solution of this problem. Nobody knows it?

"e.getEntity()" is just allowed for a point not a line.

How can I know a event that click on a line ???

silent
Posts: 50
Joined: Wed Mar 16, 2005 2:55 pm
Location: Korea
Contact:

Post by silent » Thu Mar 17, 2005 1:25 am

I've got a trick(?).

I used a mouse point in a chart and line equation with two points..
so, i need a "mouse point" in a chart.
-- two points for line equation can get in a dataset.

For knows axis of a mouse point, I used "anchor".
I think, "anchor" is used for a tooltip. it is axis for drawing a tooltip. ( I think this is not correct. just I think.. -0-;)

I'm sorry for my english... -_-;;

I will show you sample code.. let's understand with following code.. -0-/;

Code: Select all

//==============================
//initial
ChartMouseEvent event;
ChartPanel chartPanel;
//==============================

// mouseX, mouseY : mouse point in chart
Insets insets = chartPanel.getInsets();
int anchorX = (int) ((event.getTrigger().getPoint().getX() - insets.left) / chartPanel.getScaleX());
int anchorY = (int) ((event.getTrigger().getPoint().getY() - insets.top) / chartPanel.getScaleY());

PlotRenderingInfo renderingInfo = (PlotRenderingInfo) chartPanel.getChartRenderingInfo().getPlotInfo();
double mouseX = domainAxis.java2DToValue(anchorX, renderingInfo.getDataArea(), RectangleEdge.BOTTOM);
double mouseY = rangeAxis.java2DToValue(anchorY, renderingInfo.getDataArea(), RectangleEdge.LEFT);

with this code, we can get a mouse point in chart.
next time, I compared with a mouse point(mouseX, mouseY) and line equation..
I think a mouse point nearest line within a boundary, is that what we want. so, if I click near a line, then I think "click a line".

can u understand?? -_-;;
I'm so sorry for my english.... T^T. :?
Tokdo belongs to Korea.

Not a 'sea of japan', But a 'East Sea(Dong hae)'

Alexander
Posts: 24
Joined: Thu Feb 17, 2005 3:59 pm

Post by Alexander » Fri Mar 18, 2005 10:24 am

Hi all,

I am working on something to make it possible to detect line clicks on XY chart. What I came up with is the following:

Edit the XYLineAndShapeRenderer to create polygons for every line, create something like a XYEntityItem, add this "XYLineItem" to a List, make this List available to the outside.

I'm not yet sure what classes I have to change to make this working, but I think this is a correct solution, similar way as for entities, and the polygons are create before the actual mouse click.

To be able to detect the actual click one should iterate the list and check if a mouse coordinate is inside a polygon.

Someone got any more ideas/remarks? About this method, but also about the classes I need to edit.

TiA,

Alexander

Alexander
Posts: 24
Joined: Thu Feb 17, 2005 3:59 pm

Post by Alexander » Tue Mar 22, 2005 10:13 am

Hi all,

Can someone reply to this, if this is a correct way to solve this problem?

TiA,

Alexander

Carl Manaster
Posts: 35
Joined: Tue Mar 28, 2006 1:10 am
Location: La Jolla
Contact:

This is working for me

Post by Carl Manaster » Mon Apr 10, 2006 11:58 pm

Code: Select all

import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.plot.CrosshairState;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYDataset;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.ShapeUtilities;

public class XYLineAndShapeRendererThatRecordsLinesAsEntities extends
XYLineAndShapeRenderer {
	
	protected void drawSecondaryPass(
			Graphics2D			g, 
			XYPlot				plot, 
			XYDataset			dataset,
			int					pass, 
			int					series, 
			int					item,
			ValueAxis			domainAxis, 
			Rectangle2D			dataArea,
			ValueAxis			rangeAxis, 
			CrosshairState		crosshairState,
			EntityCollection	entities) {
		
		Shape entityArea = null;
		
		double x1 = dataset.getXValue(series, item);
		double y1 = dataset.getYValue(series, item);
		if (Double.isNaN(y1) || Double.isNaN(x1))
			return;
		
		PlotOrientation orientation = plot.getOrientation();
		RectangleEdge xAxisLocation = plot.getDomainAxisEdge();
		RectangleEdge yAxisLocation = plot.getRangeAxisEdge();
		double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation);
		double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation);
		
		if (getItemShapeVisible(series, item)) {
			Shape shape = getItemShape(series, item);
			shape = translated(orientation, transX1, transY1, shape);
			entityArea = shape;
			if (shape.intersects(dataArea)) {
				drawFilledShape(g, series, item, shape);
				drawOutlines(g, series, item, shape);
			}
		}
		
		drawItemLabel(g, dataset, series, item, y1, orientation, transX1, transY1);
		updateCrosshairValues(crosshairState, x1, y1, transX1, transY1, plot.getOrientation());
		addEntity(entities, entityArea, dataset, series, item, transX1, transY1);

		if (item < dataset.getItemCount(series) - 1) {
			double x2 = dataset.getXValue(series, item + 1);
			double y2 = dataset.getYValue(series, item + 1);
			double transX2 = domainAxis.valueToJava2D(x2, dataArea, xAxisLocation);
			double transY2 = rangeAxis.valueToJava2D(y2, dataArea, yAxisLocation);
			entityArea = shapeAroundSegment(transX1, transY1, transX2, transY2, getDefaultEntityRadius());
			addEntity(entities, entityArea, dataset, series, item, transX1, transY1);
		}
	}
	
	protected void addEntity(
			EntityCollection entities, 
			Shape area, 
            XYDataset dataset, 
            int series, 
            int item,
            double entityX, 
            double entityY) {
		if (entities == null)
			return;
		super.addEntity(entities, area, dataset, series, item, entityX, entityY);
	}
	
	private Shape translated(PlotOrientation orientation, double x, double y, Shape shape) {
		if (orientation == PlotOrientation.HORIZONTAL)
			return ShapeUtilities.createTranslatedShape(shape, y, x);
		if (orientation == PlotOrientation.VERTICAL)
			return ShapeUtilities.createTranslatedShape(shape, x, y);
		return shape;
	}

	private void drawFilledShape(Graphics2D g, int series, int item, Shape shape) {
		if (!getItemShapeFilled(series, item))
			return;
		final Paint paint = getUseFillPaint() ? getItemFillPaint(series, item) : getItemPaint(series, item);
		g.setPaint(paint);
		g.fill(shape);
	}

	private void drawOutlines(Graphics2D g, int series, int item, Shape shape) {
		if (!getDrawOutlines())
			return;
		final Paint paint = getUseOutlinePaint() ? getItemOutlinePaint(series, item) : getItemPaint(series, item);
		g.setPaint(paint);
		g.setStroke(getItemOutlineStroke(series, item));
		g.draw(shape);
	}

	private void drawItemLabel(Graphics2D g, XYDataset dataset, int series, int item, double y1, PlotOrientation orientation, double transX1, double transY1) {
		if (!isItemLabelVisible(series, item))
			return;
		final boolean horizontal = orientation == PlotOrientation.HORIZONTAL;
		double xx = horizontal ? transY1 : transX1;
		double yy = horizontal ? transX1 : transY1;
		drawItemLabel(g, orientation, dataset, series, item, xx, yy, (y1 < 0.0));
	}
	
	static Shape shapeAroundSegment(double x1, double y1, double x2, double y2, double r) {
		final double dX = x2 - x1;
		final double dY = y2 - y1;
		final double dT = Math.sqrt(dX*dX + dY*dY);
		
		final double rX = r * dY / dT;
		final double rY = r * dX / dT;
		
		Point2D p1 = new Point2D.Double(x1 + rX, y1 + rY);
		Point2D p2 = new Point2D.Double(x2 + rX, y2 + rY);
		Point2D p3 = new Point2D.Double(x2 - rX, y2 - rY);
		Point2D p4 = new Point2D.Double(x1 - rX, y1 - rY);
		
		GeneralPath path = new GeneralPath();
		path.moveTo((float)p1.getX(), (float)p1.getY());
		path.lineTo((float)p2.getX(), (float)p2.getY());
		path.lineTo((float)p3.getX(), (float)p3.getY());
		path.lineTo((float)p4.getX(), (float)p4.getY());
		path.lineTo((float)p1.getX(), (float)p1.getY());
		path.closePath();
		return path;
	}

}
[/code]

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 Apr 11, 2006 9:09 pm

Hi Carl,

Thanks for the code. I added an item in the Patch Manager, because I might come back and add this as an optional feature in the XYLineAndShapeRenderer class:

https://sourceforge.net/tracker/index.p ... tid=315494
David Gilbert
JFreeChart Project Leader

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

Locked