颜色与图例不同步

问题描述:

我有一个3D数据集XYZDataset,我想通过保留(x,y)坐标并通过使用多种颜色表示z轴来将其绘制为2D图.

I have a 3D dataset XYZDataset that I want to plot as a 2D plot, by keeping (x,y) coordinates and by representing the z axis using a spectrum of colors.

基于此示例,这是我的绘图类以及光谱颜色类.

Based on this example, here is my ploting class along with the spectrum color class.

package com.ingilab.algo.comparator.tools.plot;

import java.awt.Color;
import java.awt.Paint;
import java.awt.Shape;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.AxisLocation;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.PaintScale;
import org.jfree.chart.renderer.xy.XYBlockRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.title.PaintScaleLegend;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.XYZDataset;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.RectangleInsets;
import org.jfree.util.ShapeUtilities;

public class Plot2D extends ApplicationFrame {

    private static final int N = 100;

    /**
     * A demonstration application showing an XY series containing a null value.
     *
     * @param title  the frame title.
     */
    final XYSeries series;

    public Plot2D(final String title, String X, String Y, XYSeries series) {

        super(title);
        this.series = series;

        final XYSeriesCollection data = new XYSeriesCollection(series);

        final JFreeChart chart = ChartFactory.createScatterPlot(
            title,
            X, 
            Y, 
            data,
            PlotOrientation.VERTICAL,
            true,
            true,
            false
        );

        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
        setContentPane(chartPanel);

    }

    public Plot2D (final String title, JFreeChart chart) {
        super(title);
        series = null;

        final ChartPanel chartPanel = new ChartPanel(chart);
        chartPanel.setPreferredSize(new java.awt.Dimension(500, 270));
        setContentPane(chartPanel); 
    }


    /** 
     * Creates a sample chart. 
     *  
     * @param dataset  the dataset. 
     * @param max 
     *  
     * @return A sample chart. 
     */ 
    public static JFreeChart createChart(XYZDataset dataset, 
            String title, String x, String y, String z, double max) { 
        NumberAxis xAxis = new NumberAxis(x); 
        xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); 
        xAxis.setLowerMargin(0.0); 
        xAxis.setUpperMargin(0.0); 
        NumberAxis yAxis = new NumberAxis(y); 
        yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); 
        yAxis.setLowerMargin(0.0); 
        yAxis.setUpperMargin(0.0); 

        XYBlockRenderer renderer = new XYBlockRenderer();
        SpectrumPaintScale scale = new SpectrumPaintScale(0, max);
        //PaintScale scale = new GrayPaintScale(-2.0, 1.0); 
        renderer.setPaintScale(scale); 

        //Z axis
        NumberAxis scaleAxis = new NumberAxis(z);
        scaleAxis.setAxisLinePaint(Color.white);
        scaleAxis.setTickMarkPaint(Color.white);
        PaintScaleLegend legend = new PaintScaleLegend(scale, scaleAxis);
        legend.setSubdivisionCount(128);
        legend.setAxisLocation(AxisLocation.TOP_OR_RIGHT);
        legend.setPadding(new RectangleInsets(10, 10, 10, 10));
        legend.setStripWidth(20);
        legend.setPosition(RectangleEdge.RIGHT);
        legend.setBackgroundPaint(Color.WHITE);

        XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); 
        plot.setBackgroundPaint(Color.lightGray); 
        plot.setDomainGridlinesVisible(false); 
        plot.setRangeGridlinePaint(Color.white); 
        plot.setRenderer(new XYLineAndShapeRenderer(false, true) {

            @Override
            public Shape getItemShape(int row, int col) {
                    return ShapeUtilities.createDiagonalCross(5, 2);
            }
        });

        JFreeChart chart = new JFreeChart(title, plot); 
        chart.addSubtitle(legend);
        chart.removeLegend(); 
        chart.setBackgroundPaint(Color.white); 

        return chart; 
    } 


    ////////////////////////////////////
    //                                //
    //         PaintScaleColor        //
    //                                //
    ////////////////////////////////////

    private static class SpectrumPaintScale implements PaintScale {

        private static final float H1 = 0f;
        private static final float H2 = 1f;
        private final double lowerBound;
        private final double upperBound;

        public SpectrumPaintScale(double lowerBound, double upperBound) {
            this.lowerBound = lowerBound;
            this.upperBound = upperBound;
        }

        @Override
        public double getLowerBound() {
            return lowerBound;
        }

        @Override
        public double getUpperBound() {
            return upperBound;
        }

        @Override
        public Paint getPaint(double value) {
            float scaledValue = (float) (value / (getUpperBound() - getLowerBound()));
            float scaledH = H1 + scaledValue * (H2 - H1);
            return Color.getHSBColor(scaledH, 1f, 1f);
        }
    }
    public static void main(String[] args) 
    {   
        final DefaultXYZDataset timePerSizePerChrno = new 
        DefaultXYZDataset();

        ydb [0][1] = 1;
        ydb [1][1] = 78.0;
        ydb [2][1] = 1341.0;

        ydb [0][2] = 2;
        ydb [1][2] = 100.0;
        ydb [2][2] = 475.0;

        ydb [0][1] = 3;
        ydb [1][1] = 9215.0;
        ydb [2][1] = 684.0;

        ydb [0][1] = 4;
        ydb [1][1] = 90.0;
        ydb [2][1] = 251.0;

        ydb [0][1] = 5;
        ydb [1][1] = 75.0;
        ydb [2][1] = 7022.0;

        double maxZ = 7022;
        timePerSizePerChrno.addSeries("Series", ydb);

        //////////////////////////////////////////
        //         PLOTING RESUlTS              // 
        //////////////////////////////////////////
        final Plot2D plot3 = new Plot2D("Loading Performance Color Map", Plot2D.createChart (timePerSizePerChrno, 
                "Loading Performance Color Map", "Order Call", "Time in Ms", "Size in Ko", maxZ));
        plot3.pack();
        RefineryUtilities.centerFrameOnScreen(plot3);
        plot3.setVisible(true); 
    }
}

