001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2007, Geotools Project Managment Committee (PMC)
005: * (C) 2007, GeoSolutions S.A.S.
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: */
018: package org.geotools.gce.mrsid;
019:
020: import it.geosolutions.imageio.gdalframework.GDALCommonIIOImageMetadata;
021: import it.geosolutions.imageio.plugins.mrsid.MrSIDIIOImageMetadata;
022: import it.geosolutions.imageio.plugins.mrsid.MrSIDImageReaderSpi;
023: import it.geosolutions.imageio.stream.input.FileImageInputStreamExtImpl;
024:
025: import java.awt.Rectangle;
026: import java.awt.geom.AffineTransform;
027: import java.awt.image.renderable.ParameterBlock;
028: import java.io.File;
029: import java.io.FileInputStream;
030: import java.io.FileNotFoundException;
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.UnsupportedEncodingException;
034: import java.net.URL;
035: import java.net.URLDecoder;
036: import java.util.NoSuchElementException;
037: import java.util.logging.Level;
038: import java.util.logging.Logger;
039:
040: import javax.imageio.ImageIO;
041: import javax.imageio.ImageReadParam;
042: import javax.imageio.ImageReader;
043: import javax.imageio.metadata.IIOMetadata;
044: import javax.imageio.spi.ImageReaderSpi;
045: import javax.imageio.stream.FileCacheImageInputStream;
046: import javax.imageio.stream.ImageInputStream;
047: import javax.imageio.stream.MemoryCacheImageInputStream;
048: import javax.media.jai.JAI;
049: import javax.media.jai.PlanarImage;
050:
051: import org.geotools.coverage.grid.GeneralGridRange;
052: import org.geotools.coverage.grid.GridCoverage2D;
053: import org.geotools.coverage.grid.GridGeometry2D;
054: import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
055: import org.geotools.coverage.grid.io.AbstractGridFormat;
056: import org.geotools.data.DataSourceException;
057: import org.geotools.data.PrjFileReader;
058: import org.geotools.data.WorldFileReader;
059: import org.geotools.factory.Hints;
060: import org.geotools.geometry.GeneralEnvelope;
061: import org.geotools.parameter.Parameter;
062: import org.geotools.referencing.CRS;
063: import org.geotools.referencing.operation.builder.GridToEnvelopeMapper;
064: import org.geotools.referencing.operation.transform.ProjectiveTransform;
065: import org.opengis.coverage.grid.Format;
066: import org.opengis.coverage.grid.GridCoverage;
067: import org.opengis.coverage.grid.GridCoverageReader;
068: import org.opengis.geometry.Envelope;
069: import org.opengis.geometry.MismatchedDimensionException;
070: import org.opengis.parameter.GeneralParameterValue;
071: import org.opengis.referencing.FactoryException;
072: import org.opengis.referencing.datum.PixelInCell;
073: import org.opengis.referencing.operation.MathTransform;
074: import org.opengis.referencing.operation.NoninvertibleTransformException;
075: import org.opengis.referencing.operation.TransformException;
076: import org.w3c.dom.NamedNodeMap;
077: import org.w3c.dom.Node;
078:
079: /**
080: * This class can read a MrSID data source and create a {@link GridCoverage2D}
081: * from the data.
082: *
083: * @author Daniele Romagnoli, GeoSolutions
084: * @author Simone Giannecchini, GeoSolutions
085: */
086: public final class MrSIDReader extends AbstractGridCoverage2DReader
087: implements GridCoverageReader {
088: /** Logger. */
089: private final static Logger LOGGER = org.geotools.util.logging.Logging
090: .getLogger("org.geotools.gce.mrsid");
091:
092: /**
093: * Caches an <code>ImageReaderSpi</code> for a
094: * <code>MrSIDImageReader</code>.
095: */
096: private final static ImageReaderSpi readerSPI = new MrSIDImageReaderSpi();
097:
098: /** Absolute path to the parent dir for this coverage. */
099: private String parentPath;
100:
101: /**
102: * Creates a new instance of a {@link MrSIDReader}. I assume nothing about
103: * file extension.
104: *
105: * @param input
106: * Source object for which we want to build a {@link MrSIDReader}.
107: * @throws DataSourceException
108: * @throws FactoryException
109: * @throws MismatchedDimensionException
110: */
111: public MrSIDReader(Object input) throws DataSourceException,
112: MismatchedDimensionException {
113: this (input, null);
114: }
115:
116: /**
117: * Creates a new instance of a {@link MrSIDReader}. I assume nothing about
118: * file extension.
119: *
120: * @param input
121: * Source object for which we want to build a {@link MrSIDReader}.
122: * @param hints
123: * Hints to be used by this reader throughout his life.
124: * @throws DataSourceException
125: * @throws FactoryException
126: * @throws MismatchedDimensionException
127: */
128: public MrSIDReader(Object input, final Hints hints)
129: throws DataSourceException, MismatchedDimensionException {
130:
131: // /////////////////////////////////////////////////////////////////////
132: //
133: // Checking input
134: //
135: // /////////////////////////////////////////////////////////////////////
136: try {
137: // //
138: //
139: // Hints
140: //
141: // //
142: if (hints != null)
143: this .hints.add(hints);
144:
145: // //
146: //
147: // Source management
148: //
149: // //
150: checkSource(input);
151:
152: // Getting a reader for this format
153: final ImageReader reader = readerSPI.createReaderInstance();
154: reader.setInput(inStream);
155:
156: // //
157: //
158: // Setting Envelope, GridRange and CRS
159: //
160: // //
161: setOriginalProperties(reader);
162:
163: // //
164: //
165: // Information about multiple levels and such
166: //
167: // //
168: getResolutionInfo(reader);
169: reader.reset();
170: coverageName = (source instanceof File) ? ((File) source)
171: .getName() : "MrSID_coverage";
172:
173: // release the stream if we can.
174: finalStreamPreparation();
175: } catch (IOException e) {
176: if (LOGGER.isLoggable(Level.SEVERE))
177: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
178: throw new DataSourceException(e);
179: } catch (TransformException e) {
180: if (LOGGER.isLoggable(Level.SEVERE))
181: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
182: throw new DataSourceException(e);
183: }
184:
185: }
186:
187: /**
188: * Setting Envelope, GridRange and CRS from the given
189: * <code>ImageReader</code>
190: *
191: * @param reader
192: * the <code>ImageReader</code> from which to retrieve metadata
193: * (if available) for setting properties
194: * @throws IOException
195: * @throws IllegalStateException
196: * @throws TransformException
197: * @throws MismatchedDimensionException
198: */
199: private void setOriginalProperties(ImageReader reader)
200: throws IOException, IllegalStateException,
201: TransformException, MismatchedDimensionException {
202:
203: // //
204: //
205: // Getting Common metadata from GDAL
206: //
207: // //
208: IIOMetadata metadata = reader.getImageMetadata(0);
209: if (!(metadata instanceof GDALCommonIIOImageMetadata))
210: throw new DataSourceException(
211: "Unexpected error! Metadata are not of the expected class.");
212: getPropertiesFromCommonMetadata(metadata);
213:
214: // //
215: //
216: // If common metadata don't have sufficient information to set CRS
217: // envelope, try other ways, such as looking for a PRJ
218: //
219: // //
220: if (crs == null)
221: getCoordinateReferenceSystemFromPrj();
222:
223: if (originalEnvelope == null || crs == null)
224: getOriginalEnvelopeFromMrSIDMetadata(metadata);
225:
226: if (crs == null) {
227: LOGGER.info("crs not found proceeding with EPSG:4326");
228: crs = MrSIDFormat.getDefaultCRS();
229: }
230:
231: // //
232: //
233: // If common metadata doesn't have sufficient information to set the
234: // envelope, try other ways, such as looking for a WorldFile
235: //
236: // //
237: if (originalEnvelope == null)
238: checkForWorldFile();
239:
240: // setting the coordinate reference system for the envelope
241: originalEnvelope.setCoordinateReferenceSystem(crs);
242:
243: }
244:
245: /**
246: * Given a <code>IIOMetadata</code> metadata object, retrieves several
247: * properties to properly set envelope, gridrange and crs.
248: *
249: * @param metadata
250: */
251: private void getPropertiesFromCommonMetadata(IIOMetadata metadata) {
252: // casting metadata
253: final GDALCommonIIOImageMetadata commonMetadata = (GDALCommonIIOImageMetadata) metadata;
254:
255: // setting CRS and Envelope directly from GDAL, if available
256: final String wkt = commonMetadata.getProjection();
257: if (wkt != null && !(wkt.equalsIgnoreCase("")))
258: try {
259: crs = CRS.parseWKT(wkt);
260:
261: } catch (FactoryException fe) {
262: // unable to get CRS from WKT
263: if (LOGGER.isLoggable(Level.WARNING))
264: LOGGER.log(Level.WARNING, fe.getLocalizedMessage(),
265: fe);
266: crs = null;
267: }
268:
269: final int hrWidth = commonMetadata.getWidth();
270: final int hrHeight = commonMetadata.getHeight();
271: originalGridRange = new GeneralGridRange(new Rectangle(0, 0,
272: hrWidth, hrHeight));
273:
274: // getting Grid Properties
275: final double geoTransform[] = commonMetadata
276: .getGeoTransformation();
277: if (geoTransform != null && geoTransform.length == 6) {
278: final AffineTransform tempTransform = new AffineTransform(
279: geoTransform[1], geoTransform[4], geoTransform[2],
280: geoTransform[5], geoTransform[0], geoTransform[3]);
281: // attention gdal geotransform does not uses the pixel is centre
282: // convention like world files.
283: // tempTransform.translate(-0.5, -0.5);
284: this .raster2Model = ProjectiveTransform
285: .create(tempTransform);
286: try {
287:
288: // Setting Envelope
289: originalEnvelope = CRS.transform(raster2Model,
290: new GeneralEnvelope(originalGridRange
291: .toRectangle()));
292: } catch (IllegalStateException e) {
293: if (LOGGER.isLoggable(Level.WARNING))
294: LOGGER.log(Level.WARNING, e.getLocalizedMessage(),
295: e);
296: } catch (TransformException e) {
297: if (LOGGER.isLoggable(Level.WARNING))
298: LOGGER.log(Level.WARNING, e.getLocalizedMessage(),
299: e);
300: }
301: }
302: }
303:
304: /**
305: * Checks whether a world file is associated with the data source. If found,
306: * set the envelope.
307: *
308: * @throws IllegalStateException
309: * @throws TransformException
310: * @throws IOException
311: */
312: private void checkForWorldFile() throws IllegalStateException,
313: TransformException, IOException {
314:
315: final String worldFilePath = new StringBuffer(this .parentPath)
316: .append(File.separatorChar).append(this .coverageName)
317: .toString();
318:
319: File file2Parse = new File(worldFilePath + ".sdw");
320: boolean worldFileExists = file2Parse.exists();
321: if (!worldFileExists) {
322: file2Parse = new File(worldFilePath + ".wld");
323: worldFileExists = file2Parse.exists();
324: }
325: if (worldFileExists) {
326: final WorldFileReader reader = new WorldFileReader(
327: file2Parse);
328: raster2Model = reader.getTransform();
329:
330: // //
331: //
332: // In case we read from a real world file we have together the
333: // envelope
334: //
335: // //
336:
337: final AffineTransform tempTransform = new AffineTransform(
338: (AffineTransform) raster2Model);
339: tempTransform.translate(-0.5, -0.5);
340:
341: originalEnvelope = CRS.transform(ProjectiveTransform
342: .create(tempTransform), new GeneralEnvelope(
343: originalGridRange.toRectangle()));
344: }
345: }
346:
347: /**
348: * Close the <code>InStream</code> <code>ImageInputStream</code> if we
349: * open it up on purpose to read header info for this
350: * {@link AbstractGridCoverage2DReader}. If the stream cannot be closed, we
351: * just reset and mark it.
352: *
353: * @throws IOException
354: */
355: private void finalStreamPreparation() throws IOException {
356: if (closeMe)
357: inStream.close();
358: else {
359: inStream.reset();
360: inStream.mark();
361: }
362: }
363:
364: /**
365: * Checks the input provided to this {@link MrSIDReader} and sets all the
366: * other objects and flags accordingly.
367: *
368: * @param input
369: * provided to this {@link MrSIDReader}.
370: * @throws UnsupportedEncodingException
371: * @throws DataSourceException
372: * @throws IOException
373: * @throws FileNotFoundException
374: */
375: private void checkSource(Object input)
376: throws UnsupportedEncodingException, DataSourceException,
377: IOException, FileNotFoundException {
378: if (input == null) {
379: final DataSourceException ex = new DataSourceException(
380: "No source set to read this coverage.");
381: if (LOGGER.isLoggable(Level.SEVERE))
382: LOGGER.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
383: throw ex;
384: }
385: this .source = input;
386: closeMe = true;
387:
388: // //
389: // URL to FIle
390: // //
391: // if it is a URL ponting to a File I convert it to a file,
392: // otherwis, later on, I will try to get an inputstream out of it.
393: if (input instanceof URL) {
394: // URL that point to a file
395: final URL sourceURL = ((URL) input);
396: if (sourceURL.getProtocol().compareToIgnoreCase("file") == 0) {
397: this .source = input = new File(URLDecoder.decode(
398: sourceURL.getFile(), "UTF-8"));
399: }
400: }
401:
402: // //
403: // File
404: // //
405: if (input instanceof File) {
406: final File sourceFile = (File) input;
407: if (!sourceFile.exists() || sourceFile.isDirectory()
408: || !sourceFile.canRead())
409: throw new DataSourceException(
410: "Provided file does not exist or is a directory or is not readable!");
411: this .parentPath = sourceFile.getParent();
412: this .coverageName = sourceFile.getName();
413: final int dotIndex = coverageName.indexOf(".");
414: coverageName = (dotIndex == -1) ? coverageName
415: : coverageName.substring(0, dotIndex);
416: inStream = new FileImageInputStreamExtImpl(sourceFile);
417: // inStream = ImageIO.createImageInputStream(sourceFile);
418: } else
419:
420: // //
421: // URL
422: // //
423: if (input instanceof URL) {
424: final URL tempURL = ((URL) input);
425: inStream = ImageIO.createImageInputStream(tempURL
426: .openConnection().getInputStream());
427: } else
428:
429: // //
430: // InputStream
431: // //
432: if (input instanceof InputStream) {
433: closeMe = false;
434: if (ImageIO.getUseCache())
435: inStream = new FileCacheImageInputStream(
436: (InputStream) input, null);
437: else
438: inStream = new MemoryCacheImageInputStream(
439: (InputStream) input);
440: // let's mark it
441: inStream.mark();
442: } else
443:
444: // //
445: // ImageInputStream
446: // //
447: if (input instanceof ImageInputStream) {
448: closeMe = false;
449: inStream = (ImageInputStream) input;
450: inStream.mark();
451: } else
452: throw new IllegalArgumentException("Unsupported input type");
453:
454: if (inStream == null)
455: throw new DataSourceException(
456: "No input stream for the provided source");
457: }
458:
459: /**
460: * Gets resolution information about the coverage itself.
461: *
462: * @param reader
463: * an {@link ImageReader} to use for getting the resolution
464: * information.
465: * @throws IOException
466: * @throws TransformException
467: */
468: private void getResolutionInfo(ImageReader reader)
469: throws IOException, TransformException {
470:
471: // //
472: //
473: // get the dimension of the hr image and build the model as well as
474: // computing the resolution
475: //
476: // //
477: final Rectangle actualDim = new Rectangle(0, 0, reader
478: .getWidth(0), reader.getHeight(0));
479: originalGridRange = new GeneralGridRange(actualDim);
480:
481: // ///
482: //
483: // setting the higher resolution avalaible for this coverage
484: //
485: // ///
486: highestRes = getResolution(originalEnvelope, actualDim, crs);
487:
488: // ///
489: //
490: // Setting raster to model transformation
491: //
492: // ///
493: final GridToEnvelopeMapper geMapper = new GridToEnvelopeMapper();
494: geMapper.setEnvelope(originalEnvelope);
495: geMapper.setGridRange(originalGridRange);
496: geMapper.setGridType(PixelInCell.CELL_CENTER);
497: this .raster2Model = geMapper.createTransform();
498:
499: }
500:
501: /**
502: * @see org.opengis.coverage.grid.GridCoverageReader#getFormat()
503: */
504: public Format getFormat() {
505: return new MrSIDFormat();
506: }
507:
508: /**
509: * Reads a {@link GridCoverage2D} possibly matching as close as possible the
510: * resolution computed by using the input params provided by using the
511: * parameters for this {@link #read(GeneralParameterValue[])}.
512: *
513: * <p>
514: * To have an idea about the possible read parameters take a look at
515: * {@link AbstractGridFormat} class and {@link MrSIDFormat} class.
516: *
517: * @param params
518: * an array of {@link GeneralParameterValue} containing the
519: * parameters to control this read process.
520: *
521: * @return a {@link GridCoverage2D}.
522: *
523: * @see AbstractGridFormat
524: * @see MrSIDFormat
525: * @see org.opengis.coverage.grid.GridCoverageReader#read(org.opengis.parameter.GeneralParameterValue[])
526: */
527: public GridCoverage read(GeneralParameterValue[] params)
528: throws IllegalArgumentException, IOException {
529:
530: GeneralEnvelope readEnvelope = null;
531: Rectangle requestedDim = null;
532: if (params != null) {
533:
534: final int length = params.length;
535: for (int i = 0; i < length; i++) {
536: Parameter param = (Parameter) params[i];
537: String name = param.getDescriptor().getName().getCode();
538: if (name.equals(AbstractGridFormat.READ_GRIDGEOMETRY2D
539: .getName().toString())) {
540: final GridGeometry2D gg = (GridGeometry2D) param
541: .getValue();
542: if (gg == null)
543: continue;
544: readEnvelope = new GeneralEnvelope((Envelope) gg
545: .getEnvelope2D());
546: requestedDim = gg.getGridRange2D().getBounds();
547: }
548: }
549: }
550: return createCoverage(readEnvelope, requestedDim);
551: }
552:
553: /**
554: * This method creates the GridCoverage2D from the underlying file.
555: *
556: * @param requestedDim
557: * @param useJAIImageRead
558: * @param readEnvelope
559: *
560: *
561: * @return a GridCoverage
562: *
563: * @throws java.io.IOException
564: */
565: private GridCoverage createCoverage(
566: GeneralEnvelope requestedEnvelope, Rectangle requestedDim)
567: throws IOException {
568:
569: if (!closeMe) {
570: inStream.reset();
571: inStream.mark();
572: }
573: // /////////////////////////////////////////////////////////////////////
574: //
575: // Doing an image read for reading the coverage.
576: //
577: // /////////////////////////////////////////////////////////////////////
578:
579: // //
580: //
581: // Setting subsampling factors with some checkings
582: // 1) the subsampling factors cannot be zero
583: // 2) the subsampling factors cannot be such that the w 7or h are zero
584: //
585: // //
586: final ImageReadParam readP = new ImageReadParam();
587: final Integer imageChoice;
588: try {
589: imageChoice = setReadParams(readP, requestedEnvelope,
590: requestedDim);
591: } catch (IOException e) {
592: if (LOGGER.isLoggable(Level.SEVERE))
593: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
594: return null;
595: } catch (TransformException e) {
596: if (LOGGER.isLoggable(Level.SEVERE))
597: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
598: return null;
599: }
600:
601: // /////////////////////////////////////////////////////////////////////
602: //
603: // Check if we have something to load by intersecting the requested
604: // envelope with the bounds of the data set.
605: //
606: // If the requested envelope is not in the same crs of the data set crs
607: // we have to perform a conversion towards the latter crs before
608: // intersecting anything.
609: //
610: // /////////////////////////////////////////////////////////////////////
611: GeneralEnvelope intersectionEnvelope = null;
612: Rectangle sourceRegion = null;
613: if (requestedEnvelope != null) {
614: if (!CRS.equalsIgnoreMetadata(requestedEnvelope
615: .getCoordinateReferenceSystem(), this .crs)) {
616: try {
617: // //
618: //
619: // transforming the envelope back to the dataset crs in
620: // order to interact with the original envelope for this
621: // coverage.
622: //
623: // //
624: final MathTransform transform = operationFactory
625: .createOperation(
626: requestedEnvelope
627: .getCoordinateReferenceSystem(),
628: crs).getMathTransform();
629: if (!transform.isIdentity()) {
630: requestedEnvelope = CRS.transform(transform,
631: requestedEnvelope);
632: requestedEnvelope
633: .setCoordinateReferenceSystem(this .crs);
634:
635: if (LOGGER.isLoggable(Level.FINE))
636: LOGGER.fine(new StringBuffer(
637: "Reprojected envelope ").append(
638: requestedEnvelope.toString())
639: .append(" crs ")
640: .append(crs.toWKT()).toString());
641: }
642: } catch (TransformException e) {
643: throw new DataSourceException(
644: "Unable to create a coverage for this source",
645: e);
646: } catch (FactoryException e) {
647: throw new DataSourceException(
648: "Unable to create a coverage for this source",
649: e);
650: }
651: }
652: if (!requestedEnvelope.intersects(this .originalEnvelope,
653: true)) {
654: if (LOGGER.isLoggable(Level.FINE))
655: LOGGER
656: .warning("The requested envelope does not intersect the envelope of this coverage, we will return a null coverage.");
657: return null;
658: }
659: intersectionEnvelope = new GeneralEnvelope(
660: requestedEnvelope);
661: // intersect the requested area with the bounds of this layer
662: intersectionEnvelope.intersect(originalEnvelope);
663: intersectionEnvelope.setCoordinateReferenceSystem(this .crs);
664:
665: // ///
666: //
667: // Crop the sourced region
668: //
669: // ///
670: try {
671: final GeneralGridRange finalRange = new GeneralGridRange(
672: CRS.transform(this .raster2Model.inverse(),
673: intersectionEnvelope));
674: // CROP
675: sourceRegion = finalRange.toRectangle();
676: if (!sourceRegion.intersects(this .originalGridRange
677: .toRectangle()))
678: sourceRegion = null;
679: else
680: sourceRegion = sourceRegion
681: .intersection(this .originalGridRange
682: .toRectangle());
683: } catch (NoninvertibleTransformException e) {
684: if (LOGGER.isLoggable(Level.WARNING))
685: LOGGER.log(Level.WARNING, e.getLocalizedMessage(),
686: e);
687: sourceRegion = null;
688: } catch (TransformException e) {
689: if (LOGGER.isLoggable(Level.WARNING))
690: LOGGER.log(Level.WARNING, e.getLocalizedMessage(),
691: e);
692: sourceRegion = null;
693: }
694: }
695:
696: Object input;
697: if (source instanceof File)
698: input = new FileImageInputStreamExtImpl((File) source);
699: else if (source instanceof ImageInputStream
700: || source instanceof InputStream)
701: input = inStream;
702: else if (source instanceof URL) {
703: input = ImageIO.createImageInputStream(((URL) source)
704: .openConnection().getInputStream());
705: } else
706: throw new IllegalArgumentException();
707:
708: if (sourceRegion != null)
709: readP.setSourceRegion(sourceRegion);
710:
711: final PlanarImage mrsidCoverage;
712: // //
713: //
714: // image and metadata
715: //
716: // //
717: final ParameterBlock pbjImageRead = new ParameterBlock();
718: pbjImageRead.add(input);
719: pbjImageRead.add(imageChoice);
720: pbjImageRead.add(Boolean.FALSE);
721: pbjImageRead.add(Boolean.FALSE);
722: pbjImageRead.add(Boolean.FALSE);
723: pbjImageRead.add(null);
724: pbjImageRead.add(null);
725: pbjImageRead.add(readP);
726: pbjImageRead.add(readerSPI.createReaderInstance());
727: mrsidCoverage = JAI.create("ImageRead", pbjImageRead, hints);
728: // /////////////////////////////////////////////////////////////////////
729: //
730: // Creating the coverage
731: //
732: // /////////////////////////////////////////////////////////////////////
733: try {
734:
735: if (intersectionEnvelope != null) {
736: // I need to calculate a new transformation (raster2Model)
737: // between the cropped image and the required
738: // intersectionEnvelope
739: final GridToEnvelopeMapper gem = new GridToEnvelopeMapper();
740: gem.setEnvelope(intersectionEnvelope);
741: final int ssWidth = mrsidCoverage.getWidth();
742: final int ssHeight = mrsidCoverage.getHeight();
743: gem.setGridRange(new GeneralGridRange(new Rectangle(0,
744: 0, ssWidth, ssHeight)));
745: gem.setGridType(PixelInCell.CELL_CENTER);
746: return super .createImageCoverage(mrsidCoverage, gem
747: .createTransform());
748: } else {
749: // In case of not intersectionEnvelope (As an instance, when
750: // reading the whole image), I can use the originalEnvelope. So,
751: // no need to specify a raster2model parameter
752: return super .createImageCoverage(mrsidCoverage);
753: }
754: } catch (NoSuchElementException e) {
755: if (LOGGER.isLoggable(Level.SEVERE))
756: LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
757: throw new DataSourceException(e);
758: }
759: }
760:
761: /**
762: * This method is responsible for building up an envelope according to the
763: * definition of the crs. It assumes that X coordinate on the grid itself
764: * maps to longitude and y coordinate maps to latitude.
765: *
766: * @param gridMetadata
767: * The {@link MrSIDIIOImageMetadata} to parse.
768: *
769: * @throws MismatchedDimensionException
770: * @throws FactoryException
771: */
772: private void getOriginalEnvelopeFromMrSIDMetadata(
773: IIOMetadata metadata) throws MismatchedDimensionException {
774:
775: final GDALCommonIIOImageMetadata gridMetadata = (GDALCommonIIOImageMetadata) metadata;
776:
777: // getting metadata
778: final Node root = gridMetadata
779: .getAsTree(MrSIDIIOImageMetadata.mrsidImageMetadataName);
780:
781: Node child = root.getFirstChild();
782:
783: // getting GeoReferencing Properties
784: child = child.getNextSibling();
785: final NamedNodeMap attributes = child.getAttributes();
786:
787: final String xResolution = attributes.getNamedItem(
788: "IMAGE__X_RESOLUTION").getNodeValue();
789: final String yResolution = attributes.getNamedItem(
790: "IMAGE__Y_RESOLUTION").getNodeValue();
791: final String xyOrigin = attributes.getNamedItem(
792: "IMAGE__XY_ORIGIN").getNodeValue();
793: if (originalEnvelope == null) {
794: if (xResolution != null && yResolution != null
795: && xyOrigin != null
796: && !(xResolution.trim().equalsIgnoreCase(""))
797: && !(yResolution.trim().equalsIgnoreCase(""))
798: && !(xyOrigin.trim().equalsIgnoreCase(""))) {
799: double cellsizeX = Double.parseDouble(xResolution);
800: double cellsizeY = Double.parseDouble(yResolution);
801: final String[] origins = xyOrigin.split(",");
802: double xul = Double.parseDouble(origins[0]);
803: double yul = Double.parseDouble(origins[1]);
804:
805: xul -= (cellsizeX / 2d);
806: yul -= (cellsizeY / 2d);
807: final double xll = xul;
808: final double yur = yul;
809: final int width = originalGridRange.getLength(0);
810: final int height = originalGridRange.getLength(1);
811: final double xur = xul + (cellsizeX * width);
812: final double yll = yul - (cellsizeY * height);
813: originalEnvelope = new GeneralEnvelope(new double[] {
814: xll, yll }, new double[] { xur, yur });
815: }
816: }
817: // Retrieving projection Information
818: if (crs == null) {
819: Node attribute = attributes.getNamedItem("IMG__WKT");
820: if (attribute != null) {
821: String wkt = attribute.getNodeValue();
822: if (wkt != null && (wkt.trim().length() > 0))
823: try {
824: crs = CRS.parseWKT(wkt);
825: } catch (FactoryException fe) {
826: // unable to get CRS from WKT
827: crs = null;
828: }
829: }
830: }
831: }
832:
833: /**
834: * Gets the coordinate system that will be associated to the
835: * {@link GridCoverage}. The WGS84 coordinate system is used by default. It
836: * is worth to point out that when reading from a stream which is not
837: * connected to a file, like from an http connection (e.g. from a WCS) we
838: * cannot rely on receiving a prj file too. In this case the exchange of
839: * information about referencing should proceed the exchange of data thus I
840: * rely on this and I ask the user who's invoking the read operation to
841: * provide me a valid crs and envelope through read parameters.
842: *
843: * @throws FactoryException
844: * @throws IOException
845: * @throws FileNotFoundException
846: */
847: private void getCoordinateReferenceSystemFromPrj()
848: throws FileNotFoundException, IOException {
849:
850: String prjPath = null;
851: if (source instanceof File) {
852: crs = null;
853: prjPath = new StringBuffer(this .parentPath).append(
854: File.separatorChar).append(this .coverageName)
855: .append(".prj").toString();
856: // read the prj info from the file
857: PrjFileReader projReader = null;
858: try {
859: final File prj = new File(prjPath);
860: if (prj.exists()) {
861: projReader = new PrjFileReader(new FileInputStream(
862: prj).getChannel());
863: crs = projReader.getCoordinateReferenceSystem();
864: }
865: } catch (FileNotFoundException e) {
866: // warn about the error but proceed, it is not fatal
867: // we have at least the default crs to use
868: LOGGER.log(Level.INFO, e.getLocalizedMessage(), e);
869: } catch (IOException e) {
870: // warn about the error but proceed, it is not fatal
871: // we have at least the default crs to use
872: LOGGER.log(Level.INFO, e.getLocalizedMessage(), e);
873: } catch (FactoryException e) {
874: // warn about the error but proceed, it is not fatal
875: // we have at least the default crs to use
876: LOGGER.log(Level.INFO, e.getLocalizedMessage(), e);
877: } finally {
878: if (projReader != null)
879: try {
880: projReader.close();
881: } catch (IOException e) {
882: // warn about the error but proceed, it is not fatal
883: // we have at least the default crs to use
884: LOGGER.log(Level.INFO, e.getLocalizedMessage(),
885: e);
886: }
887: }
888: }
889: }
890: }
|