如何在不绘制每个图的情况下跨子图共享DomainAxis/RangeAxis? [英] How can I share DomainAxis/RangeAxis across subplots without drawing them on each plot?

查看:58
本文介绍了如何在不绘制每个图的情况下跨子图共享DomainAxis/RangeAxis?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好吧,我几乎整天都在破解它,但没有成功.我正在尝试使用JFreeChart创建XYPlot的网格,其中图的每一列和每一行都分别链接了域轴和范围轴.也就是说,同一行中的图具有相同的范围轴范围,而一列中的图具有相同的域轴范围.

Ok, I've been hacking at this for almost all day with no success. I'm trying to use JFreeChart to create a grid of XYPlots where the domain and range axes are linked for each column and row of plots, respectively. That is to say, the plots in the same row have the same range axis range, and the plots in a column have the same domain axis range.

我能够通过使用XYPlotCombinedRangeXYPlot的被入侵的CombinedDomainXYPlot来实现此功能.基本上,我做了一些XYPlot对象,并将它们添加到CombinedRangeXYPlot对象,然后将那些CombinedRangeXYPlot对象添加到了不绘制域轴的CombinedDomainXYPlot实例中. (也许因为我没有使用合并的域轴功能,所以还有一种方法可以代替CombinedDomainXYPlot来堆积图.)

I was able to achieve the functionality by using a hacked CombinedDomainXYPlot of CombinedRangeXYPlots of XYPlots. Basically I made some XYPlot objects and added them to CombinedRangeXYPlot objects, then added those CombinedRangeXYPlot objects to an instance of CombinedDomainXYPlot that doesnt draw a domain axis. (Maybe there is another way to stack plots instead of CombinedDomainXYPlot, since I'm not using the combined domain axis functionality.)

每个行的范围将按预期比例缩放.通过为列中的每个子图添加相同的域轴,我可以使域针对每个列一起缩放.结果如下所示.

The ranges scale together for each row, as expected. By adding the same domain axis to each subplot in a column, I was able to get the domains to scale together for each column. Result is shown below.

我现在有两个问题-首先,我想摆脱每一行下方的轴标签,而只将它们放在底部,但要保持比例尺链接.

I have two problems right now - first, I would like to get rid of the axis labels below each row and just have them on the bottom, but keep the scales linked.

第二,范围轴的标签位于窗口的边缘-如何找回标签?

Second, the labels for the range axes are of the edge of the window - how do I get them back?

而且,总的来说,我想了解CombinedRangeXYPlotCombinedRangeXYPlot如何在多个图上使用相同的轴范围而不在每个图下方绘制轴.

And, in general, I would like to understand how CombinedRangeXYPlot and CombinedRangeXYPlot use the same axis range for multiple plots without drawing the axes below each plot.

这是工作示例的代码:

主班

public class GridBlockPlotFrameExample {

  private final JFrame frame;
  private final XYPlot[][] phiPhiPlots;
  private final XYPlot[] phiDPlots;

  public GridBlockPlotFrameExample() {
    frame = new JFrame("Density Plot");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    phiDPlots = new XYPlot[4];
    phiPhiPlots = new XYPlot[4][4];

    createSubPlots();
    CombinedRangeXYPlot[] rowPlots = new CombinedRangeXYPlot[phiDPlots.length + 1];
    for (int i = 0; i < phiPhiPlots.length; i++) {
      rowPlots[i + 1] = new CombinedRangeXYPlot();
      for (int j = 0; j < phiPhiPlots[i].length; j++) {
        if (phiPhiPlots[i][j] != null) {
          rowPlots[i + 1].add(phiPhiPlots[i][j]);
        } else {
          rowPlots[i + 1].add(new XYPlot());
        }
      }
    }
    rowPlots[0] = new CombinedRangeXYPlot();
    for (XYPlot phiDPlot : phiDPlots) {
      rowPlots[0].add(phiDPlot);
    }

    StackedXYPlot gridPlot = new StackedXYPlot();

    for (int i = rowPlots.length - 1; i >= 1; i--) {
      XYPlot rowPlot = rowPlots[i];
      gridPlot.add(rowPlot, 2);
    }
    gridPlot.add(rowPlots[0], 1);

    JFreeChart chart = new JFreeChart("gridplot", JFreeChart.DEFAULT_TITLE_FONT, gridPlot, false);
    chart.setBackgroundPaint(Color.WHITE);

    ChartPanel panel = new ChartPanel(chart);
    panel.setPreferredSize(new Dimension(300, 300));
    panel.setMouseWheelEnabled(false);
    panel.setRangeZoomable(true);
    panel.setDomainZoomable(true);

    frame.setContentPane(panel);
    frame.pack();

    RefineryUtilities.centerFrameOnScreen(frame);
  }

  private void createSubPlots() {
    for (int i = 0; i < phiDPlots.length; i++) {
      phiDPlots[i] = createPlot(createDataset());
    }
    XYPlot tempPlot;
    for (int i = 0; i < phiPhiPlots.length; i++) {
      for (int j = 0; j < phiPhiPlots.length; j++) {
        tempPlot = createPlot(createDataset());
        phiPhiPlots[j][i] = tempPlot; // (sic) YES this inversion is intentional
        tempPlot.setDomainAxis((NumberAxis) phiDPlots[i].getDomainAxis());
      }
    }
  }

  private XYPlot createPlot(XYZDataset data) {
    NumberAxis xAxis = new NumberAxis("X");
    xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
    xAxis.setLowerMargin(0.0);
    xAxis.setUpperMargin(0.0);
    xAxis.setAxisLinePaint(Color.white);
    xAxis.setTickMarkPaint(Color.white);
    NumberAxis yAxis = new NumberAxis("Y");
    yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
    yAxis.setLowerMargin(0.0);
    yAxis.setUpperMargin(0.0);
    yAxis.setAxisLinePaint(Color.white);
    yAxis.setTickMarkPaint(Color.white);
    XYBlockRenderer renderer = new XYBlockRenderer();
    PaintScale scale = new GrayPaintScale(-2.0, 1.0);
    renderer.setPaintScale(scale);
    XYPlot plot = new XYPlot(data, xAxis, yAxis, renderer);
    plot.setBackgroundPaint(Color.lightGray);
    plot.setDomainGridlinesVisible(false);
    plot.setRangeGridlinePaint(Color.white);
    plot.setAxisOffset(new RectangleInsets(5, 5, 5, 5));
    plot.setOutlinePaint(Color.blue);
    return plot;
  }

  private XYZDataset createDataset() {
    return new XYZDataset() {

      @Override
      public int getSeriesCount() {
        return 1;
      }

      @Override
      public int getItemCount(int series) {
        return 10000;
      }

      @Override
      public Number getX(int series, int item) {
        return new Double(getXValue(series, item));
      }

      @Override
      public double getXValue(int series, int item) {
        return item / 100 - 50;
      }

      @Override
      public Number getY(int series, int item) {
        return new Double(getYValue(series, item));
      }

      @Override
      public double getYValue(int series, int item) {
        return item - (item / 100) * 100 - 50;
      }

      @Override
      public Number getZ(int series, int item) {
        return new Double(getZValue(series, item));
      }

      @Override
      public double getZValue(int series, int item) {
        double x = getXValue(series, item);
        double y = getYValue(series, item);
        return Math.sin(Math.sqrt(x * x + y * y) / 5.0);
      }

      @Override
      public void addChangeListener(DatasetChangeListener listener) {
        // ignore - this dataset never changes
      }

      @Override
      public void removeChangeListener(DatasetChangeListener listener) {
        // ignore
      }

      @Override
      public DatasetGroup getGroup() {
        return null;
      }

      @Override
      public void setGroup(DatasetGroup group) {
        // ignore
      }

      @Override
      public Comparable getSeriesKey(int series) {
        return "sin(sqrt(x + y))";
      }

      @Override
      public int indexOf(Comparable seriesKey) {
        return 0;
      }

      @Override
      public DomainOrder getDomainOrder() {
        return DomainOrder.ASCENDING;
      }
    };
  }


  public void show() {
    frame.setVisible(true);
  }

  public static void main(String[] args) {
    GridBlockPlotFrameExample example = new GridBlockPlotFrameExample();
    example.show();
  }
}

StackedXYPlot类

StackedXYPlot class

public class StackedXYPlot extends CombinedDomainXYPlot {

  public StackedXYPlot() {
    super(null);
  }

  @Override
  public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState,
      PlotRenderingInfo info) {

    // set up info collection...
    if (info != null) {
      info.setPlotArea(area);
    }

    // adjust the drawing area for plot insets (if any)...
    RectangleInsets insets = getInsets();
    insets.trim(area);

    setFixedRangeAxisSpaceForSubplots(null);
    AxisSpace space = calculateAxisSpace(g2, area);
    Rectangle2D dataArea = space.shrink(area, null);

    // set the width and height of non-shared axis of all sub-plots
    setFixedRangeAxisSpaceForSubplots(space);

    // draw all the subplots
    for (int i = 0; i < getSubplots().size(); i++) {
      XYPlot plot = (XYPlot) getSubplots().get(i);
      PlotRenderingInfo subplotInfo = null;
      if (info != null) {
        subplotInfo = new PlotRenderingInfo(info.getOwner());
        info.addSubplotInfo(subplotInfo);
      }
      plot.draw(g2, this.subplotAreas[i], anchor, parentState, subplotInfo);
    }

    if (info != null) {
      info.setDataArea(dataArea);
    }
  }

  public int findSubplotIndex(PlotRenderingInfo info, Point2D source) {
    ParamChecks.nullNotPermitted(info, "info");
    ParamChecks.nullNotPermitted(source, "source");
    XYPlot result = null;
    return info.getSubplotIndex(source);
  }

  /**
   * Multiplies the range on the range axis/axes by the specified factor.
   *
   * @param factor the zoom factor.
   * @param info the plot rendering info (<code>null</code> not permitted).
   * @param source the source point (<code>null</code> not permitted).
   */
  @Override
  public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source) {
    zoomDomainAxes(factor, info, source, false);
  }

  /**
   * Multiplies the range on the range axis/axes by the specified factor.
   *
   * @param factor the zoom factor.
   * @param state the plot state.
   * @param source the source point (in Java2D coordinates).
   * @param useAnchor use source point as zoom anchor?
   */
  @Override
  public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source,
      boolean useAnchor) {
    // delegate 'state' and 'source' argument checks...
    int subplotIndex = findSubplotIndex(state, source);
    XYPlot subplot = null;
    if (subplotIndex >= 0) {
      subplot = (XYPlot) getSubplots().get(subplotIndex);
    }
    if (subplot != null) {
      subplot.zoomDomainAxes(factor, state.getSubplotInfo(subplotIndex), source, useAnchor);
    } else {
      // if the source point doesn't fall within a subplot, we do the
      // zoom on all subplots...
      Iterator iterator = getSubplots().iterator();
      while (iterator.hasNext()) {
        subplot = (XYPlot) iterator.next();
        subplot.zoomDomainAxes(factor, state, source, useAnchor);
      }
    }
  }


  /**
   * Zooms in on the range axes.
   *
   * @param lowerPercent the lower bound.
   * @param upperPercent the upper bound.
   * @param info the plot rendering info (<code>null</code> not permitted).
   * @param source the source point (<code>null</code> not permitted).
   */
  @Override
  public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info,
      Point2D source) {
    // delegate 'info' and 'source' argument checks...
    int subplotIndex = findSubplotIndex(info, source);
    XYPlot subplot = null;
    if (subplotIndex >= 0) {
      subplot = (XYPlot) getSubplots().get(subplotIndex);
    }
    if (subplot != null) {
      subplot.zoomDomainAxes(lowerPercent, upperPercent, info.getSubplotInfo(subplotIndex), source);
    } else {
      // if the source point doesn't fall within a subplot, we do the
      // zoom on all subplots...
      Iterator iterator = getSubplots().iterator();
      while (iterator.hasNext()) {
        subplot = (XYPlot) iterator.next();
        subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source);
      }
    }
  }

  /**
   * Multiplies the range on the range axis/axes by the specified factor.
   *
   * @param factor the zoom factor.
   * @param info the plot rendering info (<code>null</code> not permitted).
   * @param source the source point (<code>null</code> not permitted).
   */
  @Override
  public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) {
    zoomRangeAxes(factor, info, source, false);
  }

  /**
   * Multiplies the range on the range axis/axes by the specified factor.
   *
   * @param factor the zoom factor.
   * @param state the plot state.
   * @param source the source point (in Java2D coordinates).
   * @param useAnchor use source point as zoom anchor?
   */
  @Override
  public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source,
      boolean useAnchor) {
    // delegate 'state' and 'source' argument checks...
    int subplotIndex = findSubplotIndex(state, source);
    XYPlot subplot = null;
    if (subplotIndex >= 0) {
      subplot = (XYPlot) getSubplots().get(subplotIndex);
    }
    if (subplot != null) {
      subplot.zoomRangeAxes(factor, state.getSubplotInfo(subplotIndex), source, useAnchor);
    } else {
      // if the source point doesn't fall within a subplot, we do the
      // zoom on all subplots...
      Iterator iterator = getSubplots().iterator();
      while (iterator.hasNext()) {
        subplot = (XYPlot) iterator.next();
        subplot.zoomRangeAxes(factor, state, source, useAnchor);
      }
    }
  }

  /**
   * Zooms in on the range axes.
   *
   * @param lowerPercent the lower bound.
   * @param upperPercent the upper bound.
   * @param info the plot rendering info (<code>null</code> not permitted).
   * @param source the source point (<code>null</code> not permitted).
   */
  @Override
  public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info,
      Point2D source) {
    // delegate 'info' and 'source' argument checks...
    int subplotIndex = findSubplotIndex(info, source);
    XYPlot subplot = null;
    if (subplotIndex >= 0) {
      subplot = (XYPlot) getSubplots().get(subplotIndex);
    }
    if (subplot != null) {
      subplot.zoomRangeAxes(lowerPercent, upperPercent, info.getSubplotInfo(subplotIndex), source);
    } else {
      // if the source point doesn't fall within a subplot, we do the
      // zoom on all subplots...
      Iterator iterator = getSubplots().iterator();
      while (iterator.hasNext()) {
        subplot = (XYPlot) iterator.next();
        subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source);
      }
    }
  }

}

