Coverage Report - org.geotoolkit.referencing.CRS
 
Classes in this File Line Coverage Branch Coverage Complexity
CRS
73 %
226/306
62 %
138/221
4,765
CRS$1
100 %
11/11
N/A
4,765
 
 1  
 /*
 2  
  *    Geotoolkit.org - An Open Source Java GIS Toolkit
 3  
  *    http://www.geotoolkit.org
 4  
  *
 5  
  *    (C) 2005-2012, Open Source Geospatial Foundation (OSGeo)
 6  
  *    (C) 2009-2012, Geomatys
 7  
  *
 8  
  *    This library is free software; you can redistribute it and/or
 9  
  *    modify it under the terms of the GNU Lesser General Public
 10  
  *    License as published by the Free Software Foundation;
 11  
  *    version 2.1 of the License.
 12  
  *
 13  
  *    This library is distributed in the hope that it will be useful,
 14  
  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 15  
  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16  
  *    Lesser General Public License for more details.
 17  
  */
 18  
 package org.geotoolkit.referencing;
 19  
 
 20  
 import java.util.Set;
 21  
 import java.util.Map;
 22  
 import java.util.List;
 23  
 import java.util.HashSet;
 24  
 import java.util.Iterator;
 25  
 import java.util.StringTokenizer;
 26  
 import java.util.NoSuchElementException;
 27  
 import java.awt.geom.Point2D;
 28  
 import java.awt.geom.Rectangle2D;
 29  
 import java.awt.geom.AffineTransform;
 30  
 import java.awt.RenderingHints;
 31  
 import javax.swing.event.ChangeEvent;
 32  
 import javax.swing.event.ChangeListener;
 33  
 
 34  
 import org.opengis.geometry.*;
 35  
 import org.opengis.referencing.*;
 36  
 import org.opengis.referencing.cs.*;
 37  
 import org.opengis.referencing.crs.*;
 38  
 import org.opengis.referencing.datum.*;
 39  
 import org.opengis.referencing.operation.*;
 40  
 import org.opengis.metadata.extent.*;
 41  
 import org.opengis.util.FactoryException;
 42  
 
 43  
 import org.geotoolkit.lang.Static;
 44  
 import org.geotoolkit.util.Version;
 45  
 import org.geotoolkit.util.Utilities;
 46  
 import org.geotoolkit.util.ComparisonMode;
 47  
 import org.geotoolkit.util.logging.Logging;
 48  
 import org.geotoolkit.factory.Hints;
 49  
 import org.geotoolkit.factory.Factory;
 50  
 import org.geotoolkit.factory.Factories;
 51  
 import org.geotoolkit.factory.FactoryFinder;
 52  
 import org.geotoolkit.factory.AuthorityFactoryFinder;
 53  
 import org.geotoolkit.factory.FactoryNotFoundException;
 54  
 import org.geotoolkit.factory.FactoryRegistryException;
 55  
 import org.geotoolkit.geometry.Envelopes;
 56  
 import org.geotoolkit.geometry.GeneralEnvelope;
 57  
 import org.geotoolkit.metadata.iso.extent.DefaultGeographicBoundingBox;
 58  
 import org.geotoolkit.referencing.crs.DefaultCompoundCRS;
 59  
 import org.geotoolkit.referencing.crs.DefaultVerticalCRS;
 60  
 import org.geotoolkit.referencing.crs.DefaultGeographicCRS;
 61  
 import org.geotoolkit.referencing.cs.DefaultEllipsoidalCS;
 62  
 import org.geotoolkit.referencing.cs.DefaultCoordinateSystemAxis;
 63  
 import org.geotoolkit.referencing.operation.MathTransforms;
 64  
 import org.geotoolkit.internal.referencing.CRSUtilities;
 65  
 import org.geotoolkit.resources.Errors;
 66  
 
 67  
 import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;
 68  
 
 69  
 
 70  
 /**
 71  
  * Utility class for making use of the {@linkplain CoordinateReferenceSystem Coordinate Reference
 72  
  * System} and associated {@linkplain org.opengis.util.Factory} implementations. This utility class
 73  
  * is made up of static functions working with arbitrary implementations of GeoAPI interfaces.
 74  
  * <p>
 75  
  * The methods defined in this class can be grouped in three categories:
 76  
  * <p>
 77  
  * <ul>
 78  
  *   <li>Methods working with factories, like {@link #decode(String)}.</li>
 79  
  *   <li>Methods providing informations, like {@link #isHorizontalCRS(CoordinateReferenceSystem)}.</li>
 80  
  *   <li>Methods performing coordinate transformations, like {@link #transform(CoordinateOperation,Envelope)}
 81  
  *       Note that many of those methods are also defined in the {@link Envelopes} class.</li>
 82  
  * </ul>
 83  
  *
 84  
  * @author Martin Desruisseaux (IRD, Geomatys)
 85  
  * @author Jody Garnett (Refractions)
 86  
  * @author Andrea Aime (TOPP)
 87  
  * @version 3.19
 88  
  *
 89  
  * @see IdentifiedObjects
 90  
  * @see Envelopes
 91  
  *
 92  
  * @since 2.1
 93  
  * @module
 94  
  */
 95  204
 public final class CRS extends Static {
 96  
     /**
 97  
      * The CRS factory to use for parsing WKT. Will be fetched when first needed
 98  
      * are stored for avoiding indirect synchronization lock in {@link #parseWKT}.
 99  
      */
 100  
     private static volatile CRSFactory crsFactory;
 101  
 
 102  
     /**
 103  
      * A factory for CRS creation as specified by the authority, which may have
 104  
      * (<var>latitude</var>, <var>longitude</var>) axis order. Will be created
 105  
      * only when first needed.
 106  
      */
 107  
     private static volatile CRSAuthorityFactory standardFactory;
 108  
 
 109  
     /**
 110  
      * A factory for CRS creation with (<var>longitude</var>, <var>latitude</var>) axis order.
 111  
      * Will be created only when first needed.
 112  
      */
 113  
     private static volatile CRSAuthorityFactory xyFactory;
 114  
 
 115  
     /**
 116  
      * A factory for default (non-lenient) operations.
 117  
      */
 118  
     private static volatile CoordinateOperationFactory strictFactory;
 119  
 
 120  
     /**
 121  
      * A factory for default lenient operations.
 122  
      */
 123  
     private static volatile CoordinateOperationFactory lenientFactory;
 124  
 
 125  
     /**
 126  
      * The default value for {@link Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER},
 127  
      * or {@code null} if not yet determined.
 128  
      */
 129  
     private static volatile Boolean defaultOrder;
 130  
 
 131  
     /**
 132  
      * The default value for {@link Hints#LENIENT_DATUM_SHIFT},
 133  
      * or {@code null} if not yet determined.
 134  
      */
 135  
     private static volatile Boolean defaultLenient;
 136  
 
 137  
     /**
 138  
      * Registers a listener automatically invoked when the system-wide configuration changed.
 139  
      */
 140  
     static {
 141  1
         Factories.addChangeListener(new ChangeListener() {
 142  
             @Override public void stateChanged(ChangeEvent e) {
 143  29
                 synchronized (CRS.class) {
 144  29
                     crsFactory      = null;
 145  29
                     standardFactory = null;
 146  29
                     xyFactory       = null;
 147  29
                     strictFactory   = null;
 148  29
                     lenientFactory  = null;
 149  29
                     defaultOrder    = null;
 150  29
                     defaultLenient  = null;
 151  29
                 }
 152  29
             }
 153  
         });
 154  1
     }
 155  
 
 156  
     /**
 157  
      * Do not allow instantiation of this class.
 158  
      */
 159  0
     private CRS() {
 160  0
     }
 161  
 
 162  
 
 163  
     //////////////////////////////////////////////////////////////
 164  
     ////                                                      ////
 165  
     ////        FACTORIES, CRS CREATION AND INSPECTION        ////
 166  
     ////                                                      ////
 167  
     //////////////////////////////////////////////////////////////
 168  
 
 169  
     /**
 170  
      * Returns the CRS factory. This is used mostly for WKT parsing.
 171  
      */
 172  
     private static CRSFactory getCRSFactory() {
 173  32
         CRSFactory factory = crsFactory;
 174  32
         if (factory == null) {
 175  5
             synchronized (CRS.class) {
 176  
                 // Double-checked locking - was a deprecated practice before Java 5.
 177  
                 // Is okay since Java 5 provided that the variable is volatile.
 178  5
                 factory = crsFactory;
 179  5
                 if (factory == null) {
 180  5
                     crsFactory = factory = FactoryFinder.getCRSFactory(null);
 181  
                 }
 182  5
             }
 183  
         }
 184  32
         return factory;
 185  
     }
 186  
 
 187  
     /**
 188  
      * Returns the CRS authority factory used by the {@link #decode(String,boolean) decode} methods.
 189  
      * This factory {@linkplain org.geotoolkit.referencing.factory.CachingAuthorityFactory uses a cache},
 190  
      * scans over {@linkplain org.geotoolkit.referencing.factory.AllAuthoritiesFactory all factories} and
 191  
      * uses additional factories as {@linkplain org.geotoolkit.referencing.factory.FallbackAuthorityFactory
 192  
      * fallbacks} if there is more than one {@linkplain AuthorityFactoryFinder#getCRSAuthorityFactories
 193  
      * registered factory} for the same authority.
 194  
      * <p>
 195  
      * This factory can be used as a kind of <cite>system-wide</cite> factory for all authorities.
 196  
      * However for more determinist behavior, consider using a more specific factory (as returned
 197  
      * by {@link AuthorityFactoryFinder#getCRSAuthorityFactory}) when the authority is known.
 198  
      *
 199  
      * @param  longitudeFirst {@code true} if axis order should be forced to
 200  
      *         (<var>longitude</var>, <var>latitude</var>), {@code false} if no order should be
 201  
      *         forced (i.e. the standard specified by the authority is respected), or {@code null}
 202  
      *         for the {@linkplain Hints#getSystemDefault system default}.
 203  
      * @return The CRS authority factory.
 204  
      * @throws FactoryRegistryException if the factory can't be created.
 205  
      *
 206  
      * @see Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER
 207  
      *
 208  
      * @category factory
 209  
      * @since 2.3
 210  
      */
 211  
     public static CRSAuthorityFactory getAuthorityFactory(Boolean longitudeFirst)
 212  
             throws FactoryRegistryException
 213  
     {
 214  
         // No need to synchronize; this is not a big deal if 'defaultOrder' is computed twice.
 215  127
         if (longitudeFirst == null) {
 216  75
             longitudeFirst = defaultOrder;
 217  75
             if (longitudeFirst == null) {
 218  14
                 longitudeFirst = Boolean.TRUE.equals(Hints.getSystemDefault(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER));
 219  14
                 defaultOrder = longitudeFirst;
 220  
             }
 221  
         }
 222  127
         CRSAuthorityFactory factory = (longitudeFirst) ? xyFactory : standardFactory;
 223  127
         if (factory == null) synchronized (CRS.class) {
 224  
             // Double-checked locking - was a deprecated practice before Java 5.
 225  
             // Is okay since Java 5 provided that the variables are volatile.
 226  20
             factory = (longitudeFirst) ? xyFactory : standardFactory;
 227  20
             if (factory == null) try {
 228  20
                 factory = DefaultAuthorityFactory.create(longitudeFirst);
 229  20
                 if (longitudeFirst) {
 230  8
                     xyFactory = factory;
 231  
                 } else {
 232  12
                     standardFactory = factory;
 233  
                 }
 234  0
             } catch (NoSuchElementException exception) {
 235  
                 // No factory registered in FactoryFinder.
 236  0
                 throw new FactoryNotFoundException(null, exception);
 237  20
             }
 238  20
         }
 239  127
         return factory;
 240  
     }
 241  
 
 242  
     /**
 243  
      * Returns the coordinate operation factory used by
 244  
      * {@link #findMathTransform(CoordinateReferenceSystem, CoordinateReferenceSystem)
 245  
      * findMathTransform} convenience methods.
 246  
      *
 247  
      * @param lenient {@code true} if the coordinate operations should be created
 248  
      *        even when there is no information available for a datum shift.
 249  
      * @return The coordinate operation factory used for finding math transform in this class.
 250  
      *
 251  
      * @see Hints#LENIENT_DATUM_SHIFT
 252  
      *
 253  
      * @category factory
 254  
      * @since 2.4
 255  
      */
 256  
     public static CoordinateOperationFactory getCoordinateOperationFactory(final boolean lenient) {
 257  19221
         CoordinateOperationFactory factory = (lenient) ? lenientFactory : strictFactory;
 258  19221
         if (factory == null) synchronized (CRS.class) {
 259  
             // Double-checked locking - was a deprecated practice before Java 5.
 260  
             // Is okay since Java 5 provided that the variables are volatile.
 261  5
             factory = (lenient) ? lenientFactory : strictFactory;
 262  5
             if (factory == null) {
 263  5
                 final Hints hints = new Hints(); // Get the system-width default hints.
 264  5
                 if (lenient) {
 265  2
                     hints.put(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE);
 266  
                 }
 267  5
                 factory = FactoryFinder.getCoordinateOperationFactory(hints);
 268  5
                 if (lenient) {
 269  2
                     lenientFactory = factory;
 270  
                 } else {
 271  3
                     strictFactory = factory;
 272  
                 }
 273  
             }
 274  5
         }
 275  19221
         return factory;
 276  
     }
 277  
 
 278  
     /**
 279  
      * Returns the version number of the specified authority database, or {@code null} if
 280  
      * not available.
 281  
      *
 282  
      * @param  authority The authority name (typically {@code "EPSG"}).
 283  
      * @return The version number of the authority database, or {@code null} if unknown.
 284  
      * @throws FactoryRegistryException if no {@link CRSAuthorityFactory} implementation
 285  
      *         was found for the specified authority.
 286  
      *
 287  
      * @see Hints#VERSION
 288  
      *
 289  
      * @category factory
 290  
      * @since 2.4
 291  
      */
 292  
     public static Version getVersion(final String authority) throws FactoryRegistryException {
 293  4
         ensureNonNull("authority", authority);
 294  4
         Object candidate = AuthorityFactoryFinder.getCRSAuthorityFactory(authority, null);
 295  4
         final Set<Factory> guard = new HashSet<Factory>();
 296  4
         while (candidate instanceof Factory) {
 297  4
             final Factory factory = (Factory) candidate;
 298  4
             if (!guard.add(factory)) {
 299  0
                 break; // Safety against never-ending recursivity.
 300  
             }
 301  4
             final Map<RenderingHints.Key,?> hints = factory.getImplementationHints();
 302  4
             final Object version = hints.get(Hints.VERSION);
 303  4
             if (version instanceof Version) {
 304  4
                 return (Version) version;
 305  
             }
 306  0
             candidate = hints.get(Hints.CRS_AUTHORITY_FACTORY);
 307  0
         }
 308  0
         return null;
 309  
     }
 310  
 
 311  
     /**
 312  
      * Gets the list of the codes that are supported by the given authority. For example
 313  
      * {@code getSupportedCodes("EPSG")} may returns {@code "EPSG:2000"}, {@code "EPSG:2001"},
 314  
      * {@code "EPSG:2002"}, <i>etc</i>. It may also returns {@code "2000"}, {@code "2001"},
 315  
      * {@code "2002"}, <i>etc.</i> without the {@code "EPSG:"} prefix. Whatever the authority
 316  
      * name is prefixed or not is factory implementation dependent.
 317  
      * <p>
 318  
      * If there is more than one factory for the given authority, then this method merges the
 319  
      * code set of all of them. If a factory fails to provide a set of supported code, then
 320  
      * this particular factory is ignored. Please be aware of the following potential issues:
 321  
      * <p>
 322  
      * <ul>
 323  
      *   <li>If there is more than one EPSG databases (for example an Access and a PostgreSQL ones),
 324  
      *       then this method will connect to all of them even if their content are identical.</li>
 325  
      *
 326  
      *   <li>If two factories format their codes differently (e.g. {@code "4326"} and
 327  
      *       {@code "EPSG:4326"}), then the returned set will contain a lot of synonymous
 328  
      *       codes.</li>
 329  
      *
 330  
      *   <li>For any code <var>c</var> in the returned set, there is no warranty that
 331  
      *       <code>{@linkplain #decode decode}(c)</code> will use the same authority
 332  
      *       factory than the one that formatted <var>c</var>.</li>
 333  
      *
 334  
      *   <li>This method doesn't report connection problems since it doesn't throw any exception.
 335  
      *       {@link FactoryException}s are logged as warnings and otherwise ignored.</li>
 336  
      * </ul>
 337  
      * <p>
 338  
      * If a more determinist behavior is wanted, consider the code below instead.
 339  
      * The following code exploit only one factory, the "preferred" one.
 340  
      *
 341  
      * {@preformat java
 342  
      *     factory = AuthorityFactoryFinder.getCRSAuthorityFactory(authority, null);
 343  
      *     Set<String> codes = factory.getAuthorityCodes(CoordinateReferenceSystem.class);
 344  
      *     String code = ...  // Choose a code here.
 345  
      *     CoordinateReferenceSystem crs = factory.createCoordinateReferenceSystem(code);
 346  
      * }
 347  
      *
 348  
      * @param  authority The authority name (for example {@code "EPSG"}).
 349  
      * @return The set of supported codes. May be empty, but never null.
 350  
      *
 351  
      * @see AuthorityFactory#getAuthorityCodes(Class)
 352  
      * @see <a href="http://www.geotoolkit.org/modules/referencing/supported-codes.html">List of authority codes</a>
 353  
      *
 354  
      * @category factory
 355  
      */
 356  
     public static Set<String> getSupportedCodes(final String authority) {
 357  0
         ensureNonNull("authority", authority);
 358  0
         return DefaultAuthorityFactory.getSupportedCodes(authority);
 359  
     }
 360  
 
 361  
     /**
 362  
      * Returns the set of the authority identifiers supported by registered authority factories.
 363  
      * This method search only for {@linkplain CRSAuthorityFactory CRS authority factories}.
 364  
      *
 365  
      * @param  returnAliases If {@code true}, the set will contain all identifiers for each
 366  
      *         authority. If {@code false}, only the first one
 367  
      * @return The set of supported authorities. May be empty, but never null.
 368  
      *
 369  
      * @category factory
 370  
      * @since 2.3.1
 371  
      */
 372  
     public static Set<String> getSupportedAuthorities(final boolean returnAliases) {
 373  2
         return DefaultAuthorityFactory.getSupportedAuthorities(returnAliases);
 374  
     }
 375  
 
 376  
     /**
 377  
      * Returns a Coordinate Reference System for the specified code.
 378  
      * Note that the code needs to mention the authority. Examples:
 379  
      * <p>
 380  
      * <ul>
 381  
      *   <li>{@code EPSG:4326}</li>
 382  
      *   <li>{@code AUTO:42001,9001,0,30}</li>
 383  
      * </ul>
 384  
      * <p>
 385  
      * If there is more than one factory implementation for the same authority, then all additional
 386  
      * factories are {@linkplain org.geotoolkit.referencing.factory.FallbackAuthorityFactory fallbacks}
 387  
      * to be used only when the first acceptable factory failed to create the requested CRS object.
 388  
      *
 389  
      * {@section Common codes}
 390  
      * A few commonly used codes are:
 391  
      * <p>
 392  
      * <ul>
 393  
      *   <li>Geographic CRS:
 394  
      *   <ul>
 395  
      *     <li>WGS 84 (2D only): EPSG:4326</li>
 396  
      *     <li>WGS 84 with ellipsoidal height: EPSG:4979</li>
 397  
      *   </ul></li>
 398  
      *   <li>Simple projected CRS:
 399  
      *   <ul>
 400  
      *     <li>Mercator: 3395</li>
 401  
      *   </ul></li>
 402  
      *   <li>Universal Transverse Mercator (UTM) projections:
 403  
      *   <ul>
 404  
      *     <li>WGS 84 (northern hemisphere): EPSG:32600 + <var>zone</var></li>
 405  
      *     <li>WGS 84 (southern hemisphere): EPSG:32700 + <var>zone</var></li>
 406  
      *     <li>WGS 72 (northern hemisphere): EPSG:32200 + <var>zone</var></li>
 407  
      *     <li>WGS 72 (southern hemisphere): EPSG:32300 + <var>zone</var></li>
 408  
      *     <li>NAD 83 (northern hemisphere): EPSG:26900 + <var>zone</var> (zone 1 to 23 only)</li>
 409  
      *     <li>NAD 27 (northern hemisphere): EPSG:26700 + <var>zone</var> (zone 1 to 22 only)</li>
 410  
      *   </ul></li>
 411  
      * </ul>
 412  
      *
 413  
      * {@section Caching}
 414  
      * CRS objects created by previous calls to this method are
 415  
      * {@linkplain org.geotoolkit.referencing.factory.CachingAuthorityFactory cached}
 416  
      * using {@linkplain java.lang.ref.WeakReference weak references}. Subsequent calls to this
 417  
      * method with the same authority code should be fast, unless the CRS object has been garbage
 418  
      * collected.
 419  
      *
 420  
      * @param  code The Coordinate Reference System authority code.
 421  
      * @return The Coordinate Reference System for the provided code.
 422  
      * @throws NoSuchAuthorityCodeException If the code could not be understood.
 423  
      * @throws FactoryException if the CRS creation failed for an other reason.
 424  
      *
 425  
      * @see #getSupportedCodes(String)
 426  
      * @see org.geotoolkit.measure.Units#valueOfEPSG(int)
 427  
      * @see <a href="http://www.geotoolkit.org/modules/referencing/supported-codes.html">List of authority codes</a>
 428  
      *
 429  
      * @category factory
 430  
      */
 431  
     public static CoordinateReferenceSystem decode(final String code)
 432  
             throws NoSuchAuthorityCodeException, FactoryException
 433  
     {
 434  75
         ensureNonNull("code", code);
 435  75
         return getAuthorityFactory(null).createCoordinateReferenceSystem(code);
 436  
     }
 437  
 
 438  
     /**
 439  
      * Returns a Coordinate Reference System for the specified code, maybe forcing the axis order
 440  
      * to (<var>longitude</var>, <var>latitude</var>). The {@code code} argument value is parsed
 441  
      * as in <code>{@linkplain #decode(String) decode}(code)</code>. The {@code longitudeFirst}
 442  
      * argument is the value to be given to the {@link Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER
 443  
      * FORCE_LONGITUDE_FIRST_AXIS_ORDER} hint.
 444  
      * <p>
 445  
      * <b>Example:</b> by default, {@code CRS.decode("EPSG:4326")} returns a Geographic CRS with
 446  
      * (<var>latitude</var>, <var>longitude</var>) axis order, while {@code CRS.decode("EPSG:4326", true)}
 447  
      * returns the same CRS except for axis order, which is  (<var>longitude</var>, <var>latitude</var>).
 448  
      *
 449  
      * @param  code The Coordinate Reference System authority code.
 450  
      * @param  longitudeFirst {@code true} if axis order should be forced to
 451  
      *         (<var>longitude</var>, <var>latitude</var>), {@code false} if no order should
 452  
      *         be forced (i.e. the standard specified by the authority is respected).
 453  
      * @return The Coordinate Reference System for the provided code.
 454  
      * @throws NoSuchAuthorityCodeException If the code could not be understood.
 455  
      * @throws FactoryException if the CRS creation failed for an other reason.
 456  
      *
 457  
      * @see Hints#FORCE_LONGITUDE_FIRST_AXIS_ORDER
 458  
      * @see org.geotoolkit.referencing.factory.epsg.LongitudeFirstEpsgFactory
 459  
      * @see <a href="http://www.geotoolkit.org/modules/referencing/supported-codes.html">List of authority codes</a>
 460  
      *
 461  
      * @category factory
 462  
      * @since 2.3
 463  
      */
 464  
     public static CoordinateReferenceSystem decode(String code, final boolean longitudeFirst)
 465  
             throws NoSuchAuthorityCodeException, FactoryException
 466  
     {
 467  17
         ensureNonNull("code", code);
 468  17
         return getAuthorityFactory(longitudeFirst).createCoordinateReferenceSystem(code);
 469  
     }
 470  
 
 471  
     /**
 472  
      * Parses a
 473  
      * <A HREF="http://www.geoapi.org/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
 474  
      * Known Text</cite></A> (WKT) into a CRS object. This convenience method is a
 475  
      * shorthand for the following:
 476  
      *
 477  
      * {@preformat java
 478  
      *     FactoryFinder.getCRSFactory(null).createFromWKT(wkt);
 479  
      * }
 480  
      *
 481  
      * @param wkt The WKT string to parse.
 482  
      * @return The parsed coordinate reference system.
 483  
      * @throws FactoryException if the given WKT can't be parsed.
 484  
      *
 485  
      * @see Envelopes#parseWKT(String)
 486  
      * @see CoordinateReferenceSystem#toWKT()
 487  
      *
 488  
      * @category factory
 489  
      */
 490  
     public static CoordinateReferenceSystem parseWKT(final String wkt) throws FactoryException {
 491  31
         ensureNonNull("wkt", wkt);
 492  31
         return getCRSFactory().createFromWKT(wkt);
 493  
     }
 494  
 
 495  
     /**
 496  
      * Returns the domain of validity for the specified coordinate reference system,
 497  
      * or {@code null} if unknown. The returned envelope is expressed in terms of the
 498  
      * specified CRS.
 499  
      * <p>
 500  
      * This method looks in two places:
 501  
      * <p>
 502  
      * <ul>
 503  
      *   <li>First, it checks the {@linkplain CoordinateReferenceSystem#getDomainOfValidity domain
 504  
      *       of validity} associated with the given CRS. Only {@linkplain GeographicExtent
 505  
      *       geographic extents} of kind {@linkplain BoundingPolygon bounding polygon} are
 506  
      *       taken in account.</li>
 507  
      *   <li>If the above step does not found found any bounding polygon, then the
 508  
      *       {@linkplain #getGeographicBoundingBox geographic bounding boxes} are
 509  
      *       used as a fallback.</li>
 510  
      * </ul>
 511  
      * <p>
 512  
      * Note that this method is also accessible from the {@link Envelopes} class.
 513  
      *
 514  
      * @param  crs The coordinate reference system, or {@code null}.
 515  
      * @return The envelope in terms of the specified CRS, or {@code null} if none.
 516  
      *
 517  
      * @see #getGeographicBoundingBox(CoordinateReferenceSystem)
 518  
      * @see Envelopes#getDomainOfValidity(CoordinateReferenceSystem)
 519  
      * @see GeneralEnvelope#reduceToDomain(boolean)
 520  
      *
 521  
      * @category information
 522  
      * @since 2.2
 523  
      */
 524  
     public static Envelope getEnvelope(final CoordinateReferenceSystem crs) {
 525  7
         Envelope envelope = null;
 526  7
         GeneralEnvelope merged = null;
 527  7
         if (crs != null) {
 528  6
             final Extent domainOfValidity = crs.getDomainOfValidity();
 529  6
             if (domainOfValidity != null) {
 530  6
                 for (final GeographicExtent extent : domainOfValidity.getGeographicElements()) {
 531  6
                     if (Boolean.FALSE.equals(extent.getInclusion())) {
 532  0
                         continue;
 533  
                     }
 534  6
                     if (extent instanceof BoundingPolygon) {
 535  0
                         for (final Geometry geometry : ((BoundingPolygon) extent).getPolygons()) {
 536  0
                             final Envelope candidate = geometry.getEnvelope();
 537  0
                             if (candidate != null) {
 538  0
                                 final CoordinateReferenceSystem sourceCRS =
 539  
                                         candidate.getCoordinateReferenceSystem();
 540  0
                                 if (sourceCRS == null || equalsIgnoreMetadata(sourceCRS, crs)) {
 541  0
                                     if (envelope == null) {
 542  0
                                         envelope = candidate;
 543  
                                     } else {
 544  0
                                         if (merged == null) {
 545  0
                                             envelope = merged = new GeneralEnvelope(envelope);
 546  
                                         }
 547  0
                                         merged.add(envelope);
 548  
                                     }
 549  
                                 }
 550  
                             }
 551  0
                         }
 552  
                     }
 553  
                 }
 554  
             }
 555  
         }
 556  
         /*
 557  
          * If no envelope was found, uses the geographic bounding box as a fallback. We will
 558  
          * need to transform it from WGS84 to the supplied CRS. This step was not required in
 559  
          * the previous block because the later selected only envelopes in the right CRS.
 560  
          */
 561  7
         if (envelope == null) {
 562  7
             final GeographicBoundingBox bounds = getGeographicBoundingBox(crs);
 563  7
             if (bounds != null && !Boolean.FALSE.equals(bounds.getInclusion())) {
 564  6
                 envelope = merged = new GeneralEnvelope(
 565  
                         new double[] {bounds.getWestBoundLongitude(), bounds.getSouthBoundLatitude()},
 566  
                         new double[] {bounds.getEastBoundLongitude(), bounds.getNorthBoundLatitude()});
 567  
                 /*
 568  
                  * We do not assign WGS84 unconditionally to the geographic bounding box, because
 569  
                  * it is not defined to be on a particular datum; it is only approximative bounds.
 570  
                  * We try to get the GeographicCRS from the user-supplied CRS and fallback on WGS
 571  
                  * 84 only if we found none.
 572  
                  */
 573  6
                 final SingleCRS     targetCRS = getHorizontalCRS(crs);
 574  6
                 final GeographicCRS sourceCRS = CRSUtilities.getStandardGeographicCRS2D(targetCRS);
 575  6
                 merged.setCoordinateReferenceSystem(sourceCRS);
 576  
                 try {
 577  6
                     envelope = transform(envelope, targetCRS);
 578  0
                 } catch (TransformException exception) {
 579  
                     /*
 580  
                      * The envelope is probably outside the range of validity for this CRS.
 581  
                      * It should not occurs, since the envelope is supposed to describe the
 582  
                      * CRS area of validity. Logs a warning and returns null, since it is a
 583  
                      * legal return value according this method contract.
 584  
                      */
 585  0
                     envelope = null;
 586  0
                     unexpectedException("getEnvelope", exception);
 587  6
                 }
 588  
                 /*
 589  
                  * If transform(...) created a new envelope, its CRS is already targetCRS so it
 590  
                  * doesn't matter if 'merged' is not anymore the right instance. If 'transform'
 591  
                  * returned the envelope unchanged, the 'merged' reference still valid and we
 592  
                  * want to ensure that it have the user-supplied CRS.
 593  
                  */
 594  6
                 merged.setCoordinateReferenceSystem(targetCRS);
 595  
             }
 596  
         }
 597  7
         return envelope;
 598  
     }
 599  
 
 600  
     /**
 601  
      * Returns the valid geographic area for the specified coordinate reference system,
 602  
      * or {@code null} if unknown.
 603  
      *
 604  
      * This method fetches the {@linkplain CoordinateReferenceSystem#getDomainOfValidity domain
 605  
      * of validity} associated with the given CRS. Only {@linkplain GeographicExtent geographic
 606  
      * extents} of kind {@linkplain GeographicBoundingBox geographic bounding box} are taken in
 607  
      * account.
 608  
      *
 609  
      * @param  crs The coordinate reference system, or {@code null}.
 610  
      * @return The geographic area, or {@code null} if none.
 611  
      *
 612  
      * @see #getEnvelope(CoordinateReferenceSystem)
 613  
      *
 614  
      * @category information
 615  
      * @since 2.3
 616  
      */
 617  
     public static GeographicBoundingBox getGeographicBoundingBox(final CoordinateReferenceSystem crs) {
 618  8
         GeographicBoundingBox bounds = null;
 619  8
         DefaultGeographicBoundingBox merged = null;
 620  8
         if (crs != null) {
 621  7
             final Extent domainOfValidity = crs.getDomainOfValidity();
 622  7
             if (domainOfValidity != null) {
 623  7
                 for (final GeographicExtent extent : domainOfValidity.getGeographicElements()) {
 624  7
                     if (extent instanceof GeographicBoundingBox) {
 625  7
                         final GeographicBoundingBox candidate = (GeographicBoundingBox) extent;
 626  7
                         if (bounds == null) {
 627  7
                             bounds = candidate;
 628  
                         } else {
 629  0
                             if (merged == null) {
 630  0
                                 bounds = merged = new DefaultGeographicBoundingBox(bounds);
 631  
                             }
 632  0
                             merged.add(candidate);
 633  
                         }
 634  7
                     }
 635  
                 }
 636  
             }
 637  
         }
 638  8
         return bounds;
 639  
     }
 640  
 
 641  
     /**
 642  
      * Returns {@code true} if the given CRS is horizontal. This method is provided because there is
 643  
      * a direct way to determine if a CRS is vertical or temporal, but no direct way to determine if
 644  
      * it is horizontal. So this method complements the check for spatio-temporal components as below:
 645  
      * <p>
 646  
      * <ul>
 647  
      *   <li>{@code if (crs instanceof TemporalCRS)} determines if the CRS is for the temporal component.</li>
 648  
      *   <li>{@code if (crs instanceof VerticalCRS)} determines if the CRS is for the vertical component.</li>
 649  
      *   <li>{@code if (CRS.isHorizontalCRS(crs))} determines if the CRS is for the horizontal component.</li>
 650  
      * </ul>
 651  
      * <p>
 652  
      * This method considers a CRS as horizontal if it is two-dimensional and comply
 653  
      * with one of the following conditions:
 654  
      * <p>
 655  
      * <ul>
 656  
      *   <li>It is an instance of {@link GeographicCRS}.</li>
 657  
      *   <li>It is an instance of {@link ProjectedCRS} (actually this is not explicitly
 658  
      *       checked, since this condition is a special case of the condition below).</li>
 659  
      *   <li>It is an instance of {@link GeneralDerivedCRS} based on a horizontal CRS
 660  
      *       and using a {@link GeodeticDatum}.</li>
 661  
      * </ul>
 662  
      * <p>
 663  
      * The last condition ({@code GeneralDerivedCRS} based on a horizontal CRS) allows for example
 664  
      * to express the coordinates of a projected CRS (which use a Cartesian coordinate system) in
 665  
      * a {@linkplain org.opengis.referencing.cs.PolarCS polar coordinate system} and still consider
 666  
      * the result as horizontal. However this assumes that the axes of the derived CRS are coplanar
 667  
      * with the axes of the base CRS. This is not always true since a derived CRS could be created
 668  
      * for an inclined plane, for example a plane fitting the slope of a mountain. ISO 19111 does
 669  
      * not specify how to handle this case. In the Geotk implementation, we suggest to define a new
 670  
      * {@linkplain Datum datum} for inclined plane which is not a geodetic datum.
 671  
      *
 672  
      * @param  crs The coordinate reference system, or {@code null}.
 673  
      * @return {@code true} if the given CRS is non-null and comply with one of the above
 674  
      *         conditions, or {@code false} otherwise.
 675  
      *
 676  
      * @see #getHorizontalCRS(CoordinateReferenceSystem)
 677  
      *
 678  
      * @category information
 679  
      * @since 3.05
 680  
      */
 681  
     public static boolean isHorizontalCRS(CoordinateReferenceSystem crs) {
 682  19
         if (crs instanceof SingleCRS) {
 683  17
             final int dimension = crs.getCoordinateSystem().getDimension();
 684  17
             if (dimension == 2) {
 685  17
                 final Datum datum = ((SingleCRS) crs).getDatum();
 686  17
                 if (datum instanceof GeodeticDatum) {
 687  26
                     while (crs instanceof GeneralDerivedCRS) {
 688  9
                         crs = ((GeneralDerivedCRS) crs).getBaseCRS();
 689  
                     }
 690  17
                     return (crs instanceof GeographicCRS);
 691  
                 }
 692  
             }
 693  
         }
 694  2
         return false;
 695  
     }
 696  
 
 697  
     /**
 698  
      * Returns the first horizontal coordinate reference system found in the given CRS,
 699  
      * or {@code null} if there is none. A horizontal CRS is usually a two-dimensional
 700  
      * {@linkplain GeographicCRS geographic} or {@linkplain ProjectedCRS projected} CRS.
 701  
      * See the {@link #isHorizontalCRS(CoordinateReferenceSystem) isHorizontalCRS} method for
 702  
      * a more accurate description about the conditions for a CRS to be considered horizontal.
 703  
      *
 704  
      * @param  crs The coordinate reference system, or {@code null}.
 705  
      * @return The horizontal CRS, or {@code null} if none.
 706  
      *
 707  
      * @category information
 708  
      * @since 2.4
 709  
      */
 710  
     public static SingleCRS getHorizontalCRS(final CoordinateReferenceSystem crs) {
 711  16
         if (crs instanceof SingleCRS) {
 712  11
             final CoordinateSystem cs = crs.getCoordinateSystem();
 713  11
             final int dimension = cs.getDimension();
 714  11
             if (dimension == 2) {
 715  
                 /*
 716  
                  * For two-dimensional CRS, returns the CRS directly if it is either a
 717  
                  * GeographicCRS, or any kind of derived CRS having a GeographicCRS as
 718  
                  * its base and a geodetic datum.
 719  
                  */
 720  10
                 final Datum datum = ((SingleCRS) crs).getDatum();
 721  10
                 if (datum instanceof GeodeticDatum) {
 722  10
                     CoordinateReferenceSystem base = crs;
 723  14
                     while (base instanceof GeneralDerivedCRS) {
 724  4
                         base = ((GeneralDerivedCRS) base).getBaseCRS();
 725  
                     }
 726  
                     // No need to test for ProjectedCRS, since the code above unwrap it.
 727  10
                     if (base instanceof GeographicCRS) {
 728  10
                         assert isHorizontalCRS(crs) : crs;
 729  10
                         return (SingleCRS) crs; // Really returns 'crs', not 'base'.
 730  
                     }
 731  
                 }
 732  0
             } else if (dimension >= 3 && crs instanceof GeographicCRS) {
 733  
                 /*
 734  
                  * For three-dimensional Geographic CRS, extracts the axis having a direction
 735  
                  * like "North", "North-East", "East", etc. If we find exactly two of them,
 736  
                  * we can build a new GeographicCRS using them.
 737  
                  */
 738  1
                 CoordinateSystemAxis axis0 = null, axis1 = null;
 739  1
                 int count = 0;
 740  4
                 for (int i=0; i<dimension; i++) {
 741  3
                     final CoordinateSystemAxis axis = cs.getAxis(i);
 742  3
 search:             if (DefaultCoordinateSystemAxis.isCompassDirection(axis.getDirection())) {
 743  2
                         switch (count++) {
 744  1
                             case 0: axis0 = axis; break;
 745  1
                             case 1: axis1 = axis; break;
 746  
                             default: break search;
 747  
                         }
 748  
                     }
 749  
                 }
 750  1
                 if (count == 2) {
 751  1
                     final GeodeticDatum datum = ((GeographicCRS) crs).getDatum();
 752  1
                     Map<String,?> properties = CRSUtilities.changeDimensionInName(cs, "3D", "2D");
 753  
                     EllipsoidalCS horizontalCS;
 754  
                     try {
 755  1
                         horizontalCS = FactoryFinder.getCSFactory(null).
 756  
                                 createEllipsoidalCS(properties, axis0, axis1);
 757  0
                     } catch (FactoryException e) {
 758  0
                         Logging.recoverableException(CRS.class, "getHorizontalCRS", e);
 759  0
                         horizontalCS = new DefaultEllipsoidalCS(properties, axis0, axis1);
 760  1
                     }
 761  1
                     properties = CRSUtilities.changeDimensionInName(crs, "3D", "2D");
 762  
                     GeographicCRS horizontalCRS;
 763  
                     try {
 764  1
                         horizontalCRS = getCRSFactory().createGeographicCRS(properties, datum, horizontalCS);
 765  0
                     } catch (FactoryException e) {
 766  0
                         Logging.recoverableException(CRS.class, "getHorizontalCRS", e);
 767  0
                         horizontalCRS = new DefaultGeographicCRS(properties, datum, horizontalCS);
 768  1
                     }
 769  1
                     assert isHorizontalCRS(horizontalCRS) : horizontalCRS;
 770  1
                     return horizontalCRS;
 771  
                 }
 772  
             }
 773  
         }
 774  5
         if (crs instanceof CompoundCRS) {
 775  5
             final CompoundCRS cp = (CompoundCRS) crs;
 776  5
             for (final CoordinateReferenceSystem c : cp.getComponents()) {
 777  5
                 final SingleCRS candidate = getHorizontalCRS(c);
 778  5
                 if (candidate != null) {
 779  5
                     assert isHorizontalCRS(candidate) : candidate;
 780  5
                     return candidate;
 781  
                 }
 782  0
             }
 783  
         }
 784  0
         return null;
 785  
     }
 786  
 
 787  
     /**
 788  
      * Returns the first projected coordinate reference system found in a the given CRS,
 789  
      * or {@code null} if there is none.
 790  
      *
 791  
      * @param  crs The coordinate reference system, or {@code null}.
 792  
      * @return The projected CRS, or {@code null} if none.
 793  
      *
 794  
      * @category information
 795  
      * @since 2.4
 796  
      */
 797  
     public static ProjectedCRS getProjectedCRS(final CoordinateReferenceSystem crs) {
 798  0
         if (crs instanceof ProjectedCRS) {
 799  0
             return (ProjectedCRS) crs;
 800  
         }
 801  0
         if (crs instanceof CompoundCRS) {
 802  0
             final CompoundCRS cp = (CompoundCRS) crs;
 803  0
             for (final CoordinateReferenceSystem c : cp.getComponents()) {
 804  0
                 final ProjectedCRS candidate = getProjectedCRS(c);
 805  0
                 if (candidate != null) {
 806  0
                     return candidate;
 807  
                 }
 808  0
             }
 809  
         }
 810  0
         return null;
 811  
     }
 812  
 
 813  
     /**
 814  
      * Returns the first vertical coordinate reference system found in a the given CRS,
 815  
      * or {@code null} if there is none.
 816  
      *
 817  
      * @param  crs The coordinate reference system, or {@code null}.
 818  
      * @return The vertical CRS, or {@code null} if none.
 819  
      *
 820  
      * @category information
 821  
      * @since 2.4
 822  
      */
 823  
     public static VerticalCRS getVerticalCRS(final CoordinateReferenceSystem crs) {
 824  8
         if (crs instanceof VerticalCRS) {
 825  2
             return (VerticalCRS) crs;
 826  
         }
 827  6
         if (crs instanceof CompoundCRS) {
 828  3
             final CompoundCRS cp = (CompoundCRS) crs;
 829  3
             for (final CoordinateReferenceSystem c : cp.getComponents()) {
 830  5
                 final VerticalCRS candidate = getVerticalCRS(c);
 831  5
                 if (candidate != null) {
 832  3
                     return candidate;
 833  
                 }
 834  2
             }
 835  
         }
 836  3
         if (crs instanceof GeographicCRS) {
 837  0
             final CoordinateSystem cs = crs.getCoordinateSystem();
 838  0
             if (cs.getDimension()  >= 3) {
 839  
                 assert CRSUtilities.dimensionColinearWith(cs,
 840  0
                         DefaultCoordinateSystemAxis.ELLIPSOIDAL_HEIGHT) >= 0 : cs;
 841  0
                 return DefaultVerticalCRS.ELLIPSOIDAL_HEIGHT;
 842  
             }
 843  
         }
 844  3
         return null;
 845  
     }
 846  
 
 847  
     /**
 848  
      * Returns the first temporal coordinate reference system found in the given CRS,
 849  
      * or {@code null} if there is none.
 850  
      *
 851  
      * @param  crs The coordinate reference system, or {@code null}.
 852  
      * @return The temporal CRS, or {@code null} if none.
 853  
      *
 854  
      * @category information
 855  
      * @since 2.4
 856  
      */
 857  
     public static TemporalCRS getTemporalCRS(final CoordinateReferenceSystem crs) {
 858  9
         if (crs instanceof TemporalCRS) {
 859  1
             return (TemporalCRS) crs;
 860  
         }
 861  8
         if (crs instanceof CompoundCRS) {
 862  3
             final CompoundCRS cp = (CompoundCRS) crs;
 863  3
             for (final CoordinateReferenceSystem c : cp.getComponents()) {
 864  6
                 final TemporalCRS candidate = getTemporalCRS(c);
 865  6
                 if (candidate != null) {
 866  1
                     return candidate;
 867  
                 }
 868  5
             }
 869  
         }
 870  7
         return null;
 871  
     }
 872  
 
 873  
     /**
 874  
      * Returns the first compound CRS which contains only the given components, in any order.
 875  
      * First, this method gets the {@link SingleCRS} components of the given compound CRS. If
 876  
      * all those components are {@linkplain #equalsIgnoreMetadata equal, ignoring metadata}
 877  
      * and order, to the {@code SingleCRS} components given to this method, then the given
 878  
      * {@code CompoundCRS} is returned. Otherwise if the given {@code CompoundCRS} contains
 879  
      * nested {@code CompoundCRS}, then those nested CRS are inspected recursively by the same
 880  
      * algorithm. Otherwise, this method returns {@code null}.
 881  
      * <p>
 882  
      * This method is useful for extracting metadata about the 3D spatial CRS part in a 4D
 883  
      * spatio-temporal CRS. For example given the following CRS:
 884  
      *
 885  
      * {@preformat wkt
 886  
      *   COMPD_CS["Mercator + height + time",
 887  
      *     COMPD_CS["Mercator + height",
 888  
      *       PROJCS["Mercator", ...etc...]
 889  
      *       VERT_CS["Ellipsoidal height", ...etc...]]
 890  
      *     TemporalCRS["Modified Julian", ...etc...]]
 891  
      * }
 892  
      *
 893  
      * Then the following code will returns the nested {@code COMPD_CS["Mercator + height"]}
 894  
      * without prior knowledge of the CRS component order (the time CRS could be first, and
 895  
      * the vertical CRS could be before the horizontal one):
 896  
      *
 897  
      * {@preformat java
 898  
      *     CompoundCRS crs = ...;
 899  
      *     SingleCRS horizontalCRS = getHorizontalCRS(crs);
 900  
      *     VerticalCRS verticalCRS = getVerticalCRS(crs);
 901  
      *     if (horizontalCRS != null && verticalCRS != null) {
 902  
      *         CompoundCRS spatialCRS = getCompoundCRS(crs, horizontalCRS, verticalCRS);
 903  
      *         if (spatialCRS != null) {
 904  
      *             // ...
 905  
      *         }
 906  
      *     }
 907  
      * }
 908  
      *
 909  
      * @param  crs The compound CRS to compare with the given component CRS, or {@code null}.
 910  
      * @param  components The CRS which must be components of the returned CRS.
 911  
      * @return A CRS which contains the given components, or {@code null} if none.
 912  
      *
 913  
      * @see DefaultCompoundCRS#getSingleCRS()
 914  
      *
 915  
      * @since 3.16
 916  
      */
 917  
     public static CompoundCRS getCompoundCRS(final CompoundCRS crs, final SingleCRS... components) {
 918  47
         final List<SingleCRS> actualComponents = DefaultCompoundCRS.getSingleCRS(crs);
 919  47
         if (actualComponents.size() == components.length) {
 920  41
             int firstValid = 0;
 921  41
             final SingleCRS[] toSearch = components.clone();
 922  41
 compare:    for (final SingleCRS component : actualComponents) {
 923  92
                 for (int i=firstValid; i<toSearch.length; i++) {
 924  90
                     if (equalsIgnoreMetadata(component, toSearch[i])) {
 925  
                         /*
 926  
                          * Found a match: remove it from the search list. Note that we copy the
 927  
                          * remaining components to the end of the array (which is unusual) rather
 928  
                          * than to the begining (as usual), in order to reduce the length of the
 929  
                          * part to copy on the assumption that the components given to this method
 930  
                          * are most likely in the same order than the elements in the CompoundCRS.
 931  
                          */
 932  82
                         System.arraycopy(toSearch, firstValid, toSearch, firstValid+1, i - firstValid);
 933  82
                         toSearch[firstValid++] = null;
 934  82
                         continue compare;
 935  
                     }
 936  
                 }
 937  
                 // No match found. We can stop the loop now.
 938  2
                 firstValid = -1;
 939  2
                 break;
 940  
             }
 941  
             /*
 942  
              * If we found all the requested components and nothing more,
 943  
              * returns the CRS.
 944  
              */
 945  41
             if (firstValid == toSearch.length) {
 946  39
                 return crs;
 947  
             }
 948  
         }
 949  
         /*
 950  
          * Search recursively in the sub-components.
 951  
          */
 952  8
         for (final CoordinateReferenceSystem component : crs.getComponents()) {
 953  12
             if (component instanceof CompoundCRS) {
 954  5
                 final CompoundCRS candidate = getCompoundCRS((CompoundCRS) component, components);
 955  5
                 if (candidate != null) {
 956  4
                     return candidate;
 957  
                 }
 958  8
             }
 959  
         }
 960  4
         return null;
 961  
     }
 962  
 
 963  
     /**
 964  
      * Returns the coordinate reference system in the given range of dimension indices.
 965  
      * This method processes as below:
 966  
      * <p>
 967  
      * <ul>
 968  
      *   <li>If the given {@code crs} is {@code null}, then this method returns {@code null}.</li>
 969  
      *   <li>Otherwise if {@code lower} is 0 and {@code upper} if the number of CRS dimensions,
 970  
      *       then this method returns the given CRS unchanged.</li>
 971  
      *   <li>Otherwise if the given CRS is an instance of {@link CompoundCRS}, then this method
 972  
      *       searches for a {@linkplain CompoundCRS#getComponents() component} where:
 973  
      *       <ul>
 974  
      *         <li>The {@linkplain CoordinateSystem#getDimension() number of dimensions} is
 975  
      *             equals to {@code upper - lower};</li>
 976  
      *         <li>The sum of the number of dimensions of all previous CRS is equals to
 977  
      *             {@code lower}.</li>
 978  
      *       </ul>
 979  
      *       If such component is found, then it is returned.</li>
 980  
      *   <li>Otherwise (i.e. no component match), this method returns {@code null}.</li>
 981  
      * </ul>
 982  
      * <p>
 983  
      * This method does <strong>not</strong> attempt to build new CRS from the components.
 984  
      * For example it does not attempt to create a 3D geographic CRS from a 2D one + a vertical
 985  
      * component. If such functionality is desired, consider using the utility methods in
 986  
      * {@link org.geotoolkit.referencing.factory.ReferencingFactoryContainer} instead.
 987  
      *
 988  
      * @param  crs   The coordinate reference system to decompose, or {@code null}.
 989  
      * @param  lower The first dimension to keep, inclusive.
 990  
      * @param  upper The last  dimension to keep, exclusive.
 991  
      * @return The sub-coordinate system, or {@code null} if the given {@code crs} was {@code null}
 992  
      *         or can't be decomposed for dimensions in the range {@code [lower..upper]}.
 993  
      * @throws IndexOutOfBoundsException If the given index are out of bounds.
 994  
      *
 995  
      * @see org.geotoolkit.referencing.factory.ReferencingFactoryContainer#separate(CoordinateReferenceSystem, int[])
 996  
      *
 997  
      * @since 3.16
 998  
      */
 999  
     public static CoordinateReferenceSystem getSubCRS(CoordinateReferenceSystem crs, int lower, int upper) {
 1000  10
         if (crs != null) {
 1001  10
             int dimension = crs.getCoordinateSystem().getDimension();
 1002  10
             if (lower < 0 || lower > upper || upper > dimension) {
 1003  0
                 throw new IndexOutOfBoundsException(Errors.format(
 1004  
                         Errors.Keys.INDEX_OUT_OF_BOUNDS_$1, lower < 0 ? lower : upper));
 1005  
             }
 1006  20
             while (lower != 0 || upper != dimension) {
 1007  11
                 final List<? extends CoordinateReferenceSystem> c = CRSUtilities.getComponents(crs);
 1008  11
                 if (c == null) {
 1009  1
                     return null;
 1010  
                 }
 1011  10
                 for (final Iterator<? extends CoordinateReferenceSystem> it=c.iterator(); it.hasNext();) {
 1012  16
                     crs = it.next();
 1013  16
                     dimension = crs.getCoordinateSystem().getDimension();
 1014  16
                     if (lower < dimension) {
 1015  10
                         break;
 1016  
                     }
 1017  6
                     lower -= dimension;
 1018  6
                     upper -= dimension;
 1019  
                 }
 1020  10
             }
 1021  
         }
 1022  9
         return crs;
 1023  
     }
 1024  
 
 1025  
     /**
 1026  
      * Returns the datum of the specified CRS, or {@code null} if none.
 1027  
      * This method processes as below:
 1028  
      * <p>
 1029  
      * <ul>
 1030  
      *   <li>If the given CRS is an instance of {@link SingleCRS}, then this method returns
 1031  
      *       <code>crs.{@linkplain SingleCRS#getDatum() getDatum()}</code>.</li>
 1032  
      *   <li>Otherwise if the given CRS is an instance of {@link CompoundCRS}, then:
 1033  
      *       <ul>
 1034  
      *         <li>If all components have the same datum, then that datum is returned.</li>
 1035  
      *         <li>Otherwise if the CRS contains only a geodetic datum with a vertical datum
 1036  
      *             of type <em>ellipsoidal height</em> (no other type accepted), then the
 1037  
      *             geodetic datum is returned.</li>
 1038  
      *       </ul></li>
 1039  
      *   <li>Otherwise this method returns {@code null}.</li>
 1040  
      * </ul>
 1041  
      *
 1042  
      * @param  crs The coordinate reference system for which to get the datum. May be {@code null}.
 1043  
      * @return The datum in the given CRS, or {@code null} if none.
 1044  
      *
 1045  
      * @see #getEllipsoid(CoordinateReferenceSystem)
 1046  
      *
 1047  
      * @category information
 1048  
      * @since 3.16
 1049  
      */
 1050  
     public static Datum getDatum(final CoordinateReferenceSystem crs) {
 1051  3
         return CRSUtilities.getDatum(crs);
 1052  
     }
 1053  
 
 1054  
     /**
 1055  
      * Returns the first ellipsoid found in a coordinate reference system,
 1056  
      * or {@code null} if there is none. More specifically:
 1057  
      * <p>
 1058  
      * <ul>
 1059  
      *   <li>If the given CRS is an instance of {@link SingleCRS} and its datum is a
 1060  
      *       {@link GeodeticDatum}, then this method returns the datum ellipsoid.</li>
 1061  
      *   <li>Otherwise if the given CRS is an instance of {@link CompoundCRS}, then this method
 1062  
      *       invokes itself recursively for each component until a geodetic datum is found.</li>
 1063  
      *   <li>Otherwise this method returns {@code null}.</li>
 1064  
      * </ul>
 1065  
      * <p>
 1066  
      * Note that this method does not check if there is more than one ellipsoid
 1067  
      * (it should never be the case).
 1068  
      *
 1069  
      * @param  crs The coordinate reference system, or {@code null}.
 1070  
      * @return The ellipsoid, or {@code null} if none.
 1071  
      *
 1072  
      * @see #getDatum(CoordinateReferenceSystem)
 1073  
      *
 1074  
      * @category information
 1075  
      * @since 2.4
 1076  
      */
 1077  
     public static Ellipsoid getEllipsoid(final CoordinateReferenceSystem crs) {
 1078  1
         if (crs instanceof SingleCRS) {
 1079  1
             final Datum datum = ((SingleCRS) crs).getDatum();
 1080  1
             if (datum instanceof GeodeticDatum) {
 1081  1
                 return ((GeodeticDatum) datum).getEllipsoid();
 1082  
             }
 1083  
         }
 1084  0
         if (crs instanceof CompoundCRS) {
 1085  0
             for (final CoordinateReferenceSystem c : ((CompoundCRS) crs).getComponents()) {
 1086  0
                 final Ellipsoid candidate = getEllipsoid(c);
 1087  0
                 if (candidate != null) {
 1088  0
                     return candidate;
 1089  
                 }
 1090  0
             }
 1091  
         }
 1092  0
         return null;
 1093  
     }
 1094  
 
 1095  
 
 1096  
     /////////////////////////////////////////////////
 1097  
     ////                                         ////
 1098  
     ////          COORDINATE OPERATIONS          ////
 1099  
     ////                                         ////
 1100  
     /////////////////////////////////////////////////
 1101  
 
 1102  
     /**
 1103  
      * Compares the specified objects for equality, ignoring metadata. If this method returns
 1104  
      * {@code true}, then:
 1105  
      *
 1106  
      * <ul>
 1107  
      *   <li><p>If the two given objects are {@link MathTransform} instances, then transforming
 1108  
      *       a set of coordinate values using one transform will produce the same results than
 1109  
      *       transforming the same coordinates with the other transform.</p></li>
 1110  
      *
 1111  
      *   <li><p>If the two given objects are {@link CoordinateReferenceSystem} instances,
 1112  
      *       then a call to <code>{@linkplain #findMathTransform(CoordinateReferenceSystem,
 1113  
      *       CoordinateReferenceSystem) findMathTransform}(crs1, crs2)</code> will return
 1114  
      *       an identity transform.</p></li>
 1115  
      * </ul>
 1116  
      *
 1117  
      * If a more lenient comparison - allowing slight differences in numerical values - is wanted,
 1118  
      * then {@link #equalsApproximatively(Object, Object)} can be used instead.
 1119  
      *
 1120  
      * {@section Implementation note}
 1121  
      * This is a convenience method for the following method call:
 1122  
      *
 1123  
      * {@preformat java
 1124  
      *     return Utilities.deepEquals(object1, object2, ComparisonMode.IGNORE_METADATA);
 1125  
      * }
 1126  
      *
 1127  
      * @param  object1 The first object to compare (may be null).
 1128  
      * @param  object2 The second object to compare (may be null).
 1129  
      * @return {@code true} if both objects are equal, ignoring metadata.
 1130  
      *
 1131  
      * @see Utilities#deepEquals(Object, Object, ComparisonMode)
 1132  
      * @see ComparisonMode#IGNORE_METADATA
 1133  
      *
 1134  
      * @category information
 1135  
      * @since 2.2
 1136  
      */
 1137  
     public static boolean equalsIgnoreMetadata(final Object object1, final Object object2) {
 1138  40118
         return Utilities.deepEquals(object1, object2, ComparisonMode.IGNORE_METADATA);
 1139  
     }
 1140  
 
 1141  
     /**
 1142  
      * Compares the specified objects for equality, ignoring metadata and slight differences
 1143  
      * in numerical values. If this method returns {@code true}, then:
 1144  
      *
 1145  
      * <ul>
 1146  
      *   <li><p>If the two given objects are {@link MathTransform} instances, then transforming a
 1147  
      *       set of coordinate values using one transform will produce <em>approximatively</em>
 1148  
      *       the same results than transforming the same coordinates with the other transform.</p></li>
 1149  
      *
 1150  
      *   <li><p>If the two given objects are {@link CoordinateReferenceSystem} instances,
 1151  
      *       then a call to <code>{@linkplain #findMathTransform(CoordinateReferenceSystem,
 1152  
      *       CoordinateReferenceSystem) findMathTransform}(crs1, crs2)</code> will return
 1153  
      *       a transform close to the identity transform.</p></li>
 1154  
      * </ul>
 1155  
      *
 1156  
      * {@section Implementation note}
 1157  
      * This is a convenience method for the following method call:
 1158  
      *
 1159  
      * {@preformat java
 1160  
      *     return Utilities.deepEquals(object1, object2, ComparisonMode.APPROXIMATIVE);
 1161  
      * }
 1162  
      *
 1163  
      * @param  object1 The first object to compare (may be null).
 1164  
      * @param  object2 The second object to compare (may be null).
 1165  
      * @return {@code true} if both objects are approximatively equal.
 1166  
      *
 1167  
      * @see Utilities#deepEquals(Object, Object, ComparisonMode)
 1168  
      * @see ComparisonMode#APPROXIMATIVE
 1169  
      *
 1170  
      * @category information
 1171  
      * @since 3.18
 1172  
      */
 1173  
     public static boolean equalsApproximatively(final Object object1, final Object object2) {
 1174  48819
         return Utilities.deepEquals(object1, object2, ComparisonMode.APPROXIMATIVE);
 1175  
     }
 1176  
 
 1177  
     /**
 1178  
      * Grabs a transform between two Coordinate Reference Systems. This convenience method is a
 1179  
      * shorthand for the following:
 1180  
      *
 1181  
      * {@preformat java
 1182  
      *     CoordinateOperationFactory factory = FactoryFinder.getCoordinateOperationFactory(null);
 1183  
      *     CoordinateOperation operation = factory.createOperation(sourceCRS, targetCRS);
 1184  
      *     MathTransform transform = operation.getMathTransform();
 1185  
      * }
 1186  
      *
 1187  
      * Note that some metadata like {@linkplain CoordinateOperation#getCoordinateOperationAccuracy
 1188  
      * coordinate operation accuracy} are lost by this method. If those metadata are wanted, use the
 1189  
      * {@linkplain CoordinateOperationFactory coordinate operation factory} directly.
 1190  
      * <p>
 1191  
      * Sample use:
 1192  
      *
 1193  
      * {@preformat java
 1194  
      *     CoordinateReferenceSystem sourceCRS = CRS.decode("EPSG:42102");
 1195  
      *     CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:4326");
 1196  
      *     MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);
 1197  
      * }
 1198  
      *
 1199  
      * @param  sourceCRS The source CRS.
 1200  
      * @param  targetCRS The target CRS.
 1201  
      * @return The math transform from {@code sourceCRS} to {@code targetCRS}.
 1202  
      * @throws FactoryException If no math transform can be created for the specified source and
 1203  
      *         target CRS.
 1204  
      *
 1205  
      * @see CoordinateOperationFactory#createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem)
 1206  
      *
 1207  
      * @category transform
 1208  
      */
 1209  
     public static MathTransform findMathTransform(final CoordinateReferenceSystem sourceCRS,
 1210  
                                                   final CoordinateReferenceSystem targetCRS)
 1211  
             throws FactoryException
 1212  
     {
 1213  
         // No need to synchronize; this is not a big deal if 'defaultLenient' is computed twice.
 1214  9612
         Boolean lenient = defaultLenient;
 1215  9612
         if (lenient == null) {
 1216  3
             defaultLenient = lenient = Boolean.TRUE.equals(
 1217  
                     Hints.getSystemDefault(Hints.LENIENT_DATUM_SHIFT));
 1218  
         }
 1219  9612
         return findMathTransform(sourceCRS, targetCRS, lenient);
 1220  
     }
 1221  
 
 1222  
     /**
 1223  
      * Grab a transform between two Coordinate Reference Systems. This method is similar to
 1224  
      * <code>{@linkplain #findMathTransform(CoordinateReferenceSystem, CoordinateReferenceSystem)
 1225  
      * findMathTransform}(sourceCRS, targetCRS)</code>, except that it specifies whatever this
 1226  
      * method should tolerate <cite>lenient datum shift</cite>. If the {@code lenient} argument
 1227  
      * is {@code true}, then this method will not throw a "<cite>Bursa-Wolf parameters required</cite>"
 1228  
      * exception during datum shifts if the Bursa-Wolf parameters are not specified.
 1229  
      * Instead it will assume a no datum shift.
 1230  
      *
 1231  
      * @param  sourceCRS The source CRS.
 1232  
      * @param  targetCRS The target CRS.
 1233  
      * @param  lenient {@code true} if the math transform should be created even when there is
 1234  
      *         no information available for a datum shift. if this argument is not specified,
 1235  
      *         then the default value is determined from the {@linkplain Hints#getSystemDefault
 1236  
      *         system default}.
 1237  
      * @return The math transform from {@code sourceCRS} to {@code targetCRS}.
 1238  
      * @throws FactoryException If no math transform can be created for the specified source and
 1239  
      *         target CRS.
 1240  
      *
 1241  
      * @see Hints#LENIENT_DATUM_SHIFT
 1242  
      * @see CoordinateOperationFactory#createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem)
 1243  
      *
 1244  
      * @category transform
 1245  
      */
 1246  
     public static MathTransform findMathTransform(final CoordinateReferenceSystem sourceCRS,
 1247  
                                                   final CoordinateReferenceSystem targetCRS,
 1248  
                                                   boolean lenient)
 1249  
             throws FactoryException
 1250  
     {
 1251  19217
         if (equalsIgnoreMetadata(sourceCRS, targetCRS)) {
 1252  
             // Slight optimization in order to avoid the overhead of loading the full referencing engine.
 1253  2
             return MathTransforms.identity(sourceCRS.getCoordinateSystem().getDimension());
 1254  
         }
 1255  19215
         ensureNonNull("sourceCRS", sourceCRS);
 1256  19215
         ensureNonNull("targetCRS", targetCRS);
 1257  19215
         CoordinateOperationFactory operationFactory = getCoordinateOperationFactory(lenient);
 1258  19215
         return operationFactory.createOperation(sourceCRS, targetCRS).getMathTransform();
 1259  
     }
 1260  
 
 1261  
     // Note: the above 4 transform methods simply delegate their work to the Envelopes class.
 1262  
     // We keep those methods mostly for historical reasons.  Some Geotk code still reference
 1263  
     // those methods instead than Envelopes. We do that when the CRS class is used anyway so
 1264  
     // there is no advantage to reference one more class.
 1265  
 
 1266  
     /**
 1267  
      * Transforms the given envelope to the specified CRS. If any argument is null, or if the
 1268  
      * {@linkplain Envelope#getCoordinateReferenceSystem() envelope CRS} is null or the same
 1269  
      * instance than the given target CRS, then the given envelope is returned unchanged.
 1270  
      * Otherwise a new transformed envelope is returned.
 1271  
      * <p>
 1272  
      * See {@link Envelopes#transform(Envelope, CoordinateReferenceSystem)} for more information.
 1273  
      * This method delegates its work to the above-cited {@code Envelopes} class and is defined
 1274  
      * in this {@code CRS} class only for convenience.
 1275  
      *
 1276  
      * @param  envelope The envelope to transform (may be {@code null}).
 1277  
      * @param  targetCRS The target CRS (may be {@code null}).
 1278  
      * @return A new transformed envelope, or directly {@code envelope} if no change was required.
 1279  
      * @throws TransformException If a transformation was required and failed.
 1280  
      *
 1281  
      * @category transform
 1282  
      * @since 2.5
 1283  
      */
 1284  
     // See the above comment about why some Geotk code still reference this method.
 1285  
     public static Envelope transform(Envelope envelope, final CoordinateReferenceSystem targetCRS)
 1286  
             throws TransformException
 1287  
     {
 1288  9
         return Envelopes.transform(envelope, targetCRS);
 1289  
     }
 1290  
 
 1291  
     /**
 1292  
      * Transforms an envelope using the given {@linkplain MathTransform math transform}.
 1293  
      * The transformation is only approximative: the returned envelope may be bigger than
 1294  
      * necessary, or smaller than required if the bounding box contains a pole.
 1295  
      * <p>
 1296  
      * See {@link Envelopes#transform(MathTransform, Envelope)} for more information.
 1297  
      * This method delegates its work to the above-cited {@code Envelopes} class and
 1298  
      * is defined in this {@code CRS} class only for convenience.
 1299  
      *
 1300  
      * @param  transform The transform to use.
 1301  
      * @param  envelope Envelope to transform, or {@code null}. This envelope will not be modified.
 1302  
      * @return The transformed envelope, or {@code null} if {@code envelope} was null.
 1303  
      * @throws TransformException if a transform failed.
 1304  
      *
 1305  
      * @category transform
 1306  
      * @since 2.4
 1307  
      */
 1308  
     // See the above comment about why some Geotk code still reference this method.
 1309  
     public static GeneralEnvelope transform(final MathTransform transform, final Envelope envelope)
 1310  
             throws TransformException
 1311  
     {
 1312  0
         return Envelopes.transform(transform, envelope);
 1313  
     }
 1314  
 
 1315  
     /**
 1316  
      * Transforms an envelope using the given {@linkplain CoordinateOperation coordinate operation}.
 1317  
      * The transformation is only approximative: the returned envelope may be bigger than the
 1318  
      * smallest possible bounding box, but should not be smaller in most cases.
 1319  
      * <p>
 1320  
      * See {@link Envelopes#transform(CoordinateOperation, Envelope)} for more information.
 1321  
      * This method delegates its work to the above-cited {@code Envelopes} class and is defined
 1322  
      * in this {@code CRS} class only for convenience.
 1323  
      *
 1324  
      * @param  operation The operation to use.
 1325  
      * @param  envelope Envelope to transform, or {@code null}. This envelope will not be modified.
 1326  
      * @return The transformed envelope, or {@code null} if {@code envelope} was null.
 1327  
      * @throws TransformException if a transform failed.
 1328  
      *
 1329  
      * @category transform
 1330  
      * @since 2.4
 1331  
      */
 1332  
     // See the above comment about why some Geotk code still reference this method.
 1333  
     public static GeneralEnvelope transform(final CoordinateOperation operation, Envelope envelope)
 1334  
             throws TransformException
 1335  
     {
 1336  0
         return Envelopes.transform(operation, envelope);
 1337  
     }
 1338  
 
 1339  
     /**
 1340  
      * Transforms a rectangular envelope using the given {@linkplain MathTransform math transform}.
 1341  
      * The transformation is only approximative: the returned envelope may be bigger than
 1342  
      * necessary, or smaller than required if the bounding box contains a pole.
 1343  
      * <p>
 1344  
      * See {@link Envelopes#transform(MathTransform2D, Rectangle2D, Rectangle2D)} for more
 1345  
      * information. This method delegates its work to the above-cited {@code Envelopes} class
 1346  
      * and is defined in this {@code CRS} class only for convenience.
 1347  
      *
 1348  
      * @param  transform   The transform to use. Source and target dimension must be 2.
 1349  
      * @param  envelope    The rectangle to transform (may be {@code null}).
 1350  
      * @param  destination The destination rectangle (may be {@code envelope}).
 1351  
      *         If {@code null}, a new rectangle will be created and returned.
 1352  
      * @return {@code destination}, or a new rectangle if {@code destination} was non-null
 1353  
      *         and {@code envelope} was null.
 1354  
      * @throws TransformException if a transform failed.
 1355  
      *
 1356  
      * @category transform
 1357  
      * @since 2.4
 1358  
      */
 1359  
     // See the above comment about why some Geotk code still reference this method.
 1360  
     public static Rectangle2D transform(final MathTransform2D transform,
 1361  
                                         final Rectangle2D     envelope,
 1362  
                                               Rectangle2D     destination)
 1363  
             throws TransformException
 1364  
     {
 1365  0
         return Envelopes.transform(transform, envelope, destination);
 1366  
     }
 1367  
 
 1368  
     /**
 1369  
      * Transforms a rectangular envelope using the given {@linkplain CoordinateOperation coordinate
 1370  
      * operation}. The transformation is only approximative: the returned envelope may be bigger
 1371  
      * than the smallest possible bounding box, but should not be smaller in most cases.
 1372  
      * <p>
 1373  
      * See {@link Envelopes#transform(CoordinateOperation, Rectangle2D, Rectangle2D)} for more
 1374  
      * information. This method delegates its work to the above-cited {@code Envelopes} class
 1375  
      * and is defined in this {@code CRS} class only for convenience.
 1376  
      *
 1377  
      * @param  operation The operation to use. Source and target dimension must be 2.
 1378  
      * @param  envelope The rectangle to transform (may be {@code null}).
 1379  
      * @param  destination The destination rectangle (may be {@code envelope}).
 1380  
      *         If {@code null}, a new rectangle will be created and returned.
 1381  
      * @return {@code destination}, or a new rectangle if {@code destination} was non-null
 1382  
      *         and {@code envelope} was null.
 1383  
      * @throws TransformException if a transform failed.
 1384  
      *
 1385  
      * @category transform
 1386  
      * @since 2.4
 1387  
      */
 1388  
     // See the above comment about why some Geotk code still reference this method.
 1389  
     public static Rectangle2D transform(final CoordinateOperation operation,
 1390  
                                         final Rectangle2D         envelope,
 1391  
                                               Rectangle2D         destination)
 1392  
             throws TransformException
 1393  
     {
 1394  0
         return Envelopes.transform(operation, envelope, destination);
 1395  
     }
 1396  
 
 1397  
     /**
 1398  
      * Transforms the given relative distance using the given transform. A relative distance
 1399  
      * vector is transformed without applying the translation components. However it needs to
 1400  
      * be computed at a particular location, given by the {@code origin} parameter in units
 1401  
      * of the source CRS.
 1402  
      *
 1403  
      * @param  transform The transformation to apply.
 1404  
      * @param  origin The position where to compute the delta transform in the source CRS.
 1405  
      * @param  vector The distance vector to be delta transformed.
 1406  
      * @return The result of the delta transformation.
 1407  
      * @throws TransformException if the transformation failed.
 1408  
      *
 1409  
      * @see AffineTransform#deltaTransform(Point2D, Point2D)
 1410  
      *
 1411  
      * @since 3.10 (derived from 2.3)
 1412  
      */
 1413  
     public static double[] deltaTransform(final MathTransform transform,
 1414  
             final DirectPosition origin, final double... vector) throws TransformException
 1415  
     {
 1416  1
         ensureNonNull("transform", transform);
 1417  1
         final int sourceDim = transform.getSourceDimensions();
 1418  1
         final int targetDim = transform.getTargetDimensions();
 1419  1
         final double[] result = new double[targetDim];
 1420  1
         if (vector.length != sourceDim) {
 1421  0
             throw new IllegalArgumentException(Errors.format(Errors.Keys.MISMATCHED_DIMENSION_$3,
 1422  
                     "vector", vector.length, sourceDim));
 1423  
         }
 1424  1
         if (transform instanceof AffineTransform) {
 1425  0
             ((AffineTransform) transform).deltaTransform(vector, 0, result, 0, 1);
 1426  
         } else {
 1427  
             /*
 1428  
              * If the optimized case in the previous "if" statement can't be used,
 1429  
              * use a more generic (but more costly) algorithm.
 1430  
              */
 1431  1
             final double[] coordinates = new double[2 * Math.max(sourceDim, targetDim)];
 1432  3
             for (int i=0; i<sourceDim; i++) {
 1433  2
                 final double c = origin.getOrdinate(i);
 1434  2
                 final double d = vector[i] * 0.5;
 1435  2
                 coordinates[i] = c - d;
 1436  2
                 coordinates[i + sourceDim] = c + d;
 1437  
             }
 1438  1
             transform.transform(coordinates, 0, coordinates, 0, 2);
 1439  3
             for (int i=0; i<targetDim; i++) {
 1440  2
                 result[i] = coordinates[i + targetDim] - coordinates[i];
 1441  
             }
 1442  
         }
 1443  1
         return result;
 1444  
     }
 1445  
 
 1446  
     /**
 1447  
      * Invoked when an unexpected exception occurred. Those exceptions must be non-fatal,
 1448  
      * i.e. the caller <strong>must</strong> have a reasonable fallback (otherwise it
 1449  
      * should propagate the exception).
 1450  
      */
 1451  
     static void unexpectedException(final String methodName, final Exception exception) {
 1452  0
         Logging.unexpectedException(CRS.class, methodName, exception);
 1453  0
     }
 1454  
 
 1455  
     /**
 1456  
      * Resets some aspects of the referencing system. The aspects to be reset are specified by
 1457  
      * a space or comma delimited string, which may include any of the following elements:
 1458  
      * <p>
 1459  
      * <ul>
 1460  
      *   <li>{@code "plugins"} for {@linkplain AuthorityFactoryFinder#scanForPlugins searching
 1461  
      *       the classpath for new plugins}.</li>
 1462  
      * </ul>
 1463  
      *
 1464  
      * @param aspects The aspects to reset, or {@code "all"} for all of them.
 1465  
      *        Unknown aspects are silently ignored.
 1466  
      *
 1467  
      * @since 2.5
 1468  
      *
 1469  
      * @deprecated This method doesn't do anything more than {@link AuthorityFactoryFinder#scanForPlugins()}.
 1470  
      */
 1471  
     @Deprecated
 1472  
     public static synchronized void reset(final String aspects) {
 1473  0
         ensureNonNull("aspects", aspects);
 1474  0
         final StringTokenizer tokens = new StringTokenizer(aspects, ", \t\n\r\f");
 1475  0
         while (tokens.hasMoreTokens()) {
 1476  0
             final String aspect = tokens.nextToken().trim();
 1477  0
             final boolean all = aspect.equalsIgnoreCase("all");
 1478  0
             if (all || aspect.equalsIgnoreCase("plugins")) {
 1479  0
                 AuthorityFactoryFinder.scanForPlugins();
 1480  0
                 standardFactory = null;
 1481  0
                 xyFactory       = null;
 1482  0
                 strictFactory   = null;
 1483  0
                 lenientFactory  = null;
 1484  
             }
 1485  0
         }
 1486  0
     }
 1487  
 }