Class ImageRenderer

java.lang.Object
org.apache.sis.coverage.grid.ImageRenderer

public class ImageRenderer extends Object
A builder for the rendered image to be returned by GridCoverage.render(GridExtent). This builder does not copy any sample values. Instead, it wraps existing data arrays into Raster objects by computing required information such as pixel stride, scanline stride and band offsets. Different setData(…) methods are provided for allowing to specify the data arrays from different objects such as Java2D DataBuffer or NIO Buffer.

All setData(…) methods assume that the first valid element in each array is the value located at GridCoverage.getGridGeometry().getExtent().getLow(). This ImageRenderer class computes automatically the offsets from that position to the position of the first value included in the sliceExtent given to the constructor.

Usage example:

Limitations

Current implementation constructs only images made of a single tile. Support for tiled images will be added in a future version.
Since:
1.0
Version:
1.3
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    private static final class 
    A BufferedImage which will compute the "org.apache.sis.GridGeometry" property when first needed.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    private int[]
    Offset to add to index of sample values in each band in order to reach the value in the DataBuffer bank.
    private final SampleDimension[]
    The sample dimensions, to be used for defining the bands.
    private int[]
    Bank indices for each band, or null for 0, 1, 2, 3….
    private DataBuffer
    The data to render, or null if not yet specified.
    The colors to use for each category.
    private final GridGeometry
    The grid geometry of the GridCoverage specified at construction time.
    private final int[]
    The dimensions to select in the grid coverage for producing an image.
    private final int
    Height (number of pixels in a column) of the image to render.
    private GridGeometry
    The result of getImageGeometry(int) if the specified number of dimension 2.
    private final int
    Pixel coordinates of the image upper-left corner, as an offset relative to the sliceExtent.
    private final int
    Pixel coordinates of the image upper-left corner, as an offset relative to the sliceExtent.
    private static final org.opengis.referencing.operation.MathTransformFactory
    The factory to use for MathTransform creations, or null for a default factory.
    private final long
    Location of the first image pixel relative to the grid coverage extent.
    private final long
    Location of the first image pixel relative to the grid coverage extent.
    private final long
    Offset to add to buffer offset for reaching the first sample value for the slice to render.
    private final int
    Number of data elements between two samples for the same band on the same line.
    The properties to give to the image, or null if none.
    private final int
    Number of data elements between a given sample and the corresponding sample in the same column of the next line.
    private final GridExtent
    The requested slice, or null if unspecified.
    private int
    Multiplication factor for pixelStride and scanlineStride.
    private int
    The band to use for defining pixel colors when the image is displayed on screen.
    private final int
    Width (number of pixels in a row) of the image to render.
  • Constructor Summary

    Constructors
    Constructor
    Description
    ImageRenderer(GridCoverage coverage, GridExtent sliceExtent)
    Creates a new image renderer for the given slice extent.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    addProperty(String key, Object value)
    Adds a value associated to a property.
    Creates an image with the data specified by the last call to a setData(…) method.
    Creates a raster with the data specified by the last call to a setData(…) method.
    private void
    ensureExpectedBandCount(int n, boolean acceptOne)
    Ensures that the given number is equal to the expected number of bands.
    final Rectangle
    Returns the location of the image upper-left corner together with the image size.
    getImageGeometry(int dimCRS)
    Computes the conversion from pixel coordinates to CRS, together with the geospatial envelope of the image.
    final int
    Returns the number of bands that the image will have.
    Returns the value associated to the given property.
    final int[]
    The dimensions to select in the grid coverage for producing an image.
    private boolean
    isSameGeometry(int dimCRS)
    Returns true if a getImageGeometry(int) request for the given number of CRS dimensions can return geometry directly.
    void
    Specifies the colors to apply for each category in a sample dimension.
    void
    Sets the data as a Java2D buffer.
    void
    setData(DataType dataType, Buffer... data)
    Sets the data as NIO buffers.
    void
    setData(Vector... data)
    Sets the data as vectors.
    void
    setInterleavedPixelOffsets(int pixelStride, int[] bandOffsets)
    Specifies the offsets to add to sample index in each band in order to reach the sample value in the DataBuffer bank.
    void
    setVisibleBand(int band)
    Specifies the band to use for defining pixel colors when the image is displayed on screen.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • geometry

      private final GridGeometry geometry
      The grid geometry of the GridCoverage specified at construction time. Never null.
    • sliceExtent

      private final GridExtent sliceExtent
      The requested slice, or null if unspecified. If unspecified, then the extent to use is the full coverage grid extent.
    • gridDimensions

      private final int[] gridDimensions
      The dimensions to select in the grid coverage for producing an image. This is an array of length 2 obtained by GridExtent.getSubspaceDimensions(int). The array content is almost always {0,1}, but this class should work with other dimensions too.
      See Also:
    • imageGeometry

      private GridGeometry imageGeometry
      The result of getImageGeometry(int) if the specified number of dimension 2. This is cached for avoiding to recompute this geometry if asked many times.
      See Also:
    • offsetZ

      private final long offsetZ
      Offset to add to buffer offset for reaching the first sample value for the slice to render. This is zero for a two-dimensional image, but may be greater for cube having more dimensions. Despite the "Z" letter in the field name, this field actually combines the offset for all dimensions other than X and Y.
    • offsetX

      private final long offsetX
      Location of the first image pixel relative to the grid coverage extent. The (0,0) offset means that the first pixel in the sliceExtent (specified at construction time) is the first pixel in the whole GridCoverage.
      Note: if those offsets exceed 32 bits integer capacity, then it may not be possible to build an image for given sliceExtent from a single DataBuffer, because accessing sample values would exceed the capacity of index in Java arrays. In those cases the image needs to be tiled.
    • offsetY

      private final long offsetY
      Location of the first image pixel relative to the grid coverage extent. The (0,0) offset means that the first pixel in the sliceExtent (specified at construction time) is the first pixel in the whole GridCoverage.
      Note: if those offsets exceed 32 bits integer capacity, then it may not be possible to build an image for given sliceExtent from a single DataBuffer, because accessing sample values would exceed the capacity of index in Java arrays. In those cases the image needs to be tiled.
    • imageX

      private final int imageX
      Pixel coordinates of the image upper-left corner, as an offset relative to the sliceExtent. This is initially zero (unless sliceExtent is partially outside the grid coverage extent), but a different value may be used if the given data are tiled.
      See Also:
    • imageY

      private final int imageY
      Pixel coordinates of the image upper-left corner, as an offset relative to the sliceExtent. This is initially zero (unless sliceExtent is partially outside the grid coverage extent), but a different value may be used if the given data are tiled.
      See Also:
    • width

      private final int width
      Width (number of pixels in a row) of the image to render. This is usually set to the grid extent along the first dimension having a size greater than 1.
      See Also:
    • height

      private final int height
      Height (number of pixels in a column) of the image to render. This is usually set to the grid extent along the second dimension having a size greater than 1.
      See Also:
    • pixelStride

      private final int pixelStride
      Number of data elements between two samples for the same band on the same line. This is the product of grid sizes of enclosing GridCoverage in all dimensions before the dimension of image width. This stride does not include the multiplication factor for the number of bands in a pixel interleaved sample model because whether this factor is needed or not depends on the data buffer, which is not known at construction time.
      See Also:
    • scanlineStride

      private final int scanlineStride
      Number of data elements between a given sample and the corresponding sample in the same column of the next line. This is the product of grid sizes of enclosing GridCoverage in all dimensions before the dimension of image height. This stride does not include the multiplication factor for the number of bands in a pixel interleaved sample model because whether this factor is needed or not depends on the data buffer, which is not known at construction time.
      See Also:
    • strideFactor

      private int strideFactor
      Multiplication factor for pixelStride and scanlineStride. This is the number of data elements between two samples in the data buffer. There is no direct equivalent in java.awt.image because pixel stride and scanline stride in SampleModel are pre-multiplied by this factor, but we need to keep this information separated in this builder because its value depends on which methods are invoked:
      See Also:
    • bands

      private final SampleDimension[] bands
      The sample dimensions, to be used for defining the bands.
    • bandOffsets

      private int[] bandOffsets
      Offset to add to index of sample values in each band in order to reach the value in the DataBuffer bank. This is closely related to ComponentSampleModel.bandOffsets but not identical, because of the following differences:
      • Another offset for offsetX and offsetY may need to be added before to give the bandOffsets to SampleModel constructor.
      • If null, a default value is inferred depending on whether the SampleModel to construct is banded or interleaved.
      See Also:
    • bankIndices

      private int[] bankIndices
      Bank indices for each band, or null for 0, 1, 2, 3…. If non-null, this array length must be equal to bands array length.
    • visibleBand

      private int visibleBand
      The band to use for defining pixel colors when the image is displayed on screen. All other bands, if any, will exist in the raster but be ignored at display time.
      See Also:
    • buffer

      private DataBuffer buffer
      The data to render, or null if not yet specified. If non-null, DataBuffer.getNumBanks() must be equal to bands array length.
    • colors

      private Function<Category,Color[]> colors
      The colors to use for each category. Never null. The function may return null, which means transparent. The default value is Colorizer.GRAYSCALE.
      See Also:
    • properties

      private Hashtable<String,Object> properties
      The properties to give to the image, or null if none.
      See Also:
    • mtFactory

      private static final org.opengis.referencing.operation.MathTransformFactory mtFactory
      The factory to use for MathTransform creations, or null for a default factory.

      For now this is fixed to null. But it may become a non-static, non-final field in a future version if we want to make this property configurable.

  • Constructor Details

    • ImageRenderer

      public ImageRenderer(GridCoverage coverage, GridExtent sliceExtent)
      Creates a new image renderer for the given slice extent.
      Parameters:
      coverage - the source coverage for which to build an image.
      sliceExtent - the domain from which to create an image, or null for the coverage extent.
      Throws:
      SubspaceNotSpecifiedException - if this method cannot infer a two-dimensional slice from sliceExtent.
      DisjointExtentException - if the given extent does not intersect the given coverage.
      ArithmeticException - if a stride calculation overflows the 32 bits integer capacity.
  • Method Details

    • getNumBands

      public final int getNumBands()
      Returns the number of bands that the image will have. By default, this is the number of sample dimensions in the grid coverage.
      Returns:
      the number of bands in the rendered image.
    • ensureExpectedBandCount

      private void ensureExpectedBandCount(int n, boolean acceptOne)
      Ensures that the given number is equal to the expected number of bands. The given number shall be either 1 (case of interleaved sample model) or getNumBands() (case of banded sample model).
    • getBounds

      public final Rectangle getBounds()
      Returns the location of the image upper-left corner together with the image size. The image coordinate system is relative to the sliceExtent specified at construction time: the (0,0) pixel coordinates correspond to the sliceExtent low coordinates. Consequently, the rectangle x and y coordinates are (0,0) if the image is located exactly in the area requested by sliceExtent, or is shifted as below otherwise:
      ( x, y ) = (grid coordinates of actually provided region) − (grid coordinates of requested region)
      Returns:
      the rendered image location and size (never null).
    • getXYDimensions

      public final int[] getXYDimensions()
      The dimensions to select in the grid coverage for producing an image. This is the array obtained by GridExtent.getSubspaceDimensions(2). The array content is almost always {0,1}, i.e. the 2 first dimensions in a coordinate tuple.
      Returns:
      indices of x and y coordinate values in a grid coordinate tuple.
      Since:
      1.3
    • getImageGeometry

      public GridGeometry getImageGeometry(int dimCRS)
      Computes the conversion from pixel coordinates to CRS, together with the geospatial envelope of the image. The GridGeometry returned by this method is derived from the coverage grid geometry with the following changes:
      Parameters:
      dimCRS - desired number of dimensions in the CRS. This is usually 2.
      Returns:
      conversion from pixel coordinates to CRS of the given number of dimensions, together with image bounds and geospatial envelope if possible.
      Since:
      1.1
      See Also:
    • getProperty

      public Object getProperty(String key)
      Returns the value associated to the given property. By default the only property is "org.apache.sis.GridGeometry", but more properties can be added by calls to addProperty(String, Object).
      Parameters:
      key - the property for which to get a value.
      Returns:
      value associated to the given property, or null if none.
      Since:
      1.1
    • addProperty

      public void addProperty(String key, Object value)
      Adds a value associated to a property. This method can be invoked only once for each key. Those properties will be given to the image created by the createImage() method.
      Parameters:
      key - key of the property to set.
      value - value to associate to the given key.
      Throws:
      IllegalArgumentException - if a value is already associated to the given key.
      Since:
      1.1
    • isSameGeometry

      private boolean isSameGeometry(int dimCRS)
      Returns true if a getImageGeometry(int) request for the given number of CRS dimensions can return geometry directly. This common case avoids the need for more costly computation with SliceGeometry.
    • setData

      public void setData(DataBuffer data)
      Sets the data as a Java2D buffer. The number of banks in the given buffer must be equal to the expected number of bands. In each bank, the value located at the bank offset is the value located at GridCoverage.getGridGeometry().getExtent().getLow(), as specified in class javadoc.
      Parameters:
      data - the Java2D buffer containing data for all bands.
      Throws:
      NullArgumentException - if data is null.
      MismatchedCoverageRangeException - if the given data buffer does not have the expected amount of banks.
    • setData

      public void setData(DataType dataType, Buffer... data)
      Sets the data as NIO buffers. The number of buffers must be equal to the expected number of bands. All buffers must be backed by arrays of the type specified by the dataType argument and have the same amount of remaining elements. This method wraps the underlying arrays of a primitive type into a Java2D buffer; data are not copied. For each buffer, the grid coverage data (not only the slice data) starts at buffer position and ends at that position + remaining.

      The data type must be specified in order to distinguish between the signed and unsigned types. DataType.BYTE and DataType.USHORT are unsigned, all other supported types are signed.

      Implementation note: the Java2D buffer is set by a call to setData(DataBuffer), which can be overridden by subclasses if desired.

      Parameters:
      dataType - type of data.
      data - the buffers wrapping arrays of primitive type.
      Throws:
      NullArgumentException - if data is null or one of data element is null.
      MismatchedCoverageRangeException - if the number of specified buffers is not equal to the number of bands.
      UnsupportedOperationException - if a buffer is not backed by an accessible array or is read-only.
      ArrayStoreException - if a buffer type is incompatible with dataType.
      RasterFormatException - if buffers do not have the same amount of remaining values.
      ArithmeticException - if a buffer position overflows the 32 bits integer capacity.
      Since:
      1.1
    • setData

      public void setData(Vector... data)
      Sets the data as vectors. The number of vectors must be equal to the expected number of bands. All vectors must be backed by arrays (indirectly, through buffers backed by arrays) and have the same size. This method wraps the underlying arrays of a primitive type into a Java2D buffer; data are not copied.

      Implementation note: the NIO buffers are set by a call to setData(DataType, Buffer...), which can be overridden by subclasses if desired.

      Parameters:
      data - the vectors wrapping arrays of primitive type.
      Throws:
      NullArgumentException - if data is null or one of data element is null.
      MismatchedCoverageRangeException - if the number of specified vectors is not equal to the number of bands.
      UnsupportedOperationException - if a vector is not backed by an accessible array or is read-only.
      RasterFormatException - if vectors do not have the same size.
      ArithmeticException - if a buffer position overflows the 32 bits integer capacity.
    • setInterleavedPixelOffsets

      public void setInterleavedPixelOffsets(int pixelStride, int[] bandOffsets)
      Specifies the offsets to add to sample index in each band in order to reach the sample value in the DataBuffer bank. This method should be invoked when the data given to setData(…) contains only one Vector, Buffer or DataBuffer bank, and the bands in that unique bank are interleaved.
      Example: for an image having three bands named Red (R), Green (G) and Blue (B), if the sample values are stored in a single bank in a R₀,G₀,B₀, R₁,G₁,B₁, R₂,G₂,B₂, R₃,G₃,B₃, etc. fashion, then this method should be invoked as below:
      Parameters:
      pixelStride - the number of data elements between each pixel in the data vector or buffer.
      bandOffsets - offsets to add to sample index in each band. This is typically {0, 1, 2, …}. The length of this array shall be equal to getNumBands().
    • setVisibleBand

      public void setVisibleBand(int band)
      Specifies the band to use for defining pixel colors when the image is displayed on screen. All other bands, if any, will exist in the raster but be ignored at display time. The default value is 0, the first (and often only) band.
      Implementation note: an IndexColorModel will be used for displaying the image.
      Parameters:
      band - the band to use for display purpose.
      Throws:
      IllegalArgumentException - if the given band is not between 0 (inclusive) and getNumBands() (exclusive).
      Since:
      1.2
    • setCategoryColors

      public void setCategoryColors(Function<Category,Color[]> colors)
      Specifies the colors to apply for each category in a sample dimension. The given function can return null, which means transparent. If this method is never invoked, then the default is a grayscale for quantitative categories and transparent for qualitative categories (typically "no data" values).

      Example

      the following code specifies a color palette from blue to red with white in the middle. This is useful for data with a clear 0 (white) in the middle of the range, with a minimal value equals to the negative of the maximal value.
      Parameters:
      colors - the colors to use for each category. The colors argument cannot be null, but colors.apply(Category) can return null.
      Since:
      1.2
    • createRaster

      public Raster createRaster()
      Creates a raster with the data specified by the last call to a setData(…) method. The raster upper-left corner is located at the position given by getBounds(). The returned raster is often an instance of WritableRaster, but read-only rasters are also allowed.
      Returns:
      the raster, usually (but not necessarily) an instance of WritableRaster.
      Throws:
      IllegalStateException - if no setData(…) method has been invoked before this method call.
      RasterFormatException - if a call to a Raster factory method failed.
      ArithmeticException - if a property of the raster to construct exceeds the capacity of 32 bits integers.
      Since:
      1.1
    • createImage

      public RenderedImage createImage()
      Creates an image with the data specified by the last call to a setData(…) method. The image upper-left corner is located at the position given by getBounds(). The two-dimensional image geometry is stored as a property associated to the "org.apache.sis.GridGeometry" key.

      The default implementation returns an instance of WritableRenderedImage if the createRaster() return value is an instance of WritableRaster, or a read-only RenderedImage otherwise.

      Returns:
      the image.
      Throws:
      IllegalStateException - if no setData(…) method has been invoked before this method call.
      RasterFormatException - if a call to a Raster factory method failed.
      ArithmeticException - if a property of the image to construct exceeds the capacity of 32 bits integers.
      Since:
      1.1