我相信要使StackedXYPlot正常工作,我要做的唯一另一件事就是将CombinedDomainXYPlot.subplotAreas的可见性更改为protected.

I believe the only other thing I had to do to get the StackedXYPlot to work is change visibility of CombinedDomainXYPlot.subplotAreas to protected.

在此示例中,我注意到域轴的鼠标缩放功能已关闭-但确实会传播到列中的其他图.

I noticed with this example that the mouse zoom of the domain axis is off - but it does propagate to the other plots in the column.

谢谢

伊戈尔

P.S.之所以要删除该图下面的图,是因为最后我至少要绘制一个6x7的图网格,并且在这么多的网格中,标签占据了大部分空间.

P.S. the reason I want to eliminate the plots below the plot is because in the end I need to plot at least a 6x7 grid of plots and with that many, the labels take up most of the space.

编辑:我已经接受Eric的回答,认为它是功能性的,但我正在尝试一种不太hacky的方式- CombinedDomainXYPlot和CombinedRangeXYPlot如何与子图共享轴信息.如果我可以完全正常运行,我将在那里进行更新.

Edit: I have accepted Eric's answer as functional, but I am working on a less hackish way of doing it - How CombinedDomainXYPlot and CombinedRangeXYPlot share Axis information with subplots. I'll update there if I get it completely functional.