我遇到的问题是颜色光谱应用于XYZDataset系列而不是z值(我的数据集中有一个唯一的序列).

The problem I am having is that the spectrum of colors is applied on the XYZDataset series and not on the z values (I have one unique serie in my dataset).

例如上图.您会看到所有点都是红色,我希望它们根据它们的值映射到右侧的光谱.我还想删除光谱末尾的红色,因为它可能会造成混淆(光谱以红色开始和结束).

For instance on the above image. You can see that all points are in red and I want them to be mapped to the spectrum of color on the right based on their values. I also want to remove the red at the end of the spectrum since it can be confusing (the spectrum starts and finishes with the red color).

关于给定系列的任何猜测,只要知道z值在[0,maxZ]之间,就可以使用一种光谱绘制不同的点(x,y).

Any guess on how for a given series, plot the different point (x,y) using a spectrum of color knowing that z values are between [0, maxZ].

您更新后的示例创建了XYBlockRenderer,如此处,然后将自定义PaintScale应用于呈现器;该比例尺还用于创建匹配的PaintScaleLegend.在使用XYBlockRenderer创建XYPlot之后,原始的XYBlockRenderer被丢弃,并替换为XYLineAndShapeRenderer,它覆盖了getItemShape().新的XYLineAndShapeRendererPaintScale一无所知.

Your updated example creates an XYBlockRenderer, as shown here, and applies a custom PaintScale to the renderer; the scale is also used to create a matching PaintScaleLegend. After using the XYBlockRenderer to create an XYPlot, the original XYBlockRenderer is discarded and replaced with an XYLineAndShapeRenderer, which overrides getItemShape(). The new XYLineAndShapeRenderer knows nothing about the PaintScale.

相反,请在XYLineAndShapeRenderer中覆盖getItemFillPaint(),如此处所示.代替显示的List<Color>,使用自定义PaintScalegetPaint()方法根据其对应的 z 值为每个数据点插入所需的Shape颜色.

Instead, override getItemFillPaint() in your XYLineAndShapeRenderer, as shown here. Instead of the List<Color> shown, use the getPaint() method of your custom PaintScale to interpolate the desired Shape color for each data point based on its corresponding z value.

XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true) {
    @Override
    public Paint getItemFillPaint(int row, int col) {
        return scale.getPaint(dataset.getZValue(row, col));
    }
    …
};

此外,

  • 要获得不同的光谱,请在PaintScale中指定所需的边界色调.

  • To get a different spectrum, specify the desired boundary hues in the PaintScale.

