You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2022/12/06 19:11:50 UTC

[sis] branch master updated (ea85b9b19d -> 2b6eec917a)

This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git


    from ea85b9b19d Merge branch 'geoapi-3.1'
     add e2a4f94058 Anticipate some of class name changes expected with upgrade to ISO 19157:2022. Not all impacted types are renamed, only the ones that are new in SIS 1.3 release. Other impacted classes and methods will be renamed later.
     add b7d5da1cc1 Minor documentation fixes.
     add 138a82a9e8 GPX data store should implement `WritableFeatureSet`.
     add aa41c4d0ec GPX store should implement `WritableFeatureSet` only if the stream allows write operations.
     add 09d94c2c6c Add a warning about possible future API change. https://issues.apache.org/jira/browse/SIS-560
     add d438a0d1b6 Fix JUnit test failures when the tests are run without EPSG database.
     add 05a9bb3b01 Merge branch 'geoapi-4.0' into geoapi-3.1
     new 2b6eec917a Merge branch 'geoapi-3.1'

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../apache/sis/console/FormattedOutputCommand.java |   2 +-
 .../org/apache/sis/console/CRSCommandTest.java     |  14 +-
 .../org/apache/sis/internal/doclet/Preformat.java  |   2 +-
 .../org/apache/sis/internal/maven/Filenames.java   |   2 +-
 .../coverage/j2d/MultiBandsIndexColorModel.java    |   2 +-
 .../org/apache/sis/internal/feature/Resources.java |   2 +-
 .../internal/jaxb/metadata/DQM_Description.java    |  18 +-
 .../sis/internal/jaxb/metadata/DQM_Measure.java    |  18 +-
 .../DQ_StandaloneQualityReportInformation.java     |  18 +-
 .../jaxb/metadata/replace/QualityParameter.java    |   4 +-
 .../sis/internal/simple/CitationConstant.java      |  12 +-
 .../org/apache/sis/metadata/PropertyAccessor.java  |   4 +-
 .../org/apache/sis/metadata/TreeNodeChildren.java  |   2 +-
 .../sis/metadata/iso/quality/AbstractElement.java  |   6 +-
 .../metadata/iso/quality/DefaultBasicMeasure.java  |   6 +-
 .../metadata/iso/quality/DefaultDataQuality.java   |   6 +-
 ...ava => DefaultEvaluationReportInformation.java} |   6 +-
 ...ription.java => DefaultMeasureDescription.java} |   8 +-
 ...aultMeasure.java => DefaultQualityMeasure.java} |  24 +-
 .../sis/metadata/iso/quality/DefaultUsability.java |   4 +-
 .../iso/quality/DefaultUsabilityElement.java       | 108 --------
 .../apache/sis/util/iso/DefaultNameFactory.java    |   2 +-
 .../org/apache/sis/util/iso/DefaultNameSpace.java  |   2 +-
 .../java/org/apache/sis/xml/NilObjectHandler.java  |   7 +-
 .../java/org/apache/sis/xml/ValueConverter.java    |   2 +-
 .../metadata/replace/QualityParameterTest.java     |   4 +-
 .../java/org/apache/sis/test/MetadataAssert.java   |   4 +-
 .../apache/sis/test/xml/DocumentComparator.java    |   7 +-
 .../java/org/apache/sis/test/xml/package-info.java |   2 +-
 .../test/java/org/apache/sis/xml/XLinkTest.java    |   2 +-
 .../apache/sis/internal/gazetteer/Resources.java   |   4 +-
 .../org/apache/sis/geometry/AbstractEnvelope.java  |   2 +-
 .../java/org/apache/sis/geometry/Envelope2D.java   |   2 +-
 .../apache/sis/internal/referencing/Resources.java |   4 +-
 .../operation/CoordinateOperationFinder.java       |   2 +-
 .../operation/matrix/AffineTransforms2D.java       |   4 +-
 .../operation/transform/AbstractMathTransform.java |   2 +-
 .../operation/transform/ConcatenatedTransform.java |   2 +-
 .../transform/ConcatenatedTransform2D.java         |   2 +-
 .../transform/ConcatenatedTransformDirect2D.java   |   2 +-
 .../operation/transform/LinearTransform1D.java     |   2 +-
 .../sis/referencing/GeodeticObjectVerifier.java    |   6 +-
 .../apache/sis/referencing/crs/HardCodedCRS.java   |   6 +-
 .../factory/CommonAuthorityFactoryTest.java        |  27 +-
 .../transform/ConcatenatedTransformTest.java       |   4 +-
 .../operation/transform/MathTransformWrapper.java  |   2 +-
 .../java/org/apache/sis/internal/jdk9/JDK9.java    |  13 +
 .../apache/sis/util/logging/MonolineFormatter.java |   2 +-
 .../java/org/apache/sis/util/resources/Errors.java |   2 +-
 .../sis/util/resources/IndexedResourceBundle.java  |   2 +-
 .../org/apache/sis/util/resources/Messages.java    |   2 +-
 .../org/apache/sis/util/resources/Vocabulary.java  |   2 +-
 pom.xml                                            |   2 +-
 .../apache/sis/storage/netcdf/AttributeNames.java  |   4 +-
 .../shapefile/jdbc/sql/ClauseResolver.java         |   2 +-
 .../sis/internal/storage/image/FormatFinder.java   |   2 +-
 .../sis/internal/storage/io/IOUtilities.java       |   2 +-
 .../sis/storage/IllegalFeatureTypeException.java   |   2 +-
 .../org/apache/sis/storage/StorageConnector.java   |   2 +-
 .../org/apache/sis/storage/WritableFeatureSet.java |  10 +
 .../apache/sis/internal/storage/gpx/Reader.java    |   2 +-
 .../org/apache/sis/internal/storage/gpx/Store.java |  63 ++---
 .../sis/internal/storage/gpx/StoreProvider.java    |  10 +-
 .../apache/sis/internal/storage/gpx/Updater.java   |  91 +++++++
 .../sis/internal/storage/gpx/WritableStore.java    | 182 +++++++++++++
 .../apache/sis/internal/storage/gpx/Writer.java    |  14 +-
 .../storage/xml/stream/RewriteOnUpdate.java        | 283 +++++++++++++++++++++
 .../internal/storage/xml/stream/StaxDataStore.java |  83 +++---
 .../storage/xml/stream/StaxStreamWriter.java       |  12 +-
 .../internal/storage/xml/stream/package-info.java  |   2 +-
 .../sis/internal/storage/gpx/UpdaterTest.java      | 182 +++++++++++++
 .../sis/internal/storage/gpx/WriterTest.java       |  14 +-
 .../org/apache/sis/test/suite/GPXTestSuite.java    |   3 +-
 73 files changed, 1014 insertions(+), 349 deletions(-)
 rename core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/{DefaultStandaloneQualityReportInformation.java => DefaultEvaluationReportInformation.java} (95%)
 rename core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/{DefaultDescription.java => DefaultMeasureDescription.java} (95%)
 rename core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/{DefaultMeasure.java => DefaultQualityMeasure.java} (95%)
 delete mode 100644 core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultUsabilityElement.java
 create mode 100644 storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Updater.java
 create mode 100644 storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/WritableStore.java
 create mode 100644 storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/RewriteOnUpdate.java
 create mode 100644 storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/UpdaterTest.java


