CRS changes for revisions 24274:24275

The idea behind the lookupIdentifier identifier has been kept, but the implementation has been rewritten in commit 25397. With the rewritten implementation, SIS/Geotk do the work in a IdentifiedObjectFinder class and replace the boolean argument by a setFullScanAllowed(boolean) method. More importantly, each IdentifiedObjectFinder instance is associated to an AuthorityFactory instance, which allows more efficient queries and makes the Set authorities argument irrelevant.

The work provided in this commit can be divided in three parts:

  1. The first part is to look if a declared identifier allows us to find the object. SIS/Geotk perform similar work in the createFromIdentifiers(…) method, using the AuthorityFactory methods instead than the CRS ones. Contrarily to this commit, SIS/Geotk does not "eat" exceptions (except NoSuchAuthorityCodeException).
  2. The second part is to look at the object name. SIS/Geotk perform similar work in the createFromNames(…) method. The same remarks than above apply.
  3. The last step is to scan the database. This part has been totally rewritten. The new implementation performs a more efficient search using SQL queries instead than iterating over the whole database content.

Command line:

svn diff --extensions "--unified --ignore-space-change --ignore-all-space --ignore-eol-style" -r24274:24275 https://svn.osgeo.org/geotools/trunk/modules/library/referencing/src/main/java/org/geotools/referencing/CRS.java
Revision 24274Revision 24275
package org.geotools.referencing;

// J2SE dependencies
import java.util.Set;
import java.util.Map;
import java.util.List;
package org.geotools.referencing;

// J2SE dependencies
import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Map;
import java.util.List;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;

// OpenGIS dependencies
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.extent.BoundingPolygon;
import org.opengis.metadata.extent.GeographicExtent;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;

import org.geotools.factory.Factory;
import org.geotools.factory.FactoryNotFoundException;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.metadata.iso.extent.GeographicBoundingBoxImpl;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.resources.CRSUtilities;
import org.geotools.resources.Utilities;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.extent.BoundingPolygon;
import org.opengis.metadata.extent.GeographicExtent;
import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.spatialschema.geometry.Envelope;
import org.opengis.spatialschema.geometry.MismatchedDimensionException;

// Geotools dependencies
import org.geotools.factory.Hints;
import org.geotools.factory.Factory;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.GeneralDirectPosition;
import org.geotools.factory.FactoryRegistryException;
import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
import org.opengis.referencing.operation.CoordinateOperationFactory;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.spatialschema.geometry.Envelope;
import org.opengis.spatialschema.geometry.MismatchedDimensionException;


// Geotools dependencies
import org.geotools.factory.Hints;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.GeneralDirectPosition;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.util.Version;
import org.geotools.util.Logging;


/**
 * Simple utility class for making use of the {@linkplain CoordinateReferenceSystem
 * coordinate reference system} and associated {@linkplain org.opengis.referencing.Factory}
import org.geotools.util.Version;
import org.geotools.util.Logging;

/**
 * Simple utility class for making use of the {@linkplain CoordinateReferenceSystem
 * coordinate reference system} and associated {@linkplain org.opengis.referencing.Factory}
}

/**
 * Return a Coordinate Reference System for the specified code.
 * Note that the code needs to mention the authority. Examples:
 *
}

/**
 * Returns the set of the authority identifiers supported by registered authority factories.
 * @param returnAliases If true, the set will contain all identifiers for each authority,
 *        if false, only the first one
 * @return The set of supported authorities. May be empty, but never null.
 * @since 2.3.1
 */
public static Set/*<String>*/ getSupportedAuthorities(boolean returnAliases) {
    return DefaultAuthorityFactory.getSupportedAuthorities();
}

/**
 * Return a Coordinate Reference System for the specified code.
 * Note that the code needs to mention the authority. Examples:
 *
}


/////////////////////////////////////////////////
////                                         ////
////          COORDINATE OPERATIONS          ////
}


/**
 * Looks up an identifier for the specified coordinate reference system
 * @param ref the coordinate reference system looked up
 * @param authorities the authority that we should look up the identifier into.
 *        If null the search will to be performed against all authorities
 * @param fullScan if true, an exhaustive full scan against all registered CRS will be performed,
 *        otherwise only a fast lookup based on embedded identifiers and names will be performed
 * @return the identifier, or null if not found
 * @since 2.3.1
 */
public static String lookupIdentifier(final CoordinateReferenceSystem ref, Set authorities, final boolean fullScan) {
    // gather the authorities we're considering
    if(authorities == null)
        authorities = getSupportedAuthorities(false);

    // first check if one of the identifiers can be used to spot directly
    // a CRS (and check it's actually equal to one in the db)
    for (Iterator it = ref.getIdentifiers().iterator(); it.hasNext();) {
        NamedIdentifier id = (NamedIdentifier) it.next();
        try {
            CoordinateReferenceSystem crs = CRS.decode(id.toString());
            if(equalsIgnoreMetadata(crs, ref)) {
                String identifier = getSRSFromCRS(crs, authorities);
                if(identifier != null)
                    return identifier;
            }
        } catch (Exception e) {
            // the identifier was not recognized, no problem, let's go on
        }
    }

    // try a quick name lookup
    try {
        CoordinateReferenceSystem crs = CRS.decode(ref.getName().toString());
        if(equalsIgnoreMetadata(crs, ref)) {
            String identifier = getSRSFromCRS(crs, authorities);
            if(identifier != null)
                return identifier;
        }
    } catch(Exception e) {
        // the name was not recognized, no problem, let's go on
    }

    // here we exhausted the quick paths, bail out if the user does not want a full scan
    if(!fullScan)
        return null;

    // a direct lookup did not work, let's try a full scan of known CRS then
    // TODO: implement a smarter method in the actual EPSG authorities, which may
    // well be this same loop if they do have no other search capabilities
    for (Iterator itAuth = authorities.iterator(); itAuth.hasNext();) {
        String authority = (String) itAuth.next();
        Set codes = CRS.getSupportedCodes(authority);
        for (Iterator itCodes = codes.iterator(); itCodes.hasNext();) {
            String code = (String) itCodes.next();
            try {
                final CoordinateReferenceSystem crs;
                if(code.indexOf(":") == -1)
                    crs = CRS.decode(authority + ":" + code, true);
                else
                    crs = CRS.decode(code, true);
                if (CRS.equalsIgnoreMetadata(crs, ref)) {
                    return getSRSFromCRS(crs, Collections.singleton(authority));
                }
            } catch (Exception e) {
                // some CRS cannot be decoded properly
            }
        }
    }

    return null;
}


/**
 * Scans the identifiers list looking for an EPSG id
 * @param crs
 * @return
 */
private static String getSRSFromCRS(final CoordinateReferenceSystem crs, final Set authorities) {
    for (Iterator itAuth = authorities.iterator(); itAuth.hasNext();) {
        final String authority = (String) itAuth.next();
        final String prefix = authority + ":";
        for (Iterator itIdent = crs.getIdentifiers().iterator(); itIdent.hasNext();) {
            NamedIdentifier id = (NamedIdentifier) itIdent.next();
            String idName = id.toString();
            if(idName.startsWith(prefix))
                return idName;
        }
    }
    return null;
}


/////////////////////////////////////////////////
////                                         ////
////          COORDINATE OPERATIONS          ////
        }
        return XRectangle2D.createFromExtremums(xmin, ymin, xmax, ymax);
    }
}
        }
        return XRectangle2D.createFromExtremums(xmin, ymin, xmax, ymax);
    }



}