推荐答案

那真的很困难……

从您的代码开始,这就是我接近的程度:

This is how close I got, starting from your code:

  1. 将域轴设置为不可见:

  1. Set the domain axes invisible:

ValueAxis a = phiDPlots[i].getDomainAxis();
a.setVisible(false);
tempPlot.setDomainAxis((NumberAxis) phiDPlots[i].getDomainAxis());

  • 如果绘制最后一行,则设置子图的域轴可见:

  • Set the subplot's domain axes visible if drawing the last row:

    // draw all the subplots
    for (int i = 0; i < this.getSubplots().size(); i++) {
        CombinedRangeXYPlot plot = (CombinedRangeXYPlot) this.getSubplots().get(i);
        PlotRenderingInfo subplotInfo = null;
        if (info != null) {
            subplotInfo = new PlotRenderingInfo(info.getOwner());
            info.addSubplotInfo(subplotInfo);
        }
    
        if(i==getSubplots().size()-1){  // If the last row
            for(int j=0; j < plot.getSubplots().size(); j++)
                ((XYPlot)plot.getSubplots().get(j)).getDomainAxis().setVisible(true);
        }
    
        plot.draw(g2, this.subplotAreas[i], anchor, parentState, subplotInfo);
    
        if(i==getSubplots().size()-1){  // If the last row
            for(int j=0; j < plot.getSubplots().size(); j++)
                ((XYPlot)plot.getSubplots().get(j)).getDomainAxis().setVisible(false);
        }
    }
    

  • 这有效,但是以某种方式仅在刷新/调整窗口大小之后,因为图形的最后一行在垂直方向上过于压缩...

    This works, but somehow only after a refresh/resize of the window, because the last row of graphs is too compressed vertically...

    这篇关于如何在不绘制每个图的情况下跨子图共享DomainAxis/RangeAxis?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