[sis] 01/01: Merge branch 'geoapi-3.1'

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 2b6eec917a34e971fa73968d92a9a67c54da0075
Merge: ea85b9b19d 05a9bb3b01
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Dec 6 20:10:34 2022 +0100

    Merge branch 'geoapi-3.1'

 .../apache/sis/console/FormattedOutputCommand.java |   2 +-
 .../org/apache/sis/console/CRSCommandTest.java     |  14 +-
 .../org/apache/sis/internal/doclet/Preformat.java  |   2 +-
 .../org/apache/sis/internal/maven/Filenames.java   |   2 +-
 .../coverage/j2d/MultiBandsIndexColorModel.java    |   2 +-
 .../org/apache/sis/internal/feature/Resources.java |   2 +-
 .../internal/jaxb/metadata/DQM_Description.java    |  18 +-
 .../sis/internal/jaxb/metadata/DQM_Measure.java    |  18 +-
 .../DQ_StandaloneQualityReportInformation.java     |  18 +-
 .../jaxb/metadata/replace/QualityParameter.java    |   4 +-
 .../sis/internal/simple/CitationConstant.java      |  12 +-
 .../org/apache/sis/metadata/PropertyAccessor.java  |   4 +-
 .../org/apache/sis/metadata/TreeNodeChildren.java  |   2 +-
 .../sis/metadata/iso/quality/AbstractElement.java  |   6 +-
 .../metadata/iso/quality/DefaultBasicMeasure.java  |   6 +-
 .../metadata/iso/quality/DefaultDataQuality.java   |   6 +-
 ...ava => DefaultEvaluationReportInformation.java} |   6 +-
 ...ription.java => DefaultMeasureDescription.java} |   8 +-
 ...aultMeasure.java => DefaultQualityMeasure.java} |  24 +-
 .../sis/metadata/iso/quality/DefaultUsability.java |   4 +-
 .../iso/quality/DefaultUsabilityElement.java       | 108 --------
 .../apache/sis/util/iso/DefaultNameFactory.java    |   2 +-
 .../org/apache/sis/util/iso/DefaultNameSpace.java  |   2 +-
 .../java/org/apache/sis/xml/NilObjectHandler.java  |   7 +-
 .../java/org/apache/sis/xml/ValueConverter.java    |   2 +-
 .../metadata/replace/QualityParameterTest.java     |   4 +-
 .../java/org/apache/sis/test/MetadataAssert.java   |   4 +-
 .../apache/sis/test/xml/DocumentComparator.java    |   7 +-
 .../java/org/apache/sis/test/xml/package-info.java |   2 +-
 .../test/java/org/apache/sis/xml/XLinkTest.java    |   2 +-
 .../apache/sis/internal/gazetteer/Resources.java   |   4 +-
 .../org/apache/sis/geometry/AbstractEnvelope.java  |   2 +-
 .../java/org/apache/sis/geometry/Envelope2D.java   |   2 +-
 .../apache/sis/internal/referencing/Resources.java |   4 +-
 .../operation/CoordinateOperationFinder.java       |   2 +-
 .../operation/matrix/AffineTransforms2D.java       |   4 +-
 .../operation/transform/AbstractMathTransform.java |   2 +-
 .../operation/transform/ConcatenatedTransform.java |   2 +-
 .../transform/ConcatenatedTransform2D.java         |   2 +-
 .../transform/ConcatenatedTransformDirect2D.java   |   2 +-
 .../operation/transform/LinearTransform1D.java     |   2 +-
 .../sis/referencing/GeodeticObjectVerifier.java    |   6 +-
 .../apache/sis/referencing/crs/HardCodedCRS.java   |   6 +-
 .../factory/CommonAuthorityFactoryTest.java        |  27 +-
 .../transform/ConcatenatedTransformTest.java       |   4 +-
 .../operation/transform/MathTransformWrapper.java  |   2 +-
 .../java/org/apache/sis/internal/jdk9/JDK9.java    |  13 +
 .../apache/sis/util/logging/MonolineFormatter.java |   2 +-
 .../java/org/apache/sis/util/resources/Errors.java |   2 +-
 .../sis/util/resources/IndexedResourceBundle.java  |   2 +-
 .../org/apache/sis/util/resources/Messages.java    |   2 +-
 .../org/apache/sis/util/resources/Vocabulary.java  |   2 +-
 pom.xml                                            |   2 +-
 .../apache/sis/storage/netcdf/AttributeNames.java  |   4 +-
 .../shapefile/jdbc/sql/ClauseResolver.java         |   2 +-
 .../sis/internal/storage/image/FormatFinder.java   |   2 +-
 .../sis/internal/storage/io/IOUtilities.java       |   2 +-
 .../sis/storage/IllegalFeatureTypeException.java   |   2 +-
 .../org/apache/sis/storage/StorageConnector.java   |   2 +-
 .../org/apache/sis/storage/WritableFeatureSet.java |  10 +
 .../apache/sis/internal/storage/gpx/Reader.java    |   2 +-
 .../org/apache/sis/internal/storage/gpx/Store.java |  63 ++---
 .../sis/internal/storage/gpx/StoreProvider.java    |  10 +-
 .../apache/sis/internal/storage/gpx/Updater.java   |  91 +++++++
 .../sis/internal/storage/gpx/WritableStore.java    | 182 +++++++++++++
 .../apache/sis/internal/storage/gpx/Writer.java    |  14 +-
 .../storage/xml/stream/RewriteOnUpdate.java        | 283 +++++++++++++++++++++
 .../internal/storage/xml/stream/StaxDataStore.java |  83 +++---
 .../storage/xml/stream/StaxStreamWriter.java       |  12 +-
 .../internal/storage/xml/stream/package-info.java  |   2 +-
 .../sis/internal/storage/gpx/UpdaterTest.java      | 182 +++++++++++++
 .../sis/internal/storage/gpx/WriterTest.java       |  14 +-
 .../org/apache/sis/test/suite/GPXTestSuite.java    |   3 +-
 73 files changed, 1014 insertions(+), 349 deletions(-)

diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Description.java
index e0d21d9efb,2334675a62..35b4851233
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Description.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Description.java
@@@ -17,7 -17,8 +17,7 @@@
  package org.apache.sis.internal.jaxb.metadata;
  
  import javax.xml.bind.annotation.XmlElementRef;
- import org.apache.sis.metadata.iso.quality.DefaultDescription;
 -import org.opengis.metadata.quality.Description;
+ import org.apache.sis.metadata.iso.quality.DefaultMeasureDescription;
  import org.apache.sis.internal.jaxb.gco.PropertyType;
  
  
@@@ -30,7 -31,7 +30,7 @@@
   * @since   1.3
   * @module
   */
