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
commit ec0eaa68d945dadbc82f028fd79dc46b296aa2cb Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Nov 19 17:04:11 2022 +0100 Bug fixes related to unmarshalling of GML documents. Those bugs were identified by OGC TestBed 18 D025 scenario. This is a port of 3 commits on `master` branch. --- .../sis/internal/jaxb/IdentifierMapAdapter.java | 30 +++++- .../sis/internal/jaxb/SpecializedIdentifier.java | 2 + .../org/apache/sis/internal/jaxb/package-info.java | 2 +- .../org/apache/sis/internal/metadata/Merger.java | 6 +- .../java/org/apache/sis/xml/NilObjectHandler.java | 2 +- .../java/org/apache/sis/xml/ReferenceResolver.java | 8 +- .../referencing/CC_GeneralOperationParameter.java | 39 +++++-- .../jaxb/referencing/CC_GeneralParameterValue.java | 11 +- .../jaxb/referencing/CC_OperationMethod.java | 11 +- .../jaxb/referencing/CC_OperationParameter.java | 54 +++++++--- .../internal/jaxb/referencing/package-info.java | 2 +- .../sis/internal/referencing/AxisDirections.java | 57 +++++++++-- .../sis/parameter/AbstractParameterDescriptor.java | 4 +- .../sis/parameter/DefaultParameterDescriptor.java | 70 ++++++++++--- .../sis/parameter/DefaultParameterValue.java | 13 ++- .../sis/parameter/DefaultParameterValueGroup.java | 8 +- .../org/apache/sis/parameter/ParameterFormat.java | 3 +- .../java/org/apache/sis/parameter/Parameters.java | 9 +- .../sis/parameter/UnmodifiableParameterValue.java | 13 ++- .../sis/referencing/crs/AbstractDerivedCRS.java | 13 +-- .../sis/referencing/cs/CoordinateSystems.java | 7 ++ .../org/apache/sis/referencing/cs/Normalizer.java | 26 +++-- .../operation/AbstractCoordinateOperation.java | 20 +--- .../operation/AbstractSingleOperation.java | 9 +- .../operation/DefaultConcatenatedOperation.java | 2 +- .../referencing/operation/DefaultConversion.java | 16 +-- .../operation/DefaultOperationMethod.java | 26 +++-- .../operation/DefaultPassThroughOperation.java | 112 +++++++++++++++------ .../apache/sis/referencing/operation/SubTypes.java | 2 +- .../java/org/apache/sis/io/wkt/WKTParserTest.java | 1 + .../storage/csv/MovingFeatureIterator.java | 3 +- .../org/apache/sis/internal/storage/csv/Store.java | 2 +- 32 files changed, 406 insertions(+), 177 deletions(-) diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java index 95dcf08a4c..2dc9ae3436 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/IdentifierMapAdapter.java @@ -18,8 +18,10 @@ package org.apache.sis.internal.jaxb; import java.net.URI; import java.util.Set; +import java.util.List; import java.util.HashMap; import java.util.HashSet; +import java.util.ArrayList; import java.util.Iterator; import java.util.Collection; import java.util.Collections; @@ -81,7 +83,7 @@ import static org.apache.sis.util.collection.Containers.hashMapCapacity; * This class is thread safe if the underlying identifier collection is thread safe. * * @author Martin Desruisseaux (Geomatys) - * @version 0.7 + * @version 1.3 * * @see org.apache.sis.xml.IdentifiedObject * @@ -101,7 +103,10 @@ public class IdentifierMapAdapter extends AbstractMap<Citation,String> implement /** * The identifiers to wrap in a map view. + * + * @see #getIdentifiers(Class) */ + @SuppressWarnings("serial") // Not statically typed as Serializable. public final Collection<Identifier> identifiers; /** @@ -113,6 +118,29 @@ public class IdentifierMapAdapter extends AbstractMap<Citation,String> implement this.identifiers = identifiers; } + /** + * Returns the identifiers as a collection of the specified type. + * The given type is the return type of the {@code getIdentifiers()} method which is delegating to this method. + * The returned collection is modifiable only if {@link #identifiers} is already of the desired type. + * This is the case for {@link org.apache.sis.metadata.iso.ISOMetadata#getIdentifiers()}, + * which is the API for which we want modifiable collections. + * + * @param type {@code Collection.class}, {@code List.class} or {@code Set.class}. + * @return the identifiers as a collection of the specified type. + */ + @SuppressWarnings("ReturnOfCollectionOrArrayField") + public final Collection<Identifier> getIdentifiers(final Class<?> type) { + if (!type.isInstance(identifiers)) { + if (type.isAssignableFrom(Set.class)) { + return new HashSet<>(identifiers); // TODO: use Set.copyOf in JDK10. + } + if (type.isAssignableFrom(List.class)) { + return new ArrayList<>(identifiers); // TODO: use List.copyOf in JDK10. + } + } + return identifiers; + } + /** * If the given authority is a special case, returns its {@link NonMarshalledAuthority} integer enum. * Otherwise returns -1. See javadoc for more information about special cases. diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java index 235660151b..1f6f91b95d 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java @@ -60,6 +60,7 @@ public final class SpecializedIdentifier<T> implements Identifier, Cloneable, Se * * @see #getAuthority() */ + @SuppressWarnings("serial") // Not statically typed as Serializable. private final IdentifierSpace<T> authority; /** @@ -72,6 +73,7 @@ public final class SpecializedIdentifier<T> implements Identifier, Cloneable, Se * @see #getValue() * @see #getCode() */ + @SuppressWarnings("serial") // Not statically typed as Serializable. T value; /** diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java index 24080ec8e7..ba864f8ea2 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/package-info.java @@ -35,7 +35,7 @@ * @author Cédric Briançon (Geomatys) * @author Cullen Rombach (Image Matters) * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.3 * @since 0.3 * @module */ diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java index d4e6d80b81..f1af008592 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/Merger.java @@ -146,13 +146,13 @@ public class Merger { * we are going to merge those two metadata and verify that we are not in an infinite loop. * We will also verify that the target metadata does not contain a source, or vice-versa. */ - { // For keeping 'sourceDone' and 'targetDone' more local. + { // For keeping `sourceDone` and `targetDone` more local. final Boolean sourceDone = done.put(source, Boolean.FALSE); final Boolean targetDone = done.put(target, Boolean.TRUE); if (sourceDone != null || targetDone != null) { if (Boolean.FALSE.equals(sourceDone) && Boolean.TRUE.equals(targetDone)) { /* - * At least, the 'source' and 'target' status are consistent. Pretend that we have already + * At least, the `source` and `target` status are consistent. Pretend that we have already * merged those metadata since actually the merge operation is probably underway by the caller. */ return true; @@ -265,7 +265,7 @@ distribute: while (it.hasNext()) { if (!success) { if (dryRun) break; merge(target, propertyName, sourceValue, targetValue); - success = true; // If no exception has been thrown by 'merged', assume the conflict solved. + success = true; // If no exception has been thrown by `merged`, assume the conflict solved. } } } diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java index bf4ce28745..4cb46aeeb6 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/NilObjectHandler.java @@ -134,7 +134,7 @@ final class NilObjectHandler implements InvocationHandler { } case "getIdentifiers": { return (attribute instanceof IdentifierMapAdapter) ? - ((IdentifierMapAdapter) attribute).identifiers : null; + ((IdentifierMapAdapter) attribute).getIdentifiers(method.getReturnType()) : null; } case "toString": { return Strings.bracket(getInterface(proxy), attribute); diff --git a/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java b/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java index e570506272..9370c71f42 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/xml/ReferenceResolver.java @@ -68,8 +68,8 @@ public class ReferenceResolver { * <li>{@link IdentifiedObject#getIdentifierMap()} will return a {@link java.util.Map} * view over the given identifiers.</li> * <li>All other methods except the ones inherited from the {@link Object} class will return - * an empty collection, an empty array, {@code null}, {@link Double#NaN NaN}, 0 or - * {@code false}, depending on the method return type.</li> + * an empty collection, an empty array, {@code null}, {@link Double#NaN}, 0 or {@code false}, + * depending on the method return type.</li> * </ul> * * @param <T> the compile-time type of the {@code type} argument. @@ -135,10 +135,10 @@ public class ReferenceResolver { return type.cast(object); } else { final short key; - final Object args; + final Object[] args; if (object == null) { key = Errors.Keys.NotABackwardReference_1; - args = id; + args = new Object[] {id}; } else { key = Errors.Keys.UnexpectedTypeForReference_3; args = new Object[] {id, type, object.getClass()}; diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralOperationParameter.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralOperationParameter.java index 717cec4cd2..c67f5b7249 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralOperationParameter.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralOperationParameter.java @@ -38,11 +38,15 @@ import org.apache.sis.parameter.DefaultParameterValueGroup; import org.apache.sis.parameter.Parameters; import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.IdentifiedObjects; +import org.apache.sis.referencing.GeodeticException; import org.apache.sis.util.collection.Containers; import org.apache.sis.util.CorruptedObjectException; import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.internal.jaxb.gco.PropertyType; import org.apache.sis.internal.jaxb.Context; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.xml.IdentifiedObject; +import org.apache.sis.xml.IdentifierSpace; /** @@ -61,7 +65,7 @@ import org.apache.sis.internal.jaxb.Context; * </ul> * * @author Martin Desruisseaux (Geomatys) - * @version 0.6 + * @version 1.3 * @since 0.6 * @module */ @@ -107,7 +111,7 @@ public final class CC_GeneralOperationParameter extends PropertyType<CC_GeneralO } /** - * Constructor for the {@link #wrap} method only. + * Constructor for the {@link #wrap(GeneralParameterDescriptor)} method only. */ private CC_GeneralOperationParameter(final GeneralParameterDescriptor parameter) { super(parameter); @@ -161,9 +165,32 @@ public final class CC_GeneralOperationParameter extends PropertyType<CC_GeneralO /** * Verifies that the given descriptor is non-null and contains at least a name. * This method is used after unmarshalling. + * We do this validation because parameter descriptors are mandatory and SIS classes need them. + * So we provide an error message here instead of waiting for a {@link NullPointerException} + * to occur in some arbitrary place. + * + * @param descriptor the descriptor to validate. + * @param parent the name of the element to report as the parent of {@code property}. + * @param property the name of the property to report as missing if an exception is thrown. + * @throws GeodeticException if the parameters are missing or invalid. */ - static boolean isValid(final GeneralParameterDescriptor descriptor) { - return descriptor != null && descriptor.getName() != null; + static void validate(final GeneralParameterDescriptor descriptor, final String parent, final String property) { + if (descriptor == null || descriptor.getName() == null) { + short key = Errors.Keys.MissingComponentInElement_2; + String[] args = {parent, property}; + /* + * The exception thrown by this method must be unchecked, + * otherwise JAXB just reports is without propagating it. + */ + if (descriptor instanceof IdentifiedObject) { + final String link = ((IdentifiedObject) descriptor).getIdentifierMap().get(IdentifierSpace.XLINK); + if (link != null) { + key = Errors.Keys.NotABackwardReference_1; + args = new String[] {link}; + } + } + throw new GeodeticException(Errors.format(key, args)); + } } /** @@ -259,8 +286,8 @@ public final class CC_GeneralOperationParameter extends PropertyType<CC_GeneralO * be invoked recursively for each parameter in the group. */ final Map<String,Object> merged = new HashMap<>(expected); - merged.putAll(actual); // May overwrite predefined properties. - mergeArrays(GeneralParameterDescriptor.ALIAS_KEY, GenericName.class, provided.getAlias(), merged, complete.getName()); + merged.putAll(actual); // May overwrite predefined properties. + mergeArrays(GeneralParameterDescriptor.ALIAS_KEY, GenericName.class, provided.getAlias(), merged, complete.getName()); mergeArrays(GeneralParameterDescriptor.IDENTIFIERS_KEY, Identifier.class, provided.getIdentifiers(), merged, null); if (isGroup) { final List<GeneralParameterDescriptor> descriptors = ((ParameterDescriptorGroup) provided).descriptors(); diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralParameterValue.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralParameterValue.java index 04ce67518b..7856c9dc1d 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralParameterValue.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_GeneralParameterValue.java @@ -24,7 +24,6 @@ import org.opengis.parameter.GeneralParameterValue; import org.apache.sis.parameter.DefaultParameterValue; import org.apache.sis.parameter.DefaultParameterValueGroup; import org.apache.sis.internal.jaxb.gco.PropertyType; -import org.apache.sis.util.resources.Errors; /** @@ -32,7 +31,7 @@ import org.apache.sis.util.resources.Errors; * package documentation for more information about JAXB and interface. * * @author Martin Desruisseaux (Geomatys) - * @version 0.6 + * @version 1.3 * @since 0.6 * @module */ @@ -110,13 +109,7 @@ public final class CC_GeneralParameterValue extends PropertyType<CC_GeneralParam * @param parameter the unmarshalled element. */ public void setElement(final GeneralParameterValue parameter) { - if (!CC_GeneralOperationParameter.isValid(parameter.getDescriptor())) { - /* - * Descriptors are mandatory and SIS classes need them. Provide an error message - * here instead of waiting for a NullPointerException in some arbitrary place. - */ - throw new IllegalArgumentException(Errors.format(Errors.Keys.MissingValueForProperty_1, "operationParameter")); - } metadata = parameter; + CC_GeneralOperationParameter.validate(parameter.getDescriptor(), "ParameterValue", "operationParameter"); } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationMethod.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationMethod.java index 0d565bc285..5d4ff2f852 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationMethod.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationMethod.java @@ -32,7 +32,6 @@ import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.referencing.operation.OperationMethod; import org.apache.sis.internal.jaxb.Context; import org.apache.sis.internal.jaxb.gco.PropertyType; -import org.apache.sis.internal.metadata.Identifiers; import org.apache.sis.internal.referencing.CoordinateOperations; import org.apache.sis.internal.referencing.provider.MapProjection; import org.apache.sis.parameter.DefaultParameterValue; @@ -48,7 +47,7 @@ import org.apache.sis.util.ArraysExt; * package documentation for more information about JAXB and interface. * * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.3 * @since 0.6 * @module */ @@ -108,14 +107,8 @@ public final class CC_OperationMethod extends PropertyType<CC_OperationMethod, O * @param method the unmarshalled element. */ public void setElement(final DefaultOperationMethod method) { - if (!CC_GeneralOperationParameter.isValid(method.getParameters())) { - /* - * Parameters are mandatory and SIS classes need them. Provide an error message - * here instead of waiting for a NullPointerException in some arbitrary place. - */ - throw new IllegalArgumentException(Identifiers.missingValueForProperty(method.getName(), "parameters")); - } metadata = method; + CC_GeneralOperationParameter.validate(method.getParameters(), "OperationMethod", "parameter"); } /** diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java index a00d2a31f0..7970ef1f42 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/CC_OperationParameter.java @@ -37,7 +37,7 @@ import org.apache.sis.parameter.DefaultParameterDescriptor; * infer it from the actual value.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 0.8 + * @version 1.3 * @since 0.6 * @module */ @@ -118,6 +118,40 @@ public final class CC_OperationParameter extends PropertyType<CC_OperationParame metadata = parameter; } + /** + * Returns the base class of parameter values, or {@code null} if unknown. + * This method assumes that the parameter value does not yet have a descriptor + * (which happens at GML unmarshalling time) and that the type must be derived + * from the actual value. + * + * @param param the parameter from which to get the value class. + * @return base class of values, or {@code null} if unknown. + */ + public static Class<?> valueClass(final ParameterValue<?> param) { + final Object value = param.getValue(); + return (value != null) ? value.getClass() : null; + } + + /** + * Saves the unit of measurement in a boundless range. This method should be invoked only when + * {@link #valueClass} is {@link Double} or {@code double[]}. It is the case in a well-formed GML. + * + * @param param the parameter from which to get the unit of measurement. + * @return unit of measurement wrapped in a boundless range, or {@code null} if none. + */ + public static MeasurementRange<?> valueDomain(final ParameterValue<?> param) { + Unit<?> unit = param.getUnit(); + if (unit == null) { + return null; + } + unit = unit.getSystemUnit(); + if (Units.RADIAN.equals(unit)) { + unit = Units.DEGREE; + } + return MeasurementRange.create(Double.NEGATIVE_INFINITY, false, + Double.POSITIVE_INFINITY, false, unit); + } + /** * Invoked by JAXB during unmarshalling of the enclosing {@code <gml:OperationParameter>}, * before the child {@link DefaultParameterDescriptor}. This method stores the class and @@ -129,21 +163,9 @@ public final class CC_OperationParameter extends PropertyType<CC_OperationParame */ private void beforeUnmarshal(final Unmarshaller unmarshaller, final Object parent) { if (parent instanceof ParameterValue<?>) { - final Object value = ((ParameterValue<?>) parent).getValue(); - if (value != null) { - valueClass = value.getClass(); - Unit<?> unit = ((ParameterValue<?>) parent).getUnit(); - if (unit != null) { - unit = unit.getSystemUnit(); - if (Units.RADIAN.equals(unit)) { - unit = Units.DEGREE; - } - assert (valueClass == Double.class) || (valueClass == double[].class) : valueClass; - valueDomain = MeasurementRange.create(Double.NEGATIVE_INFINITY, false, - Double.POSITIVE_INFINITY, false, unit); - } - Context.setWrapper(Context.current(), this); - } + valueClass = valueClass ((ParameterValue<?>) parent); + valueDomain = valueDomain((ParameterValue<?>) parent); + Context.setWrapper(Context.current(), this); } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java index 90b01e5d26..f1ca5cea95 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/jaxb/referencing/package-info.java @@ -24,7 +24,7 @@ * @author Guilhem Legal (Geomatys) * @author Cédric Briançon (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 0.7 + * @version 1.3 * * @see javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter * diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java index 61b238cb1c..1ce30a6e87 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java @@ -44,7 +44,7 @@ import static org.apache.sis.util.CharSequences.*; * Utilities methods related to {@link AxisDirection}. * * @author Martin Desruisseaux (Geomatys) - * @version 1.2 + * @version 1.3 * @since 0.4 * @module */ @@ -76,17 +76,31 @@ public final class AxisDirections extends Static { private static final int LAST_ORDINAL = DISPLAY_DOWN.ordinal(); /** - * Distance from the origin in a polar coordinate system. - * Specified in ISO 19162 but not yet in ISO 19111. + * Forward direction. + * For an observer at the centre of the object this is will be towards its front, bow or nose. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). * - * @since 0.7 + * @since 1.3 + */ + @UML(identifier="forward", obligation=CONDITIONAL, specification=ISO_19162) + public static final AxisDirection FORWARD = AxisDirection.valueOf("FORWARD"); + /* + * TODO: remove @Ignore in `WKTParserTest` after the code list values in this class have been removed. */ - @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=ISO_19162) - public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM"); + + /** + * Starboard direction. + * For an observer at the centre of the object this will be towards its right. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). + * + * @since 1.3 + */ + @UML(identifier="starboard", obligation=CONDITIONAL, specification=ISO_19162) + public static final AxisDirection STARBOARD = AxisDirection.valueOf("STARBOARD"); /** * Direction of geographic angles (bearing). - * Specified in ISO 19162 but not yet in ISO 19111. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). * * @since 0.7 */ @@ -95,13 +109,22 @@ public final class AxisDirections extends Static { /** * Direction of arithmetic angles. Used in polar coordinate systems. - * Specified in ISO 19162 but not yet in ISO 19111. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). * * @since 0.7 */ @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=ISO_19162) public static final AxisDirection COUNTER_CLOCKWISE = AxisDirection.valueOf("COUNTER_CLOCKWISE"); + /** + * Distance from the origin in a polar coordinate system. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). + * + * @since 0.7 + */ + @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=ISO_19162) + public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM"); + /** * For each direction, the opposite direction. * This map shall be immutable after construction. @@ -345,6 +368,20 @@ public final class AxisDirections extends Static { return ordinal >= COLUMN_POSITIVE.ordinal() && ordinal <= ROW_NEGATIVE.ordinal(); } + /** + * Arithmetic angle between forward/aft/port/starboard directions only. + * This is the angle as viewed from above the vehicle. + * + * @param source the start direction. + * @param target the final direction. + * @return the angle as a multiple of 90°, or {@link Integer#MIN_VALUE} if none. + */ + public static int angleForVehicle(final AxisDirection source, final AxisDirection target) { + if (source == STARBOARD && target == FORWARD) return +1; + if (source == FORWARD && target == STARBOARD) return -1; + return Integer.MIN_VALUE; + } + /** * Angle between geocentric directions only. * @@ -367,7 +404,7 @@ public final class AxisDirections extends Static { } /** - * Angle between compass directions only (not for angle between direction along meridians). + * Arithmetic angle between compass directions only (not for angle between direction along meridians). * * @param source the start direction. * @param target the final direction. @@ -394,7 +431,7 @@ public final class AxisDirections extends Static { } /** - * Angle between display directions only. + * Arithmetic angle between display directions only. * * @param source the start direction. * @param target the final direction. diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java index 7ad14d59cf..4e2d96d22d 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/AbstractParameterDescriptor.java @@ -124,7 +124,7 @@ public abstract class AbstractParameterDescriptor extends AbstractIdentifiedObje * The maximum number of times that values for this parameter group are required, as an unsigned short. * Value {@code 0xFFFF} (or -1) means an unrestricted number of occurrences. * - * <p>We use a short because this value is usually 1 or a very small number like 2 or 3. This also serve + * <p>We use a short because this value is usually 1 or a very small number like 2 or 3. It also serves * as a safety since a large number would be a bad idea with this parameter implementation.</p> * * <p><b>Consider this field as final!</b> @@ -203,7 +203,7 @@ public abstract class AbstractParameterDescriptor extends AbstractIdentifiedObje maximumOccurs = crop(descriptor.getMaximumOccurs()); } - // NOTE: There is no 'castOrCopy' static method in this class because AbstractParameterDescriptor is abstract. + // NOTE: There is no `castOrCopy` static method in this class because AbstractParameterDescriptor is abstract. // If nevertheless we choose to add such method in the future, then CC_GeneralOperationParameter.getElement() // should be simplified. diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java index e3005a2139..4e0cc586da 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterDescriptor.java @@ -87,10 +87,12 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i /** * The class that describe the type of parameter values. + * This field should be considered final after construction. + * This is declared non-final only for GML unmarshalling. * * @see #getValueClass() */ - private final Class<T> valueClass; + private Class<T> valueClass; /** * A set of valid values (usually from a {@linkplain CodeList code list}) @@ -112,9 +114,12 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i * <code>valueClass.{@linkplain Class#getComponentType() getComponentType()}</code>.</li> * </ul> * + * This field should be considered final after construction. + * This is declared non-final only for GML unmarshalling. + * * @see #getValueDomain() */ - private final Range<?> valueDomain; + private Range<?> valueDomain; /** * The default value for the parameter, or {@code null}. @@ -272,7 +277,6 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i * * @see #castOrCopy(ParameterDescriptor) */ - @SuppressWarnings("unchecked") protected DefaultParameterDescriptor(final ParameterDescriptor<T> descriptor) { super(descriptor); valueClass = descriptor.getValueClass(); @@ -399,6 +403,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i @Override @SuppressWarnings("unchecked") public Comparable<T> getMinimumValue() { + final Range<?> valueDomain = this.valueDomain; return (valueDomain != null && valueDomain.getElementType() == valueClass) ? (Comparable<T>) valueDomain.getMinValue() : null; } @@ -417,6 +422,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i @Override @SuppressWarnings("unchecked") public Comparable<T> getMaximumValue() { + final Range<?> valueDomain = this.valueDomain; return (valueDomain != null && valueDomain.getElementType() == valueClass) ? (Comparable<T>) valueDomain.getMaxValue() : null; } @@ -447,6 +453,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i */ @Override public Unit<?> getUnit() { + final Range<?> valueDomain = this.valueDomain; return (valueDomain instanceof MeasurementRange<?>) ? ((MeasurementRange<?>) valueDomain).unit() : null; } @@ -517,10 +524,10 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i } case STRICT: { final DefaultParameterDescriptor<?> that = (DefaultParameterDescriptor<?>) object; - return this.valueClass == that.valueClass && - Objects. equals(this.validValues, that.validValues) && - Objects. equals(this.valueDomain, that.valueDomain) && - Objects.deepEquals(this.defaultValue, that.defaultValue); + return valueClass == that.valueClass && + Objects. equals(validValues, that.validValues) && + Objects. equals(valueDomain, that.valueDomain) && + Objects.deepEquals(defaultValue, that.defaultValue); } } } @@ -553,7 +560,7 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i /** - * Constructs a new object in which every attributes are set to a null value. + * Constructs a new object in which attributes may be set to a null value. * <strong>This is not a valid object.</strong> This constructor is strictly * reserved to JAXB, which will assign values to the fields using reflection. * @@ -570,16 +577,55 @@ public class DefaultParameterDescriptor<T> extends AbstractParameterDescriptor i * This unsafe cast would be forbidden if this constructor was public or used in any context where the * user can choose the value of <T>. But this constructor should be invoked only during unmarshalling, * after the creation of the ParameterValue (this is the reverse creation order than what we normally - * do through the public API). The 'valueClass' should be compatible with DefaultParameterValue.value, + * do through the public API). The `valueClass` should be compatible with DefaultParameterValue.value, * and the parameterized type visible to the user should be only <?>. */ valueClass = (Class) param.valueClass; valueDomain = param.valueDomain; - } else { - valueClass = null; - valueDomain = null; } validValues = null; defaultValue = null; } + + /** + * Invoked by {@link DefaultParameterValue} when the descriptor is set after the value at unmarshalling time. + * There is two scenarios in a valid GML document. The first scenario is when the descriptor is defined inside + * the parameter value element, like below. In such case, {@link #valueClass} is defined at construction time + * by {@link #DefaultParameterDescriptor()} because the value is before the descriptor. + * + * {@preformat xml + * <gml:ParameterValue> + * <gml:value uom="…">…</gml:value> + * <gml:operationParameter> + * <gml:OperationParameter> + * … + * </gml:OperationParameter> + * </gml:operationParameter> + * </gml:ParameterValue> + * } + * + * In the second scenario shows below, the descriptor was defined before the value and is referenced by a link. + * In that case, {@link #valueClass} is {@code null} the first time that this method is invoked. It may become + * non-null if the same parameter descriptor is reused for many parameter values. + * + * {@preformat xml + * <gml:ParameterValue> + * <gml:value uom="…">…</gml:value> + * <gml:operationParameter xlink:href="#LongitudeRotation"/> + * </gml:ParameterValue> + * } + * + * This method modifies the state of this class despite the fact that it should be immutable. + * It is okay because we are updating an instance created during GML unmarshalling, and that + * instance should not have been given to user yet. + * + * @param param the parameter value from which to infer the value type. + */ + @SuppressWarnings("unchecked") + final void setValueClass(final DefaultParameterValue<?> param) { + valueClass = (Class) Classes.findCommonClass(valueClass, CC_OperationParameter.valueClass(param)); + if (valueDomain == null) { + valueDomain = CC_OperationParameter.valueDomain(param); + } + } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java index 3a3fd6be37..6c423cdcd0 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValue.java @@ -118,7 +118,7 @@ import static org.apache.sis.util.Utilities.deepEquals; * for modifying the behavior of all getter and setter methods. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.2 + * @version 1.3 * * @param <T> the type of the value stored in this parameter. * @@ -1129,14 +1129,21 @@ convert: if (componentType != null) { /** * Invoked by JAXB at unmarshalling time. - * May also be invoked by {@link DefaultParameterValueGroup} if the descriptor as been completed + * May also be invoked by {@link DefaultParameterValueGroup} if the descriptor has been completed * with additional information provided in the {@code <gml:group>} element of a descriptor group. * * @see #getDescriptor() */ final void setDescriptor(final ParameterDescriptor<T> descriptor) { this.descriptor = descriptor; - assert (value == null) || descriptor.getValueClass().isInstance(value) : this; + if (descriptor instanceof DefaultParameterDescriptor<?>) { + ((DefaultParameterDescriptor<?>) descriptor).setValueClass(this); + } + /* + * A previous version was doing `assert descriptor.getValueClass().isInstance(value)` + * where the value class was inferred by `DefaultParameterDescriptor()`. But it does + * not always work, and the `NullPointerException` seems to be caught by JAXB. + */ } /** diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java index b0c28806aa..e28988010e 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/DefaultParameterValueGroup.java @@ -559,7 +559,7 @@ scan: for (final GeneralParameterValue param : actual.values()) { /** * Invoked by JAXB for setting the group parameter descriptor. Those parameter are redundant with * the parameters associated to the values given to {@link #setValues(GeneralParameterValue[])}, - * except the the group identification (name, <i>etc.</i>) and for any optional parameters which + * except for the group identification (name, <i>etc.</i>) and for any optional parameters which * were not present in the above {@code GeneralParameterValue} array. * * @see #getDescriptor() @@ -611,9 +611,9 @@ scan: for (final GeneralParameterValue param : actual.values()) { * Appends all parameter values. In this process, we may need to update the descriptor of some values * if those descriptors changed as a result of the above merge process. * - * @param parameters The parameters to add, or {@code null} for {@link #values}. - * @param replacements The replacements to apply in the {@code GeneralParameterValue} instances. - * @param addTo Where to store the new values. + * @param parameters the parameters to add, or {@code null} for {@link #values}. + * @param replacements the replacements to apply in the {@code GeneralParameterValue} instances. + * @param addTo where to store the new values. */ @SuppressWarnings({"unchecked", "AssignmentToCollectionOrArrayFieldFromParameter"}) private void setValues(GeneralParameterValue[] parameters, diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java index 7d7131bb37..e34af12dd4 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java @@ -692,7 +692,8 @@ public class ParameterFormat extends TabularFormat<Object> { /* * Writes the values, each on its own line, together with their unit of measurement. */ - final byte alignment = Number.class.isAssignableFrom(valueClass) ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT; + final byte alignment = (valueClass != null && Number.class.isAssignableFrom(valueClass)) + ? TableAppender.ALIGN_RIGHT : TableAppender.ALIGN_LEFT; table.setCellAlignment(alignment); final int length = row.values.size(); for (int i=0; i<length; i++) { diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java index f20dd66569..ed74415469 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/Parameters.java @@ -38,6 +38,7 @@ import org.apache.sis.util.UnconvertibleObjectException; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.Classes; import org.apache.sis.util.Debug; @@ -356,9 +357,15 @@ public abstract class Parameters implements ParameterValueGroup, Cloneable { if (descriptor instanceof DefaultParameterDescriptor<?>) { return ((DefaultParameterDescriptor<?>) descriptor).getValueDomain(); } - final Class<?> valueClass = descriptor.getValueClass(); + Class<?> valueClass = descriptor.getValueClass(); final Comparable<?> minimumValue = descriptor.getMinimumValue(); final Comparable<?> maximumValue = descriptor.getMaximumValue(); + if (valueClass == null) { // Should never be null, but invalid objects exist. + valueClass = Classes.findCommonClass(Classes.getClass(minimumValue), Classes.getClass(maximumValue)); + if (valueClass == null) { + valueClass = Object.class; + } + } if ((minimumValue == null || valueClass.isInstance(minimumValue)) && (maximumValue == null || valueClass.isInstance(maximumValue))) { diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java index bccdeb3951..4fa432ec27 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/UnmodifiableParameterValue.java @@ -49,7 +49,7 @@ import org.apache.sis.util.resources.Errors; * </div> * * @author Martin Desruisseaux (Geomatys) - * @version 0.6 + * @version 1.3 * * @param <T> the type of the value stored in this parameter. * @@ -100,10 +100,13 @@ final class UnmodifiableParameterValue<T> extends DefaultParameterValue<T> { @Override public T getValue() { T value = super.getValue(); - if (value instanceof Cloneable) try { - value = getDescriptor().getValueClass().cast(Cloner.cloneIfPublic(value)); - } catch (CloneNotSupportedException e) { - throw new UnsupportedOperationException(Errors.format(Errors.Keys.CloneNotSupported_1, value.getClass()), e); + if (value instanceof Cloneable) { + final Class<T> type = getDescriptor().getValueClass(); // May be null after GML unmarshalling. + if (type != null) try { + value = type.cast(Cloner.cloneIfPublic(value)); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(Errors.format(Errors.Keys.CloneNotSupported_1, value.getClass()), e); + } } return value; } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java index 0f167cd410..4b3a1316d9 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/AbstractDerivedCRS.java @@ -22,7 +22,6 @@ import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.ValidationException; import org.opengis.util.FactoryException; import org.opengis.referencing.datum.Datum; import org.opengis.referencing.crs.SingleCRS; @@ -34,12 +33,12 @@ import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.geometry.MismatchedDimensionException; +import org.apache.sis.referencing.GeodeticException; import org.apache.sis.referencing.operation.DefaultConversion; import org.apache.sis.internal.jaxb.referencing.CC_Conversion; import org.apache.sis.internal.referencing.ReferencingFactoryContainer; import org.apache.sis.internal.metadata.ImplementationHelper; import org.apache.sis.internal.metadata.Identifiers; -import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.system.Semaphores; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; @@ -54,7 +53,7 @@ import static org.apache.sis.util.Utilities.deepEquals; * (not by a {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}). * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.2 + * @version 1.3 * * @param <C> the conversion type, either {@code Conversion} or {@code Projection}. * @@ -82,6 +81,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * * @see #getConversionFromBase() */ + @SuppressWarnings("serial") // Not statically typed as Serializable. private C conversionFromBase; /** @@ -162,9 +162,6 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl if (properties != null) { factory = (MathTransformFactory) properties.get(ReferencingFactoryContainer.MT_FACTORY); } - if (factory == null) { - factory = DefaultFactories.forBuildin(MathTransformFactory.class); - } try { return DefaultConversion.castOrCopy(conversion).specialize(getConversionType(), baseCRS, this, factory); } catch (FactoryException e) { @@ -332,7 +329,7 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * coordinate system (CS). The CS information is required by {@code createConversionFromBase(…)} * in order to create a {@link MathTransform} with correct axis swapping and unit conversions. */ - private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws ValidationException { + private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { String property = "conversion"; if (conversionFromBase != null) { final SingleCRS baseCRS = CC_Conversion.setBaseCRS(conversionFromBase, null); // Clear the temporary value now. @@ -350,6 +347,6 @@ abstract class AbstractDerivedCRS<C extends Conversion> extends AbstractCRS impl * and call to `getConversionFromBase()` will throw a ClassCastException if this instance is actually * a ProjectedCRS (because of the method overriding with return type covariance). */ - throw new ValidationException(Identifiers.missingValueForProperty(getName(), property)); + throw new GeodeticException(Identifiers.missingValueForProperty(getName(), property)); } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java index d8134a7a9c..531b4a2401 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java @@ -239,6 +239,13 @@ public final class CoordinateSystems extends Static { if (c != Integer.MIN_VALUE) { return new Angle(c * 90); } + /* + * Check for FORWARD, AFT, PORT, STARBOARD. + */ + c = AxisDirections.angleForVehicle(source, target); + if (c != Integer.MIN_VALUE) { + return new Angle(c * 90); + } /* * Check for DISPLAY_UP, DISPLAY_DOWN, etc. assuming a flat screen. * Note that we do not check for grid directions (COLUMN_POSITIVE, diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java index dfdc5672c3..8010bc2365 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java @@ -106,7 +106,7 @@ final class Normalizer implements Comparable<Normalizer> { * * @see #order(AxisDirection) */ - private static final int SHIFT = 2; + private static final int SHIFT = 3; /** * Custom code list values to handle as if the where defined between two GeoAPI values. @@ -115,12 +115,15 @@ final class Normalizer implements Comparable<Normalizer> { */ private static final Map<AxisDirection,Integer> ORDER = new HashMap<>(); static { - final Map<AxisDirection,Integer> m = ORDER; // Get ordinal of last compass direction defined by GeoAPI. We will continue on the horizontal plane. - final int horizontal = (AxisDirection.NORTH.ordinal() + (AxisDirections.COMPASS_COUNT - 1)) << SHIFT; - m.put(AxisDirections.AWAY_FROM, horizontal + 1); - m.put(AxisDirections.COUNTER_CLOCKWISE, horizontal + 2); - m.put(AxisDirections.CLOCKWISE, horizontal + 3); + int code = (AxisDirection.NORTH.ordinal() + (AxisDirections.COMPASS_COUNT - 1)) << SHIFT; + for (final AxisDirection d : new AxisDirection[] { + AxisDirections.FORWARD, + AxisDirections.STARBOARD, + AxisDirections.COUNTER_CLOCKWISE, + AxisDirections.CLOCKWISE, + AxisDirections.AWAY_FROM + }) ORDER.put(d, ++code); } /** @@ -170,8 +173,9 @@ final class Normalizer implements Comparable<Normalizer> { if (d == 0) { final AxisDirection d1 = this.axis.getDirection(); final AxisDirection d2 = that.axis.getDirection(); - d = AxisDirections.angleForCompass(d2, d1); - if (d == Integer.MIN_VALUE) { + if ((d = AxisDirections.angleForCompass(d2, d1)) == Integer.MIN_VALUE && + (d = AxisDirections.angleForVehicle(d2, d1)) == Integer.MIN_VALUE) + { if (meridian != null) { if (that.meridian != null) { d = meridian.compareTo(that.meridian); @@ -445,10 +449,10 @@ final class Normalizer implements Comparable<Normalizer> { */ static AbstractCS forConvention(final CoordinateSystem cs, final AxesConvention convention) { switch (convention) { - case NORMALIZED: // Fall through + 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 RIGHT_HANDED: return normalize(cs, null, true); + case POSITIVE_RANGE: return shiftAxisRange(cs); default: throw new AssertionError(convention); } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java index 1528ffa922..6f7b8d5b96 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java @@ -670,23 +670,8 @@ check: for (int isTarget=0; ; isTarget++) { // 0 == source check; 1 } /** - * Returns the object for transforming coordinates in the {@linkplain #getSourceCRS() source CRS} - * to coordinates in the {@linkplain #getTargetCRS() target CRS}. - * - * <h4>Use with interpolation CRS</h4> - * If the {@linkplain #getInterpolationCRS() interpolation CRS} is non-null, then the math transform - * input coordinates shall by (<var>interpolation</var>, <var>source</var>) tuples: for each value - * to transform, the interpolation point coordinates shall be first, followed by the source coordinates. - * - * <div class="note"><b>Example:</b> - * in a transformation between two {@linkplain org.apache.sis.referencing.crs.DefaultVerticalCRS vertical CRS}, - * if the {@linkplain #getSourceCRS() source} coordinates are (<var>z</var>) values but the coordinate operation - * additionally requires (<var>x</var>,<var>y</var>) values for {@linkplain #getInterpolationCRS() interpolation} - * purpose, then the math transform input coordinates shall be (<var>x</var>,<var>y</var>,<var>z</var>) tuples in - * that order.</div> - * - * The interpolation coordinates will {@linkplain DefaultPassThroughOperation pass through the operation} - * and appear in the math transform outputs, in the same order than inputs. + * Returns the object for transforming coordinates in the source CRS to coordinates in the target CRS. + * The transform may be {@code null} if this coordinate operation is a defining conversion. * * @return the transform from source to target CRS, or {@code null} if not applicable. */ @@ -1208,6 +1193,7 @@ check: for (int isTarget=0; ; isTarget++) { // 0 == source check; 1 /** * Invoked by JAXB after unmarshalling. + * May be overridden by subclasses. */ void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { computeTransientFields(); diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java index 128cea0c0c..a44cc95a55 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java @@ -49,6 +49,7 @@ import org.apache.sis.internal.referencing.ReferencingUtilities; import org.apache.sis.internal.metadata.ImplementationHelper; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.metadata.Identifiers; +import org.apache.sis.referencing.GeodeticException; import org.apache.sis.util.collection.Containers; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ComparisonMode; @@ -408,7 +409,7 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin private void setParameters(final GeneralParameterValue[] values) { if (parameters == null) { if (!(method instanceof DefaultOperationMethod)) { // May be a non-null proxy if defined only by xlink:href. - throw new IllegalStateException(Identifiers.missingValueForProperty(getName(), "method")); + throw new GeodeticException(Identifiers.missingValueForProperty(getName(), "method")); } /* * The descriptors in the <gml:method> element do not know the class of parameter value @@ -463,6 +464,12 @@ class AbstractSingleOperation extends AbstractCoordinateOperation implements Sin @Override final void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { super.afterUnmarshal(unmarshaller, parent); + if (parameters == null && method != null) { + final ParameterDescriptorGroup descriptor = method.getParameters(); + if (descriptor != null && descriptor.descriptors().isEmpty()) { + parameters = descriptor.createValue(); + } + } final CoordinateReferenceSystem sourceCRS = super.getSourceCRS(); final CoordinateReferenceSystem targetCRS = super.getTargetCRS(); if (transform == null && sourceCRS != null && targetCRS != null && parameters != null) try { diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java index 33808373a9..6106052ceb 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java @@ -242,7 +242,7 @@ final class DefaultConcatenatedOperation extends AbstractCoordinateOperation imp } else if (!step.isIdentity()) { flattened.add(op); } - if (mtFactory != null && step != null) { + if (mtFactory != null) { transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step; } /* diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java index fe908c6503..ccc51273de 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java @@ -36,6 +36,7 @@ import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactor import org.apache.sis.referencing.operation.matrix.Matrices; import org.apache.sis.internal.referencing.ReferencingUtilities; import org.apache.sis.internal.referencing.Resources; +import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.Utilities; @@ -217,8 +218,8 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver /** * Constructs a new conversion with the same values than the specified one, together with the - * specified source and target CRS. While the source conversion can be an arbitrary one, it is - * typically a defining conversion. + * specified source and target CRS. While the source conversion can be an arbitrary one, + * it is typically a defining conversion. * * @param definition the defining conversion. * @param source the new source CRS. @@ -376,13 +377,14 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver * * This {@code specialize(…)} method returns a conversion which implement at least the given {@code baseType} * interface, but may also implement a more specific GeoAPI interface if {@code specialize(…)} has been able - * to infer the type from this operation {@linkplain #getMethod() method}. + * to infer the type from the {@linkplain #getMethod() operation method}. * * @param <T> compile-time type of the {@code baseType} argument. * @param baseType the base GeoAPI interface to be implemented by the conversion to return. * @param sourceCRS the source CRS. * @param targetCRS the target CRS. - * @param factory the factory to use for creating a transform from the parameters or for performing axis changes. + * @param factory the factory to use for creating a transform from the parameters or for performing axis changes, + * or {@code null} for the default factory. * @return the conversion of the given type between the given CRS. * @throws ClassCastException if a contradiction is found between the given {@code baseType}, * the defining {@linkplain DefaultConversion#getInterface() conversion type} and @@ -397,12 +399,11 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver */ public <T extends Conversion> T specialize(final Class<T> baseType, final CoordinateReferenceSystem sourceCRS, final CoordinateReferenceSystem targetCRS, - final MathTransformFactory factory) throws FactoryException + MathTransformFactory factory) throws FactoryException { ArgumentChecks.ensureNonNull("baseType", baseType); ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS); ArgumentChecks.ensureNonNull("targetCRS", targetCRS); - ArgumentChecks.ensureNonNull("factory", factory); /* * Conceptual consistency check: verify that the new CRS use the same datum than the previous ones, * since the purpose of this method is not to apply datum changes. Datum changes are the purpose of @@ -425,6 +426,9 @@ public class DefaultConversion extends AbstractSingleOperation implements Conver ensureCompatibleDatum("targetCRS", sourceCRS, super.getTargetCRS()); } } + if (factory == null) { + factory = DefaultFactories.forBuildin(MathTransformFactory.class); + } return SubTypes.create(baseType, this, sourceCRS, targetCRS, factory); } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java index 9d6b58bde6..e195d20aaf 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.Collections; +import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -117,7 +118,7 @@ import static org.apache.sis.util.ArgumentChecks.*; * {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory}. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.3 * * @see DefaultConversion * @see DefaultTransformation @@ -192,7 +193,8 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements * The set of parameters, or {@code null} if none. * * <p><b>Consider this field as final!</b> - * This field is modified only at unmarshalling time by {@link #setDescriptors(GeneralParameterDescriptor[])}</p> + * This field is modified only at unmarshalling time by {@link #setDescriptors(GeneralParameterDescriptor[])} + * or {@link #afterUnmarshal(Unmarshaller, Object)}.</p> */ @SuppressWarnings("serial") // Not statically typed as Serializable. private ParameterDescriptorGroup parameters; @@ -303,10 +305,7 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements targetDimensions = transform.getTargetDimensions(); if (transform instanceof Parameterized) { parameters = ((Parameterized) transform).getParameterDescriptors(); - } else { - parameters = null; } - formula = null; } /** @@ -632,9 +631,8 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements * Returns the set of parameters. * * <div class="note"><b>Departure from the ISO 19111 standard:</b> - * this property is mandatory according ISO 19111, but may be null in Apache SIS if the - * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it - * or if this {@code OperationMethod} has been read from an incomplete GML document.</div> + * this property is mandatory according ISO 19111, but may be {@code null} in Apache SIS if the + * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it.</div> * * @return the parameters, or {@code null} if unknown. * @@ -971,4 +969,16 @@ public class DefaultOperationMethod extends AbstractIdentifiedObject implements parameters = new DefaultParameterDescriptorGroup(IdentifiedObjects.getProperties(previous), previous.getMinimumOccurs(), previous.getMaximumOccurs(), descriptors); } + + /** + * Invoked by JAXB after unmarshalling. If the {@code <gml:OperationMethod>} element does not contain + * any {@code <gml:parameter>}, we assume that this is a valid parameterless operation (as opposed to + * an operation with unknown parameters). We need this assumption because, contrarily to GeoAPI model, + * the GML schema does not differentiate "no parameters" from "unspecified parameters". + */ + private void afterUnmarshal(final Unmarshaller unmarshaller, final Object parent) { + if (parameters == null) { + parameters = CC_OperationMethod.group(super.getName(), new GeneralParameterDescriptor[0]); + } + } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java index 950731c638..1d3b09fc96 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java @@ -19,24 +19,29 @@ package org.apache.sis.referencing.operation; import java.util.Map; import java.util.Arrays; import java.util.Objects; +import javax.xml.bind.Unmarshaller; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.opengis.util.FactoryException; import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.PassThroughOperation; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.CompoundCRS; +import org.apache.sis.referencing.GeodeticException; import org.apache.sis.referencing.operation.transform.MathTransforms; import org.apache.sis.referencing.operation.transform.PassThroughTransform; import org.apache.sis.internal.referencing.ReferencingUtilities; import org.apache.sis.internal.metadata.ImplementationHelper; import org.apache.sis.util.UnsupportedImplementationException; -import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.ArraysExt; import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.resources.Errors; import org.apache.sis.io.wkt.FormattableObject; import org.apache.sis.io.wkt.Formatter; +import org.apache.sis.referencing.CRS; import static org.apache.sis.util.Utilities.deepEquals; @@ -45,7 +50,7 @@ import static org.apache.sis.util.Utilities.deepEquals; * Specifies that a subset of a coordinate tuple is subject to a specific coordinate operation. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 0.7 + * @version 1.3 * @since 0.6 * @module */ @@ -111,9 +116,8 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp final int firstAffectedCoordinate, final int numTrailingCoordinates) { - super(properties, sourceCRS, targetCRS, null, MathTransforms.passThrough( - firstAffectedCoordinate, operation.getMathTransform(), numTrailingCoordinates)); - ArgumentChecks.ensureNonNull("operation", operation); + super(properties, sourceCRS, targetCRS, null, + MathTransforms.passThrough(firstAffectedCoordinate, operation.getMathTransform(), numTrailingCoordinates)); this.operation = operation; } @@ -192,11 +196,11 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp final MathTransform transform = super.getMathTransform(); if (transform instanceof PassThroughTransform) { return ((PassThroughTransform) transform).getModifiedCoordinates(); - } else { + } else if (operation != null) { /* - * Should not happen with objects created by public methods since the constructor created the transform - * itself. However may happen with operations parsed from GML. As a fallback, search in the components - * of CompoundCRS. This is not a universal fallback, but work for the most straightforward cases. + * Should not happen with objects created by public methods since the constructor created the transform itself. + * However may happen with operations parsed from GML. As a fallback, search in the components of CompoundCRS. + * This is not a universal fallback, but works for the most straightforward cases. */ final CoordinateReferenceSystem sourceCRS = super.getSourceCRS(); if (sourceCRS instanceof CompoundCRS) { @@ -214,8 +218,8 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp firstAffectedCoordinate += dim; } } - throw new UnsupportedImplementationException(transform.getClass()); } + throw new UnsupportedImplementationException(transform.getClass()); } /** @@ -300,8 +304,8 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp private DefaultPassThroughOperation() { /* * A sub-operation 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 an exception in that method causes the - * whole unmarshalling to fail. But the CC_CoordinateOperation adapter does some verifications. + * would have to be done in an `afterMarshal(…)` method and throwing an exception in that method causes the + * whole unmarshalling to fail. But the `CC_CoordinateOperation` adapter does some verifications. */ } @@ -327,37 +331,79 @@ public class DefaultPassThroughOperation extends AbstractCoordinateOperation imp */ @XmlElement(name = "modifiedCoordinate", required = true) private int[] getIndices() { - final int[] indices = getModifiedCoordinates(); - for (int i=0; i<indices.length; i++) { - indices[i]++; + final int[] dimensions = getModifiedCoordinates(); + for (int i=0; i<dimensions.length; i++) { + dimensions[i]++; } - return indices; + return dimensions; } /** * Invoked by JAXB at unmarshalling time for setting the modified coordinates. + * This method needs to be invoked last, even if the {@code <gml:modifiedCoordinate>} + * elements are not last in the GML document. It is the case when using JAXB because + * multiple occurrences of {@code <gml:modifiedCoordinate>} are aggregated in an array. */ - private void setIndices(final int[] coordinates) { - String missing = "sourceCRS"; - final CoordinateReferenceSystem sourceCRS = super.getSourceCRS(); - if (sourceCRS != null) { - missing = "modifiedCoordinate"; - if (coordinates != null && coordinates.length != 0) { - missing = "coordOperation"; - if (operation != null) { - for (int i=1; i<coordinates.length; i++) { - final int previous = coordinates[i-1]; - if (previous < 1 || coordinates[i] != previous + 1) { - throw new IllegalArgumentException(Errors.format( - Errors.Keys.CanNotAssign_2, missing, Arrays.toString(coordinates))); + private void setIndices(final int[] dimensions) { + /* + * Argument and state validation. + */ + String missing = "modifiedCoordinate"; + FactoryException cause = null; + final int n = dimensions.length; + if (n != 0) { + if (!ArraysExt.isRange(dimensions[0], dimensions)) { + throw new GeodeticException(Errors.format(Errors.Keys.CanNotAssign_2, missing, Arrays.toString(dimensions))); + } + missing = "sourceCRS"; + final CoordinateReferenceSystem sourceCRS = super.getSourceCRS(); + if (sourceCRS != null) { + missing = "targetCRS"; + final CoordinateReferenceSystem targetCRS = super.getTargetCRS(); + if (targetCRS != null) { + missing = "coordOperation"; + if (operation != null) { + /* + * If the operation is a defining operation, we need to replace it by a full operation. + * After that, we can store the modified coordinate indices in the transform field. + */ + MathTransform subTransform = operation.getMathTransform(); + if (operation instanceof Conversion) { + CoordinateReferenceSystem sourceSub = operation.getSourceCRS(); + CoordinateReferenceSystem targetSub = operation.getTargetCRS(); + if (subTransform == null || sourceSub == null || targetSub == null) try { + final int[] zeroBased = dimensions.clone(); + for (int i=0; i<n; i++) zeroBased[i]--; + if (sourceSub == null) sourceSub = CRS.selectDimensions(sourceCRS, zeroBased); + if (targetSub == null) targetSub = CRS.selectDimensions(targetCRS, zeroBased); + operation = DefaultConversion.castOrCopy((Conversion) operation) + .specialize(Conversion.class, sourceSub, targetSub, null); + subTransform = operation.getMathTransform(); + } catch (FactoryException e) { + cause = e; + } + } + if (subTransform != null) { + transform = MathTransforms.passThrough(dimensions[0] - 1, subTransform, + ReferencingUtilities.getDimension(sourceCRS) - dimensions[n-1]); + return; } } - transform = MathTransforms.passThrough(coordinates[0] - 1, operation.getMathTransform(), - ReferencingUtilities.getDimension(sourceCRS) - coordinates[coordinates.length - 1]); - return; } } } - throw new IllegalStateException(Errors.format(Errors.Keys.MissingComponentInElement_2, missing, "PassThroughOperation")); + throw new GeodeticException(Errors.format(Errors.Keys.MissingComponentInElement_2, "PassThroughOperation", missing), cause); + } + + /** + * Invoked by JAXB after unmarshalling. If needed, this method tries to infer source/target CRS + * of the nested operation from the source/target CRS if the enclosing pass-through operation. + */ + @Override + void afterUnmarshal(Unmarshaller unmarshaller, Object parent) { + super.afterUnmarshal(unmarshaller, parent); + if (transform == null) { + setIndices(ArraysExt.EMPTY_INT); // Cause an exception to be thrown. + } } } diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java index 51e1e6445c..ea3e72e12f 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/SubTypes.java @@ -168,7 +168,7 @@ final class SubTypes { conversion = new DefaultConversion(definition, sourceCRS, targetCRS, factory, actual); } /* - * The DefaultConversion constructor may have used by MathTransformFactory for creating the actual + * The DefaultConversion constructor may have used MathTransformFactory for creating the actual * MathTransform object. In such case, we can use the knownledge that the factory has about the * coordinate operation for refining again the type of the object to be returned. */ diff --git a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java index cb937dbd6c..9433d0f367 100644 --- a/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java +++ b/core/sis-referencing/src/test/java/org/apache/sis/io/wkt/WKTParserTest.java @@ -480,6 +480,7 @@ public final strictfp class WKTParserTest extends CRSParserTest { */ @Test @Override + @org.junit.Ignore("Pending new AxisDirection code list in GeoAPI.") public void testEngineeringForShip() throws FactoryException { super.testEngineeringForShip(); final CoordinateSystem cs = object.getCoordinateSystem(); diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java index ad85cad854..6dfb161f0c 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/MovingFeatureIterator.java @@ -114,7 +114,8 @@ final class MovingFeatureIterator extends FeatureIterator implements Consumer<Lo /** * Executes the given action for the next moving feature or for all remaining moving features. - * This method assumes that the 4 first columns are as documented in the code inside constructor. + * This method assumes that the 4 first columns are identifier, start time, end time and + * optional attributes in that order. * * @param action the action to execute as soon as the {@code mfidref} change, or {@code null} if none. * @param all {@code true} for executing the given action on all remaining features. diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java index 1322b78f86..27f9685fb7 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java @@ -263,7 +263,7 @@ final class Store extends URIDataStore implements FeatureSet { throw new DataStoreContentException(Resources.forLocale(getLocale()) .getString(Resources.Keys.ShallBeDeclaredBefore_2, "@columns", "@stboundedby")); } - envelope = parseEnvelope(elements); // Also set 'timeEncoding' and 'spatialDimensionCount'. + envelope = parseEnvelope(elements); // Also set `timeEncoding` and `spatialDimensionCount`. dissociate |= (timeEncoding == null); // Need to be updated before parseFeatureType(…) execution. break; }