blob: 5605a36c7d591bd47eee0c6ca1fe5213ce4d5c6e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.referencing.factory;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogRecord;
import java.util.concurrent.atomic.AtomicReference;
import java.lang.reflect.Constructor;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.measure.Unit;
import javax.measure.quantity.Angle;
import javax.measure.quantity.Length;
import javax.xml.bind.JAXBException;
import org.opengis.util.GenericName;
import org.opengis.util.FactoryException;
import org.opengis.util.InternationalString;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.extent.Extent;
import org.opengis.referencing.*;
import org.opengis.referencing.cs.*;
import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.parameter.ParameterNotFoundException;
import org.apache.sis.referencing.cs.*;
import org.apache.sis.referencing.crs.*;
import org.apache.sis.referencing.datum.*;
import org.apache.sis.referencing.NamedIdentifier;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.AbstractReferenceSystem;
import org.apache.sis.referencing.AbstractIdentifiedObject;
import org.apache.sis.internal.referencing.ReferencingFactoryContainer;
import org.apache.sis.internal.referencing.MergedProperties;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.system.Loggers;
import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.util.collection.WeakHashSet;
import org.apache.sis.util.iso.AbstractFactory;
import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.io.wkt.Parser;
import org.apache.sis.util.Exceptions;
import org.apache.sis.xml.XML;
/**
* Creates {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate Reference System} (CRS) implementations,
* with their {@linkplain org.apache.sis.referencing.cs.AbstractCS Coordinate System} (CS)
* and {@linkplain org.apache.sis.referencing.datum.AbstractDatum Datum} components.
* This factory serves two purposes:
*
* <ul>
* <li><b>For users</b>, allows the creation of complex objects that can not be created by the authority factories,
* without explicit dependency to Apache SIS (when using the GeoAPI interfaces implemented by this class).</li>
* <li><b>For providers</b>, allows <cite>inversion of control</cite> by overriding methods in this class,
* then specifying the customized instance to other services that consume {@code CRSFactory} (for example
* authority factories or {@linkplain org.apache.sis.io.wkt.WKTFormat WKT parsers}).</li>
* </ul>
*
* This {@code GeodeticObjectFactory} class is not easy to use directly.
* Users are encouraged to use an authority factory instead
* (or the {@link org.apache.sis.referencing.CRS#forCode(String)} convenience method)
* when the CRS object to construct can be identified by a code in the namespace of an authority (typically EPSG).
*
* <div class="section">Object properties</div>
* Most factory methods expect a {@link Map Map&lt;String,?&gt;} argument, often followed by explicit arguments.
* Unless otherwise noticed, information provided in the {@code properties} map are considered ignorable metadata
* while information provided in explicit arguments have an impact on coordinate transformation results.
*
* <p>The following table lists the keys recognized by the {@code GeodeticObjectFactory} default implementation,
* together with the type of values associated to those keys.
* A value for the {@code "name"} key is mandatory for all objects, while all other properties are optional.
* {@code GeodeticObjectFactory} methods ignore all unknown properties.</p>
*
* <table class="sis">
* <caption>Recognized properties (non exhaustive list)</caption>
* <tr>
* <th>Property name</th>
* <th>Value type</th>
* <th>Returned by</th>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td>
* <td>{@link Identifier} or {@link String}</td>
* <td>{@link AbstractIdentifiedObject#getName()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.metadata.Identifier#AUTHORITY_KEY}</td>
* <td>{@link String} or {@link org.opengis.metadata.citation.Citation}</td>
* <td>{@link NamedIdentifier#getAuthority()} on the {@linkplain AbstractIdentifiedObject#getName() name}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.metadata.Identifier#CODE_KEY}</td>
* <td>{@link String}</td>
* <td>{@link NamedIdentifier#getCode()} on the {@linkplain AbstractIdentifiedObject#getName() name}</td>
* </tr>
* <tr>
* <td>"codespace"</td>
* <td>{@link String}</td>
* <td>{@link NamedIdentifier#getCodeSpace()} on the {@linkplain AbstractIdentifiedObject#getName() name}</td>
* </tr>
* <tr>
* <td>"version"</td>
* <td>{@link String}</td>
* <td>{@link NamedIdentifier#getVersion()} on the {@linkplain AbstractIdentifiedObject#getName() name}</td>
* </tr>
* <tr>
* <td>"description"</td>
* <td>{@link String}</td>
* <td>{@link NamedIdentifier#getDescription()} on the {@linkplain AbstractIdentifiedObject#getName() name}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td>
* <td>{@link GenericName} or {@link CharSequence} (optionally as array)</td>
* <td>{@link AbstractIdentifiedObject#getAlias()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td>
* <td>{@link Identifier} (optionally as array)</td>
* <td>{@link AbstractIdentifiedObject#getIdentifiers()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.ReferenceSystem#DOMAIN_OF_VALIDITY_KEY}</td>
* <td>{@link Extent}</td>
* <td>{@link AbstractReferenceSystem#getDomainOfValidity()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.ReferenceSystem#SCOPE_KEY}</td>
* <td>{@link String} or {@link InternationalString}</td>
* <td>{@link AbstractReferenceSystem#getScope()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.datum.Datum#ANCHOR_POINT_KEY}</td>
* <td>{@link InternationalString} or {@link String}</td>
* <td>{@link AbstractDatum#getAnchorPoint()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.datum.Datum#REALIZATION_EPOCH_KEY}</td>
* <td>{@link Date}</td>
* <td>{@link AbstractDatum#getRealizationEpoch()}</td>
* </tr>
* <tr>
* <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td>
* <td>{@link InternationalString} or {@link String}</td>
* <td>{@link AbstractIdentifiedObject#getRemarks()}</td>
* </tr>
* <tr>
* <td>{@value org.apache.sis.referencing.AbstractIdentifiedObject#DEPRECATED_KEY}</td>
* <td>{@link Boolean}</td>
* <td>{@link AbstractIdentifiedObject#isDeprecated()}</td>
* </tr>
* <tr>
* <td>{@value org.apache.sis.referencing.AbstractIdentifiedObject#LOCALE_KEY}</td>
* <td>{@link Locale}</td>
* <td>(none)</td>
* </tr>
* </table>
*
* <div class="section">Localization</div>
* All localizable attributes like {@code "remarks"} may have a language and country code suffix.
* For example the {@code "remarks_fr"} property stands for remarks in {@linkplain Locale#FRENCH French} and
* the {@code "remarks_fr_CA"} property stands for remarks in {@linkplain Locale#CANADA_FRENCH French Canadian}.
* They are convenience properties for building the {@code InternationalString} value.
*
* <p>The {@code "locale"} property applies only in case of exception for formatting the error message, and
* is used only on a <cite>best effort</cite> basis. The locale is discarded after successful construction
* since localizations are applied by the {@link InternationalString#toString(Locale)} method.</p>
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Guilhem Legal (Geomatys)
* @author Johann Sorel (Geomatys)
* @version 0.8
* @since 0.6
* @module
*/
public class GeodeticObjectFactory extends AbstractFactory implements CRSFactory, CSFactory, DatumFactory, Parser {
/**
* The logger to use for reporting object creations.
*/
private static final Logger LOGGER = Logging.getLogger(Loggers.CRS_FACTORY);
/**
* The constructor for WKT parsers, fetched when first needed. The WKT parser is defined in the
* same module than this class, so we will hopefully not have security issues. But we have to
* use reflection because the parser class is not yet public (because we do not want to commit
* its API yet).
*/
private static volatile Constructor<? extends Parser> parserConstructor;
/**
* The default properties, or an empty map if none. This map shall not change after construction in
* order to allow usage without synchronization in multi-thread context. But we do not need to wrap
* in a unmodifiable map since {@code GeodeticObjectFactory} does not provide public access to it.
*/
private final Map<String,?> defaultProperties;
/**
* The math transform factory. Will be created only when first needed.
* This is normally not needed by this factory, except when constructing derived and projected CRS.
*
* @see #getMathTransformFactory()
*/
private volatile MathTransformFactory mtFactory;
/**
* Weak references to existing objects (CRS, CS, Datum, Ellipsoid or PrimeMeridian).
* This set is used in order to return a pre-existing object instead of creating a new one.
*/
private final WeakHashSet<AbstractIdentifiedObject> pool;
/**
* The <cite>Well Known Text</cite> parser for {@code CoordinateReferenceSystem} instances.
* This parser is not thread-safe, so we need to prevent two threads from using the same instance in same time.
*/
private final AtomicReference<Parser> parser;
/**
* Constructs a factory with no default properties.
*/
public GeodeticObjectFactory() {
this(null);
}
/**
* Constructs a factory with the given default properties.
* {@code GeodeticObjectFactory} will fallback on the map given to this constructor for any property
* not present in the map provided to a {@code createFoo(Map<String,?>, …)} method.
*
* @param properties the default properties, or {@code null} if none.
*/
public GeodeticObjectFactory(Map<String,?> properties) {
if (properties == null || properties.isEmpty()) {
properties = Collections.emptyMap();
} else {
properties = CollectionsExt.compact(new HashMap<>(properties));
}
defaultProperties = properties;
pool = new WeakHashSet<>(AbstractIdentifiedObject.class);
parser = new AtomicReference<>();
}
/**
* Returns the union of the given {@code properties} map with the default properties given at
* {@linkplain #GeodeticObjectFactory(Map) construction time}. Entries in the given properties
* map have precedence, even if their {@linkplain java.util.Map.Entry#getValue() value} is {@code null}
* (i.e. a null value "erase" the default property value).
* Entries with null value after the union will be omitted.
*
* <p>This method is invoked by all {@code createFoo(Map<String,?>, …)} methods.</p>
*
* @param properties the user-supplied properties.
* @return the union of the given properties with the default properties.
*/
protected Map<String,?> complete(final Map<String,?> properties) {
ArgumentChecks.ensureNonNull("properties", properties);
return new MergedProperties(properties, defaultProperties) {
/**
* Handles the {@code "mtFactory"} key in a special way since this is normally not needed for
* {@link GeodeticObjectFactory}, except when creating the SIS implementation of derived or
* projected CRS (because of the way we implemented derived CRS, but this is specific to SIS).
*/
@Override
protected Object invisibleEntry(final Object key) {
if (ReferencingFactoryContainer.MT_FACTORY.equals(key)) {
return getMathTransformFactory();
} else {
return super.invisibleEntry(key);
}
}
};
}
/**
* Returns the math transform factory for internal usage only.
* The {@code MathTransformFactory} is normally not needed by {@code GeodeticObjectFactory},
* except when constructing the Apache SIS implementation of derived and projected CRS.
* For this reason, we will fetch this dependency only if really requested.
*/
final MathTransformFactory getMathTransformFactory() {
MathTransformFactory factory = mtFactory;
if (factory == null) {
mtFactory = factory = DefaultFactories.forBuildin(MathTransformFactory.class);
}
return factory;
}
/**
* Returns a unique instance of the given object. If this method recycles an existing object,
* then the existing instance is returned silently. Otherwise this method logs a message at
* {@link Level#FINE} telling that a new object has been created.
*/
private <T extends AbstractIdentifiedObject> T unique(final String caller, final T object) {
final T c = pool.unique(object);
if (c == object && LOGGER.isLoggable(Level.FINE)) {
final String id = IdentifiedObjects.toString(IdentifiedObjects.getIdentifier(c, null));
final LogRecord record = Messages.getResources(null).getLogRecord(Level.FINE,
(id != null) ? Messages.Keys.CreatedIdentifiedObject_3
: Messages.Keys.CreatedNamedObject_2,
c.getInterface(), c.getName().getCode(), id);
record.setSourceClassName(GeodeticObjectFactory.class.getCanonicalName());
record.setSourceMethodName(caller);
record.setLoggerName(LOGGER.getName());
LOGGER.log(record);
}
return c;
}
/**
* Creates a geocentric coordinate reference system from a {@linkplain CartesianCS Cartesian coordinate system}.
* Geocentric CRS have their origin at the approximate centre of mass of the earth.
* An {@linkplain #createGeocentricCRS(Map, GeodeticDatum, SphericalCS) alternate method} allows creation of the
* same kind of CRS with spherical coordinate system instead than a Cartesian one.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* <li>One of:<ul>
* <li>{@link #createEllipsoid(Map, double, double, Unit)}</li>
* <li>{@link #createFlattenedSphere(Map, double, double, Unit)}</li>
* </ul></li>
* <li>{@link #createPrimeMeridian(Map, double, Unit)}</li>
* <li>{@link #createGeodeticDatum(Map, Ellipsoid, PrimeMeridian)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultGeocentricCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum the geodetic datum to use in created CRS.
* @param cs the three-dimensional Cartesian coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see GeodeticAuthorityFactory#createGeocentricCRS(String)
* @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, CartesianCS)
*/
@Override
public GeocentricCRS createGeocentricCRS(final Map<String,?> properties,
final GeodeticDatum datum, final CartesianCS cs) throws FactoryException
{
final DefaultGeocentricCRS crs;
try {
crs = new DefaultGeocentricCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createGeocentricCRS", crs);
}
/**
* Creates a three-dimensional Cartesian coordinate system from the given set of axis.
* This coordinate system can be used with geocentric, engineering and derived CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultCartesianCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis (e.g. “Geocentric X”).
* @param axis1 the second axis (e.g. “Geocentric Y”).
* @param axis2 the third axis (e.g. “Geocentric Z”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultCartesianCS#DefaultCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createCartesianCS(String)
*/
@Override
public CartesianCS createCartesianCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultCartesianCS cs;
try {
cs = new DefaultCartesianCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createCartesianCS", cs);
}
/**
* Creates a geocentric coordinate reference system from a {@linkplain SphericalCS spherical coordinate system}.
* Geocentric CRS have their origin at the approximate centre of mass of the earth.
* An {@linkplain #createGeocentricCRS(Map, GeodeticDatum, CartesianCS) alternate method} allows creation of the
* same kind of CRS with Cartesian coordinate system instead than a spherical one.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createSphericalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* <li>One of:<ul>
* <li>{@link #createEllipsoid(Map, double, double, Unit)}</li>
* <li>{@link #createFlattenedSphere(Map, double, double, Unit)}</li>
* </ul></li>
* <li>{@link #createPrimeMeridian(Map, double, Unit)}</li>
* <li>{@link #createGeodeticDatum(Map, Ellipsoid, PrimeMeridian)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultGeocentricCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum geodetic datum to use in created CRS.
* @param cs the spherical coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, SphericalCS)
* @see GeodeticAuthorityFactory#createGeocentricCRS(String)
*/
@Override
public GeocentricCRS createGeocentricCRS(final Map<String,?> properties,
final GeodeticDatum datum, final SphericalCS cs) throws FactoryException
{
final DefaultGeocentricCRS crs;
try {
crs = new DefaultGeocentricCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createGeocentricCRS", crs);
}
/**
* Creates a spherical coordinate system from the given set of axis.
* This coordinate system can be used with geocentric, engineering and derived CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultSphericalCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis (e.g. “Spherical latitude”).
* @param axis1 the second axis (e.g. “Spherical longitude”).
* @param axis2 the third axis (e.g. “Geocentric radius”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultSphericalCS#DefaultSphericalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createSphericalCS(String)
*/
@Override
public SphericalCS createSphericalCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultSphericalCS cs;
try {
cs = new DefaultSphericalCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createSphericalCS", cs);
}
/**
* Creates a geographic coordinate reference system. It can be (<var>latitude</var>, <var>longitude</var>)
* or (<var>longitude</var>, <var>latitude</var>), optionally with an ellipsoidal height.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>One of:<ul>
* <li>{@link #createEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* <li>{@link #createEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* </ul></li>
* <li>One of:<ul>
* <li>{@link #createEllipsoid(Map, double, double, Unit)}</li>
* <li>{@link #createFlattenedSphere(Map, double, double, Unit)}</li>
* </ul></li>
* <li>{@link #createPrimeMeridian(Map, double, Unit)}</li>
* <li>{@link #createGeodeticDatum(Map, Ellipsoid, PrimeMeridian)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultGeographicCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum geodetic datum to use in created CRS.
* @param cs the two- or three-dimensional ellipsoidal coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultGeographicCRS#DefaultGeographicCRS(Map, GeodeticDatum, EllipsoidalCS)
* @see GeodeticAuthorityFactory#createGeographicCRS(String)
*/
@Override
public GeographicCRS createGeographicCRS(final Map<String,?> properties,
final GeodeticDatum datum, final EllipsoidalCS cs) throws FactoryException
{
final DefaultGeographicCRS crs;
try {
crs = new DefaultGeographicCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createGeographicCRS", crs);
}
/**
* Creates geodetic datum from ellipsoid and (optionally) Bursa-Wolf parameters.
* Geodetic datum defines the location and orientation of an ellipsoid that approximates the shape of the earth.
* This datum can be used with geographic, geocentric and engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>One of:<ul>
* <li>{@link #createEllipsoid(Map, double, double, Unit)}</li>
* <li>{@link #createFlattenedSphere(Map, double, double, Unit)}</li>
* </ul></li>
* <li>{@link #createPrimeMeridian(Map, double, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultGeodeticDatum} instance.
*
* @param properties name and other properties to give to the new object.
* @param ellipsoid the ellipsoid to use in new geodetic datum.
* @param primeMeridian the prime meridian to use in new geodetic datum.
* @throws FactoryException if the object creation failed.
*
* @see DefaultGeodeticDatum#DefaultGeodeticDatum(Map, Ellipsoid, PrimeMeridian)
* @see GeodeticAuthorityFactory#createGeodeticDatum(String)
*/
@Override
public GeodeticDatum createGeodeticDatum(final Map<String,?> properties,
final Ellipsoid ellipsoid, final PrimeMeridian primeMeridian) throws FactoryException
{
final DefaultGeodeticDatum datum;
try {
datum = new DefaultGeodeticDatum(complete(properties), ellipsoid, primeMeridian);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createGeodeticDatum", datum);
}
/**
* Creates a prime meridian, relative to Greenwich.
* Defines the origin from which longitude values are determined.
*
* <p>The default implementation creates a {@link DefaultPrimeMeridian} instance.</p>
*
* @param properties name and other properties to give to the new object.
* @param longitude the longitude of prime meridian in supplied angular units East of Greenwich.
* @param angularUnit the angular units of longitude.
* @throws FactoryException if the object creation failed.
*
* @see DefaultPrimeMeridian#DefaultPrimeMeridian(Map, double, Unit)
* @see GeodeticAuthorityFactory#createPrimeMeridian(String)
*/
@Override
public PrimeMeridian createPrimeMeridian(final Map<String,?> properties,
final double longitude, final Unit<Angle> angularUnit) throws FactoryException
{
final DefaultPrimeMeridian meridian;
try {
meridian = new DefaultPrimeMeridian(complete(properties), longitude, angularUnit);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createPrimeMeridian", meridian);
}
/**
* Creates an ellipsoidal coordinate system without ellipsoidal height.
* It can be (<var>latitude</var>, <var>longitude</var>) or (<var>longitude</var>, <var>latitude</var>).
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultEllipsoidalCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis (e.g. “Geodetic latitude”).
* @param axis1 the second axis (e.g. “Geodetic longitude”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultEllipsoidalCS#DefaultEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createEllipsoidalCS(String)
*/
@Override
public EllipsoidalCS createEllipsoidalCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1) throws FactoryException
{
final DefaultEllipsoidalCS cs;
try {
cs = new DefaultEllipsoidalCS(complete(properties), axis0, axis1);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createEllipsoidalCS", cs);
}
/**
* Creates an ellipsoidal coordinate system with ellipsoidal height.
* It can be (<var>latitude</var>, <var>longitude</var>, <var>height</var>)
* or (<var>longitude</var>, <var>latitude</var>, <var>height</var>).
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultEllipsoidalCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis (e.g. “Geodetic latitude”).
* @param axis1 the second axis (e.g. “Geodetic longitude”).
* @param axis2 the third axis (e.g. “Ellipsoidal height”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultEllipsoidalCS#DefaultEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createEllipsoidalCS(String)
*/
@Override
public EllipsoidalCS createEllipsoidalCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultEllipsoidalCS cs;
try {
cs = new DefaultEllipsoidalCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createEllipsoidalCS", cs);
}
/**
* Creates an ellipsoid from semi-axis length values.
* The default implementation creates a {@link DefaultEllipsoid} instance.
*
* @param properties name and other properties to give to the new object.
* @param semiMajorAxis the equatorial radius in supplied linear units.
* @param semiMinorAxis the polar radius in supplied linear units.
* @param unit the linear units of ellipsoid axes.
* @throws FactoryException if the object creation failed.
*
* @see DefaultEllipsoid#createEllipsoid(Map, double, double, Unit)
* @see GeodeticAuthorityFactory#createEllipsoid(String)
*/
@Override
public Ellipsoid createEllipsoid(final Map<String,?> properties,
final double semiMajorAxis, final double semiMinorAxis,
final Unit<Length> unit) throws FactoryException
{
final DefaultEllipsoid ellipsoid;
try {
ellipsoid = DefaultEllipsoid.createEllipsoid(complete(properties), semiMajorAxis, semiMinorAxis, unit);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createEllipsoid", ellipsoid);
}
/**
* Creates an ellipsoid from a major semi-axis length and inverse flattening.
* The default implementation creates a {@link DefaultEllipsoid} instance.
*
* @param properties name and other properties to give to the new object.
* @param semiMajorAxis the equatorial radius in supplied linear units.
* @param inverseFlattening the eccentricity of ellipsoid.
* @param unit the linear units of major axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultEllipsoid#createFlattenedSphere(Map, double, double, Unit)
* @see GeodeticAuthorityFactory#createEllipsoid(String)
*/
@Override
public Ellipsoid createFlattenedSphere(final Map<String,?> properties,
final double semiMajorAxis, final double inverseFlattening,
final Unit<Length> unit) throws FactoryException
{
final DefaultEllipsoid ellipsoid;
try {
ellipsoid = DefaultEllipsoid.createFlattenedSphere(complete(properties), semiMajorAxis, inverseFlattening, unit);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createFlattenedSphere", ellipsoid);
}
/**
* Creates a projected coordinate reference system from a conversion.
* Projected CRS are used to approximate the shape of the earth on a planar surface in such a way
* that the distortion that is inherent to the approximation is controlled and known.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* <li>{@link #createEllipsoidalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)}</li>
* <li>One of:<ul>
* <li>{@link #createEllipsoid(Map, double, double, Unit)}</li>
* <li>{@link #createFlattenedSphere(Map, double, double, Unit)}</li>
* </ul></li>
* <li>{@link #createPrimeMeridian(Map, double, Unit)}</li>
* <li>{@link #createGeodeticDatum(Map, Ellipsoid, PrimeMeridian)}</li>
* <li>{@link #createGeographicCRS(Map, GeodeticDatum, EllipsoidalCS)}</li>
* <li>{@link org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory#createDefiningConversion(Map, OperationMethod, ParameterValueGroup)}</li>
* </ol></div>
*
* The supplied {@code conversion} argument shall <strong>not</strong> includes the operation steps
* for performing {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes unit
* conversions and change of axis order} since those operations will be inferred by this constructor.
*
* <p>The default implementation creates a {@link DefaultProjectedCRS} instance.</p>
*
* @param properties name and other properties to give to the new object.
* @param baseCRS the geographic coordinate reference system to base projection on.
* @param conversion the defining conversion from a {@linkplain org.apache.sis.referencing.cs.AxesConvention#NORMALIZED
* normalized} base to a normalized derived CRS.
* @param derivedCS the coordinate system for the projected CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultProjectedCRS#DefaultProjectedCRS(Map, GeographicCRS, Conversion, CartesianCS)
* @see GeodeticAuthorityFactory#createProjectedCRS(String)
*/
@Override
public ProjectedCRS createProjectedCRS(final Map<String,?> properties,
final GeographicCRS baseCRS, final Conversion conversion,
final CartesianCS derivedCS) throws FactoryException
{
final DefaultProjectedCRS crs;
try {
crs = new DefaultProjectedCRS(complete(properties), baseCRS, conversion, derivedCS);
} catch (IllegalArgumentException exception) {
final Throwable cause = exception.getCause();
if (cause instanceof FactoryException) {
throw (FactoryException) cause; // Must be propagated for allowing caller to catch NoSuchIdentifierException.
}
throw new InvalidGeodeticParameterException(exception);
}
return unique("createProjectedCRS", crs);
}
/**
* Creates a two-dimensional Cartesian coordinate system from the given pair of axis.
* This coordinate system can be used with projected, engineering and derived CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultCartesianCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis (e.g. “Easting”).
* @param axis1 the second axis (e.g. “Northing”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultCartesianCS#DefaultCartesianCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createCartesianCS(String)
*/
@Override
public CartesianCS createCartesianCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1) throws FactoryException
{
final DefaultCartesianCS cs;
try {
cs = new DefaultCartesianCS(complete(properties), axis0, axis1);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createCartesianCS", cs);
}
/**
* Creates a derived coordinate reference system from a conversion.
* The derived CRS returned by this method may also implement the {@link GeodeticCRS}, {@link VerticalCRS},
* {@link TemporalCRS} or {@link EngineeringCRS} interface depending on the type of the base CRS and the
* coordinate system.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>A {@code createFooCS(…)} method for Cartesian, spherical, ellipsoidal, vertical, temporal, linear, affine, polar, cylindrical or user-defined CS.</li>
* <li>An other {@code createFooCRS(…)} method for geocentric, geographic, vertical, temporal or engineering CRS.</li>
* <li>{@link org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory#createDefiningConversion(Map, OperationMethod, ParameterValueGroup)}</li>
* </ol></div>
*
* The supplied {@code conversion} argument shall <strong>not</strong> includes the operation steps
* for performing {@linkplain org.apache.sis.referencing.cs.CoordinateSystems#swapAndScaleAxes unit
* conversions and change of axis order} since those operations will be inferred by this constructor.
*
* <p>The default implementation creates a {@link DefaultDerivedCRS} instance.</p>
*
* @param properties name and other properties to give to the new object.
* @param baseCRS the coordinate reference system to base projection on. Shall be an instance of {@link SingleCRS}.
* @param conversion the defining conversion from a {@linkplain org.apache.sis.referencing.cs.AxesConvention#NORMALIZED
* normalized} base to a normalized derived CRS.
* @param derivedCS the coordinate system for the derived CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultDerivedCRS#create(Map, SingleCRS, Conversion, CoordinateSystem)
* @see GeodeticAuthorityFactory#createDerivedCRS(String)
*/
@Override
public DerivedCRS createDerivedCRS(final Map<String,?> properties,
final CoordinateReferenceSystem baseCRS, final Conversion conversion,
final CoordinateSystem derivedCS) throws FactoryException
{
ArgumentChecks.ensureCanCast("baseCRS", SingleCRS.class, baseCRS);
final DefaultDerivedCRS crs;
try {
crs = DefaultDerivedCRS.create(complete(properties), (SingleCRS) baseCRS, conversion, derivedCS);
} catch (IllegalArgumentException exception) {
final Throwable cause = exception.getCause();
if (cause instanceof FactoryException) {
throw (FactoryException) cause; // Must be propagated for allowing caller to catch NoSuchIdentifierException.
}
throw new InvalidGeodeticParameterException(exception);
}
return unique("createDerivedCRS", crs);
}
/**
* Creates a vertical coordinate reference system.
* Vertical CRSs make use of the direction of gravity to define the concept of height or depth,
* but the relationship with gravity may not be straightforward.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createVerticalCS(Map, CoordinateSystemAxis)}</li>
* <li>{@link #createVerticalDatum(Map, VerticalDatumType)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultVerticalCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum the vertical datum to use in created CRS.
* @param cs the vertical coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultVerticalCRS#DefaultVerticalCRS(Map, VerticalDatum, VerticalCS)
* @see GeodeticAuthorityFactory#createVerticalCRS(String)
*/
@Override
public VerticalCRS createVerticalCRS(final Map<String,?> properties,
final VerticalDatum datum, final VerticalCS cs) throws FactoryException
{
final DefaultVerticalCRS crs;
try {
crs = new DefaultVerticalCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createVerticalCRS", crs);
}
/**
* Creates a vertical datum from an enumerated type value.
* The default implementation creates a {@link DefaultVerticalDatum} instance.
*
* @param properties name and other properties to give to the new object.
* @param type the type of this vertical datum (often geoidal).
* @throws FactoryException if the object creation failed.
*
* @see DefaultVerticalDatum#DefaultVerticalDatum(Map, VerticalDatumType)
* @see GeodeticAuthorityFactory#createVerticalDatum(String)
*/
@Override
public VerticalDatum createVerticalDatum(final Map<String,?> properties,
final VerticalDatumType type) throws FactoryException
{
final DefaultVerticalDatum datum;
try {
datum = new DefaultVerticalDatum(complete(properties), type);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createVerticalDatum", datum);
}
/**
* Creates a vertical coordinate system.
* This coordinate system can be used with vertical and derived CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultVerticalCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis the single axis (e.g. “height” or “depth”).
* @throws FactoryException if the object creation failed.
*
* @see DefaultVerticalCS#DefaultVerticalCS(Map, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createVerticalCS(String)
*/
@Override
public VerticalCS createVerticalCS(final Map<String,?> properties,
final CoordinateSystemAxis axis) throws FactoryException
{
final DefaultVerticalCS cs;
try {
cs = new DefaultVerticalCS(complete(properties), axis);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createVerticalCS", cs);
}
/**
* Creates a temporal coordinate reference system.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createTimeCS(Map, CoordinateSystemAxis)}</li>
* <li>{@link #createTemporalDatum(Map, Date)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultTemporalCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum the temporal datum to use in created CRS.
* @param cs the temporal coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultTemporalCRS#DefaultTemporalCRS(Map, TemporalDatum, TimeCS)
* @see GeodeticAuthorityFactory#createTemporalCRS(String)
*/
@Override
public TemporalCRS createTemporalCRS(final Map<String,?> properties,
final TemporalDatum datum, final TimeCS cs) throws FactoryException
{
final DefaultTemporalCRS crs;
try {
crs = new DefaultTemporalCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createTemporalCRS", crs);
}
/**
* Creates a temporal datum from an enumerated type value.
* The default implementation creates a {@link DefaultTemporalDatum} instance.
*
* @param properties name and other properties to give to the new object.
* @param origin the date and time origin of this temporal datum.
* @throws FactoryException if the object creation failed.
*
* @see DefaultTemporalDatum#DefaultTemporalDatum(Map, Date)
* @see GeodeticAuthorityFactory#createTemporalDatum(String)
*/
@Override
public TemporalDatum createTemporalDatum(final Map<String,?> properties,
final Date origin) throws FactoryException
{
final DefaultTemporalDatum datum;
try {
datum = new DefaultTemporalDatum(complete(properties), origin);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createTemporalDatum", datum);
}
/**
* Creates a temporal coordinate system.
* This coordinate system can be used with temporal and derived CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultTimeCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis the single axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultTimeCS#DefaultTimeCS(Map, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createTimeCS(String)
*/
@Override
public TimeCS createTimeCS(final Map<String,?> properties,
final CoordinateSystemAxis axis) throws FactoryException
{
final DefaultTimeCS cs;
try {
cs = new DefaultTimeCS(complete(properties), axis);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createTimeCS", cs);
}
/**
* Creates a parametric coordinate reference system.
* Parametric CRS can be used for physical properties or functions that vary monotonically with height.
* A typical example is the pressure in meteorological applications.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>{@link #createParametricCS(Map, CoordinateSystemAxis)}</li>
* <li>{@link #createParametricDatum(Map)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultParametricCRS} instance.
*
* <div class="warning"><b>Warning:</b> in a future SIS version, the parameter types may be changed to
* {@code org.opengis.referencing.datum.ParametricDatum} and {@code org.opengis.referencing.cs.ParametricCS},
* and the return type may be changed to {@code org.opengis.referencing.crs.ParametricCRS}.
* Those change are pending GeoAPI revision.</div>
*
* @param properties name and other properties to give to the new object.
* @param datum the parametric datum to use in created CRS.
* @param cs the parametric coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultParametricCRS#DefaultParametricCRS(Map, DefaultParametricDatum, DefaultParametricCS)
* @see GeodeticAuthorityFactory#createParametricCRS(String)
*/
public DefaultParametricCRS createParametricCRS(final Map<String,?> properties,
final DefaultParametricDatum datum, final DefaultParametricCS cs) throws FactoryException
{
final DefaultParametricCRS crs;
try {
crs = new DefaultParametricCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createParametricCRS", crs);
}
/**
* Creates a parametric datum.
* The default implementation creates a {@link DefaultParametricDatum} instance.
*
* <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
* to {@code org.opengis.referencing.datum.ParametricDatum}. This change is pending GeoAPI revision.</div>
*
* @param properties name and other properties to give to the new object.
* @throws FactoryException if the object creation failed.
*
* @see DefaultParametricDatum#DefaultParametricDatum(Map)
* @see GeodeticAuthorityFactory#createParametricDatum(String)
*/
public DefaultParametricDatum createParametricDatum(final Map<String,?> properties)
throws FactoryException
{
final DefaultParametricDatum datum;
try {
datum = new DefaultParametricDatum(complete(properties));
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createParametricDatum", datum);
}
/**
* Creates a parametric coordinate system.
* This coordinate system can be used only with parametric CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultParametricCS} instance.
*
* <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
* to {@code org.opengis.referencing.cs.ParametricCS}. This change is pending GeoAPI revision.</div>
*
* @param properties name and other properties to give to the new object.
* @param axis the single axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultParametricCS#DefaultParametricCS(Map, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createParametricCS(String)
*/
public DefaultParametricCS createParametricCS(Map<String, ?> properties, CoordinateSystemAxis axis) throws FactoryException {
final DefaultParametricCS cs;
try {
cs = new DefaultParametricCS(complete(properties), axis);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createParametricCS", cs);
}
/**
* Creates a compound coordinate reference system from an ordered list of CRS components.
* Apache SIS is permissive on the order of components that can be used in a compound CRS.
* However for better inter-operability, users are encouraged to follow the order mandated by ISO 19162:
*
* <ol>
* <li>A mandatory horizontal CRS (only one of two-dimensional {@code GeographicCRS} or {@code ProjectedCRS} or {@code EngineeringCRS}).</li>
* <li>Optionally followed by a {@code VerticalCRS} or a {@code ParametricCRS} (but not both).</li>
* <li>Optionally followed by a {@code TemporalCRS}.</li>
* </ol>
*
* The default implementation creates a {@link DefaultCompoundCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param components the sequence of coordinate reference systems making the compound CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultCompoundCRS#DefaultCompoundCRS(Map, CoordinateReferenceSystem...)
* @see GeodeticAuthorityFactory#createCompoundCRS(String)
* @see org.apache.sis.referencing.CRS#compound(CoordinateReferenceSystem...)
*/
@Override
public CompoundCRS createCompoundCRS(final Map<String,?> properties,
final CoordinateReferenceSystem... components) throws FactoryException
{
final DefaultCompoundCRS crs;
try {
crs = new DefaultCompoundCRS(complete(properties), components);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createCompoundCRS", crs);
}
/**
* Creates an image coordinate reference system.
* The default implementation creates a {@link DefaultImageCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum the image datum to use in created CRS.
* @param cs the Cartesian or oblique Cartesian coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultImageCRS#DefaultImageCRS(Map, ImageDatum, AffineCS)
* @see GeodeticAuthorityFactory#createImageCRS(String)
*/
@Override
public ImageCRS createImageCRS(final Map<String,?> properties,
final ImageDatum datum, final AffineCS cs) throws FactoryException
{
final DefaultImageCRS crs;
try {
crs = new DefaultImageCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createImageCRS", crs);
}
/**
* Creates an image datum.
* The default implementation creates a {@link DefaultImageDatum} instance.
*
* @param properties Name and other properties to give to the new object.
* @param pixelInCell Specification of the way the image grid is associated with the image data attributes.
* @throws FactoryException if the object creation failed.
*
* @see DefaultImageDatum#DefaultImageDatum(Map, PixelInCell)
* @see GeodeticAuthorityFactory#createImageDatum(String)
*/
@Override
public ImageDatum createImageDatum(final Map<String,?> properties,
final PixelInCell pixelInCell) throws FactoryException
{
final DefaultImageDatum datum;
try {
datum = new DefaultImageDatum(complete(properties), pixelInCell);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createImageDatum", datum);
}
/**
* Creates a two-dimensional affine coordinate system from the given pair of axis.
* This coordinate system can be used with image and engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultAffineCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultAffineCS#DefaultAffineCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
*/
@Override
public AffineCS createAffineCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1) throws FactoryException
{
final DefaultAffineCS cs;
try {
cs = new DefaultAffineCS(complete(properties), axis0, axis1);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createAffineCS", cs);
}
/**
* Creates a engineering coordinate reference system.
* Engineering CRS can be divided into two broad categories:
*
* <ul>
* <li>earth-fixed systems applied to engineering activities on or near the surface of the earth;</li>
* <li>CRSs on moving platforms such as road vehicles, vessels, aircraft, or spacecraft.</li>
* </ul>
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* <li>A {@code createFooCS(…)} method for Cartesian, spherical, linear, affine, polar, cylindrical or user-defined CS.</li>
* <li>{@link #createEngineeringDatum(Map)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultEngineeringCRS} instance.
*
* @param properties name and other properties to give to the new object.
* @param datum the engineering datum to use in created CRS.
* @param cs the coordinate system for the created CRS.
* @throws FactoryException if the object creation failed.
*
* @see DefaultEngineeringCRS#DefaultEngineeringCRS(Map, EngineeringDatum, CoordinateSystem)
* @see GeodeticAuthorityFactory#createEngineeringCRS(String)
*/
@Override
public EngineeringCRS createEngineeringCRS(final Map<String,?> properties,
final EngineeringDatum datum, final CoordinateSystem cs) throws FactoryException
{
final DefaultEngineeringCRS crs;
try {
crs = new DefaultEngineeringCRS(complete(properties), datum, cs);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createEngineeringCRS", crs);
}
/**
* Creates an engineering datum.
* The default implementation creates a {@link DefaultEngineeringDatum} instance.
*
* @param properties name and other properties to give to the new object.
* @throws FactoryException if the object creation failed.
*
* @see DefaultEngineeringDatum#DefaultEngineeringDatum(Map)
* @see GeodeticAuthorityFactory#createEngineeringDatum(String)
*/
@Override
public EngineeringDatum createEngineeringDatum(final Map<String,?> properties)
throws FactoryException
{
final DefaultEngineeringDatum datum;
try {
datum = new DefaultEngineeringDatum(complete(properties));
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createEngineeringDatum", datum);
}
/**
* Creates a three-dimensional affine coordinate system from the given set of axis.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultAffineCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @param axis2 the third axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultAffineCS#DefaultAffineCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
*/
@Override
public AffineCS createAffineCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultAffineCS cs;
try {
cs = new DefaultAffineCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createAffineCS", cs);
}
/**
* Creates a cylindrical coordinate system from the given set of axis.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultCylindricalCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @param axis2 the third axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultCylindricalCS#DefaultCylindricalCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createCylindricalCS(String)
*/
@Override
public CylindricalCS createCylindricalCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultCylindricalCS cs;
try {
cs = new DefaultCylindricalCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createCylindricalCS", cs);
}
/**
* Creates a polar coordinate system from the given pair of axis.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultPolarCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultPolarCS#DefaultPolarCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
* @see GeodeticAuthorityFactory#createPolarCS(String)
*/
@Override
public PolarCS createPolarCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1) throws FactoryException
{
final DefaultPolarCS cs;
try {
cs = new DefaultPolarCS(complete(properties), axis0, axis1);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createPolarCS", cs);
}
/**
* Creates a linear coordinate system.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultLinearCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis the single axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultLinearCS#DefaultLinearCS(Map, CoordinateSystemAxis)
*/
@Override
public LinearCS createLinearCS(final Map<String,?> properties,
final CoordinateSystemAxis axis) throws FactoryException
{
final DefaultLinearCS cs;
try {
cs = new DefaultLinearCS(complete(properties), axis);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createLinearCS", cs);
}
/**
* Creates a two-dimensional user defined coordinate system from the given pair of axis.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultUserDefinedCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultUserDefinedCS#DefaultUserDefinedCS(Map, CoordinateSystemAxis, CoordinateSystemAxis)
*/
@Override
public UserDefinedCS createUserDefinedCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1) throws FactoryException
{
final DefaultUserDefinedCS cs;
try {
cs = new DefaultUserDefinedCS(complete(properties), axis0, axis1);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createUserDefinedCS", cs);
}
/**
* Creates a three-dimensional user defined coordinate system from the given set of axis.
* This coordinate system can be used with engineering CRS.
*
* <div class="note"><b>Dependencies:</b>
* the components needed by this method can be created by the following methods:
* <ol>
* <li>{@link #createCoordinateSystemAxis(Map, String, AxisDirection, Unit)}</li>
* </ol></div>
*
* The default implementation creates a {@link DefaultUserDefinedCS} instance.
*
* @param properties name and other properties to give to the new object.
* @param axis0 the first axis.
* @param axis1 the second axis.
* @param axis2 the third axis.
* @throws FactoryException if the object creation failed.
*
* @see DefaultUserDefinedCS#DefaultUserDefinedCS(Map, CoordinateSystemAxis, CoordinateSystemAxis, CoordinateSystemAxis)
*/
@Override
public UserDefinedCS createUserDefinedCS(final Map<String,?> properties,
final CoordinateSystemAxis axis0,
final CoordinateSystemAxis axis1,
final CoordinateSystemAxis axis2) throws FactoryException
{
final DefaultUserDefinedCS cs;
try {
cs = new DefaultUserDefinedCS(complete(properties), axis0, axis1, axis2);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createUserDefinedCS", cs);
}
/**
* Creates a coordinate system axis from an abbreviation and a unit.
* Note that the axis name is constrained by ISO 19111 depending on the coordinate reference system type.
* See the GeoAPI {@link CoordinateSystemAxis} javadoc for more information.
*
* <p>The default implementation creates a {@link DefaultCoordinateSystemAxis} instance.</p>
*
* @param properties name and other properties to give to the new object.
* @param abbreviation the coordinate axis abbreviation.
* @param direction the axis direction.
* @param unit the coordinate axis unit.
* @throws FactoryException if the object creation failed.
*
* @see DefaultCoordinateSystemAxis#DefaultCoordinateSystemAxis(Map, String, AxisDirection, Unit)
* @see GeodeticAuthorityFactory#createCoordinateSystemAxis(String)
*/
@Override
public CoordinateSystemAxis createCoordinateSystemAxis(final Map<String,?> properties,
final String abbreviation, final AxisDirection direction,
final Unit<?> unit) throws FactoryException
{
final DefaultCoordinateSystemAxis axis;
try {
axis = new DefaultCoordinateSystemAxis(complete(properties), abbreviation, direction, unit);
} catch (IllegalArgumentException exception) {
throw new InvalidGeodeticParameterException(exception);
}
return unique("createCoordinateSystemAxis", axis);
}
/**
* Creates a coordinate reference system object from a XML string.
* Note that the given argument is the XML document itself,
* <strong>not</strong> a URL to a XML document.
*
* <p>The default implementation delegates to {@link XML#unmarshal(String)}</p>
*
* @param xml coordinate reference system encoded in XML format.
* @throws FactoryException if the object creation failed.
*
* @see XML#unmarshal(String)
* @see org.apache.sis.referencing.CRS#fromXML(String)
*/
@Override
public CoordinateReferenceSystem createFromXML(final String xml) throws FactoryException {
final Object object;
try {
object = XML.unmarshal(xml);
} catch (JAXBException e) {
/*
* The JAXB exception if often a wrapper around other exceptions, sometime InvocationTargetException.
* The exception cause is called "linked exception" by JAXB, presumably because it predates standard
* chained exception mechanism introduced in Java 1.4. The JAXB linked exceptions do not propagate the
* error message, so we have to take it from the cause, skipping InvocationTargetException since they
* are wrapper for other causes. If the cause is a JAXBException, we will keep it as the declared cause
* for simplifying the stack trace.
*/
String message = e.getLocalizedMessage();
Throwable cause = e.getCause();
if (cause instanceof Exception) {
cause = Exceptions.unwrap((Exception) cause);
if (cause instanceof JAXBException) {
e = (JAXBException) cause;
}
if (message == null) {
message = cause.getLocalizedMessage();
}
}
throw new FactoryException(message, e);
}
if (object instanceof CoordinateReferenceSystem) {
return (CoordinateReferenceSystem) object;
} else {
throw new FactoryException(Errors.getResources(defaultProperties).getString(
Errors.Keys.IllegalClass_2, CoordinateReferenceSystem.class, object.getClass()));
}
}
/**
* Creates a Coordinate Reference System object from a <cite>Well Known Text</cite> (WKT).
* This method understands both version 1 (a.k.a. OGC 01-009) and version 2 (a.k.a. ISO 19162)
* of the WKT format.
*
* <div class="note"><b>Example:</b> below is a slightly simplified WKT 2 string for a Mercator projection.
* For making this example smaller, some optional {@code UNIT[…]} and {@code ORDER[…]} elements have been omitted.
*
* {@preformat wkt
* ProjectedCRS["SIRGAS 2000 / Brazil Mercator",
* BaseGeodCRS["SIRGAS 2000",
* Datum["Sistema de Referencia Geocentrico para las Americas 2000",
* Ellipsoid["GRS 1980", 6378137, 298.257222101]]],
* Conversion["Petrobras Mercator",
* Method["Mercator (variant B)", Id["EPSG",9805]],
* Parameter["Latitude of 1st standard parallel", -2],
* Parameter["Longitude of natural origin", -43],
* Parameter["False easting", 5000000],
* Parameter["False northing", 10000000]],
* CS[cartesian,2],
* Axis["easting (E)", east],
* Axis["northing (N)", north],
* LengthUnit["metre", 1],
* Id["EPSG",5641]]
* }
* </div>
*
* If the given text contains non-fatal anomalies
* (unknown or unsupported WKT elements, inconsistent unit definitions, unparsable axis abbreviations, <i>etc.</i>),
* warnings may be reported in a {@linkplain java.util.logging.Logger logger} named {@code "org.apache.sis.io.wkt"}.
* However this parser does not verify if the overall parsed object matches the EPSG (or other authority) definition,
* since this geodetic object factory is not an {@linkplain GeodeticAuthorityFactory authority factory}.
* For such verification, see the {@link org.apache.sis.referencing.CRS#fromWKT(String)} convenience method.
*
* <div class="section">Usage and performance considerations</div>
* The default implementation uses a shared instance of {@link org.apache.sis.io.wkt.WKTFormat}
* with the addition of thread-safety. This is okay for occasional use,
* but is sub-optimal if this method is extensively used in a multi-thread environment.
* Furthermore this method offers no control on the WKT {@linkplain org.apache.sis.io.wkt.Convention conventions}
* in use and on the handling of {@linkplain org.apache.sis.io.wkt.Warnings warnings}.
* Applications which need to parse a large amount of WKT strings should consider to use
* the {@link org.apache.sis.io.wkt.WKTFormat} class instead than this method.
*
* @param text coordinate system encoded in Well-Known Text format (version 1 or 2).
* @throws FactoryException if the object creation failed.
*
* @see org.apache.sis.io.wkt
* @see org.apache.sis.referencing.CRS#fromWKT(String)
* @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html">WKT 2 specification</a>
* @see <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Legacy WKT 1</a>
*/
@Override
public CoordinateReferenceSystem createFromWKT(final String text) throws FactoryException {
Parser p = parser.getAndSet(null);
if (p == null) try {
Constructor<? extends Parser> c = parserConstructor;
if (c == null) {
c = Class.forName("org.apache.sis.io.wkt.GeodeticObjectParser").asSubclass(Parser.class)
.getConstructor(Map.class, ObjectFactory.class, MathTransformFactory.class);
final Constructor<?> cp = c; // For allowing use in inner class or lambda expression.
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
cp.setAccessible(true);
return null;
});
parserConstructor = c;
}
p = c.newInstance(defaultProperties, this, getMathTransformFactory());
} catch (ReflectiveOperationException e) {
throw new FactoryException(e);
}
final Object object;
try {
object = p.createFromWKT(text);
} catch (FactoryException e) {
/*
* In the case of map projection, the parsing may fail because a projection parameter is not known to SIS.
* If this happen, replace the generic exception thrown be the parser (which is FactoryException) by a
* more specific one. Note that InvalidGeodeticParameterException is defined only in this sis-referencing
* module, so we could not throw it from the sis-metadata module that contain the parser.
*/
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof ParameterNotFoundException) {
throw new InvalidGeodeticParameterException(e.getLocalizedMessage(), cause);
}
cause = cause.getCause();
}
throw e;
}
parser.set(p);
if (object instanceof CoordinateReferenceSystem) {
return (CoordinateReferenceSystem) object;
} else {
throw new FactoryException(Errors.getResources(defaultProperties).getString(
Errors.Keys.IllegalClass_2, CoordinateReferenceSystem.class, object.getClass()));
}
}
}