This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new 2a816a05e5 Better management of change of axis order: - Share cache, for avoiding to redo the same operation. - Make possible to get back the original definition. - Always sort time dimension last in "normalized" CS. 2a816a05e5 is described below commit 2a816a05e50503c04669614ec3fcee6f34c45e49 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sun Jan 14 21:46:03 2024 +0100 Better management of change of axis order: - Share cache, for avoiding to redo the same operation. - Make possible to get back the original definition. - Always sort time dimension last in "normalized" CS. --- .../apache/sis/coverage/grid/GridOrientation.java | 2 +- .../apache/sis/referencing/crs/AbstractCRS.java | 116 +++++++------ .../sis/referencing/crs/AbstractDerivedCRS.java | 16 +- .../sis/referencing/crs/DefaultCompoundCRS.java | 126 +++++++------- .../sis/referencing/crs/DefaultDerivedCRS.java | 73 +++++--- .../sis/referencing/crs/DefaultEngineeringCRS.java | 22 ++- .../sis/referencing/crs/DefaultGeocentricCRS.java | 29 ++-- .../sis/referencing/crs/DefaultGeodeticCRS.java | 20 ++- .../sis/referencing/crs/DefaultGeographicCRS.java | 25 ++- .../sis/referencing/crs/DefaultImageCRS.java | 19 ++- .../sis/referencing/crs/DefaultParametricCRS.java | 21 ++- .../sis/referencing/crs/DefaultProjectedCRS.java | 17 +- .../sis/referencing/crs/DefaultTemporalCRS.java | 20 ++- .../sis/referencing/crs/DefaultVerticalCRS.java | 17 +- .../apache/sis/referencing/crs/package-info.java | 2 +- .../org/apache/sis/referencing/cs/AbstractCS.java | 185 +++++++++++++-------- .../apache/sis/referencing/cs/AxesConvention.java | 14 +- .../apache/sis/referencing/cs/DefaultAffineCS.java | 33 ++-- .../sis/referencing/cs/DefaultCartesianCS.java | 71 +++++--- .../sis/referencing/cs/DefaultCompoundCS.java | 44 ++++- .../sis/referencing/cs/DefaultCylindricalCS.java | 36 ++-- .../sis/referencing/cs/DefaultEllipsoidalCS.java | 43 ++--- .../apache/sis/referencing/cs/DefaultLinearCS.java | 34 ++-- .../sis/referencing/cs/DefaultParametricCS.java | 34 ++-- .../apache/sis/referencing/cs/DefaultPolarCS.java | 34 ++-- .../sis/referencing/cs/DefaultSphericalCS.java | 41 ++--- .../apache/sis/referencing/cs/DefaultTimeCS.java | 34 ++-- .../sis/referencing/cs/DefaultUserDefinedCS.java | 34 ++-- .../sis/referencing/cs/DefaultVerticalCS.java | 34 ++-- .../org/apache/sis/referencing/cs/Normalizer.java | 16 +- .../org/apache/sis/referencing/cs/SubTypes.java | 31 ++++ .../apache/sis/referencing/cs/package-info.java | 2 +- .../sis/referencing/util/ReferencingUtilities.java | 28 ++++ .../sis/referencing/cs/DefaultPolarCSTest.java | 4 +- 34 files changed, 828 insertions(+), 449 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridOrientation.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridOrientation.java index fef4d1925c..4ef84ef025 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridOrientation.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridOrientation.java @@ -231,7 +231,7 @@ public final class GridOrientation implements Serializable { if (variant == crsVariant) { return this; } - if (variant == AxesConvention.NORMALIZED) { + if (variant == AxesConvention.NORMALIZED || variant == AxesConvention.ORIGINAL) { throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedArgumentValue_1, variant)); } return new GridOrientation(flippedAxes, variant, canReorderGridAxis); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java index 70292de25b..0dab42d7cc 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractCRS.java @@ -18,7 +18,6 @@ package org.apache.sis.referencing.crs; import java.util.Map; import java.util.EnumMap; -import java.util.HashMap; import java.util.Objects; import java.util.ConcurrentModificationException; import jakarta.xml.bind.annotation.XmlType; @@ -33,8 +32,6 @@ import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.GeneralDerivedCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.referencing.AbstractReferenceSystem; -import org.apache.sis.referencing.IdentifiedObjects; -import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.util.ReferencingUtilities; import org.apache.sis.metadata.internal.ImplementationHelper; import org.apache.sis.referencing.cs.AxesConvention; @@ -45,6 +42,7 @@ import org.apache.sis.io.wkt.Formatter; import static org.apache.sis.util.Utilities.deepEquals; import static org.apache.sis.util.ArgumentChecks.ensureNonNull; import static org.apache.sis.referencing.util.WKTUtilities.toFormattable; +import org.apache.sis.util.resources.Errors; // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.metadata.Identifier; @@ -82,7 +80,7 @@ import org.opengis.metadata.Identifier; * without synchronization. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see AbstractCS * @see org.apache.sis.referencing.datum.AbstractDatum @@ -105,7 +103,7 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe /** * Serial number for inter-operability with different versions. */ - private static final long serialVersionUID = -7433284548909530047L; + private static final long serialVersionUID = -4925108294894867598L; /** * The coordinate system. @@ -120,11 +118,22 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe /** * Other coordinate reference systems computed from this CRS by the application of an axes convention. - * Created only when first needed. + * This map is shared by all instances derived from the same original {@code AbstractCRS} instance. + * It is serialized in order to preserve metadata about the original instance. + * All accesses to this map shall be synchronized on {@code forConvention}. * * @see #forConvention(AxesConvention) */ - private transient Map<AxesConvention,AbstractCRS> forConvention; + final EnumMap<AxesConvention,AbstractCRS> forConvention; + + /** + * Creates the value to assign to the {@link #forConvention} map by constructors. + */ + private EnumMap<AxesConvention,AbstractCRS> forConvention() { + var m = new EnumMap<AxesConvention,AbstractCRS>(AxesConvention.class); + m.put(AxesConvention.ORIGINAL, this); + return m; + } /** * Creates a coordinate reference system from the given properties and coordinate system. @@ -164,10 +173,27 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe * @param properties the properties to be given to the coordinate reference system. * @param cs the coordinate system. */ + @SuppressWarnings("this-escape") public AbstractCRS(final Map<String,?> properties, final CoordinateSystem cs) { super(properties); ensureNonNull("cs", cs); coordinateSystem = cs; + forConvention = forConvention(); + } + + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * + * @param original the original coordinate system from which to derive a new one. + * @param id new identifier for this CRS, or {@code null} if none. + * @param cs coordinate system with new axis order or units of measurement. + * + * @see #createSameType(CoordinateSystem) + */ + AbstractCRS(final AbstractCRS original, final Identifier id, final CoordinateSystem cs) { + super(ReferencingUtilities.getPropertiesWithoutIdentifiers(original, (id == null) ? null : Map.of(IDENTIFIERS_KEY, id))); + coordinateSystem = cs; + forConvention = original.forConvention; } /** @@ -181,9 +207,11 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe * * @see #castOrCopy(CoordinateReferenceSystem) */ + @SuppressWarnings("this-escape") protected AbstractCRS(final CoordinateReferenceSystem crs) { super(crs); coordinateSystem = crs.getCoordinateSystem(); + forConvention = forConvention(); } /** @@ -277,14 +305,6 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe return null; } - /** - * Returns the existing CRS for the given convention, or {@code null} if none. - */ - final AbstractCRS getCached(final AxesConvention convention) { - assert Thread.holdsLock(this); - return (forConvention != null) ? forConvention.get(convention) : null; - } - /** * Sets the CRS for the given axes convention. * @@ -292,15 +312,11 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe * @return the cached CRS. May be different than the given {@code crs} if an existing instance has been found. */ final AbstractCRS setCached(final AxesConvention convention, AbstractCRS crs) { - assert Thread.holdsLock(this); - if (forConvention == null) { - forConvention = new EnumMap<>(AxesConvention.class); - } else if (crs != this) { - for (final AbstractCRS existing : forConvention.values()) { - if (crs.equals(existing)) { - crs = existing; - break; - } + assert Thread.holdsLock(forConvention); + for (final AbstractCRS existing : forConvention.values()) { + if (crs.equals(existing, ComparisonMode.IGNORE_METADATA)) { + crs = existing; + break; } } if (forConvention.put(convention, crs) != null) { @@ -318,42 +334,39 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe * * @see AbstractCS#forConvention(AxesConvention) */ - public synchronized AbstractCRS forConvention(final AxesConvention convention) { + public AbstractCRS forConvention(final AxesConvention convention) { ensureNonNull("convention", convention); - AbstractCRS crs = getCached(convention); - if (crs == null) { - final AbstractCS cs = AbstractCS.castOrCopy(coordinateSystem); - final AbstractCS candidate = cs.forConvention(convention); - if (candidate == cs) { - crs = this; - } else { - /* - * Copy properties (scope, domain of validity) except the identifier (e.g. "EPSG:4326") - * because the modified CRS is no longer conform to the authoritative definition. - * If name contains a namespace (e.g. "EPSG"), remove that namespace for the same reason. - * For example, "EPSG:WGS 84" will become simply "WGS 84". - */ - Map<String,?> properties = IdentifiedObjects.getProperties(this, IDENTIFIERS_KEY); - Identifier name = getName(); - if (name.getCodeSpace() != null || name.getAuthority() != null) { - name = new NamedIdentifier(null, name.getCode()); - final Map<String,Object> copy = new HashMap<>(properties); - copy.put(NAME_KEY, name); - properties = copy; + synchronized (forConvention) { + AbstractCRS crs = forConvention.get(convention); + if (crs == null) { + final AbstractCRS original = forConvention.get(AxesConvention.ORIGINAL); + final AbstractCS cs = AbstractCS.castOrCopy(original.coordinateSystem); + final AbstractCS candidate = cs.forConvention(convention); + if (candidate.equals(cs, ComparisonMode.IGNORE_METADATA)) { + crs = original; + } else if (original != this && candidate.equals(coordinateSystem, ComparisonMode.IGNORE_METADATA)) { + crs = this; + } else try { + crs = createSameType(candidate); + } catch (ClassCastException e) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.CanNotCompute_1, convention), e); } - crs = createSameType(properties, candidate); + crs = setCached(convention, crs); } - crs = setCached(convention, crs); + return crs; } - return crs; } /** * Returns a coordinate reference system of the same type as this CRS but with different axes. * This method shall be overridden by all {@code AbstractCRS} subclasses in this package. + * + * @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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ - AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new AbstractCRS(properties, cs); + AbstractCRS createSameType(final CoordinateSystem cs) { + return new AbstractCRS(this, null, cs); } /** @@ -525,9 +538,10 @@ public class AbstractCRS extends AbstractReferenceSystem implements CoordinateRe */ AbstractCRS() { super(org.apache.sis.referencing.util.NilReferencingObject.INSTANCE); + forConvention = forConvention(); /* * The coordinate system is mandatory for SIS working. We do not verify its presence here - * because the verification would have to be done in an 'afterMarshal(…)' method and throwing + * because the verification would have to be done in an `afterMarshal(…)` method and throwing * an exception in that method causes the whole unmarshalling to fail. But the SC_CRS adapter * does some verifications. */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java index fb5a177d56..83c3e4942f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/AbstractDerivedCRS.java @@ -47,9 +47,7 @@ import static org.apache.sis.util.Utilities.deepEquals; /** - * A coordinate reference system that is defined by its coordinate - * {@linkplain org.apache.sis.referencing.operation.DefaultConversion conversion} from another CRS - * (not by a {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}). + * A coordinate reference system that is defined by its coordinate conversion from another CRS. * * @author Martin Desruisseaux (IRD, Geomatys) * @@ -108,6 +106,16 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl conversionFromBase = createConversionFromBase(properties, baseCRS, conversion); } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + AbstractDerivedCRS(final AbstractDerivedCRS<C> original, final CoordinateSystem derivedCS) { + super(original, null, derivedCS); + final Conversion conversion = original.conversionFromBase; + conversionFromBase = createConversionFromBase(null, (SingleCRS) conversion.getSourceCRS(), conversion); + } + /** * For {@link DefaultDerivedCRS#DefaultDerivedCRS(Map, SingleCRS, CoordinateReferenceSystem, OperationMethod, * MathTransform, CoordinateSystem)} constructor only (<strong>not legal for {@code ProjectedCRS}</strong>). @@ -150,7 +158,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * (through {@link DefaultConversion}) the {@link #getCoordinateSystem()} method on {@code this}. * Consequently, this method shall be invoked only after the construction of this {@code AbstractDerivedCRS} * instance is advanced enough for allowing the {@code getCoordinateSystem()} method to execute. - * Subclasses may consider to make the {@code getCoordinateSystem()} method final for better guarantees.</p> + * Subclasses should make their {@code getCoordinateSystem()} method final for better guarantees.</p> */ private C createConversionFromBase(final Map<String,?> properties, final SingleCRS baseCRS, final Conversion conversion) { MathTransformFactory factory = null; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java index 8e0d0f126e..d3b4d4945b 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultCompoundCRS.java @@ -37,7 +37,6 @@ import org.opengis.referencing.crs.TemporalCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.CoordinateSystem; import org.apache.sis.referencing.AbstractReferenceSystem; -import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.cs.AxesConvention; import org.apache.sis.referencing.cs.DefaultCompoundCS; import org.apache.sis.referencing.util.WKTKeywords; @@ -112,7 +111,7 @@ import org.opengis.referencing.crs.ParametricCRS; * SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createCompoundCRS(String) * @@ -131,7 +130,8 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { * May be the same reference as {@link #singles}. * * <p><b>Consider this field as final!</b> - * This field is modified only at construction and unmarshalling time by {@link #setComponents(List)}</p> + * This field is set at construction time by {@link #setComponents(Map, List)} and at + * unmarshalling time by {@link #setXMLComponents(CoordinateReferenceSystem[])}.</p> */ @SuppressWarnings("serial") // Most SIS implementations are serializable. private List<? extends CoordinateReferenceSystem> components; @@ -187,15 +187,16 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { */ public DefaultCompoundCRS(final Map<String,?> properties, final CoordinateReferenceSystem... components) { super(properties, createCoordinateSystem(properties, components)); - setComponents(Arrays.asList(components)); - /* - * 'singles' is computed by the above method call. Now verify that we do not have an ellipsoidal - * height with a geographic or projected CRS (see https://issues.apache.org/jira/browse/SIS-303). - * Note that this is already be done if the given array does not contain nested CompoundCRS. - */ - if (singles != this.components) { - verify(properties, singles.toArray(SingleCRS[]::new)); - } + setComponents(properties, Arrays.asList(components)); + } + + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #forConvention(AxesConvention)} method only. + */ + private DefaultCompoundCRS(final DefaultCompoundCRS original, final CoordinateReferenceSystem[] components) { + super(original, null, createCoordinateSystem(null, components)); + setComponents(null, Arrays.asList(components)); } /** @@ -276,7 +277,7 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { this.components = that.components; this.singles = that.singles; } else { - setComponents(crs.getComponents()); + setComponents(null, crs.getComponents()); } } @@ -337,13 +338,21 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { * Computes the {@link #components} and {@link #singles} fields from the given CRS list. * If the two lists have the same content, then the two fields will reference the same list. * + * @param properties properties of the compound CRS to construct, or {@code null} if unknown. + * @param elements components to set for the CRS. + * * @see #getComponents() */ - private void setComponents(final List<? extends CoordinateReferenceSystem> crs) { - if (setSingleComponents(crs)) { + private void setComponents(final Map<String,?> properties, final List<? extends CoordinateReferenceSystem> elements) { + if (setSingleComponents(elements)) { components = singles; // Shares the same list. } else { - components = UnmodifiableArrayList.wrap(crs.toArray(CoordinateReferenceSystem[]::new)); + components = UnmodifiableArrayList.wrap(elements.toArray(CoordinateReferenceSystem[]::new)); + /* + * Verify that we do not have an ellipsoidal height with a geographic or projected CRS. + * https://issues.apache.org/jira/browse/SIS-303 + */ + verify(properties, singles.toArray(SingleCRS[]::new)); } } @@ -372,9 +381,9 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { * * @see #getSingleComponents() */ - private boolean setSingleComponents(final List<? extends CoordinateReferenceSystem> crs) { - final List<SingleCRS> flattened = new ArrayList<>(crs.size()); - final boolean identical = ReferencingUtilities.getSingleComponents(crs, flattened); + private boolean setSingleComponents(final List<? extends CoordinateReferenceSystem> elements) { + final List<SingleCRS> flattened = new ArrayList<>(elements.size()); + final boolean identical = ReferencingUtilities.getSingleComponents(elements, flattened); singles = UnmodifiableArrayList.wrap(flattened.toArray(SingleCRS[]::new)); return identical; } @@ -389,6 +398,7 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { @SuppressWarnings("unchecked") private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); + @SuppressWarnings("LocalVariableHidesMemberVariable") final List<? extends CoordinateReferenceSystem> components = this.components; if (components instanceof CheckedContainer<?>) { final Class<?> type = ((CheckedContainer<?>) components).getElementType(); @@ -471,49 +481,51 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { } /** - * {@inheritDoc} - * - * <p>If the given convention is {@link AxesConvention#DISPLAY_ORIENTED} or - * {@link AxesConvention#NORMALIZED}, then this method will also reorder the components - * with horizontal CRS (geodetic or projected) first, then vertical CRS, then temporal CRS.</p> + * Returns a compound CRS equivalent to this one but with axes rearranged according the given convention. + * This method first reorders the axes of each individual CRS {@linkplain #getComponents() component}. + * Then, if the given convention is {@link AxesConvention#DISPLAY_ORIENTED} or {@link AxesConvention#NORMALIZED}, + * this method will also reorder the components with horizontal CRS (geodetic or projected) first, + * then vertical CRS, then temporal CRS. * * @return {@inheritDoc} */ @Override - public synchronized DefaultCompoundCRS forConvention(final AxesConvention convention) { + public DefaultCompoundCRS forConvention(final AxesConvention convention) { ArgumentChecks.ensureNonNull("convention", convention); - DefaultCompoundCRS crs = (DefaultCompoundCRS) getCached(convention); - if (crs == null) { - crs = this; - boolean changed = false; - final boolean reorderCRS = convention.ordinal() <= AxesConvention.DISPLAY_ORIENTED.ordinal(); - final List<? extends CoordinateReferenceSystem> components = reorderCRS ? singles : this.components; - final CoordinateReferenceSystem[] newComponents = new CoordinateReferenceSystem[components.size()]; - for (int i=0; i<newComponents.length; i++) { - CoordinateReferenceSystem component = components.get(i); - AbstractCRS m = castOrCopy(component); - if (m != (m = m.forConvention(convention))) { - component = m; - changed = true; + synchronized (forConvention) { + DefaultCompoundCRS crs = (DefaultCompoundCRS) forConvention.get(convention); + if (crs == null) { + crs = (DefaultCompoundCRS) forConvention.get(AxesConvention.ORIGINAL); + boolean changed = false; + final boolean reorderCRS = convention.ordinal() <= AxesConvention.DISPLAY_ORIENTED.ordinal(); + final List<? extends CoordinateReferenceSystem> elements = reorderCRS ? crs.singles : crs.components; + final CoordinateReferenceSystem[] newComponents = new CoordinateReferenceSystem[elements.size()]; + for (int i=0; i<newComponents.length; i++) { + CoordinateReferenceSystem component = elements.get(i); + AbstractCRS m = castOrCopy(component); + if (m != (m = m.forConvention(convention))) { + component = m; + changed = true; + } + newComponents[i] = component; } - newComponents[i] = component; - } - if (changed) { - if (reorderCRS) { - Arrays.sort(newComponents, SubTypes.BY_TYPE); // This array typically has less than 4 elements. + if (changed) { + if (reorderCRS) { + Arrays.sort(newComponents, SubTypes.BY_TYPE); // This array typically has less than 4 elements. + } + crs = new DefaultCompoundCRS(crs, newComponents); } - crs = new DefaultCompoundCRS(IdentifiedObjects.getProperties(this, IDENTIFIERS_KEY), newComponents); + crs = (DefaultCompoundCRS) setCached(convention, crs); } - crs = (DefaultCompoundCRS) setCached(convention, crs); + return crs; } - return crs; } /** * Should never be invoked since we override {@link AbstractCRS#forConvention(AxesConvention)}. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { + final AbstractCRS createSameType(final CoordinateSystem cs) { throw new AssertionError(); } @@ -577,19 +589,19 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { protected String formatTo(final Formatter formatter) { WKTUtilities.appendName(this, formatter, null); final Convention convention = formatter.getConvention(); - final List<? extends CoordinateReferenceSystem> crs; + final List<? extends CoordinateReferenceSystem> elements; final boolean isStandardCompliant; final boolean isWKT1 = convention.majorVersion() == 1; if (isWKT1 || convention == Convention.INTERNAL) { - crs = getComponents(); + elements = getComponents(); isStandardCompliant = true; // WKT 1 does not put any restriction. } else { - crs = getSingleComponents(); - isStandardCompliant = isStandardCompliant(crs); + elements = getSingleComponents(); + isStandardCompliant = isStandardCompliant(elements); } - for (final CoordinateReferenceSystem element : crs) { + for (final CoordinateReferenceSystem component : elements) { formatter.newLine(); - formatter.append(WKTUtilities.toFormattable(element)); + formatter.append(WKTUtilities.toFormattable(component)); } formatter.newLine(); // For writing the ID[…] element on its own line. if (!isStandardCompliant) { @@ -624,7 +636,7 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { singles = List.of(); /* * At least one component CRS is mandatory for SIS working. We do not verify their presence here - * because the verification would have to be done in an 'afterMarshal(…)' method and throwing an + * because the verification would have to be done in an `afterMarshal(…)` method and throwing an * exception in that method causes the whole unmarshalling to fail. But the SC_CRS adapter does * some verifications (indirectly, by testing for coordinate system existence). */ @@ -652,8 +664,8 @@ public class DefaultCompoundCRS extends AbstractCRS implements CompoundCRS { /** * Invoked by JAXB for setting the components of this compound CRS. */ - private void setXMLComponents(final CoordinateReferenceSystem[] crs) { - components = setSingleComponents(Arrays.asList(crs)) ? singles : UnmodifiableArrayList.wrap(crs); - setCoordinateSystem("coordinateSystem", createCoordinateSystem(null, crs)); + private void setXMLComponents(final CoordinateReferenceSystem[] elements) { + components = setSingleComponents(Arrays.asList(elements)) ? singles : UnmodifiableArrayList.wrap(elements); + setCoordinateSystem("coordinateSystem", createCoordinateSystem(null, elements)); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java index feb4007d2e..1252bb3b01 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java @@ -94,7 +94,7 @@ import org.opengis.referencing.cs.ParametricCS; * * @author Martin Desruisseaux (IRD, Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createDerivedCRS(String) * @@ -171,6 +171,14 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements 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(CoordinateSystem)} method only. + */ + DefaultDerivedCRS(final DefaultDerivedCRS original, final CoordinateSystem 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); @@ -468,11 +476,14 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements /** * 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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = super.getConversionFromBase(); - return new DefaultDerivedCRS(properties, (SingleCRS) conversionFromBase.getSourceCRS(), conversionFromBase, derivedCS); + AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new DefaultDerivedCRS(this, derivedCS); } /** @@ -627,6 +638,11 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements super(other); } + /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ + private Geodetic(final Geodetic original, final CoordinateSystem 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); @@ -645,10 +661,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ - @Override AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = getConversionFromBase(); - return new Geodetic(properties, (GeodeticCRS) conversionFromBase.getSourceCRS(), - conversionFromBase, derivedCS); + @Override AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new Geodetic(this, derivedCS); } /** Returns the WKT keyword for this derived CRS type. */ @@ -675,6 +689,11 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements super(other); } + /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ + private Vertical(final Vertical original, final VerticalCS 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); @@ -698,10 +717,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ - @Override AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = getConversionFromBase(); - return new Vertical(properties, (VerticalCRS) conversionFromBase.getSourceCRS(), - conversionFromBase, (VerticalCS) derivedCS); + @Override AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new Vertical(this, (VerticalCS) derivedCS); } /** Returns the WKT keyword for this derived CRS type. */ @@ -728,6 +745,11 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements super(other); } + /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ + private Temporal(final Temporal original, final TimeCS 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); @@ -751,10 +773,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ - @Override AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = getConversionFromBase(); - return new Temporal(properties, (TemporalCRS) conversionFromBase.getSourceCRS(), - conversionFromBase, (TimeCS) derivedCS); + @Override AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new Temporal(this, (TimeCS) derivedCS); } /** Returns the WKT keyword for this derived CRS type. */ @@ -781,6 +801,11 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements super(other); } + /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ + private Parametric(final Parametric original, final ParametricCS 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); @@ -804,10 +829,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ - @Override AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = getConversionFromBase(); - return new Parametric(properties, (ParametricCRS) conversionFromBase.getSourceCRS(), - conversionFromBase, (ParametricCS) derivedCS); + @Override AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new Parametric(this, (ParametricCS) derivedCS); } /** Returns the WKT keyword for this derived CRS type. */ @@ -837,6 +860,11 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements super(other); } + /** Creates a new CRS derived from the specified one, but with different axis order or unit. */ + private Engineering(final Engineering original, final CoordinateSystem 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); @@ -855,9 +883,8 @@ public class DefaultDerivedCRS extends AbstractDerivedCRS<Conversion> implements } /** Returns a coordinate reference system of the same type as this CRS but with different axes. */ - @Override AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem derivedCS) { - final Conversion conversionFromBase = getConversionFromBase(); - return new Engineering(properties, (EngineeringCRS) conversionFromBase.getSourceCRS(), conversionFromBase, derivedCS); + @Override AbstractCRS createSameType(final CoordinateSystem derivedCS) { + return new Engineering(this, derivedCS); } /** Returns the WKT keyword for this derived CRS type. */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java index fdd1f87bad..3467f5b3d9 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultEngineeringCRS.java @@ -60,7 +60,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureNonNull; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.datum.DefaultEngineeringDatum * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createEngineeringCRS(String) @@ -138,14 +138,23 @@ public class DefaultEngineeringCRS extends AbstractCRS implements EngineeringCRS * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createEngineeringCRS(Map, EngineeringDatum, CoordinateSystem) */ public DefaultEngineeringCRS(final Map<String,?> properties, - final EngineeringDatum datum, - final CoordinateSystem cs) + final EngineeringDatum datum, + final CoordinateSystem cs) { super(properties, cs); ensureNonNull("datum", datum); this.datum = datum; } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultEngineeringCRS(final DefaultEngineeringCRS original, final CoordinateSystem cs) { + super(original, null, cs); + datum = original.datum; + } + /** * 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 @@ -216,10 +225,13 @@ public class DefaultEngineeringCRS extends AbstractCRS implements EngineeringCRS /** * 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 - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultEngineeringCRS(properties, datum, cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultEngineeringCRS(this, cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java index 45d8f08ef0..afa8eacab4 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java @@ -69,7 +69,7 @@ import org.apache.sis.referencing.cs.AxesConvention; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createGeocentricCRS(String) * @@ -82,17 +82,6 @@ public class DefaultGeocentricCRS extends DefaultGeodeticCRS implements Geocentr */ private static final long serialVersionUID = 6784642848287659827L; - /** - * For {@link #createSameType(Map, CoordinateSystem)} usage only. - * This constructor does not verify the coordinate system type. - */ - private DefaultGeocentricCRS(final Map<String,?> properties, - final GeodeticDatum datum, - final CoordinateSystem cs) - { - super(properties, datum, cs); - } - /** * Creates a coordinate reference system from the given properties, datum and coordinate system. * The properties given in argument follow the same rules as for the @@ -159,6 +148,15 @@ public class DefaultGeocentricCRS extends DefaultGeodeticCRS implements Geocentr super(properties, datum, cs); } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + * This constructor does not verify the coordinate system type. + */ + private DefaultGeocentricCRS(final DefaultGeocentricCRS original, final CoordinateSystem cs) { + super(original, null, cs); + } + /** * 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 @@ -228,10 +226,13 @@ public class DefaultGeocentricCRS extends DefaultGeodeticCRS implements Geocentr /** * 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 - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultGeocentricCRS(properties, super.getDatum(), cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultGeocentricCRS(this, cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java index 858ebe70b4..d87eac98bf 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java @@ -44,6 +44,9 @@ import org.apache.sis.io.wkt.Formatter; import org.apache.sis.measure.Units; import static org.apache.sis.util.ArgumentChecks.ensureNonNull; +// Specific to the geoapi-3.1 and geoapi-4.0 branches: +import org.opengis.metadata.Identifier; + /** * A 2- or 3-dimensional coordinate reference system based on a geodetic datum. @@ -104,6 +107,15 @@ class DefaultGeodeticCRS extends AbstractCRS implements GeodeticCRS { // If made this.datum = datum; } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + DefaultGeodeticCRS(final DefaultGeodeticCRS original, final Identifier id, final CoordinateSystem cs) { + super(original, id, cs); + datum = original.datum; + } + /** * 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 @@ -149,10 +161,14 @@ class DefaultGeodeticCRS extends AbstractCRS implements GeodeticCRS { // If made /** * Returns a coordinate reference system of the same type as this CRS but with different axes. * This method shall be overridden by all {@code DefaultGeodeticCRS} subclasses in this package. + * + * @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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultGeodeticCRS(properties, datum, cs); + AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultGeodeticCRS(this, null, cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java index 111f571eeb..0d3d037918 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java @@ -17,7 +17,6 @@ package org.apache.sis.referencing.crs; import java.util.Map; -import java.util.HashMap; import java.util.Arrays; import jakarta.xml.bind.annotation.XmlTransient; import org.opengis.referencing.datum.GeodeticDatum; @@ -85,7 +84,7 @@ import org.opengis.metadata.Identifier; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createGeographicCRS(String) * @@ -157,6 +156,14 @@ public class DefaultGeographicCRS extends DefaultGeodeticCRS implements Geograph super(properties, datum, cs); } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultGeographicCRS(final DefaultGeographicCRS original, final Identifier id, final EllipsoidalCS cs) { + super(original, id, cs); + } + /** * 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 @@ -242,9 +249,14 @@ public class DefaultGeographicCRS extends DefaultGeodeticCRS implements Geograph * EPSG:4269 or EPSG:4326, then this method magically add the CRS:27, CRS:83 or CRS:84 identifier. * Without this special case, the normal behavior would be no identifier. The expected behavior is * that {@code CommonCRS.WGS84.normalizedGeographic()} returns a CRS having the "CRS:84" identifier. + * + * @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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(Map<String,?> properties, final CoordinateSystem cs) { + final AbstractCRS createSameType(final CoordinateSystem cs) { + Identifier id = null; final CoordinateSystemAxis axis = cs.getAxis(0); if (axis.getMinimumValue() == Longitude.MIN_VALUE && axis.getMaximumValue() == Longitude.MAX_VALUE) // For excluding the AxesConvention.POSITIVE_RANGE case. @@ -253,16 +265,15 @@ public class DefaultGeographicCRS extends DefaultGeodeticCRS implements Geograph if (EPSG.equals(identifier.getCodeSpace())) try { final int i = Arrays.binarySearch(EPSG_CODES, Short.parseShort(identifier.getCode())); if (i >= 0) { - final Map<String,Object> c = new HashMap<>(properties); - c.put(IDENTIFIERS_KEY, new ImmutableIdentifier(Citations.WMS, CRS, Short.toString(CRS_CODES[i]))); - properties = c; + id = new ImmutableIdentifier(Citations.WMS, CRS, Short.toString(CRS_CODES[i])); + break; } } catch (NumberFormatException e) { // Okay to igore, because it is not the purpose of this method to disallow non-numeric codes. } } } - return new DefaultGeographicCRS(properties, super.getDatum(), (EllipsoidalCS) cs); + return new DefaultGeographicCRS(this, id, (EllipsoidalCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java index e7c8162843..4d880b390f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultImageCRS.java @@ -51,7 +51,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureNonNull; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.datum.DefaultImageDatum * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createImageCRS(String) @@ -131,6 +131,15 @@ public class DefaultImageCRS extends AbstractCRS implements ImageCRS { this.datum = datum; } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultImageCRS(final DefaultImageCRS original, final AffineCS cs) { + super(original, null, cs); + datum = original.datum; + } + /** * 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 @@ -211,10 +220,14 @@ public class DefaultImageCRS extends AbstractCRS implements ImageCRS { /** * 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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultImageCRS(properties, datum, (AffineCS) cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultImageCRS(this, (AffineCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java index 13b595e8d3..c53b99bd4a 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultParametricCRS.java @@ -51,7 +51,7 @@ import org.opengis.referencing.datum.ParametricDatum; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Johann Sorel (Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.datum.DefaultParametricDatum * @see org.apache.sis.referencing.cs.DefaultParametricCS @@ -124,13 +124,22 @@ public class DefaultParametricCRS extends AbstractCRS implements ParametricCRS { */ public DefaultParametricCRS(final Map<String,?> properties, final ParametricDatum datum, - final ParametricCS cs) + final ParametricCS cs) { super(properties, cs); ensureNonNull("datum", datum); this.datum = datum; } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultParametricCRS(final DefaultParametricCRS original, final ParametricCS cs) { + super(original, null, cs); + datum = original.datum; + } + /** * 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 @@ -212,10 +221,14 @@ public class DefaultParametricCRS extends AbstractCRS implements ParametricCRS { /** * 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. + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultParametricCRS(properties, datum, (ParametricCS) cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultParametricCRS(this, (ParametricCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java index fe05bf66ff..870c17a9ae 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java @@ -59,7 +59,7 @@ import static org.apache.sis.referencing.util.WKTUtilities.toFormattable; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createProjectedCRS(String) * @@ -135,6 +135,14 @@ public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implemen 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(CoordinateSystem)} method only. + */ + private DefaultProjectedCRS(final DefaultProjectedCRS original, final CartesianCS derivedCS) { + super(original, 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 @@ -261,11 +269,12 @@ public class DefaultProjectedCRS extends AbstractDerivedCRS<Projection> implemen /** * Returns a coordinate reference system of the same type as this CRS but with different axes. + * + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - final Projection conversion = super.getConversionFromBase(); - return new DefaultProjectedCRS(properties, conversion.getSourceCRS(), conversion, (CartesianCS) cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultProjectedCRS(this, (CartesianCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java index cbc9d8c614..3be96cf380 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultTemporalCRS.java @@ -65,7 +65,7 @@ import static org.apache.sis.util.internal.StandardDateFormat.MILLIS_PER_SECOND; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.datum.DefaultTemporalDatum * @see org.apache.sis.referencing.cs.DefaultTimeCS @@ -152,6 +152,7 @@ public class DefaultTemporalCRS extends AbstractCRS implements TemporalCRS { * * @see org.apache.sis.referencing.factory.GeodeticObjectFactory#createTemporalCRS(Map, TemporalDatum, TimeCS) */ + @SuppressWarnings("this-escape") public DefaultTemporalCRS(final Map<String,?> properties, final TemporalDatum datum, final TimeCS cs) @@ -162,6 +163,16 @@ public class DefaultTemporalCRS extends AbstractCRS implements TemporalCRS { initializeConverter(); } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultTemporalCRS(final DefaultTemporalCRS original, final TimeCS cs) { + super(original, null, cs); + datum = original.datum; + initializeConverter(); + } + /** * 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 @@ -173,6 +184,7 @@ public class DefaultTemporalCRS extends AbstractCRS implements TemporalCRS { * * @see #castOrCopy(TemporalCRS) */ + @SuppressWarnings("this-escape") protected DefaultTemporalCRS(final TemporalCRS crs) { super(crs); datum = crs.getDatum(); @@ -296,10 +308,12 @@ public class DefaultTemporalCRS extends AbstractCRS implements TemporalCRS { /** * Returns a coordinate reference system of the same type as this CRS but with different axes. + * + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultTemporalCRS(properties, datum, (TimeCS) cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultTemporalCRS(this, (TimeCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java index e19f5bb66d..5554f4e16c 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultVerticalCRS.java @@ -49,7 +49,7 @@ import static org.apache.sis.util.ArgumentChecks.ensureNonNull; * in the javadoc, this condition holds if all components were created using only SIS factories and static constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.datum.DefaultVerticalDatum * @see org.apache.sis.referencing.cs.DefaultVerticalCS @@ -129,6 +129,15 @@ public class DefaultVerticalCRS extends AbstractCRS implements VerticalCRS { this.datum = datum; } + /** + * Creates a new CRS derived from the specified one, but with different axis order or unit. + * This is for implementing the {@link #createSameType(CoordinateSystem)} method only. + */ + private DefaultVerticalCRS(final DefaultVerticalCRS original, final VerticalCS cs) { + super(original, null, cs); + datum = original.datum; + } + /** * 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 @@ -210,10 +219,12 @@ public class DefaultVerticalCRS extends AbstractCRS implements VerticalCRS { /** * Returns a coordinate reference system of the same type as this CRS but with different axes. + * + * @throws ClassCastException if the type of the given coordinate system is invalid. */ @Override - final AbstractCRS createSameType(final Map<String,?> properties, final CoordinateSystem cs) { - return new DefaultVerticalCRS(properties, datum, (VerticalCS) cs); + final AbstractCRS createSameType(final CoordinateSystem cs) { + return new DefaultVerticalCRS(this, (VerticalCS) cs); } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/package-info.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/package-info.java index ddd2d99cf2..d71a18efa6 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/package-info.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/package-info.java @@ -73,7 +73,7 @@ * * @author Martin Desruisseaux (IRD, Geomatys) * @author Cédric Briançon (Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlSchema(location = "http://schemas.opengis.net/gml/3.2.1/coordinateReferenceSystems.xsd", diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java index d93d6d76f9..dd4d8d06ca 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AbstractCS.java @@ -19,6 +19,7 @@ package org.apache.sis.referencing.cs; import java.util.Map; import java.util.EnumMap; import java.util.Arrays; +import java.util.ConcurrentModificationException; import java.util.logging.Logger; import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.XmlElement; @@ -42,6 +43,7 @@ import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.util.WKTUtilities; import org.apache.sis.referencing.util.AxisDirections; import org.apache.sis.referencing.util.WKTKeywords; +import org.apache.sis.referencing.util.ReferencingUtilities; import org.apache.sis.referencing.internal.Resources; import org.apache.sis.system.Modules; import org.apache.sis.util.Utilities; @@ -51,7 +53,6 @@ import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Errors; import org.apache.sis.io.wkt.ElementKind; import org.apache.sis.io.wkt.Formatter; -import org.apache.sis.measure.Angle; import static org.apache.sis.util.ArgumentChecks.*; @@ -75,7 +76,7 @@ import static org.apache.sis.util.ArgumentChecks.*; * objects and passed between threads without synchronization. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see DefaultCoordinateSystemAxis * @see org.apache.sis.referencing.crs.AbstractCRS @@ -106,7 +107,7 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy /** * Serial number for inter-operability with different versions. */ - private static final long serialVersionUID = 6757665252533744744L; + private static final long serialVersionUID = 3394376886951478970L; /** * Return value for {@link #validateAxis(AxisDirection, Unit)} @@ -126,11 +127,22 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy /** * Other coordinate systems derived from this coordinate systems for other axes conventions. - * Created only when first needed. + * This map is shared by all instances derived from the same original {@code AbstractCRS} instance. + * It is serialized in order to preserve metadata about the original instance. + * All accesses to this map shall be synchronized on {@code forConvention}. * * @see #forConvention(AxesConvention) */ - private transient Map<AxesConvention,AbstractCS> derived; + final EnumMap<AxesConvention,AbstractCS> forConvention; + + /** + * Creates the value to assign to the {@link #forConvention} map by constructors. + */ + private EnumMap<AxesConvention,AbstractCS> forConvention() { + var m = new EnumMap<AxesConvention,AbstractCS>(AxesConvention.class); + m.put(AxesConvention.ORIGINAL, this); + return m; + } /** * Constructs a coordinate system from a set of properties and a sequence of axes. @@ -165,12 +177,25 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy * * @param properties the properties to be given to the identified object. * @param axes the sequence of axes. + * @throws IllegalArgumentException if an axis has an illegal direction or an illegal unit of measurement. */ - @SuppressWarnings("OverridableMethodCallInConstructor") - public AbstractCS(final Map<String,?> properties, CoordinateSystemAxis... axes) { + @SuppressWarnings({"this-escape", "OverridableMethodCallInConstructor"}) + public AbstractCS(final Map<String,?> properties, final CoordinateSystemAxis... axes) { super(properties); ensureNonNull("axes", axes); - this.axes = axes = axes.clone(); + this.axes = axes.clone(); + validate(properties); + forConvention = forConvention(); + } + + /** + * Verifies that the coordinate system axes are non-null, then validates their directions and units of measurement. + * Subclasses may override for adding more verifications, for example ensuring that all axes are perpendicular. + * + * @param properties properties given at construction time, or {@code null} if none. + * @throws IllegalArgumentException if an axis has an illegal direction or an illegal unit of measurement. + */ + void validate(final Map<String,?> properties) { for (int i=0; i<axes.length; i++) { final CoordinateSystemAxis axis = axes[i]; ensureNonNullElement("axes", i, axis); @@ -236,30 +261,22 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy } /** - * Ensures that all known axes are perpendicular, ignoring unknown axis directions. - * This method can be invoked by the constructors of coordinate systems having this requirement. + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @param original the original coordinate system from which to derive a new one. + * @param name name of the new coordinate system, or {@code null} to inherit. + * @param axes the new axes. This array is not cloned. + * @param share whether the new CS should use a cache shared with the original CS. + * @throws IllegalArgumentException if an axis has illegal unit or direction. * - * @param properties properties given at construction time. - * @throws IllegalArgumentException if an illegal angle is found between two axes. + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) */ - final void ensurePerpendicularAxis(final Map<String,?> properties) throws IllegalArgumentException { - final int dimension = getDimension(); - for (int i=0; i<dimension; i++) { - final AxisDirection axis0 = getAxis(i).getDirection(); - for (int j=i; ++j<dimension;) { - final AxisDirection axis1 = getAxis(j).getDirection(); - final Angle angle = CoordinateSystems.angle(axis0, axis1); - /* - * The angle may be null for grid directions (COLUMN_POSITIVE, COLUMN_NEGATIVE, - * ROW_POSITIVE, ROW_NEGATIVE). We conservatively accept those directions even if - * they are not really for Cartesian CS because we do not know the grid geometry. - */ - if (angle != null && Math.abs(angle.degrees()) != 90) { - throw new IllegalArgumentException(Resources.forProperties(properties).getString( - Resources.Keys.NonPerpendicularDirections_2, axis0, axis1)); - } - } - } + @SuppressWarnings({"this-escape", "OverridableMethodCallInConstructor"}) + AbstractCS(final AbstractCS original, final String name, final CoordinateSystemAxis[] axes, final boolean share) { + super(original.getPropertiesWithoutIdentifiers(name)); + this.axes = axes; + validate(null); + forConvention = share ? original.forConvention : forConvention(); } /** @@ -269,13 +286,16 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(CoordinateSystem) */ - protected AbstractCS(final CoordinateSystem cs) { - super(cs); - axes = (cs instanceof AbstractCS) ? ((AbstractCS) cs).axes : getAxes(cs); + @SuppressWarnings({"this-escape", "OverridableMethodCallInConstructor"}) + protected AbstractCS(final CoordinateSystem original) { + super(original); + axes = (original instanceof AbstractCS) ? ((AbstractCS) original).axes : getAxes(original); + validate(null); + forConvention = forConvention(); } /** @@ -337,6 +357,16 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy return CoordinateSystem.class; } + /** + * Returns the properties (scope, domain of validity) except the identifiers and the EPSG namespace. + * + * @param name name to associate to the {@link #NAME_KEY} in the returned map, or {@code null} to inherit. + * @return the identified object properties without identifier. + */ + final Map<String,?> getPropertiesWithoutIdentifiers(final String name) { + return ReferencingUtilities.getPropertiesWithoutIdentifiers(this, (name == null) ? null : Map.of(NAME_KEY, name)); + } + /** * Returns the number of dimensions of this coordinate system. * This is the number of axes given at construction time. @@ -360,6 +390,30 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy return axes[dimension]; } + /** + * Sets the CS for the given axes convention. + * + * @param cs the CS to cache. + * @return the cached CS. May be different than the given {@code cs} if an existing instance has been found. + */ + final AbstractCS setCached(final AxesConvention convention, AbstractCS cs) { + assert Thread.holdsLock(forConvention); + /* + * It happens often that the CRS created by RIGHT_HANDED, DISPLAY_ORIENTED and NORMALIZED are the same. + * Sharing the same instance not only saves memory, but can also makes future comparisons faster. + */ + for (final AbstractCS existing : forConvention.values()) { + if (cs.equals(existing, ComparisonMode.IGNORE_METADATA)) { + cs = existing; + break; + } + } + if (forConvention.put(convention, cs) != null) { + throw new ConcurrentModificationException(); // Should never happen, unless we have a synchronization bug. + } + return cs; + } + /** * Returns a coordinate system equivalent to this one but with axes rearranged according the given convention. * If this coordinate system is already compatible with the given convention, then this method returns {@code this}. @@ -369,33 +423,24 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy * * @see org.apache.sis.referencing.crs.AbstractCRS#forConvention(AxesConvention) */ - public synchronized AbstractCS forConvention(final AxesConvention convention) { + public AbstractCS forConvention(final AxesConvention convention) { ensureNonNull("convention", convention); - if (derived == null) { - derived = new EnumMap<>(AxesConvention.class); - } - AbstractCS cs = derived.get(convention); - if (cs == null) { - cs = Normalizer.forConvention(this, convention); + synchronized (forConvention) { + AbstractCS cs = forConvention.get(convention); if (cs == null) { - cs = this; // This coordinate system is already normalized. - } else if (convention != AxesConvention.POSITIVE_RANGE) { - cs = cs.resolveEPSG(this); - } - /* - * It happen often that the CRS created by RIGHT_HANDED, DISPLAY_ORIENTED and - * NORMALIZED are the same. If this is the case, sharing the same instance - * not only save memory but can also make future comparisons faster. - */ - for (final AbstractCS existing : derived.values()) { - if (cs.equals(existing)) { - cs = existing; - break; + final AbstractCS original = forConvention.get(AxesConvention.ORIGINAL); + cs = Normalizer.forConvention(original, convention); + if (cs == null) { + cs = original; // The given coordinate system is already normalized. + } else if (equals(cs, ComparisonMode.IGNORE_METADATA)) { + cs = this; + } else if (convention != AxesConvention.POSITIVE_RANGE) { + cs = cs.resolveEPSG(original); } + cs = setCached(convention, cs); } - derived.put(convention, cs); + return cs; } - return cs; } /** @@ -406,13 +451,15 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy * But if the given {@code axes} array has less elements than this coordinate system dimension, then * this method may return another kind of coordinate system. See {@link AxisFilter} for an example.</p> * - * @param axes the set of axes to give to the new coordinate system. + * @param name name of the new coordinate system. + * @param axes the set of axes to give to the new coordinate system. + * @param share whether the new CS should use a cache shared with the original CS. * @return a new coordinate system of the same type as {@code this}, but using the given axes. * @throws IllegalArgumentException if {@code axes} contains an unexpected number of axes, * or if an axis has an unexpected direction or unexpected unit of measurement. */ - AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - return new AbstractCS(properties, axes); + AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { + return new AbstractCS(this, name, axes, share); } /** @@ -456,18 +503,19 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy } /** - * Convenience method for implementations of {@link #createForAxes(Map, CoordinateSystemAxis[])} + * Convenience method for implementations of {@code createForAxes(…)} * when the resulting coordinate system would have an unexpected number of dimensions. * - * @param properties the properties which was supposed to be given to the constructor. - * @param axes the axes which was supposed to be given to the constructor. - * @param expected the minimal expected number of dimensions (may be less than {@link #getDimension()}). + * @param axes the axes which were supposed to be given to the constructor. + * @param min minimum number of dimensions, inclusive. + * @param max maximum number of dimensions, inclusive. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) */ - static IllegalArgumentException unexpectedDimension(final Map<String,?> properties, - final CoordinateSystemAxis[] axes, final int expected) - { - return new MismatchedDimensionException(Errors.getResources(properties).getString( - Errors.Keys.MismatchedDimension_3, "filter(cs)", expected, axes.length)); + static IllegalArgumentException unexpectedDimension(final CoordinateSystemAxis[] axes, final int min, final int max) { + final int n = axes.length; + final int e = (n < min) ? min : max; + return new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3, "filter(cs)", e, n)); } /** @@ -592,6 +640,7 @@ public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSy */ AbstractCS() { super(org.apache.sis.referencing.util.NilReferencingObject.INSTANCE); + forConvention = forConvention(); axes = EMPTY; /* * Coordinate system axes are mandatory for SIS working. We do not verify their presence here diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AxesConvention.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AxesConvention.java index 846e3ec515..762f5f6977 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AxesConvention.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/AxesConvention.java @@ -101,7 +101,7 @@ import org.apache.sis.measure.Units; * (e.g. {@link org.apache.sis.geometry.GeneralEnvelope#normalize()}). * * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.5 * * @see AbstractCS#forConvention(AxesConvention) * @see org.apache.sis.referencing.crs.AbstractCRS#forConvention(AxesConvention) @@ -274,5 +274,15 @@ public enum AxesConvention implements AxisFilter { * * @see org.opengis.referencing.cs.RangeMeaning#WRAPAROUND */ - POSITIVE_RANGE; + POSITIVE_RANGE, + + /** + * The axis order as they were specified in the original coordinate system. + * The first time that a {@code forConvention(…)} method is invoked on a new coordinate system (CS), + * a reference to that original CS is associated to this enumeration value and can be retrieved from + * any derived object. + * + * @since 1.5 + */ + ORIGINAL; } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultAffineCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultAffineCS.java index 6330148790..12c9fa3f09 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultAffineCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultAffineCS.java @@ -51,7 +51,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlType(name = "AffineCSType") @@ -62,14 +62,6 @@ public class DefaultAffineCS extends AbstractCS implements AffineCS { */ private static final long serialVersionUID = 7977674229369042440L; - /** - * Constructs a coordinate system of arbitrary dimension. This constructor is - * not public because {@code AffineCS} are restricted to 2 and 3 dimensions. - */ - DefaultAffineCS(final Map<String,?> properties, final CoordinateSystemAxis[] axis) { - super(properties, axis); - } - /** * Constructs a two-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -133,6 +125,17 @@ public class DefaultAffineCS extends AbstractCS implements AffineCS { super(properties, axis0, axis1, axis2); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + DefaultAffineCS(final DefaultAffineCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -140,12 +143,12 @@ public class DefaultAffineCS extends AbstractCS implements AffineCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(AffineCS) */ - protected DefaultAffineCS(final AffineCS cs) { - super(cs); + protected DefaultAffineCS(final AffineCS original) { + super(original); } /** @@ -218,11 +221,11 @@ public class DefaultAffineCS extends AbstractCS implements AffineCS { * This method shall be overridden by all {@code AffineCS} subclasses in this package. */ @Override - AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { case 2: // Fall through - case 3: return new DefaultAffineCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 3: return new DefaultAffineCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 2, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCartesianCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCartesianCS.java index 2cf03c2f2a..993b983171 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCartesianCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCartesianCS.java @@ -19,8 +19,11 @@ package org.apache.sis.referencing.cs; import java.util.Map; import jakarta.xml.bind.annotation.XmlType; import jakarta.xml.bind.annotation.XmlRootElement; +import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.apache.sis.referencing.internal.Resources; +import org.apache.sis.measure.Angle; /** @@ -53,7 +56,7 @@ import org.opengis.referencing.cs.CoordinateSystemAxis; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createCartesianCS(String) * @@ -67,15 +70,6 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { */ private static final long serialVersionUID = -6182037957705712945L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultCartesianCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a two-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -118,7 +112,6 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { final CoordinateSystemAxis axis1) { super(properties, axis0, axis1); - ensurePerpendicularAxis(properties); } /** @@ -139,7 +132,17 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { final CoordinateSystemAxis axis2) { super(properties, axis0, axis1, axis2); - ensurePerpendicularAxis(properties); + } + + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + DefaultCartesianCS(final DefaultCartesianCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); } /** @@ -149,13 +152,12 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(CartesianCS) */ - protected DefaultCartesianCS(final CartesianCS cs) { - super(cs); - ensurePerpendicularAxis(null); + protected DefaultCartesianCS(final CartesianCS original) { + super(original); } /** @@ -173,6 +175,35 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { ? (DefaultCartesianCS) object : new DefaultCartesianCS(object); } + /** + * Ensures that all known axes are perpendicular, ignoring unknown axis directions. + * This method can be invoked by the constructors of coordinate systems having this requirement. + * + * @param properties properties given at construction time, or {@code null} if none. + * @throws IllegalArgumentException if an illegal angle is found between two axes. + */ + @Override + final void validate(final Map<String,?> properties) { + super.validate(properties); + final int dimension = getDimension(); + for (int i=0; i<dimension; i++) { + final AxisDirection axis0 = getAxis(i).getDirection(); + for (int j=i; ++j < dimension;) { + final AxisDirection axis1 = getAxis(j).getDirection(); + final Angle angle = CoordinateSystems.angle(axis0, axis1); + /* + * The angle may be null for grid directions (COLUMN_POSITIVE, COLUMN_NEGATIVE, + * ROW_POSITIVE, ROW_NEGATIVE). We conservatively accept those directions even if + * they are not really for Cartesian CS because we do not know the grid geometry. + */ + if (angle != null && Math.abs(angle.degrees()) != 90) { + throw new IllegalArgumentException(Resources.forProperties(properties).getString( + Resources.Keys.NonPerpendicularDirections_2, axis0, axis1)); + } + } + } + } + /** * Returns the GeoAPI interface implemented by this class. * The SIS implementation returns {@code CartesianCS.class}. @@ -203,12 +234,12 @@ public class DefaultCartesianCS extends DefaultAffineCS implements CartesianCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultVerticalCS(properties, axes); + case 1: return SubTypes.createOneDimensional(this, name, axes); case 2: // Fall through - case 3: return new DefaultCartesianCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 3: return new DefaultCartesianCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCompoundCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCompoundCS.java index 37946de847..e1ce919b4c 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCompoundCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCompoundCS.java @@ -50,7 +50,7 @@ import static org.apache.sis.util.Utilities.deepEquals; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlTransient @@ -105,6 +105,15 @@ public class DefaultCompoundCS extends AbstractCS { this.components = UnmodifiableArrayList.wrap(components); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * This is used for the {@link #forConvention(AxesConvention)} implementation only. + */ + private DefaultCompoundCS(final DefaultCompoundCS original, final CoordinateSystem[] components) { + super(original, null, getAxes(components), true); + this.components = UnmodifiableArrayList.wrap(components); + } + /** * Constructs a compound coordinate system from a sequence of coordinate systems. * A default name for this CS will be inferred from the names of all specified CS. @@ -170,6 +179,39 @@ public class DefaultCompoundCS extends AbstractCS { return components; // Unmodifiable. } + /** + * Returns a compound CS equivalent to this one but with axes rearranged according the given convention. + * This method reorders the axes of each individual coordinate system {@linkplain #getComponents() component}. + * + * @return {@inheritDoc} + */ + @Override + public DefaultCompoundCS forConvention(final AxesConvention convention) { + ensureNonNull("convention", convention); + synchronized (forConvention) { + DefaultCompoundCS cs = (DefaultCompoundCS) forConvention.get(convention); + if (cs == null) { + cs = (DefaultCompoundCS) forConvention.get(AxesConvention.ORIGINAL); + boolean changed = false; + final CoordinateSystem[] newComponents = new CoordinateSystem[cs.components.size()]; + for (int i=0; i<newComponents.length; i++) { + CoordinateSystem component = cs.components.get(i); + AbstractCS m = castOrCopy(component); + if (m != (m = m.forConvention(convention))) { + component = m; + changed = true; + } + newComponents[i] = component; + } + if (changed) { + cs = new DefaultCompoundCS(cs, newComponents); + } + cs = (DefaultCompoundCS) setCached(convention, cs); + } + return cs; + } + } + /* * Do not override createForAxes(…) and forConvention(…) because we cannot create a new DefaultCompoundCS * without knownledge of the CoordinateSystem components to give to it. It would be possible to recursively diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCylindricalCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCylindricalCS.java index afa4399b83..ae6855ecf0 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCylindricalCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultCylindricalCS.java @@ -48,7 +48,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see DefaultPolarCS * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createCylindricalCS(String) @@ -63,15 +63,6 @@ public class DefaultCylindricalCS extends AbstractCS implements CylindricalCS { */ private static final long serialVersionUID = -8290402732390917907L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultCylindricalCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a three-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -118,6 +109,17 @@ public class DefaultCylindricalCS extends AbstractCS implements CylindricalCS { super(properties, axis0, axis1, axis2); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultCylindricalCS(final DefaultCylindricalCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -125,12 +127,12 @@ public class DefaultCylindricalCS extends AbstractCS implements CylindricalCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(CylindricalCS) */ - protected DefaultCylindricalCS(final CylindricalCS cs) { - super(cs); + protected DefaultCylindricalCS(final CylindricalCS original) { + super(original); } /** @@ -197,11 +199,11 @@ public class DefaultCylindricalCS extends AbstractCS implements CylindricalCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 2: return new DefaultPolarCS(properties, axes); - case 3: return new DefaultCylindricalCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 2: return new DefaultPolarCS(getPropertiesWithoutIdentifiers(name), axes[0], axes[1]); + case 3: return new DefaultCylindricalCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 2, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultEllipsoidalCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultEllipsoidalCS.java index 63ab943a46..c843716613 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultEllipsoidalCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultEllipsoidalCS.java @@ -48,7 +48,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createEllipsoidalCS(String) * @@ -62,16 +62,6 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { */ private static final long serialVersionUID = -1452492488902329211L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultEllipsoidalCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - validateAxes(properties); - } - /** * Constructs a two-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -114,7 +104,6 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { final CoordinateSystemAxis axis1) { super(properties, axis0, axis1); - validateAxes(properties); } /** @@ -135,7 +124,17 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { final CoordinateSystemAxis axis2) { super(properties, axis0, axis1, axis2); - validateAxes(properties); + } + + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultEllipsoidalCS(final DefaultEllipsoidalCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); } /** @@ -145,12 +144,12 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(EllipsoidalCS) */ - protected DefaultEllipsoidalCS(final EllipsoidalCS cs) { - super(cs); + protected DefaultEllipsoidalCS(final EllipsoidalCS original) { + super(original); } /** @@ -196,7 +195,9 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { * * @param properties the properties given at construction time. */ - private void validateAxes(final Map<String,?> properties) { + @Override + final void validate(final Map<String,?> properties) { + super.validate(properties); int i = super.getDimension(); int n = i - 2; // Number of vertical axes allowed. while (--i >= 0) { @@ -238,12 +239,12 @@ public class DefaultEllipsoidalCS extends AbstractCS implements EllipsoidalCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultVerticalCS(properties, axes); + case 1: return SubTypes.createOneDimensional(this, name, axes); case 2: // Fall through - case 3: return new DefaultEllipsoidalCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 1); + case 3: return new DefaultEllipsoidalCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultLinearCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultLinearCS.java index 668684f0ae..01bb2aa3dc 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultLinearCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultLinearCS.java @@ -49,7 +49,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlType(name = "LinearCSType") @@ -60,15 +60,6 @@ public class DefaultLinearCS extends AbstractCS implements LinearCS { */ private static final long serialVersionUID = -6890723478287625763L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultLinearCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a coordinate system from a set of properties. * The properties map is given unchanged to the @@ -109,6 +100,17 @@ public class DefaultLinearCS extends AbstractCS implements LinearCS { super(properties, axis); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultLinearCS(final DefaultLinearCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -116,12 +118,12 @@ public class DefaultLinearCS extends AbstractCS implements LinearCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(LinearCS) */ - protected DefaultLinearCS(final LinearCS cs) { - super(cs); + protected DefaultLinearCS(final LinearCS original) { + super(original); } /** @@ -188,10 +190,10 @@ public class DefaultLinearCS extends AbstractCS implements LinearCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultLinearCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 1); + case 1: return new DefaultLinearCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 1); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultParametricCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultParametricCS.java index 229ddc778c..b026f56205 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultParametricCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultParametricCS.java @@ -45,7 +45,7 @@ import org.opengis.referencing.cs.ParametricCS; * constants. * * @author Johann Sorel (Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.crs.DefaultParametricCRS * @see org.apache.sis.referencing.datum.DefaultParametricDatum @@ -61,15 +61,6 @@ public class DefaultParametricCS extends AbstractCS implements ParametricCS { */ private static final long serialVersionUID = -5588239024582484514L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultParametricCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a coordinate system from a set of properties. * The properties map is given unchanged to the @@ -110,6 +101,17 @@ public class DefaultParametricCS extends AbstractCS implements ParametricCS { super(properties, axis); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultParametricCS(final DefaultParametricCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -117,12 +119,12 @@ public class DefaultParametricCS extends AbstractCS implements ParametricCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(ParametricCS) */ - protected DefaultParametricCS(final ParametricCS cs) { - super(cs); + protected DefaultParametricCS(final ParametricCS original) { + super(original); } /** @@ -170,10 +172,10 @@ public class DefaultParametricCS extends AbstractCS implements ParametricCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultParametricCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 1); + case 1: return new DefaultParametricCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 1); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultPolarCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultPolarCS.java index 1332dc078c..b20a4b9113 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultPolarCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultPolarCS.java @@ -48,7 +48,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see DefaultCylindricalCS * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createPolarCS(String) @@ -63,15 +63,6 @@ public class DefaultPolarCS extends AbstractCS implements PolarCS { */ private static final long serialVersionUID = 3960197260975470951L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - DefaultPolarCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a two-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -116,6 +107,17 @@ public class DefaultPolarCS extends AbstractCS implements PolarCS { super(properties, axis0, axis1); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultPolarCS(final DefaultPolarCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -123,12 +125,12 @@ public class DefaultPolarCS extends AbstractCS implements PolarCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(PolarCS) */ - protected DefaultPolarCS(final PolarCS cs) { - super(cs); + protected DefaultPolarCS(final PolarCS original) { + super(original); } /** @@ -195,10 +197,10 @@ public class DefaultPolarCS extends AbstractCS implements PolarCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 2: return new DefaultPolarCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 2: return new DefaultPolarCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 2, 2); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultSphericalCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultSphericalCS.java index 42365f5448..688eeece48 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultSphericalCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultSphericalCS.java @@ -53,7 +53,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.factory.GeodeticAuthorityFactory#createSphericalCS(String) * @@ -67,15 +67,6 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS { */ private static final long serialVersionUID = 196295996465774477L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultSphericalCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a three-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -143,6 +134,17 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS { super(properties, axis0, axis1); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultSphericalCS(final DefaultSphericalCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -150,12 +152,12 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(SphericalCS) */ - protected DefaultSphericalCS(final SphericalCS cs) { - super(cs); + protected DefaultSphericalCS(final SphericalCS original) { + super(original); } /** @@ -220,17 +222,18 @@ public class DefaultSphericalCS extends AbstractCS implements SphericalCS { * Returns a coordinate system with different axes. */ @Override - @SuppressWarnings("fallthrough") - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { case 2: { + final Map<String,?> properties = getPropertiesWithoutIdentifiers(name); if (Units.isLinear(axes[0].getUnit()) || Units.isLinear(axes[1].getUnit())) { - return new DefaultPolarCS(properties, axes); + return new DefaultPolarCS(properties, axes[0], axes[1]); + } else { + return new DefaultSphericalCS(properties, axes[0], axes[1]); } - // Fall through } - case 3: return new DefaultSphericalCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 3: return new DefaultSphericalCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 2, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultTimeCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultTimeCS.java index f1dcef7016..8f8a556816 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultTimeCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultTimeCS.java @@ -47,7 +47,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.crs.DefaultTemporalCRS * @see org.apache.sis.referencing.datum.DefaultTemporalDatum @@ -63,15 +63,6 @@ public class DefaultTimeCS extends AbstractCS implements TimeCS { */ private static final long serialVersionUID = 5222911412381303989L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultTimeCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a coordinate system from a set of properties. * The properties map is given unchanged to the @@ -112,6 +103,17 @@ public class DefaultTimeCS extends AbstractCS implements TimeCS { super(properties, axis); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultTimeCS(final DefaultTimeCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -119,12 +121,12 @@ public class DefaultTimeCS extends AbstractCS implements TimeCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(TimeCS) */ - protected DefaultTimeCS(final TimeCS cs) { - super(cs); + protected DefaultTimeCS(final TimeCS original) { + super(original); } /** @@ -189,10 +191,10 @@ public class DefaultTimeCS extends AbstractCS implements TimeCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultTimeCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 1); + case 1: return new DefaultTimeCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 1); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultUserDefinedCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultUserDefinedCS.java index 2ff922858e..1637f2536f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultUserDefinedCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultUserDefinedCS.java @@ -43,7 +43,7 @@ import org.opengis.referencing.cs.CoordinateSystemAxis; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlType(name = "UserDefinedCSType") @@ -54,15 +54,6 @@ public class DefaultUserDefinedCS extends AbstractCS implements UserDefinedCS { */ private static final long serialVersionUID = -4904091898305706316L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - private DefaultUserDefinedCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a two-dimensional coordinate system from a set of properties. * The properties map is given unchanged to the @@ -127,6 +118,17 @@ public class DefaultUserDefinedCS extends AbstractCS implements UserDefinedCS { super(properties, axis0, axis1, axis2); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultUserDefinedCS(final DefaultUserDefinedCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -134,12 +136,12 @@ public class DefaultUserDefinedCS extends AbstractCS implements UserDefinedCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(UserDefinedCS) */ - protected DefaultUserDefinedCS(final UserDefinedCS cs) { - super(cs); + protected DefaultUserDefinedCS(final UserDefinedCS original) { + super(original); } /** @@ -187,11 +189,11 @@ public class DefaultUserDefinedCS extends AbstractCS implements UserDefinedCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { case 2: // Fall through - case 3: return new DefaultUserDefinedCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 2); + case 3: return new DefaultUserDefinedCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 2, 3); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultVerticalCS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultVerticalCS.java index 6f50d5b0b6..69f646c179 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultVerticalCS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/DefaultVerticalCS.java @@ -58,7 +58,7 @@ import org.apache.sis.measure.Units; * constants. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * * @see org.apache.sis.referencing.crs.DefaultVerticalCRS * @see org.apache.sis.referencing.datum.DefaultVerticalDatum @@ -74,15 +74,6 @@ public class DefaultVerticalCS extends AbstractCS implements VerticalCS { */ private static final long serialVersionUID = 1201155778896630499L; - /** - * Creates a new coordinate system from an arbitrary number of axes. This constructor is for - * implementations of the {@link #createForAxes(Map, CoordinateSystemAxis[])} method only, - * because it does not verify the number of axes. - */ - DefaultVerticalCS(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { - super(properties, axes); - } - /** * Constructs a coordinate system from a set of properties. * The properties map is given unchanged to the @@ -123,6 +114,17 @@ public class DefaultVerticalCS extends AbstractCS implements VerticalCS { super(properties, axis); } + /** + * Creates a new CS derived from the specified one, but with different axis order or unit. + * + * @see #createForAxes(String, CoordinateSystemAxis[], boolean) + */ + private DefaultVerticalCS(final DefaultVerticalCS original, final String name, + final CoordinateSystemAxis[] axes, final boolean share) + { + super(original, name, axes, share); + } + /** * Creates a new coordinate system with the same values as the specified one. * This copy constructor provides a way to convert an arbitrary implementation into a SIS one @@ -130,12 +132,12 @@ public class DefaultVerticalCS extends AbstractCS implements VerticalCS { * * <p>This constructor performs a shallow copy, i.e. the properties are not cloned.</p> * - * @param cs the coordinate system to copy. + * @param original the coordinate system to copy. * * @see #castOrCopy(VerticalCS) */ - protected DefaultVerticalCS(final VerticalCS cs) { - super(cs); + protected DefaultVerticalCS(final VerticalCS original) { + super(original); } /** @@ -205,10 +207,10 @@ public class DefaultVerticalCS extends AbstractCS implements VerticalCS { * Returns a coordinate system with different axes. */ @Override - final AbstractCS createForAxes(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { + final AbstractCS createForAxes(final String name, final CoordinateSystemAxis[] axes, final boolean share) { switch (axes.length) { - case 1: return new DefaultVerticalCS(properties, axes); - default: throw unexpectedDimension(properties, axes, 1); + case 1: return new DefaultVerticalCS(this, name, axes, share); + default: throw unexpectedDimension(axes, 1, 1); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/Normalizer.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/Normalizer.java index e9e3ae16bc..8859b632a8 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/Normalizer.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/Normalizer.java @@ -120,6 +120,9 @@ final class Normalizer implements Comparable<Normalizer> { AxisDirections.CLOCKWISE, AxisDirections.AWAY_FROM }) ORDER.put(d, ++code); + // Set the time coordinate as the last coordinate in all cases. + ORDER.put(AxisDirection.PAST, (Integer.MAX_VALUE >>> 1) - 1); + ORDER.put(AxisDirection.FUTURE, (Integer.MAX_VALUE >>> 1)); } /** @@ -359,8 +362,9 @@ final class Normalizer implements Comparable<Normalizer> { * We need to change the Coordinate System name, since it is likely to not be valid anymore. */ final AbstractCS impl = castOrCopy(cs); - final StringBuilder buffer = (StringBuilder) CharSequences.camelCaseToSentence(impl.getInterface().getSimpleName()); - return impl.createForAxes(Map.of(AbstractCS.NAME_KEY, AxisDirections.appendTo(buffer, newAxes)), newAxes); + final var buffer = (StringBuilder) CharSequences.camelCaseToSentence(impl.getInterface().getSimpleName()); + final String name = AxisDirections.appendTo(buffer, newAxes); + return impl.createForAxes(name, newAxes, changes instanceof AxesConvention); } /** @@ -382,9 +386,11 @@ final class Normalizer implements Comparable<Normalizer> { * of -60° still locate the same point in the old and the new coordinate system. But the preferred way to * locate that point become the 300° value if the longitude range has been shifted to positive values.</p> * + * @param cs the coordinate system to shift. + * @param share whether the new CS should use a cache shared with the original CS. * @return a coordinate system using the given kind of longitude range, or {@code null} if no change is needed. */ - private static AbstractCS shiftAxisRange(final CoordinateSystem cs) { + private static AbstractCS shiftAxisRange(final CoordinateSystem cs, final boolean share) { boolean changed = false; final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[cs.getDimension()]; for (int i=0; i<axes.length; i++) { @@ -408,7 +414,7 @@ final class Normalizer implements Comparable<Normalizer> { if (!changed) { return null; } - return castOrCopy(cs).createForAxes(IdentifiedObjects.getProperties(cs, EXCLUDES), axes); + return castOrCopy(cs).createForAxes(null, axes, share); } /** @@ -448,7 +454,7 @@ final class Normalizer implements Comparable<Normalizer> { case NORMALIZED: // Fall through case DISPLAY_ORIENTED: return normalize(cs, convention, true); case RIGHT_HANDED: return normalize(cs, null, true); - case POSITIVE_RANGE: return shiftAxisRange(cs); + case POSITIVE_RANGE: return shiftAxisRange(cs, true); default: throw new AssertionError(convention); } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/SubTypes.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/SubTypes.java index 71ae3137b9..83783054bc 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/SubTypes.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/SubTypes.java @@ -16,8 +16,11 @@ */ package org.apache.sis.referencing.cs; +import java.util.Map; import org.opengis.referencing.cs.AffineCS; +import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystem; +import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.cs.CylindricalCS; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.LinearCS; @@ -26,6 +29,7 @@ import org.opengis.referencing.cs.SphericalCS; import org.opengis.referencing.cs.TimeCS; import org.opengis.referencing.cs.UserDefinedCS; import org.opengis.referencing.cs.VerticalCS; +import org.apache.sis.referencing.util.AxisDirections; /** @@ -91,4 +95,31 @@ final class SubTypes { } return new AbstractCS(object); } + + /** + * Creates a one-dimensional coordinate system derived from the specified CS. + * + * @param original the two- or three-dimensional CRS from which to extract an axis. + * @param name name of the new coordinate system. + * @param axes the user-specified coordinate system axes. Only the first one will be used. + * @return the one-dimensional coordinate system with the specified axis. + */ + static AbstractCS createOneDimensional(final AbstractCS original, final String name, final CoordinateSystemAxis[] axes) { + final CoordinateSystemAxis axis = axes[0]; + final AxisDirection dir = AxisDirections.absolute(axis.getDirection()); + final boolean isTemporal; + if (AxisDirection.UP.equals(dir)) { + isTemporal = false; + } else if (AxisDirection.FUTURE.equals(dir)) { // Happen with Minkowski coordinate system. + isTemporal = true; + } else { + throw AbstractCS.unexpectedDimension(axes, 2, 3); + } + final Map<String,?> properties = original.getPropertiesWithoutIdentifiers(name); + if (isTemporal) { + return new DefaultTimeCS(properties, axis); + } else { + return new DefaultVerticalCS(properties, axis); + } + } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/package-info.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/package-info.java index 0b9e22c1be..ba79901991 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/package-info.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/cs/package-info.java @@ -33,7 +33,7 @@ * and units between two coordinate systems, or filtering axes. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.4 + * @version 1.5 * @since 0.4 */ @XmlSchema(location = "http://schemas.opengis.net/gml/3.2.1/coordinateSystems.xsd", diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java index 5a96079516..fb136a72ac 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/ReferencingUtilities.java @@ -47,6 +47,7 @@ import org.apache.sis.util.CharSequences; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.AbstractIdentifiedObject; import org.apache.sis.referencing.datum.DefaultPrimeMeridian; @@ -424,6 +425,33 @@ public final class ReferencingUtilities extends Static { && AxisDirection.EAST .equals(cs.getAxis(1).getDirection()); } + /** + * Returns the properties (scope, domain of validity) except the identifiers and the EPSG namespace. + * The identifiers are removed because a modified CRS is no longer conform to the authoritative definition. + * If the name contains a namespace (e.g. "EPSG"), this method removes that namespace for the same reason. + * For example, "EPSG:WGS 84" will become simply "WGS 84". + * + * @param object the identified object for which to get properties map. + * @param overwrite properties overwriting the inherited ones, or {@code null} if none. + * @return the identified object properties. + */ + public static Map<String,?> getPropertiesWithoutIdentifiers(final IdentifiedObject object, final Map<String,?> overwrite) { + final Map<String,?> properties = IdentifiedObjects.getProperties(object, IdentifiedObject.IDENTIFIERS_KEY); + final Identifier name = object.getName(); + final boolean keepName = name.getCodeSpace() == null && name.getAuthority() == null; + if (keepName && overwrite == null) { + return properties; + } + final var copy = new HashMap<String,Object>(properties); + if (!keepName) { + copy.put(IdentifiedObject.NAME_KEY, new NamedIdentifier(null, name.getCode())); + } + if (overwrite != null) { + copy.putAll(overwrite); + } + return copy; + } + /** * Returns the properties of the given object but potentially with a modified name. * Current implement truncates the name at the first non-white character which is not diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java index 623c0894bc..02aea810f8 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/cs/DefaultPolarCSTest.java @@ -74,10 +74,10 @@ public final class DefaultPolarCSTest extends TestCase { radius); DefaultPolarCS normalized = cs.forConvention(AxesConvention.RIGHT_HANDED); - assertNotSame("Should create a new CoordinateSystem.", cs, normalized); assertAxisDirectionsEqual("Right-handed", normalized, - AxisDirections.CLOCKWISE, // Interchanged (r,θ) order for making right handed. + AxisDirections.CLOCKWISE, AxisDirection.SOUTH); + assertSame(cs, normalized); normalized = cs.forConvention(AxesConvention.NORMALIZED); assertNotSame("Should create a new CoordinateSystem.", cs, normalized);