/*
 * GRAL: GRAphing Library for Java(R)
 *
 * (C) Copyright 2009-2016 Erich Seifert ,
 * Michael Seifert 
 *
 * This file is part of GRAL.
 *
 * GRAL is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * GRAL is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with GRAL.  If not, see .
 */
package de.erichseifert.gral.plots;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

import de.erichseifert.gral.data.DataSource;
import de.erichseifert.gral.data.DataTable;
import de.erichseifert.gral.data.Row;
import de.erichseifert.gral.data.statistics.Statistics;
import de.erichseifert.gral.graphics.AbstractDrawable;
import de.erichseifert.gral.graphics.Drawable;
import de.erichseifert.gral.graphics.DrawingContext;
import de.erichseifert.gral.plots.axes.Axis;
import de.erichseifert.gral.plots.axes.AxisRenderer;
import de.erichseifert.gral.plots.colors.ColorMapper;
import de.erichseifert.gral.plots.colors.ContinuousColorMapper;
import de.erichseifert.gral.plots.colors.Grayscale;
import de.erichseifert.gral.plots.points.AbstractPointRenderer;
import de.erichseifert.gral.plots.points.PointData;
import de.erichseifert.gral.util.GraphicsUtils;
import de.erichseifert.gral.util.PointND;


/**
 * 

Class that displays two coordinate values and a value as a raster of * boxes. The data source must provide at least three columns:

*
    *
  • x coordinate
  • *
  • y coordinate
  • *
  • value
  • *
*

The method {@link #createRasterData(DataSource)} can be used to convert * a matrix of values to the (coordinates, value) format.

* *

To create a new {@code RasterPlot} simply create a new instance using * a suitable data source. Example:

*
 * DataTable data = new DataTable(Double.class, Double.class);
 * data.add(10.98, -12.34);
 * data.add( 7.65,  45.67);
 * data.add(43.21,  89.01);
 * DataSource rasterData = RasterPlot.createRasterData(data);
 * RasterPlot plot = new RasterPlot(rasterData);
 * 
*/ public class RasterPlot extends XYPlot { /** Version id for serialization. */ private static final long serialVersionUID = 5844862286358250831L; /** Offset of the raster pixels to the origin. */ private final Point2D offset; /** Size of the raster pixels. */ private final Dimension2D distance; /** Color mapping to fill the raster pixels. */ private ColorMapper colors; /** * Class that renders the grid points of a {@code RasterPlot}. */ protected static class RasterRenderer extends AbstractPointRenderer { /** Version id for serialization. */ private static final long serialVersionUID = 1266585364126459761L; /** Plot specific settings. */ private final RasterPlot plot; /** Horizontal position of the current raster pixel. */ private int xColumn; /** Vertical position of the current raster pixel. */ private int yColumn; /** Value of the current raster pixel. */ private int valueColumn; /** * Constructor that creates a new instance and initializes it with a * plot as data provider. The default columns for (x, y, value) are set * to (0, 1, 2) * @param plot Plot storing global settings. */ public RasterRenderer(RasterPlot plot) { this.plot = plot; xColumn = 0; yColumn = 1; valueColumn = 2; } /** * Returns the index of the column which is used for the x coordinate * of a point. * @return Index of the column for the x coordinate of a point. */ public int getXColumn() { return xColumn; } /** * Sets the index of the column which will be used for the x coordinate * of a point. * @param columnIndex Index of the column for the x coordinate of a point. */ public void setXColumn(int columnIndex) { this.xColumn = columnIndex; } /** * Returns the index of the column which is used for the y coordinate * of a point. * @return Index of the column for the y coordinate of a point. */ public int getYColumn() { return yColumn; } /** * Sets the index of the column which will be used for the y coordinate * of a point. * @param columnIndex Index of the column for the y coordinate of a point. */ public void setYColumn(int columnIndex) { this.yColumn = columnIndex; } /** * Returns the index of the column which is used for the value of a * point. * @return Index of the column for the value of a point. */ @Override public int getValueColumn() { return valueColumn; } /** * Sets the index of the column which will be used for the value of a * point. * @param columnIndex Index of the column for the value of a point. */ @Override public void setValueColumn(int columnIndex) { this.valueColumn = columnIndex; } @Override public Drawable getPoint(final PointData data, final Shape shape) { return new AbstractDrawable() { /** Version id for serialization. */ private static final long serialVersionUID = -1136689797647794969L; public void draw(DrawingContext context) { RasterRenderer renderer = RasterRenderer.this; Axis axisX = data.axes.get(0); Axis axisY = data.axes.get(1); AxisRenderer axisXRenderer = data.axisRenderers.get(0); AxisRenderer axisYRenderer = data.axisRenderers.get(1); Row row = data.row; int colX = renderer.getXColumn(); if (colX < 0 || colX >= row.size() || !row.isColumnNumeric(colX)) { return; } int colY = renderer.getYColumn(); if (colY < 0 || colY >= row.size() || !row.isColumnNumeric(colY)) { return; } int colValue = renderer.getValueColumn(); if (colValue < 0 || colValue >= row.size() || !row.isColumnNumeric(colValue)) { return; } double valueX = ((Number) row.get(colX)).doubleValue(); double valueY = ((Number) row.get(colY)).doubleValue(); Number value = (Number) row.get(colValue); // Pixel dimensions double xMin = axisXRenderer .getPosition(axisX, valueX - 0.5, true, false) .get(PointND.X); double xMax = axisXRenderer .getPosition(axisX, valueX + 0.5, true, false) .get(PointND.X); double width = Math.abs(xMax - xMin) + 1.0; double yMin = axisYRenderer .getPosition(axisY, valueY - 0.5, true, false) .get(PointND.Y); double yMax = axisYRenderer .getPosition(axisY, valueY + 0.5, true, false) .get(PointND.Y); double height = Math.abs(yMax - yMin) + 1.0; // Create shape for pixel // The origin of all shapes is (boxX, boxY) Rectangle2D shapeBounds = shape.getBounds2D(); AffineTransform tx = new AffineTransform(); tx.scale(width/shapeBounds.getWidth(), height/shapeBounds.getHeight()); tx.translate(-shapeBounds.getMinX(), -shapeBounds.getMinY()); Shape pixel = tx.createTransformedShape(shape); // Paint pixel Graphics2D graphics = context.getGraphics(); ColorMapper colorMapper = plot.getColors(); Paint paint; if (colorMapper instanceof ContinuousColorMapper) { paint = ((ContinuousColorMapper) colorMapper) .get(value.doubleValue()); } else if (colorMapper != null) { Integer index = value.intValue(); paint = colorMapper.get(index); } else { paint = Color.BLACK; } GraphicsUtils.fillPaintedShape( graphics, pixel, paint, pixel.getBounds2D()); } }; } /** * Returns a {@code Shape} instance that can be used for further * calculations. * @param data Information on axes, renderers, and values. * @return Outline that describes the point's shape. */ public Shape getPointShape(PointData data) { return getShape(); } /** * Returns a graphical representation of the value label to be drawn for * the specified data value. * @param data Information on axes, renderers, and values. * @param shape Outline that describes the bounds for the value label. * @return Component that can be used to draw the value label. */ public Drawable getValue(final PointData data, final Shape shape) { Drawable drawable = new AbstractDrawable() { /** Version id for serialization. */ private static final long serialVersionUID = -8402945980942955359L; public void draw(DrawingContext context) { // TODO Implement rendering of value label } }; return drawable; } } /** * Initializes a new box-and-whisker plot with the specified data source. * @param data Data to be displayed. */ public RasterPlot(DataSource data) { offset = new Point2D.Double(); distance = new de.erichseifert.gral.graphics.Dimension2D.Double(1.0, 1.0); colors = new Grayscale(); ((XYPlotArea2D) getPlotArea()).setMajorGridX(false); ((XYPlotArea2D) getPlotArea()).setMajorGridY(false); //getAxisRenderer(AXIS_X).setSetting(AxisRenderer.TICKS, false); //getAxisRenderer(AXIS_Y).setSetting(AxisRenderer.TICKS, false); getAxisRenderer(AXIS_X).setIntersection(-Double.MAX_VALUE); getAxisRenderer(AXIS_Y).setIntersection(-Double.MAX_VALUE); // Store data add(data); // Adjust axes to the data series autoscaleAxes(); } @Override public void autoscaleAxis(String axisName) { if (AXIS_X.equals(axisName) || AXIS_Y.equals(axisName)) { Dimension2D dist = getDistance(); // In case we get called before settings defaults have been set, // just set distance to a sane default if (dist == null) { dist = new de.erichseifert.gral.graphics.Dimension2D.Double(1.0, 1.0); } Axis axis = getAxis(axisName); if (axis == null || !axis.isAutoscaled()) { return; } double min = getAxisMin(axisName); double max = getAxisMax(axisName); if (AXIS_X.equals(axisName)) { axis.setRange(min, max + dist.getWidth()); } else if (AXIS_Y.equals(axisName)) { axis.setRange(min - dist.getHeight(), max); } } else { super.autoscaleAxis(axisName); } } /** * Takes a matrix of values and creates a new data source that stores the * values in (x, y, value) format. * @param data Original data source with values in each cell. * @return New data source with (x, y, value) columns */ @SuppressWarnings("unchecked") public static DataSource createRasterData(DataSource data) { if (data == null) { throw new NullPointerException("Cannot convert null data source."); } DataTable coordsValueData = new DataTable(Double.class, Double.class, Double.class); // Generate pixel data with (x, y, value) double min = ((Number) data.getRowStatistics(Statistics.MIN). getColumnStatistics(Statistics.MIN).get(0, 0)).doubleValue(); double max = ((Number) data.getRowStatistics(Statistics.MAX). getColumnStatistics(Statistics.MAX).get(0, 0)).doubleValue(); double range = max - min; int i = 0; for (Comparable cell : data) { int x = i%data.getColumnCount(); int y = -i/data.getColumnCount(); double v = Double.NaN; if (cell instanceof Number) { Number numericCell = (Number) cell; v = (numericCell.doubleValue() - min) / range; } coordsValueData.add((double) x, (double) y, v); i++; } return coordsValueData; } @Override public void add(int index, DataSource source, boolean visible) { if (getData().size() > 0) { throw new IllegalArgumentException( "This plot type only supports a single data source."); //$NON-NLS-1$ } // Add data source super.add(index, source, visible); // Adjust rendering // FIXME: Overwrites possible present point and line renderers setLineRenderers(source, null); setPointRenderers(source, new RasterRenderer(this)); } /** * Returns the horizontal and vertical offset of the raster from the * origin. * @return Horizontal and vertical offset of the raster from the origin. */ public Point2D getOffset() { return offset; } /** * Sets the horizontal and vertical offset of the raster from the * origin. * @param offset Horizontal and vertical offset of the raster from the * origin. */ public void setOffset(Point2D offset) { this.offset.setLocation(offset); } /** * Returns the horizontal and vertical distance of the raster elements. * @return Horizontal and vertical distance of the raster elements. */ public Dimension2D getDistance() { return distance; } /** * Returns the horizontal and vertical distance of the raster elements. * @param distance Horizontal and vertical distance of the raster elements. */ public void setDistance(Dimension2D distance) { this.distance.setSize(distance); } /** * Returns the object which is used to map pixel values to colors. * @return Object which is used to map pixel values to colors. */ public ColorMapper getColors() { return colors; } /** * Sets the object which will be used to map pixel values to colors. * @param colors Object which will be used to map pixel values to colors. */ public void setColors(ColorMapper colors) { this.colors = colors; } }

Ads help maintain this website.