2

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

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).

enter image description here

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).

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].

trashgod
  • 203,806
  • 29
  • 246
  • 1,045
Mr. D
  • 657
  • 1
  • 8
  • 21

1 Answers1

2

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.

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

In addition,

  • 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);
    
  • Use DatasetUtils.findZBounds() to determine the dataset range.

    Range r = DatasetUtils.findZBounds(dataset);
    
  • Construct and manipulate Swing GUI objects only on the event dispatch thread.

plot

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);
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045