BUG IN JFREE CHART - zooming

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
Craig

BUG IN JFREE CHART - zooming

Post by Craig » Tue Oct 04, 2005 7:26 pm

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?

michael75
Posts: 16
Joined: Thu Sep 15, 2005 4:42 pm

Post by michael75 » Thu Oct 06, 2005 7:32 pm

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. :wink:

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());
                }
            }
        }
    }
and the mouse released method with this one:

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());
            }
        }

    }
cu,
Michael

Craig

zooming error

Post by Craig » Fri Oct 07, 2005 1:44 am

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:

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

michael75
Posts: 16
Joined: Thu Sep 15, 2005 4:42 pm

Post by michael75 » Fri Oct 07, 2005 9:25 am

hi craig,

hm strange... could u please post code of your chart so that i can check where the error is? i would not handle this issue in the axis code because the area selected for zooming is determined earlier.

cu,
michael

Craig

more code

Post by Craig » Tue Oct 11, 2005 7:27 pm

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

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;
      }
      
   }

Locked