001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2004, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.coverage.grid;
021:
022: // J2SE dependencies
023: import java.awt.geom.Point2D;
024: import java.text.FieldPosition;
025: import java.text.NumberFormat;
026: import java.util.Arrays;
027: import java.util.Collections;
028: import java.util.List;
029: import java.util.Locale;
030: import java.util.Map;
031: import java.util.logging.Logger;
032:
033: // JAI dependencies
034: import javax.media.jai.PlanarImage;
035: import javax.media.jai.PropertySource;
036: import javax.media.jai.util.CaselessStringKey; // For javadoc
037:
038: // OpenGIS dependencies
039: import org.opengis.coverage.grid.GridCoverage;
040: import org.opengis.coverage.grid.GridGeometry;
041: import org.opengis.coverage.grid.GridPacking;
042: import org.opengis.coverage.grid.GridRange;
043: import org.opengis.coverage.grid.GridNotEditableException;
044: import org.opengis.coverage.grid.InvalidRangeException;
045: import org.opengis.referencing.crs.CoordinateReferenceSystem;
046: import org.opengis.geometry.DirectPosition;
047:
048: // Geotools dependencies
049: import org.geotools.coverage.AbstractCoverage;
050: import org.geotools.geometry.DirectPosition2D;
051: import org.geotools.util.logging.Logging;
052: import org.geotools.resources.i18n.Errors;
053: import org.geotools.resources.i18n.ErrorKeys;
054:
055: /**
056: * Base class for Geotools implementation of grid coverage.
057: *
058: * @since 2.1
059: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/main/java/org/geotools/coverage/grid/AbstractGridCoverage.java $
060: * @version $Id: AbstractGridCoverage.java 27862 2007-11-12 19:51:19Z desruisseaux $
061: * @author Martin Desruisseaux
062: */
063: public abstract class AbstractGridCoverage extends AbstractCoverage
064: implements GridCoverage {
065: /**
066: * For compatibility during cross-version serialization.
067: */
068: private static final long serialVersionUID = 6476934258101450793L;
069:
070: /**
071: * The logger for grid coverage operations.
072: */
073: public static final Logger LOGGER = Logging
074: .getLogger("org.geotools.coverage.grid");
075:
076: /**
077: * Sources grid coverage, or {@code null} if none. This information is lost during
078: * serialization, in order to avoid sending a too large amount of data over the network.
079: */
080: private final transient List sources;
081:
082: /**
083: * Constructs a grid coverage using the specified coordinate reference system. If the
084: * coordinate reference system is {@code null}, then the subclasses must override
085: * {@link #getDimension()}.
086: *
087: * @param name The grid coverage name.
088: * @param crs The coordinate reference system. This specifies the coordinate
089: * system used when accessing a coverage or grid coverage with the
090: * {@code evaluate(...)} methods.
091: * @param source The source for this coverage, or {@code null} if none.
092: * Source may be (but is not limited to) a {@link PlanarImage} or an
093: * other {@code AbstractGridCoverage} object.
094: * @param properties The set of properties for this coverage, or {@code null} if there is none.
095: * "Properties" in <cite>Java Advanced Imaging</cite> is what OpenGIS calls "Metadata".
096: * Keys are {@link String} objects ({@link CaselessStringKey} are accepted as well),
097: * while values may be any {@link Object}.
098: */
099: protected AbstractGridCoverage(final CharSequence name,
100: final CoordinateReferenceSystem crs,
101: final PropertySource source, final Map properties) {
102: super (name, crs, source, properties);
103: sources = null;
104: }
105:
106: /**
107: * Constructs a grid coverage with sources. Arguments are the same than for the
108: * {@linkplain #AbstractGridCoverage(CharSequence,CoordinateReferenceSystem,PropertySource,Map)
109: * previous constructor}, with an additional {@code sources} argument.
110: *
111: * @param name The grid coverage name.
112: * @param crs The coordinate reference system.
113: * @param sources The {@linkplain #getSources source data} for a grid coverage,
114: * or {@code null} if none.
115: * @param source The source for properties for this coverage, or {@code null} if none.
116: * @param properties Set of additional properties for this coverage, or {@code null} if there
117: * is none.
118: */
119: protected AbstractGridCoverage(final CharSequence name,
120: final CoordinateReferenceSystem crs,
121: final GridCoverage[] sources, final PropertySource source,
122: final Map properties) {
123: super (name, crs, source, properties);
124: if (sources != null) {
125: switch (sources.length) {
126: case 0:
127: this .sources = null;
128: break;
129: case 1:
130: this .sources = Collections.singletonList(sources[0]);
131: break;
132: default:
133: this .sources = Collections.unmodifiableList(Arrays
134: .asList((GridCoverage[]) sources.clone()));
135: }
136: } else {
137: this .sources = null;
138: }
139: }
140:
141: /**
142: * Constructs a new coverage with the same parameters than the specified coverage.
143: *
144: * @param name The name for this coverage, or {@code null} for the same than {@code coverage}.
145: * @param coverage The source coverage.
146: */
147: protected AbstractGridCoverage(final CharSequence name,
148: final GridCoverage coverage) {
149: super (name, coverage);
150: sources = Collections.singletonList(coverage);
151: }
152:
153: /**
154: * Returns the source data for a grid coverage. If the {@code GridCoverage} was produced from
155: * an underlying dataset, the returned list is an empty list. If the {@code GridCoverage} was
156: * produced using {@link org.opengis.coverage.grid.GridCoverageProcessor}, then it should
157: * return the source grid coverage of the one used as input to {@code GridCoverageProcessor}.
158: * In general the {@code getSources()} method is intended to return the original
159: * {@code GridCoverage} on which it depends. This is intended to allow applications
160: * to establish what {@code GridCoverage}s will be affected when others are updated,
161: * as well as to trace back to the "raw data".
162: */
163: public List getSources() {
164: return (sources != null) ? sources : Collections.EMPTY_LIST;
165: }
166:
167: /**
168: * Returns {@code true} if grid data can be edited. The default
169: * implementation returns {@code false}.
170: */
171: public boolean isDataEditable() {
172: return false;
173: }
174:
175: /**
176: * Returns the number of predetermined overviews for the grid.
177: * The default implementation returns 0.
178: */
179: public int getNumOverviews() {
180: return 0;
181: }
182:
183: /**
184: * Returns the grid geometry for an overview. The default implementation always throws
185: * an exception, since the default {@linkplain #getNumOverviews number of overviews} is 0.
186: *
187: * @throws IndexOutOfBoundsException if the specified index is out of bounds.
188: */
189: public GridGeometry getOverviewGridGeometry(int index)
190: throws IndexOutOfBoundsException {
191: throw new IndexOutOfBoundsException(indexOutOfBounds(index));
192: }
193:
194: /**
195: * Returns a pre-calculated overview for a grid coverage. The default implementation always
196: * throws an exception, since the default {@linkplain #getNumOverviews number of overviews}
197: * is 0.
198: *
199: * @throws IndexOutOfBoundsException if the specified index is out of bounds.
200: */
201: public GridCoverage getOverview(int index)
202: throws IndexOutOfBoundsException {
203: throw new IndexOutOfBoundsException(indexOutOfBounds(index));
204: }
205:
206: /**
207: * Returns information for the packing of grid coverage values.
208: * The default implementation throws an {@link UnsupportedOperationException}.
209: * We don't know at this time if and when this method will be implemented, since
210: * the API is going to change when we will shift to ISO 19123.
211: *
212: * @deprecated Not yet implemented, and maybe will never be implemented.
213: */
214: public GridPacking getGridPacking() {
215: throw new UnsupportedOperationException("Not yet implemented");
216: }
217:
218: /**
219: * Returns a block of grid coverage data for all sample dimensions.
220: * The default implementation throws an {@link UnsupportedOperationException}.
221: * We don't know at this time if and when this method will be implemented, since
222: * the API is going to change when we will shift to ISO 19123.
223: */
224: public byte[] getPackedDataBlock(final GridRange range)
225: throws InvalidRangeException {
226: throw new UnsupportedOperationException("Not yet implemented");
227: }
228:
229: /**
230: * Returns a sequence of byte values for a block.
231: * The default implementation throws an {@link UnsupportedOperationException}.
232: * We don't know at this time if and when this method will be implemented, since
233: * the API is going to change when we will shift to ISO 19123.
234: */
235: public boolean[] getDataBlock(final GridRange range,
236: boolean[] destination) throws InvalidRangeException,
237: ArrayIndexOutOfBoundsException {
238: throw new UnsupportedOperationException("Not yet implemented");
239: }
240:
241: /**
242: * Returns a sequence of byte values for a block.
243: * The default implementation throws an {@link UnsupportedOperationException}.
244: * We don't know at this time if and when this method will be implemented, since
245: * the API is going to change when we will shift to ISO 19123.
246: */
247: public byte[] getDataBlock(final GridRange range, byte[] destination)
248: throws InvalidRangeException,
249: ArrayIndexOutOfBoundsException {
250: throw new UnsupportedOperationException("Not yet implemented");
251: }
252:
253: /**
254: * Returns a sequence of short values for a block.
255: * The default implementation throws an {@link UnsupportedOperationException}.
256: * We don't know at this time if and when this method will be implemented, since
257: * the API is going to change when we will shift to ISO 19123.
258: */
259: public short[] getDataBlock(final GridRange range,
260: short[] destination) throws InvalidRangeException,
261: ArrayIndexOutOfBoundsException {
262: throw new UnsupportedOperationException("Not yet implemented");
263: }
264:
265: /**
266: * Returns a sequence of integer values for a block.
267: * The default implementation throws an {@link UnsupportedOperationException}.
268: * We don't know at this time if and when this method will be implemented, since
269: * the API is going to change when we will shift to ISO 19123.
270: */
271: public int[] getDataBlock(final GridRange range, int[] destination)
272: throws InvalidRangeException,
273: ArrayIndexOutOfBoundsException {
274: throw new UnsupportedOperationException("Not yet implemented");
275: }
276:
277: /**
278: * Returns a sequence of float values for a block.
279: * The default implementation throws an {@link UnsupportedOperationException}.
280: * We don't know at this time if and when this method will be implemented, since
281: * the API is going to change when we will shift to ISO 19123.
282: */
283: public float[] getDataBlock(final GridRange range,
284: final float[] destination) throws InvalidRangeException,
285: ArrayIndexOutOfBoundsException {
286: throw new UnsupportedOperationException("Not yet implemented");
287: }
288:
289: /**
290: * Returns a sequence of double values for a block.
291: * The default implementation throws an {@link UnsupportedOperationException}.
292: * We don't know at this time if and when this method will be implemented, since
293: * the API is going to change when we will shift to ISO 19123.
294: */
295: public double[] getDataBlock(final GridRange range,
296: final double[] destination) throws InvalidRangeException,
297: ArrayIndexOutOfBoundsException {
298: throw new UnsupportedOperationException("Not yet implemented");
299: }
300:
301: /**
302: * Set a block of values for all sample dimensions. The default implementation always throws
303: * an exception, since this grid coverage is not editable by default.
304: */
305: public void setDataBlock(GridRange gridRange, boolean[] values)
306: throws InvalidRangeException, GridNotEditableException,
307: ArrayIndexOutOfBoundsException {
308: throw new GridNotEditableException(); // TODO: provides a localized message.
309: }
310:
311: /**
312: * Set a block of values for all sample dimensions. The default implementation always throws
313: * an exception, since this grid coverage is not editable by default.
314: */
315: public void setDataBlock(GridRange gridRange, byte[] values)
316: throws InvalidRangeException, GridNotEditableException,
317: ArrayIndexOutOfBoundsException {
318: throw new GridNotEditableException(); // TODO: provides a localized message.
319: }
320:
321: /**
322: * Set a block of values for all sample dimensions. The default implementation always throws
323: * an exception, since this grid coverage is not editable by default.
324: */
325: public void setDataBlock(GridRange gridRange, short[] values)
326: throws InvalidRangeException, GridNotEditableException,
327: ArrayIndexOutOfBoundsException {
328: throw new GridNotEditableException(); // TODO: provides a localized message.
329: }
330:
331: /**
332: * Set a block of values for all sample dimensions. The default implementation always throws
333: * an exception, since this grid coverage is not editable by default.
334: */
335: public void setDataBlock(GridRange gridRange, int[] values)
336: throws InvalidRangeException, GridNotEditableException,
337: ArrayIndexOutOfBoundsException {
338: throw new GridNotEditableException(); // TODO: provides a localized message.
339: }
340:
341: /**
342: * Set a block of values for all sample dimensions. The default implementation always throws
343: * an exception, since this grid coverage is not editable by default.
344: */
345: public void setDataBlock(GridRange gridRange, float[] values)
346: throws InvalidRangeException, GridNotEditableException,
347: ArrayIndexOutOfBoundsException {
348: throw new GridNotEditableException(); // TODO: provides a localized message.
349: }
350:
351: /**
352: * Set a block of values for all sample dimensions. The default implementation always throws
353: * an exception, since this grid coverage is not editable by default.
354: */
355: public void setDataBlock(GridRange gridRange, double[] values)
356: throws InvalidRangeException, GridNotEditableException,
357: ArrayIndexOutOfBoundsException {
358: throw new GridNotEditableException(); // TODO: provides a localized message.
359: }
360:
361: /**
362: * Set a block of values for all sample dimensions. The default implementation always throws
363: * an exception, since this grid coverage is not editable by default.
364: *
365: * @deprecated This operation can hardly be implemented efficiently in Java with a
366: * {@code byte[]} argument type, since we can't easily cast an array
367: * of {@code byte[]} to an array of arbitrary type.
368: */
369: public void setPackedDataBlock(GridRange gridRange, byte[] values)
370: throws InvalidRangeException, GridNotEditableException,
371: ArrayIndexOutOfBoundsException {
372: throw new GridNotEditableException(); // TODO: provides a localized message.
373: }
374:
375: /**
376: * Returns a localized error message for {@link IndexOutOfBoundsException}.
377: */
378: private String indexOutOfBounds(final int index) {
379: return Errors.getResources(getLocale()).getString(
380: ErrorKeys.ILLEGAL_ARGUMENT_$2, "index",
381: new Integer(index));
382: }
383:
384: /**
385: * Constructs an error message for a point outside the coverage.
386: * This is used for formatting error messages.
387: *
388: * @param point The coordinate point to format.
389: * @return An error message.
390: */
391: protected String pointOutsideCoverage(final Point2D point) {
392: return pointOutsideCoverage((DirectPosition) new DirectPosition2D(
393: point));
394: }
395:
396: /**
397: * Constructs an error message for a point outside the coverage.
398: * This is used for formatting error messages.
399: *
400: * @param point The coordinate point to format.
401: * @return An error message.
402: */
403: protected String pointOutsideCoverage(final DirectPosition point) {
404: final Locale locale = getLocale();
405: return Errors.getResources(locale).getString(
406: ErrorKeys.POINT_OUTSIDE_COVERAGE_$1,
407: toString(point, locale));
408: }
409:
410: /**
411: * Constructs a string for the specified point.
412: * This is used for formatting error messages.
413: *
414: * @param point The coordinate point to format.
415: * @param locale The locale for formatting numbers.
416: * @return The coordinate point as a string, without '(' or ')' characters.
417: */
418: static String toString(final Point2D point, final Locale locale) {
419: return toString((DirectPosition) new DirectPosition2D(point),
420: locale);
421: }
422:
423: /**
424: * Constructs a string for the specified point.
425: * This is used for formatting error messages.
426: *
427: * @param point The coordinate point to format.
428: * @param locale The locale for formatting numbers.
429: * @return The coordinate point as a string, without '(' or ')' characters.
430: */
431: static String toString(final DirectPosition point,
432: final Locale locale) {
433: final StringBuffer buffer = new StringBuffer();
434: final FieldPosition dummy = new FieldPosition(0);
435: final NumberFormat format = NumberFormat
436: .getNumberInstance(locale);
437: final int dimension = point.getDimension();
438: for (int i = 0; i < dimension; i++) {
439: if (i != 0) {
440: buffer.append(", ");
441: }
442: format.format(point.getOrdinate(i), buffer, dummy);
443: }
444: return buffer.toString();
445: }
446: }
|