Class ImageRenderer
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.
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 ClassesModifier and TypeClassDescriptionprivate static final class
ABufferedImage
which will compute the "org.apache.sis.GridGeometry" property when first needed. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate int[]
Offset to add to index of sample values in each band in order to reach the value in theDataBuffer
bank.private final SampleDimension[]
The sample dimensions, to be used for defining the bands.private int[]
Bank indices for each band, ornull
for 0, 1, 2, 3….private DataBuffer
The data to render, ornull
if not yet specified.The colors to use for each category.private final GridGeometry
The grid geometry of theGridCoverage
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 ofgetImageGeometry(int)
if the specified number of dimension 2.private final int
Pixel coordinates of the image upper-left corner, as an offset relative to thesliceExtent
.private final int
Pixel coordinates of the image upper-left corner, as an offset relative to thesliceExtent
.private static final org.opengis.referencing.operation.MathTransformFactory
The factory to use forMathTransform
creations, ornull
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 tobuffer
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, ornull
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, ornull
if unspecified.private int
Multiplication factor forpixelStride
andscanlineStride
.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
ConstructorsConstructorDescriptionImageRenderer
(GridCoverage coverage, GridExtent sliceExtent) Creates a new image renderer for the given slice extent. -
Method Summary
Modifier and TypeMethodDescriptionvoid
addProperty
(String key, Object value) Adds a value associated to a property.Creates an image with the data specified by the last call to asetData(…)
method.Creates a raster with the data specified by the last call to asetData(…)
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.getProperty
(String key) 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) Returnstrue
if agetImageGeometry(int)
request for the given number of CRS dimensions can returngeometry
directly.void
setCategoryColors
(Function<Category, Color[]> colors) Specifies the colors to apply for each category in a sample dimension.void
setData
(DataBuffer data) Sets the data as a Java2D buffer.void
Sets the data as NIO buffers.void
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 theDataBuffer
bank.void
setVisibleBand
(int band) Specifies the band to use for defining pixel colors when the image is displayed on screen.
-
Field Details
-
geometry
The grid geometry of theGridCoverage
specified at construction time. Nevernull
. -
sliceExtent
The requested slice, ornull
if unspecified. If unspecified, then the extent to use is the full coverage grid extent. -
gridDimensions
private final int[] gridDimensionsThe dimensions to select in the grid coverage for producing an image. This is an array of length 2 obtained byGridExtent.getSubspaceDimensions(int)
. The array content is almost always {0,1}, but this class should work with other dimensions too.- See Also:
-
imageGeometry
The result ofgetImageGeometry(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 offsetZOffset to add tobuffer
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 offsetXLocation of the first image pixel relative to the grid coverage extent. The (0,0) offset means that the first pixel in thesliceExtent
(specified at construction time) is the first pixel in the wholeGridCoverage
.Note: if those offsets exceed 32 bits integer capacity, then it may not be possible to build an image for givensliceExtent
from a singleDataBuffer
, 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 offsetYLocation of the first image pixel relative to the grid coverage extent. The (0,0) offset means that the first pixel in thesliceExtent
(specified at construction time) is the first pixel in the wholeGridCoverage
.Note: if those offsets exceed 32 bits integer capacity, then it may not be possible to build an image for givensliceExtent
from a singleDataBuffer
, 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 imageXPixel coordinates of the image upper-left corner, as an offset relative to thesliceExtent
. This is initially zero (unlesssliceExtent
is partially outside the grid coverage extent), but a different value may be used if the given data are tiled. -
imageY
private final int imageYPixel coordinates of the image upper-left corner, as an offset relative to thesliceExtent
. This is initially zero (unlesssliceExtent
is partially outside the grid coverage extent), but a different value may be used if the given data are tiled. -
width
private final int widthWidth (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 heightHeight (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 pixelStrideNumber of data elements between two samples for the same band on the same line. This is the product of grid sizes of enclosingGridCoverage
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 scanlineStrideNumber 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 enclosingGridCoverage
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. -
strideFactor
private int strideFactorMultiplication factor forpixelStride
andscanlineStride
. This is the number of data elements between two samples in the databuffer
. There is no direct equivalent injava.awt.image
because pixel stride and scanline stride inSampleModel
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:- If
setInterleavedPixelOffsets(int, int[])
is invoked, this is the value given to that method. - Otherwise if
setData(DataBuffer)
is invoked and the given buffer has only one bank, then this isgetNumBands()
. - Otherwise this is 1.
- See Also:
- If
-
bands
The sample dimensions, to be used for defining the bands. -
bandOffsets
private int[] bandOffsetsOffset to add to index of sample values in each band in order to reach the value in theDataBuffer
bank. This is closely related toComponentSampleModel.bandOffsets
but not identical, because of the following differences:- Another offset for
offsetX
andoffsetY
may need to be added before to give thebandOffsets
toSampleModel
constructor. - If null, a default value is inferred depending on whether the
SampleModel
to construct is banded or interleaved.
- See Also:
- Another offset for
-
bankIndices
private int[] bankIndicesBank indices for each band, ornull
for 0, 1, 2, 3…. If non-null, this array length must be equal tobands
array length. -
visibleBand
private int visibleBandThe 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
The data to render, ornull
if not yet specified. If non-null,DataBuffer.getNumBanks()
must be equal tobands
array length. -
colors
The colors to use for each category. Nevernull
. The function may returnnull
, which means transparent. The default value isColorizer.GRAYSCALE
.- See Also:
-
properties
The properties to give to the image, ornull
if none.- See Also:
-
mtFactory
private static final org.opengis.referencing.operation.MathTransformFactory mtFactoryThe factory to use forMathTransform
creations, ornull
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
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, ornull
for thecoverage
extent.- Throws:
SubspaceNotSpecifiedException
- if this method cannot infer a two-dimensional slice fromsliceExtent
.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) orgetNumBands()
(case of banded sample model). -
getBounds
Returns the location of the image upper-left corner together with the image size. The image coordinate system is relative to thesliceExtent
specified at construction time: the (0,0) pixel coordinates correspond to thesliceExtent
low coordinates. Consequently, the rectangle x and y coordinates are (0,0) if the image is located exactly in the area requested bysliceExtent
, 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
. The array content is almost always {0,1}, i.e. the 2 first dimensions in a coordinate tuple.GridExtent.getSubspaceDimensions(2)
- Returns:
- indices of x and y coordinate values in a grid coordinate tuple.
- Since:
- 1.3
-
getImageGeometry
Computes the conversion from pixel coordinates to CRS, together with the geospatial envelope of the image. TheGridGeometry
returned by this method is derived from the coverage grid geometry with the following changes:- The number of grid dimensions is always 2.
- The number of CRS dimensions
is specified by
dimCRS
(usually 2). - The envelope may be a sub-region of the coverage envelope.
- The grid extent is the image bounds.
- The grid to CRS transform is derived from the coverage transform
with a translation for mapping the
sliceExtent
low coordinates to (0,0) pixel coordinates.
- 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
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 toaddProperty(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
Adds a value associated to a property. This method can be invoked only once for eachkey
. Those properties will be given to the image created by thecreateImage()
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) Returnstrue
if agetImageGeometry(int)
request for the given number of CRS dimensions can returngeometry
directly. This common case avoids the need for more costly computation withSliceGeometry
. -
setData
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 atGridCoverage.getGridGeometry().getExtent().getLow()
, as specified in class javadoc.- Parameters:
data
- the Java2D buffer containing data for all bands.- Throws:
NullArgumentException
- ifdata
is null.MismatchedCoverageRangeException
- if the given data buffer does not have the expected amount of banks.
-
setData
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 thedataType
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
andDataType.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
- ifdata
is null or one ofdata
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 withdataType
.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
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
- ifdata
is null or one ofdata
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 theDataBuffer
bank. This method should be invoked when the data given tosetData(…)
contains only oneVector
,Buffer
orDataBuffer
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 togetNumBands()
.
-
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: anIndexColorModel
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) andgetNumBands()
(exclusive).- Since:
- 1.2
-
setCategoryColors
Specifies the colors to apply for each category in a sample dimension. The given function can returnnull
, 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. Thecolors
argument cannot be null, butcolors.apply(Category)
can return null.- Since:
- 1.2
-
createRaster
Creates a raster with the data specified by the last call to asetData(…)
method. The raster upper-left corner is located at the position given bygetBounds()
. The returned raster is often an instance ofWritableRaster
, but read-only rasters are also allowed.- Returns:
- the raster, usually (but not necessarily) an instance of
WritableRaster
. - Throws:
IllegalStateException
- if nosetData(…)
method has been invoked before this method call.RasterFormatException
- if a call to aRaster
factory method failed.ArithmeticException
- if a property of the raster to construct exceeds the capacity of 32 bits integers.- Since:
- 1.1
-
createImage
Creates an image with the data specified by the last call to asetData(…)
method. The image upper-left corner is located at the position given bygetBounds()
. 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 thecreateRaster()
return value is an instance ofWritableRaster
, or a read-onlyRenderedImage
otherwise.- Returns:
- the image.
- Throws:
IllegalStateException
- if nosetData(…)
method has been invoked before this method call.RasterFormatException
- if a call to aRaster
factory method failed.ArithmeticException
- if a property of the image to construct exceeds the capacity of 32 bits integers.- Since:
- 1.1
-