/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2001-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* This package contains documentation from OpenGIS specifications.
* OpenGIS consortium's work is fully acknowledged here.
*/
package org.geotoolkit.referencing;
import java.util.Map;
import java.util.HashMap;
import java.util.Comparator;
import org.opengis.util.GenericName;
import org.opengis.util.FactoryException;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.metadata.Identifier;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.geotoolkit.lang.Static;
import org.geotoolkit.resources.Errors;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.util.iso.DefaultNameSpace;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
import org.apache.sis.referencing.NamedIdentifier;
import static org.opengis.referencing.IdentifiedObject.NAME_KEY;
import static org.opengis.referencing.IdentifiedObject.IDENTIFIERS_KEY;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
/**
* Utility methods working on arbitrary implementations of the {@link IdentifiedObject}
* interface.
*
* {@section Note on Spatial Reference System (SRS) identifiers}
* OGC Web Services have the concept of a Spatial Reference System identifier used to
* communicate CRS information between systems. In <cite>Well Known Text</cite> (WKT)
* format, this identifier is declared in the {@code AUTHORITY} element.
* <p>
* Examples of Spatial Reference System (SRS) values:
* <ul>
* <li>{@code EPSG:4326} - this was understood to mean <cite>force XY axis order</cite> in
* old Web Map Services (WMS). Note that latest WMS specifications require the respect
* of axis order as declared in the EPSG database, which is (<var>latitude</var>,
* <var>longitude</var>).</li>
* <li>{@code urn:ogc:def:crs:EPSG::4326} - understood to match the EPSG database axis order
* in all cases, no matter the WMS version.</li>
* <li>{@code AUTO:43200} - without the parameters that are specific to AUTO codes.</li>
* </ul>
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Guilhem Legal (Geomatys)
* @version 3.20
*
* @see CRS
* @see org.geotoolkit.geometry.Envelopes
*
* @since 3.18 (derived from 1.2)
* @module
*
* @deprecated Moved to Apache SIS.
*/
@Deprecated
public final class IdentifiedObjects extends Static {
/**
* Do not allows instantiation of this class.
*/
private IdentifiedObjects() {
}
/**
* An empty array of identifiers. This is useful for fetching identifiers as an array,
* using the following idiom:
*
* {@preformat java
* getIdentifiers().toArray(EMPTY_IDENTIFIER_ARRAY);
* }
*
* @see IdentifiedObject#getIdentifiers()
*/
public static final Identifier[] EMPTY_IDENTIFIER_ARRAY = new Identifier[0];
/**
* An empty array of alias. This is useful for fetching alias as an array,
* using the following idiom:
*
* {@preformat java
* getAlias().toArray(EMPTY_ALIAS_ARRAY);
* }
*
* @see IdentifiedObject#getAlias()
*/
public static final GenericName[] EMPTY_ALIAS_ARRAY = new GenericName[0];
/**
* A comparator for sorting identified objects by {@linkplain IdentifiedObject#getName() name}.
*/
public static final Comparator<IdentifiedObject> NAME_COMPARATOR = new NameComparator();
/**
* A comparator for sorting identified objects by {@linkplain IdentifiedObject#getIdentifiers identifiers}.
* Identifiers are compared in their iteration order.
*/
public static final Comparator<IdentifiedObject> IDENTIFIER_COMPARATOR = new IdentifierComparator();
/**
* A comparator for sorting identified objects by {@linkplain IdentifiedObject#getRemarks remarks}.
*/
public static final Comparator<IdentifiedObject> REMARKS_COMPARATOR = new RemarksComparator();
/**
* Compares two objects for order. Any object may be null. This method is
* used for implementation of {@link #NAME_COMPARATOR} and its friends.
*/
static <E extends Comparable<E>> int doCompare(final E c1, final E c2) {
if (c1 == null) {
return (c2 == null) ? 0 : -1;
}
if (c2 == null) {
return +1;
}
return c1.compareTo(c2);
}
/**
* Returns the properties to be given to an identified object derived from the specified one.
* This method returns the same properties than the supplied argument (as of
* <code>{@linkplain #getProperties(IdentifiedObject) getProperties}(info)</code>), except for
* the following:
* <p>
* <ul>
* <li>The {@linkplain IdentifiedObject#getName() name}'s authority is replaced by the specified one.</li>
* <li>All {@linkplain IdentifiedObject#getIdentifiers identifiers} are removed, because the new object
* to be created is probably not endorsed by the original authority.</li>
* </ul>
* <p>
* This method returns a mutable map. Consequently, callers can add their own identifiers
* directly to this map if they wish.
*
* @param info The identified object to view as a properties map.
* @param authority The new authority for the object to be created, or {@code null} if it
* is not going to have any declared authority.
* @return The identified object properties in a mutable map.
*
* @deprecated Will be removed.
*/
@Deprecated // Now a package-private method in DefaultOperationMethod.
public static Map<String,Object> getProperties(final IdentifiedObject info, final Citation authority) {
final Map<String,Object> properties = new HashMap<>(org.apache.sis.referencing.IdentifiedObjects.getProperties(info));
properties.put(NAME_KEY, new NamedIdentifier(authority, info.getName().getCode()));
properties.remove(IDENTIFIERS_KEY);
return properties;
}
/**
* Looks up an {@linkplain Identifier identifier}, such as {@code "EPSG:4326"},
* of the specified object. This method searches in registered factories for an object
* {@linkplain ComparisonMode#APPROXIMATIVE approximatively equals} to the specified
* object. If such an object is found, then its first identifier is returned. Otherwise
* this method returns {@code null}.
* <p>
* <strong>Note that this method checks the identifier validity</strong>. If the given object
* declares explicitly an identifier, then this method will instantiate an object from the
* authority factory using that identifier and compare it with the given object. If the
* comparison fails, then this method returns {@code null}. Consequently this method may
* returns {@code null} even if the given object declares explicitly its identifier. If
* the declared identifier is wanted unconditionally, use
* {@link #getIdentifier(IdentifiedObject)} instead.
*
* @param object The object (usually a {@linkplain CoordinateReferenceSystem coordinate
* reference system}) whose identifier is to be found, or {@code null}.
* @param fullScan If {@code true}, an exhaustive full scan against all registered objects
* should be performed (may be slow). Otherwise only a fast lookup based on embedded
* identifiers and names will be performed.
* @return The identifier, or {@code null} if none was found or if the given object was null.
* @throws FactoryException If an unexpected failure occurred during the search.
*
* @see AbstractAuthorityFactory#getIdentifiedObjectFinder(Class)
* @see IdentifiedObjectFinder#findIdentifier(IdentifiedObject)
*
* @deprecated Moved to Apache SIS {@link org.apache.sis.referencing.IdentifiedObjects}.
*/
@Deprecated
public static String lookupIdentifier(final IdentifiedObject object, final boolean fullScan)
throws FactoryException
{
if (object == null) {
return null;
}
IdentifiedObjectFinder f = org.apache.sis.referencing.IdentifiedObjects.newFinder(null);
f.setSearchDomain(fullScan ? IdentifiedObjectFinder.Domain.ALL_DATASET : IdentifiedObjectFinder.Domain.DECLARATION);
for (final IdentifiedObject o : f.find(object)) {
final String i = org.apache.sis.referencing.IdentifiedObjects.toString(
org.apache.sis.referencing.IdentifiedObjects.getIdentifier(o, null));
if (i != null) return i;
}
return null;
}
/**
* Looks up an {@linkplain Identifier identifier} in the namespace of the given
* authority, such as {@link Citations#EPSG EPSG}, of the specified CRS. Invoking this
* method is equivalent to invoking
* <code>{@linkplain #lookupIdentifier(IdentifiedObject, boolean) lookupIdentifier}(object,
* fullScan)</code> except that the search is performed only among the factories of the given
* authority.
*
* {@section Identifiers in URN and HTTP namespaces}
* Note that if the given authority is {@link Citations#URN_OGC} or {@link Citations#HTTP_OGC},
* then this method behaves as if the code was searched in all authority factories and the
* result formatted in a {@code "urn:ogc:def:"} or
* {@value org.geotoolkit.referencing.factory.web.HTTP_AuthorityFactory#BASE_URL} namespace.
*
* @param authority The authority for the code to search.
* @param object The object (usually a {@linkplain CoordinateReferenceSystem coordinate
* reference system}) whose identifier is to be found, or {@code null}.
* @param fullScan If {@code true}, an exhaustive full scan against all registered objects
* should be performed (may be slow). Otherwise only a fast lookup based on embedded
* identifiers and names will be performed.
* @return The identifier, or {@code null} if none was found or if the given object was null.
* @throws FactoryException If an unexpected failure occurred during the search.
*
* @category information
*
* @deprecated Moved to Apache SIS {@link org.apache.sis.referencing.IdentifiedObjects}.
*/
@Deprecated
public static String lookupIdentifier(final Citation authority, final IdentifiedObject object,
final boolean fullScan) throws FactoryException
{
ensureNonNull("authority", authority);
if (object == null) {
return null;
}
Identifier id = org.apache.sis.referencing.IdentifiedObjects.getIdentifier(object, authority);
if (id != null) {
return id.getCode();
}
if (authority == org.geotoolkit.metadata.Citations.URN_OGC) {
String urn = org.apache.sis.referencing.IdentifiedObjects.lookupURN(object, null);
if (urn != null) {
urn = urn.toLowerCase();
}
return urn;
}
IdentifiedObjectFinder f = org.apache.sis.referencing.IdentifiedObjects.newFinder(null);
f.setSearchDomain(fullScan ? IdentifiedObjectFinder.Domain.ALL_DATASET : IdentifiedObjectFinder.Domain.DECLARATION);
for (final IdentifiedObject o : f.find(object)) {
final Identifier i = org.apache.sis.referencing.IdentifiedObjects.getIdentifier(o, authority);
if (i != null) return i.getCode();
}
return null;
}
/**
* Looks up an EPSG code of the given {@linkplain CoordinateReferenceSystem
* coordinate reference system}). This is a convenience method for <code>{@linkplain
* #lookupIdentifier(Citation, IdentifiedObject, boolean) lookupIdentifier}({@linkplain
* Citations#EPSG EPSG}, crs, fullScan)</code> with the returned code parsed as an integer.
*
* @param object The object (usually a {@linkplain CoordinateReferenceSystem coordinate
* reference system}) whose identifier is to be found, or {@code null}.
* @param fullScan If {@code true}, an exhaustive full scan against all registered objects
* should be performed (may be slow). Otherwise only a fast lookup based on embedded
* identifiers and names will be performed.
* @return The identifier, or {@code null} if none was found or if the given object was null.
* @throws FactoryException If an unexpected failure occurred during the search.
*
* @category information
*
* @deprecated Moved to Apache SIS {@link org.apache.sis.referencing.IdentifiedObjects}.
*/
@Deprecated
public static Integer lookupEpsgCode(final IdentifiedObject object, final boolean fullScan)
throws FactoryException
{
final String identifier = lookupIdentifier(Citations.EPSG, object, fullScan);
if (identifier != null) {
final int split = identifier.lastIndexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
final String code = identifier.substring(split + 1);
// The above code works even if the separator was not found, since in such case
// split == -1, which implies a call to substring(0) which returns 'identifier'.
try {
return Integer.parseInt(code);
} catch (NumberFormatException e) {
throw new FactoryException(Errors.format(Errors.Keys.IllegalIdentifier_1, identifier), e);
}
}
return null;
}
}