Hallo,
ich arbeite momentan an einer Anwendung, in der ich Messreihen vertikal geplottet darstelle. Dabei nutze ich JFreeChart. Die Messreihen unterliegen als subplots einem CombinedDomainXYPlot und können je nach Belieben entweder angezeigt oder versteckt werden. Hierzu folgender Demo-Code:
Beim Checken der CheckBoxen links im weißen Panel wird jeweiliger subplot rechts im grauen Panel im CombinedDomainXYPlot angezeigt. Beim Unchecken wird jeweiliger subplot versteckt.
Folgendes ist zu beobachten und unerwünscht:
Da JFreeChart keine richtigen Lösungen für das pixelgenaue Platzieren von subplots ermöglicht, sei mal hier die Frage an den JFreeChart-affinen Teil der Community gerichtet, wie sich die Punkte 1 bis 3 unter Berücksichtigung der Kriterien A bis C bewerkstelligen lässt.
Cheers-
ich arbeite momentan an einer Anwendung, in der ich Messreihen vertikal geplottet darstelle. Dabei nutze ich JFreeChart. Die Messreihen unterliegen als subplots einem CombinedDomainXYPlot und können je nach Belieben entweder angezeigt oder versteckt werden. Hierzu folgender Demo-Code:
Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ItemEvent;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisSpace;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.DomainOrder;
import org.jfree.data.general.DatasetChangeListener;
import org.jfree.data.general.DatasetGroup;
import org.jfree.data.xy.AbstractXYDataset;
public class VerticalPlots {
private final JFrame frame;
private final ChartPanel chartPanel;
private final JPanel checkBoxPanel;
private final JCheckBox checkBox[] = new JCheckBox[5];
private final XYPlot[] subPlot = new XYPlot[5];
private final TestDataset[] dataset = new TestDataset[5];
private final int dataColumnWidth = 42;
private final int depthColumnWidth = 54;
private CombinedDomainXYPlot combinedPlot;
public VerticalPlots() {
generateAndPopulateDataset();
checkBoxPanel = new JPanel();
checkBoxPanel.setBackground(Color.WHITE);
BoxLayout layout = new BoxLayout(checkBoxPanel, BoxLayout.Y_AXIS);
checkBoxPanel.setLayout(layout);
checkBoxPanel.setPreferredSize(new Dimension(130 - depthColumnWidth, 512));
checkBoxPanel.setMinimumSize(checkBoxPanel.getPreferredSize());
checkBox[0] = new JCheckBox("Plot1");
checkBox[0].setOpaque(false);
checkBox[0].addItemListener((ItemEvent e) -> {
if (e.getSource().equals(checkBox[0])) {
updateUI();
}
});
checkBox[1] = new JCheckBox("Plot2");
checkBox[1].setOpaque(false);
checkBox[1].addItemListener((ItemEvent e) -> {
if (e.getSource().equals(checkBox[1])) {
updateUI();
}
});
checkBox[2] = new JCheckBox("Plot3");
checkBox[2].setOpaque(false);
checkBox[2].addItemListener((ItemEvent e) -> {
if (e.getSource().equals(checkBox[2])) {
updateUI();
}
});
checkBox[3] = new JCheckBox("Plot4");
checkBox[3].setOpaque(false);
checkBox[3].addItemListener((ItemEvent e) -> {
if (e.getSource().equals(checkBox[3])) {
updateUI();
}
});
checkBox[4] = new JCheckBox("Plot5");
checkBox[4].setOpaque(false);
checkBox[4].addItemListener((ItemEvent e) -> {
if (e.getSource().equals(checkBox[4])) {
updateUI();
}
});
checkBoxPanel.add(checkBox[0]);
checkBoxPanel.add(checkBox[1]);
checkBoxPanel.add(checkBox[2]);
checkBoxPanel.add(checkBox[3]);
checkBoxPanel.add(checkBox[4]);
chartPanel = createChartPanel();
chartPanel.setPreferredSize(new Dimension(depthColumnWidth, 512));
chartPanel.setMinimumDrawWidth(0);
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setLocation(512, 240);
frame.setPreferredSize(new Dimension(depthColumnWidth, 512));
frame.add(checkBoxPanel, BorderLayout.WEST);
frame.add(chartPanel, BorderLayout.EAST);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new VerticalPlots();
}
private ChartPanel createChartPanel() {
NumberAxis sharedDomainAxis = new NumberAxis("Depth (.m)");
sharedDomainAxis.setInverted(true);
sharedDomainAxis.setRange(0, 500);
NumberAxis rangeAxis = new NumberAxis();
rangeAxis.setRange(0, 100);
AxisSpace space = new AxisSpace();
space.setLeft(depthColumnWidth);
combinedPlot = new CombinedDomainXYPlot(sharedDomainAxis);
combinedPlot.setFixedDomainAxisSpace(space);
combinedPlot.setGap(-1);
combinedPlot.setOrientation(PlotOrientation.HORIZONTAL);
XYItemRenderer renderer = new StandardXYItemRenderer();
for (int i = 0; i < checkBox.length; i++) {
subPlot[i] = new XYPlot(dataset[i], null, rangeAxis, renderer);
subPlot[i].setDataset(0, dataset[i]);
subPlot[i].setRenderer(0, renderer);
subPlot[i].setOrientation(PlotOrientation.HORIZONTAL);
subPlot[i].setRangeAxis(rangeAxis);
subPlot[i].getRangeAxis().setVisible(false);
subPlot[i].setOutlinePaint(Color.BLACK);
}
JFreeChart chart = new JFreeChart(combinedPlot);
chart.removeLegend();
chart.setAntiAlias(false);
chart.setBackgroundPaint(Color.LIGHT_GRAY);
chart.setBorderVisible(false);
ChartPanel panel = new ChartPanel(chart);
panel.setVisible(true);
return panel;
}
/**
*
*/
private void updateUI() {
Dimension dimension = frame.getSize();
int newHeight = dimension.height;
int newWidth = checkBoxPanel.getMinimumSize().width + depthColumnWidth;
/* Retrieve all currently shown subplots. */
CopyOnWriteArrayList<XYPlot> subPlots = new CopyOnWriteArrayList<>(combinedPlot.getSubplots());
/* Remove all currently shown suplots. */
subPlots.forEach((plot) -> {
combinedPlot.remove(plot);
});
/* Iterate through each checkbox to check whether corresponding subplot is visible; if so, add the subplot to the combined plot. */
for (int i = 0; i < checkBox.length; i++) {
if (checkBox[i].isSelected()) {
combinedPlot.add(subPlot[i]);
newWidth += dataColumnWidth;
}
}
frame.setSize(new Dimension(newWidth, newHeight));
chartPanel.setPreferredSize(new Dimension(newWidth - checkBoxPanel.getMinimumSize().width, newHeight));
frame.revalidate();
}
private void generateAndPopulateDataset() {
for (int i = 0; i < checkBox.length; i++) {
dataset[i] = new TestDataset();
Random random = new Random();
for (int j = 0; j <= 500; j++) {
((TestDataset) dataset[i]).setYValue(25 + random.nextInt(50), j);
}
}
}
class TestDataset extends AbstractXYDataset {
int[] values = new int[501];
@Override
public final Number getX(int series, int item) {
return getXValue(0, item);
}
@Override
public final double getXValue(int series, int item) {
return item - 1;
}
public final void setYValue(int value, int item) {
values[item] = value;
}
@Override
public final Number getY(int series, int item) {
return getYValue(0, item);
}
@Override
public final double getYValue(int series, int item) {
return values[item];
}
@Override
public final DomainOrder getDomainOrder() { return DomainOrder.ASCENDING; }
@Override
public final int getItemCount(int series) { return 500; }
@Override
public final int getSeriesCount() { return 1; }
@Override
public final Comparable getSeriesKey(int series) { return ""; }
@Override
public final int indexOf(Comparable seriesKey) { return 0; }
@Override
public final void addChangeListener(DatasetChangeListener listener) {}
@Override
public final void removeChangeListener(DatasetChangeListener listener) {}
@Override
public final DatasetGroup getGroup() { return null; }
@Override
public final void setGroup(DatasetGroup group) {}
}
}
Beim Checken der CheckBoxen links im weißen Panel wird jeweiliger subplot rechts im grauen Panel im CombinedDomainXYPlot angezeigt. Beim Unchecken wird jeweiliger subplot versteckt.
Folgendes ist zu beobachten und unerwünscht:
- Enthält der CombinedDomainXYPlot keine subplots (wie beim Start der Anwendung), ist die Depth-Domaine leicht nach rechts außerhalb des Frames verschoben.
- Wird der erste Plot durch eine CheckBox dargestellt, so rutscht zwar die Depth-Domain in die richtige Position, aber der subplot wird schmaler, als mit der Konstante dataColumnWidth vorgegeben, dargestellt.
- Werden immer mehr subplots über die CheckBoxen sichtbar gemacht, so nimmt die Breite der einzelnen subplots sukzessive zu.
A. Die geteilte Domaine (Depth (.m) mit jeweiligen ticks marks und tick labels) im grauen Panel soll standardmäßig immer angezeigt werden, egal ob momentan subplots angezeigt werden oder nicht.
B. Die Depth-Domaine hat immer die gleiche Breite vom linken Rand des grauen Bereichs bis hin zur eigentlichen Achse mit den ticks.
C. Die Breite der subplots soll immer konstant (= dataColumnWidth) sein.
B. Die Depth-Domaine hat immer die gleiche Breite vom linken Rand des grauen Bereichs bis hin zur eigentlichen Achse mit den ticks.
C. Die Breite der subplots soll immer konstant (= dataColumnWidth) sein.
Da JFreeChart keine richtigen Lösungen für das pixelgenaue Platzieren von subplots ermöglicht, sei mal hier die Frage an den JFreeChart-affinen Teil der Community gerichtet, wie sich die Punkte 1 bis 3 unter Berücksichtigung der Kriterien A bis C bewerkstelligen lässt.
Cheers-