如何在子图上共享DomainAxis / RangeAxis而不在每个图上绘制它们?

时间:2015-09-29 06:30:11

标签: java plot jfreechart

好的,我几乎整天都在黑客攻击,但没有成功。我试图使用JFreeChart来创建XYPlot的网格,其中域和范围轴分别链接到每个列和行的图。也就是说,同一行中的图具有相同的范围轴范围,并且列中的图具有相同的域轴范围。

我能够通过使用CombinedDomainXYPlot CombinedRangeXYPlot XYPlotXYPlot来实现此功能。基本上我创建了一些CombinedRangeXYPlot个对象并将它们添加到CombinedRangeXYPlot个对象中,然后将这些CombinedDomainXYPlot个对象添加到不绘制域轴的CombinedDomainXYPlot实例中。 (也许还有另一种方法来叠加图而不是CombinedRangeXYPlot,因为我没有使用组合域轴功能。)

对于每一行,范围按预期一起缩放。通过将相同的域轴添加到列中的每个子图,我能够使每个列的域一起扩展。结果如下所示。 enter image description here

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

其次,范围轴的标签位于窗口的边缘 - 我该如何取回它们?

而且,一般来说,我想了解CombinedRangeXYPlotpublic 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(); } } 如何在不绘制每个图下方的轴的情况下为多个绘图使用相同的轴范围。

编辑:以下是工作演示的代码:

主要课程

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

enter image description here enter image description here

我认为让StackedXYPlot工作的另一件事就是将protected的可见性更改为dateadd()

我注意到这个例子中域轴的鼠标缩放是关闭的 - 但它确实传播到列中的其他图。

谢谢,

伊戈尔

P.S。我想要消除情节下面的情节的原因是因为最后我需要绘制至少一个6x7网格的图,并且有很多,标签占据了大部分空间。

编辑:我已经接受了Eric的答案,但我正在采用一种不那么强硬的方式 - How CombinedDomainXYPlot and CombinedRangeXYPlot share Axis information with subplots。如果我完全正常运行,我会在那里更新。

1 个答案:

答案 0 :(得分:1)

那真的很难......

从我的代码开始,这就是我的距离:

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

    ValueAxis a = phiDPlots[i].getDomainAxis();
    a.setVisible(false);
    tempPlot.setDomainAxis((NumberAxis) phiDPlots[i].getDomainAxis());
    
  2. 如果绘制最后一行,则设置子图的域轴可见:

    // 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);
        }
    }
    
  3. 这样可行,但不知何故只是在刷新/调整窗口大小之后,因为最后一行图形太垂直压缩...

    enter image description here