private static final float H1 = 0f;
private static final float H2 = (float) (Math.PI / 8);

  • 使用DatasetUtils.findZBounds()确定数据集范围.

  • Use DatasetUtils.findZBounds() to determine the dataset range.

    Range r = DatasetUtils.findZBounds(dataset);
    

  • 仅在的Swing GUI对象="nofollow noreferrer">事件分配线程.

  • Construct and manipulate Swing GUI objects only on the event dispatch thread.

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Paint;
    import java.awt.Shape;
    import javax.swing.JFrame;
    import org.jfree.chart.ChartPanel;
    import org.jfree.chart.JFreeChart;
    import org.jfree.chart.axis.AxisLocation;
    import org.jfree.chart.axis.NumberAxis;
    import org.jfree.chart.plot.XYPlot;
    import org.jfree.chart.renderer.PaintScale;
    import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
    import org.jfree.chart.title.PaintScaleLegend;
    import org.jfree.data.Range;
    import org.jfree.data.xy.DefaultXYZDataset;
    import org.jfree.data.xy.XYZDataset;
    import org.jfree.chart.ui.RectangleEdge;
    import org.jfree.chart.ui.RectangleInsets;
    import org.jfree.chart.util.ShapeUtils;
    import org.jfree.data.general.DatasetUtils;
    
    /**
     * @see https://stackoverflow.com/a/54180207/230513
     * @see https://stackoverflow.com/a/37235165/230513
     */
    public class Plot2D {
    
        public static JFreeChart createChart(XYZDataset dataset,
            String title, String x, String y, String z, Range r) {
            NumberAxis xAxis = new NumberAxis(x);
            xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
            NumberAxis yAxis = new NumberAxis(y);
            yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
            SpectrumPaintScale scale = new SpectrumPaintScale(r);
            NumberAxis scaleAxis = new NumberAxis(z);
            scaleAxis.setAxisLinePaint(Color.white);
            scaleAxis.setTickMarkPaint(Color.white);
            PaintScaleLegend legend = new PaintScaleLegend(scale, scaleAxis);
            legend.setSubdivisionCount(128);
            legend.setAxisLocation(AxisLocation.TOP_OR_RIGHT);
            legend.setPadding(new RectangleInsets(10, 10, 10, 10));
            legend.setStripWidth(20);
            legend.setPosition(RectangleEdge.RIGHT);
            legend.setBackgroundPaint(Color.WHITE);
            XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(false, true) {
                @Override
                public Paint getItemFillPaint(int row, int col) {
                    return scale.getPaint(dataset.getZValue(row, col));
                }
    
                @Override
                public Shape getItemShape(int row, int col) {
                    return ShapeUtils.createDiagonalCross(5, 2);
                }
            };
            renderer.setUseFillPaint(true);
            renderer.setSeriesShapesFilled(0, true);
            renderer.setSeriesShapesVisible(0, true);
            XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
            plot.setBackgroundPaint(Color.lightGray);
            plot.setDomainGridlinesVisible(false);
            plot.setRangeGridlinePaint(Color.white);
            plot.setRenderer((renderer));
    
            JFreeChart chart = new JFreeChart(title, plot);
            chart.addSubtitle(legend);
            chart.removeLegend();
            chart.setBackgroundPaint(Color.white);
    
            return chart;
        }
    
        private static class SpectrumPaintScale implements PaintScale {
    
            private static final float H1 = 0f;
            private static final float H2 = (float) (Math.PI / 8);
            private final Range range;
    
            public SpectrumPaintScale(Range r) {
                this.range = r;
            }
    
            @Override
            public double getLowerBound() {
                return range.getLowerBound();
            }
    
            @Override
            public double getUpperBound() {
                return range.getUpperBound();
            }
    
            @Override
            public Paint getPaint(double value) {
                float scaledValue = (float) (value / (getUpperBound() - getLowerBound()));
                float scaledH = H1 + scaledValue * (H2 - H1);
                return Color.getHSBColor(scaledH, 1f, 1f);
            }
        }
    
        public static void main(String[] args) {
            double xyz[][] = {
                {    1,    2,    3,    4,    5 }, // x
                { 1000, 3000, 9215, 4000, 1000 }, // y
                { 1341,  500, 3125, 1000, 7022 }  // z
            };
            final DefaultXYZDataset dataset = new DefaultXYZDataset();
            dataset.addSeries("Series", xyz);
            Range r = DatasetUtils.findZBounds(dataset);
            EventQueue.invokeLater(() -> {
                JFrame f = new JFrame("Color Map");
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                JFreeChart chart = Plot2D.createChart(dataset, "Color Map",
                    "Order Call", "Time in Ms", "Size in Ko", r);
                f.add(new ChartPanel(chart) {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(600, 300);
                    }
                });
                f.pack();
                f.setLocationRelativeTo(null);
                f.setVisible(true);
            });
        }
    }