BUG IN JFREE CHART - zooming
BUG IN JFREE CHART - zooming
I have a 3dBarChart. Zooming works fine. However, JFreeChart allows you to click underneath the chart and then zoom in. It makes the range of the chart from 0 to 0..(i think). How can I disable zooming when clicking outside of the chart to fix this bug?
hello craig,
i had the same problem with a line chart. as zooming seems to be implemented the same way for all charts u can do the same changes as i did. if u do, please give feedback if it works. any feedback by david is very appreciated as well, of course.
u can fix it in org/jfree/chart/ChartPanel.java.
replace mousePressed method with this one:
and the mouse released method with this one:
cu,
Michael
i had the same problem with a line chart. as zooming seems to be implemented the same way for all charts u can do the same changes as i did. if u do, please give feedback if it works. any feedback by david is very appreciated as well, of course.

u can fix it in org/jfree/chart/ChartPanel.java.
replace mousePressed method with this one:
Code: Select all
public void mousePressed(MouseEvent e) {
if (this.zoomRectangle == null) {
Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY());
if (screenDataArea != null
&& e.getX() < screenDataArea.getMaxX() && e.getY() < screenDataArea.getMaxY()) {
this.zoomPoint = getPointInRectangle(e.getX(), e.getY(),
screenDataArea);
}
else {
this.zoomPoint = null;
}
if (e.isPopupTrigger()) {
if (this.popup != null) {
displayPopupMenu(e.getX(), e.getY());
}
}
}
}
Code: Select all
public void mouseReleased(MouseEvent e) {
if (this.zoomRectangle != null) {
boolean hZoom = false;
boolean vZoom = false;
if (this.orientation == PlotOrientation.HORIZONTAL) {
hZoom = this.rangeZoomable;
vZoom = this.domainZoomable;
}
else {
hZoom = this.domainZoomable;
vZoom = this.rangeZoomable;
}
boolean zoomTrigger1 = hZoom && Math.abs(e.getX()
- this.zoomPoint.getX()) >= this.zoomTriggerDistance;
boolean zoomTrigger2 = vZoom && Math.abs(e.getY()
- this.zoomPoint.getY()) >= this.zoomTriggerDistance;
if (zoomTrigger1 || zoomTrigger2) {
Rectangle2D screenDataArea = getScreenDataArea(
(int) this.zoomPoint.getX(),
(int) this.zoomPoint.getY()
);
// if the lower right point of the zoomRectangle is outside the chart
// at this time, the user created a rectangle left and/or above the screenDataArea.
// just do nothing in this case.
Point checkPoint = getPointInRectangle((int) this.zoomRectangle.getMaxX(),
(int) this.zoomRectangle.getMaxY(), screenDataArea);
if (checkPoint.x != (int) this.zoomRectangle.getMaxX() ||
checkPoint.y != (int) this.zoomRectangle.getMaxY())
return;
if ((hZoom && (e.getX() < this.zoomPoint.getX()))
|| (vZoom && (e.getY() < this.zoomPoint.getY()))) {
restoreAutoBounds();
}
else {
double x, y, w, h;
checkPoint = getPointInRectangle((int) this.zoomRectangle.getMinX(),
(int) this.zoomRectangle.getMinY(), screenDataArea);
if (checkPoint.x != (int) this.zoomRectangle.getMinX() ||
checkPoint.y != (int) this.zoomRectangle.getMinY())
return;
// for mouseReleased event, (horizontalZoom || verticalZoom)
// will be true, so we can just test for either being false;
// otherwise both are true
if (!vZoom) {
x = this.zoomPoint.getX();
y = screenDataArea.getMinY();
w = Math.min(
this.zoomRectangle.getWidth(),
screenDataArea.getMaxX() - this.zoomPoint.getX()
);
h = screenDataArea.getHeight();
}
else if (!hZoom) {
x = screenDataArea.getMinX();
y = this.zoomPoint.getY();
w = screenDataArea.getWidth();
h = Math.min(
this.zoomRectangle.getHeight(),
screenDataArea.getMaxY() - this.zoomPoint.getY()
);
}
else {
x = this.zoomPoint.getX();
y = this.zoomPoint.getY();
w = Math.min(
this.zoomRectangle.getWidth(),
screenDataArea.getMaxX() - this.zoomPoint.getX()
);
h = Math.min(
this.zoomRectangle.getHeight(),
screenDataArea.getMaxY() - this.zoomPoint.getY()
);
}
Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h);
zoom(zoomArea);
}
this.zoomPoint = null;
this.zoomRectangle = null;
}
else {
Graphics2D g2 = (Graphics2D) getGraphics();
g2.setXORMode(java.awt.Color.gray);
if (this.fillZoomRectangle) {
g2.fill(this.zoomRectangle);
}
else {
g2.draw(this.zoomRectangle);
}
g2.dispose();
this.zoomPoint = null;
this.zoomRectangle = null;
}
}
else if (e.isPopupTrigger()) {
if (this.popup != null) {
displayPopupMenu(e.getX(), e.getY());
}
}
}
Michael
zooming error
Hi Michael,
I tried using your code but it didn't work for me. I think there was a problem with the mouseReleased method. It would just draw the box around the selected view area and sit there. Zooming never got called and the chart didn't repaint. You didn't have that problem? I didn't see where the error was but I also didn't look too hard. I fixed the problem by writing a CustomNumberAxis class, altho this is pretty much just a hack. If you know how to make this work correctly let me know, that would be awesome. Here's what I did:
I basically just figured out where the bottom of the chart was (as far as upperPercent was concerned) by printing that value out and then doing the less than thing.
Craig
I tried using your code but it didn't work for me. I think there was a problem with the mouseReleased method. It would just draw the box around the selected view area and sit there. Zooming never got called and the chart didn't repaint. You didn't have that problem? I didn't see where the error was but I also didn't look too hard. I fixed the problem by writing a CustomNumberAxis class, altho this is pretty much just a hack. If you know how to make this work correctly let me know, that would be awesome. Here's what I did:
Code: Select all
/*
* Custom NumberAxis to disable zooming on a chart when the zoom area is outside of the chart.
*/
private class CustomValueAxis extends NumberAxis {
/**
* 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) {
/*
* Overwrote this method so that the zoom doesn't allow the user to zoom in to an
* area that isn't part of the chart. If the upperPercent is too small (underneath the chart)
* then the chart just doesnt zoom at all. Everything inside the first if statement is copied
* straight from the ValueAxis class' method.
*/
//Approximate value of where the bottom of the chart is.
//Derived this number emperically
if (!(upperPercent < 3E-3)) {
double start = getRange().getLowerBound();
double length = getRange().getLength();
Range adjusted = null;
if (isInverted()) {
adjusted = new Range(start + (length * (1 - upperPercent)),
start + (length * (1 - lowerPercent)));
}
else {
adjusted = new Range(
start + length * lowerPercent, start + length * upperPercent
);
}
setRange(adjusted);
}
}
}
I basically just figured out where the bottom of the chart was (as far as upperPercent was concerned) by printing that value out and then doing the less than thing.
Craig
more code
Hey Michael,
Here's what the code to create my chart looks like. Let me know if you figure out where that error was.
-Craig
Here's what the code to create my chart looks like. Let me know if you figure out where that error was.
-Craig
Code: Select all
private class SCBarChart extends Widget {
public SCBarChart(SupplyCenter sc) {
super(sc.getDomain());
JFreeChart chart = createChart(createDataset(sc));
ChartPanel chartPanel = new ChartPanel(chart, false);
chartPanel.setPreferredSize(new Dimension(500, 270));
setMainComponent(new JScrollPane(chartPanel));
setMainComponent(chartPanel);
add(chartPanel);
//setJToolBar(new ChartToolBar(chart.getCategoryPlot().getRenderer()));
//TODO: uncomment the above line to add a toolbar to this chart.
setBackground(new Color(206, 227, 253));
}
private CategoryDataset createDataset(SupplyCenter sc) {
DefaultCategoryDataset dataset = new DefaultCategoryDataset();
List l = sc.getWeightByType();
Iterator iter = l.iterator();
while (iter.hasNext()) {
Object[] values = (Object[])iter.next();
String typeName = values[0].toString();
double tons = Double.parseDouble(values[1].toString());
dataset.addValue(tons, "", typeName);
}
return dataset;
}
/**
* Creates a sample chart.
*
* @param dataset the dataset.
*
* @return The chart.
*/
private JFreeChart createChart(CategoryDataset dataset) {
// create the chart...
JFreeChart chart = ChartFactory.createBarChart3D(
"Inventory Short Tons by Type", // chart title
"Inventory Type", // domain axis label
"Short Tons", // range axis label
dataset, // data
PlotOrientation.VERTICAL, // orientation
false, // include legend
true, // tooltips?
false // URLs?
);
// NOW DO SOME OPTIONAL CUSTOMISATION OF THE CHART...
chart.setBackgroundPaint(new Color(136, 192, 249));
// Get a reference to the plot for further customization...
CategoryPlot plot = chart.getCategoryPlot();
String label = plot.getRangeAxis().getLabel();
plot.setRangeAxis(new CustomValueAxis());
plot.setBackgroundPaint(new Color(206, 227, 253));
plot.setDomainGridlinesVisible(false);
plot.setRangeGridlinePaint(Color.gray);
// Set the range axis to display integers only...
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
rangeAxis.setUpperMargin(0.15);
rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
//These are the colors to paint the bars with
Paint[] colors = new Paint[] {new Color(1, 1, 180), new Color(255, 255, 100), new Color(170, 17, 89),
new Color(9, 140, 18), new Color(233, 139, 12), new Color(73, 215, 226),
new Color(217, 0, 5), new Color(118, 250, 99), new Color(250, 150, 222), Color.gray};
BarRenderer3D renderer = new CustomRenderer(colors);
renderer.setDrawBarOutline(false);
String value = System.getProperty("CDM:Transway:Graphs:limitBarWidth");
if (value != null && value.equals("true")) {
renderer.setMaxBarWidth(.02);
}
CategoryItemLabelGenerator clg = new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("###"));
ItemLabelPosition ilp = new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.BASELINE_CENTER);
renderer.setItemLabelGenerator(clg);
renderer.setItemLabelFont(new Font("TimesNewRoman", Font.PLAIN, 11));
//renderer.setItemLabelsVisible(true);
renderer.setPositiveItemLabelPosition(ilp);
renderer.setItemLabelAnchorOffset(25);
List categories = plot.getCategories();
/*
* Populate Legend Item Collection
*/
Shape legendShape = plot.getLegendItems().get(0).getShape();
LegendItemCollection lic = new LegendItemCollection();
for (int i = 0; i < categories.size(); i++) {
String legendName = (String) categories.get(i);
lic.add(new LegendItem(
legendName, //label
legendName, //description
legendName, //toolTipText
legendName, //urlText
legendShape, //shape
colors[i % colors.length] //paint
));
}
plot.setFixedLegendItems(lic);
/*
* Done w/ legends
*/
renderer.setToolTipGenerator(new CustomToolTipGenerator(dataset, categories));
ItemLabelPosition itemlabelposition = new ItemLabelPosition(ItemLabelAnchor.OUTSIDE12, TextAnchor.TOP_LEFT);
renderer.setPositiveItemLabelPosition(itemlabelposition);
plot.setRenderer(renderer);
CategoryAxis domainAxis = plot.getDomainAxis();
domainAxis.setCategoryMargin(0.1);
domainAxis.setLowerMargin(0.01);
domainAxis.setUpperMargin(0.01);
domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_90);
domainAxis.setCategoryLabelPositions(
CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 6.0)
);
plot.setDomainAxis(domainAxis);
// OPTIONAL CUSTOMISATION COMPLETED.
return chart;
}
}