All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.data.BufferWrapperRaster Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.data;

import gov.nasa.worldwind.Disposable;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.cache.Cacheable;
import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.util.*;

/**
 * @author dcollins
 * @version $Id: BufferWrapperRaster.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class BufferWrapperRaster extends AbstractDataRaster implements Cacheable, Disposable
{
    protected BufferWrapper buffer;

    public BufferWrapperRaster(int width, int height, Sector sector, BufferWrapper buffer, AVList list)
    {
        super(width, height, sector, list);

        if (buffer == null)
        {
            String message = Logging.getMessage("nullValue.BufferNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        int expectedValues = width * height;
        if (buffer.length() < expectedValues)
        {
            String message = Logging.getMessage("generic.BufferSize", "buffer.length() < " + expectedValues);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.buffer = buffer;
    }

    public BufferWrapperRaster(int width, int height, Sector sector, BufferWrapper buffer)
    {
        this(width, height, sector, buffer, null);
    }

    public BufferWrapper getBuffer()
    {
        return this.buffer;
    }

    public long getSizeInBytes()
    {
        return this.buffer.getSizeInBytes();
    }

    public void dispose()
    {
    }

    public double getDoubleAtPosition(int row, int col)
    {
        if ((row < 0) || (col < 0) || (row > (this.getHeight() - 1)) || (col > (this.getWidth() - 1)))
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", String.format("%d, %d", row, col));
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        return this.getBuffer().getDouble(indexFor(col, row));
    }

    public void setDoubleAtPosition(int row, int col, double value)
    {
        if ((row < 0) || (col < 0) || (row > (this.getHeight() - 1)) || (col > (this.getWidth() - 1)))
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", String.format("%d, %d", row, col));
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.getBuffer().putDouble(indexFor(col, row), value);
    }

    public double getTransparentValue()
    {
        if (this.hasKey(AVKey.MISSING_DATA_SIGNAL))
        {
            Object o = this.getValue(AVKey.MISSING_DATA_SIGNAL);
            if (null != o && o instanceof Double)
                return (Double) o;
        }
        return Double.MAX_VALUE;
    }

    public void setTransparentValue(double transparentValue)
    {
        this.setValue(AVKey.MISSING_DATA_SIGNAL, transparentValue);
    }

    /**
     * Returns a two-element array containing this raster's extreme scalar values, ignoring any values marked as
     * missing-data. This returns null if this raster contains no values, or if it contains only values marked as
     * missing-data.
     *
     * @return a two-element array containing this raster's extreme values, or null if none exist. Entry 0 contains the
     *         minimum value; entry 1 contains the maximum value.
     */
    public double[] getExtremes()
    {
        // Create local variables to store the raster's dimensions and missing data signal to eliminate any overhead in
        // the loops below.
        int width = this.getWidth();
        int height = this.getHeight();
        double missingDataSignal = this.getTransparentValue();

        // Allocate a buffer to hold one row of scalar values.
        double[] buffer = new double[width];

        // Allocate a buffer to hold the extreme values.
        double[] extremes = null;

        for (int j = 0; j < height; j++)
        {
            this.get(0, j, width, buffer, 0); // Get the row starting at (0, j).

            for (int i = 0; i < width; i++)
            {
                if (buffer[i] == missingDataSignal) // Ignore values marked as missing-data.
                    continue;

                if (extremes == null)
                    extremes = WWUtil.defaultMinMix();

                if (extremes[0] > buffer[i])
                    extremes[0] = buffer[i];
                if (extremes[1] < buffer[i])
                    extremes[1] = buffer[i];
            }
        }

        // Extremes is null if this raster is empty, or contains only values marked as missing-data.
        return extremes;
    }

    public void fill(double value)
    {
        int width = this.getWidth();
        int height = this.getHeight();
        double[] samples = new double[width];
        java.util.Arrays.fill(samples, value);

        // Fill each row of this raster with the clear color.
        for (int j = 0; j < height; j++)
        {
            this.put(0, j, samples, 0, width);
        }
    }

    public void drawOnTo(DataRaster canvas)
    {
        if (canvas == null)
        {
            String message = Logging.getMessage("nullValue.DestinationIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (!(canvas instanceof BufferWrapperRaster))
        {
            String message = Logging.getMessage("DataRaster.IncompatibleRaster", canvas);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.doDrawOnTo((BufferWrapperRaster) canvas);
    }

    @Override
    DataRaster doGetSubRaster(int width, int height, Sector sector, AVList params)
    {
        DataRaster canvas = this.createSubRaster(width, height, sector, params);
        this.drawOnTo(canvas);
        return canvas;
    }

    /**
     * This returns a new sub-raster initialized with the specified properties. Called from doGetSubRaster
     * to create the sub-raster instance before populating its contents. This does not place any restrictions on the
     * specified width, height, sector or params.
     * 

* This returns a {@link gov.nasa.worldwind.util.BufferWrapper.ByteBufferWrapper}, a subclass of * BufferWrapperRaster backed by a ByteBuffer. * * @param width the width of the sub-raster, in pixels. * @param height the height of the sub-raster, in pixels. * @param sector the sector the sub-raster occupies. * @param params the parameters associated with the sub-raster. * * @return a new sub-raster initialized with the specified width, height, * sector and params. */ protected BufferWrapperRaster createSubRaster(int width, int height, Sector sector, AVList params) { return new ByteBufferRaster(width, height, sector, params); } protected void doDrawOnTo(BufferWrapperRaster canvas) { if (!this.getSector().intersects(canvas.getSector())) return; int thisWidth = this.getWidth(); int thisHeight = this.getHeight(); int canvasWidth = canvas.getWidth(); int canvasHeight = canvas.getHeight(); double thisTransparentValue = this.getTransparentValue(); // Compute the transform from the canvas' coordinate system to this raster's coordinate system. java.awt.geom.AffineTransform canvasToThis = this.computeSourceToDestTransform( canvasWidth, canvasHeight, canvas.getSector(), thisWidth, thisHeight, this.getSector()); /// Compute the region of the destination raster to be be clipped by the specified clipping sector. If no // clipping sector is specified, then perform no clipping. We compute the clip region for the destination // raster because this region is used to limit which pixels are rasterized to the destination. java.awt.Rectangle clipRect = new java.awt.Rectangle(0, 0, canvasWidth - 1, canvasHeight - 1); // if (clipSector != null) // { // java.awt.Rectangle rect = this.computeClipRect(clipSector, canvas); // clipRect = clipRect.intersection(rect); // } // Precompute the interpolation values for each transformed x- and y-coordinate. InterpolantLookupTable lut = this.createLookupTable( canvasWidth, canvasHeight, // lookup table dimensions 0, thisWidth - 1, 0, thisHeight - 1, // lookup table xMin, xMax, yMin, yMax canvasToThis); // lookup transform // If the lookup table is null, then no values in the canvas fall within this raster's bounds. This means // either the two rasters do not intersect or that this raster fits entirely between two x-coordinates or two // y-coordinates (or both) in the canvas. In either case, we do not rasterize any contribution from this raster // into the canvas, and simply exit. if (lut == null) return; // Allocate space to hold the lookup table parameters. double[] xParams = new double[3]; double[] yParams = new double[3]; // Compute the range of x-values in this raster that are needed during rendering. lut.computeRangeX(xParams); int xParamMin = (int) Math.floor(xParams[0]); int xParamMax = (int) Math.ceil(xParams[1]); int xParamWidth = xParamMax - xParamMin + 1; // Allocate a buffer for two rows of samples from this raster, and allocate a buffer for one row of samples // from the canvas. double[] thisSamples = new double[2 * xParamWidth]; double[] canvasSamples = new double[canvasWidth]; int x1, x2, y1, y2; double xf, yf; // Iterate over each canvas row, filling canvas pixels with samples from this raster. for (int j = clipRect.y; j <= (clipRect.y + clipRect.height); j++) { // If the interpolant lookup table has an entry for "j", then process this row. if (lut.getInterpolantY(j, yParams)) { y1 = (int) yParams[0]; y2 = (int) yParams[1]; yf = yParams[2]; // Read the two rows of image samples that straddle yf. this.get(xParamMin, y1, xParamWidth, thisSamples, 0); this.get(xParamMin, y2, xParamWidth, thisSamples, xParamWidth); // Read the canvas row samples. canvas.get(0, j, canvasWidth, canvasSamples, 0); // Iterate over each canvas column, sampling canvas pixels. for (int i = clipRect.x; i <= (clipRect.x + clipRect.width); i++) { // If the interpolant lookup table has an entry for "i", then process this column. if (lut.getInterpolantX(i, xParams)) { x1 = (int) xParams[0] - xParamMin; x2 = (int) xParams[1] - xParamMin; xf = xParams[2]; // Sample this raster with the interpolated coordinates. This produces a bi-linear mix // of the four values surrounding the canvas pixel. Place the output in the canvas sample array. sample(thisSamples, x1, x2, xf, 0, 1, yf, xParamWidth, thisTransparentValue, canvasSamples, i); } } // Write the canvas row samples. canvas.put(0, j, canvasSamples, 0, canvasWidth); } } } protected void get(int x, int y, int length, double[] buffer, int pos) { int index = this.indexFor(x, y); this.getBuffer().getDouble(index, buffer, pos, length); } protected void put(int x, int y, double[] buffer, int pos, int length) { int index = this.indexFor(x, y); this.getBuffer().putDouble(index, buffer, pos, length); } protected final int indexFor(int x, int y) { // Map raster coordinates to buffer coordinates. return x + y * this.getWidth(); } @Override protected java.awt.geom.AffineTransform computeSourceToDestTransform( int sourceWidth, int sourceHeight, Sector sourceSector, int destWidth, int destHeight, Sector destSector) { // Compute the the transform from source to destination coordinates. In this computation a pixel is assumed // to have no dimension. We measure the distance between pixels rather than some pixel dimension. double ty = (destHeight - 1) * -(sourceSector.getMaxLatitude().degrees - destSector.getMaxLatitude().degrees) / destSector.getDeltaLatDegrees(); double tx = (destWidth - 1) * (sourceSector.getMinLongitude().degrees - destSector.getMinLongitude().degrees) / destSector.getDeltaLonDegrees(); double sy = ((double) (destHeight - 1) / (double) (sourceHeight - 1)) * (sourceSector.getDeltaLatDegrees() / destSector.getDeltaLatDegrees()); double sx = ((double) (destWidth - 1) / (double) (sourceWidth - 1)) * (sourceSector.getDeltaLonDegrees() / destSector.getDeltaLonDegrees()); java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(); transform.translate(tx, ty); transform.scale(sx, sy); return transform; } @Override protected java.awt.geom.AffineTransform computeGeographicToRasterTransform(int width, int height, Sector sector) { // Compute the the transform from geographic to raster coordinates. In this computation a pixel is assumed // to have no dimension. We measure the distance between pixels rather than some pixel dimension. double ty = -sector.getMaxLatitude().degrees; double tx = -sector.getMinLongitude().degrees; double sy = -((height - 1) / sector.getDeltaLatDegrees()); double sx = ((width - 1) / sector.getDeltaLonDegrees()); java.awt.geom.AffineTransform transform = new java.awt.geom.AffineTransform(); transform.scale(sx, sy); transform.translate(tx, ty); return transform; } protected static void sample(double[] source, int x1, int x2, double xf, int y1, int y2, double yf, int width, double transparent, double[] dest, int destPos) { double ul = source[x1 + y1 * width]; double ll = source[x1 + y2 * width]; double lr = source[x2 + y2 * width]; double ur = source[x2 + y1 * width]; // If all four sample values are not transparent (or missing), then write the interpolated value to the // destination buffer. if ((ul != transparent) && (ur != transparent) && (lr != transparent) && (ll != transparent)) { dest[destPos] = ((1.0 - xf) * (1.0 - yf) * ul) + ((1.0 - xf) * (yf) * ll) + ((xf) * (yf) * lr) + ((xf) * (1.0 - yf) * ur); } } protected static class InterpolantLookupTable { protected int width; protected int height; protected double[] xParams; protected double[] yParams; public InterpolantLookupTable(int width, int height) { this.width = width; this.height = height; this.xParams = new double[3 * width]; this.yParams = new double[3 * height]; java.util.Arrays.fill(this.xParams, -1d); java.util.Arrays.fill(this.yParams, -1d); } public final boolean getInterpolantX(int x, double[] params) { params[0] = this.xParams[3 * x]; params[1] = this.xParams[3 * x + 1]; params[2] = this.xParams[3 * x + 2]; return params[0] != -1d; } public final boolean getInterpolantY(int y, double[] params) { params[0] = this.yParams[3 * y]; params[1] = this.yParams[3 * y + 1]; params[2] = this.yParams[3 * y + 2]; return params[0] != -1d; } public final void computeRangeX(double[] params) { computeInterpolantRange(this.xParams, this.width, params); } public final void computeRangeY(double[] params) { computeInterpolantRange(this.yParams, this.height, params); } protected static void computeInterpolantRange(double[] params, int size, double[] result) { double min = Double.MAX_VALUE; double max = -Double.MIN_VALUE; int index; for (int i = 0; i < size; i++) { index = 3 * i; if (params[index] != -1d) { // Compute the minimum first parameter (x1 or y1). if (params[index] < min) min = params[index]; // Compute the maximum second parameters (x2 or y2). if (params[index + 1] > max) max = params[index + 1]; } } result[0] = min; result[1] = max; } } protected InterpolantLookupTable createLookupTable(int width, int height, double xMin, double xMax, double yMin, double yMax, java.awt.geom.AffineTransform lookupTransform) { // Compute the interpolation values for each transformed x- and y-coordinate. This assumes that the transform // is composed of translations and scales (no rotations or shears). Therefore the transformed coordinates of // each row or column would be identical. InterpolantLookupTable lut = new InterpolantLookupTable(width, height); double threshold = -1e-6; // Numerical roundoff error threshold. boolean haveXParam = false; boolean haveYParam = false; java.awt.geom.Point2D thisPoint = new java.awt.geom.Point2D.Double(); java.awt.geom.Point2D canvasPoint = new java.awt.geom.Point2D.Double(); double x, y; int index; for (int i = 0; i < width; i++) { canvasPoint.setLocation(i, 0); lookupTransform.transform(canvasPoint, thisPoint); x = thisPoint.getX(); if (((x - xMin) > threshold) && ((xMax - x) > threshold)) { x = (x < xMin) ? xMin : ((x > xMax) ? xMax : x); index = 3 * i; lut.xParams[index] = Math.floor(x); lut.xParams[index + 1] = Math.ceil(x); lut.xParams[index + 2] = x - lut.xParams[index]; haveXParam = true; } } for (int j = 0; j < height; j++) { canvasPoint.setLocation(0, j); lookupTransform.transform(canvasPoint, thisPoint); y = thisPoint.getY(); if (((y - yMin) > threshold) && ((yMax - y) > threshold)) { y = (y < yMin) ? yMin : ((y > yMax) ? yMax : y); index = 3 * j; lut.yParams[index] = Math.floor(y); lut.yParams[index + 1] = Math.ceil(y); lut.yParams[index + 2] = y - lut.yParams[index]; haveYParam = true; } } if (haveXParam && haveYParam) { return lut; } return null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy