/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2005-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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.
*/
package org.geotools.referencing.factory.wms;
// J2SE dependencies and extensions
import java.util.Map;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Iterator;
import java.util.Set;
import java.util.LinkedHashSet;
// OpenGIS dependencies
import org.opengis.metadata.Identifier;
import org.opengis.util.InternationalString;
import org.opengis.metadata.citation.Citation;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.CRSAuthorityFactory;
// Geotools dependencies
import org.geotools.factory.Hints;
import org.geotools.util.SimpleInternationalString;
import org.geotools.metadata.iso.citation.Citations;
import org.geotools.referencing.NamedIdentifier;
import org.geotools.referencing.cs.DefaultEllipsoidalCS;
import org.geotools.referencing.datum.DefaultEllipsoid;
import org.geotools.referencing.datum.DefaultPrimeMeridian;
import org.geotools.referencing.factory.DirectAuthorityFactory;
/**
* The factory for {@linkplain CoordinateReferenceSystem coordinate reference systems}
* in the {@code CRS} space.
*
* @since 2.2
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux
*
* @deprecated This class will move in a <code>org.geotools.referencing.factory.<strong>web</strong></code>
* package in a future Geotools version, in order to put together other web-related factories defined
* outside the WMS specification. Don't use this class directly. You should not need to
* anyway - use {@link org.geotools.referencing.ReferencingFactoryFinder} instead, which
* will continue to work no matter where this class is located.
*/
public class WebCRSFactory extends DirectAuthorityFactory implements CRSAuthorityFactory {
/**
* An optional prefix put in front of code. For example a code may be {@code "CRS84"}
* instead of a plain {@code "84"}. This is usefull in order to understand URN syntax
* like {@code "urn:ogc:def:crs:OGC:1.3:CRS84"}. Must be uppercase for this implementation
* (but parsing will be case-insensitive).
*/
private static final String PREFIX = "CRS";
/**
* The map of pre-defined CRS.
*/
private final Map<Integer,CoordinateReferenceSystem> crsMap =
new TreeMap<Integer,CoordinateReferenceSystem>();
/**
* Constructs a default factory for the {@code CRS} authority.
*/
public WebCRSFactory() {
this(null);
}
/**
* Constructs a factory for the {@code CRS} authority using the specified hints.
*/
public WebCRSFactory(final Hints hints) {
super(hints, NORMAL_PRIORITY);
}
/**
* Ensures that {@link #crsMap} is initialized. This method can't be invoked in the constructor
* because the constructor is invoked while {@code FactoryFinder.scanForPlugins()} is still
* running. Because the {@link #add} method uses factories for creating CRS objects, invoking
* this method during {@code FactoryFinder.scanForPlugins()} execution may result in unexpected
* behavior, like GEOT-935.
*/
private synchronized void ensureInitialized() throws FactoryException {
if (crsMap.isEmpty()) {
add(84, "WGS84", DefaultEllipsoid.WGS84 );
add(83, "NAD83", DefaultEllipsoid.GRS80 );
add(27, "NAD27", DefaultEllipsoid.CLARKE_1866);
}
}
/**
* Adds a geographic CRS from the specified parameters.
*
* @param code The CRS code.
* @param name The CRS and datum name.
* @param ellipsoid The ellipsoid.
*
* @throws FactoryException if factories failed to creates the CRS.
*/
private void add(final int code,
final String name,
final Ellipsoid ellipsoid)
throws FactoryException
{
assert Thread.holdsLock(this);
final Map properties = new HashMap();
final Citation authority = getAuthority();
final String text = String.valueOf(code);
properties.put(IdentifiedObject.NAME_KEY, name);
properties.put(Identifier.AUTHORITY_KEY, authority);
final GeodeticDatum datum = factories.getDatumFactory().createGeodeticDatum(
properties, ellipsoid, DefaultPrimeMeridian.GREENWICH);
properties.put(IdentifiedObject.IDENTIFIERS_KEY, new NamedIdentifier[] {
new NamedIdentifier(authority, text),
new NamedIdentifier(authority, PREFIX + text)
});
final CoordinateReferenceSystem crs = factories.getCRSFactory().createGeographicCRS(
properties, datum, DefaultEllipsoidalCS.GEODETIC_2D);
if (crsMap.put(code, crs) != null) {
throw new IllegalArgumentException(text);
}
}
/**
* Returns the authority for this factory, which is {@link Citations#CRS CRS}.
*/
public Citation getAuthority() {
return Citations.CRS;
}
/**
* Provides a complete set of the known codes provided by this authority. The returned set
* contains only numeric identifiers like {@code "84"}, {@code "27"}, <cite>etc</cite>.
* The authority name ({@code "CRS"}) is not included. This is consistent with the
* {@linkplain org.geotools.referencing.factory.epsg.DirectEpsgFactory#getAuthorityCodes
* codes returned by the EPSG factory} and avoid duplication, since the authority is the
* same for every codes returned by this factory. It also make it easier for clients to
* prepend whatever authority name they wish, as for example in the
* {@linkplain org.geotools.referencing.factory.AllAuthoritiesFactory#getAuthorityCodes
* all authorities factory}.
*/
public Set getAuthorityCodes(final Class type) throws FactoryException {
ensureInitialized();
final Set set = new LinkedHashSet();
for (final Iterator it=crsMap.entrySet().iterator(); it.hasNext();) {
final Map.Entry entry = (Map.Entry) it.next();
final CoordinateReferenceSystem crs = (CoordinateReferenceSystem) entry.getValue();
if (type.isAssignableFrom(crs.getClass())) {
final Integer code = (Integer) entry.getKey();
set.add(String.valueOf(code));
}
}
return set;
}
/**
* Returns the CRS name for the given code.
*/
public InternationalString getDescriptionText(final String code) throws FactoryException {
return new SimpleInternationalString(createObject(code).getName().getCode());
}
/**
* Creates an object from the specified code. The default implementation delegates to
* <code>{@linkplain #createCoordinateReferenceSystem createCoordinateReferenceSystem}(code)</code>.
*/
public IdentifiedObject createObject(final String code) throws FactoryException {
return createCoordinateReferenceSystem(code);
}
/**
* Creates a coordinate reference system from the specified code.
*/
public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code)
throws FactoryException
{
String c = trimAuthority(code).toUpperCase();
if (c.startsWith(PREFIX)) {
c = c.substring(PREFIX.length());
}
final int i;
try {
i = Integer.parseInt(c);
} catch (NumberFormatException exception) {
// If a number can't be parsed, then this is an invalid authority code.
NoSuchAuthorityCodeException e = noSuchAuthorityCode(CoordinateReferenceSystem.class, code);
e.initCause(exception);
throw e;
}
ensureInitialized();
final CoordinateReferenceSystem crs = crsMap.get(i);
if (crs != null) {
return crs;
}
throw noSuchAuthorityCode(CoordinateReferenceSystem.class, code);
}
}