001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, GeoTools Project Managment Committee (PMC)
005: * (C) 2007, Geomatys
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;
010: * version 2.1 of the License.
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: package org.geotools.image.io.metadata;
018:
019: // J2SE dependencies
020: import java.util.List;
021: import javax.imageio.ImageTypeSpecifier;
022: import javax.imageio.metadata.IIOMetadataFormatImpl;
023:
024: // OpenGIS dependencies
025: import org.opengis.geometry.Envelope;
026: import org.opengis.coverage.SampleDimension;
027: import org.opengis.coverage.grid.GridGeometry;
028: import org.opengis.referencing.crs.CoordinateReferenceSystem;
029: import org.opengis.referencing.cs.CoordinateSystem;
030: import org.opengis.referencing.cs.CoordinateSystemAxis;
031: import org.opengis.referencing.datum.Datum;
032: import org.opengis.referencing.operation.MathTransform;
033:
034: // Geotools dependencies
035: import org.geotools.resources.UnmodifiableArrayList;
036:
037: /**
038: * Describes the structure of {@linkplain GeographicMetadata geographic metadata}.
039: * The following formatting rules apply:
040: * <p>
041: * <ul>
042: * <li>Numbers must be formatted as in the {@linkplain Locale#US US locale}, i.e.
043: * as {@link Integer#toString(int)} or {@link Double#toString(double)}.</li>
044: * <li>Dates must be formatted with the {@code "yyyy-MM-dd HH:mm:ss"}
045: * {@linkplain SimpleDateFormat pattern} in UTC {@linkplain TimeZone timezone}.</li>
046: * </ul>
047: *
048: * @since 2.4
049: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/coverageio/src/main/java/org/geotools/image/io/metadata/GeographicMetadataFormat.java $
050: * @version $Id: GeographicMetadataFormat.java 27583 2007-10-23 11:29:26Z desruisseaux $
051: * @author Martin Desruisseaux
052: */
053: public class GeographicMetadataFormat extends IIOMetadataFormatImpl {
054: /**
055: * The metadata format name.
056: */
057: public static final String FORMAT_NAME = "geotools_coverage_1.0";
058:
059: /**
060: * The maximum number of dimension allowed for the image coordinate system. Images must be
061: * at least two-dimensional. Some plugins consider the set of bands as the third dimension
062: * (for example slices at different depths). An additional "1 pixel large" temporal dimension
063: * is sometime used for storing the image timestamp.
064: */
065: private static final int MAXIMUM_DIMENSIONS = 4;
066:
067: /**
068: * The maximum number of bands allowed. This is a somewhat arbitrary value since there is
069: * no reason (except memory or disk space constraints) to restrict the number of bands in
070: * the image stream. The number of bands actually read is usually much smaller.
071: */
072: private static final int MAXIMUM_BANDS = Short.MAX_VALUE;
073:
074: /**
075: * The geographic {@linkplain CoordinateReferenceSystem coordinate reference system} type.
076: * This is often used together with the {@linkplain #ELLIPSOIDAL ellipsoidal} coordinate
077: * system type.
078: *
079: * @see #setCoordinateReferenceSystem
080: */
081: public static final String GEOGRAPHIC = "geographic";
082:
083: /**
084: * The geographic {@linkplain CoordinateReferenceSystem coordinate reference system} type
085: * with a vertical axis. This is often used together with a three-dimensional {@linkplain
086: * #ELLIPSOIDAL ellipsoidal} coordinate system type.
087: * <p>
088: * If the coordinate reference system has no vertical axis, or has additional axis of
089: * other kind than vertical (for example only a temporal axis), then the type should be
090: * the plain {@value #GEOGRAPHIC}. This is because such CRS are usually constructed as
091: * {@linkplain org.opengis.referencing.crs.CompoundCRS compound CRS} rather than a CRS
092: * with a three-dimensional coordinate system.
093: * <p>
094: * To be strict, a 3D CRS should be allowed only if the vertical axis is of the kind
095: * "height above the ellipsoid" (as opposed to "height above the geoid" for example),
096: * otherwise we have a compound CRS. But many datafile don't make this distinction.
097: *
098: * @see #setCoordinateReferenceSystem
099: */
100: public static final String GEOGRAPHIC_3D = "geographic3D";
101:
102: /**
103: * The projected {@linkplain CoordinateReferenceSystem coordinate reference system} type.
104: * This is often used together with the {@linkplain #CARTESIAN cartesian} coordinate
105: * system type.
106: *
107: * @see #setCoordinateReferenceSystem
108: */
109: public static final String PROJECTED = "projected";
110:
111: /**
112: * The projected {@linkplain CoordinateReferenceSystem coordinate reference system} type
113: * with a vertical axis. This is often used together with a three-dimensional {@linkplain
114: * #CARTESIAN cartesian} coordinate system type.
115: * <p>
116: * If the coordinate reference system has no vertical axis, or has additional axis of
117: * other kind than vertical (for example only a temporal axis), then the type should be
118: * the plain {@value #PROJECTED}. This is because such CRS are usually constructed as
119: * {@linkplain org.opengis.referencing.crs.CompoundCRS compound CRS} rather than a CRS
120: * with a three-dimensional coordinate system.
121: * <p>
122: * To be strict, a 3D CRS should be allowed only if the vertical axis is of the kind
123: * "height above the ellipsoid" (as opposed to "height above the geoid" for example),
124: * otherwise we have a compound CRS. But many datafile don't make this distinction.
125: *
126: * @see #setCoordinateReferenceSystem
127: */
128: public static final String PROJECTED_3D = "projected3D";
129:
130: /**
131: * The ellipsoidal {@linkplain CoordinateSystem coordinate system} type.
132: *
133: * @see #setCoordinateSystem
134: */
135: public static final String ELLIPSOIDAL = "ellipsoidal";
136:
137: /**
138: * The cartesian {@linkplain CoordinateSystem coordinate system} type.
139: *
140: * @see #setCoordinateSystem
141: */
142: public static final String CARTESIAN = "cartesian";
143:
144: /**
145: * The geophysics {@linkplain SampleDimension sample dimension} type.
146: * Pixels in the {@linkplain java.awt.image.RenderedImage rendered image} produced by
147: * the image reader contain directly geophysics values like temperature or elevation.
148: * Sample type is typically {@code float} or {@code double} and missing value, if any,
149: * <strong>must</strong> be one of {@linkplain Float#isNaN NaN values}.
150: */
151: public static final String GEOPHYSICS = "geophysics";
152:
153: /**
154: * The packed {@linkplain SampleDimension sample dimension} type.
155: * Pixels in the {@linkplain java.awt.image.RenderedImage rendered image} produced by
156: * the image reader contain packed data, typically as {@code byte} or {@code short}
157: * integer type. Conversions to geophysics values are performed by the application of
158: * a scale and offset. Some special values are typically used for missing values.
159: */
160: public static final String PACKED = "packed";
161:
162: /**
163: * Enumeration of valid coordinate reference system types.
164: */
165: static final List/*<String>*/CRS_TYPES = new UnmodifiableArrayList(
166: new String[] { GEOGRAPHIC, PROJECTED });
167:
168: /**
169: * Enumeration of valid coordinate system types.
170: */
171: static final List/*<String>*/CS_TYPES = new UnmodifiableArrayList(
172: new String[] { ELLIPSOIDAL, CARTESIAN });
173:
174: /**
175: * Enumeration of valid axis directions. We do not declare {@link String} constants for them
176: * since they are already available as {@linkplain org.opengis.referencing.cs.AxisDirection
177: * axis direction} code list.
178: */
179: static final List/*<String>*/DIRECTIONS = new UnmodifiableArrayList(
180: new String[] { "north", "east", "south", "west", "up",
181: "down" });
182:
183: /**
184: * Enumeration of valid pixel orientation. We do not declare {@link String} constants for them
185: * since they are already available as {@linkplain org.opengis.metadata.spatial.PixelOrientation
186: * pixel orientation} code list.
187: */
188: static final List/*<String>*/PIXEL_ORIENTATIONS = new UnmodifiableArrayList(
189: new String[] { "center", "lower left", "lower right",
190: "upper right", "upper left" });
191:
192: /**
193: * Enumeration of valid sample dimention types.
194: */
195: static final List/*<String>*/SAMPLE_TYPES = new UnmodifiableArrayList(
196: new String[] { GEOPHYSICS, PACKED });
197:
198: /**
199: * The default instance. Will be created only when first needed.
200: *
201: * @see #getInstance
202: */
203: private static GeographicMetadataFormat DEFAULT;
204:
205: /**
206: * Creates a default metadata format.
207: */
208: private GeographicMetadataFormat() {
209: this (FORMAT_NAME, MAXIMUM_DIMENSIONS, MAXIMUM_BANDS);
210: }
211:
212: /**
213: * Creates a metadata format of the given name. Subclasses should invoke the various
214: * {@link #addElement(String,String,int) addElement} or {@link #addAttribute addAttribute}
215: * methods for adding new elements compared to the {@linkplain #getInstance default instance}.
216: *
217: * @param rootName the name of the root element.
218: * @param maximumDimensions The maximum number of dimensions allowed for coordinate systems.
219: * @param maximumBands The maximum number of sample dimensions allowed for images.
220: */
221: protected GeographicMetadataFormat(final String rootName,
222: final int maximumDimensions, final int maximumBands) {
223: super (rootName, CHILD_POLICY_SOME);
224: /*
225: * root
226: * +-- CoordinateReferenceSystem (name, type, WKT)
227: * | +-- Datum (name)
228: * | +-- CoordinateSystem (name, type)
229: * | +-- Axis[0] (name, direction, units, origin)
230: * | +-- Axis[1] (name, direction, units, origin)
231: * | +-- ...etc...
232: * +-- GridGeometry (pixelOrientation)
233: * +-- GridRange
234: * | +-- IndexRange[0] (minimum, maximum)
235: * | +-- IndexRange[1] (minimum, maximum)
236: * | +-- ...etc...
237: * +-- Envelope
238: * | +-- CoordinateValues[0] (minimum, maximum)
239: * | +-- CoordinateValues[1] (minimum, maximum)
240: * | +-- ...etc...
241: * +-- AffineTransform (elements[6..n])
242: */
243: addElement("CoordinateReferenceSystem", rootName,
244: CHILD_POLICY_SOME);
245: addAttribute("CoordinateReferenceSystem", "name",
246: DATATYPE_STRING);
247: addAttribute("CoordinateReferenceSystem", "type",
248: DATATYPE_STRING, false, null, CRS_TYPES);
249: addAttribute("CoordinateReferenceSystem", "WKT",
250: DATATYPE_STRING);
251: addElement("Datum", "CoordinateReferenceSystem",
252: CHILD_POLICY_EMPTY);
253: addAttribute("Datum", "name", DATATYPE_STRING, true, null);
254: addElement("CoordinateSystem", "CoordinateReferenceSystem", 2,
255: maximumDimensions);
256: addAttribute("CoordinateSystem", "name", DATATYPE_STRING);
257: addAttribute("CoordinateSystem", "type", DATATYPE_STRING,
258: false, null, CS_TYPES);
259: addElement("Axis", "CoordinateSystem", CHILD_POLICY_EMPTY);
260: addAttribute("Axis", "name", DATATYPE_STRING);
261: addAttribute("Axis", "direction", DATATYPE_STRING, false, null,
262: DIRECTIONS);
263: addAttribute("Axis", "units", DATATYPE_STRING);
264: addAttribute("Axis", "origin", DATATYPE_STRING);
265: addElement("GridGeometry", rootName, CHILD_POLICY_SOME);
266: addAttribute("GridGeometry", "pixelOrientation",
267: DATATYPE_STRING, false, "center", PIXEL_ORIENTATIONS);
268: addElement("GridRange", "GridGeometry", CHILD_POLICY_SEQUENCE);
269: addElement("IndexRange", "GridRange", CHILD_POLICY_EMPTY);
270: addAttribute("IndexRange", "minimum", DATATYPE_INTEGER, true,
271: "0");
272: addAttribute("IndexRange", "maximum", DATATYPE_INTEGER, true,
273: null); // inclusive
274: addElement("Envelope", "GridGeometry", CHILD_POLICY_SEQUENCE);
275: addElement("CoordinateValues", "Envelope", CHILD_POLICY_EMPTY);
276: addAttribute("CoordinateValues", "minimum", DATATYPE_DOUBLE,
277: true, null);
278: addAttribute("CoordinateValues", "maximum", DATATYPE_DOUBLE,
279: true, null); // inclusive
280: addElement("AffineTransform", "GridGeometry",
281: CHILD_POLICY_EMPTY);
282: addAttribute("AffineTransform", "elements", DATATYPE_DOUBLE,
283: true, 6, maximumDimensions * (maximumDimensions - 1));
284: /*
285: * root
286: * +-- SampleDimensions (type)
287: * +-- SampleDimension[0] (name, scale, offset, minValue, maxValue, fillValues)
288: * +-- SampleDimension[1] (name, scale, offset, minValue, maxValue, fillValues)
289: * +-- ...etc...
290: */
291: addElement("SampleDimensions", rootName, 1, maximumBands);
292: addAttribute("SampleDimensions", "type", DATATYPE_STRING,
293: false, null, SAMPLE_TYPES);
294: addElement("SampleDimension", "SampleDimensions",
295: CHILD_POLICY_SOME);
296: addAttribute("SampleDimension", "name", DATATYPE_STRING);
297: addAttribute("SampleDimension", "scale", DATATYPE_DOUBLE);
298: addAttribute("SampleDimension", "offset", DATATYPE_DOUBLE);
299: addAttribute("SampleDimension", "minValue", DATATYPE_DOUBLE);
300: addAttribute("SampleDimension", "maxValue", DATATYPE_DOUBLE);
301: addAttribute("SampleDimension", "fillValues", DATATYPE_DOUBLE,
302: false, 0, Short.MAX_VALUE);
303: /*
304: * Allow users to specify fully-constructed GeoAPI objects.
305: */
306: addObjectValue("CoordinateReferenceSystem",
307: CoordinateReferenceSystem.class);
308: addObjectValue("Datum", Datum.class);
309: addObjectValue("CoordinateSystem", CoordinateSystem.class);
310: addObjectValue("Axis", CoordinateSystemAxis.class);
311: addObjectValue("GridGeometry", GridGeometry.class);
312: addObjectValue("Envelope", Envelope.class);
313: addObjectValue("AffineTransform", MathTransform.class);
314: addObjectValue("SampleDimension", SampleDimension.class);
315: }
316:
317: /**
318: * Adds an optional attribute of the specified data type.
319: */
320: private void addAttribute(final String elementName,
321: final String attrName, final int dataType) {
322: addAttribute(elementName, attrName, dataType, false, null);
323: }
324:
325: /**
326: * Adds an optional object value of the specified class.
327: */
328: private void addObjectValue(final String elementName,
329: final Class/*<*>*/classType) {
330: addObjectValue(elementName, classType, false, null);
331: }
332:
333: /**
334: * Returns {@code true} if the element (and the subtree below it) is allowed to appear
335: * in a metadata document for an image of the given type. The default implementation
336: * always returns {@code true}.
337: */
338: public boolean canNodeAppear(final String elementName,
339: final ImageTypeSpecifier imageType) {
340: return true;
341: }
342:
343: /**
344: * Returns the default geographic metadata format instance.
345: */
346: public static synchronized GeographicMetadataFormat getInstance() {
347: if (DEFAULT == null) {
348: DEFAULT = new GeographicMetadataFormat();
349: }
350: return DEFAULT;
351: }
352: }
|