- public final class DQM_Description extends PropertyType<DQM_Description, DefaultDescription> {
 -public final class DQM_Description extends PropertyType<DQM_Description, Description> {
++public final class DQM_Description extends PropertyType<DQM_Description, DefaultMeasureDescription> {
      /**
       * Empty constructor for JAXB only.
       */
@@@ -42,17 -43,17 +42,17 @@@
       * This method is indirectly invoked by the private constructor
       * below, so it shall not depend on the state of this object.
       *
-      * @return {@code DefaultDescription.class}
 -     * @return {@code Description.class}
++     * @return {@code DefaultMeasureDescription.class}
       */
      @Override
-     protected Class<DefaultDescription> getBoundType() {
-         return DefaultDescription.class;
 -    protected Class<Description> getBoundType() {
 -        return Description.class;
++    protected Class<DefaultMeasureDescription> getBoundType() {
++        return DefaultMeasureDescription.class;
      }
  
      /**
       * Constructor for the {@link #wrap} method only.
       */
-     private DQM_Description(final DefaultDescription metadata) {
 -    private DQM_Description(final Description metadata) {
++    private DQM_Description(final DefaultMeasureDescription metadata) {
          super(metadata);
      }
  
@@@ -64,7 -65,7 +64,7 @@@
       * @return a {@code PropertyType} wrapping the given the metadata element.
       */
      @Override
-     protected DQM_Description wrap(final DefaultDescription metadata) {
 -    protected DQM_Description wrap(final Description metadata) {
++    protected DQM_Description wrap(final DefaultMeasureDescription metadata) {
          return new DQM_Description(metadata);
      }
  
@@@ -76,8 -77,8 +76,8 @@@
       * @return the metadata to be marshalled.
       */
      @XmlElementRef
-     public DefaultDescription getElement() {
+     public DefaultMeasureDescription getElement() {
 -        return DefaultMeasureDescription.castOrCopy(metadata);
 +        return metadata;
      }
  
      /**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Measure.java
index 9849de5be8,26563d3502..08faf49d64
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Measure.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQM_Measure.java
@@@ -17,7 -17,8 +17,7 @@@
  package org.apache.sis.internal.jaxb.metadata;
  
  import javax.xml.bind.annotation.XmlElementRef;
- import org.apache.sis.metadata.iso.quality.DefaultMeasure;
 -import org.opengis.metadata.quality.Measure;
+ import org.apache.sis.metadata.iso.quality.DefaultQualityMeasure;
  import org.apache.sis.internal.jaxb.gco.PropertyType;
  
  
@@@ -30,7 -31,7 +30,7 @@@
   * @since   1.3
   * @module
   */
- public final class DQM_Measure extends PropertyType<DQM_Measure, DefaultMeasure> {
 -public final class DQM_Measure extends PropertyType<DQM_Measure, Measure> {
++public final class DQM_Measure extends PropertyType<DQM_Measure, DefaultQualityMeasure> {
      /**
       * Empty constructor for JAXB only.
       */
@@@ -42,17 -43,17 +42,17 @@@
       * This method is indirectly invoked by the private constructor
       * below, so it shall not depend on the state of this object.
       *
-      * @return {@code DefaultMeasure.class}
 -     * @return {@code Measure.class}
++     * @return {@code DefaultQualityMeasure.class}
       */
      @Override
-     protected Class<DefaultMeasure> getBoundType() {
-         return DefaultMeasure.class;
 -    protected Class<Measure> getBoundType() {
 -        return Measure.class;
++    protected Class<DefaultQualityMeasure> getBoundType() {
++        return DefaultQualityMeasure.class;
      }
  
      /**
       * Constructor for the {@link #wrap} method only.
       */
-     private DQM_Measure(final DefaultMeasure metadata) {
 -    private DQM_Measure(final Measure metadata) {
++    private DQM_Measure(final DefaultQualityMeasure metadata) {
          super(metadata);
      }
  
@@@ -64,7 -65,7 +64,7 @@@
       * @return a {@code PropertyType} wrapping the given the metadata element.
       */
      @Override
-     protected DQM_Measure wrap(final DefaultMeasure metadata) {
 -    protected DQM_Measure wrap(final Measure metadata) {
++    protected DQM_Measure wrap(final DefaultQualityMeasure metadata) {
          return new DQM_Measure(metadata);
      }
  
@@@ -76,8 -77,8 +76,8 @@@
       * @return the metadata to be marshalled.
       */
      @XmlElementRef
-     public DefaultMeasure getElement() {
+     public DefaultQualityMeasure getElement() {
 -        return DefaultQualityMeasure.castOrCopy(metadata);
 +        return metadata;
      }
  
      /**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQ_StandaloneQualityReportInformation.java
index b219193903,cb94df4e17..53b25b04bc
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQ_StandaloneQualityReportInformation.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/DQ_StandaloneQualityReportInformation.java
@@@ -17,7 -17,8 +17,7 @@@
  package org.apache.sis.internal.jaxb.metadata;
  
  import javax.xml.bind.annotation.XmlElementRef;
- import org.apache.sis.metadata.iso.quality.DefaultStandaloneQualityReportInformation;
 -import org.opengis.metadata.quality.StandaloneQualityReportInformation;
+ import org.apache.sis.metadata.iso.quality.DefaultEvaluationReportInformation;
  import org.apache.sis.internal.jaxb.gco.PropertyType;
  
  
@@@ -35,7 -36,7 +35,7 @@@
   * @module
   */
  public final class DQ_StandaloneQualityReportInformation extends
-         PropertyType<DQ_StandaloneQualityReportInformation, DefaultStandaloneQualityReportInformation>
 -        PropertyType<DQ_StandaloneQualityReportInformation, StandaloneQualityReportInformation>
++        PropertyType<DQ_StandaloneQualityReportInformation, DefaultEvaluationReportInformation>
  {
      /**
       * Empty constructor for JAXB only.
@@@ -48,17 -49,17 +48,17 @@@
       * This method is indirectly invoked by the private constructor
       * below, so it shall not depend on the state of this object.
       *
-      * @return {@code DefaultStandaloneQualityReportInformation.class}
 -     * @return {@code StandaloneQualityReportInformation.class}
++     * @return {@code DefaultEvaluationReportInformation.class}
       */
      @Override
-     protected Class<DefaultStandaloneQualityReportInformation> getBoundType() {
-         return DefaultStandaloneQualityReportInformation.class;
 -    protected Class<StandaloneQualityReportInformation> getBoundType() {
 -        return StandaloneQualityReportInformation.class;
++    protected Class<DefaultEvaluationReportInformation> getBoundType() {
++        return DefaultEvaluationReportInformation.class;
      }
  
      /**
       * Constructor for the {@link #wrap} method only.
       */
-     private DQ_StandaloneQualityReportInformation(final DefaultStandaloneQualityReportInformation metadata) {
 -    private DQ_StandaloneQualityReportInformation(final StandaloneQualityReportInformation metadata) {
++    private DQ_StandaloneQualityReportInformation(final DefaultEvaluationReportInformation metadata) {
          super(metadata);
      }
  
@@@ -71,7 -72,7 +71,7 @@@
       *         or {@code null} if marshalling a too old version of the standard.
       */
      @Override
-     protected DQ_StandaloneQualityReportInformation wrap(final DefaultStandaloneQualityReportInformation metadata) {
 -    protected DQ_StandaloneQualityReportInformation wrap(final StandaloneQualityReportInformation metadata) {
++    protected DQ_StandaloneQualityReportInformation wrap(final DefaultEvaluationReportInformation metadata) {
          return accept2014() ? new DQ_StandaloneQualityReportInformation(metadata) : null;
      }
  
@@@ -83,8 -84,8 +83,8 @@@
       * @return the metadata to be marshalled.
       */
      @XmlElementRef
-     public DefaultStandaloneQualityReportInformation getElement() {
+     public DefaultEvaluationReportInformation getElement() {
 -        return DefaultEvaluationReportInformation.castOrCopy(metadata);
 +        return metadata;
      }
  
      /**
diff --cc core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameter.java
index 5091d07037,3f04454a6e..0ea368d643
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameter.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameter.java
@@@ -93,7 -96,7 +93,7 @@@ public final class QualityParameter ext
       * @see #getDescription()
       */
      @XmlElement
-     DefaultDescription description;
 -    Description description;
++    DefaultMeasureDescription description;
  
      /**
       * Value type of the data quality parameter (shall be one of the data types defined in ISO/TS 19103:2005).
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
index bc69208f3c,8524487ccb..0dccfd88c9
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/AbstractElement.java
@@@ -221,8 -218,11 +221,8 @@@ public class AbstractElement extends IS
              return AbstractCompleteness.castOrCopy((Completeness) object);
          }
          if (object instanceof Usability) {
-             return DefaultUsabilityElement.castOrCopy((Usability) object);
+             return DefaultUsability.castOrCopy((Usability) object);
          }
 -        if (object instanceof Metaquality) {
 -            return AbstractMetaquality.castOrCopy((Metaquality) object);
 -        }
          // Intentionally tested after the sub-interfaces.
          if (object == null || object instanceof AbstractElement) {
              return (AbstractElement) object;
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultBasicMeasure.java
index 29e6e4fa80,c89a56ce63..a9e675f497
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultBasicMeasure.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultBasicMeasure.java
@@@ -86,7 -81,7 +86,7 @@@ public class DefaultBasicMeasure extend
       * Illustration of the use of a data quality measure.
       */
      @SuppressWarnings("serial")
-     private DefaultDescription example;
 -    private Description example;
++    private DefaultMeasureDescription example;
  
      /**
       * Value type for the result of the basic measure.
@@@ -164,9 -186,9 +164,9 @@@
       *
       * @return usage example, or {@code null} if none.
       */
 -    @Override
      @XmlElement(name = "example")
 -    public Description getExample() {
 +    @UML(identifier="example", obligation=OPTIONAL, specification=UNSPECIFIED)
-     public DefaultDescription getExample() {
++    public DefaultMeasureDescription getExample() {
          return example;
      }
  
@@@ -175,7 -197,7 +175,7 @@@
       *
       * @param  newValues  the new basic measure example.
       */
-     public void setExample(final DefaultDescription newValues) {
 -    public void setExample(final Description newValues) {
++    public void setExample(final DefaultMeasureDescription newValues) {
          checkWritePermission(example);
          example = newValues;
      }
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
index cb44ed7531,ec58188f17..506ee71b45
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultDataQuality.java
@@@ -100,7 -96,7 +100,7 @@@ public class DefaultDataQuality extend
       * Can be used for providing more details than reported as standard metadata.
       */
      @SuppressWarnings("serial")
-     private DefaultStandaloneQualityReportInformation standaloneQualityReport;
 -    private StandaloneQualityReportInformation standaloneQualityReport;
++    private DefaultEvaluationReportInformation standaloneQualityReport;
  
      /**
       * Constructs an initially empty data quality.
@@@ -226,9 -220,9 +226,9 @@@
       *
       * @since 1.3
       */
 -    @Override
      @XmlElement(name = "standaloneQualityReport")
 -    public StandaloneQualityReportInformation getStandaloneQualityReport() {
 +    @UML(identifier="standaloneQualityReport", obligation=OPTIONAL, specification=UNSPECIFIED)
-     public DefaultStandaloneQualityReportInformation getStandaloneQualityReport() {
++    public DefaultEvaluationReportInformation getStandaloneQualityReport() {
          return standaloneQualityReport;
      }
  
@@@ -239,7 -233,7 +239,7 @@@
       *
       * @since 1.3
       */
-     public void setStandaloneQualityReport(final DefaultStandaloneQualityReportInformation newValue) {
 -    public void setStandaloneQualityReport(final StandaloneQualityReportInformation newValue) {
++    public void setStandaloneQualityReport(final DefaultEvaluationReportInformation newValue) {
          checkWritePermission(standaloneQualityReport);
          standaloneQualityReport = newValue;
      }
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationReportInformation.java
index 9fe15af3ab,63d88ad90e..e983b6961e
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationReportInformation.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationReportInformation.java
@@@ -57,8 -53,7 +57,8 @@@ import static org.opengis.annotation.Sp
      "abstract"
  })
  @XmlRootElement(name = "DQ_StandaloneQualityReportInformation")
 -public class DefaultEvaluationReportInformation extends ISOMetadata implements StandaloneQualityReportInformation {
 +@UML(identifier="DQ_StandaloneQualityReportInformation", specification=UNSPECIFIED)
- public class DefaultStandaloneQualityReportInformation extends ISOMetadata {
++public class DefaultEvaluationReportInformation extends ISOMetadata {
      /**
       * Serial number for inter-operability with different versions.
       */
@@@ -88,8 -83,10 +88,8 @@@
       * given object are not recursively copied.
       *
       * @param object  the metadata to copy values from, or {@code null} if none.
 -     *
 -     * @see #castOrCopy(StandaloneQualityReportInformation)
       */
-     public DefaultStandaloneQualityReportInformation(final DefaultStandaloneQualityReportInformation object) {
 -    public DefaultEvaluationReportInformation(final StandaloneQualityReportInformation object) {
++    public DefaultEvaluationReportInformation(final DefaultEvaluationReportInformation object) {
          super(object);
          if (object != null) {
              reportReference  = object.getReportReference();
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultMeasureDescription.java
index 8187447261,1f6a6c3c8a..cfb6188b1e
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultMeasureDescription.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultMeasureDescription.java
@@@ -59,8 -53,7 +59,8 @@@ import static org.opengis.annotation.Sp
      "extendedDescription"
  })
  @XmlRootElement(name = "DQM_Description", namespace = Namespaces.DQM)
 -public class DefaultMeasureDescription extends ISOMetadata implements Description {
 +@UML(identifier="DQM_Description", specification=UNSPECIFIED)
- public class DefaultDescription extends ISOMetadata {
++public class DefaultMeasureDescription extends ISOMetadata {
      /**
       * Serial number for inter-operability with different versions.
       */
@@@ -99,8 -92,10 +99,8 @@@
       * given object are not recursively copied.
       *
       * @param object  the metadata to copy values from, or {@code null} if none.
 -     *
 -     * @see #castOrCopy(Description)
       */
-     public DefaultDescription(final DefaultDescription object) {
 -    public DefaultMeasureDescription(final Description object) {
++    public DefaultMeasureDescription(final DefaultMeasureDescription object) {
          super(object);
          if (object != null) {
              textDescription     = object.getTextDescription();
diff --cc core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQualityMeasure.java
index 2a8a9f6e13,bce5b0d225..a934aaa629
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQualityMeasure.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultQualityMeasure.java
@@@ -75,8 -74,7 +75,8 @@@ import static org.opengis.annotation.Sp
      "parameters"
  })
  @XmlRootElement(name = "DQM_Measure", namespace = Namespaces.DQM)
 -public class DefaultQualityMeasure extends ISOMetadata implements Measure {
 +@UML(identifier="DQM_Measure", specification=UNSPECIFIED)
- public class DefaultMeasure extends ISOMetadata {
++public class DefaultQualityMeasure extends ISOMetadata {
      /**
       * Serial number for inter-operability with different versions.
       */
@@@ -124,7 -122,7 +124,7 @@@
       * needed to establish the result of applying the measure.
       */
      @SuppressWarnings("serial")
-     private DefaultDescription description;
 -    private Description description;
++    private DefaultMeasureDescription description;
  
      /**
       * Reference to the source of an item that has been adopted from an external source.
@@@ -148,7 -151,7 +148,7 @@@
       * Illustration of the use of a data quality measure.
       */
      @SuppressWarnings("serial")
-     private Collection<DefaultDescription> examples;
 -    private Collection<Description> examples;
++    private Collection<DefaultMeasureDescription> examples;
  
      /**
       * Constructs an initially empty element.
@@@ -162,9 -165,11 +162,9 @@@
       * given object are not recursively copied.
       *
       * @param object  the metadata to copy values from, or {@code null} if none.
 -     *
 -     * @see #castOrCopy(Measure)
       */
      @SuppressWarnings({"unchecked", "rawtypes"})
-     public DefaultMeasure(final DefaultMeasure object) {
 -    public DefaultQualityMeasure(final Measure object) {
++    public DefaultQualityMeasure(final DefaultQualityMeasure object) {
          super(object);
          if (object != null) {
              measureIdentifier = object.getMeasureIdentifier();
@@@ -174,9 -179,10 +174,9 @@@
              definition        = object.getDefinition();
              description       = object.getDescription();
              valueType         = object.getValueType();
-             examples          = copyCollection(object.getExamples(), DefaultDescription.class);
 -            valueStructure    = object.getValueStructure();
 -            examples          = copyCollection(object.getExamples(), Description.class);
++            examples          = copyCollection(object.getExamples(), DefaultMeasureDescription.class);
              basicMeasure      = object.getBasicMeasure();
 -            sourceReferences  = copyCollection(object.getSourceReferences(), SourceReference.class);
 +            sourceReferences  = copyCollection(object.getSourceReferences(), DefaultSourceReference.class);
              parameters        = copyCollection(object.getParameters(), (Class) ParameterDescriptor.class);
          }
      }
@@@ -315,9 -346,9 +315,9 @@@
       *
       * @return description of data quality measure, or {@code null} if none.
       */
 -    @Override
      @XmlElement(name = "description")
 -    public Description getDescription() {
 +    @UML(identifier="description", obligation=CONDITIONAL, specification=UNSPECIFIED)
-     public DefaultDescription getDescription() {
++    public DefaultMeasureDescription getDescription() {
         return description;
      }
  
@@@ -326,7 -357,7 +326,7 @@@
       *
       * @param  newValue  the new measure description.
       */
-     public void setDescription(final DefaultDescription newValue)  {
 -    public void setDescription(final Description newValue)  {
++    public void setDescription(final DefaultMeasureDescription newValue)  {
          checkWritePermission(description);
          description = newValue;
      }
@@@ -404,10 -456,10 +404,10 @@@
       *
       * @return examples of applying the measure or the result obtained for the measure.
       */
 -    @Override
      @XmlElement(name = "example")
 -    public Collection<Description> getExamples() {
 -        return examples = nonNullCollection(examples, Description.class);
 +    @UML(identifier="example", obligation=OPTIONAL, specification=UNSPECIFIED)
-     public Collection<DefaultDescription> getExamples() {
-         return examples = nonNullCollection(examples, DefaultDescription.class);
++    public Collection<DefaultMeasureDescription> getExamples() {
++        return examples = nonNullCollection(examples, DefaultMeasureDescription.class);
      }
  
      /**
@@@ -415,7 -467,7 +415,7 @@@
       *
       * @param  newValues  the new examples.
       */
-     public void setExamples(final Collection<? extends DefaultDescription> newValues) {
-         examples = writeCollection(newValues, examples, DefaultDescription.class);
 -    public void setExamples(final Collection<? extends Description> newValues) {
 -        examples = writeCollection(newValues, examples, Description.class);
++    public void setExamples(final Collection<? extends DefaultMeasureDescription> newValues) {
++        examples = writeCollection(newValues, examples, DefaultMeasureDescription.class);
      }
  }
diff --cc core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameterTest.java
index 52ad321b87,8c53a55d1c..7783205172
--- a/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameterTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/internal/jaxb/metadata/replace/QualityParameterTest.java
@@@ -17,10 -17,12 +17,10 @@@
  package org.apache.sis.internal.jaxb.metadata.replace;
  
  import javax.xml.bind.JAXBException;
 -import org.opengis.metadata.Identifier;
 -import org.opengis.referencing.operation.Matrix;
 -import org.opengis.metadata.quality.ValueStructure;
 +import org.opengis.referencing.ReferenceIdentifier;
  import org.apache.sis.util.iso.Names;
  import org.apache.sis.util.SimpleInternationalString;
- import org.apache.sis.metadata.iso.quality.DefaultDescription;
+ import org.apache.sis.metadata.iso.quality.DefaultMeasureDescription;
  import org.apache.sis.test.xml.TestCase;
  import org.apache.sis.xml.Namespaces;
  import org.junit.Test;
@@@ -44,10 -46,11 +44,10 @@@ public final strictfp class QualityPara
       */
      public static QualityParameter create() {
          final QualityParameter param = new QualityParameter();
 -        param.code           = "some parameter";
 -        param.definition     = new SimpleInternationalString("a definition");
 -        param.description    = new DefaultMeasureDescription("a description");
 -        param.valueStructure = ValueStructure.MATRIX;
 -        param.valueType      = Names.createTypeName(Integer.class);
 +        param.code        = "some parameter";
 +        param.definition  = new SimpleInternationalString("a definition");
-         param.description = new DefaultDescription("a description");
++        param.description = new DefaultMeasureDescription("a description");
 +        param.valueType   = Names.createTypeName(Integer.class);
          return param;
      }
  
diff --cc core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/CommonAuthorityFactoryTest.java
index 3fd2c9823b,4217d64240..2724082835
--- a/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/CommonAuthorityFactoryTest.java
+++ b/core/sis-referencing/src/test/java/org/apache/sis/referencing/factory/CommonAuthorityFactoryTest.java
@@@ -388,10 -393,12 +393,12 @@@ public final strictfp class CommonAutho
                  "    AXIS[“Longitude (L)”, east, ORDER[1]],\n" +
                  "    AXIS[“Latitude (B)”, north, ORDER[2]],\n" +
                  "    ANGLEUNIT[“degree”, 0.017453292519943295],\n" +
-                 "  SCOPE[“Horizontal component of 3D system.\\E.*\\Q”],\n" +
+                 "\\E(?:  SCOPE\\[“.+”\\],\n)?\\Q" +                     // Ignore SCOPE[…] if present.
                  "  AREA[“World\\E.*\\Q”],\n" +
                  "  BBOX[-90.00, -180.00, 90.00, 180.00],\n" +
-                 "  ID[“CRS”, 84, CITATION[“WMS”], URI[“urn:ogc:def:crs:OGC:1.3:CRS84”]]]\\E", crs);
 -                "  ID[“CRS”, 84, CITATION[“OGC:WMS”], URI[“urn:ogc:def:crs:OGC:1.3:CRS84”]]" +
++                "  ID[“CRS”, 84, CITATION[“WMS”], URI[“urn:ogc:def:crs:OGC:1.3:CRS84”]]" +
+                 "\\E(?:,\n  REMARK\\[“.+”\\])?\\]",                     // Ignore trailing REMARK[…] if present.
+                 crs);
          /*
           * Note: the WKT specification defines the ID element as:
           *
diff --cc storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Updater.java
index 0000000000,d67c57bcaf..52c2e18f2b
mode 000000,100644..100644
--- a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Updater.java
+++ b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Updater.java
@@@ -1,0 -1,89 +1,91 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.internal.storage.gpx;
+ 
+ import java.io.IOException;
+ import java.io.OutputStream;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.util.stream.Stream;
+ import org.apache.sis.internal.storage.xml.stream.RewriteOnUpdate;
+ import org.apache.sis.internal.storage.xml.stream.StaxStreamWriter;
+ import org.apache.sis.storage.DataStoreException;
 -import org.opengis.feature.Feature;
++
++// Branch-dependent imports
++import org.apache.sis.feature.AbstractFeature;
+ 
+ 
+ /**
+  * Updates the content of a GPX file by rewriting it.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.3
+  * @since   1.3
+  * @module
+  */
+ final class Updater extends RewriteOnUpdate {
+     /**
+      * The metadata to write.
+      */
+     private Metadata metadata;
+ 
+     /**
+      * Creates an updater for the given source of features.
+      *
+      * @param  source    the set of features to update.
+      * @param  location  the main file, or {@code null} if unknown.
+      * @throws IOException if an error occurred while determining whether the file is empty.
+      */
+     Updater(final WritableStore source, final Path location) throws IOException {
+         super(source, location);
+     }
+ 
+     /**
+      * Returns the stream of features to copy.
+      *
+      * @return all features contained in the dataset.
+      * @throws DataStoreException if an error occurred while fetching the features.
+      */
+     @Override
 -    protected Stream<? extends Feature> features() throws DataStoreException {
++    protected Stream<? extends AbstractFeature> features() throws DataStoreException {
+         metadata = Metadata.castOrCopy(source.getMetadata(), getLocale());
+         return super.features();
+     }
+ 
+     /**
+      * Creates an initially empty temporary file.
+      *
+      * @return the temporary file.
+      * @throws IOException if an error occurred while creating the temporary file.
+      */
+     @Override
+     protected Path createTemporaryFile() throws IOException {
+         return Files.createTempFile(StoreProvider.NAME, ".xml");
+     }
+ 
+     /**
+      * Creates a new GPX writer for an output in the specified file.
+      *
+      * @param  temporary  the temporary stream where to write, or {@code null} for writing directly in the store file.
+      * @return the writer where to copy updated features.
+      * @throws Exception if an error occurred while creating the writer.
+      */
+     @Override
+     protected StaxStreamWriter createWriter(OutputStream temporary) throws Exception {
+         return new Writer((WritableStore) source, metadata, temporary);
+     }
+ }
diff --cc storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/WritableStore.java
index 0000000000,a4ced88d7e..44bb071d34
mode 000000,100644..100644
--- a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/WritableStore.java
+++ b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/WritableStore.java
@@@ -1,0 -1,182 +1,182 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.internal.storage.gpx;
+ 
+ import java.util.Iterator;
+ import java.util.function.Predicate;
+ import java.util.function.UnaryOperator;
+ import java.util.stream.Stream;
+ import java.io.IOException;
+ import java.io.UncheckedIOException;
+ import org.opengis.metadata.Metadata;
+ import org.apache.sis.storage.WritableFeatureSet;
+ import org.apache.sis.storage.StorageConnector;
+ import org.apache.sis.storage.DataStoreException;
+ import org.apache.sis.storage.ConcurrentReadException;
+ import org.apache.sis.storage.IllegalFeatureTypeException;
+ import org.apache.sis.util.collection.BackingStoreException;
+ 
+ // Branch-dependent imports
 -import org.opengis.feature.Feature;
 -import org.opengis.feature.FeatureType;
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.feature.DefaultFeatureType;
+ 
+ 
+ /**
+  * A GPX store capable to write GPX file.
+  *
+  * @author  Johann Sorel (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.3
+  * @since   1.3
+  * @module
+  */
+ public final class WritableStore extends Store implements WritableFeatureSet {
+     /**
+      * Creates a new GPX store from the given file, URL or stream object.
+      * This constructor invokes {@link StorageConnector#closeAllExcept(Object)},
+      * keeping open only the needed resource.
+      *
+      * @param  provider   the provider of this data store, or {@code null} if unspecified.
+      * @param  connector  information about the storage (URL, stream, <i>etc</i>).
+      * @throws DataStoreException if an error occurred while opening the GPX file.
+      */
+     public WritableStore(final StoreProvider provider, final StorageConnector connector) throws DataStoreException {
+         super(provider, connector);
+     }
+ 
+     /**
+      * Verifies the type of feature instances in this feature set.
+      * This method does nothing if the specified type is equal to {@link #getType()},
+      * or throws {@link IllegalFeatureTypeException} otherwise.
+      *
+      * @param  newType  new feature type definition (not {@code null}).
+      * @throws DataStoreException if the given type is not compatible with the types supported by the store.
+      */
+     @Override
 -    public void updateType(final FeatureType newType) throws DataStoreException {
++    public void updateType(final DefaultFeatureType newType) throws DataStoreException {
+         if (!newType.equals(getType())) {
+             throw new IllegalFeatureTypeException(getLocale(), StoreProvider.NAME, newType.getName());
+         }
+     }
+ 
+     /**
+      * Appends new feature instances in this {@code FeatureSet}.
+      * Any feature already present in this {@link FeatureSet} will remain unmodified.
+      *
+      * @param  features  feature instances to append in this {@code FeatureSet}.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
+     @Override
 -    public synchronized void add(final Iterator<? extends Feature> features) throws DataStoreException {
++    public synchronized void add(final Iterator<? extends AbstractFeature> features) throws DataStoreException {
+         try (Updater updater = updater()) {
+             updater.add(features);
+             updater.flush();
+         }
+     }
+ 
+     /**
+      * Removes all feature instances from this {@code FeatureSet} which matches the given predicate.
+      *
+      * @param  filter  a predicate which returns {@code true} for feature instances to be removed.
+      * @return {@code true} if any elements were removed.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
+     @Override
 -    public synchronized boolean removeIf(final Predicate<? super Feature> filter) throws DataStoreException {
++    public synchronized boolean removeIf(final Predicate<? super AbstractFeature> filter) throws DataStoreException {
+         try (Updater updater = updater()) {
+             return updater.removeIf(filter);
+         }
+     }
+ 
+     /**
+      * Updates all feature instances from this {@code FeatureSet} which match the given predicate.
+      * If the given operator returns {@code null}, then the filtered feature is removed.
+      *
+      * @param  filter       a predicate which returns {@code true} for feature instances to be updated.
 -     * @param  replacement  operation called for each matching {@link Feature} instance. May return {@code null}.
++     * @param  replacement  operation called for each matching {@code Feature} instance. May return {@code null}.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
+     @Override
 -    public synchronized void replaceIf(final Predicate<? super Feature> filter, final UnaryOperator<Feature> replacement)
++    public synchronized void replaceIf(final Predicate<? super AbstractFeature> filter, final UnaryOperator<AbstractFeature> replacement)
+             throws DataStoreException
+     {
+         try (Updater updater = updater()) {
+             updater.replaceIf(filter, replacement);
+             updater.flush();
+         }
+     }
+ 
+     /**
+      * Returns the helper object to use for updating the GPX file.
+      *
+      * @todo In current version, we flush the updater after each write operation.
+      *       In a future version, we should keep it in a private field and flush
+      *       only after some delay, on close, or before a read operation.
+      */
+     private Updater updater() throws DataStoreException {
+         try {
+             return new Updater(this, getSpecifiedPath());
+         } catch (IOException e) {
+             throw new DataStoreException(e);
+         }
+     }
+ 
+     /**
+      * Replaces the content of this GPX file by the given metadata and features.
+      *
+      * @param  metadata  the metadata to write, or {@code null} if none.
+      * @param  features  the features to write, or {@code null} if none.
+      * @throws ConcurrentReadException if the {@code features} stream was provided by this data store.
+      * @throws DataStoreException if an error occurred while writing the data.
+      *
+      * @deprecated To be replaced by {@link #add(Iterator)}, after we resolved how to specify metadata.
+      *
+      * @see <a href="https://issues.apache.org/jira/browse/SIS-411">SIS-411</a>
+      */
+     @Deprecated
 -    public synchronized void write(final Metadata metadata, final Stream<? extends Feature> features) throws DataStoreException {
++    public synchronized void write(final Metadata metadata, final Stream<? extends AbstractFeature> features) throws DataStoreException {
+         try {
+             /*
+              * If we created a reader for reading metadata, we need to close that reader now otherwise the call
+              * to `new Writer(…)` will fail.  Note that if that reader was in use by someone else, the `reader`
+              * field would be null and the `new Writer(…)` call should detect that a reader is in use somewhere.
+              */
+             closeReader();
+             /*
+              * Get the writer if no read or other write operation is in progress, then write the data.
+              */
+             try (Writer writer = new Writer(this, org.apache.sis.internal.storage.gpx.Metadata.castOrCopy(metadata, locale), null)) {
+                 writer.writeStartDocument();
+                 if (features != null) {
+                     features.forEachOrdered(writer);
+                 }
+                 writer.writeEndDocument();
+             }
+         } catch (BackingStoreException e) {
+             final Throwable cause = e.getCause();
+             if (cause instanceof DataStoreException) {
+                 throw (DataStoreException) cause;
+             }
+             throw new DataStoreException(e.getLocalizedMessage(), cause);
+         } catch (Exception e) {
+             if (e instanceof UncheckedIOException) {
+                 e = ((UncheckedIOException) e).getCause();
+             }
+             throw new DataStoreException(e);
+         }
+     }
+ }
diff --cc storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java
index 6e1d70ffc6,8c5ac95d73..33aec7c586
--- a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java
+++ b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Writer.java
@@@ -142,10 -144,10 +144,10 @@@ final class Writer extends StaxStreamWr
       * @throws JAXBException if underlying JAXB marshaller encounter an error.
       */
      @Override
 -    public void write(final Feature feature) throws DataStoreException, XMLStreamException, JAXBException {
 +    public void write(final AbstractFeature feature) throws DataStoreException, XMLStreamException, JAXBException {
          if (feature != null) {
-             final Types types = ((Store) owner).types;
+             final Types types = ((WritableStore) owner).types;
 -            final FeatureType type = feature.getType();
 +            final DefaultFeatureType type = feature.getType();
              if (types.wayPoint.isAssignableFrom(type)) {
                  writeWayPoint(feature, Tags.WAY_POINT);
              } else {
diff --cc storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/RewriteOnUpdate.java
index 0000000000,5377bda50b..7478d62bb6
mode 000000,100644..100644
--- a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/RewriteOnUpdate.java
+++ b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/RewriteOnUpdate.java
@@@ -1,0 -1,283 +1,283 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.internal.storage.xml.stream;
+ 
+ import java.util.Locale;
+ import java.util.Iterator;
+ import java.util.Spliterator;
+ import java.util.Spliterators;
+ import java.util.stream.Stream;
+ import java.util.function.Predicate;
+ import java.util.function.UnaryOperator;
+ import java.util.stream.StreamSupport;
+ import java.io.IOException;
+ import java.io.OutputStream;
+ import java.io.UncheckedIOException;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.nio.file.StandardCopyOption;
+ import org.apache.sis.storage.FeatureSet;
+ import org.apache.sis.storage.DataStoreException;
+ import org.apache.sis.storage.ReadOnlyStorageException;
+ import org.apache.sis.util.collection.BackingStoreException;
+ import org.apache.sis.util.ArgumentChecks;
+ 
+ // Branch-dependent imports
 -import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+ 
+ 
+ /**
+  * Helper class for updating an existing XML file, with no feature type change permitted.
+  * The implementation strategy is to rewrite fully the updated features in a temporary file,
+  * then replaces the source file by the temporary file when ready.
+  *
+  * <p>The {@link #flush()} method should always been invoked before a {@code RewriteOnUpdate}
+  * reference is lost, otherwise data may be lost.</p>
+  *
+  * <h2>Multi-threading</h2>
+  * This class is not synchronized for multi-threading. Synchronization is caller's responsibility,
+  * because the caller usually needs to take in account other data store operations such as reads.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.3
+  * @since   1.3
+  * @module
+  */
+ public abstract class RewriteOnUpdate implements AutoCloseable {
+     /**
+      * The set of features to update. This is the set specified at construction time.
+      */
+     protected final FeatureSet source;
+ 
+     /**
+      * The main file, or {@code null} if unknown.
+      */
+     private final Path location;
+ 
+     /**
+      * Whether the store is initially empty.
+      * It may be the underlying file does not exist or has a length of zero.
+      */
+     private boolean isSourceEmpty;
+ 
+     /**
+      * The features to write, fetched when first needed.
+      *
+      * @see #filtered()
+      */
 -    private Stream<? extends Feature> filtered;
++    private Stream<? extends AbstractFeature> filtered;
+ 
+     /**
+      * Creates an updater for the given source of features.
+      *
+      * @param  source    the set of features to update.
+      * @param  location  the main file, or {@code null} if unknown.
+      * @throws IOException if an error occurred while determining whether the file is empty.
+      */
+     public RewriteOnUpdate(final FeatureSet source, final Path location) throws IOException {
+         this.source   = source;
+         this.location = location;
+         isSourceEmpty = (location == null) || Files.notExists(location) || Files.size(location) == 0;
+     }
+ 
+     /**
+      * Returns the locale to use for locale-sensitive data, or {@code null} if unspecified.
+      * This is <strong>not</strong> for logging or warning messages.
+      *
+      * @return the data locale, or {@code null}.
+      */
+     protected final Locale getLocale() {
+         return (source instanceof StaxDataStore) ? ((StaxDataStore) source).locale : null;
+     }
+ 
+     /**
+      * Returns {@code true} if there is currently no data.
+      */
+     private boolean isEmpty() throws ReadOnlyStorageException {
+         if (isSourceEmpty) {
+             return filtered == null;
+         } else if (location != null) {
+             return false;
+         } else {
+             throw new ReadOnlyStorageException();
+         }
+     }
+ 
+     /**
+      * Returns the features to write.
+      *
+      * @throws DataStoreException if the feature stream cannot be obtained.
+      */
 -    private Stream<? extends Feature> filtered() throws DataStoreException {
++    private Stream<? extends AbstractFeature> filtered() throws DataStoreException {
+         if (filtered == null) {
+             filtered = features();
+         }
+         return filtered;
+     }
+ 
+     /**
+      * Returns the stream of features to copy.
+      * The default implementation delegates to {@link FeatureSet#features(boolean)}.
+      *
+      * @return all features contained in the dataset.
+      * @throws DataStoreException if an error occurred while fetching the features.
+      */
 -    protected Stream<? extends Feature> features() throws DataStoreException {
++    protected Stream<? extends AbstractFeature> features() throws DataStoreException {
+         return source.features(false);
+     }
+ 
+     /**
+      * Appends new feature instances in the {@code FeatureSet}.
+      * Any feature already present in the {@link FeatureSet} will remain unmodified.
+      *
+      * @param  features  feature instances to append in the {@code FeatureSet}.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
 -    public void add(final Iterator<? extends Feature> features) throws DataStoreException {
++    public void add(final Iterator<? extends AbstractFeature> features) throws DataStoreException {
+         ArgumentChecks.ensureNonNull("features", features);
 -        final Stream<? extends Feature> toAdd = StreamSupport.stream(
++        final Stream<? extends AbstractFeature> toAdd = StreamSupport.stream(
+                 Spliterators.spliteratorUnknownSize(features, Spliterator.ORDERED), false);
+         if (isEmpty()) {
+             filtered = toAdd;
+         } else {
+             filtered = Stream.concat(filtered(), toAdd);
+         }
+     }
+ 
+     /**
+      * Removes all feature instances from the {@code FeatureSet} which matches the given predicate.
+      *
+      * @param  filter  a predicate which returns {@code true} for feature instances to be removed.
+      * @return {@code true} if any elements were removed.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
 -    public boolean removeIf(final Predicate<? super Feature> filter) throws DataStoreException {
++    public boolean removeIf(final Predicate<? super AbstractFeature> filter) throws DataStoreException {
+         ArgumentChecks.ensureNonNull("filter", filter);
+         if (isEmpty()) {
+             return false;
+         }
+         filtered = filtered().filter((feature) -> {
+             boolean r = filter.test(feature);
+             if (r) modified = true;
+             return !r;
+         });
+         modified = false;
+         flush();            // Need immediate execution for getting the boolean value.
+         return modified;
+     }
+ 
+     /**
+      * A flag telling whether {@link #removeIf(Predicate)} removed at least one feature.
+      */
+     private boolean modified;
+ 
+     /**
+      * Updates all feature instances from the {@code FeatureSet} which match the given predicate.
+      * If the given operator returns {@code null}, then the filtered feature is removed.
+      *
+      * @param  filter   a predicate which returns {@code true} for feature instances to be updated.
 -     * @param  updater  operation called for each matching {@link Feature} instance. May return {@code null}.
++     * @param  updater  operation called for each matching {@code Feature} instance. May return {@code null}.
+      * @throws DataStoreException if the feature stream cannot be obtained or updated.
+      */
 -    public void replaceIf(final Predicate<? super Feature> filter, final UnaryOperator<Feature> updater) throws DataStoreException {
++    public void replaceIf(final Predicate<? super AbstractFeature> filter, final UnaryOperator<AbstractFeature> updater) throws DataStoreException {
+         ArgumentChecks.ensureNonNull("filter",  filter);
+         ArgumentChecks.ensureNonNull("updater", updater);
+         if (!isEmpty()) {
+             filtered = filtered().map((feature) -> (feature != null) && filter.test(feature) ? updater.apply(feature) : feature);
+         }
+     }
+ 
+     /**
+      * Creates an initially empty temporary file.
+      *
+      * @return the temporary file.
+      * @throws IOException if an error occurred while creating the temporary file.
+      */
+     protected abstract Path createTemporaryFile() throws IOException;
+ 
+     /**
+      * Creates a new XML document writer for an output in the specified temporary file.
+      * Caller is responsible for closing the writer.
+      *
+      * @param  temporary  the temporary stream where to write, or {@code null} for writing directly in the store file.
+      * @return the writer where to copy updated features.
+      * @throws Exception if an error occurred while creating the writer.
+      *         May be {@link DataStoreException}, {@link IOException}, {@link RuntimeException}, <i>etc.</i>
+      */
+     protected abstract StaxStreamWriter createWriter(OutputStream temporary) throws Exception;
+ 
+     /**
+      * Writes immediately all feature instances.
+      * This method does nothing if there is no data to write.
+      *
+      * @throws DataStoreException if an error occurred.
+      */
+     public void flush() throws DataStoreException {
 -        try (Stream<? extends Feature> content = filtered) {
++        try (Stream<? extends AbstractFeature> content = filtered) {
+             if (content != null) {
+                 filtered = null;
+                 OutputStream temporary = null;
+                 Path target = isSourceEmpty ? null : createTemporaryFile();
+                 try {
+                     if (target != null) {
+                         temporary = Files.newOutputStream(target);
+                     }
+                     try (StaxStreamWriter writer = createWriter(temporary)) {
+                         temporary = null;       // Stream will be closed by writer.
+                         isSourceEmpty = false;
+                         writer.writeStartDocument();
+                         content.sequential().forEachOrdered(writer);
+                         writer.writeEndDocument();
+                     }
+                     if (target != null) {
+                         Files.move(target, location, StandardCopyOption.REPLACE_EXISTING);
+                         target = null;
+                     }
+                 } finally {
+                     if (temporary != null) temporary.close();
+                     if (target != null) Files.delete(target);       // Delete the temporary file if an error occurred.
+                 }
+             }
+         } catch (DataStoreException e) {
+             throw e;
+         } catch (BackingStoreException e) {
+             final Throwable cause = e.getCause();
+             if (cause instanceof DataStoreException) {
+                 throw (DataStoreException) cause;
+             }
+             throw new DataStoreException(e.getLocalizedMessage(), cause);
+         } catch (Exception e) {
+             if (e instanceof UncheckedIOException) {
+                 e = ((UncheckedIOException) e).getCause();
+             }
+             throw new DataStoreException(e);
+         }
+     }
+ 
+     /**
+      * Releases resources used by this updater. If {@link #flush()} has not been invoked, data may be lost.
+      * This method is useful in try-with-resource in case something fails before {@link #flush()} invocation.
+      */
+     @Override
+     public void close() {
 -        final Stream<? extends Feature> content = filtered;
++        final Stream<? extends AbstractFeature> content = filtered;
+         if (content != null) {
+             filtered = null;
+             content.close();
+         }
+     }
+ }
diff --cc storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/UpdaterTest.java
index 0000000000,8f2fc1b77e..20874e3cc9
mode 000000,100644..100644
--- a/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/UpdaterTest.java
+++ b/storage/sis-xmlstore/src/test/java/org/apache/sis/internal/storage/gpx/UpdaterTest.java
@@@ -1,0 -1,182 +1,182 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.internal.storage.gpx;
+ 
+ import java.util.Arrays;
+ import java.io.IOException;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.nio.file.StandardOpenOption;
+ import java.time.Instant;
+ import com.esri.core.geometry.Point;
+ import java.io.InputStream;
+ import java.nio.file.StandardCopyOption;
+ import org.apache.sis.setup.GeometryLibrary;
+ import org.apache.sis.setup.OptionKey;
+ import org.apache.sis.storage.DataStoreException;
+ import org.apache.sis.storage.StorageConnector;
+ import org.apache.sis.test.DependsOn;
+ import org.apache.sis.test.TestCase;
+ import org.junit.BeforeClass;
+ import org.junit.AfterClass;
+ import org.junit.Before;
+ import org.junit.After;
+ import org.junit.Test;
+ 
+ import static org.apache.sis.test.MetadataAssert.*;
+ 
+ // Branch-dependent imports
 -import org.opengis.feature.Feature;
++import org.apache.sis.feature.AbstractFeature;
+ 
+ 
+ /**
+  * Tests (indirectly) the {@link Updater} class.
+  *
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.3
+  *
+  * @see <a href="https://issues.apache.org/jira/browse/SIS-411">SIS-411</a>
+  *
+  * @since 1.3
+  * @module
+  */
+ @DependsOn(WriterTest.class)
+ public final strictfp class UpdaterTest extends TestCase {
+     /**
+      * The provider shared by all data stores created in this test class.
+      */
+     private static StoreProvider provider;
+ 
+     /**
+      * Creates the provider to be shared by all data stores created in this test class.
+      */
+     @BeforeClass
+     public static void createProvider() {
+         provider = new StoreProvider();
+     }
+ 
+     /**
+      * Disposes the data store provider after all tests have been completed.
+      */
+     @AfterClass
+     public static void disposeProvider() {
+         provider = null;
+     }
+ 
+     /**
+      * Temporary file where to write the GPX file.
+      */
+     private Path file;
+ 
+     /**
+      * Creates the temporary file before test execution.
+      *
+      * @throws IOException if the temporary file cannot be created.
+      */
+     @Before
+     public void createTemporaryFile() throws IOException {
+         file = Files.createTempFile("GPX", ".xml");
+     }
+ 
+     /**
+      * Deletes temporary file after test execution.
+      *
+      * @throws IOException if the temporary file cannot be deleted.
+      */
+     @After
+     public void deleteTemporaryFile() throws IOException {
+         if (file != null) {
+             Files.delete(file);
+         }
+     }
+ 
+     /**
+      * Creates a new GPX data store which will read and write in a temporary file.
+      */
+     private WritableStore create() throws DataStoreException, IOException {
+         final StorageConnector connector = new StorageConnector(file);
+         connector.setOption(OptionKey.GEOMETRY_LIBRARY, GeometryLibrary.ESRI);
+         connector.setOption(OptionKey.OPEN_OPTIONS, new StandardOpenOption[] {
+                             StandardOpenOption.READ, StandardOpenOption.WRITE});
+         return new WritableStore(provider, connector);
+     }
+ 
+     /**
+      * Tests writing in an initially empty file.
+      *
+      * @throws IOException if an error occurred while creating the temporary file.
+      * @throws DataStoreException if an error occurred while using the GPX store.
+      */
+     @Test
+     public void testWriteEmpty() throws DataStoreException, IOException {
+         try (final WritableStore store = create()) {
+             final Types types = store.types;
 -            final Feature point1 = types.wayPoint.newInstance();
 -            final Feature point2 = types.wayPoint.newInstance();
 -            final Feature point3 = types.wayPoint.newInstance();
++            final AbstractFeature point1 = types.wayPoint.newInstance();
++            final AbstractFeature point2 = types.wayPoint.newInstance();
++            final AbstractFeature point3 = types.wayPoint.newInstance();
+             point1.setPropertyValue("sis:geometry", new Point(15, 10));
+             point2.setPropertyValue("sis:geometry", new Point(25, 20));
+             point3.setPropertyValue("sis:geometry", new Point(35, 30));
+             point1.setPropertyValue("time", Instant.parse("2010-01-10T00:00:00Z"));
+             point3.setPropertyValue("time", Instant.parse("2010-01-30T00:00:00Z"));
+             store.add(Arrays.asList(point1, point2, point3).iterator());
+         }
+         assertXmlEquals(
+                 "<gpx xmlns=\"" + Tags.NAMESPACE + "1/1\" version=\"1.1\">\n" +
+                 "  <wpt lat=\"10.0\" lon=\"15.0\">\n" +
+                 "    <time>2010-01-10T00:00:00Z</time>\n" +
+                 "  </wpt>\n" +
+                 "  <wpt lat=\"20.0\" lon=\"25.0\"/>\n" +
+                 "  <wpt lat=\"30.0\" lon=\"35.0\">\n" +
+                 "    <time>2010-01-30T00:00:00Z</time>\n" +
+                 "  </wpt>\n" +
+                 "</gpx>", file, "xmlns:*");
+     }
+ 
+     /**
+      * Tests an update which requires rewriting the XML file.
+      *
+      * @throws IOException if an error occurred while creating the temporary file.
+      * @throws DataStoreException if an error occurred while using the GPX store.
+      */
+     @Test
+     public void testRewrite() throws DataStoreException, IOException {
+         try (InputStream in = UpdaterTest.class.getResourceAsStream("1.1/waypoint.xml")) {
+             Files.copy(in, file, StandardCopyOption.REPLACE_EXISTING);
+         }
+         assertTrue(containsLat20());
+         final boolean result;
+         try (final WritableStore store = create()) {
+             result = store.removeIf((feature) -> {
+                 Object point = feature.getPropertyValue("sis:geometry");
+                 return ((Point) point).getY() == 20;
+             });
+         }
+         assertTrue(result);
+         assertFalse(containsLat20());
+     }
+ 
+     /**
+      * Returns whether the temporary file contains the {@code lat="20"} string.
+      * Also checks some invariants such as the presence of metadata.
+      */
+     private boolean containsLat20() throws IOException {
+         final String xml = org.apache.sis.internal.jdk9.JDK9.readString(file);
+         assertTrue(xml.contains("<bounds "));       // Sentinel value for presence of metadata.
+         return xml.contains("lat=\"20");            // May have trailing ".0".
+     }
+ }