| /* |
| * 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.crs; |
| |
| import java.util.Map; |
| import jakarta.xml.bind.annotation.XmlType; |
| import jakarta.xml.bind.annotation.XmlElement; |
| import jakarta.xml.bind.annotation.XmlRootElement; |
| import jakarta.xml.bind.annotation.XmlTransient; |
| import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
| import org.opengis.referencing.datum.Datum; |
| import org.opengis.referencing.datum.GeodeticDatum; |
| import org.opengis.referencing.datum.VerticalDatum; |
| import org.opengis.referencing.datum.TemporalDatum; |
| import org.opengis.referencing.datum.EngineeringDatum; |
| import org.opengis.referencing.crs.SingleCRS; |
| import org.opengis.referencing.crs.DerivedCRS; |
| import org.opengis.referencing.crs.GeodeticCRS; |
| import org.opengis.referencing.crs.VerticalCRS; |
| import org.opengis.referencing.crs.TemporalCRS; |
| import org.opengis.referencing.crs.ProjectedCRS; |
| import org.opengis.referencing.crs.EngineeringCRS; |
| import org.opengis.referencing.crs.CoordinateReferenceSystem; |
| import org.opengis.referencing.cs.CoordinateSystem; |
| import org.opengis.referencing.cs.VerticalCS; |
| import org.opengis.referencing.cs.TimeCS; |
| import org.opengis.referencing.operation.Conversion; |
| import org.opengis.referencing.operation.OperationMethod; |
| import org.opengis.referencing.operation.MathTransform; |
| import org.opengis.referencing.operation.NoninvertibleTransformException; |
| import org.opengis.geometry.MismatchedDimensionException; |
| import org.apache.sis.referencing.AbstractIdentifiedObject; |
| import org.apache.sis.referencing.operation.DefaultConversion; |
| import org.apache.sis.referencing.cs.AbstractCS; |
| import org.apache.sis.referencing.cs.AxesConvention; |
| import org.apache.sis.referencing.cs.CoordinateSystems; |
| import org.apache.sis.xml.bind.referencing.SC_SingleCRS; |
| import org.apache.sis.xml.bind.referencing.SC_DerivedCRSType; |
| import org.apache.sis.xml.bind.referencing.CS_CoordinateSystem; |
| import org.apache.sis.referencing.privy.ReferencingUtilities; |
| import org.apache.sis.referencing.privy.WKTUtilities; |
| import org.apache.sis.referencing.privy.WKTKeywords; |
| import org.apache.sis.io.wkt.Convention; |
| import org.apache.sis.io.wkt.Formatter; |
| import org.apache.sis.util.ComparisonMode; |
| |
| // Specific to the geoapi-3.1 and geoapi-4.0 branches: |
| import org.opengis.referencing.datum.ParametricDatum; |
| import org.opengis.referencing.crs.ParametricCRS; |
| import org.opengis.referencing.cs.ParametricCS; |
| |
| |
| /** |
| * A coordinate reference system that is defined by its coordinate {@linkplain DefaultConversion conversion} |
| * from another CRS (not by a {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}). {@code DerivedCRS} |
| * cannot be {@linkplain DefaultProjectedCRS projected CRS} themselves, but may be derived from a projected CRS |
| * (for example in order to use a {@linkplain org.apache.sis.referencing.cs.DefaultPolarCS polar coordinate system}). |
| * |
| * <p>A {@code DerivedCRS} instance may also implement one of the interfaces listed below, |
| * provided that the conditions in the right column are met (derived from ISO 19162):</p> |
| * |
| * <table class="sis"> |
| * <caption>Derived CRS types</caption> |
| * <tr><th>Type</th> <th>Conditions</th></tr> |
| * <tr><td>{@link GeodeticCRS}</td> <td>Base CRS is also a {@code GeodeticCRS} and is associated to the same type of coordinate system.</td></tr> |
| * <tr><td>{@link VerticalCRS}</td> <td>Base CRS is also a {@code VerticalCRS} and coordinate system is a {@code VerticalCS}.</td></tr> |
| * <tr><td>{@link TemporalCRS}</td> <td>Base CRS is also a {@code TemporalCRS} and coordinate system is a {@code TimeCS}.</td></tr> |
| * <tr><td>{@link ParametricCRS}</td> <td>Base CRS is also a {@code ParametricCRS} and coordinate system is a {@code ParametricCS}.</td></tr> |
| * <tr><td>{@link EngineeringCRS}</td> <td>Base CRS is a {@code GeodeticCRS}, {@code ProjectedCRS} or {@code EngineeringCRS}.</td></tr> |
| * </table> |
| * |
| * Those specialized subclasses can be inferred automatically by the {@link #create create(…)} static method. |
| * Alternatively, users can create their own {@code DefaultDerivedCRS} subclass implementing the desired interface. |
| * |
| * <h2>Immutability and thread safety</h2> |
| * This base class is immutable and thus thread-safe if the property <em>values</em> (not necessarily the map itself) |
| * given to the constructor are also immutable. Most SIS subclasses and related classes are immutable under similar |
| * conditions. This means that unless otherwise noted in the javadoc, {@code GeneralDerivedCRS} instances created |
| * using only SIS factories and static constants can be shared by many objects and passed between threads without |
| * synchronization. |
| * |
| * @author Martin Desruisseaux (IRD, Geomatys) |
| * @author Johann Sorel (Geomatys) |
| * @version 1.5 |
| * |
| * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createDerivedCRS(String) |
| * |
| * @since 0.6 |
| */ |
| @XmlType(name="DerivedCRSType", propOrder = { |
| "baseCRS", |
| "type", |
| "coordinateSystem" |
| }) |
| @XmlRootElement(name = "DerivedCRS") |
| public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements DerivedCRS { |
| /** |
| * Serial number for inter-operability with different versions. |
| */ |
| private static final long serialVersionUID = -8149602276542469876L; |
| |
| /** |
| * Creates a derived CRS from a defining conversion. |
| * The properties given in argument follow the same rules as for the |
| * {@linkplain AbstractCRS#AbstractCRS(Map, CoordinateSystem) super-class constructor}. |
| * The following table is a reminder of main (not all) properties: |
| * |
| * <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 org.opengis.metadata.Identifier} or {@link String}</td> |
| * <td>{@link #getName()}</td> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#ALIAS_KEY}</td> |
| * <td>{@link org.opengis.util.GenericName} or {@link CharSequence} (optionally as array)</td> |
| * <td>{@link #getAlias()}</td> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> |
| * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> |
| * <td>{@link #getIdentifiers()}</td> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#DOMAINS_KEY}</td> |
| * <td>{@link org.opengis.referencing.ObjectDomain} (optionally as array)</td> |
| * <td>{@link #getDomains()}</td> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#REMARKS_KEY}</td> |
| * <td>{@link org.opengis.util.InternationalString} or {@link String}</td> |
| * <td>{@link #getRemarks()}</td> |
| * </tr> |
| * </table> |
| * |
| * 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. |
| * |
| * @param properties the properties to be given to the new derived CRS object. |
| * @param baseCRS coordinate reference system to base the derived CRS on. |
| * @param conversion the defining conversion from a {@linkplain AxesConvention#NORMALIZED normalized} |
| * base to a normalized derived CRS. |
| * @param derivedCS the coordinate system for the derived CRS. The number of axes must match |
| * the target dimension of the {@code baseToDerived} transform. |
| * @throws MismatchedDimensionException if the source and target dimensions of {@code baseToDerived} |
| * do not match the dimensions of {@code base} and {@code derivedCS} respectively. |
| * |
| * @see #create(Map, SingleCRS, Conversion, CoordinateSystem) |
| */ |
| protected DefaultDerivedCRS(final Map<String,?> properties, |
| final SingleCRS baseCRS, |
| final Conversion conversion, |
| final CoordinateSystem derivedCS) |
| throws MismatchedDimensionException |
| { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** |
| * Creates a new CRS derived from the specified one, but with different axis order or unit. |
| * This is for implementing the {@link #createSameType(AbstractCS)} method only. |
| */ |
| DefaultDerivedCRS(final DefaultDerivedCRS original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** |
| * Creates a derived CRS from a math transform. The given {@code MathTransform} shall transform coordinate |
| * values specifically from the {@code baseCRS} to {@code this} CRS (optionally with an interpolation CRS); |
| * there is no consideration about <q>normalized CRS</q> in this constructor. |
| * |
| * <h4>Conversion properties</h4> |
| * The {@code properties} map given in argument can contain any entries documented in the |
| * {@linkplain #DefaultDerivedCRS(Map, SingleCRS, Conversion, CoordinateSystem) above constructor}, |
| * together with any entries documented by the {@linkplain DefaultConversion#DefaultConversion(Map, |
| * CoordinateReferenceSystem, CoordinateReferenceSystem, CoordinateReferenceSystem, OperationMethod, MathTransform) |
| * conversion constructor} provided that the {@code Conversion} entry keys are prefixed by {@code "conversion."}. |
| * In particular, the two first properties listed below are mandatory: |
| * |
| * <table class="sis"> |
| * <caption>Mandatory properties and some optional properties</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 org.opengis.metadata.Identifier} or {@link String}</td> |
| * <td>{@code this.getName()}</td> |
| * </tr><tr> |
| * <td>"conversion.name"</td> |
| * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> |
| * <td>{@code conversionFromBase.getName()}</td> |
| * </tr><tr> |
| * <th colspan="3" class="hsep">Optional properties (non exhaustive list)</th> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> |
| * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> |
| * <td>{@code this.getIdentifiers()}</td> |
| * </tr><tr> |
| * <td>{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}</td> |
| * <td>{@link org.opengis.metadata.extent.Extent}</td> |
| * <td>{@code conversionFromBase.getDomainOfValidity()}</td> |
| * </tr> |
| * </table> |
| * |
| * @param properties the properties to be given to the {@link DefaultConversion} object |
| * (with keys prefixed by {@code "conversion."}) and to the new derived CRS object. |
| * @param baseCRS coordinate reference system to base the derived CRS on. |
| * @param interpolationCRS the CRS of additional coordinates needed for the operation, or {@code null} if none. |
| * @param method the coordinate operation method (mandatory in all cases). |
| * @param baseToDerived transform from positions in the base CRS to positions in this target CRS. |
| * @param derivedCS the coordinate system for the derived CRS. |
| * @throws IllegalArgumentException if at least one argument has an incompatible number of dimensions. |
| * |
| * @see #create(Map, SingleCRS, CoordinateReferenceSystem, OperationMethod, MathTransform, CoordinateSystem) |
| */ |
| protected DefaultDerivedCRS(final Map<String,?> properties, |
| final SingleCRS baseCRS, |
| final CoordinateReferenceSystem interpolationCRS, |
| final OperationMethod method, |
| final MathTransform baseToDerived, |
| final CoordinateSystem derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** |
| * Constructs a new coordinate reference system with the same values as the specified one. |
| * This copy constructor provides a way to convert an arbitrary implementation into a SIS one |
| * or a user-defined one (as a subclass), usually in order to leverage some implementation-specific API. |
| * |
| * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> |
| * |
| * @param crs the coordinate reference system to copy. |
| * |
| * @see #castOrCopy(DerivedCRS) |
| */ |
| protected DefaultDerivedCRS(final DerivedCRS crs) { |
| super(crs); |
| } |
| |
| /** |
| * Creates a derived CRS from a defining conversion and a type inferred from the given arguments. |
| * This method expects the same arguments and performs the same work as the |
| * {@linkplain #DefaultDerivedCRS(Map, SingleCRS, Conversion, CoordinateSystem) above constructor}, |
| * except that the {@code DerivedCRS} instance returned by this method may additionally implement |
| * the {@link GeodeticCRS}, {@link VerticalCRS}, {@link TemporalCRS}, {@link ParametricCRS} or |
| * {@link EngineeringCRS} interface. |
| * See the class javadoc for more information. |
| * |
| * @param properties the properties to be given to the new derived CRS object. |
| * @param baseCRS coordinate reference system to base the derived CRS on. |
| * @param conversion the defining conversion from a normalized base to a normalized derived CRS. |
| * @param derivedCS the coordinate system for the derived CRS. The number of axes |
| * must match the target dimension of the {@code baseToDerived} transform. |
| * @return the newly created derived CRS, potentially implementing an additional CRS interface. |
| * @throws MismatchedDimensionException if the source and target dimensions of {@code baseToDerived} |
| * do not match the dimensions of {@code base} and {@code derivedCS} respectively. |
| * |
| * @see #DefaultDerivedCRS(Map, SingleCRS, Conversion, CoordinateSystem) |
| * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createDerivedCRS(Map, CoordinateReferenceSystem, Conversion, CoordinateSystem) |
| */ |
| public static DefaultDerivedCRS create(final Map<String,?> properties, |
| final SingleCRS baseCRS, |
| final Conversion conversion, |
| final CoordinateSystem derivedCS) |
| throws MismatchedDimensionException |
| { |
| if (baseCRS != null && derivedCS != null) { |
| final String type = getType(baseCRS, derivedCS); |
| if (type != null) switch (type) { |
| case WKTKeywords.GeodeticCRS: return new Geodetic (properties, (GeodeticCRS) baseCRS, conversion, derivedCS); |
| case WKTKeywords.VerticalCRS: return new Vertical (properties, (VerticalCRS) baseCRS, conversion, (VerticalCS) derivedCS); |
| case WKTKeywords.TimeCRS: return new Temporal (properties, (TemporalCRS) baseCRS, conversion, (TimeCS) derivedCS); |
| case WKTKeywords.ParametricCRS: return new Parametric(properties, (ParametricCRS) baseCRS, conversion, (ParametricCS) derivedCS); |
| case WKTKeywords.EngineeringCRS: { |
| /* |
| * This case may happen for baseCRS of kind GeodeticCRS, ProjectedCRS or EngineeringCRS. |
| * But only the latter is associated to EngineeringDatum; the two formers are associated |
| * to GeodeticDatum. Consequently, we can implement the EngineeringCRS.getDatum() method |
| * only if the base CRS is itself of kind EngineeringCRS. Otherwise we will return the |
| * "type-neutral" DefaultDerivedCRS implementation. Note that even in the latter case, |
| * the WKT format will still be able to detect that the WKT keyword is "EngineeringCRS". |
| */ |
| if (baseCRS instanceof EngineeringCRS) { |
| return new Engineering(properties, (EngineeringCRS) baseCRS, conversion, derivedCS); |
| } |
| break; |
| } |
| } |
| } |
| return new DefaultDerivedCRS(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** |
| * Creates a derived CRS from a math transform and a type inferred from the given arguments. |
| * This method expects the same arguments and performs the same work as the |
| * {@linkplain #DefaultDerivedCRS(Map, SingleCRS, CoordinateReferenceSystem, OperationMethod, MathTransform, |
| * CoordinateSystem) above constructor}, |
| * except that the {@code DerivedCRS} instance returned by this method may additionally implement |
| * the {@link GeodeticCRS}, {@link VerticalCRS}, {@link TemporalCRS}, {@link ParametricCRS} or |
| * {@link EngineeringCRS} interface. |
| * See the class javadoc for more information. |
| * |
| * @param properties the properties to be given to the {@link DefaultConversion} object |
| * (with keys prefixed by {@code "conversion."}) and to the new derived CRS object. |
| * @param baseCRS coordinate reference system to base the derived CRS on. |
| * @param interpolationCRS the CRS of additional coordinates needed for the operation, or {@code null} if none. |
| * @param method the coordinate operation method (mandatory in all cases). |
| * @param baseToDerived transform from positions in the base CRS to positions in this target CRS. |
| * @param derivedCS the coordinate system for the derived CRS. |
| * @return the newly created derived CRS, potentially implementing an additional CRS interface. |
| * @throws IllegalArgumentException if at least one argument has an incompatible number of dimensions. |
| * |
| * @see #DefaultDerivedCRS(Map, SingleCRS, CoordinateReferenceSystem, OperationMethod, MathTransform, CoordinateSystem) |
| */ |
| public static DefaultDerivedCRS create(final Map<String,?> properties, |
| final SingleCRS baseCRS, |
| final CoordinateReferenceSystem interpolationCRS, |
| final OperationMethod method, |
| final MathTransform baseToDerived, |
| final CoordinateSystem derivedCS) |
| { |
| if (baseCRS != null && derivedCS != null) { |
| final String type = getType(baseCRS, derivedCS); |
| if (type != null) switch (type) { |
| case WKTKeywords.GeodeticCRS: return new Geodetic (properties, (GeodeticCRS) baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| case WKTKeywords.VerticalCRS: return new Vertical (properties, (VerticalCRS) baseCRS, interpolationCRS, method, baseToDerived, (VerticalCS) derivedCS); |
| case WKTKeywords.TimeCRS: return new Temporal (properties, (TemporalCRS) baseCRS, interpolationCRS, method, baseToDerived, (TimeCS) derivedCS); |
| case WKTKeywords.ParametricCRS: return new Parametric(properties, (ParametricCRS) baseCRS, interpolationCRS, method, baseToDerived, (ParametricCS) derivedCS); |
| case WKTKeywords.EngineeringCRS: { |
| if (baseCRS instanceof EngineeringCRS) { |
| // See the comment in create(Map, SingleCRS, Conversion, CoordinateSystem) |
| return new Engineering(properties, (EngineeringCRS) baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| break; |
| } |
| } |
| } |
| return new DefaultDerivedCRS(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** |
| * Returns a SIS coordinate reference system implementation with the same values as the given |
| * arbitrary implementation. If the given object is {@code null}, then this method returns {@code null}. |
| * Otherwise if the given object is already a SIS implementation, then the given object is returned unchanged. |
| * Otherwise a new SIS implementation is created and initialized to the attribute values of the given object. |
| * |
| * @param object the object to get as a SIS implementation, or {@code null} if none. |
| * @return a SIS implementation containing the values of the given object (may be the |
| * given object itself), or {@code null} if the argument was null. |
| */ |
| public static DefaultDerivedCRS castOrCopy(final DerivedCRS object) { |
| if (object == null || object instanceof DefaultDerivedCRS) { |
| return (DefaultDerivedCRS) object; |
| } else { |
| final String type = getType(object.getBaseCRS(), object.getCoordinateSystem()); |
| if (type != null) switch (type) { |
| case WKTKeywords.GeodeticCRS: return new Geodetic (object); |
| case WKTKeywords.VerticalCRS: return new Vertical (object); |
| case WKTKeywords.TimeCRS: return new Temporal (object); |
| case WKTKeywords.ParametricCRS: return new Parametric (object); |
| case WKTKeywords.EngineeringCRS: return new Engineering(object); |
| } |
| return new DefaultDerivedCRS(object); |
| } |
| } |
| |
| /** |
| * Returns the type of conversion associated to this {@code DefaultDerivedCRS}. |
| * Must be a hard-coded, constant value (not dependent on object state). |
| */ |
| @Override |
| final Class<Conversion> getConversionType() { |
| return Conversion.class; |
| } |
| |
| /** |
| * Returns the GeoAPI interface implemented by this class. |
| * The SIS implementation returns {@code DerivedCRS.class}. |
| * |
| * <h4>Note for implementers</h4> |
| * Subclasses usually do not need to override this method since GeoAPI does not define {@code DerivedCRS} |
| * sub-interface. Overriding possibility is left mostly for implementers who wish to extend GeoAPI with |
| * their own set of interfaces. |
| * |
| * @return {@code DerivedCRS.class} or a user-defined sub-interface. |
| */ |
| @Override |
| public Class<? extends DerivedCRS> getInterface() { |
| return DerivedCRS.class; |
| } |
| |
| /** |
| * Returns the datum of the {@linkplain #getBaseCRS() base CRS}. |
| * |
| * @return the datum of the base CRS. |
| */ |
| @Override |
| public Datum getDatum() { |
| return getBaseCRS().getDatum(); |
| } |
| |
| /** |
| * Returns the CRS on which the conversion is applied. |
| * This CRS defines the {@linkplain #getDatum() datum} of this CRS and (at least implicitly) |
| * the {@linkplain DefaultConversion#getSourceCRS() source} of |
| * the {@linkplain #getConversionFromBase() conversion from base}. |
| * |
| * @return the base coordinate reference system. |
| */ |
| @Override |
| @XmlElement(name = "baseCRS", required = true) |
| @XmlJavaTypeAdapter(SC_SingleCRS.class) |
| public SingleCRS getBaseCRS() { |
| return (SingleCRS) super.getConversionFromBase().getSourceCRS(); |
| } |
| |
| /** |
| * Returns the conversion from the {@linkplain #getBaseCRS() base CRS} to this CRS. |
| * In Apache SIS, the conversion source and target CRS are set to the following values: |
| * |
| * <ul> |
| * <li>The conversion {@linkplain DefaultConversion#getSourceCRS() source CRS} |
| * is the {@linkplain #getBaseCRS() base CRS} of {@code this} CRS.</li> |
| * <li>The conversion {@linkplain DefaultConversion#getTargetCRS() target CRS} is {@code this} CRS. |
| * </ul> |
| * |
| * <div class="note"><b>Note:</b> |
| * This is different than ISO 19111, which allows source and target CRS to be {@code null}.</div> |
| * |
| * @return the conversion to this CRS. |
| */ |
| @Override |
| public Conversion getConversionFromBase() { |
| return super.getConversionFromBase(); |
| } |
| |
| /** |
| * Returns the coordinate system. |
| * |
| * @return the coordinate system. |
| */ |
| @Override |
| @XmlElement(name = "coordinateSystem", required = true) |
| @XmlJavaTypeAdapter(CS_CoordinateSystem.class) |
| public CoordinateSystem getCoordinateSystem() { |
| return super.getCoordinateSystem(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @return {@inheritDoc} |
| */ |
| @Override |
| public DefaultDerivedCRS forConvention(final AxesConvention convention) { |
| return (DefaultDerivedCRS) super.forConvention(convention); |
| } |
| |
| /** |
| * Returns a coordinate reference system of the same type as this CRS but with different axes. |
| * |
| * @param cs the coordinate system with new axes. |
| * @return new CRS of the same type and datum than this CRS, but with the given axes. |
| */ |
| @Override |
| AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new DefaultDerivedCRS(this, derivedCS); |
| } |
| |
| /** |
| * Compares this coordinate reference system with the specified object for equality. |
| * In addition to the metadata documented in the |
| * {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#equals(Object, ComparisonMode) parent class}, |
| * this method considers coordinate system axes of the {@linkplain #getBaseCRS() base CRS} as metadata. |
| * This means that if the given {@code ComparisonMode} is {@code IGNORE_METADATA} or {@code APPROXIMATE}, |
| * then axis order of the base CRS are ignored |
| * (but <strong>not</strong> axis order of <strong>this</strong> derived CRS). |
| * |
| * @param object the object to compare to {@code this}. |
| * @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or |
| * {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only |
| * properties relevant to coordinate transformations. |
| * @return {@code true} if both objects are equal. |
| */ |
| @Override |
| public boolean equals(final Object object, final ComparisonMode mode) { |
| return super.equals(object, mode); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * @return {@inheritDoc} |
| */ |
| @Override |
| protected long computeHashCode() { |
| return super.computeHashCode(); |
| } |
| |
| /** |
| * Formats the inner part of the <i>Well Known Text</i> (WKT) representation of this CRS. |
| * |
| * @return {@code "Fitted_CS"} (WKT 1) or a type-dependent keyword (WKT 2). |
| * |
| * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#93">WKT 2 specification §15</a> |
| */ |
| @Override |
| protected String formatTo(final Formatter formatter) { |
| final Conversion conversion = getConversionFromBase(); // Gives to users a chance to override. |
| if (conversion == null) { |
| /* |
| * Should never happen except temporarily at construction time, or if the user invoked the copy |
| * constructor with an invalid Conversion, or if the user overrides the getConversionFromBase() |
| * method. Delegates to the super-class method for avoiding a NullPointerException. That method |
| * returns 'null', which will cause the WKT to be declared invalid. |
| */ |
| return super.formatTo(formatter); |
| } |
| WKTUtilities.appendName(this, formatter, null); |
| final Convention convention = formatter.getConvention(); |
| final boolean isWKT1 = (convention.majorVersion() == 1); |
| /* |
| * Both WKT 1 and WKT 2 format the base CRS. But WKT 1 formats the MathTransform before the base CRS, |
| * while WKT 2 formats the conversion method and parameter values after the base CRS. |
| */ |
| if (isWKT1) { |
| MathTransform inverse = conversion.getMathTransform(); |
| try { |
| inverse = inverse.inverse(); |
| } catch (NoninvertibleTransformException exception) { |
| formatter.setInvalidWKT(this, exception); |
| inverse = null; |
| } |
| formatter.newLine(); |
| formatter.append(inverse); |
| } |
| formatter.newLine(); |
| formatter.append(WKTUtilities.toFormattable(getBaseCRS())); |
| if (isWKT1) { |
| return WKTKeywords.Fitted_CS; |
| } else { |
| formatter.newLine(); |
| formatter.append(new ExplicitParameters(this, WKTKeywords.DerivingConversion)); // Format inside a "DefiningConversion" element. |
| if (convention == Convention.INTERNAL || !isBaseCRS(formatter)) { |
| final CoordinateSystem cs = getCoordinateSystem(); |
| formatCS(formatter, cs, ReferencingUtilities.getUnit(cs), isWKT1); |
| } |
| return keyword(formatter); |
| } |
| } |
| |
| /** |
| * Return the WKT 2 keyword for this {@code DerivedCRS}, or {@code null} if unknown. |
| * Inner subclasses will override this method for returning a constant value instead |
| * than trying to infer it from the components. |
| */ |
| String keyword(final Formatter formatter) { |
| final String longKeyword = getType(getBaseCRS(), getCoordinateSystem()); |
| final String shortKeyword; |
| if (longKeyword == null) { |
| return null; |
| } |
| switch (longKeyword) { |
| case WKTKeywords.GeodeticCRS: shortKeyword = WKTKeywords.GeodCRS; break; |
| case WKTKeywords.VerticalCRS: shortKeyword = WKTKeywords.VertCRS; break; |
| case WKTKeywords.EngineeringCRS: shortKeyword = WKTKeywords.EngCRS; break; |
| default: return longKeyword; |
| } |
| return formatter.shortOrLong(shortKeyword, longKeyword); |
| } |
| |
| /** |
| * Returns the WKT 2 keyword for a {@code DerivedCRS} having the given base CRS and derived coordinate system. |
| * If the type cannot be identifier, then this method returns {@code null}. |
| */ |
| static String getType(final SingleCRS baseCRS, final CoordinateSystem derivedCS) { |
| final Class<?> type; |
| if (baseCRS instanceof AbstractIdentifiedObject) { |
| /* |
| * For avoiding ambiguity if a user chooses to implement more |
| * than 1 CRS interface (not recommended, but may happen). |
| */ |
| type = ((AbstractIdentifiedObject) baseCRS).getInterface(); |
| } else if (baseCRS != null) { |
| type = baseCRS.getClass(); |
| } else { |
| return null; |
| } |
| if (GeodeticCRS.class.isAssignableFrom(type) && CoordinateSystems.isGeodetic(derivedCS)) { |
| return WKTKeywords.GeodeticCRS; |
| } else if (VerticalCRS.class.isAssignableFrom(type) && derivedCS instanceof VerticalCS) { |
| return WKTKeywords.VerticalCRS; |
| } else if (TemporalCRS.class.isAssignableFrom(type) && derivedCS instanceof TimeCS) { |
| return WKTKeywords.TimeCRS; |
| } else if (ParametricCRS.class.isAssignableFrom(type) && derivedCS instanceof ParametricCS) { |
| return WKTKeywords.ParametricCRS; |
| } else if (ProjectedCRS.class.isAssignableFrom(type) || EngineeringCRS.class.isAssignableFrom(type)) { |
| return WKTKeywords.EngineeringCRS; |
| } else { |
| return null; |
| } |
| } |
| |
| |
| |
| |
| /** |
| * A derived geodetic CRS. Note that base CRS of kind {@link GeodeticCRS} can be used both with this class |
| * and with {@link org.apache.sis.referencing.crs.DefaultDerivedCRS.Engineering}. Consequently, an ambiguity |
| * may exist when choosing the kind if {@code DerivedCRS} to create for a given {@code GeodeticCRS}. |
| */ |
| @XmlTransient |
| private static final class Geodetic extends DefaultDerivedCRS implements GeodeticCRS { |
| /** For cross-version compatibility. */ |
| private static final long serialVersionUID = -1263243517380302846L; |
| |
| /** Creates a copy of the given CRS. */ |
| Geodetic(DerivedCRS other) { |
| super(other); |
| } |
| |
| /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ |
| private Geodetic(final Geodetic original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** Creates a new geodetic CRS from the given properties. */ |
| Geodetic(Map<String,?> properties, GeodeticCRS baseCRS, Conversion conversion, CoordinateSystem derivedCS) { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** Creates a new geodetic CRS from the given properties. */ |
| Geodetic(Map<String,?> properties, GeodeticCRS baseCRS, CoordinateReferenceSystem interpolationCRS, |
| OperationMethod method, MathTransform baseToDerived, CoordinateSystem derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** Returns the datum of the base geodetic CRS. */ |
| @Override public GeodeticDatum getDatum() { |
| return (GeodeticDatum) super.getDatum(); |
| } |
| |
| /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ |
| @Override AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new Geodetic(this, derivedCS); |
| } |
| |
| /** Returns the WKT keyword for this derived CRS type. */ |
| @Override String keyword(final Formatter formatter) { |
| return formatter.shortOrLong(WKTKeywords.GeodCRS, WKTKeywords.GeodeticCRS); |
| } |
| |
| /** Returns the GML code for this derived CRS type. */ |
| @Override SC_DerivedCRSType getType() { |
| return new SC_DerivedCRSType("geodetic"); |
| } |
| } |
| |
| /** |
| * A derived vertical CRS. |
| */ |
| @XmlTransient |
| private static final class Vertical extends DefaultDerivedCRS implements VerticalCRS { |
| /** For cross-version compatibility. */ |
| private static final long serialVersionUID = -5599709829566076972L; |
| |
| /** Creates a copy of the given CRS. */ |
| Vertical(DerivedCRS other) { |
| super(other); |
| } |
| |
| /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ |
| private Vertical(final Vertical original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** Creates a new vertical CRS from the given properties. */ |
| Vertical(Map<String,?> properties, VerticalCRS baseCRS, Conversion conversion, VerticalCS derivedCS) { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** Creates a new vertical CRS from the given properties. */ |
| Vertical(Map<String,?> properties, VerticalCRS baseCRS, CoordinateReferenceSystem interpolationCRS, |
| OperationMethod method, MathTransform baseToDerived, VerticalCS derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** Returns the datum of the base vertical CRS. */ |
| @Override public VerticalDatum getDatum() { |
| return (VerticalDatum) super.getDatum(); |
| } |
| |
| /** Returns the coordinate system given at construction time. */ |
| @Override public VerticalCS getCoordinateSystem() { |
| return (VerticalCS) super.getCoordinateSystem(); |
| } |
| |
| /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ |
| @Override AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new Vertical(this, derivedCS); |
| } |
| |
| /** Returns the WKT keyword for this derived CRS type. */ |
| @Override String keyword(final Formatter formatter) { |
| return formatter.shortOrLong(WKTKeywords.VertCRS, WKTKeywords.VerticalCRS); |
| } |
| |
| /** Returns the GML code for this derived CRS type. */ |
| @Override SC_DerivedCRSType getType() { |
| return new SC_DerivedCRSType("vertical"); |
| } |
| } |
| |
| /** |
| * A derived temporal CRS. |
| */ |
| @XmlTransient |
| private static final class Temporal extends DefaultDerivedCRS implements TemporalCRS { |
| /** For cross-version compatibility. */ |
| private static final long serialVersionUID = -4721311735720248819L; |
| |
| /** Creates a copy of the given CRS. */ |
| Temporal(DerivedCRS other) { |
| super(other); |
| } |
| |
| /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ |
| private Temporal(final Temporal original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** Creates a new temporal CRS from the given properties. */ |
| Temporal(Map<String,?> properties, TemporalCRS baseCRS, Conversion conversion, TimeCS derivedCS) { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** Creates a new temporal CRS from the given properties. */ |
| Temporal(Map<String,?> properties, TemporalCRS baseCRS, CoordinateReferenceSystem interpolationCRS, |
| OperationMethod method, MathTransform baseToDerived, TimeCS derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** Returns the datum of the base temporal CRS. */ |
| @Override public TemporalDatum getDatum() { |
| return (TemporalDatum) super.getDatum(); |
| } |
| |
| /** Returns the coordinate system given at construction time. */ |
| @Override public TimeCS getCoordinateSystem() { |
| return (TimeCS) super.getCoordinateSystem(); |
| } |
| |
| /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ |
| @Override AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new Temporal(this, derivedCS); |
| } |
| |
| /** Returns the WKT keyword for this derived CRS type. */ |
| @Override String keyword(final Formatter formatter) { |
| return WKTKeywords.TimeCRS; |
| } |
| |
| /** Returns the GML code for this derived CRS type. */ |
| @Override SC_DerivedCRSType getType() { |
| return new SC_DerivedCRSType("time"); |
| } |
| } |
| |
| /** |
| * A derived parametric CRS. |
| */ |
| @XmlTransient |
| private static final class Parametric extends DefaultDerivedCRS implements ParametricCRS { |
| /** For cross-version compatibility. */ |
| private static final long serialVersionUID = 2344979923957294024L; |
| |
| /** Creates a copy of the given CRS. */ |
| Parametric(DerivedCRS other) { |
| super(other); |
| } |
| |
| /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ |
| private Parametric(final Parametric original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** Creates a new parametric CRS from the given properties. */ |
| Parametric(Map<String,?> properties, ParametricCRS baseCRS, Conversion conversion, ParametricCS derivedCS) { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** Creates a new parametric CRS from the given properties. */ |
| Parametric(Map<String,?> properties, ParametricCRS baseCRS, CoordinateReferenceSystem interpolationCRS, |
| OperationMethod method, MathTransform baseToDerived, ParametricCS derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** Returns the datum of the base parametric CRS. */ |
| @Override public ParametricDatum getDatum() { |
| return (ParametricDatum) super.getDatum(); |
| } |
| |
| /** Returns the coordinate system given at construction time. */ |
| @Override public ParametricCS getCoordinateSystem() { |
| return (ParametricCS) super.getCoordinateSystem(); |
| } |
| |
| /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ |
| @Override AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new Parametric(this, derivedCS); |
| } |
| |
| /** Returns the WKT keyword for this derived CRS type. */ |
| @Override String keyword(final Formatter formatter) { |
| return WKTKeywords.ParametricCRS; |
| } |
| |
| /** Returns the GML code for this derived CRS type. */ |
| @Override SC_DerivedCRSType getType() { |
| return new SC_DerivedCRSType("parametric"); |
| } |
| } |
| |
| /** |
| * An derived engineering CRS. ISO 19162 restricts the base CRS to {@code EngineeringCRS}, {@code ProjectedCRS} |
| * or {@code GeodeticCRS}. Note that in the latter case, an ambiguity may exist with the |
| * {@link org.apache.sis.referencing.crs.DefaultDerivedCRS.Geodetic} when deciding which {@code DerivedCRS} to |
| * create. |
| */ |
| @XmlTransient |
| private static final class Engineering extends DefaultDerivedCRS implements EngineeringCRS { |
| /** For cross-version compatibility. */ |
| private static final long serialVersionUID = 42334975023270039L; |
| |
| /** Creates a copy of the given CRS. */ |
| Engineering(DerivedCRS other) { |
| super(other); |
| } |
| |
| /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ |
| private Engineering(final Engineering original, final AbstractCS derivedCS) { |
| super(original, derivedCS); |
| } |
| |
| /** Creates a new engineering CRS from the given properties. */ |
| Engineering(Map<String,?> properties, EngineeringCRS baseCRS, Conversion conversion, CoordinateSystem derivedCS) { |
| super(properties, baseCRS, conversion, derivedCS); |
| } |
| |
| /** Creates a new engineering CRS from the given properties. */ |
| Engineering(Map<String,?> properties, EngineeringCRS baseCRS, CoordinateReferenceSystem interpolationCRS, |
| OperationMethod method, MathTransform baseToDerived, CoordinateSystem derivedCS) |
| { |
| super(properties, baseCRS, interpolationCRS, method, baseToDerived, derivedCS); |
| } |
| |
| /** Returns the datum of the base engineering CRS. */ |
| @Override public EngineeringDatum getDatum() { |
| return (EngineeringDatum) super.getDatum(); |
| } |
| |
| /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ |
| @Override AbstractCRS createSameType(final AbstractCS derivedCS) { |
| return new Engineering(this, derivedCS); |
| } |
| |
| /** Returns the WKT keyword for this derived CRS type. */ |
| @Override String keyword(final Formatter formatter) { |
| return formatter.shortOrLong(WKTKeywords.EngCRS, WKTKeywords.EngineeringCRS); |
| } |
| |
| /** Returns the GML code for this derived CRS type. */ |
| @Override SC_DerivedCRSType getType() { |
| return new SC_DerivedCRSType("engineering"); |
| } |
| } |
| |
| |
| |
| /* |
| ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ |
| ┃ ┃ |
| ┃ XML support with JAXB ┃ |
| ┃ ┃ |
| ┃ The following methods are invoked by JAXB using reflection (even if ┃ |
| ┃ they are private) or are helpers for other methods invoked by JAXB. ┃ |
| ┃ Those methods can be safely removed if Geographic Markup Language ┃ |
| ┃ (GML) support is not needed. ┃ |
| ┃ ┃ |
| ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ |
| */ |
| |
| /** |
| * Constructs a new object in which every attributes are set to a default value. |
| * <strong>This is not a valid object.</strong> This constructor is strictly |
| * reserved to JAXB, which will assign values to the fields using reflection. |
| */ |
| private DefaultDerivedCRS() { |
| } |
| |
| /** |
| * Returns the {@code <gml:derivedCRSType>} element to marshal. The default implementation tries to infer this |
| * information from the {@code DefaultDerivedCRS} properties, but subclasses will override for more determinism. |
| * |
| * <h4>API note</h4> |
| * There is no setter at this time because SIS does not store this information in a {@code DefaultDerivedCRS} |
| * field. Instead, we rely on the interface that we implement. For example, a {@code DefaultDerivedCRS} of type |
| * {@code SC_DerivedCRSType.vertical} will implement the {@link VerticalCRS} interface. |
| */ |
| @XmlElement(name = "derivedCRSType", required = true) |
| SC_DerivedCRSType getType() { |
| return SC_DerivedCRSType.fromWKT(getType(getBaseCRS(), getCoordinateSystem())); |
| } |
| |
| /** |
| * Used by JAXB only (invoked by reflection). |
| * |
| * @see #getBaseCRS() |
| */ |
| private void setBaseCRS(final SingleCRS crs) { |
| setBaseCRS("baseCRS", crs); |
| } |
| |
| /** |
| * Used by JAXB only (invoked by reflection). |
| * |
| * @see #getCoordinateSystem() |
| */ |
| private void setCoordinateSystem(final CoordinateSystem cs) { |
| setCoordinateSystem("coordinateSystem", cs); |
| } |
| } |