Source Code Cross Referenced for GeoTiffMetadata2CRSAdapter.java in  » GIS » GeoTools-2.4.1 » org » geotools » gce » geotiff » crs_adapters » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.gce.geotiff.crs_adapters 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2005-2006, GeoTools Project Managment Committee (PMC)
0005:         *
0006:         *    This library is free software; you can redistribute it and/or
0007:         *    modify it under the terms of the GNU Lesser General Public
0008:         *    License as published by the Free Software Foundation;
0009:         *    version 2.1 of the License.
0010:         *
0011:         *    This library is distributed in the hope that it will be useful,
0012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014:         *    Lesser General Public License for more details.
0015:         */
0016:        /*
0017:         * NOTICE OF RELEASE TO THE PUBLIC DOMAIN
0018:         *
0019:         * This work was created by employees of the USDA Forest Service's
0020:         * Fire Science Lab for internal use.  It is therefore ineligible for
0021:         * copyright under title 17, section 105 of the United States Code.  You
0022:         * may treat it as you would treat any public domain work: it may be used,
0023:         * changed, copied, or redistributed, with or without permission of the
0024:         * authors, for free or for compensation.  You may not claim exclusive
0025:         * ownership of this code because it is already owned by everyone.  Use this
0026:         * software entirely at your own risk.  No warranty of any kind is given.
0027:         *
0028:         * A copy of 17-USC-105 should have accompanied this distribution in the file
0029:         * 17USC105.html.  If not, you may access the law via the US Government's
0030:         * public websites:
0031:         *   - http://www.copyright.gov/title17/92chap1.html#105
0032:         *   - http://www.gpoaccess.gov/uscode/  (enter "17USC105" in the search box.)
0033:         */
0034:        package org.geotools.gce.geotiff.crs_adapters;
0035:
0036:        import java.awt.geom.AffineTransform;
0037:        import java.io.IOException;
0038:        import java.lang.ref.Reference;
0039:        import java.util.Collections;
0040:        import java.util.HashMap;
0041:        import java.util.Map;
0042:        import java.util.logging.Level;
0043:        import java.util.logging.Logger;
0044:
0045:        import javax.units.NonSI;
0046:        import javax.units.SI;
0047:        import javax.units.Unit;
0048:
0049:        import org.geotools.factory.Hints;
0050:        import org.geotools.gce.geotiff.GeoTiffException;
0051:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.GeoTiffIIOMetadataDecoder;
0052:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.PixelScale;
0053:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.TiePoint;
0054:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.utils.GeoTiffConstants;
0055:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.utils.codes.GeoTiffCoordinateTransformationsCodes;
0056:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.utils.codes.GeoTiffGCSCodes;
0057:        import org.geotools.gce.geotiff.IIOMetadataAdpaters.utils.codes.GeoTiffPCSCodes;
0058:        import org.geotools.metadata.iso.citation.CitationImpl;
0059:        import org.geotools.referencing.CRS;
0060:        import org.geotools.referencing.ReferencingFactoryFinder;
0061:        import org.geotools.referencing.crs.DefaultGeographicCRS;
0062:        import org.geotools.referencing.crs.DefaultProjectedCRS;
0063:        import org.geotools.referencing.cs.DefaultCartesianCS;
0064:        import org.geotools.referencing.cs.DefaultCoordinateSystemAxis;
0065:        import org.geotools.referencing.cs.DefaultEllipsoidalCS;
0066:        import org.geotools.referencing.datum.DefaultEllipsoid;
0067:        import org.geotools.referencing.datum.DefaultGeodeticDatum;
0068:        import org.geotools.referencing.datum.DefaultPrimeMeridian;
0069:        import org.geotools.referencing.factory.AllAuthoritiesFactory;
0070:        import org.geotools.referencing.factory.ReferencingFactoryContainer;
0071:        import org.geotools.referencing.operation.DefaultMathTransformFactory;
0072:        import org.geotools.referencing.operation.matrix.GeneralMatrix;
0073:        import org.geotools.referencing.operation.transform.ProjectiveTransform;
0074:        import org.geotools.resources.i18n.Vocabulary;
0075:        import org.geotools.resources.i18n.VocabularyKeys;
0076:        import org.geotools.util.SoftValueHashMap;
0077:        import org.opengis.parameter.ParameterValueGroup;
0078:        import org.opengis.referencing.FactoryException;
0079:        import org.opengis.referencing.NoSuchIdentifierException;
0080:        import org.opengis.referencing.crs.CRSFactory;
0081:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0082:        import org.opengis.referencing.crs.GeographicCRS;
0083:        import org.opengis.referencing.crs.ImageCRS;
0084:        import org.opengis.referencing.crs.ProjectedCRS;
0085:        import org.opengis.referencing.cs.AxisDirection;
0086:        import org.opengis.referencing.cs.CSFactory;
0087:        import org.opengis.referencing.cs.CartesianCS;
0088:        import org.opengis.referencing.cs.CoordinateSystem;
0089:        import org.opengis.referencing.datum.DatumFactory;
0090:        import org.opengis.referencing.datum.Ellipsoid;
0091:        import org.opengis.referencing.datum.GeodeticDatum;
0092:        import org.opengis.referencing.datum.ImageDatum;
0093:        import org.opengis.referencing.datum.PixelInCell;
0094:        import org.opengis.referencing.datum.PrimeMeridian;
0095:        import org.opengis.referencing.operation.Conversion;
0096:        import org.opengis.referencing.operation.MathTransform;
0097:        import org.opengis.referencing.operation.MathTransformFactory;
0098:
0099:        /**
0100:         * The <code>GeoTiffMetadata2CRSAdapter</code> is responsible for interpreting
0101:         * the metadata provided by the <code>GeoTiffIIOMetadataDecoder</code> for the
0102:         * purposes of constructing a CoordinateSystem object representative of the
0103:         * information found in the tags.
0104:         * 
0105:         * <p>
0106:         * This class implements the flow indicated by the following diagram:
0107:         * </p>
0108:         * 
0109:         * <p align="center">
0110:         * <img src="../../../../../../../doc-files/GeoTiffFlow.png">
0111:         * </p>
0112:         * 
0113:         * <p>
0114:         * To use this class, the <CODE>GeoTiffReader</CODE> should create an instance
0115:         * with the <code>CoordinateSystemAuthorityFactory</code> specified by the
0116:         * <CODE>GeoTiffFormat</CODE> instance which created the reader. The image
0117:         * specific metadata should then be set with the appropriate accessor methods.
0118:         * Finally, the <code>createCoordinateSystem()</code> method is called to
0119:         * produce the <code>CoordinateReferenceSystem</code> object specified by the
0120:         * metadata.
0121:         * </p>
0122:         * 
0123:         * @author Bryce Nordgren / USDA Forest Service
0124:         * @author Simone Giannecchini
0125:         * @source $URL:
0126:         *         http://svn.geotools.org/geotools/trunk/gt/plugin/geotiff/src/org/geotools/gce/geotiff/crs_adapters/GeoTiffMetadata2CRSAdapter.java $
0127:         */
0128:        public final class GeoTiffMetadata2CRSAdapter {
0129:
0130:            /** {@link Logger}. */
0131:            private final static Logger LOGGER = org.geotools.util.logging.Logging
0132:                    .getLogger("org.geotools.gce.geotiff.crs_adapters");
0133:
0134:            /**
0135:             * This {@link AffineTransform} can be used when the underlying geotiff
0136:             * declares to work with {@link GeoTiffConstants#RasterPixelIsArea} pixel
0137:             * interpretation in order to convert the transformtion back to using the
0138:             * {@link GeoTiffConstants#RasterPixelIsArea} convention which is the one
0139:             * OGC requires for coverage.
0140:             */
0141:            private static final AffineTransform PixelIsArea2PixelIsPoint = AffineTransform
0142:                    .getTranslateInstance(0.5, 0.5);
0143:
0144:            /** EPSG Factory for creating {@link GeodeticDatum}objects. */
0145:            private final DatumFactory datumObjFactory;
0146:
0147:            /** CRS Factory for creating CRS objects. */
0148:            private final CRSFactory crsFactory;
0149:
0150:            /**
0151:             * {@link Hints} to control the creation of the factories for this
0152:             * {@link GeoTiffMetadata2CRSAdapter} object.
0153:             */
0154:            private Hints hints;
0155:
0156:            /**
0157:             * Cached {@link MathTransformFactory} for building {@link MathTransform}
0158:             * objects.
0159:             */
0160:            private final static MathTransformFactory mtFactory = new DefaultMathTransformFactory();
0161:
0162:            /**
0163:             * The default value for {@link #maxStrongReferences} .
0164:             */
0165:            public static final int DEFAULT_MAX = 20;
0166:
0167:            /**
0168:             * The pool of cached objects.
0169:             */
0170:            private final static Map pool = Collections
0171:                    .synchronizedMap(new SoftValueHashMap(DEFAULT_MAX));
0172:
0173:            /** Group Factory for creating {@link ProjectedCRS} objects. */
0174:            private final ReferencingFactoryContainer factories;
0175:
0176:            /** CS Factory for creating {@link CoordinateSystem} objects. */
0177:            private final CSFactory csFactory;
0178:
0179:            /** EPSG factories for various purposes. */
0180:            private final AllAuthoritiesFactory allAuthoritiesFactory;
0181:
0182:            /**
0183:             * Creates a new instance of GeoTiffMetadata2CRSAdapter
0184:             * 
0185:             * @param hints
0186:             *            a map of hints to locate the authority and object factories.
0187:             *            (can be null)
0188:             */
0189:            public GeoTiffMetadata2CRSAdapter(Hints hints) {
0190:
0191:                final Hints tempHints = hints != null ? new Hints(hints)
0192:                        : new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER,
0193:                                Boolean.TRUE);
0194:
0195:                this .hints = (Hints) tempHints.clone();
0196:                allAuthoritiesFactory = new AllAuthoritiesFactory(this .hints);
0197:
0198:                // factory = new ThreadedEpsgFactory(hints);
0199:                datumObjFactory = ReferencingFactoryFinder
0200:                        .getDatumFactory(this .hints);
0201:                crsFactory = ReferencingFactoryFinder.getCRSFactory(this .hints);
0202:                csFactory = ReferencingFactoryFinder.getCSFactory(this .hints);
0203:                tempHints.put(Hints.DATUM_AUTHORITY_FACTORY,
0204:                        allAuthoritiesFactory);
0205:                tempHints.put(Hints.CS_FACTORY, csFactory);
0206:                tempHints.put(Hints.CRS_FACTORY, crsFactory);
0207:                tempHints.put(Hints.MATH_TRANSFORM_FACTORY, mtFactory);
0208:                factories = ReferencingFactoryContainer.instance(tempHints);
0209:            }
0210:
0211:            /**
0212:             * This method creates a <code>CoordinateReferenceSystem</code> object
0213:             * from the metadata which has been set earlier. If it cannot create the
0214:             * <code>CoordinateReferenceSystem</code>, then one of three exceptions
0215:             * is thrown to indicate the error.
0216:             * 
0217:             * @return the <code>CoordinateReferenceSystem</code> object representing
0218:             *         the file data
0219:             * 
0220:             * @throws IOException
0221:             *             if there is unexpected data in the GeoKey tags.
0222:             * @throws FactoryException
0223:             * @throws NullPointerException
0224:             *             if the <code>csAuthorityFactory</code>,
0225:             *             <code>datumFactory</code>, <code>crsFactory</code> or
0226:             *             <code>metadata</code> are uninitialized
0227:             * @throws UnsupportedOperationException
0228:             *             if the coordinate system specified by the GeoTiff file is not
0229:             *             supported.
0230:             */
0231:            public CoordinateReferenceSystem createCoordinateSystem(
0232:                    final GeoTiffIIOMetadataDecoder metadata)
0233:                    throws IOException, FactoryException {
0234:
0235:                // the first thing to check is the Model Type.
0236:                // is it "Projected" or is it "Geographic"?
0237:                // "Geocentric" is not supported.
0238:                switch (getGeoKeyAsInt(GeoTiffConstants.GTModelTypeGeoKey,
0239:                        metadata)) {
0240:                case GeoTiffPCSCodes.ModelTypeProjected:
0241:                    return createProjectedCoordinateSystem(metadata);
0242:
0243:                case GeoTiffGCSCodes.ModelTypeGeographic:
0244:                    return createGeographicCoordinateSystem(metadata);
0245:
0246:                default:
0247:                    throw new UnsupportedOperationException(
0248:                            "GeoTiffMetadata2CRSAdapter::createCoordinateSystem:Only Geographic & Projected Systems are supported.  ");
0249:                }
0250:
0251:            }
0252:
0253:            /**
0254:             * This code is responsible for creating a projected coordinate reference
0255:             * system as specified in the GeoTiff specification. User defined values are
0256:             * supported throughout the evolution of this specification with except of
0257:             * the coordinate transformation which must be one of the supported types.
0258:             * 
0259:             * @param metadata
0260:             *            to use for building a {@link ProjectedCRS}.
0261:             * 
0262:             * @return
0263:             * @throws IOException
0264:             * @throws FactoryException
0265:             */
0266:            private ProjectedCRS createProjectedCoordinateSystem(
0267:                    GeoTiffIIOMetadataDecoder metadata) throws IOException,
0268:                    FactoryException {
0269:
0270:                // //
0271:                //
0272:                // Get the projection reference system code in case we have one by
0273:                // lookig for the ProjectedCSTypeGeoKey key
0274:                //
0275:                // //
0276:                String tempCode = metadata
0277:                        .getGeoKey(GeoTiffPCSCodes.ProjectedCSTypeGeoKey);
0278:                if (tempCode == null)
0279:                    tempCode = "unnamed".intern();
0280:                final StringBuffer projCode = new StringBuffer(tempCode.trim()
0281:                        .intern());
0282:
0283:                // //
0284:                //
0285:                // getting the linear unit used by this coordinate reference system
0286:                // since we will use it anyway.
0287:                //
0288:                // //
0289:                Unit linearUnit;
0290:                try {
0291:                    linearUnit = createUnit(
0292:                            GeoTiffPCSCodes.ProjLinearUnitsGeoKey,
0293:                            GeoTiffPCSCodes.ProjLinearUnitSizeGeoKey, SI.METER,
0294:                            SI.METER, metadata);
0295:                } catch (GeoTiffException e) {
0296:                    linearUnit = null;
0297:                }
0298:                // //
0299:                //
0300:                // if it's user defined, there's a lot of work to do, we have to parse
0301:                // many information.
0302:                //
0303:                // //
0304:                if (tempCode.equalsIgnoreCase("unnamed")
0305:                        || tempCode
0306:                                .equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
0307:                    return createUserDefinedPCS(metadata, linearUnit);
0308:
0309:                }
0310:                // //
0311:                //
0312:                // if it's not user defined, just use the EPSG factory to create the
0313:                // coordinate system
0314:                //
0315:                // //
0316:                try {
0317:                    if (!tempCode.startsWith("EPSG")
0318:                            && !tempCode.startsWith("epsg")) {
0319:                        projCode.insert(0, "EPSG:");
0320:                    }
0321:                    // it is an EPSG crs let's create it.
0322:                    final ProjectedCRS pcrs = (ProjectedCRS) CRS.decode(
0323:                            projCode.toString(), true);
0324:                    // //
0325:                    //
0326:                    // We have nothing to do with the unit of measure
0327:                    //
0328:                    // //
0329:                    if (linearUnit == null
0330:                            || linearUnit.equals(pcrs.getCoordinateSystem()
0331:                                    .getAxis(0).getUnit()))
0332:                        return pcrs;
0333:                    // //
0334:                    //
0335:                    // Creating anew projected CRS
0336:                    //
0337:                    // //
0338:                    return new DefaultProjectedCRS(DefaultEllipsoidalCS
0339:                            .getName(pcrs, new CitationImpl("EPSG")), pcrs
0340:                            .getConversionFromBase().getMethod(),
0341:                            (GeographicCRS) pcrs.getBaseCRS(),
0342:                            pcrs.getConversionFromBase().getMathTransform(),
0343:                            createProjectedCS(linearUnit));
0344:                } catch (FactoryException fe) {
0345:                    final IOException ex = new GeoTiffException(metadata, fe
0346:                            .getLocalizedMessage(), fe);
0347:                    throw ex;
0348:                }
0349:            }
0350:
0351:            /**
0352:             * Creation of a geographic coordinate reference system as specified in the
0353:             * GeoTiff specification. User defined values are supported for all the
0354:             * possible levels of the above mentioned specification.
0355:             * 
0356:             * @param metadata
0357:             *            to use for building a {@link GeographicCRS}.
0358:             * 
0359:             * @return
0360:             * @throws IOException
0361:             */
0362:            private GeographicCRS createGeographicCoordinateSystem(
0363:                    final GeoTiffIIOMetadataDecoder metadata)
0364:                    throws IOException {
0365:                GeographicCRS gcs = null;
0366:
0367:                // ////////////////////////////////////////////////////////////////////
0368:                //
0369:                // Get the crs code
0370:                //
0371:                // ////////////////////////////////////////////////////////////////////
0372:                final String tempCode = metadata
0373:                        .getGeoKey(GeoTiffGCSCodes.GeographicTypeGeoKey);
0374:                // lookup the angular units used in this geotiff image
0375:                Unit angularUnit = null;
0376:                try {
0377:                    angularUnit = createUnit(
0378:                            GeoTiffGCSCodes.GeogAngularUnitsGeoKey,
0379:                            GeoTiffGCSCodes.GeogAngularUnitSizeGeoKey,
0380:                            SI.RADIAN, NonSI.DEGREE_ANGLE, metadata);
0381:                } catch (GeoTiffException e) {
0382:                    angularUnit = null;
0383:                }
0384:                // linear unit
0385:                Unit linearUnit = null;
0386:                try {
0387:                    linearUnit = createUnit(
0388:                            GeoTiffGCSCodes.GeogLinearUnitsGeoKey,
0389:                            GeoTiffGCSCodes.GeogLinearUnitSizeGeoKey, SI.METER,
0390:                            SI.METER, metadata);
0391:                } catch (GeoTiffException e) {
0392:                    linearUnit = null;
0393:                }
0394:                // if it's user defined, there's a lot of work to do
0395:                if (tempCode == null
0396:                        || tempCode
0397:                                .equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
0398:                    // ////////////////////////////////////////////////////////////////////
0399:                    //
0400:                    // it is user-defined we have to parse a lot of information in order
0401:                    // to built it.
0402:                    //
0403:                    // ////////////////////////////////////////////////////////////////////
0404:                    gcs = createUserDefinedGCS(metadata, linearUnit,
0405:                            angularUnit);
0406:
0407:                } else {
0408:                    try {
0409:
0410:                        // ////////////////////////////////////////////////////////////////////
0411:                        //
0412:                        // If it's not user defined, just use the EPSG factory to create
0413:                        // the coordinate system but check if the user specified a
0414:                        // different angular unit. In this case we need to create a
0415:                        // user-defined GCRS.
0416:                        //
0417:                        // ////////////////////////////////////////////////////////////////////
0418:                        final StringBuffer geogCode = new StringBuffer(tempCode);
0419:                        if (!tempCode.startsWith("EPSG")
0420:                                && !tempCode.startsWith("epsg")) {
0421:                            geogCode.insert(0, "EPSG:");
0422:                        }
0423:                        gcs = (GeographicCRS) CRS.decode(geogCode.toString(),
0424:                                true);
0425:                        if (angularUnit != null
0426:                                && !angularUnit.equals(gcs
0427:                                        .getCoordinateSystem().getAxis(0)
0428:                                        .getUnit())) {
0429:                            // //
0430:                            //
0431:                            // Create a user-defined GCRS using the provided angular
0432:                            // unit.
0433:                            //
0434:                            // //
0435:                            gcs = new DefaultGeographicCRS(DefaultEllipsoidalCS
0436:                                    .getName(gcs, new CitationImpl("EPSG")),
0437:                                    (GeodeticDatum) gcs.getDatum(),
0438:                                    DefaultEllipsoidalCS.GEODETIC_2D
0439:                                            .usingUnit(angularUnit));
0440:                        }
0441:                    } catch (FactoryException fe) {
0442:                        final IOException ex = new GeoTiffException(metadata,
0443:                                fe.getLocalizedMessage(), fe);
0444:
0445:                        throw ex;
0446:                    }
0447:                }
0448:
0449:                return gcs;
0450:            }
0451:
0452:            /**
0453:             * Getting a specified geotiff geo key as a int. It is somehow tolerant in
0454:             * the sense that in case such a key does not exist it retrieves 0.
0455:             * 
0456:             * @param key
0457:             *            we want to get the value for.
0458:             * @param metadata
0459:             *            containing the key we are looking for.
0460:             * @return
0461:             */
0462:            private int getGeoKeyAsInt(final int key,
0463:                    final GeoTiffIIOMetadataDecoder metadata) {
0464:
0465:                try {
0466:                    return Integer.parseInt(metadata.getGeoKey(key));
0467:                } catch (NumberFormatException ne) {
0468:                    if (LOGGER.isLoggable(Level.WARNING))
0469:                        LOGGER.log(Level.WARNING, ne.getLocalizedMessage(), ne);
0470:                    return Integer.MIN_VALUE;
0471:                }
0472:
0473:            }
0474:
0475:            /**
0476:             * Create the grid to world (or raster to model) transformation for this
0477:             * source repsecting ALWAYS the OGC {@link PixelInCell#CELL_CENTER}
0478:             * onvention for the {@link ImageDatum} of the underlying {@link ImageCRS}.
0479:             * 
0480:             * @see <a
0481:             *      href="http://lists.maptools.org/pipermail/geotiff/2006-January/000213.html">this
0482:             *      email post</a>
0483:             * @param metadata
0484:             *            containing the information to build the {@link MathTransform}
0485:             *            for going from grid to world.
0486:             * 
0487:             * @throws GeoTiffException
0488:             */
0489:            public MathTransform getRasterToModel(
0490:                    final GeoTiffIIOMetadataDecoder metadata)
0491:                    throws GeoTiffException {
0492:                // /////////////////////////////////////////////////////////////////////
0493:                //
0494:                // Load initials
0495:                //
0496:                // /////////////////////////////////////////////////////////////////////
0497:                final boolean hasTiePoints = metadata.hasTiePoints();
0498:                final boolean hasPixelScales = metadata.hasPixelScales();
0499:                final boolean hasModelTransformation = metadata
0500:                        .hasModelTrasformation();
0501:                int rasterType = getGeoKeyAsInt(
0502:                        GeoTiffConstants.GTRasterTypeGeoKey, metadata);
0503:                // geotiff spec says that PixelIsArea is the default
0504:                if (rasterType == GeoTiffConstants.UNDEFINED)
0505:                    rasterType = GeoTiffConstants.RasterPixelIsArea;
0506:                MathTransform xform = null;
0507:                if (hasTiePoints && hasPixelScales) {
0508:                    final TiePoint[] tiePoints = metadata.getModelTiePoints();
0509:                    final PixelScale pixScales = metadata.getModelPixelScales();
0510:
0511:                    // /////////////////////////////////////////////////////////////////////
0512:                    //
0513:                    // checking the directions of the axes.
0514:                    // we need to understand how the axes of this gridcoverage are
0515:                    // specified.
0516:                    // trying to understand the direction of the first axis in order to
0517:                    //
0518:                    // /////////////////////////////////////////////////////////////////////
0519:                    // latitude index
0520:
0521:                    final GeneralMatrix gm = new GeneralMatrix(3); // identity
0522:                    final double scaleRaster2ModelLongitude = pixScales
0523:                            .getScaleX();
0524:                    final double scaleRaster2ModelLatitude = -pixScales
0525:                            .getScaleY();
0526:                    final double tiePointColumn = tiePoints[0].getValueAt(0)
0527:                            + (rasterType == GeoTiffConstants.RasterPixelIsArea ? -0.5
0528:                                    : 0); // "raster" space
0529:                    // coordinates
0530:                    // (indicies)
0531:                    final double tiePointRow = tiePoints[0].getValueAt(1)
0532:                            + (rasterType == GeoTiffConstants.RasterPixelIsArea ? -0.5
0533:                                    : 0);
0534:
0535:                    // compute an "offset and scale" matrix
0536:                    gm.setElement(0, 0, scaleRaster2ModelLongitude);
0537:                    gm.setElement(1, 1, scaleRaster2ModelLatitude);
0538:                    gm.setElement(0, 1, 0);
0539:                    gm.setElement(1, 0, 0);
0540:
0541:                    gm.setElement(0, 2, tiePoints[0].getValueAt(3)
0542:                            - (scaleRaster2ModelLongitude * tiePointColumn));
0543:                    gm.setElement(1, 2, tiePoints[0].getValueAt(4)
0544:                            - (scaleRaster2ModelLatitude * tiePointRow));
0545:
0546:                    // make it a LinearTransform
0547:                    xform = ProjectiveTransform.create(gm);
0548:
0549:                } else if (hasModelTransformation) {
0550:                    if (rasterType == GeoTiffConstants.RasterPixelIsArea)
0551:                        xform = ProjectiveTransform.create(metadata
0552:                                .getModelTransformation());
0553:                    else {
0554:                        assert rasterType == GeoTiffConstants.RasterPixelIsPoint;
0555:                        final AffineTransform tempTransform = new AffineTransform(
0556:                                metadata.getModelTransformation());
0557:                        tempTransform.concatenate(PixelIsArea2PixelIsPoint);
0558:                        xform = ProjectiveTransform.create(tempTransform);
0559:
0560:                    }
0561:                } else
0562:                    throw new GeoTiffException(metadata,
0563:                            "Unknown Raster to Model configuration.", null);
0564:
0565:                return xform;
0566:            }
0567:
0568:            /**
0569:             * Getting a specified geotiff geo key as a double. It is somehow tolerant
0570:             * in the sense that in case such a key does not exist it retrieves 0.
0571:             * 
0572:             * @param key
0573:             *            we want to get the value for.
0574:             * @param metadata
0575:             *            containing the key we are looking for.
0576:             * @return the value for the provided key.
0577:             * @throws IOException
0578:             */
0579:            private double getGeoKeyAsDouble(final int key,
0580:                    final GeoTiffIIOMetadataDecoder metadata) {
0581:
0582:                try {
0583:                    return Double.parseDouble(metadata.getGeoKey(key));
0584:                } catch (NumberFormatException ne) {
0585:                    if (LOGGER.isLoggable(Level.WARNING))
0586:                        LOGGER.log(Level.WARNING, ne.getLocalizedMessage(), ne);
0587:                    return Double.NaN;
0588:                } catch (Exception e) {
0589:                    LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
0590:                    return Double.NaN;
0591:                }
0592:            }
0593:
0594:            /**
0595:             * We have a user defined {@link ProjectedCRS}, let's try to parse it.
0596:             * 
0597:             * @param linearUnit
0598:             *            is the UoM that this {@link ProjectedCRS} will use. It could
0599:             *            be null.
0600:             * 
0601:             * @return a user-defined {@link ProjectedCRS}.
0602:             * @throws IOException
0603:             * @throws FactoryException
0604:             */
0605:            private ProjectedCRS createUserDefinedPCS(
0606:                    final GeoTiffIIOMetadataDecoder metadata, Unit linearUnit)
0607:                    throws IOException, FactoryException {
0608:
0609:                // /////////////////////////////////////////////////////////////////
0610:                // 
0611:                // At the top level a user-defined PCRS is made by
0612:                // <ol>
0613:                // <li>PCSCitationGeoKey (NAME)
0614:                // <li>ProjectionGeoKey
0615:                // <li>GeographicTypeGeoKey
0616:                // </ol>
0617:                // 
0618:                // 
0619:                // /////////////////////////////////////////////////////////////////
0620:                // //
0621:                //
0622:                // NAME of the user defined projected coordinate reference system.
0623:                //
0624:                // //
0625:                String projectedCrsName = metadata
0626:                        .getGeoKey(GeoTiffPCSCodes.PCSCitationGeoKey);
0627:                if (projectedCrsName == null)
0628:                    projectedCrsName = "unnamed".intern();
0629:                else
0630:                    projectedCrsName = cleanName(projectedCrsName);
0631:
0632:                // /////////////////////////////////////////////////////////////////////
0633:                //
0634:                // PROJECTION geo key for this projected coordinate reference system.
0635:                // get the projection code for this PCRS to build it from the GCS.
0636:                //
0637:                // In case i is user defined it requires:
0638:                // PCSCitationGeoKey
0639:                // ProjCoordTransGeoKey
0640:                // ProjLinearUnitsGeoKey
0641:                //
0642:                // /////////////////////////////////////////////////////////////////////
0643:                final String projCode = metadata
0644:                        .getGeoKey(GeoTiffPCSCodes.ProjectionGeoKey);
0645:                boolean projUserDefined = false;
0646:                if (projCode == null
0647:                        || projCode
0648:                                .equals(GeoTiffConstants.GTUserDefinedGeoKey_String))
0649:                    projUserDefined = true;
0650:
0651:                // is it user defined?
0652:                Conversion projection = null;
0653:                final ParameterValueGroup parameters;
0654:                if (projUserDefined) {
0655:                    // /////////////////////////////////////////////////////////////////
0656:                    // A user defined projection is made up by
0657:                    // <ol>
0658:                    // <li>PCSCitationGeoKey (NAME)
0659:                    // <li>ProjCoordTransGeoKey
0660:                    // <li>ProjLinearUnitsGeoKey
0661:                    // </ol>
0662:                    // /////////////////////////////////////////////////////////////////
0663:                    // NAME of this projection coordinate transformation
0664:                    // getting user defined parameters
0665:                    String projectionName = metadata
0666:                            .getGeoKey(GeoTiffPCSCodes.PCSCitationGeoKey);
0667:                    if (projectionName == null)
0668:                        projectionName = "unnamed";
0669:
0670:                    // //
0671:                    //
0672:                    // getting default parameters for this projection and filling them
0673:                    // with the values found
0674:                    // inside the geokeys list.
0675:                    //
0676:                    // //
0677:                    parameters = createUserDefinedProjectionParameter(
0678:                            projectionName, metadata);
0679:                    if (parameters == null)
0680:                        throw new GeoTiffException(
0681:                                metadata,
0682:                                "GeoTiffMetadata2CRSAdapter::createUserDefinedPCS:Projection is not supported.",
0683:                                null);
0684:
0685:                } else {
0686:                    parameters = null;
0687:                    projection = (Conversion) this .allAuthoritiesFactory
0688:                            .createCoordinateOperation(new StringBuffer("EPSG:")
0689:                                    .append(projCode).toString());
0690:                }
0691:
0692:                // /////////////////////////////////////////////////////////////////////
0693:                //
0694:                // GEOGRAPHIC CRS
0695:                //
0696:                // /////////////////////////////////////////////////////////////////////
0697:                final GeographicCRS gcs = createGeographicCoordinateSystem(metadata);
0698:
0699:                // was the projection user defined?
0700:                // in such case we need to set the remaining parameters.
0701:                if (projUserDefined) {
0702:                    final GeodeticDatum tempDatum = ((GeodeticDatum) gcs
0703:                            .getDatum());
0704:                    final DefaultEllipsoid tempEll = (DefaultEllipsoid) tempDatum
0705:                            .getEllipsoid();
0706:                    double inverseFlattening = tempEll.getInverseFlattening();
0707:                    double semiMajorAxis = tempEll.getSemiMajorAxis();
0708:                    // setting missing parameters
0709:                    parameters.parameter("semi_minor").setValue(
0710:                            semiMajorAxis * (1 - (1 / inverseFlattening)));
0711:                    parameters.parameter("semi_major").setValue(semiMajorAxis);
0712:
0713:                }
0714:
0715:                // /////////////////////////////////////////////////////////////////////
0716:                //
0717:                // PROJECTED CRS
0718:                //
0719:                // /////////////////////////////////////////////////////////////////////
0720:                // //
0721:                //
0722:                // I am putting particular attention on the management of the unit
0723:                // of measure since it seems that very often people change the unit
0724:                // of measure to feet even if the standard UoM for the request
0725:                // projection is M.
0726:                //
0727:                // ///
0728:                if (projUserDefined) {
0729:                    // user defined projection
0730:                    if (linearUnit != null && linearUnit.equals(SI.METER))
0731:                        return this .factories.createProjectedCRS(Collections
0732:                                .singletonMap("name", projectedCrsName), gcs,
0733:                                null, parameters, DefaultCartesianCS.PROJECTED);
0734:                    return factories.createProjectedCRS(Collections
0735:                            .singletonMap("name", projectedCrsName), gcs, null,
0736:                            parameters, DefaultCartesianCS.PROJECTED
0737:                                    .usingUnit(linearUnit));
0738:                }
0739:                // standard projection
0740:                if (linearUnit != null && !linearUnit.equals(SI.METER))
0741:                    return factories.createProjectedCRS(Collections
0742:                            .singletonMap("name", projectedCrsName), gcs,
0743:                            projection, DefaultCartesianCS.PROJECTED
0744:                                    .usingUnit(linearUnit));
0745:                return factories.createProjectedCRS(Collections.singletonMap(
0746:                        "name", projectedCrsName), gcs, projection,
0747:                        DefaultCartesianCS.PROJECTED);
0748:            }
0749:
0750:            /**
0751:             * Clean the provided parameters <code>tiffName</code> from strange
0752:             * strings like it happens with erdas imageine.
0753:             * 
0754:             * @param tiffName
0755:             *            is the {@link String} to clean up.
0756:             * @return a cleaned up {@link String}.
0757:             */
0758:            private final static String cleanName(String tiffName) {
0759:                // look fofr strange chars
0760:                // $
0761:                int index = tiffName.lastIndexOf('$');
0762:                if (index != -1)
0763:                    tiffName = tiffName.substring(index + 1);
0764:                // \n
0765:                index = tiffName.lastIndexOf('\n');
0766:                if (index != -1)
0767:                    tiffName = tiffName.substring(index + 1);
0768:                // \r
0769:                index = tiffName.lastIndexOf('\r');
0770:                if (index != -1)
0771:                    tiffName = tiffName.substring(index + 1);
0772:                return tiffName;
0773:            }
0774:
0775:            /**
0776:             * Creates a {@link CartesianCS} for a {@link ProjectedCRS} given the
0777:             * provided {@link Unit}.
0778:             * 
0779:             * @todo conside caching this items
0780:             * @param linearUnit
0781:             *            to be used for building this {@link CartesianCS}.
0782:             * @return an instance of {@link CartesianCS} using the provided
0783:             *         {@link Unit},
0784:             */
0785:            private DefaultCartesianCS createProjectedCS(Unit linearUnit) {
0786:                if (linearUnit == null)
0787:                    throw new NullPointerException(
0788:                            "Error when trying to create a PCS using this linear UoM ");
0789:                if (!linearUnit.isCompatible(SI.METER))
0790:                    throw new IllegalArgumentException(
0791:                            "Error when trying to create a PCS using this linear UoM "
0792:                                    + linearUnit.toString());
0793:                return new DefaultCartesianCS(Vocabulary.formatInternational(
0794:                        VocabularyKeys.PROJECTED).toString(),
0795:                        new DefaultCoordinateSystemAxis(Vocabulary
0796:                                .formatInternational(VocabularyKeys.EASTING),
0797:                                "E", AxisDirection.EAST, linearUnit),
0798:                        new DefaultCoordinateSystemAxis(Vocabulary
0799:                                .formatInternational(VocabularyKeys.NORTHING),
0800:                                "N", AxisDirection.NORTH, linearUnit));
0801:            }
0802:
0803:            /**
0804:             * Creating a prime meridian for the gcs we are creating at an higher level.
0805:             * As usua this method tries to follow tthe geotiff specification.
0806:             * 
0807:             * @param linearUnit
0808:             *            to use for building this {@link PrimeMeridian}.
0809:             * 
0810:             * @return a {@link PrimeMeridian} built using the provided {@link Unit} and
0811:             *         the provided metadata.
0812:             * @throws IOException
0813:             */
0814:            private PrimeMeridian createPrimeMeridian(
0815:                    final GeoTiffIIOMetadataDecoder metadata, Unit linearUnit)
0816:                    throws IOException {
0817:                // look up the prime meridian:
0818:                // + could be an EPSG code
0819:                // + could be user defined
0820:                // + not defined = greenwich
0821:                final String pmCode = metadata
0822:                        .getGeoKey(GeoTiffGCSCodes.GeogPrimeMeridianGeoKey);
0823:                PrimeMeridian pm = null;
0824:
0825:                try {
0826:                    if (pmCode != null) {
0827:                        if (pmCode
0828:                                .equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
0829:                            try {
0830:                                final String name = metadata
0831:                                        .getGeoKey(GeoTiffGCSCodes.GeogCitationGeoKey);
0832:                                final String pmValue = metadata
0833:                                        .getGeoKey(GeoTiffGCSCodes.GeogPrimeMeridianLongGeoKey);
0834:                                final double pmNumeric = Double
0835:                                        .parseDouble(pmValue);
0836:                                // is it Greenwich?
0837:                                if (pmNumeric == 0)
0838:                                    return DefaultPrimeMeridian.GREENWICH;
0839:                                final Map props = new HashMap();
0840:                                props
0841:                                        .put(
0842:                                                "name",
0843:                                                (name != null) ? name
0844:                                                        : "User Defined GEOTIFF Prime Meridian");
0845:                                pm = datumObjFactory.createPrimeMeridian(props,
0846:                                        pmNumeric, linearUnit);
0847:                            } catch (NumberFormatException nfe) {
0848:                                final IOException io = new GeoTiffException(
0849:                                        metadata,
0850:                                        "Invalid user-defined prime meridian spec.",
0851:                                        nfe);
0852:
0853:                                throw io;
0854:                            }
0855:                        } else {
0856:                            pm = this .allAuthoritiesFactory
0857:                                    .createPrimeMeridian("EPSG:" + pmCode);
0858:                        }
0859:                    } else {
0860:                        pm = DefaultPrimeMeridian.GREENWICH;
0861:                    }
0862:                } catch (FactoryException fe) {
0863:                    final IOException io = new GeoTiffException(metadata, fe
0864:                            .getLocalizedMessage(), fe);
0865:                    throw io;
0866:                }
0867:
0868:                return pm;
0869:            }
0870:
0871:            /**
0872:             * Looks up the Geodetic Datum as specified in the GeoTIFFWritingUtilities
0873:             * file. The geotools definition of the geodetic datum includes both an
0874:             * ellipsoid and a prime meridian, but the code in the
0875:             * GeoTIFFWritingUtilities file does NOT include the prime meridian, as it
0876:             * is specified separately. This code currently does not support user
0877:             * defined datum.
0878:             * 
0879:             * @param unit
0880:             *            to use for building this {@link GeodeticDatum}.
0881:             * 
0882:             * @return a {@link GeodeticDatum}.
0883:             * 
0884:             * @throws IOException
0885:             * 
0886:             * @throws GeoTiffException
0887:             * 
0888:             */
0889:            private GeodeticDatum createGeodeticDatum(final Unit unit,
0890:                    final GeoTiffIIOMetadataDecoder metadata)
0891:                    throws IOException {
0892:                // lookup the datum (w/o PrimeMeridian), error if "user defined"
0893:                GeodeticDatum datum = null;
0894:                final String datumCode = metadata
0895:                        .getGeoKey(GeoTiffGCSCodes.GeogGeodeticDatumGeoKey);
0896:
0897:                if (datumCode == null) {
0898:                    throw new GeoTiffException(
0899:                            metadata,
0900:                            "GeoTiffMetadata2CRSAdapter::createGeodeticDatum(Unit unit):A user defined Geographic Coordinate system must include a predefined datum!",
0901:                            null);
0902:                }
0903:
0904:                if (datumCode
0905:                        .equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
0906:                    /**
0907:                     * 
0908:                     * 
0909:                     * USER DEFINED DATUM
0910:                     * 
0911:                     * 
0912:                     * 
0913:                     */
0914:                    // datum name
0915:                    final String datumName = (metadata
0916:                            .getGeoKey(GeoTiffGCSCodes.GeogCitationGeoKey) != null ? metadata
0917:                            .getGeoKey(GeoTiffGCSCodes.GeogCitationGeoKey)
0918:                            : "unnamed");
0919:
0920:                    // is it WGS84?
0921:                    if (datumName.trim().equalsIgnoreCase("WGS84"))
0922:                        return DefaultGeodeticDatum.WGS84;
0923:
0924:                    // ELLIPSOID
0925:                    final Ellipsoid ellipsoid = createEllipsoid(unit, metadata);
0926:
0927:                    // PRIME MERIDIAN
0928:                    // lookup the Prime Meridian.
0929:                    final PrimeMeridian primeMeridian = createPrimeMeridian(
0930:                            metadata, unit);
0931:
0932:                    // DATUM
0933:                    datum = new DefaultGeodeticDatum(datumName, ellipsoid,
0934:                            primeMeridian);
0935:                } else {
0936:                    /**
0937:                     * NOT USER DEFINED DATUM
0938:                     */
0939:
0940:                    // we are going to use the provided EPSG code
0941:                    try {
0942:                        datum = (GeodeticDatum) (this .allAuthoritiesFactory
0943:                                .createDatum(new StringBuffer("EPSG:").append(
0944:                                        datumCode).toString()));
0945:                    } catch (FactoryException fe) {
0946:                        final GeoTiffException ex = new GeoTiffException(
0947:                                metadata, fe.getLocalizedMessage(), fe);
0948:
0949:                        throw ex;
0950:                    } catch (ClassCastException cce) {
0951:                        final GeoTiffException ex = new GeoTiffException(
0952:                                metadata, cce.getLocalizedMessage(), cce);
0953:                        throw ex;
0954:                    }
0955:                }
0956:
0957:                return datum;
0958:            }
0959:
0960:            /**
0961:             * Creating an ellipsoid following the GeoTiff spec.
0962:             * 
0963:             * @param unit
0964:             *            to build this {@link Ellipsoid}..
0965:             * 
0966:             * @return an {@link Ellipsoid}.
0967:             * 
0968:             * @throws GeoTiffException
0969:             */
0970:            private Ellipsoid createEllipsoid(final Unit unit,
0971:                    final GeoTiffIIOMetadataDecoder metadata)
0972:                    throws GeoTiffException {
0973:                // /////////////////////////////////////////////////////////////////////
0974:                //
0975:                // Getting the ellipsoid key in order to understand if we are working
0976:                // against a common ellipsoid or a user defined one.
0977:                //
0978:                // /////////////////////////////////////////////////////////////////////
0979:                // ellipsoid key
0980:                final String ellipsoidKey = metadata
0981:                        .getGeoKey(GeoTiffGCSCodes.GeogEllipsoidGeoKey);
0982:                String temp = null;
0983:                // is the ellipsoid user defined?
0984:                if (ellipsoidKey
0985:                        .equalsIgnoreCase(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
0986:                    // /////////////////////////////////////////////////////////////////////
0987:                    //
0988:                    // USER DEFINED ELLIPSOID
0989:                    //
0990:                    // /////////////////////////////////////////////////////////////////////
0991:                    String nameEllipsoid = metadata
0992:                            .getGeoKey(GeoTiffGCSCodes.GeogCitationGeoKey);
0993:                    if (nameEllipsoid == null)
0994:                        nameEllipsoid = "unnamed";
0995:                    // is it the default for WGS84?
0996:                    if (nameEllipsoid.trim().equalsIgnoreCase("WGS84"))
0997:                        return DefaultEllipsoid.WGS84;
0998:
0999:                    // //
1000:                    //
1001:                    // It is worth to point out that I ALWAYS use the inverse flattening
1002:                    // along with the semi-major axis to builde the Flattened Sphere.
1003:                    // This
1004:                    // has to be done in order to comply with the opposite process of
1005:                    // goin from CRS to metadata where this coupls is always used.
1006:                    //
1007:                    // //
1008:                    // getting temporary parameters
1009:                    temp = metadata
1010:                            .getGeoKey(GeoTiffGCSCodes.GeogSemiMajorAxisGeoKey);
1011:                    final double semiMajorAxis = (temp != null ? Double
1012:                            .parseDouble(temp) : Double.NaN);
1013:                    temp = metadata
1014:                            .getGeoKey(GeoTiffGCSCodes.GeogInvFlatteningGeoKey);
1015:                    final double inverseFlattening;
1016:                    if (temp != null) {
1017:                        inverseFlattening = (temp != null ? Double
1018:                                .parseDouble(temp) : Double.NaN);
1019:                    } else {
1020:                        temp = metadata
1021:                                .getGeoKey(GeoTiffGCSCodes.GeogSemiMinorAxisGeoKey);
1022:                        final double semiMinorAxis = (temp != null ? Double
1023:                                .parseDouble(temp) : Double.NaN);
1024:                        inverseFlattening = semiMajorAxis
1025:                                / (semiMajorAxis - semiMinorAxis);
1026:
1027:                    }
1028:                    // look for the Ellipsoid first then build the datum
1029:                    return DefaultEllipsoid.createFlattenedSphere(
1030:                            nameEllipsoid, semiMajorAxis, inverseFlattening,
1031:                            unit);
1032:                }
1033:
1034:                try {
1035:                    // /////////////////////////////////////////////////////////////////////
1036:                    //
1037:                    // EPSG STANDARD ELLIPSOID
1038:                    //
1039:                    // /////////////////////////////////////////////////////////////////////
1040:                    return this .allAuthoritiesFactory
1041:                            .createEllipsoid(new StringBuffer("EPSG:").append(
1042:                                    ellipsoidKey).toString());
1043:                } catch (FactoryException fe) {
1044:                    final GeoTiffException ex = new GeoTiffException(metadata,
1045:                            fe.getLocalizedMessage(), fe);
1046:
1047:                    throw ex;
1048:                }
1049:            }
1050:
1051:            /**
1052:             * The GeoTIFFWritingUtilities spec requires that a user defined GCS be
1053:             * comprised of the following:
1054:             * 
1055:             * <ul>
1056:             * <li> a citation </li>
1057:             * <li> a datum definition </li>
1058:             * <li> a prime meridian definition (if not Greenwich) </li>
1059:             * <li> an angular unit definition (if not degrees) </li>
1060:             * </ul>
1061:             * 
1062:             * @param metadata
1063:             *            to use fo building this {@link GeographicCRS}.
1064:             * @param linearUnit
1065:             * @param angularUnit
1066:             * @return a {@link GeographicCRS}.
1067:             * 
1068:             * @throws IOException
1069:             */
1070:            private GeographicCRS createUserDefinedGCS(
1071:                    final GeoTiffIIOMetadataDecoder metadata, Unit linearUnit,
1072:                    Unit angularUnit) throws IOException {
1073:                // //
1074:                //
1075:                // coordinate reference system name (GeogCitationGeoKey)
1076:                //
1077:                // //
1078:                String name = metadata
1079:                        .getGeoKey(GeoTiffGCSCodes.GeogCitationGeoKey);
1080:                if (name == null)
1081:                    name = "unnamed";
1082:
1083:                // lookup the Geodetic datum
1084:                final GeodeticDatum datum = createGeodeticDatum(linearUnit,
1085:                        metadata);
1086:
1087:                // coordinate reference system
1088:                GeographicCRS gcs = null;
1089:
1090:                try {
1091:                    // property map is reused
1092:                    final Map props = new HashMap();
1093:                    // make the user defined GCS from all the components...
1094:                    props.put("name", name);
1095:                    gcs = crsFactory.createGeographicCRS(props, datum,
1096:                            DefaultEllipsoidalCS.GEODETIC_2D
1097:                                    .usingUnit(angularUnit));
1098:                } catch (FactoryException fe) {
1099:                    final GeoTiffException io = new GeoTiffException(metadata,
1100:                            fe.getLocalizedMessage(), fe);
1101:                    throw io;
1102:                }
1103:
1104:                return gcs;
1105:            }
1106:
1107:            /**
1108:             * 
1109:             * @todo we should somehow try to to support user defined coordinate
1110:             *       transformation even if for the moment is not so clear to me how we
1111:             *       could achieve that since if we have no clue about the coordinate
1112:             *       transform what we are supposed to do in order to build a
1113:             *       conversion, guess it? How could we pick up the parameters, should
1114:             *       look for all and then guess the right transformation?
1115:             * 
1116:             * @param name
1117:             *            indicates the name for the projection.
1118:             * @param metadata
1119:             *            to use fo building this {@link ParameterValueGroup}.
1120:             * @return a {@link ParameterValueGroup} that can be used to trigger this
1121:             *         projection.
1122:             * @throws IOException
1123:             * @throws FactoryException
1124:             */
1125:            private ParameterValueGroup createUserDefinedProjectionParameter(
1126:                    String name, final GeoTiffIIOMetadataDecoder metadata)
1127:                    throws IOException, FactoryException {
1128:                // //
1129:                //
1130:                // Trying to get the name for the coordinate transformation involved.
1131:                //
1132:                // ///
1133:                final String coordTrans = metadata
1134:                        .getGeoKey(GeoTiffPCSCodes.ProjCoordTransGeoKey);
1135:
1136:                // throw descriptive exception if ProjCoordTransGeoKey not defined
1137:                if ((coordTrans == null)
1138:                        || coordTrans
1139:                                .equalsIgnoreCase(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
1140:                    throw new GeoTiffException(
1141:                            metadata,
1142:                            "GeoTiffMetadata2CRSAdapter::createUserDefinedProjectionParameter(String name):User defined projections must specify"
1143:                                    + " coordinate transformation code in ProjCoordTransGeoKey",
1144:                            null);
1145:                }
1146:
1147:                // getting math transform factory
1148:                return setParametersForProjection(name, coordTrans, metadata);
1149:            }
1150:
1151:            /**
1152:             * Set the projection parameters basing its decision on the projection name.
1153:             * I found a complete list of projections on the geotiff website at address
1154:             * http://www.remotesensing.org/geotiff/proj_list.
1155:             * 
1156:             * I had no time to implement support for all of them therefore you will not
1157:             * find all of them. If you want go ahead and add support for the missing
1158:             * ones. I have tested this code against some geotiff files you can find on
1159:             * the geotiff website under the ftp sample directory but I can say that
1160:             * they are a real mess! I am respecting the specification strictly while
1161:             * many of those fiels do not! I could make this method trickier and use
1162:             * workarounds in order to be less strict but I will not do this, since I
1163:             * believe it is may lead us just on a very dangerous path.
1164:             * 
1165:             * 
1166:             * @param name
1167:             * @param metadata
1168:             *            to use fo building this {@link ParameterValueGroup}.
1169:             * @param coordTrans
1170:             *            a {@link ParameterValueGroup} that can be used to trigger this
1171:             *            projection.
1172:             * 
1173:             * @return
1174:             * @throws GeoTiffException
1175:             */
1176:            private ParameterValueGroup setParametersForProjection(String name,
1177:                    final String coordTransCode,
1178:                    final GeoTiffIIOMetadataDecoder metadata)
1179:                    throws GeoTiffException {
1180:                ParameterValueGroup parameters = null;
1181:                try {
1182:                    int code = 0;
1183:                    if (coordTransCode != null)
1184:                        code = Integer.parseInt(coordTransCode);
1185:                    if (name == null)
1186:                        name = "unnamed";
1187:                    /**
1188:                     * 
1189:                     * Transverse Mercator
1190:                     * 
1191:                     */
1192:                    if (name.equalsIgnoreCase("transverse_mercator")
1193:                            || code == GeoTiffCoordinateTransformationsCodes.CT_TransverseMercator) {
1194:                        parameters = mtFactory
1195:                                .getDefaultParameters("transverse_mercator");
1196:                        parameters.parameter("central_meridian").setValue(
1197:                                getOriginLong(metadata));
1198:                        parameters.parameter("latitude_of_origin").setValue(
1199:                                getOriginLat(metadata));
1200:                        parameters
1201:                                .parameter("scale_factor")
1202:                                .setValue(
1203:                                        this 
1204:                                                .getGeoKeyAsDouble(
1205:                                                        GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey,
1206:                                                        metadata));
1207:                        parameters.parameter("false_easting").setValue(
1208:                                getFalseEasting(metadata));
1209:                        parameters.parameter("false_northing").setValue(
1210:                                getFalseNorthing(metadata));
1211:
1212:                        return parameters;
1213:                    }
1214:
1215:                    /**
1216:                     * 
1217:                     * Equidistant Cylindrical - Plate Caree - Equirectangular
1218:                     * 
1219:                     */
1220:                    if (name.equalsIgnoreCase("Equidistant_Cylindrical")
1221:                            || name.equalsIgnoreCase("Plate_Carree")
1222:                            || name.equalsIgnoreCase("Equidistant_Cylindrical")
1223:                            || code == GeoTiffCoordinateTransformationsCodes.CT_Equirectangular) {
1224:                        parameters = mtFactory
1225:                                .getDefaultParameters("Equidistant_Cylindrical");
1226:                        parameters.parameter("latitude_of_origin").setValue(
1227:                                getOriginLat(metadata));
1228:                        parameters.parameter("central_meridian").setValue(
1229:                                getOriginLong(metadata));
1230:
1231:                        parameters.parameter("false_easting").setValue(
1232:                                getFalseEasting(metadata));
1233:                        parameters.parameter("false_northing").setValue(
1234:                                getFalseNorthing(metadata));
1235:
1236:                        return parameters;
1237:                    }
1238:                    /**
1239:                     * 
1240:                     * Mercator_1SP
1241:                     * 
1242:                     */
1243:                    if (name.equalsIgnoreCase("mercator_1SP")
1244:                            || name.equalsIgnoreCase("Mercator_2SP")
1245:                            || code == GeoTiffCoordinateTransformationsCodes.CT_Mercator) {
1246:                        parameters = mtFactory
1247:                                .getDefaultParameters("Mercator_1SP");
1248:                        parameters.parameter("central_meridian").setValue(
1249:                                getOriginLong(metadata));
1250:                        // parameters
1251:                        // .parameter("latitude_of_origin")
1252:                        // .setValue(
1253:                        // this
1254:                        // .getGeoKeyAsDouble(GeoTiffPCSCodes.ProjNatOriginLatGeoKey));
1255:                        parameters
1256:                                .parameter("scale_factor")
1257:                                .setValue(
1258:                                        this 
1259:                                                .getGeoKeyAsDouble(
1260:                                                        GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey,
1261:                                                        metadata));
1262:                        parameters.parameter("false_easting").setValue(
1263:                                getFalseEasting(metadata));
1264:                        parameters.parameter("false_northing").setValue(
1265:                                getFalseNorthing(metadata));
1266:
1267:                        return parameters;
1268:                    }
1269:
1270:                    /**
1271:                     * 
1272:                     * Mercator_2Sp
1273:                     * 
1274:                     */
1275:                    if (name.equalsIgnoreCase("lambert_conformal_conic_1SP")
1276:                            || code == GeoTiffCoordinateTransformationsCodes.CT_LambertConfConic_Helmert) {
1277:                        parameters = mtFactory
1278:                                .getDefaultParameters("lambert_conformal_conic_1SP");
1279:                        parameters.parameter("central_meridian").setValue(
1280:                                getOriginLong(metadata));
1281:                        parameters.parameter("latitude_of_origin").setValue(
1282:                                getOriginLat(metadata));
1283:                        parameters
1284:                                .parameter("scale_factor")
1285:                                .setValue(
1286:                                        this 
1287:                                                .getGeoKeyAsDouble(
1288:                                                        GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey,
1289:                                                        metadata));
1290:                        parameters.parameter("false_easting").setValue(
1291:                                getFalseEasting(metadata));
1292:                        parameters.parameter("false_northing").setValue(
1293:                                getFalseNorthing(metadata));
1294:
1295:                        return parameters;
1296:                    }
1297:
1298:                    /**
1299:                     * 
1300:                     * LAMBERT_CONFORMAT_CONIC_2SP
1301:                     * 
1302:                     */
1303:                    if (name.equalsIgnoreCase("lambert_conformal_conic_2SP")
1304:                            || name
1305:                                    .equalsIgnoreCase("lambert_conformal_conic_2SP_Belgium")
1306:                            || code == GeoTiffCoordinateTransformationsCodes.CT_LambertConfConic_2SP) {
1307:                        parameters = mtFactory
1308:                                .getDefaultParameters("lambert_conformal_conic_2SP");
1309:                        parameters.parameter("central_meridian").setValue(
1310:                                getOriginLong(metadata));
1311:                        parameters.parameter("latitude_of_origin").setValue(
1312:                                getOriginLat(metadata));
1313:                        parameters.parameter("standard_parallel_1").setValue(
1314:                                this .getGeoKeyAsDouble(
1315:                                        GeoTiffPCSCodes.ProjStdParallel1GeoKey,
1316:                                        metadata));
1317:                        parameters.parameter("standard_parallel_2").setValue(
1318:                                this .getGeoKeyAsDouble(
1319:                                        GeoTiffPCSCodes.ProjStdParallel2GeoKey,
1320:                                        metadata));
1321:                        parameters.parameter("false_easting").setValue(
1322:                                getFalseEasting(metadata));
1323:                        parameters.parameter("false_northing").setValue(
1324:                                getFalseNorthing(metadata));
1325:
1326:                        return parameters;
1327:                    }
1328:
1329:                    /**
1330:                     * 
1331:                     * Krovak
1332:                     * 
1333:                     */
1334:                    if (name.equalsIgnoreCase("Krovak")) {
1335:                        parameters = mtFactory.getDefaultParameters("Krovak");
1336:                        parameters.parameter("longitude_of_center").setValue(
1337:                                getOriginLong(metadata));
1338:                        parameters.parameter("latitude_of_center").setValue(
1339:                                getOriginLat(metadata));
1340:                        parameters.parameter("azimuth").setValue(
1341:                                this .getGeoKeyAsDouble(
1342:                                        GeoTiffPCSCodes.ProjStdParallel1GeoKey,
1343:                                        metadata));
1344:                        parameters
1345:                                .parameter("pseudo_standard_parallel_1")
1346:                                .setValue(
1347:                                        this 
1348:                                                .getGeoKeyAsDouble(
1349:                                                        GeoTiffPCSCodes.ProjStdParallel2GeoKey,
1350:                                                        metadata));
1351:                        parameters.parameter("scale_factor").setValue(
1352:                                getFalseEasting(metadata));
1353:
1354:                        return parameters;
1355:                    }
1356:
1357:                    // if (name.equalsIgnoreCase("equidistant_conic")
1358:                    // || code == GeoTiffMetadata2CRSAdapter.CT_EquidistantConic) {
1359:                    // parameters = mtFactory
1360:                    // .getDefaultParameters("equidistant_conic");
1361:                    // parameters.parameter("central_meridian").setValue(
1362:                    // getOriginLong());
1363:                    // parameters.parameter("latitude_of_origin").setValue(
1364:                    // getOriginLat());
1365:                    // parameters
1366:                    // .parameter("standard_parallel_1")
1367:                    // .setValue(
1368:                    // this
1369:                    // .getGeoKeyAsDouble(GeoTiffIIOMetadataDecoder.ProjStdParallel1GeoKey));
1370:                    // parameters
1371:                    // .parameter("standard_parallel_2")
1372:                    // .setValue(
1373:                    // this
1374:                    // .getGeoKeyAsDouble(GeoTiffIIOMetadataDecoder.ProjStdParallel2GeoKey));
1375:                    // parameters.parameter("false_easting").setValue(
1376:                    // getFalseEasting());
1377:                    // parameters.parameter("false_northing").setValue(
1378:                    // getFalseNorthing());
1379:                    //
1380:                    // return parameters;
1381:                    // }
1382:
1383:                    /**
1384:                     * 
1385:                     * STEREOGRAPHIC
1386:                     * 
1387:                     */
1388:                    if (name.equalsIgnoreCase("stereographic")
1389:                            || code == GeoTiffCoordinateTransformationsCodes.CT_Stereographic) {
1390:                        parameters = mtFactory
1391:                                .getDefaultParameters("stereographic");
1392:                        parameters.parameter("central_meridian").setValue(
1393:                                this .getOriginLong(metadata));
1394:
1395:                        parameters.parameter("latitude_of_origin").setValue(
1396:
1397:                        this .getOriginLat(metadata));
1398:                        parameters
1399:                                .parameter("scale_factor")
1400:                                .setValue(
1401:                                        this 
1402:                                                .getGeoKeyAsDouble(
1403:                                                        GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey,
1404:                                                        metadata));
1405:                        parameters.parameter("false_easting").setValue(
1406:                                getFalseEasting(metadata));
1407:                        parameters.parameter("false_northing").setValue(
1408:                                getFalseNorthing(metadata));
1409:
1410:                        return parameters;
1411:                    }
1412:
1413:                    /**
1414:                     * 
1415:                     * POLAR_STEREOGRAPHIC.
1416:                     * 
1417:                     */
1418:                    if (name.equalsIgnoreCase("polar_stereographic")
1419:                            || code == GeoTiffCoordinateTransformationsCodes.CT_PolarStereographic) {
1420:                        parameters = mtFactory
1421:                                .getDefaultParameters("polar_stereographic");
1422:
1423:                        parameters.parameter("latitude_of_origin").setValue(
1424:                                this .getOriginLat(metadata));
1425:                        parameters
1426:                                .parameter("scale_factor")
1427:                                .setValue(
1428:                                        this 
1429:                                                .getGeoKeyAsDouble(
1430:                                                        GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey,
1431:                                                        metadata));
1432:                        parameters.parameter("false_easting").setValue(
1433:                                getFalseEasting(metadata));
1434:                        parameters.parameter("false_northing").setValue(
1435:                                getFalseNorthing(metadata));
1436:                        parameters.parameter("central_meridian").setValue(
1437:                                getOriginLong(metadata));
1438:
1439:                        return parameters;
1440:                    }
1441:
1442:                    /**
1443:                     * 
1444:                     * OBLIQUE_MERCATOR.
1445:                     * 
1446:                     */
1447:                    if (name.equalsIgnoreCase("oblique_mercator")
1448:                            || name.equalsIgnoreCase("hotine_oblique_mercator")
1449:                            || code == GeoTiffCoordinateTransformationsCodes.CT_ObliqueMercator) {
1450:                        parameters = mtFactory
1451:                                .getDefaultParameters("oblique_mercator");
1452:
1453:                        parameters.parameter("scale_factor").setValue(
1454:                                getScaleFactor(metadata));
1455:                        parameters.parameter("azimuth").setValue(
1456:                                this .getGeoKeyAsDouble(
1457:                                        GeoTiffPCSCodes.ProjAzimuthAngleGeoKey,
1458:                                        metadata));
1459:                        parameters.parameter("false_easting").setValue(
1460:                                getFalseEasting(metadata));
1461:                        parameters.parameter("false_northing").setValue(
1462:                                getFalseNorthing(metadata));
1463:                        parameters.parameter("longitude_of_center").setValue(
1464:                                getOriginLong(metadata));
1465:                        parameters.parameter("latitude_of_center").setValue(
1466:                                getOriginLat(metadata));
1467:                        return parameters;
1468:                    }
1469:
1470:                    /**
1471:                     * 
1472:                     * albers_Conic_Equal_Area
1473:                     * 
1474:                     */
1475:                    if (name.equalsIgnoreCase("albers_Conic_Equal_Area")
1476:                            || code == GeoTiffCoordinateTransformationsCodes.CT_AlbersEqualArea) {
1477:                        parameters = mtFactory
1478:                                .getDefaultParameters("Albers_Conic_Equal_Area");
1479:                        parameters.parameter("standard_parallel_1").setValue(
1480:                                this .getGeoKeyAsDouble(
1481:                                        GeoTiffPCSCodes.ProjStdParallel1GeoKey,
1482:                                        metadata));
1483:                        parameters.parameter("standard_parallel_2").setValue(
1484:                                this .getGeoKeyAsDouble(
1485:                                        GeoTiffPCSCodes.ProjStdParallel2GeoKey,
1486:                                        metadata));
1487:                        parameters.parameter("latitude_of_center").setValue(
1488:                                getOriginLat(metadata));
1489:                        parameters.parameter("longitude_of_center").setValue(
1490:                                getOriginLong(metadata));
1491:                        parameters.parameter("false_easting").setValue(
1492:                                getFalseEasting(metadata));
1493:                        parameters.parameter("false_northing").setValue(
1494:                                getFalseNorthing(metadata));
1495:
1496:                        return parameters;
1497:                    }
1498:
1499:                    /**
1500:                     * 
1501:                     * Orthographic
1502:                     * 
1503:                     */
1504:                    if (name.equalsIgnoreCase("Orthographic")
1505:                            || code == GeoTiffCoordinateTransformationsCodes.CT_Orthographic) {
1506:                        parameters = mtFactory
1507:                                .getDefaultParameters("orthographic");
1508:
1509:                        parameters.parameter("latitude_of_origin").setValue(
1510:                                getOriginLat(metadata));
1511:                        parameters.parameter("longitude_of_origin").setValue(
1512:                                getOriginLong(metadata));
1513:                        parameters.parameter("false_easting").setValue(
1514:                                getFalseEasting(metadata));
1515:                        parameters.parameter("false_northing").setValue(
1516:                                getFalseNorthing(metadata));
1517:
1518:                        return parameters;
1519:                    }
1520:
1521:                    /**
1522:                     * 
1523:                     * New Zealand Map Grid
1524:                     * 
1525:                     */
1526:                    if (name.equalsIgnoreCase("New_Zealand_Map_Grid")
1527:                            || code == GeoTiffCoordinateTransformationsCodes.CT_NewZealandMapGrid) {
1528:                        parameters = mtFactory
1529:                                .getDefaultParameters("New_Zealand_Map_Grid");
1530:
1531:                        parameters.parameter("semi_major").setValue(
1532:                                getOriginLat(metadata));
1533:                        parameters.parameter("semi_minor").setValue(
1534:                                getOriginLong(metadata));
1535:                        parameters.parameter("latitude_of_origin").setValue(
1536:                                this .getOriginLat(metadata));
1537:                        parameters.parameter("central_meridian").setValue(
1538:                                getOriginLong(metadata));
1539:                        parameters.parameter("false_easting").setValue(
1540:                                getFalseEasting(metadata));
1541:                        parameters.parameter("false_northing").setValue(
1542:                                getFalseNorthing(metadata));
1543:
1544:                        return parameters;
1545:                    }
1546:
1547:                } catch (NoSuchIdentifierException e) {
1548:                    throw new GeoTiffException(metadata, e
1549:                            .getLocalizedMessage(), e);
1550:                }
1551:
1552:                return parameters;
1553:            }
1554:
1555:            /**
1556:             * Retrieve the scale factor parameter as defined by the geotiff
1557:             * specification.
1558:             * 
1559:             * @param metadata
1560:             *            to use for searching the scale factor.
1561:             * @return the scale factor
1562:             */
1563:            private double getScaleFactor(
1564:                    final GeoTiffIIOMetadataDecoder metadata) {
1565:                String scale = metadata
1566:                        .getGeoKey(GeoTiffPCSCodes.ProjScaleAtCenterGeoKey);
1567:                if (scale == null)
1568:                    scale = metadata
1569:                            .getGeoKey(GeoTiffPCSCodes.ProjScaleAtNatOriginGeoKey);
1570:                if (scale == null)
1571:                    return 0.0;
1572:                return Double.parseDouble(scale);
1573:            }
1574:
1575:            /**
1576:             * Getting the false easting with a minimum of tolerance with respect to the
1577:             * parameters name. I saw that ofetn people use the wrong geokey to store
1578:             * the false eassting, we cannot be too picky we need to get going pretty
1579:             * smoouthly.
1580:             * 
1581:             * @param metadata
1582:             *            to use for searching the false easting.
1583:             * @return double False easting.
1584:             */
1585:            private double getFalseEasting(
1586:                    final GeoTiffIIOMetadataDecoder metadata) {
1587:                String easting = metadata
1588:                        .getGeoKey(GeoTiffPCSCodes.ProjFalseEastingGeoKey);
1589:                if (easting == null)
1590:                    easting = metadata
1591:                            .getGeoKey(GeoTiffPCSCodes.ProjFalseOriginEastingGeoKey);
1592:                if (easting == null)
1593:                    return 0.0;
1594:                return Double.parseDouble(easting);
1595:
1596:            }
1597:
1598:            /**
1599:             * Getting the false northing with a minimum of tolerance with respect to
1600:             * the parameters name. I saw that ofetn people use the wrong geokey to
1601:             * store the false eassting, we cannot be too picky we need to get going
1602:             * pretty smoouthly.
1603:             * 
1604:             * @param metadata
1605:             *            to use for searching the false northing.
1606:             * @return double False northing.
1607:             */
1608:            private double getFalseNorthing(
1609:                    final GeoTiffIIOMetadataDecoder metadata) {
1610:                String northing = metadata
1611:                        .getGeoKey(GeoTiffPCSCodes.ProjFalseNorthingGeoKey);
1612:                if (northing == null)
1613:                    northing = metadata
1614:                            .getGeoKey(GeoTiffPCSCodes.ProjFalseOriginNorthingGeoKey);
1615:                if (northing == null)
1616:                    return 0.0;
1617:                return Double.parseDouble(northing);
1618:
1619:            }
1620:
1621:            /**
1622:             * Getting the origin long with a minimum of tolerance with respect to the
1623:             * parameters name. I saw that ofetn people use the wrong geokey to store
1624:             * the false eassting, we cannot be too picky we need to get going pretty
1625:             * smoouthly.
1626:             * 
1627:             * @param metadata
1628:             *            to use for searching the originating longitude.
1629:             * @return double origin longitude.
1630:             */
1631:            private double getOriginLong(
1632:                    final GeoTiffIIOMetadataDecoder metadata) {
1633:                String origin = metadata
1634:                        .getGeoKey(GeoTiffPCSCodes.ProjCenterLongGeoKey);
1635:                if (origin == null)
1636:                    origin = metadata
1637:                            .getGeoKey(GeoTiffPCSCodes.ProjNatOriginLongGeoKey);
1638:                if (origin == null)
1639:                    origin = metadata
1640:                            .getGeoKey(GeoTiffPCSCodes.ProjFalseOriginLongGeoKey);
1641:                if (origin == null)
1642:                    origin = metadata
1643:                            .getGeoKey(GeoTiffPCSCodes.ProjFalseNorthingGeoKey);
1644:                if (origin == null)
1645:                    return 0.0;
1646:                return Double.parseDouble(origin);
1647:            }
1648:
1649:            /**
1650:             * Getting the origin lat with a minimum of tolerance with respect to the
1651:             * parameters name. I saw that ofetn people use the wrong geokey to store
1652:             * the false eassting, we cannot be too picky we need to get going pretty
1653:             * smoouthly.
1654:             * 
1655:             * @param metadata
1656:             *            to use for searching the origin latitude.
1657:             * @return double origin latitude.
1658:             */
1659:            private double getOriginLat(final GeoTiffIIOMetadataDecoder metadata) {
1660:                String origin = metadata
1661:                        .getGeoKey(GeoTiffPCSCodes.ProjCenterLatGeoKey);
1662:                if (origin == null)
1663:                    origin = metadata
1664:                            .getGeoKey(GeoTiffPCSCodes.ProjNatOriginLatGeoKey);
1665:                if (origin == null)
1666:                    origin = metadata
1667:                            .getGeoKey(GeoTiffPCSCodes.ProjFalseOriginLatGeoKey);
1668:                if (origin == null)
1669:                    return 0.0;
1670:
1671:                return Double.parseDouble(origin);
1672:            }
1673:
1674:            /**
1675:             * This code creates an <code>javax.Units.Unit</code> object out of the
1676:             * <code>ProjLinearUnitsGeoKey</code> and the
1677:             * <code>ProjLinearUnitSizeGeoKey</code>. The unit may either be
1678:             * specified as a standard EPSG recognized unit, or may be user defined.
1679:             * 
1680:             * @param key
1681:             * 
1682:             * @param userDefinedKey
1683:             * 
1684:             * @param base
1685:             * 
1686:             * @param def
1687:             * 
1688:             * 
1689:             * @return <code>Unit</code> object representative of the tags in the
1690:             *         file.
1691:             * 
1692:             * @throws IOException
1693:             *             if the<code>ProjLinearUnitsGeoKey</code> is not specified
1694:             *             or if unit is user defined and
1695:             *             <code>ProjLinearUnitSizeGeoKey</code> is either not defined
1696:             *             or does not contain a number.
1697:             */
1698:            private Unit createUnit(int key, int userDefinedKey, Unit base,
1699:                    Unit def, final GeoTiffIIOMetadataDecoder metadata)
1700:                    throws IOException {
1701:                final String unitCode = metadata.getGeoKey(key);
1702:
1703:                // //
1704:                //
1705:                // if not defined, return the default unit of measure
1706:                //
1707:                // //
1708:                if (unitCode == null) {
1709:                    return def;
1710:                }
1711:                // //
1712:                //
1713:                // if specified, retrieve the appropriate unit code. There are two case
1714:                // to keep into account, first case is when the unit of measure has an
1715:                // EPSG code, alternatively it can be instantiated as a conversion from
1716:                // meter.
1717:                //
1718:                // //
1719:                if (unitCode
1720:                        .equals(GeoTiffConstants.GTUserDefinedGeoKey_String)) {
1721:                    try {
1722:                        final String unitSize = metadata
1723:                                .getGeoKey(userDefinedKey);
1724:
1725:                        // throw descriptive exception if required key is not there.
1726:                        if (unitSize == null) {
1727:                            throw new GeoTiffException(
1728:                                    metadata,
1729:                                    new StringBuffer(
1730:                                            "GeoTiffMetadata2CRSAdapter::createUnit:Must define unit length when using a user ")
1731:                                            .append("defined unit").toString(),
1732:                                    null);
1733:                        }
1734:
1735:                        double sz = Double.parseDouble(unitSize);
1736:                        return base.multiply(sz);
1737:                    } catch (NumberFormatException nfe) {
1738:                        final IOException ioe = new GeoTiffException(metadata,
1739:                                nfe.getLocalizedMessage(), nfe);
1740:                        throw ioe;
1741:                    }
1742:                } else {
1743:                    try {
1744:                        // using epsg code for this unit
1745:                        return (Unit) this .allAuthoritiesFactory
1746:                                .createUnit(new StringBuffer("EPSG:").append(
1747:                                        unitCode).toString());
1748:                    } catch (FactoryException fe) {
1749:                        final IOException io = new GeoTiffException(metadata,
1750:                                fe.getLocalizedMessage(), fe);
1751:                        throw io;
1752:                    }
1753:                }
1754:
1755:            }
1756:
1757:            /**
1758:             * Returns an object from the pool for the specified code. If the object was
1759:             * retained as a {@linkplain Reference weak reference}, the
1760:             * {@link Reference#get referent} is returned.
1761:             * 
1762:             * @param key
1763:             *            to use for doing the lookup inside the pool.
1764:             * @return a cached instance of a {@link GeoTiffMetadata2CRSAdapter}
1765:             *         suitable for the provided key.
1766:             * @todo Consider logging a message here to the finer or finest level.
1767:             */
1768:            public static Object get(final Object key) {
1769:                synchronized (pool) {
1770:
1771:                    Object object = pool.get(key);
1772:                    if (object == null) {
1773:                        object = new GeoTiffMetadata2CRSAdapter((Hints) key);
1774:                        put(key, object);
1775:                    }
1776:                    return object;
1777:                }
1778:            }
1779:
1780:            /**
1781:             * Put an element in the pool. This method is invoked everytime a
1782:             * {@code createFoo(...)} method is invoked, even if an object was already
1783:             * in the pool for the given code, for the following reasons: 1) Replaces
1784:             * weak reference by strong reference (if applicable) and 2) Alters the
1785:             * linked hash set order, so that this object is declared as the last one
1786:             * used.
1787:             */
1788:            private static void put(final Object key, final Object object) {
1789:                synchronized (pool) {
1790:                    pool.put(key, object);
1791:
1792:                }
1793:            }
1794:
1795:            /**
1796:             * Returns the {@link Hints} for this {@link GeoTiffMetadata2CRSAdapter}.
1797:             * 
1798:             * @return {@link Hints} for this {@link GeoTiffMetadata2CRSAdapter}.
1799:             */
1800:            public Hints getHints() {
1801:                return hints;
1802:            }
1803:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.