001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
005: * (C) 2002, 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: package org.geotools.coverage.grid;
018:
019: // J2SE dependencies and extensions
020: import java.awt.Color;
021: import java.awt.geom.Point2D;
022: import java.awt.geom.Rectangle2D;
023: import java.awt.image.BufferedImage;
024: import java.awt.image.DataBuffer;
025: import java.awt.image.RenderedImage;
026: import java.awt.image.WritableRaster;
027: import java.io.File;
028: import java.io.IOException;
029: import java.util.Random;
030: import javax.imageio.ImageIO;
031: import javax.units.SI;
032: import junit.framework.Assert;
033:
034: // OpenGIS dependencies
035: import org.opengis.referencing.crs.CoordinateReferenceSystem;
036:
037: // Geotools dependencies
038: import org.geotools.factory.Hints;
039: import org.geotools.referencing.crs.DefaultGeographicCRS;
040: import org.geotools.coverage.Category;
041: import org.geotools.coverage.FactoryFinder;
042: import org.geotools.coverage.GridSampleDimension;
043: import org.geotools.geometry.Envelope2D;
044: import org.geotools.geometry.GeneralEnvelope;
045: import org.geotools.test.TestData;
046: import org.geotools.util.NumberRange;
047:
048: // Sun implementation of JAI
049: import com.sun.media.jai.codecimpl.util.RasterFactory;
050:
051: /**
052: * A factory for sample {@link GridCoverage2D}, which may be used for tests
053: * in other modules. The two following methods are for this purpose:
054: *
055: * <ul>
056: * <li>{@link #getNumExamples}</li>
057: * <li>{@link #getExample}</li>
058: * </ul>
059: *
060: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/coverage/src/test/java/org/geotools/coverage/grid/GridCoverageExamples.java $
061: * @version $Id: GridCoverageExamples.java 25591 2007-05-20 10:43:59Z desruisseaux $
062: * @author Martin Desruisseaux
063: */
064: public final class GridCoverageExamples extends Assert {
065: /**
066: * Small value for comparaison of sample values. Since most grid coverage implementation in
067: * Geotools 2 store geophysics values as {@code float} numbers, this {@code EPS} value must
068: * be of the order of {@code float} relative precision, not {@code double}.
069: */
070: private static final double EPS = 1E-5;
071:
072: /**
073: * Random number generator for this test.
074: */
075: private static final Random random = new Random(684673898634768L);
076:
077: /**
078: * Do not allows instantiation of this class.
079: */
080: private GridCoverageExamples() {
081: }
082:
083: /**
084: * Returns a grid coverage filled with random values. The coordinate
085: * reference system default to {@link DefaultGeographicCRS#WGS84}.
086: *
087: * @return A random coverage.
088: */
089: public static GridCoverage2D getRandomCoverage() {
090: return getRandomCoverage(DefaultGeographicCRS.WGS84);
091: }
092:
093: /**
094: * Returns a grid coverage filled with random values.
095: *
096: * @param crs The coverage coordinate reference system.
097: * @return A random coverage.
098: */
099: public static GridCoverage2D getRandomCoverage(
100: final CoordinateReferenceSystem crs) {
101: /*
102: * Some constants used for the construction and tests of the grid coverage.
103: */
104: final double SCALE = 0.1; // Scale factor for pixel transcoding.
105: final double OFFSET = 5.0; // Offset factor for pixel transcoding.
106: final double PIXEL_SIZE = .25; // Pixel size (in degrees). Used in transformations.
107: final int BEGIN_VALID = 3; // The minimal valid index for quantative category.
108: /*
109: * Constructs the grid coverage. We will assume that the grid coverage use
110: * (longitude,latitude) coordinates, pixels of 0.25 degrees and a lower
111: * left corner at 10°W 30°N.
112: */
113: final GridCoverage2D coverage; // The final grid coverage.
114: final BufferedImage image; // The GridCoverage's data.
115: final WritableRaster raster; // The image's data as a raster.
116: final Rectangle2D bounds; // The GridCoverage's envelope.
117: final GridSampleDimension band; // The only image's band.
118: band = new GridSampleDimension("Temperature", new Category[] {
119: new Category("No data", null, 0),
120: new Category("Land", null, 1),
121: new Category("Cloud", null, 2),
122: new Category("Temperature", null, BEGIN_VALID, 256,
123: SCALE, OFFSET) }, SI.CELSIUS);
124: image = new BufferedImage(120, 80,
125: BufferedImage.TYPE_BYTE_INDEXED);
126: raster = image.getRaster();
127: for (int i = raster.getWidth(); --i >= 0;) {
128: for (int j = raster.getHeight(); --j >= 0;) {
129: raster.setSample(i, j, 0, random.nextInt(256));
130: }
131: }
132: bounds = new Rectangle2D.Double(-10, 30, PIXEL_SIZE
133: * image.getWidth(), PIXEL_SIZE * image.getHeight());
134: final GeneralEnvelope envelope = new GeneralEnvelope(crs);
135: envelope.setRange(0, bounds.getMinX(), bounds.getMaxX());
136: envelope.setRange(1, bounds.getMinY(), bounds.getMaxY());
137: for (int i = envelope.getDimension(); --i >= 2;) {
138: envelope.setRange(i, 10 * i, 10 * i + 5);
139: }
140: final Hints hints = new Hints(Hints.TILE_ENCODING, "raw");
141: final GridCoverageFactory factory = FactoryFinder
142: .getGridCoverageFactory(hints);
143: coverage = (GridCoverage2D) factory.create("Test", image,
144: envelope, new GridSampleDimension[] { band }, null,
145: null);
146: assertEquals("raw", coverage.tileEncoding);
147: /*
148: * Grid coverage construction finished. Now test it. Some tests will not be applicable
149: * if a subclass overridden the 'transform' method are returned a transformed coverage.
150: * We detect this case when 'coverage != original'.
151: */
152: assertSame(coverage.getRenderedImage(), coverage
153: .getRenderableImage(0, 1).createDefaultRendering());
154: assertSame(image.getTile(0, 0), coverage.getRenderedImage()
155: .getTile(0, 0));
156: /*
157: * Tests the creation of a "geophysics" view. This test make sure that the
158: * 'geophysics' method do not creates more grid coverage than needed.
159: */
160: GridCoverage2D geophysics = coverage.geophysics(true);
161: assertSame(coverage, coverage.geophysics(false));
162: assertSame(coverage, geophysics.geophysics(false));
163: assertSame(geophysics, geophysics.geophysics(true));
164: assertFalse(coverage.equals(geophysics));
165: assertFalse(coverage.getSampleDimension(0)
166: .getSampleToGeophysics().isIdentity());
167: assertTrue(geophysics.getSampleDimension(0)
168: .getSampleToGeophysics().isIdentity());
169: /*
170: * Compares data.
171: */
172: final int bandN = 0; // Band to test.
173: double[] bufferCov = null;
174: double[] bufferGeo = null;
175: final double left = bounds.getMinX() + (0.5 * PIXEL_SIZE); // Includes translation to center
176: final double upper = bounds.getMaxY() - (0.5 * PIXEL_SIZE); // Includes translation to center
177: final Point2D.Double point = new Point2D.Double(); // Will maps to pixel center.
178: for (int j = raster.getHeight(); --j >= 0;) {
179: for (int i = raster.getWidth(); --i >= 0;) {
180: point.x = left + PIXEL_SIZE * i;
181: point.y = upper - PIXEL_SIZE * j;
182: double r = raster.getSampleDouble(i, j, bandN);
183: bufferCov = coverage.evaluate(point, bufferCov);
184: bufferGeo = geophysics.evaluate(point, bufferGeo);
185: assertEquals(r, bufferCov[bandN], EPS);
186:
187: // Compares transcoded samples.
188: if (r < BEGIN_VALID) {
189: assertTrue(Double.isNaN(bufferGeo[bandN]));
190: } else {
191: assertEquals(OFFSET + SCALE * r, bufferGeo[bandN],
192: EPS);
193: }
194: }
195: }
196: return coverage;
197: }
198:
199: /**
200: * Returns the number of available image which may be used as example.
201: */
202: public static int getNumExamples() {
203: return 1; // TODO: set to '5' if we commit the 'CHL01195.png' image (160 ko).
204: }
205:
206: /**
207: * Returns a {@link GridCoverage} which may be used as a "real world" example.
208: *
209: * @param number The example number. Numbers are numeroted from
210: * 0 to {@link #getNumExamples()} exclusive.
211: * @return The "real world" grid coverage.
212: * @throws IOException if an I/O operation was needed and failed.
213: */
214: public static GridCoverage2D getExample(final int number)
215: throws IOException {
216: final GridCoverageFactory factory = FactoryFinder
217: .getGridCoverageFactory(null);
218: final String path;
219: final Category[] categories;
220: final CoordinateReferenceSystem crs;
221: final Rectangle2D bounds;
222: final GridSampleDimension[] bands;
223: switch (number) {
224: default: {
225: throw new IllegalArgumentException(String.valueOf(number));
226: }
227: case 0: {
228: //unit = "°C";
229: path = "QL95209.png";
230: crs = DefaultGeographicCRS.WGS84;
231: categories = new Category[] {
232: new Category("Coast line", Color.decode("#000000"),
233: new NumberRange(0, 0)),
234: new Category("Cloud", Color.decode("#C3C3C3"),
235: new NumberRange(1, 9)),
236: new Category("Unused", Color.decode("#822382"),
237: new NumberRange(10, 29)),
238: new Category("Sea Surface Temperature", null,
239: new NumberRange(30, 219), 0.1, 10.0),
240: new Category("Unused", Color.decode("#A0505C"),
241: new NumberRange(220, 239)),
242: new Category("Land", Color.decode("#D2C8A0"),
243: new NumberRange(240, 254)),
244: new Category("No data", Color.decode("#FFFFFF"),
245: new NumberRange(255, 255)), };
246: // 41°S - 5°N ; 35°E - 80°E (450 x 460 pixels)
247: bounds = new Rectangle2D.Double(35, -41, 45, 46);
248: bands = new GridSampleDimension[] { new GridSampleDimension(
249: "Measure", categories, null) };
250: break;
251: }
252: case 1: {
253: //unit = "mg/m³";
254: path = "CHL01195.png";
255: crs = DefaultGeographicCRS.WGS84;
256: categories = new Category[] {
257: new Category("Land", Color.decode("#000000"),
258: new NumberRange(255, 255)),
259: new Category("No data", Color.decode("#FFFFFF"),
260: new NumberRange(0, 0)),
261: new Category("Log chl-a", null, new NumberRange(1,
262: 254), 0.015, -1.985) };
263: // 34°N - 45°N ; 07°W - 12°E (1200 x 700 pixels)
264: bounds = new Rectangle2D.Double(-7, 34, 19, 11);
265: bands = new GridSampleDimension[] { new GridSampleDimension(
266: "Measure", categories, null).geophysics(false) };
267:
268: break;
269: }
270: case 2: {
271: ////
272: //
273: // WORLD DEM
274: //
275: ////
276: path = "world_dem.gif";
277: bounds = new Rectangle2D.Double(-180, -90, 360, 180);
278: crs = DefaultGeographicCRS.WGS84;
279: bands = null;
280: break;
281: }
282: case 3: {
283: ////
284: //
285: // WORLD BATHY
286: //
287: ////
288: path = "BATHY.gif";
289: bounds = new Rectangle2D.Double(-180, -90, 360, 180);
290: crs = DefaultGeographicCRS.WGS84;
291: bands = null;
292: break;
293: }
294: case 4: {
295: ////
296: //
297: // A float coverage
298: //
299: ////
300: /*
301: * Set the pixel values. Because we use only one tile with one band, the code below
302: * is pretty similar to the code we would have if we were just setting the values in
303: * a matrix.
304: */
305: final int width = 500;
306: final int height = 500;
307: WritableRaster raster = RasterFactory.createBandedRaster(
308: DataBuffer.TYPE_FLOAT, width, height, 1, null);
309: for (int y = 0; y < height; y++) {
310: for (int x = 0; x < width; x++) {
311: raster.setSample(x, y, 0, x + y);
312: }
313: }
314: /*
315: * Set some metadata (the CRS, the geographic envelope, etc.) and display the image.
316: * The display may be slow, since the translation from floating-point values to some
317: * color (or grayscale) is performed on the fly everytime the image is rendered.
318: */
319: Color[] colors = new Color[] { Color.BLUE, Color.CYAN,
320: Color.WHITE, Color.YELLOW, Color.RED };
321: return factory.create("My colored coverage", raster,
322: new Envelope2D(DefaultGeographicCRS.WGS84, 35, -41,
323: 35 + 45, -41 + 46), null, null, null,
324: new Color[][] { colors }, null);
325:
326: }
327: }
328: final GeneralEnvelope envelope = new GeneralEnvelope(bounds);
329: final RenderedImage image = ImageIO.read(TestData.getResource(
330: GridCoverageExamples.class, path));
331: final String filename = new File(path).getName();
332: envelope.setCoordinateReferenceSystem(crs);
333: return (GridCoverage2D) factory.create(filename, image,
334: envelope, bands, null, null);
335: }
336: }
|