/* * Geotools 2 - OpenSource mapping toolkit * (C) 2003, Geotools Project Management Committee (PMC) * (C) 2001, Institut de Recherche pour le Développement * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.cv; // Images import java.awt.Dimension; import java.awt.Image; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.ColorModel; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.renderable.ParameterBlock; import java.awt.image.renderable.RenderContext; import java.awt.image.renderable.RenderableImage; import java.lang.ref.WeakReference; import java.rmi.RemoteException; import java.rmi.ServerException; import java.rmi.server.UnicastRemoteObject; import java.util.Arrays; import java.util.Locale; import java.util.Map; import java.util.Vector; import javax.media.jai.ImageFunction; import javax.media.jai.ImageLayout; import javax.media.jai.JAI; import javax.media.jai.PlanarImage; import javax.media.jai.PropertySource; import javax.media.jai.PropertySourceImpl; import javax.media.jai.TiledImage; import javax.media.jai.iterator.RectIterFactory; import javax.media.jai.iterator.WritableRectIter; import javax.media.jai.operator.ImageFunctionDescriptor; import javax.media.jai.util.CaselessStringKey; import org.geotools.cs.AxisOrientation; import org.geotools.cs.CoordinateSystem; import org.geotools.pt.CoordinatePoint; import org.geotools.pt.Dimensioned; import org.geotools.pt.Envelope; import org.geotools.pt.Matrix; import org.geotools.resources.Utilities; import org.geotools.resources.XArray; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Errors; import org.geotools.resources.geometry.XAffineTransform; import org.geotools.resources.image.ImageUtilities; import org.opengis.coverage.CannotEvaluateException; import org.opengis.cs.CS_CoordinateSystem; import org.opengis.cv.CV_Coverage; import org.opengis.cv.CV_SampleDimension; import org.opengis.gc.GC_GridCoverage; import org.opengis.pt.PT_CoordinatePoint; import org.opengis.pt.PT_Envelope; /** * Base class of all coverage type. The essential property of coverage is to be able * to generate a value for any point within its domain. How coverage is represented * internally is not a concern. For example consider the following different internal * representations of coverage: * *
{@link #getCoordinateSystem}.getDimension();
{@link #getDimensionNames}.length;
{@link #getDimension};
{@link #getSampleDimensions()}.length
). The later
* may be better understood as the number of bands for 2D grid coverage.
* A coverage has a corresponding {@link SampleDimension} for each sample
* dimension in the coverage.
* getMetadataValue(...)
method in this implementation.
* OpenGIS's metadata are called "Properties" in Java Advanced Imaging.
* Use {@link #getProperty} instead.
*
* @source $URL$
* @version $Id$
* @author OpenGIS
* @author Martin Desruisseaux
*
* @see org.opengis.cv.CV_Coverage
*
* @deprecated Replaced by {@link org.geotools.coverage.Coverage}
* in the org.geotools.coverage
package.
*/
public abstract class Coverage extends PropertySourceImpl implements Dimensioned {
/**
* The set of default axis name.
*/
private static final String[] DIMENSION_NAMES = {"x", "y", "z", "t"};
/**
* The sample dimension to make visible by {@link #getRenderableImage}.
*/
private static final int VISIBLE_BAND = 0;
/**
* The coverage name.
*/
private final String name;
/**
* The coordinate system, or null
if there is none.
*/
protected final CoordinateSystem coordinateSystem;
/**
* OpenGIS object returned by {@link #getProxy}.
* It may be a hard or a weak reference.
*/
transient Object proxy;
/**
* Construct a coverage using the specified coordinate system. If the coordinate system
* is null
, then the subclasses must override {@link #getDimension()}.
*
* @param name The coverage name.
* @param coordinateSystem The coordinate system. This specifies the coordinate
* system used when accessing a coverage or grid coverage with the
* evaluate(...)
methods.
* @param source The source for this coverage, or null
if none.
* Source may be (but is not limited to) a {@link PlanarImage} or an
* other Coverage
object.
* @param properties The set of properties for this coverage, or null
if
* there is none. "Properties" in Java Advanced Imaging is what
* OpenGIS calls "Metadata". There is no getMetadataValue(...)
* method in this implementation. Use {@link #getProperty} instead. Keys may
* be {@link String} or {@link CaselessStringKey} objects, while values may
* be any {@link Object}.
*/
protected Coverage(final String name,
final CoordinateSystem coordinateSystem,
final PropertySource source,
final Map properties)
{
super(properties, source);
this.name = name;
this.coordinateSystem = coordinateSystem;
}
/**
* Construct a new coverage with the same
* parameters than the specified coverage.
*/
protected Coverage(final Coverage coverage) {
// NOTE: This constructor keep a strong reference to the
// source coverage (through 'PropertySourceImpl').
// In many cases, it is not a problem since GridCoverage
// will retains a strong reference to its source anyway.
super(null, coverage);
this.name = coverage.name;
this.coordinateSystem = coverage.coordinateSystem;
}
/**
* Returns the coverage name, localized for the supplied locale.
* If the specified locale is not available, returns a name in an
* arbitrary locale. The default implementation returns the name
* specified at construction time.
*
* @param locale The desired locale, or null
for a default locale.
* @return The coverage name in the specified locale, or in an arbitrary locale
* if the specified localization is not available.
*/
public String getName(final Locale locale) {
return name;
}
/**
* Returns the coordinate system. This specifies the coordinate system used when
* accessing a coverage or grid coverage with the evaluate(...)
methods.
* It is also the coordinate system of the coordinates used with the math transform
* {@link org.geotools.gc.GridGeometry#getGridToCoordinateSystem}. This coordinate
* system is usually different than the grid coordinate system of the grid. A grid
* coverage can be accessed (re-projected) with new coordinate system with the
* {@link org.geotools.gp.GridCoverageProcessor} component.
* In this case, a new instance of a grid coverage is created.
* null
.
* The {@link org.geotools.gc.GridGeometry#getGridToCoordinateSystem
* GridGeometry.gridToCoordinateSystem}) attribute should also be
* null
if the coordinate system is null
.
*
* @return The coordinate system, or null
if this coverage
* does not have an associated coordinate system.
*
* @see CV_Coverage#getCoordinateSystem()
* @see org.geotools.gc.GridGeometry#getGridToCoordinateSystem
*/
public CoordinateSystem getCoordinateSystem() {
return coordinateSystem;
}
/**
* Returns The bounding box for the coverage domain in coordinate system coordinates.
* May be null if this coverage has no associated coordinate system.
* For grid coverages, the grid cells are centered on each grid coordinate.
* The envelope for a 2-D grid coverage includes the following corner positions.
*
* * * The default implementation returns the coordinate system envelope if there is one. * * @return The bounding box for the coverage domain in coordinate system coordinates. * * @see CV_Coverage#getEnvelope() */ public Envelope getEnvelope() { final CoordinateSystem cs = getCoordinateSystem(); return (cs!=null) ? cs.getDefaultEnvelope() : null; } /** * Returns the dimension of the grid coverage. The default implementation * returns the dimension of the underlying {@link CoordinateSystem}. * * @return The number of dimensions of this coverage. */ public int getDimension() { return getCoordinateSystem().getDimension(); } /** * Returns the names of each dimension in this coverage. Typically these names * are "x", "y", "z" and "t". The number of items in the sequence is the number * of dimensions in the coverage. Grid coverages are typically 2D (x, * y) while other coverages may be 3D (x, y, * z) or 4D (x, y, z, t). * The {@linkplain #getDimension number of dimensions} of the coverage is the * number of entries in the list of dimension names. * * The default implementation ask for {@link CoordinateSystem} * axis names, or returns "x", "y"... if this coverage has no coordinate system. * * @param locale The desired locale, or* (Minimum row - 0.5, Minimum column - 0.5) for the minimum coordinates * (Maximum row - 0.5, Maximum column - 0.5) for the maximum coordinates *
null
for the default locale.
* @return The names of each dimension. The array's length is equals to {@link #getDimension}.
*
* @see CV_Coverage#getDimensionNames()
*/
public String[] getDimensionNames(final Locale locale) {
final CoordinateSystem cs = getCoordinateSystem();
if (cs != null) {
final String[] names = new String[cs.getDimension()];
for (int i=0; i{@link #getSampleDimensions()}.length
long.
* @return The dest
array, or a newly created array if dest
was null.
* @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
* More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
* failed because the input point has invalid coordinates.
*
* @see CV_Coverage#evaluateAsBoolean
*/
public boolean[] evaluate(final CoordinatePoint coord, boolean[] dest)
throws CannotEvaluateException
{
final double[] result = evaluate(coord, (double[])null);
if (dest == null) {
dest = new boolean[result.length];
}
for (int i=0; i{@link #getSampleDimensions()}.length
long.
* @return The dest
array, or a newly created array if dest
was null.
* @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
* More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
* failed because the input point has invalid coordinates.
*
* @see CV_Coverage#evaluateAsInteger
*/
public byte[] evaluate(final CoordinatePoint coord, byte[] dest)
throws CannotEvaluateException
{
final double[] result = evaluate(coord, (double[])null);
if (dest == null) {
dest = new byte[result.length];
}
for (int i=0; inull
to
* create a new array. If non-null, this array must be at least
* {@link #getSampleDimensions()}.length
long.
* @return The dest
array, or a newly created array if dest
was null.
* @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
* More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
* failed because the input point has invalid coordinates.
*
* @see CV_Coverage#evaluateAsInteger
*/
public int[] evaluate(final CoordinatePoint coord, int[] dest)
throws CannotEvaluateException
{
final double[] result = evaluate(coord, (double[])null);
if (dest == null) {
dest = new int[result.length];
}
for (int i=0; inull
to
* create a new array. If non-null, this array must be at least
* {@link #getSampleDimensions()}.length
long.
* @return The dest
array, or a newly created array if dest
was null.
* @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
* More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
* failed because the input point has invalid coordinates.
*/
public float[] evaluate(final CoordinatePoint coord, float[] dest)
throws CannotEvaluateException
{
final double[] result = evaluate(coord, (double[])null);
if (dest == null) {
dest = new float[result.length];
}
for (int i=0; i{@link #getSampleDimensions()}.length
long.
* @return The dest
array, or a newly created array if dest
was null.
* @throws CannotEvaluateException if the values can't be computed at the specified coordinate.
* More specifically, {@link PointOutsideCoverageException} is thrown if the evaluation
* failed because the input point has invalid coordinates.
*
* @see CV_Coverage#evaluateAsDouble
*/
public abstract double[] evaluate(CoordinatePoint coord, double[] dest)
throws CannotEvaluateException;
/**
* Returns 2D view of this grid coverage as a renderable image.
* This method allows interoperability with Java2D.
*
* @param xAxis Dimension to use for x axis.
* @param yAxis Dimension to use for y axis.
* @return A 2D view of this grid coverage as a renderable image.
*/
public RenderableImage getRenderableImage(final int xAxis, final int yAxis) {
return new Renderable(xAxis, yAxis);
}
/////////////////////////////////////////////////////////////////////////
//////////////// ////////////////
//////////////// RenderableImage / ImageFunction ////////////////
//////////////// ////////////////
/////////////////////////////////////////////////////////////////////////
/**
* Base class for renderable image view of a coverage. Renderable images allow interoperability
* with Java2D for a two-dimensional
* view of a coverage (which may or may not be a {@linkplain org.geotools.gc.GridCoverage grid
* coverage}).
*
* @version $Id$
* @author Martin Desruisseaux
*
* @see Coverage#getRenderableImage
*/
protected class Renderable extends PropertySourceImpl implements RenderableImage, ImageFunction
{
/**
* The two dimensional view of the coverage's envelope.
*/
private final Rectangle2D bounds;
/**
* Dimension to use for x axis.
*/
protected final int xAxis;
/**
* Dimension to use for y axis.
*/
protected final int yAxis;
/**
* A coordinate point where to evaluate the function. The point dimension is equals to
* the {@linkplain Coverage#getDimension coverage's dimension}. The {@linkplain #xAxis
* x} and {@link #yAxis y} ordinates will be ignored, since they will vary for each pixel
* to be evaluated. Other ordinates, if any, should be set to a fixed value. For example
* a coverage may be three-dimensional, where the third dimension is the time axis. In
* such case, coordinate.ord[2]
should be set to the point in time where
* to evaluate the coverage. By default, all ordinates are initialized to 0. Subclasses
* should set the desired values in their constructor if needed.
*/
protected final CoordinatePoint coordinate = new CoordinatePoint(getDimension());
/**
* Construct a renderable image.
*
* @param xAxis Dimension to use for x axis.
* @param yAxis Dimension to use for y axis.
*/
public Renderable(final int xAxis, final int yAxis) {
super(null, Coverage.this);
this.xAxis = xAxis;
this.yAxis = yAxis;
final Envelope envelope = getEnvelope();
bounds = new Rectangle2D.Double(envelope.getMinimum(xAxis),
envelope.getMinimum(yAxis),
envelope.getLength (xAxis),
envelope.getLength (yAxis));
}
/**
* Returns null
to indicate that no source information is available.
*/
public Vector getSources() {
return null;
}
/**
* Returns true
if successive renderings with the same arguments may
* produce different results. The default implementation returns false
.
*
* @see org.geotools.gc.GridCoverage#isDataEditable
*/
public boolean isDynamic() {
return false;
}
/**
* Returns false
since values are not complex.
*/
public boolean isComplex() {
return false;
}
/**
* Gets the width in coverage coordinate space.
*
* @see Coverage#getEnvelope
* @see Coverage#getCoordinateSystem
*/
public float getWidth() {
return (float)bounds.getWidth();
}
/**
* Gets the height in coverage coordinate space.
*
* @see Coverage#getEnvelope
* @see Coverage#getCoordinateSystem
*/
public float getHeight() {
return (float)bounds.getHeight();
}
/**
* Gets the minimum X coordinate of the rendering-independent image data.
* This is the {@linkplain Coverage#getEnvelope coverage's envelope} minimal value
* for the {@linkplain #xAxis x axis}.
*
* @see Coverage#getEnvelope
* @see Coverage#getCoordinateSystem
*/
public float getMinX() {
return (float)bounds.getX();
}
/**
* Gets the minimum Y coordinate of the rendering-independent image data.
* This is the {@linkplain Coverage#getEnvelope coverage's envelope} minimal value
* for the {@linkplain #yAxis y axis}.
*
* @see Coverage#getEnvelope
* @see Coverage#getCoordinateSystem
*/
public float getMinY() {
return (float)bounds.getY();
}
/**
* Returns a rendered image with a default width and height in pixels.
*
* @return A rendered image containing the rendered data
*/
public RenderedImage createDefaultRendering() {
return createScaledRendering(512, 0, null);
}
/**
* Creates a rendered image with width width
and height height
* in pixels. If width
is 0, it will be computed automatically from
* height
. Conversely, if height
is 0, il will be computed
* automatically from width
.
*
* The default implementation creates a render context with {@link #createRenderContext}
* and invokes {@link #createRendering(RenderContext)}.
*
* @param width The width of rendered image in pixels, or 0.
* @param height The height of rendered image in pixels, or 0.
* @param hints Rendering hints, or null
.
* @return A rendered image containing the rendered data
*/
public RenderedImage createScaledRendering(int width, int height,
final RenderingHints hints)
{
final double boundsWidth = bounds.getWidth();
final double boundsHeight = bounds.getHeight();
if (!(width > 0)) { // Use '!' in order to catch NaN
if (!(height > 0)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.UNSPECIFIED_IMAGE_SIZE));
}
width = (int)Math.round(height * (boundsWidth/boundsHeight));
} else if (!(height > 0)) {
height = (int)Math.round(width * (boundsHeight/boundsWidth));
}
return createRendering(createRenderContext(new Rectangle(0, 0, width, height), hints));
}
/**
* Creates a rendered image using a given render context. This method will uses
* an "{@link ImageFunctionDescriptor ImageFunction}" operation if possible
* (i.e. if the area of interect is rectangular and the affine transform contains
* only translation and scale coefficients).
*
* @param context The render context to use to produce the rendering.
* @return A rendered image containing the rendered data
*/
public RenderedImage createRendering(final RenderContext context) {
final AffineTransform csToGrid = context.getTransform();
final Shape area = context.getAreaOfInterest();
final Rectangle gridBounds;
if (true) {
/*
* Compute the grid bounds for the coverage bounds (or the area of interest).
* The default implementation of Rectangle uses Math.floor and Math.ceil for
* computing a box which contains fully the Rectangle2D. But in our particular
* case, we really want to round toward the nearest integer.
*/
final Rectangle2D bounds = XAffineTransform.transform(csToGrid,
(area!=null) ? area.getBounds2D() : this.bounds, null);
final int xmin = (int)Math.round(bounds.getMinX());
final int ymin = (int)Math.round(bounds.getMinY());
final int xmax = (int)Math.round(bounds.getMaxX());
final int ymax = (int)Math.round(bounds.getMaxY());
gridBounds = new Rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
}
/*
* Compute some properties of the image to be created.
*/
final Dimension tileSize = ImageUtilities.toTileSize(gridBounds.getSize());
final SampleDimension[] sampleDimensions = getSampleDimensions();
final ColorModel colorModel = sampleDimensions[VISIBLE_BAND].getColorModel(
VISIBLE_BAND, sampleDimensions.length);
final SampleModel sampleModel = colorModel.createCompatibleSampleModel(
tileSize.width, tileSize.height);
/*
* If the image can be created using the ImageFunction operation, do it.
* It allow JAI to defer the computation until a tile is really requested.
*/
final PlanarImage image;
if ((area==null || area instanceof Rectangle2D) &&
csToGrid.getShearX()==0 && csToGrid.getShearY()==0)
{
image = JAI.create("ImageFunction",
new ParameterBlock()
.add(this) // The functional description
.add(gridBounds.width) // The image width
.add(gridBounds.height) // The image height
.add((float)(1/csToGrid.getScaleX())) // The X scale factor
.add((float)(1/csToGrid.getScaleY())) // The Y scale factor
.add((float) csToGrid.getTranslateX()) // The X translation
.add((float) csToGrid.getTranslateY()), // The Y translation
new RenderingHints(JAI.KEY_IMAGE_LAYOUT, new ImageLayout()
.setMinX (gridBounds.x)
.setMinY (gridBounds.y)
.setTileWidth (tileSize.width)
.setTileHeight (tileSize.height)
.setSampleModel(sampleModel)
.setColorModel (colorModel)));
} else {
/*
* Creates immediately a rendered image using a given render context. This block
* is run when the image can't be created with JAI's ImageFunction operator, for
* example because the affine transform swap axis or because there is an area of
* interest.
*/
// Clones the coordinate point in order to allow multi-thread invocation.
final CoordinatePoint coordinate = new CoordinatePoint(this.coordinate);
final TiledImage tiled = new TiledImage(gridBounds.x, gridBounds.y,
gridBounds.width, gridBounds.height,
0, 0, sampleModel, colorModel);
final Point2D.Double point2D = new Point2D.Double();
final int numBands = tiled.getNumBands();
final double[] samples = new double[numBands];
final double[] padNaNs = new double[numBands];
Arrays.fill(padNaNs, Double.NaN);
final WritableRectIter iterator = RectIterFactory.createWritable(tiled, gridBounds);
if (!iterator.finishedLines()) try {
int y=gridBounds.y; do {
iterator.startPixels();
if (!iterator.finishedPixels()) {
int x=gridBounds.x; do {
point2D.x = x;
point2D.y = y;
csToGrid.inverseTransform(point2D, point2D);
if (area==null || area.contains(point2D)) {
coordinate.ord[xAxis] = point2D.x;
coordinate.ord[yAxis] = point2D.y;
iterator.setPixel(evaluate(coordinate, samples));
} else {
iterator.setPixel(padNaNs);
}
x++;
}
while (!iterator.nextPixelDone());
assert(x == gridBounds.x + gridBounds.width);
y++;
}
}
while (!iterator.nextLineDone());
assert(y == gridBounds.y + gridBounds.height);
}
catch (NoninvertibleTransformException exception) {
final IllegalArgumentException e= new IllegalArgumentException("RenderContext");
e.initCause(exception);
throw e;
}
image = tiled;
}
/*
* Add a 'gridToCoordinateSystem' property to the image. This is an important
* information for constructing a GridCoverage from this image later.
*/
try {
image.setProperty("gridToCoordinateSystem", csToGrid.createInverse());
} catch (NoninvertibleTransformException exception) {
// Can't add the property. Too bad, the image has been created anyway.
// Maybe the user know what he is doing...
Utilities.unexpectedException("org.geotools.cv", "Coverage.Renderable",
"createRendering", exception);
}
return image;
}
/**
* Initialize a render context with an affine transform that maps the coverage envelope
* to the specified destination rectangle. The affine transform mays swap axis in order
* to normalize their order (i.e. make them appear in the (x,y)
* order), so that the image appears properly oriented when rendered.
*
* @param gridBounds The two-dimensional destination rectangle.
* @param hints The rendering hints, or null
if none.
* @return A render context initialized with an affine transform from the coverage
* to the grid coordinate system. This transform is the inverse of
* {@link org.geotools.gc.GridGeometry#getGridToCoordinateSystem2D}.
*
* @see org.geotools.gc.GridGeometry#getGridToCoordinateSystem
*/
protected RenderContext createRenderContext(final Rectangle2D gridBounds,
final RenderingHints hints)
{
final Matrix matrix;
final Envelope srcEnvelope = new Envelope(bounds);
final Envelope dstEnvelope = new Envelope(gridBounds);
final CoordinateSystem cs = getCoordinateSystem();
if (cs != null) {
final AxisOrientation[] axis = new AxisOrientation[] {
cs.getAxis(xAxis).orientation,
cs.getAxis(yAxis).orientation
};
final AxisOrientation[] normalized = (AxisOrientation[]) axis.clone();
if (true) {
// Normalize axis: Is it really a good idea?
// We should provide a rendering hint for configuring that.
Arrays.sort(normalized);
for (int i=normalized.length; --i>=0;) {
normalized[i] = normalized[i].absolute();
}
}
normalized[1] = normalized[1].inverse(); // Image's Y axis is downward.
matrix = Matrix.createAffineTransform(srcEnvelope, axis, dstEnvelope, normalized);
} else {
matrix = Matrix.createAffineTransform(srcEnvelope, dstEnvelope);
}
return new RenderContext(matrix.toAffineTransform2D(), hints);
}
/**
* Returns the number of elements per value at each position. This is the maximum
* value plus 1 allowed in getElements(...)
methods invocation. The
* default implementation returns the number of sample dimensions in the coverage.
*/
public int getNumElements() {
return getSampleDimensions().length;
}
/**
* Returns all values of a given element for a specified set of coordinates.
* This method is automatically invoked at rendering time for populating an
* image tile, providing that the rendered image is created using the
* "{@link ImageFunctionDescriptor ImageFunction}" operator and the image
* type is not double
. The default implementation invokes
* {@link Coverage#evaluate(CoordinatePoint,float[])} recursively.
*/
public void getElements(final float startX, final float startY,
final float deltaX, final float deltaY,
final int countX, final int countY, final int element,
final float[] real, final float[] imag)
{
int index = 0;
float[] buffer = null;
// Clones the coordinate point in order to allow multi-thread invocation.
final CoordinatePoint coordinate = new CoordinatePoint(this.coordinate);
coordinate.ord[1] = startY;
for (int j=0; jExport
instance
* can be queried with {@link Adapters#export(Coverage)}.
*
* @param adapters The originating adapter.
* @throws RemoteException if this object can't be exported through RMI.
*/
protected Export(final Adapters adapters) throws RemoteException {
super(); // TODO: Fetch the port number from the adapter.
this.adapters = adapters;
proxy = new WeakReference(this);
}
/**
* Returns the underlying implementation. Note: This method is not available or remote
* machine, since we don't implement {@link org.geotools.resources.RemoteProxy}. This
* is because {@link Coverage} may not be serializable. For example,
* {@link org.geotools.gc.GridCoverage} will usually contains a non-serializable
* {@link java.awt.image.RenderedImage}.
*/
final Coverage getImplementation() {
return Coverage.this;
}
/**
* Returns the names of each dimension in the coverage.
* The default implementation invokes {@link Coverage#getDimensionNames}.
*
* @return the names of each dimension in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public String[] getDimensionNames() throws RemoteException {
return Coverage.this.getDimensionNames(null);
}
/**
* Returns The number of sample dimensions in the coverage.
* The default implementation invokes {@link Coverage#getSampleDimensions}.
*
* @return the number of sample dimensions in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public int getNumSampleDimensions() throws RemoteException {
return Coverage.this.getSampleDimensions().length;
}
/**
* Retrieve sample dimension information for the coverage.
* The default implementation invokes {@link Coverage#getSampleDimensions}.
*
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public CV_SampleDimension getSampleDimension(int index) throws RemoteException {
return adapters.export(Coverage.this.getSampleDimensions()[index]);
}
/**
* Returns the number of grid coverages which the grid coverage was derived from.
* The default implementation returns 0
.
*
* @return the number of grid coverages which this coverage was derived from.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public int getNumSources() throws RemoteException {
return 0;
}
/**
* Returns the source data for a grid coverage.
* The default implementation throws an {@link ArrayIndexOutOfBoundsException},
* since {@link #getNumSources} returned 0.
*
* @param sourceDataIndex Source grid coverage index. Indexes start at 0.
* @return the source data for a grid coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public GC_GridCoverage getSource(int sourceDataIndex) throws RemoteException {
throw new ArrayIndexOutOfBoundsException(sourceDataIndex);
}
/**
* Returns the list of metadata keywords for a coverage.
* The default implementation invokes {@link #getPropertyNames}.
*
* @return the list of metadata keywords for a coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public String[] getMetadataNames() throws RemoteException {
return getPropertyNames();
}
/**
* Retrieve the metadata value for a given metadata name.
* The default implementation invokes {@link #getProperty}
* and replace {@link java.awt.Image#UndefinedProperty} by
* null
.
*
* @param name Metadata keyword for which to retrieve data.
* @return the metadata value for a given metadata name.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public String getMetadataValue(String name) throws RemoteException {
final Object value = getProperty(name);
return (value!=null && value!=Image.UndefinedProperty) ? value.toString() : null;
}
/**
* Returns an array of {@link String}s recognized as names by this property source.
* The default implementation invokes {@link Coverage#getPropertyNames()}.
*
* @return an array of strings giving the valid property names, or null
.
*/
public String[] getPropertyNames() {
return Coverage.this.getPropertyNames();
}
/**
* Returns an array of {@link String}s recognized as names by this property source
* that begin with the supplied prefix. The default implementation invokes {@link
* Coverage#getPropertyNames(String)}.
*
* @return an array of strings giving the valid property names.
*/
public String[] getPropertyNames(final String prefix) {
return Coverage.this.getPropertyNames(prefix);
}
/**
* Returns the class expected to be returned by a request for the property with
* the specified name. If this information is unavailable, null
will
* be returned.
*
* @param name the name of the property.
* @return The class expected to be return by a request for the value
* of this property, or null
.
*/
public Class getPropertyClass(final String name) {
return Coverage.this.getPropertyClass(name);
}
/**
* Returns the value of a property. If the property name is not recognized,
* then {@link java.awt.Image#UndefinedProperty} will be returned.
*
* @param name the name of the property.
* @return the value of the property, or the value
* {@link java.awt.Image#UndefinedProperty}.
*/
public Object getProperty(final String name) {
return Coverage.this.getProperty(name);
}
/**
* Returns coordinate system used when accessing a coverage or grid coverage
* with the evaluate
methods. The default implementation invokes
* {@link Coverage#getCoordinateSystem}.
*
* @return the coordinate system used when accessing a coverage or
* grid coverage with the evaluate
methods.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public CS_CoordinateSystem getCoordinateSystem() throws RemoteException {
return adapters.CTS.export(Coverage.this.getCoordinateSystem());
}
/**
* Returns the bounding box for the coverage domain in coordinate system coordinates.
* The default implementation invokes {@link Coverage#getEnvelope}.
*
* @return the bounding box for the coverage domain in coordinate system coordinates.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public PT_Envelope getEnvelope() throws RemoteException {
return adapters.CTS.export(Coverage.this.getEnvelope());
}
/**
* Return the value vector for a given point in the coverage.
* The default implementation invokes one of other CV_Coverage
method
* (for example {@link #evaluateAsDouble}) according the underlying data type.
*
* @param point point at which to find the grid values.
* @return the value vector for a given point in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*
* @task TODO: Check the underlying data type.
*/
public Object evaluate(PT_CoordinatePoint point) throws RemoteException {
return evaluateAsDouble(point);
}
/**
* Return a sequence of Boolean values for a given point in the coverage.
* The default implementation invokes {@link Coverage#evaluate(CoordinatePoint, boolean[])}.
*
* @param point point at which to find the coverage values.
* @return a sequence of boolean values for a given point in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public boolean[] evaluateAsBoolean(PT_CoordinatePoint point) throws RemoteException {
return Coverage.this.evaluate(adapters.CTS.wrap(point), (boolean[]) null);
}
/**
* Return a sequence of unsigned byte values for a given point in the coverage.
* The default implementation invokes {@link Coverage#evaluate(CoordinatePoint, byte[])}.
*
* @param point point at which to find the coverage values.
* @return a sequence of unsigned byte values for a given point in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public byte[] evaluateAsByte(PT_CoordinatePoint point) throws RemoteException {
return Coverage.this.evaluate(adapters.CTS.wrap(point), (byte[]) null);
}
/**
* Return a sequence of integer values for a given point in the coverage.
* The default implementation invokes {@link Coverage#evaluate(CoordinatePoint, int[])}.
*
* @param point point at which to find the grid values.
* @return a sequence of integer values for a given point in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public int[] evaluateAsInteger(PT_CoordinatePoint point) throws RemoteException {
return Coverage.this.evaluate(adapters.CTS.wrap(point), (int[]) null);
}
/**
* Return a sequence of double values for a given point in the coverage.
* The default implementation invokes {@link Coverage#evaluate(CoordinatePoint, double[])}.
*
* @param point point at which to find the grid values.
* @return a sequence of double values for a given point in the coverage.
* @throws RemoteException if a remote call failed. More specifically, the exception will
* be an instance of {@link ServerException} if an error occurs on the server side.
*/
public double[] evaluateAsDouble(PT_CoordinatePoint point) throws RemoteException {
return Coverage.this.evaluate(adapters.CTS.wrap(point), (double[]) null);
}
}
}