0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2003-2006, Geotools Project Management Committee (PMC)
0005: * (C) 2001, Institut de Recherche pour le Développement
0006: *
0007: * This library is free software; you can redistribute it and/or
0008: * modify it under the terms of the GNU Lesser General Public
0009: * License as published by the Free Software Foundation; either
0010: * version 2.1 of the License, or (at your option) any later version.
0011: *
0012: * This library is distributed in the hope that it will be useful,
0013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0015: * Lesser General Public License for more details.
0016: *
0017: * This package contains documentation from OpenGIS specifications.
0018: * OpenGIS consortium's work is fully acknowledged here.
0019: */
0020: package org.geotools.coverage;
0021:
0022: // J2SE dependencies
0023: import java.awt.Dimension;
0024: import java.awt.EventQueue;
0025: import java.awt.Frame;
0026: import java.awt.Rectangle;
0027: import java.awt.RenderingHints;
0028: import java.awt.Shape;
0029: import java.awt.event.WindowAdapter;
0030: import java.awt.event.WindowEvent;
0031: import java.awt.geom.AffineTransform;
0032: import java.awt.geom.NoninvertibleTransformException;
0033: import java.awt.geom.Point2D;
0034: import java.awt.geom.Rectangle2D;
0035: import java.awt.image.ColorModel;
0036: import java.awt.image.RenderedImage;
0037: import java.awt.image.SampleModel;
0038: import java.awt.image.renderable.ParameterBlock;
0039: import java.awt.image.renderable.RenderContext;
0040: import java.awt.image.renderable.RenderableImage;
0041: import java.io.IOException;
0042: import java.io.StringWriter;
0043: import java.lang.reflect.Array;
0044: import java.util.Arrays;
0045: import java.util.Collections;
0046: import java.util.HashSet;
0047: import java.util.List;
0048: import java.util.Locale;
0049: import java.util.Map;
0050: import java.util.Set;
0051: import java.util.Vector;
0052:
0053: // JAI dependencies
0054: import javax.media.jai.ImageFunction;
0055: import javax.media.jai.ImageLayout;
0056: import javax.media.jai.InterpolationNearest; // For Javadoc
0057: import javax.media.jai.JAI;
0058: import javax.media.jai.PlanarImage;
0059: import javax.media.jai.PropertySource;
0060: import javax.media.jai.PropertySourceImpl;
0061: import javax.media.jai.TiledImage;
0062: import javax.media.jai.iterator.RectIterFactory;
0063: import javax.media.jai.iterator.WritableRectIter;
0064: import javax.media.jai.operator.ImageFunctionDescriptor; // For Javadoc
0065: import javax.media.jai.util.CaselessStringKey; // For Javadoc
0066: import javax.media.jai.widget.ScrollingImagePanel;
0067:
0068: // OpenGIS dependencies
0069: import org.opengis.coverage.CannotEvaluateException;
0070: import org.opengis.coverage.CommonPointRule;
0071: import org.opengis.coverage.Coverage;
0072: import org.opengis.coverage.GeometryValuePair;
0073: import org.opengis.coverage.MetadataNameNotFoundException;
0074: import org.opengis.coverage.grid.GridCoverage; // For javadoc
0075: import org.opengis.coverage.grid.GridGeometry; // For javadoc
0076: import org.opengis.coverage.processing.GridCoverageProcessor; // For javadoc
0077: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0078: import org.opengis.referencing.cs.AxisDirection;
0079: import org.opengis.referencing.cs.CoordinateSystem;
0080: import org.opengis.geometry.DirectPosition;
0081: import org.opengis.geometry.Envelope;
0082: import org.opengis.geometry.Geometry;
0083: import org.opengis.temporal.Period;
0084: import org.opengis.util.InternationalString;
0085: import org.opengis.util.Record;
0086: import org.opengis.util.RecordType;
0087:
0088: // Geotools dependencies
0089: import org.geotools.geometry.GeneralDirectPosition;
0090: import org.geotools.geometry.GeneralEnvelope;
0091: import org.geotools.referencing.CRS;
0092: import org.geotools.referencing.operation.matrix.GeneralMatrix;
0093: import org.geotools.referencing.operation.matrix.XAffineTransform;
0094: import org.geotools.util.logging.Logging;
0095: import org.geotools.util.SimpleInternationalString;
0096:
0097: // Resources
0098: import org.geotools.io.LineWriter;
0099: import org.geotools.resources.Utilities;
0100: import org.geotools.resources.XArray;
0101: import org.geotools.resources.image.ImageUtilities;
0102: import org.geotools.resources.i18n.Errors;
0103: import org.geotools.resources.i18n.ErrorKeys;
0104:
0105: /**
0106: * Base class of all coverage type. The essential property of coverage is to be able
0107: * to generate a value for any point within its domain. How coverage is represented
0108: * internally is not a concern. For example consider the following different internal
0109: * representations of coverage:
0110: * <p>
0111: * <ul>
0112: * <li>A coverage may be represented by a set of polygons which exhaustively tile a
0113: * plane (that is each point on the plane falls in precisely one polygon). The
0114: * value returned by the coverage for a point is the value of an attribute of
0115: * the polygon that contains the point.</li>
0116: * <li>A coverage may be represented by a grid of values. The value returned by the
0117: * coverage for a point is that of the grid value whose location is nearest the
0118: * point.</li>
0119: * <li>Coverage may be represented by a mathematical function. The value returned
0120: * by the coverage for a point is just the return value of the function when
0121: * supplied the coordinates of the point as arguments.</li>
0122: * <li>Coverage may be represented by combination of these. For example, coverage
0123: * may be represented by a combination of mathematical functions valid over a
0124: * set of polynomials.</li>
0125: * </ul>
0126: *
0127: * @since 2.1
0128: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/AbstractCoverage.java $
0129: * @version $Id: AbstractCoverage.java 27848 2007-11-12 13:10:32Z desruisseaux $
0130: * @author Martin Desruisseaux
0131: */
0132: public abstract class AbstractCoverage extends PropertySourceImpl
0133: implements Coverage {
0134: /**
0135: * For compatibility during cross-version serialization.
0136: */
0137: private static final long serialVersionUID = -2989320942499746295L;
0138:
0139: /**
0140: * The set of default axis name.
0141: */
0142: private static final String[] DIMENSION_NAMES = { "x", "y", "z",
0143: "t" };
0144:
0145: /**
0146: * The sequence of string to returns when there is no metadata.
0147: */
0148: private static final String[] NO_PROPERTIES = new String[0];
0149:
0150: /**
0151: * The sample dimension to make visible by {@link #getRenderableImage}.
0152: */
0153: private static final int VISIBLE_BAND = 0;
0154:
0155: /**
0156: * The coverage name.
0157: */
0158: private final InternationalString name;
0159:
0160: /**
0161: * The coordinate reference system, or {@code null} if there is none.
0162: */
0163: protected final CoordinateReferenceSystem crs;
0164:
0165: /**
0166: * Constructs a coverage using the specified coordinate reference system. If the coordinate
0167: * reference system is {@code null}, then the subclasses must override {@link #getDimension()}.
0168: *
0169: * @param name The coverage name.
0170: * @param crs The coordinate reference system. This specifies the CRS used when accessing
0171: * a coverage or grid coverage with the {@code evaluate(...)} methods.
0172: * @param source The source for this coverage, or {@code null} if none. Source may be (but is
0173: * not limited to) a {@link PlanarImage} or an other {@code AbstractCoverage}
0174: * object.
0175: * @param properties The set of properties for this coverage, or {@code null} if there is
0176: * none. "Properties" in <cite>Java Advanced Imaging</cite> is what OpenGIS
0177: * calls "Metadata". Keys are {@link String} objects ({@link CaselessStringKey}
0178: * are accepted as well), while values may be any {@link Object}.
0179: */
0180: protected AbstractCoverage(final CharSequence name,
0181: final CoordinateReferenceSystem crs,
0182: final PropertySource source, final Map properties) {
0183: super (properties, source);
0184: this .name = SimpleInternationalString.wrap(name);
0185: this .crs = crs;
0186: }
0187:
0188: /**
0189: * Constructs a new coverage with the same parameters than the specified coverage.
0190: * <p>
0191: * <strong>Note:</strong> This constructor keeps a strong reference to the source
0192: * coverage (through {@link PropertySourceImpl}). In many cases, it is not a problem
0193: * since {@link GridCoverage} will retains a strong reference to its source anyway.
0194: *
0195: * @param name The name for this coverage, or {@code null} for the same than {@code coverage}.
0196: * @param coverage The source coverage.
0197: */
0198: protected AbstractCoverage(final CharSequence name,
0199: final Coverage coverage) {
0200: super (
0201: null,
0202: (coverage instanceof PropertySource) ? (PropertySource) coverage
0203: : null);
0204: final InternationalString n = SimpleInternationalString
0205: .wrap(name);
0206: if (coverage instanceof AbstractCoverage) {
0207: final AbstractCoverage source = (AbstractCoverage) coverage;
0208: this .name = (n != null) ? n : source.name;
0209: this .crs = source.crs;
0210: } else {
0211: this .name = (n != null) ? n
0212: : new SimpleInternationalString(coverage.toString());
0213: this .crs = coverage.getCoordinateReferenceSystem();
0214: }
0215: }
0216:
0217: /**
0218: * Returns the coverage name. The default implementation returns the name
0219: * specified at construction time.
0220: */
0221: public InternationalString getName() {
0222: return name;
0223: }
0224:
0225: /**
0226: * Returns the dimension of this coverage. This is a shortcut for
0227: * <code>{@linkplain #crs}.getCoordinateSystem().getDimension()</code>.
0228: */
0229: public final int getDimension() {
0230: return crs.getCoordinateSystem().getDimension();
0231: }
0232:
0233: /**
0234: * Returns the coordinate reference system to which the objects in its domain are
0235: * referenced. This is the CRS used when accessing a coverage or grid coverage with
0236: * the {@code evaluate(...)} methods. This coordinate reference system is usually
0237: * different than coordinate system of the grid. It is the target coordinate reference
0238: * system of the {@link GridGeometry#getGridToCRS gridToCRS} math transform.
0239: * <p>
0240: * Grid coverage can be accessed (re-projected) with new coordinate reference system
0241: * with the {@link GridCoverageProcessor} component. In this case, a new instance of
0242: * a grid coverage is created.
0243: *
0244: * @return The coordinate reference system used when accessing a coverage or
0245: * grid coverage with the {@code evaluate(...)} methods.
0246: *
0247: * @see org.geotools.coverage.grid.GeneralGridGeometry#getGridToCRS
0248: */
0249: public CoordinateReferenceSystem getCoordinateReferenceSystem() {
0250: return crs;
0251: }
0252:
0253: /**
0254: * Returns the names of each dimension in this coverage. Typically these
0255: * names are "x", "y", "z" and "t". The number of items in the sequence is
0256: * the number of dimensions in the coverage. Grid coverages are typically
0257: * 2D (<var>x</var>, <var>y</var>) while other coverages may be
0258: * 3D (<var>x</var>, <var>y</var>, <var>z</var>) or
0259: * 4D (<var>x</var>, <var>y</var>, <var>z</var>, <var>t</var>).
0260: * The {@linkplain #getDimension number of dimensions} of the coverage is
0261: * the number of entries in the list of dimension names.
0262: * <p>
0263: * The default implementation ask for
0264: * {@linkplain CoordinateSystem coordinate system} axis names, or returns
0265: * "x", "y"... if this coverage has no CRS.
0266: *
0267: * @return The names of each dimension. The array's length is equals to
0268: * {@link #getDimension}.
0269: *
0270: * @deprecated This information can be obtained from the underlying coordinate system.
0271: */
0272: public InternationalString[] getDimensionNames() {
0273: final InternationalString[] names;
0274: if (crs != null) {
0275: final CoordinateSystem cs = crs.getCoordinateSystem();
0276: names = new InternationalString[cs.getDimension()];
0277: for (int i = 0; i < names.length; i++) {
0278: names[i] = new SimpleInternationalString(cs.getAxis(i)
0279: .getName().getCode());
0280: }
0281: } else {
0282: names = (InternationalString[]) XArray.resize(
0283: DIMENSION_NAMES, getDimension());
0284: for (int i = DIMENSION_NAMES.length; i < names.length; i++) {
0285: names[i] = new SimpleInternationalString("dim"
0286: + (i + 1));
0287: }
0288: }
0289: return names;
0290: }
0291:
0292: /**
0293: * Returns the names of each dimension in this coverage.
0294: *
0295: * @deprecated Replaced by {@link #getDimensionNames()}.
0296: */
0297: public final String[] getDimensionNames(final Locale locale) {
0298: final InternationalString[] inter = getDimensionNames();
0299: final String[] names = new String[inter.length];
0300: for (int i = 0; i < names.length; i++) {
0301: names[i] = inter[i].toString(locale);
0302: }
0303: return names;
0304: }
0305:
0306: /**
0307: * Returns the bounding box for the coverage domain in
0308: * {@linkplain #getCoordinateReferenceSystem coordinate reference system} coordinates. May
0309: * be {@code null} if this coverage has no associated coordinate reference system. For grid
0310: * coverages, the grid cells are centered on each grid coordinate. The envelope for a 2-D
0311: * grid coverage includes the following corner positions.
0312: *
0313: * <blockquote><pre>
0314: * (Minimum row - 0.5, Minimum column - 0.5) for the minimum coordinates
0315: * (Maximum row - 0.5, Maximum column - 0.5) for the maximum coordinates
0316: * </pre></blockquote>
0317: *
0318: * The default implementation returns the domain of validity of the CRS, if there is one.
0319: *
0320: * @return The bounding box for the coverage domain in coordinate system coordinates.
0321: */
0322: public Envelope getEnvelope() {
0323: return CRS.getEnvelope(crs);
0324: }
0325:
0326: /**
0327: * Returns the extent of the domain of the coverage. Extents may be specified in space,
0328: * time or space-time. The collection must contains at least one element.
0329: * <p>
0330: * <strong>This method is not yet implemented.</strong>
0331: *
0332: * @since 2.3
0333: *
0334: * @todo Proposed default implementation: invokes {@link #getEnvelope}, extract
0335: * the spatial and temporal parts and put them in a {@link Extent} object.
0336: */
0337: public Set/*<Extent>*/getDomainExtents() {
0338: throw unsupported();
0339: }
0340:
0341: /**
0342: * Returns the set of domain objects in the domain.
0343: * The collection must contains at least one element.
0344: * <p>
0345: * <strong>This method is not yet implemented.</strong>
0346: *
0347: * @since 2.3
0348: *
0349: * @todo Proposed default implementation: invokes {@link #getEnvelope}, extract the
0350: * spatial and temporal parts, get the grid geometry and create on-the-fly a
0351: * {@link DomainObject} for each cell.
0352: */
0353: public Set/*<? extends DomainObject>*/getDomainElements() {
0354: throw unsupported();
0355: }
0356:
0357: /**
0358: * Returns the set of attribute values in the range. The range of a coverage shall be a
0359: * homogeneous collection of records. That is, the range shall have a constant dimension
0360: * over the entire domain, and each field of the record shall provide a value of the same
0361: * attribute type over the entire domain.
0362: * <p>
0363: * In the case of a {@linkplain DiscreteCoverage discrete coverage}, the size of the range
0364: * collection equals that of the {@linkplain #getDomainElements domains} collection. In other
0365: * words, there is one instance of {@link AttributeValues} for each instance of
0366: * {@link DomainObject}. Usually, these are stored values that are accessed by the
0367: * {@link #evaluate(DirectPosition,Collection) evaluate} operation.
0368: * <p>
0369: * In the case of a {@linkplain ContinuousCoverage continuous coverage}, there is a transfinite
0370: * number of instances of {@link AttributeValues} for each {@link DomainObject}. A few instances
0371: * may be stored as input for the {@link #evaluate(DirectPosition,Collection) evaluate}
0372: * operation, but most are generated as needed by that operation.
0373: * <p>
0374: * <B>NOTE:</B> ISO 19123 does not specify how the {@linkplain #getDomainElements domain}
0375: * and {@linkplain #getRangeElements range} associations are to be implemented. The relevant
0376: * data may be generated in real time, it may be held in persistent local storage, or it may
0377: * be electronically accessible from remote locations.
0378: * <p>
0379: * <strong>This method is not yet implemented.</strong>
0380: *
0381: * @since 2.3
0382: */
0383: public Set/*<AttributeValues>*/getRangeElements() {
0384: throw unsupported();
0385: }
0386:
0387: /**
0388: * Describes the range of the coverage. It consists of a list of attribute name/data type pairs.
0389: * A simple list is the most common form of range type, but {@code RecordType} can be used
0390: * recursively to describe more complex structures. The range type for a specific coverage
0391: * shall be specified in an application schema.
0392: * <p>
0393: * <strong>This method is not yet implemented.</strong>
0394: *
0395: * @since 2.3
0396: */
0397: public RecordType getRangeType() {
0398: throw unsupported();
0399: }
0400:
0401: /**
0402: * Identifies the procedure to be used for evaluating the coverage at a position that falls
0403: * either on a boundary between geometric objects or within the boundaries of two or more
0404: * overlapping geometric objects. The geometric objects are either {@linkplain DomainObject
0405: * domain objects} or {@linkplain ValueObject value objects}.
0406: * <p>
0407: * <strong>This method is not yet implemented.</strong>
0408: *
0409: * @since 2.3
0410: */
0411: public CommonPointRule getCommonPointRule() {
0412: throw unsupported();
0413: }
0414:
0415: /**
0416: * Returns the dictionary of <var>geometry</var>-<var>value</var> pairs that contain the
0417: * {@linkplain DomainObject objects} in the domain of the coverage each paired with its
0418: * record of feature attribute values. In the case of an analytical coverage, the operation
0419: * shall return the empty set.
0420: * <p>
0421: * <strong>This method is not yet implemented.</strong>
0422: *
0423: * @since 2.3
0424: */
0425: public Set list() {
0426: throw unsupported();
0427: }
0428:
0429: /**
0430: * Returns the set of <var>geometry</var>-<var>value</var> pairs that contain
0431: * {@linkplain DomainObject domain objects} that lie within the specified geometry and period.
0432: * If {@code s} is null, the operation shall return all <var>geometry</var>-<var>value</var>
0433: * pairs that contain {@linkplain DomainObject domain objects} within {@code t}. If the value
0434: * of {@code t} is null, the operation shall return all <var>geometry</var>-<var>value</var>
0435: * pair that contain {@linkplain DomainObject domain objects} within {@code s}. In the case
0436: * of an analytical coverage, the operation shall return the empty set.
0437: * <p>
0438: * <strong>This method is not yet implemented.</strong>
0439: *
0440: * @since 2.3
0441: */
0442: public Set select(Geometry arg0, Period arg1) {
0443: throw unsupported();
0444: }
0445:
0446: /**
0447: * Returns the sequence of <var>geometry</var>-<var>value</var> pairs that include the
0448: * {@linkplain DomainObject domain objects} nearest to the direct position and their
0449: * distances from the direction position. The sequence shall be ordered by distance from
0450: * the direct position, beginning with the record containing the {@linkplain DomainObject
0451: * domain object} nearest to the direct position. The length of the sequence (the number
0452: * of <var>geometry</var>-<var>value</var> pairs returned) shall be no greater than the
0453: * number specified by the parameter {@code limit}. The default shall be to return a single
0454: * <var>geometry</var>-<var>value</var> pair. The operation shall return a warning if the
0455: * last {@linkplain DomainObject domain object} in the sequence is at a distance from the
0456: * direct position equal to the distance of other {@linkplain DomainObject domain objects}
0457: * that are not included in the sequence. In the case of an analytical coverage, the operation
0458: * shall return the empty set.
0459: * <p>
0460: * <B>NOTE:</B> This operation is useful when the domain of a coverage does not exhaustively
0461: * partition the extent of the coverage. Even in that case, the first element of the sequence
0462: * returned may be the <var>geometry</var>-<var>value</var> pair that contains the input direct
0463: * position.
0464: * <p>
0465: * <strong>This method is not yet implemented.</strong>
0466: *
0467: * @since 2.3
0468: */
0469: public List find(DirectPosition p, int limit) {
0470: throw unsupported();
0471: }
0472:
0473: /**
0474: * Returns the nearest <var>geometry</var>-<var>value</var> pair
0475: * from the specified direct position. This is a shortcut for
0476: * <code>{@linkplain #find(DirectPosition,int) find}(p,1)</code>.
0477: *
0478: * @since 2.3
0479: */
0480: public GeometryValuePair find(final DirectPosition p) {
0481: final List pairs = find(p, 1);
0482: return pairs.isEmpty() ? null : (GeometryValuePair) pairs
0483: .get(0);
0484: }
0485:
0486: /**
0487: * Invoked when an unsupported operation is invoked.
0488: */
0489: private static final UnsupportedOperationException unsupported() {
0490: throw new UnsupportedOperationException(
0491: "This method is currently not implemented. "
0492: + "It may be implemented by next version of coverage module.");
0493: }
0494:
0495: /**
0496: * Returns a localized error message for the specified array.
0497: */
0498: private static String formatErrorMessage(final Object array) {
0499: String text = "<null>";
0500: if (array != null) {
0501: Class type = array.getClass();
0502: if (type.isArray()) {
0503: type = type.getComponentType();
0504: }
0505: text = Utilities.getShortName(type);
0506: }
0507: return Errors.format(ErrorKeys.CANT_CONVERT_FROM_TYPE_$1, text);
0508: }
0509:
0510: /**
0511: * Returns a set of records of feature attribute values for the specified direct position. The
0512: * parameter {@code list} is a sequence of feature attribute names each of which identifies a
0513: * field of the range type. If {@code list} is null, the operation shall return a value for
0514: * every field of the range type. Otherwise, it shall return a value for each field included in
0515: * {@code list}. If the direct position passed is not in the domain of the coverage, then an
0516: * exception is thrown. If the input direct position falls within two or more geometric objects
0517: * within the domain, the operation shall return records of feature attribute values computed
0518: * according to the {@linkplain #getCommonPointRule common point rule}.
0519: * <P>
0520: * <B>NOTE:</B> Normally, the operation will return a single record of feature attribute values.
0521: *
0522: * @since 2.3
0523: *
0524: * @deprecated Current implementation is incorrect, since it ignores the {@link #list} argument.
0525: */
0526: public Set/*<Record>*/evaluate(final DirectPosition coord,
0527: final Set/*<String>*/list) {
0528: final Set set = new HashSet();
0529: final Object array = evaluate(coord);
0530: try {
0531: final int length = Array.getLength(array);
0532: for (int index = 0; index < length; index++) {
0533: set.add(Array.get(array, index));
0534: }
0535: } catch (IllegalArgumentException exception) {
0536: throw new CannotEvaluateException(formatErrorMessage(set),
0537: exception);
0538: }
0539: return set;
0540: }
0541:
0542: /**
0543: * Returns a sequence of boolean values for a given point in the coverage. A value for each
0544: * {@linkplain SampleDimension sample dimension} is included in the sequence. The default
0545: * interpolation type used when accessing grid values for points which fall between grid
0546: * cells is {@linkplain InterpolationNearest nearest neighbor}, but it can be changed by
0547: * some {@linkplain org.geotools.coverage.grid.Interpolator2D subclasses}. The CRS of the
0548: * point is the same as the grid coverage {@linkplain #getCoordinateReferenceSystem coordinate
0549: * reference system}.
0550: *
0551: * @param coord The coordinate point where to evaluate.
0552: * @param dest An array in which to store values, or {@code null} to create a new array.
0553: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
0554: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0555: * has invalid coordinates.
0556: * @throws CannotEvaluateException if the values can't be computed at the specified coordinate
0557: * for an other reason. It may be thrown if the coverage data type can't be converted
0558: * to {@code boolean} by an identity or widening conversion. Subclasses may relax this
0559: * constraint if appropriate.
0560: */
0561: public boolean[] evaluate(final DirectPosition coord, boolean[] dest)
0562: throws CannotEvaluateException {
0563: final Object array = evaluate(coord);
0564: try {
0565: final int length = Array.getLength(array);
0566: if (dest == null) {
0567: dest = new boolean[length];
0568: }
0569: for (int i = 0; i < length; i++) {
0570: dest[i] = Array.getBoolean(array, i);
0571: }
0572: } catch (IllegalArgumentException exception) {
0573: throw new CannotEvaluateException(
0574: formatErrorMessage(array), exception);
0575: }
0576: return dest;
0577: }
0578:
0579: /**
0580: * Returns a sequence of byte values for a given point in the coverage. A value for each
0581: * {@linkplain SampleDimension sample dimension} is included in the sequence. The default
0582: * interpolation type used when accessing grid values for points which fall between grid
0583: * cells is {@linkplain InterpolationNearest nearest neighbor}, but it can be changed by
0584: * some {@linkplain org.geotools.coverage.grid.Interpolator2D subclasses}. The CRS of the
0585: * point is the same as the grid coverage {@linkplain #getCoordinateReferenceSystem coordinate
0586: * reference system}.
0587: *
0588: * @param coord The coordinate point where to evaluate.
0589: * @param dest An array in which to store values, or {@code null} to create a new array.
0590: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
0591: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0592: * has invalid coordinates.
0593: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0594: * has invalid coordinates.
0595: * @throws CannotEvaluateException if the values can't be computed at the specified coordinate
0596: * for an other reason. It may be thrown if the coverage data type can't be converted
0597: * to {@code byte} by an identity or widening conversion. Subclasses may relax this
0598: * constraint if appropriate.
0599: */
0600: public byte[] evaluate(final DirectPosition coord, byte[] dest)
0601: throws CannotEvaluateException {
0602: final Object array = evaluate(coord);
0603: try {
0604: final int length = Array.getLength(array);
0605: if (dest == null) {
0606: dest = new byte[length];
0607: }
0608: for (int i = 0; i < length; i++) {
0609: dest[i] = Array.getByte(array, i);
0610: }
0611: } catch (IllegalArgumentException exception) {
0612: throw new CannotEvaluateException(
0613: formatErrorMessage(array), exception);
0614: }
0615: return dest;
0616: }
0617:
0618: /**
0619: * Returns a sequence of integer values for a given point in the coverage. A value for each
0620: * {@linkplain SampleDimension sample dimension} is included in the sequence. The default
0621: * interpolation type used when accessing grid values for points which fall between grid
0622: * cells is {@linkplain InterpolationNearest nearest neighbor}, but it can be changed by
0623: * some {@linkplain org.geotools.coverage.grid.Interpolator2D subclasses}. The CRS of the
0624: * point is the same as the grid coverage {@linkplain #getCoordinateReferenceSystem coordinate
0625: * reference system}.
0626: *
0627: * @param coord The coordinate point where to evaluate.
0628: * @param dest An array in which to store values, or {@code null} to create a new array.
0629: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
0630: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0631: * has invalid coordinates.
0632: * @throws CannotEvaluateException if the values can't be computed at the specified coordinate
0633: * for an other reason. It may be thrown if the coverage data type can't be converted
0634: * to {@code int} by an identity or widening conversion. Subclasses may relax this
0635: * constraint if appropriate.
0636: */
0637: public int[] evaluate(final DirectPosition coord, int[] dest)
0638: throws CannotEvaluateException {
0639: final Object array = evaluate(coord);
0640: try {
0641: final int length = Array.getLength(array);
0642: if (dest == null) {
0643: dest = new int[length];
0644: }
0645: for (int i = 0; i < length; i++) {
0646: dest[i] = Array.getInt(array, i);
0647: }
0648: } catch (IllegalArgumentException exception) {
0649: throw new CannotEvaluateException(
0650: formatErrorMessage(array), exception);
0651: }
0652: return dest;
0653: }
0654:
0655: /**
0656: * Returns a sequence of float values for a given point in the coverage. A value for each
0657: * {@linkplain SampleDimension sample dimension} is included in the sequence. The default
0658: * interpolation type used when accessing grid values for points which fall between grid
0659: * cells is {@linkplain InterpolationNearest nearest neighbor}, but it can be changed by
0660: * some {@linkplain org.geotools.coverage.grid.Interpolator2D subclasses}. The CRS of the
0661: * point is the same as the grid coverage {@linkplain #getCoordinateReferenceSystem coordinate
0662: * reference system}.
0663: *
0664: * @param coord The coordinate point where to evaluate.
0665: * @param dest An array in which to store values, or {@code null} to create a new array.
0666: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
0667: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0668: * has invalid coordinates.
0669: * @throws CannotEvaluateException if the values can't be computed at the specified coordinate
0670: * for an other reason. It may be thrown if the coverage data type can't be converted
0671: * to {@code float} by an identity or widening conversion. Subclasses may relax this
0672: * constraint if appropriate.
0673: */
0674: public float[] evaluate(final DirectPosition coord, float[] dest)
0675: throws CannotEvaluateException {
0676: final Object array = evaluate(coord);
0677: try {
0678: final int length = Array.getLength(array);
0679: if (dest == null) {
0680: dest = new float[length];
0681: }
0682: for (int i = 0; i < length; i++) {
0683: dest[i] = Array.getFloat(array, i);
0684: }
0685: } catch (IllegalArgumentException exception) {
0686: throw new CannotEvaluateException(
0687: formatErrorMessage(array), exception);
0688: }
0689: return dest;
0690: }
0691:
0692: /**
0693: * Returns a sequence of double values for a given point in the coverage. A value for each
0694: * {@linkplain SampleDimension sample dimension} is included in the sequence. The default
0695: * interpolation type used when accessing grid values for points which fall between grid
0696: * cells is {@linkplain InterpolationNearest nearest neighbor}, but it can be changed by
0697: * some {@linkplain org.geotools.coverage.grid.Interpolator2D subclasses}. The CRS of the
0698: * point is the same as the grid coverage {@linkplain #getCoordinateReferenceSystem coordinate
0699: * reference system}.
0700: *
0701: * @param coord The coordinate point where to evaluate.
0702: * @param dest An array in which to store values, or {@code null} to create a new array.
0703: * @return The {@code dest} array, or a newly created array if {@code dest} was null.
0704: * @throws PointOutsideCoverageException if the evaluation failed because the input point
0705: * has invalid coordinates.
0706: * @throws CannotEvaluateException if the values can't be computed at the specified coordinate
0707: * for an other reason. It may be thrown if the coverage data type can't be converted
0708: * to {@code double} by an identity or widening conversion. Subclasses may relax this
0709: * constraint if appropriate.
0710: */
0711: public double[] evaluate(final DirectPosition coord, double[] dest)
0712: throws CannotEvaluateException {
0713: final Object array = evaluate(coord);
0714: try {
0715: final int length = Array.getLength(array);
0716: if (dest == null) {
0717: dest = new double[length];
0718: }
0719: for (int i = 0; i < length; i++) {
0720: dest[i] = Array.getDouble(array, i);
0721: }
0722: } catch (IllegalArgumentException exception) {
0723: throw new CannotEvaluateException(
0724: formatErrorMessage(array), exception);
0725: }
0726: return dest;
0727: }
0728:
0729: /**
0730: * Returns a set of {@linkplain DomainObject domain objects} for the specified record of feature
0731: * attribute values. Normally, this method returns the set of {@linkplain DomainObject objects}
0732: * in the domain that are associated with values equal to those in the input record. However,
0733: * the operation may return other {@linkplain DomainObject objects} derived from those in the
0734: * domain, as specified by the application schema.
0735: * <p>
0736: * <B>Example:</B> The {@code evaluateInverse} operation could return a set
0737: * of contours derived from the feature attribute values associated with the
0738: * {@linkplain org.opengis.coverage.grid.GridPoint grid points} of a grid coverage.
0739: * <p>
0740: * <strong>This method is not yet implemented.</strong>
0741: *
0742: * @since 2.3
0743: */
0744: public Set evaluateInverse(Record v) {
0745: throw unsupported();
0746: }
0747:
0748: /**
0749: * Returns 2D view of this grid coverage as a renderable image. This method
0750: * allows interoperability with Java2D.
0751: *
0752: * @param xAxis Dimension to use for the <var>x</var> display axis.
0753: * @param yAxis Dimension to use for the <var>y</var> display axis.
0754: * @return A 2D view of this grid coverage as a renderable image.
0755: */
0756: public RenderableImage getRenderableImage(final int xAxis,
0757: final int yAxis) {
0758: return new Renderable(xAxis, yAxis);
0759: }
0760:
0761: /////////////////////////////////////////////////////////////////////////
0762: //////////////// ////////////////
0763: //////////////// RenderableImage / ImageFunction ////////////////
0764: //////////////// ////////////////
0765: /////////////////////////////////////////////////////////////////////////
0766:
0767: /**
0768: * A view of a {@linkplain AbstractCoverage coverage} as a renderable image. Renderable images
0769: * allow interoperability with <A HREF="http://java.sun.com/products/java-media/2D/">Java2D</A>
0770: * for a two-dimensional slice of a coverage (which may or may not be a
0771: * {@linkplain org.geotools.coverage.grid.GridCoverage2D grid coverage}).
0772: *
0773: * @version $Id: AbstractCoverage.java 27848 2007-11-12 13:10:32Z desruisseaux $
0774: * @author Martin Desruisseaux
0775: *
0776: * @see AbstractCoverage#getRenderableImage
0777: */
0778: protected class Renderable extends PropertySourceImpl implements
0779: RenderableImage, ImageFunction {
0780: /**
0781: * The two dimensional view of the coverage's envelope.
0782: */
0783: private final Rectangle2D bounds;
0784:
0785: /**
0786: * Dimension to use for <var>x</var> axis.
0787: */
0788: protected final int xAxis;
0789:
0790: /**
0791: * Dimension to use for <var>y</var> axis.
0792: */
0793: protected final int yAxis;
0794:
0795: /**
0796: * A coordinate point where to evaluate the function. The point dimension is equals to the
0797: * {@linkplain AbstractCoverage#getDimension coverage's dimension}. The {@linkplain #xAxis
0798: * x} and {@link #yAxis y} ordinates will be ignored, since they will vary for each pixel
0799: * to be evaluated. Other ordinates, if any, should be set to a fixed value. For example a
0800: * coverage may be three-dimensional, where the third dimension is the time axis. In such
0801: * case, {@code coordinate.ord[2]} should be set to the point in time where to evaluate the
0802: * coverage. By default, all ordinates are initialized to 0. Subclasses should set the
0803: * desired values in their constructor if needed.
0804: */
0805: protected final GeneralDirectPosition coordinate = new GeneralDirectPosition(
0806: getDimension());
0807:
0808: /**
0809: * Constructs a renderable image.
0810: *
0811: * @param xAxis Dimension to use for <var>x</var> axis.
0812: * @param yAxis Dimension to use for <var>y</var> axis.
0813: */
0814: public Renderable(final int xAxis, final int yAxis) {
0815: super (null, AbstractCoverage.this );
0816: this .xAxis = xAxis;
0817: this .yAxis = yAxis;
0818: final Envelope envelope = getEnvelope();
0819: bounds = new Rectangle2D.Double(envelope.getMinimum(xAxis),
0820: envelope.getMinimum(yAxis), envelope
0821: .getLength(xAxis), envelope
0822: .getLength(yAxis));
0823: }
0824:
0825: /**
0826: * Returns {@code null} to indicate that no source information is available.
0827: */
0828: public Vector getSources() {
0829: return null;
0830: }
0831:
0832: /**
0833: * Returns {@code true} if successive renderings with the same arguments
0834: * may produce different results. The default implementation returns {@code false}.
0835: *
0836: * @see org.geotools.coverage.grid.GridCoverage2D#isDataEditable
0837: */
0838: public boolean isDynamic() {
0839: return false;
0840: }
0841:
0842: /**
0843: * Returns {@code false} since values are not complex.
0844: */
0845: public boolean isComplex() {
0846: return false;
0847: }
0848:
0849: /**
0850: * Gets the width in coverage coordinate space.
0851: *
0852: * @see AbstractCoverage#getEnvelope
0853: * @see AbstractCoverage#getCoordinateReferenceSystem
0854: */
0855: public float getWidth() {
0856: return (float) bounds.getWidth();
0857: }
0858:
0859: /**
0860: * Gets the height in coverage coordinate space.
0861: *
0862: * @see AbstractCoverage#getEnvelope
0863: * @see AbstractCoverage#getCoordinateReferenceSystem
0864: */
0865: public float getHeight() {
0866: return (float) bounds.getHeight();
0867: }
0868:
0869: /**
0870: * Gets the minimum <var>X</var> coordinate of the rendering-independent image
0871: * data. This is the {@linkplain AbstractCoverage#getEnvelope coverage's envelope}
0872: * minimal value for the {@linkplain #xAxis x axis}.
0873: *
0874: * @see AbstractCoverage#getEnvelope
0875: * @see AbstractCoverage#getCoordinateReferenceSystem
0876: */
0877: public float getMinX() {
0878: return (float) bounds.getX();
0879: }
0880:
0881: /**
0882: * Gets the minimum <var>Y</var> coordinate of the rendering-independent image
0883: * data. This is the {@linkplain AbstractCoverage#getEnvelope coverage's envelope}
0884: * minimal value for the {@linkplain #yAxis y axis}.
0885: *
0886: * @see AbstractCoverage#getEnvelope
0887: * @see AbstractCoverage#getCoordinateReferenceSystem
0888: */
0889: public float getMinY() {
0890: return (float) bounds.getY();
0891: }
0892:
0893: /**
0894: * Returns a rendered image with a default width and height in pixels.
0895: *
0896: * @return A rendered image containing the rendered data
0897: */
0898: public RenderedImage createDefaultRendering() {
0899: return createScaledRendering(512, 0, null);
0900: }
0901:
0902: /**
0903: * Creates a rendered image with width {@code width} and height {@code height} in pixels.
0904: * If {@code width} is 0, it will be computed automatically from {@code height}. Conversely,
0905: * if {@code height} is 0, il will be computed automatically from {@code width}.
0906: * <p>
0907: * The default implementation creates a render context with {@link #createRenderContext}
0908: * and invokes {@link #createRendering(RenderContext)}.
0909: *
0910: * @param width The width of rendered image in pixels, or 0.
0911: * @param height The height of rendered image in pixels, or 0.
0912: * @param hints Rendering hints, or {@code null}.
0913: * @return A rendered image containing the rendered data
0914: */
0915: public RenderedImage createScaledRendering(int width,
0916: int height, final RenderingHints hints) {
0917: final double boundsWidth = bounds.getWidth();
0918: final double boundsHeight = bounds.getHeight();
0919: if (!(width > 0)) { // Use '!' in order to catch NaN
0920: if (!(height > 0)) {
0921: throw new IllegalArgumentException(Errors
0922: .format(ErrorKeys.UNSPECIFIED_IMAGE_SIZE));
0923: }
0924: width = (int) Math.round(height
0925: * (boundsWidth / boundsHeight));
0926: } else if (!(height > 0)) {
0927: height = (int) Math.round(width
0928: * (boundsHeight / boundsWidth));
0929: }
0930: return createRendering(createRenderContext(new Rectangle(0,
0931: 0, width, height), hints));
0932: }
0933:
0934: /**
0935: * Creates a rendered image using a given render context. This method will uses an
0936: * "{@link ImageFunctionDescriptor ImageFunction}" operation if possible (i.e. if
0937: * the area of interect is rectangular and the affine transform contains only
0938: * translation and scale coefficients).
0939: *
0940: * @param context The render context to use to produce the rendering.
0941: * @return A rendered image containing the rendered data
0942: */
0943: public RenderedImage createRendering(final RenderContext context) {
0944: final AffineTransform crsToGrid = context.getTransform();
0945: final Shape area = context.getAreaOfInterest();
0946: final Rectangle gridBounds;
0947: if (true) {
0948: /*
0949: * Computes the grid bounds for the coverage bounds (or the area of interest).
0950: * The default implementation of Rectangle uses Math.floor and Math.ceil for
0951: * computing a box which contains fully the Rectangle2D. But in our particular
0952: * case, we really want to round toward the nearest integer.
0953: */
0954: final Rectangle2D bounds = XAffineTransform.transform(
0955: crsToGrid, (area != null) ? area.getBounds2D()
0956: : this .bounds, null);
0957: final int xmin = (int) Math.round(bounds.getMinX());
0958: final int ymin = (int) Math.round(bounds.getMinY());
0959: final int xmax = (int) Math.round(bounds.getMaxX());
0960: final int ymax = (int) Math.round(bounds.getMaxY());
0961: gridBounds = new Rectangle(xmin, ymin, xmax - xmin,
0962: ymax - ymin);
0963: }
0964: /*
0965: * Computes some properties of the image to be created.
0966: */
0967: final Dimension tileSize = ImageUtilities
0968: .toTileSize(gridBounds.getSize());
0969: final GridSampleDimension band = GridSampleDimension
0970: .wrap(getSampleDimension(VISIBLE_BAND));
0971: final ColorModel colorModel = band.getColorModel(
0972: VISIBLE_BAND, getNumSampleDimensions());
0973: final SampleModel sampleModel = colorModel
0974: .createCompatibleSampleModel(tileSize.width,
0975: tileSize.height);
0976: /*
0977: * If the image can be created using the ImageFunction operation, do it.
0978: * It allow JAI to defer the computation until a tile is really requested.
0979: */
0980: final PlanarImage image;
0981: if ((area == null || area instanceof Rectangle2D)
0982: && crsToGrid.getShearX() == 0
0983: && crsToGrid.getShearY() == 0) {
0984: image = JAI
0985: .create(
0986: "ImageFunction",
0987: new ParameterBlock()
0988: .add(this )
0989: // The functional description
0990: .add(gridBounds.width)
0991: // The image width
0992: .add(gridBounds.height)
0993: // The image height
0994: .add(
0995: (float) (1 / crsToGrid
0996: .getScaleX()))
0997: // The X scale factor
0998: .add(
0999: (float) (1 / crsToGrid
1000: .getScaleY()))
1001: // The Y scale factor
1002: .add(
1003: (float) crsToGrid
1004: .getTranslateX()) // The X translation
1005: .add(
1006: (float) crsToGrid
1007: .getTranslateY()), // The Y translation
1008: new RenderingHints(
1009: JAI.KEY_IMAGE_LAYOUT,
1010: new ImageLayout()
1011: .setMinX(gridBounds.x)
1012: .setMinY(gridBounds.y)
1013: .setTileWidth(
1014: tileSize.width)
1015: .setTileHeight(
1016: tileSize.height)
1017: .setSampleModel(
1018: sampleModel)
1019: .setColorModel(
1020: colorModel)));
1021: } else {
1022: /*
1023: * Creates immediately a rendered image using a given render context. This block
1024: * is run when the image can't be created with JAI's ImageFunction operator, for
1025: * example because the affine transform swap axis or because there is an area of
1026: * interest.
1027: */
1028: // Clones the coordinate point in order to allow multi-thread
1029: // invocation.
1030: final GeneralDirectPosition coordinate = new GeneralDirectPosition(
1031: this .coordinate);
1032: final TiledImage tiled = new TiledImage(gridBounds.x,
1033: gridBounds.y, gridBounds.width,
1034: gridBounds.height, 0, 0, sampleModel,
1035: colorModel);
1036: final Point2D.Double point2D = new Point2D.Double();
1037: final int numBands = tiled.getNumBands();
1038: final double[] samples = new double[numBands];
1039: final double[] padNaNs = new double[numBands];
1040: Arrays.fill(padNaNs, Double.NaN);
1041: final WritableRectIter iterator = RectIterFactory
1042: .createWritable(tiled, gridBounds);
1043: if (!iterator.finishedLines())
1044: try {
1045: int y = gridBounds.y;
1046: do {
1047: iterator.startPixels();
1048: if (!iterator.finishedPixels()) {
1049: int x = gridBounds.x;
1050: do {
1051: point2D.x = x;
1052: point2D.y = y;
1053: crsToGrid.inverseTransform(point2D,
1054: point2D);
1055: if (area == null
1056: || area.contains(point2D)) {
1057: coordinate.ordinates[xAxis] = point2D.x;
1058: coordinate.ordinates[yAxis] = point2D.y;
1059: iterator.setPixel(evaluate(
1060: coordinate, samples));
1061: } else {
1062: iterator.setPixel(padNaNs);
1063: }
1064: x++;
1065: } while (!iterator.nextPixelDone());
1066: assert (x == gridBounds.x
1067: + gridBounds.width);
1068: y++;
1069: }
1070: } while (!iterator.nextLineDone());
1071: assert (y == gridBounds.y + gridBounds.height);
1072: } catch (NoninvertibleTransformException exception) {
1073: // TODO: give cause to constructor when we will be allowed to target J2SE 1.5.
1074: final IllegalArgumentException e = new IllegalArgumentException(
1075: Errors.format(
1076: ErrorKeys.ILLEGAL_ARGUMENT_$1,
1077: "context"));
1078: e.initCause(exception);
1079: throw e;
1080: }
1081: image = tiled;
1082: }
1083: /*
1084: * Adds a 'gridToCRS' property to the image. This is an important
1085: * information for constructing a GridCoverage from this image later.
1086: */
1087: try {
1088: image.setProperty("gridToCRS", crsToGrid
1089: .createInverse());
1090: } catch (NoninvertibleTransformException exception) {
1091: // Can't add the property. Too bad, the image has been created
1092: // anyway. Maybe the user know what he is doing...
1093: Logging.unexpectedException("org.geotools.coverage",
1094: Renderable.class, "createRendering", exception);
1095: }
1096: return image;
1097: }
1098:
1099: /**
1100: * Initializes a render context with an affine transform that maps the coverage envelope
1101: * to the specified destination rectangle. The affine transform mays swap axis in order to
1102: * normalize their order (i.e. make them appear in the (<var>x</var>,<var>y</var>) order),
1103: * so that the image appears properly oriented when rendered.
1104: *
1105: * @param gridBounds The two-dimensional destination rectangle.
1106: * @param hints The rendering hints, or {@code null} if none.
1107: * @return A render context initialized with an affine transform from the coverage
1108: * to the grid coordinate system. This transform is the inverse of
1109: * {@link org.geotools.coverage.grid.GridGeometry2D#getGridToCRS2D}.
1110: *
1111: * @see org.geotools.coverage.grid.GridGeometry2D#getGridToCRS2D
1112: */
1113: protected RenderContext createRenderContext(
1114: final Rectangle2D gridBounds, final RenderingHints hints) {
1115: final GeneralMatrix matrix;
1116: final GeneralEnvelope srcEnvelope = new GeneralEnvelope(
1117: bounds);
1118: final GeneralEnvelope dstEnvelope = new GeneralEnvelope(
1119: gridBounds);
1120: if (crs != null) {
1121: final CoordinateSystem cs = crs.getCoordinateSystem();
1122: final AxisDirection[] axis = new AxisDirection[] {
1123: cs.getAxis(xAxis).getDirection(),
1124: cs.getAxis(yAxis).getDirection() };
1125: final AxisDirection[] normalized = (AxisDirection[]) axis
1126: .clone();
1127: if (false) {
1128: // Normalize axis: Is it really a good idea?
1129: // We should provide a rendering hint for configuring that.
1130: Arrays.sort(normalized);
1131: for (int i = normalized.length; --i >= 0;) {
1132: normalized[i] = normalized[i].absolute();
1133: }
1134: }
1135: normalized[1] = normalized[1].opposite(); // Image's Y axis is downward.
1136: matrix = new GeneralMatrix(srcEnvelope, axis,
1137: dstEnvelope, normalized);
1138: } else {
1139: matrix = new GeneralMatrix(srcEnvelope, dstEnvelope);
1140: }
1141: return new RenderContext(matrix.toAffineTransform2D(),
1142: hints);
1143: }
1144:
1145: /**
1146: * Returns the number of elements per value at each position. This is
1147: * the maximum value plus 1 allowed in {@code getElements(...)} methods
1148: * invocation. The default implementation returns the number of sample
1149: * dimensions in the coverage.
1150: */
1151: public int getNumElements() {
1152: return getNumSampleDimensions();
1153: }
1154:
1155: /**
1156: * Returns all values of a given element for a specified set of coordinates.
1157: * This method is automatically invoked at rendering time for populating an
1158: * image tile, providing that the rendered image is created using the
1159: * "{@link ImageFunctionDescriptor ImageFunction}" operator and the image
1160: * type is not {@code double}. The default implementation invokes
1161: * {@link AbstractCoverage#evaluate(DirectPosition,float[])} recursively.
1162: */
1163: public void getElements(final float startX, final float startY,
1164: final float deltaX, final float deltaY,
1165: final int countX, final int countY, final int element,
1166: final float[] real, final float[] imag) {
1167: int index = 0;
1168: float[] buffer = null;
1169: // Clones the coordinate point in order to allow multi-thread invocation.
1170: final GeneralDirectPosition coordinate = new GeneralDirectPosition(
1171: this .coordinate);
1172: coordinate.ordinates[1] = startY;
1173: for (int j = 0; j < countY; j++) {
1174: coordinate.ordinates[0] = startX;
1175: for (int i = 0; i < countX; i++) {
1176: buffer = evaluate(coordinate, buffer);
1177: real[index++] = buffer[element];
1178: coordinate.ordinates[0] += deltaX;
1179: }
1180: coordinate.ordinates[1] += deltaY;
1181: }
1182: }
1183:
1184: /**
1185: * Returns all values of a given element for a specified set of coordinates.
1186: * This method is automatically invoked at rendering time for populating an
1187: * image tile, providing that the rendered image is created using the
1188: * "{@link ImageFunctionDescriptor ImageFunction}" operator and the image
1189: * type is {@code double}. The default implementation invokes
1190: * {@link AbstractCoverage#evaluate(DirectPosition,double[])} recursively.
1191: */
1192: public void getElements(final double startX,
1193: final double startY, final double deltaX,
1194: final double deltaY, final int countX,
1195: final int countY, final int element,
1196: final double[] real, final double[] imag) {
1197: int index = 0;
1198: double[] buffer = null;
1199: // Clones the coordinate point in order to allow multi-thread invocation.
1200: final GeneralDirectPosition coordinate = new GeneralDirectPosition(
1201: this .coordinate);
1202: coordinate.ordinates[1] = startY;
1203: for (int j = 0; j < countY; j++) {
1204: coordinate.ordinates[0] = startX;
1205: for (int i = 0; i < countX; i++) {
1206: buffer = evaluate(coordinate, buffer);
1207: real[index++] = buffer[element];
1208: coordinate.ordinates[0] += deltaX;
1209: }
1210: coordinate.ordinates[1] += deltaY;
1211: }
1212: }
1213: }
1214:
1215: /**
1216: * Display this coverage in a windows. This convenience method is used for debugging purpose.
1217: * The exact appareance of the windows and the tools provided may changes in future versions.
1218: *
1219: * @param xAxis Dimension to use for the <var>x</var> display axis.
1220: * @param yAxis Dimension to use for the <var>y</var> display axis.
1221: *
1222: * @deprecated Use {@link #show(String, int, int)}.
1223: */
1224: public void show(final int xAxis, final int yAxis) {
1225: show(null, xAxis, yAxis);
1226: }
1227:
1228: /**
1229: * Display this coverage in a windows. This convenience method is used for debugging purpose.
1230: * The exact appareance of the windows and the tools provided may changes in future versions.
1231: *
1232: * @param title The window title, or {@code null} for default value.
1233: * @param xAxis Dimension to use for the <var>x</var> display axis.
1234: * @param yAxis Dimension to use for the <var>y</var> display axis.
1235: *
1236: * @since 2.3
1237: */
1238: public void show(String title, final int xAxis, final int yAxis) {
1239: if (title == null || (title = title.trim()).length() == 0) {
1240: title = String.valueOf(getName());
1241: }
1242: // In the following line, the constructor display immediately the viewer.
1243: new Viewer(title, getRenderableImage(xAxis, yAxis)
1244: .createDefaultRendering());
1245: }
1246:
1247: /**
1248: * A trivial viewer implementation to be used by {@link AbstractCoverage#show(String,int,int)}
1249: * method.
1250: * <p>
1251: * <strong>Implementation note:</strong>
1252: * We use AWT Frame, not Swing JFrame, because {@link ScrollingImagePane} is an AWT
1253: * component. Swing is an overhead in this context without clear benefict. Note also
1254: * that {@code ScrollingImagePanel} includes the scroll bar, so there is no need to
1255: * put this component in an other {@code JScrollPane}.
1256: */
1257: private static final class Viewer extends WindowAdapter implements
1258: Runnable {
1259: /**
1260: * The frame to dispose once closed.
1261: */
1262: private final Frame frame;
1263:
1264: /**
1265: * Displays the specified image in a window with the specified title.
1266: */
1267: public Viewer(final String title, final RenderedImage image) {
1268: final int width = Math.max(Math.min(image.getWidth(), 800),
1269: 24);
1270: final int height = Math.max(Math
1271: .min(image.getHeight(), 600), 24);
1272: frame = new Frame(title);
1273: frame.add(new ScrollingImagePanel(image, width, height));
1274: frame.addWindowListener(this );
1275: EventQueue.invokeLater(this );
1276: }
1277:
1278: /**
1279: * Display the window in the event queue.
1280: * Required because 'pack()' is invoked before 'setVisible(true)'.
1281: */
1282: public void run() {
1283: frame.pack();
1284: frame.setVisible(true);
1285: }
1286:
1287: /**
1288: * Invoked when the user dispose the window.
1289: */
1290: public void windowClosing(WindowEvent e) {
1291: frame.dispose();
1292: }
1293: }
1294:
1295: /**
1296: * Display this coverage in a windows. This convenience method is used for
1297: * debugging purpose. The exact appareance of the windows and the tools
1298: * provided may changes in future versions.
1299: *
1300: * @param title The window title, or {@code null} for default value.
1301: *
1302: * @since 2.3
1303: */
1304: public void show(final String title) {
1305: show(title, 0, 1);
1306: }
1307:
1308: /**
1309: * Displays this coverage in a windows. This convenience method is used for debugging purpose.
1310: * The exact appareance of the windows and the tools provided may changes in future versions.
1311: */
1312: public void show() {
1313: show(null);
1314: }
1315:
1316: /**
1317: * Returns the source data for a coverage. The default implementation returns an empty list.
1318: */
1319: public List getSources() {
1320: return Collections.EMPTY_LIST;
1321: }
1322:
1323: /**
1324: * List of metadata keywords for a coverage. If no metadata is available,
1325: * the sequence will be empty. The default implementation gets the list of
1326: * metadata names from the {@link #getPropertyNames()} method.
1327: *
1328: * @return the list of metadata keywords for a coverage.
1329: *
1330: * @deprecated Use {@link #getPropertyNames()} instead.
1331: */
1332: public String[] getMetadataNames() {
1333: final String[] list = getPropertyNames();
1334: return (list != null) ? list : NO_PROPERTIES;
1335: }
1336:
1337: /**
1338: * Retrieve the metadata value for a given metadata name. The default
1339: * implementation query the {@link #getProperty(String)} method.
1340: *
1341: * @param name Metadata keyword for which to retrieve data.
1342: * @return the metadata value for a given metadata name.
1343: * @throws MetadataNameNotFoundException
1344: * if there is no value for the specified metadata name.
1345: *
1346: * @deprecated Use {@link #getProperty(String)} instead.
1347: */
1348: public String getMetadataValue(final String name)
1349: throws MetadataNameNotFoundException {
1350: final Object value = getProperty(name);
1351: if (value == java.awt.Image.UndefinedProperty) {
1352: throw new MetadataNameNotFoundException(Errors.format(
1353: ErrorKeys.UNDEFINED_PROPERTY_$1, name));
1354: }
1355: return (value != null) ? value.toString() : null;
1356: }
1357:
1358: /**
1359: * Returns the default locale for logging, error messages, <cite>etc.</cite>.
1360: */
1361: public Locale getLocale() {
1362: return Locale.getDefault();
1363: }
1364:
1365: /**
1366: * Returns a string representation of this coverage. This string is for
1367: * debugging purpose only and may change in future version.
1368: */
1369: //@Override
1370: public String toString() {
1371: final StringWriter buffer = new StringWriter();
1372: buffer.write(Utilities.getShortClassName(this ));
1373: buffer.write("[\"");
1374: buffer.write(String.valueOf(getName()));
1375: buffer.write('"');
1376: final Envelope envelope = getEnvelope();
1377: if (envelope != null) {
1378: buffer.write(", ");
1379: buffer.write(envelope.toString());
1380: }
1381: if (crs != null) {
1382: buffer.write(", ");
1383: buffer.write(Utilities.getShortClassName(crs));
1384: buffer.write("[\"");
1385: buffer.write(crs.getName().getCode());
1386: buffer.write("\"]");
1387: }
1388: buffer.write(']');
1389: final String lineSeparator = System.getProperty(
1390: "line.separator", "\n");
1391: final LineWriter filter = new LineWriter(buffer, lineSeparator
1392: + " ");
1393: final int n = getNumSampleDimensions();
1394: try {
1395: filter.write(lineSeparator);
1396: for (int i = 0; i < n; i++) {
1397: filter.write(getSampleDimension(i).toString());
1398: }
1399: filter.flush();
1400: } catch (IOException exception) {
1401: // Should not happen
1402: throw new AssertionError(exception);
1403: }
1404: return buffer.toString();
1405: }
1406:
1407: /**
1408: * Provides a hint that a coverage will no longer be accessed from a reference in user space.
1409: * This can be used as a hint in situations where waiting for garbage collection would be
1410: * overly conservative. The results of referencing a coverage after a call to {@code dispose}
1411: * are undefined, except if this method returned {@code false}.
1412: * <p>
1413: * This method can work in a <cite>conservative</cite> mode or a <cite>forced</cite> mode,
1414: * determined by the {@code force} argument:
1415: *
1416: * <ul>
1417: * <li><p>If {@code force} is {@code false} (the recommanded value), this method may process
1418: * only under some conditions. For example a grid coverage may dispose its planar image only
1419: * if it has no {@linkplain PlanarImage#getSinks sinks}. This method returns {@code true} if
1420: * it disposed at least some resources, or {@code false} if this method vetoed against the
1421: * disposal. In the later case, no resources were disposed and this coverage can still be
1422: * used.</p></li>
1423: *
1424: * <li><p>If {@code force} is {@code true}, then this method processes inconditionnally and
1425: * returns always {@code true}. This is a more risky behavior.</p></li>
1426: * </ul>
1427: *
1428: * The conservative mode ({@code force = false}) performs its safety checks on a
1429: * <cite>best-effort</cite> basis, with no guarantees. Therefore, it would be wrong to write
1430: * a program that depended on the safety checks for its correctness. In case of doubt about
1431: * whatever this coverage still in use or not, it is safer to rely on the garbage collector.
1432: *
1433: * @param force {@code true} for forcing an inconditionnal disposal, or {@code false} for
1434: * performing a conservative disposal. The recommanded value is {@code false}.
1435: * @return {@code true} if this method disposed at least some resources, or {@code false}
1436: * if this method vetoed against the disposal.
1437: *
1438: * @see PlanarImage#dispose
1439: *
1440: * @since 2.4
1441: */
1442: public boolean dispose(boolean force) {
1443: return true;
1444: }
1445:
1446: /**
1447: * @deprecated Use {@link #dispose(boolean)} instead.
1448: */
1449: public void dispose() {
1450: dispose(false);
1451: }
1452: }
|