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

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

commit b9cb14901d4f3b6ba40a7ff92174347d12ae6951
Merge: fafbe8a7b6 fb00d38fa4
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Thu Nov 23 16:08:22 2023 +0100

    Merge branch 'geoapi-3.1'.
    The main works are in Shapefile, GeoTIFF and metadata "nil values".

 .../org/apache/sis/buildtools/book/Assembler.java  |  14 +-
 .../apache/sis/buildtools/book/CodeColorizer.java  |   2 +-
 .../apache/sis/buildtools/book/package-info.java   |   2 +-
 .../org.apache.sis.console/main/module-info.java   |   3 +-
 .../main/org/apache/sis/console/Command.java       |   4 +-
 .../main/org/apache/sis/console/CommandRunner.java |  26 +
 .../org/apache/sis/console/Commands.properties     |   1 +
 .../org/apache/sis/console/Commands_fr.properties  |  17 +-
 .../main/org/apache/sis/console/HelpCommand.java   |   3 +-
 .../main/org/apache/sis/console/Option.java        |   8 +-
 .../main/org/apache/sis/console/Options.properties |   3 +-
 .../org/apache/sis/console/Options_fr.properties   |  23 +-
 .../org/apache/sis/console/TransformCommand.java   |   6 +-
 .../org/apache/sis/console/TranslateCommand.java   | 105 ++++
 .../main/org/apache/sis/console/package-info.java  |  11 +-
 .../org.apache.sis.feature/main/module-info.java   |   5 +-
 .../org/apache/sis/coverage/CoverageCombiner.java  |   8 +-
 .../org/apache/sis/coverage/RegionOfInterest.java  |   2 +-
 .../sis/coverage/grid/GridCoverageProcessor.java   |  21 +-
 .../org/apache/sis/coverage/grid/GridGeometry.java |   2 +-
 .../org/apache/sis/filter/AssociationValue.java    |  12 +-
 .../apache/sis/filter/DefaultFilterFactory.java    |  14 +-
 .../main/org/apache/sis/filter/PropertyValue.java  |  36 +-
 .../apache/sis/filter/internal/FunctionNames.java  |  12 +
 .../main/org/apache/sis/filter/internal/XPath.java | 193 +++++--
 .../main/org/apache/sis/filter/package-info.java   |   2 +-
 .../test/org/apache/sis/filter/XPathTest.java      |  40 +-
 .../org.apache.sis.metadata/main/module-info.java  |   1 +
 .../org/apache/sis/metadata/AbstractMetadata.java  | 122 ++++-
 .../main/org/apache/sis/metadata/IndexMap.java     |  33 +-
 .../org/apache/sis/metadata/InformationMap.java    |  31 +-
 .../org/apache/sis/metadata/MetadataColumn.java    |  67 +++
 .../org/apache/sis/metadata/MetadataStandard.java  |  78 ++-
 .../apache/sis/metadata/ModifiableMetadata.java    |  21 +-
 .../main/org/apache/sis/metadata/NameMap.java      |  34 +-
 .../main/org/apache/sis/metadata/NilReasonMap.java | 209 ++++++++
 .../org/apache/sis/metadata/PropertyAccessor.java  |  43 +-
 .../main/org/apache/sis/metadata/PropertyMap.java  | 265 +++++++---
 .../main/org/apache/sis/metadata/TreeNode.java     | 228 ++++++--
 .../org/apache/sis/metadata/TreeNodeChildren.java  |  64 ++-
 .../org/apache/sis/metadata/TreeTableView.java     |  47 +-
 .../main/org/apache/sis/metadata/TypeMap.java      |  33 +-
 .../apache/sis/metadata/ValueExistencePolicy.java  |  12 +
 .../main/org/apache/sis/metadata/ValueMap.java     | 228 +-------
 .../apache/sis/metadata/internal/Resources.java    |   2 +-
 .../sis/metadata/internal/Resources.properties     |   2 +-
 .../sis/metadata/internal/Resources_fr.properties  |   2 +-
 .../iso/DefaultExtendedElementInformation.java     |  22 +-
 .../apache/sis/metadata/iso/DefaultMetadata.java   |  22 +-
 .../org/apache/sis/metadata/iso/ISOMetadata.java   |  27 +-
 .../sis/metadata/iso/citation/DefaultCitation.java |  22 +-
 .../sis/metadata/iso/citation/DefaultContact.java  |  22 +-
 .../iso/citation/DefaultResponsibility.java        |  22 +-
 .../iso/constraint/DefaultConstraints.java         |  22 +-
 .../iso/content/DefaultCoverageDescription.java    |  22 +-
 .../DefaultFeatureCatalogueDescription.java        |  22 +-
 .../iso/content/DefaultImageDescription.java       |  22 +-
 .../iso/content/DefaultRangeDimension.java         |  22 +-
 .../DefaultDigitalTransferOptions.java             |  22 +-
 .../metadata/iso/distribution/DefaultMedium.java   |  22 +-
 .../iso/identification/AbstractIdentification.java |  22 +-
 .../DefaultAggregateInformation.java               |  22 +-
 .../iso/identification/DefaultBrowseGraphic.java   |  22 +-
 .../iso/identification/DefaultCoupledResource.java |  22 +-
 .../identification/DefaultDataIdentification.java  |  22 +-
 .../DefaultRepresentativeFraction.java             |  22 +-
 .../DefaultServiceIdentification.java              |  22 +-
 .../metadata/iso/identification/DefaultUsage.java  |  22 +-
 .../sis/metadata/iso/lineage/DefaultLineage.java   |  22 +-
 .../metadata/iso/lineage/DefaultProcessStep.java   |  22 +-
 .../sis/metadata/iso/lineage/DefaultSource.java    |  22 +-
 .../maintenance/DefaultMaintenanceInformation.java |  22 +-
 .../sis/metadata/iso/quality/AbstractElement.java  |  22 +-
 .../iso/quality/DefaultConformanceResult.java      |  48 +-
 .../sis/metadata/iso/quality/package-info.java     |   3 +-
 .../sis/metadata/iso/spatial/DefaultDimension.java |  44 +-
 .../metadata/iso/spatial/DefaultGCPCollection.java |  44 +-
 .../spatial/DefaultGridSpatialRepresentation.java  |  44 +-
 .../sis/metadata/iso/spatial/package-info.java     |   2 +-
 .../main/org/apache/sis/metadata/package-info.java |  27 +-
 .../apache/sis/metadata/sql/util/package-info.java |   2 +-
 .../org/apache/sis/util/iso/DefaultLocalName.java  |  22 +-
 .../org/apache/sis/util/iso/DefaultMemberName.java |  22 +-
 .../org/apache/sis/util/iso/DefaultRecord.java     |  22 +-
 .../org/apache/sis/util/iso/DefaultRecordType.java |  22 +-
 .../org/apache/sis/util/iso/DefaultTypeName.java   |  22 +-
 .../main/org/apache/sis/util/iso/Names.java        |   2 +
 .../org/apache/sis/xml/NilInternationalString.java |   2 +-
 .../main/org/apache/sis/xml/NilReason.java         |  49 +-
 .../main/org/apache/sis/xml/ValueConverter.java    |   4 +-
 .../main/org/apache/sis/xml/XPointer.java          |   2 +-
 .../apache/sis/xml/bind/FinalClassExtensions.java  |   2 +-
 .../org/apache/sis/xml/bind/gco/GO_Boolean.java    |  13 +
 .../org/apache/sis/xml/bind/gco/GO_Integer.java    |  13 +
 .../org/apache/sis/xml/bind/gco/PropertyType.java  |  51 +-
 .../main/org/apache/sis/xml/package-info.java      |   2 +-
 .../org/apache/sis/metadata/NilReasonMapTest.java  | 102 ++++
 .../org/apache/sis/metadata/TreeTableViewTest.java | 103 +++-
 .../metadata/iso/quality/AbstractElementTest.java  |   2 +-
 .../quality/AbstractPositionalAccuracyTest.java    |   5 +-
 .../iso/quality/DefaultConformanceResultTest.java  | 118 +++++
 .../iso/quality/DefaultDomainConsistencyTest.java  |   2 +-
 .../iso/quality/DefaultEvaluationMethodTest.java   |  12 +-
 .../iso/quality/DefaultQuantitativeResultTest.java |   2 +-
 .../sis/metadata/iso/quality/ScopeCodeTest.java    |   4 +-
 .../test/org/apache/sis/xml/NilReasonTest.java     |  75 +--
 .../test/org/apache/sis/xml/test/TestCase.java     |  12 +
 .../apache/sis/io/wkt/GeodeticObjectParser.java    |   8 +
 .../sis/parameter/AbstractParameterDescriptor.java |  22 +-
 .../sis/parameter/DefaultParameterDescriptor.java  |  23 +-
 .../parameter/DefaultParameterDescriptorGroup.java |  22 +-
 .../sis/parameter/DefaultParameterValue.java       |  22 +-
 .../sis/parameter/DefaultParameterValueGroup.java  |  22 +-
 .../sis/referencing/AbstractIdentifiedObject.java  |  22 +-
 .../sis/referencing/AbstractReferenceSystem.java   |  22 +-
 .../apache/sis/referencing/AuthorityFactories.java |   2 +-
 .../apache/sis/referencing/crs/AbstractCRS.java    |  22 +-
 .../sis/referencing/crs/AbstractDerivedCRS.java    |  22 +-
 .../sis/referencing/crs/DefaultCompoundCRS.java    |  22 +-
 .../sis/referencing/crs/DefaultDerivedCRS.java     |  22 +-
 .../sis/referencing/crs/DefaultEngineeringCRS.java |  22 +-
 .../sis/referencing/crs/DefaultGeocentricCRS.java  |  22 +-
 .../sis/referencing/crs/DefaultGeodeticCRS.java    |  22 +-
 .../sis/referencing/crs/DefaultGeographicCRS.java  |  22 +-
 .../sis/referencing/crs/DefaultImageCRS.java       |  22 +-
 .../sis/referencing/crs/DefaultParametricCRS.java  |  22 +-
 .../sis/referencing/crs/DefaultProjectedCRS.java   |  22 +-
 .../sis/referencing/crs/DefaultTemporalCRS.java    |  22 +-
 .../sis/referencing/crs/DefaultVerticalCRS.java    |  22 +-
 .../org/apache/sis/referencing/cs/AbstractCS.java  |  22 +-
 .../apache/sis/referencing/cs/DefaultAffineCS.java |  22 +-
 .../sis/referencing/cs/DefaultCartesianCS.java     |  22 +-
 .../cs/DefaultCoordinateSystemAxis.java            |  22 +-
 .../sis/referencing/cs/DefaultCylindricalCS.java   |  22 +-
 .../sis/referencing/cs/DefaultEllipsoidalCS.java   |  22 +-
 .../apache/sis/referencing/cs/DefaultLinearCS.java |  22 +-
 .../sis/referencing/cs/DefaultParametricCS.java    |  22 +-
 .../apache/sis/referencing/cs/DefaultPolarCS.java  |  22 +-
 .../sis/referencing/cs/DefaultSphericalCS.java     |  22 +-
 .../apache/sis/referencing/cs/DefaultTimeCS.java   |  22 +-
 .../sis/referencing/cs/DefaultUserDefinedCS.java   |  22 +-
 .../sis/referencing/cs/DefaultVerticalCS.java      |  22 +-
 .../sis/referencing/datum/AbstractDatum.java       |  22 +-
 .../sis/referencing/datum/DefaultEllipsoid.java    |  22 +-
 .../referencing/datum/DefaultEngineeringDatum.java |  22 +-
 .../referencing/datum/DefaultGeodeticDatum.java    |  22 +-
 .../sis/referencing/datum/DefaultImageDatum.java   |  22 +-
 .../referencing/datum/DefaultParametricDatum.java  |  22 +-
 .../referencing/datum/DefaultPrimeMeridian.java    |  22 +-
 .../referencing/datum/DefaultTemporalDatum.java    |  22 +-
 .../referencing/datum/DefaultVerticalDatum.java    |  22 +-
 .../sis/referencing/internal/MergedProperties.java |   4 +-
 .../operation/AbstractCoordinateOperation.java     |  22 +-
 .../operation/AbstractSingleOperation.java         |  22 +-
 .../operation/DefaultConcatenatedOperation.java    |  22 +-
 .../referencing/operation/DefaultConversion.java   |  22 +-
 .../operation/DefaultOperationMethod.java          |  22 +-
 .../operation/DefaultPassThroughOperation.java     |  22 +-
 .../operation/DefaultTransformation.java           |  22 +-
 .../sis/referencing/util/AxisDirections.java       |   8 +
 .../referencing/operation/matrix/SolverTest.java   |   2 +-
 .../transform/CartesianToSphericalTest.java        |   2 +-
 .../apache/sis/storage/geotiff/GeoTiffStore.java   |  36 +-
 .../sis/storage/geotiff/GeoTiffStoreProvider.java  |   5 +-
 .../sis/storage/geotiff/ImageFileDirectory.java    |   6 +-
 .../org/apache/sis/storage/geotiff/Reader.java     |   8 +
 .../apache/sis/storage/geotiff/WritableStore.java  |  83 +++
 .../org/apache/sis/storage/geotiff/Writer.java     |  16 +
 .../sis/storage/geotiff/writer/TileMatrix.java     |  21 +-
 .../org/apache/sis/storage/gpx/Description.java    |   2 +-
 .../apache/sis/io/stream/HyperRectangleWriter.java |  98 ++--
 .../sis/io/stream/SubsampledRectangleWriter.java   | 331 ++++++++++++
 .../main/org/apache/sis/storage/DataStores.java    |  60 ++-
 .../main/org/apache/sis/storage/FeatureQuery.java  |   4 +-
 .../apache/sis/storage/RasterLoadingStrategy.java  |   5 +-
 .../sis/storage/aggregate/GroupAggregate.java      |   6 +-
 .../apache/sis/storage/base/MetadataBuilder.java   |  39 +-
 .../apache/sis/storage/base/SimpleAggregate.java   |  57 ++
 .../org/apache/sis/storage/base/URIDataStore.java  |   2 +
 .../sis/storage/base/WritableAggregateSupport.java | 118 +++++
 .../WritableGridCoverageSupport.java}              |  29 +-
 .../main/org/apache/sis/storage/csv/Store.java     |   7 +-
 .../org/apache/sis/storage/esri/RasterStore.java   |   7 +-
 .../org/apache/sis/storage/esri/WritableStore.java |   6 +-
 .../apache/sis/storage/image/SingleImageStore.java |   5 +-
 .../apache/sis/storage/image/WorldFileStore.java   |   6 +-
 .../apache/sis/storage/image/WritableResource.java |   6 +-
 .../org/apache/sis/storage/internal/Resources.java |   6 +
 .../sis/storage/internal/Resources.properties      |   1 +
 .../sis/storage/internal/Resources_fr.properties   |   1 +
 .../io/stream/SubsampledRectangleWriterTest.java   | 192 +++++++
 .../main/org/apache/sis/util/CharSequences.java    |   8 +
 .../apache/sis/util/collection/TableColumn.java    |  29 +-
 .../main/org/apache/sis/util/resources/Errors.java |   2 +-
 .../apache/sis/util/resources/Errors.properties    |   2 +-
 .../apache/sis/util/resources/Errors_fr.properties |   2 +-
 .../org/apache/sis/util/resources/Vocabulary.java  |   5 +
 .../sis/util/resources/Vocabulary.properties       |   1 +
 .../sis/util/resources/Vocabulary_fr.properties    |   1 +
 .../org/apache/sis/util/CharSequencesTest.java     |   1 +
 gradle/wrapper/gradle-wrapper.jar                  | Bin 0 -> 63721 bytes
 gradle/wrapper/gradle-wrapper.properties           |   7 +
 gradlew                                            | 249 +++++++++
 gradlew.bat                                        |  92 ++++
 .../org.apache.sis.storage.DataStoreProvider       |   4 +
 .../main/module-info.java                          |   3 +
 .../storage/shapefile/ListingPropertyVisitor.java  |  79 +++
 .../sis/storage/shapefile/ShapefileProvider.java   |   4 +-
 .../sis/storage/shapefile/ShapefileStore.java      | 573 +++++++++++++++++++--
 .../apache/sis/storage/shapefile/dbf/DBFField.java | 264 +++++++++-
 .../sis/storage/shapefile/dbf/DBFFieldEncoder.java | 206 --------
 .../sis/storage/shapefile/dbf/DBFHeader.java       |  50 +-
 .../sis/storage/shapefile/dbf/DBFReader.java       |  38 +-
 .../{shp/ShapeWriter.java => dbf/DBFWriter.java}   |  44 +-
 .../shapefile/shp/ShapeGeometryEncoder.java        | 303 ++++++-----
 .../sis/storage/shapefile/shp/ShapeHeader.java     |  18 +-
 .../sis/storage/shapefile/shp/ShapeReader.java     |  21 +-
 .../sis/storage/shapefile/shp/ShapeRecord.java     |  34 +-
 .../sis/storage/shapefile/shp/ShapeWriter.java     |  51 +-
 .../{shp/ShapeWriter.java => shx/IndexWriter.java} |  39 +-
 .../sis/storage/shapefile/ShapefileStoreTest.java  | 109 ++++
 .../sis/storage/shapefile/dbf/DBFIOTest.java       | 100 +++-
 .../sis/storage/shapefile/shp/ShapeIOTest.java     |  72 ++-
 netbeans-project/ivy.xml                           |   8 +-
 netbeans-project/nbproject/project.xml             |   3 +
 optional/src/org.apache.sis.gui/bundle/bin/sis     |   4 +-
 optional/src/org.apache.sis.gui/bundle/bin/sis.bat |   2 +-
 optional/src/org.apache.sis.gui/bundle/bin/sisfx   |   4 +-
 .../src/org.apache.sis.gui/bundle/bin/sisfx.bat    |   2 +-
 parent/README.md                                   |   8 +
 .../module-info.java => parent/build.gradle.kts    |  20 +-
 parent/pom.xml                                     |   2 +-
 settings.gradle.kts                                |   9 +-
 233 files changed, 6082 insertions(+), 2234 deletions(-)

diff --cc endorsed/src/org.apache.sis.feature/main/module-info.java
index ceec296980,7018c1308f..a8270aa1d2
--- a/endorsed/src/org.apache.sis.feature/main/module-info.java
+++ b/endorsed/src/org.apache.sis.feature/main/module-info.java
@@@ -41,14 -41,11 +41,16 @@@ module org.apache.sis.feature 
      exports org.apache.sis.filter;
      exports org.apache.sis.index.tree;
  
 +    exports org.apache.sis.pending.geoapi.filter to
 +            org.apache.sis.storage,
 +            org.apache.sis.storage.sql,
++            org.apache.sis.storage.shapefile,       // In the "incubator" 
sub-project.
 +            org.apache.sis.portrayal;
 +
      exports org.apache.sis.filter.internal to
              org.apache.sis.storage,
              org.apache.sis.storage.sql,
+             org.apache.sis.storage.shapefile,       // In the "incubator" 
sub-project.
 -            org.apache.sis.cql,                     // In the "incubator" 
sub-project.
              org.apache.sis.portrayal;
  
      exports org.apache.sis.feature.internal to
@@@ -63,7 -60,9 +65,8 @@@
              org.apache.sis.storage,
              org.apache.sis.storage.xml,
              org.apache.sis.storage.sql,
-             org.apache.sis.storage.netcdf;
+             org.apache.sis.storage.netcdf,
 -            org.apache.sis.storage.shapefile,       // In the "incubator" 
sub-project.
 -            org.apache.sis.cql;                     // In the "incubator" 
sub-project.
++            org.apache.sis.storage.shapefile;       // In the "incubator" 
sub-project.
  
      exports org.apache.sis.geometry.wrapper.j2d to
              org.apache.sis.gui;                     // In the "optional" 
sub-project.
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java
index 9ec6cc3477,3b8242bf4c..c3ae920f32
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java
@@@ -24,17 -24,17 +24,17 @@@ import java.util.Optional
  import org.apache.sis.feature.Features;
  import org.apache.sis.feature.builder.FeatureTypeBuilder;
  import org.apache.sis.feature.builder.PropertyTypeBuilder;
+ import org.apache.sis.filter.internal.XPath;
  import org.apache.sis.math.FunctionProperty;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.feature.Feature;
 -import org.opengis.feature.FeatureAssociationRole;
 -import org.opengis.feature.FeatureType;
 -import org.opengis.feature.PropertyType;
 -import org.opengis.feature.PropertyNotFoundException;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.ValueReference;
 +// Specific to the main branch:
 +import org.opengis.util.ScopedName;
 +import org.apache.sis.feature.AbstractFeature;
 +import org.apache.sis.feature.AbstractIdentifiedType;
 +import org.apache.sis.feature.DefaultAssociationRole;
 +import org.apache.sis.feature.DefaultFeatureType;
 +import org.apache.sis.pending.geoapi.filter.Name;
 +import org.apache.sis.pending.geoapi.filter.ValueReference;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
index b7273dbe8f,4b8a162e81..b16688724f
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java
@@@ -55,9 -51,9 +55,9 @@@ import org.apache.sis.pending.geoapi.fi
   *
   * @author  Johann Sorel (Geomatys)
   * @author  Martin Desruisseaux (Geomatys)
-  * @version 1.4
+  * @version 1.5
   *
 - * @param  <R>  the type of resources (e.g. {@link 
org.opengis.feature.Feature}) to use as inputs.
 + * @param  <R>  the type of resources (e.g. {@link AbstractFeature}) to use 
as inputs.
   * @param  <G>  base class of geometry objects. The implementation-neutral 
type is GeoAPI {@link Geometry},
   *              but this factory allows the use of other implementations such 
as JTS
   *              {@link org.locationtech.jts.geom.Geometry} or ESRI {@link 
com.esri.core.geometry.Geometry}.
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
index 5284b66341,6b77bf77e6..99b42b947d
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
@@@ -102,10 -97,11 +102,11 @@@ abstract class PropertyValue<V> extend
       * @throws IllegalArgumentException if the given XPath is not supported.
       */
      @SuppressWarnings("unchecked")
-     static <V> ValueReference<AbstractFeature,V> create(String xpath, final 
Class<V> type) {
 -    static <V> ValueReference<Feature,V> create(final String xpath, final 
Class<V> type) {
++    static <V> ValueReference<AbstractFeature,V> create(final String xpath, 
final Class<V> type) {
+         final var parsed = new XPath(xpath);
+         List<String> path = parsed.path;
          boolean isVirtual = false;
-         List<String> path = XPath.split(xpath);
- split:  if (path != null) {
+         if (parsed.isAbsolute) {
              /*
               * If the XPath is like "/∗/property" where the root "/" is the 
feature instance,
               * we interpret that as meaning "property of a feature of any 
type", which means
diff --cc 
endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java
index 1d0a1e9380,4cacfb13fe..d0d511fc53
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java
@@@ -27,10 -27,22 +27,22 @@@ import org.apache.sis.filter.sqlmm.SQLM
   * @author  Martin Desruisseaux (Geomatys)
   */
  public final class FunctionNames extends Static {
 -    /** Value of {@link org.opengis.filter.NullOperator#getOperatorType()}. */
++    /** Value of {@code NullOperator.getOperatorType()}. */
+     public static final String PROPERTY_IS_NULL = "PROPERTY_IS_NULL";
+ 
 -    /** Value of {@link org.opengis.filter.NilOperator#getOperatorType()}. */
++    /** Value of {@code NilOperator.getOperatorType()}. */
+     public static final String PROPERTY_IS_NIL = "PROPERTY_IS_NIL";
+ 
 -    /** Value of {@link org.opengis.filter.LikeOperator#getOperatorType()}. */
++    /** Value of {@code LikeOperator.getOperatorType()}. */
+     public static final String PROPERTY_IS_LIKE = "PROPERTY_IS_LIKE";
+ 
+     /** Value of {@link 
org.opengis.filter.BetweenComparisonOperator#getOperatorType()}. */
+     public static final String PROPERTY_IS_BETWEEN = "PROPERTY_IS_BETWEEN";
+ 
 -    /** Value of {@link org.opengis.filter.Literal#getFunctionName()}. */
 +    /** Value of {@code Literal.getFunctionName()}. */
      public static final String Literal = "Literal";
  
 -    /** Value of {@link org.opengis.filter.ValueReference#getFunctionName()}. 
*/
 +    /** Value of {@code ValueReference.getFunctionName()}. */
      public static final String ValueReference = "ValueReference";
  
      /** The "Add" (+) arithmetic expression. */
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java
index 37f80d5989,36725fa120..8f07e02aa2
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java
@@@ -67,7 -67,7 +67,6 @@@
  //  @XmlJavaTypeAdapter(DQM_Measure.class),             // Not directly 
referenced, but a "weak" association exists.
      @XmlJavaTypeAdapter(DQM_Parameter.class),
      @XmlJavaTypeAdapter(DQM_SourceReference.class),
-     @XmlJavaTypeAdapter(GO_Boolean.class),
 -    @XmlJavaTypeAdapter(DQM_ValueStructure.class),
      @XmlJavaTypeAdapter(GO_Temporal.class),
      @XmlJavaTypeAdapter(GO_DateTime.class),
      @XmlJavaTypeAdapter(GO_GenericName.class),
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
index 86982e69fc,b49daea01c..c98cf18e67
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
@@@ -30,10 -31,10 +31,10 @@@
   * defined in the {@link org.opengis.metadata} package and sub-packages. That 
standard is identified in SIS by the
   * {@link org.apache.sis.metadata.MetadataStandard#ISO_19115} constant. Other 
standards are defined as well,
   * for example the {@link org.apache.sis.metadata.MetadataStandard#ISO_19123} 
constant stands for the standards
 - * defined by the interfaces in the {@link org.opengis.coverage} package and 
sub-packages.
 + * defined by the interfaces in the {@code org.opengis.coverage} package and 
sub-packages.
   *
-  * <p>For each interface, the collection of declared getter methods defines 
its <cite>properties</cite>
-  * (or <cite>attributes</cite>). If a {@link org.opengis.annotation.UML} 
annotation is attached to the getter method,
+  * <p>For each interface, the collection of declared getter methods defines 
its <dfn>properties</dfn>
+  * (or <dfn>attributes</dfn>). If a {@link org.opengis.annotation.UML} 
annotation is attached to the getter method,
   * the identifier declared in that annotation is taken as the property name. 
This is typically the name defined by the
   * International Standard from which the interface is derived. Otherwise (if 
there is no {@code UML} annotation)
   * the property name is inferred from the method name like what the 
<cite>Java Beans</cite> framework does.</p>
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java
index c3b725e6fb,bf708118a7..c14d0206d2
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java
@@@ -23,11 -23,11 +23,11 @@@ import org.opengis.metadata.maintenance
  import org.junit.Test;
  import org.apache.sis.xml.test.TestCase;
  
- import static org.junit.Assert.*;
+ import static org.junit.jupiter.api.Assertions.*;
  import static org.apache.sis.metadata.Assertions.assertXmlEquals;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.maintenance.Scope;
 +// Specific to the main branch:
 +import org.opengis.metadata.quality.Scope;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java
index 559156295f,1d86c8b4dc..d97bdf1c2c
--- 
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java
+++ 
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java
@@@ -27,11 -28,10 +28,10 @@@ import org.apache.sis.util.ArraysExt
  import org.junit.Test;
  import org.apache.sis.test.TestCase;
  
- import static org.junit.Assert.*;
- import static org.opengis.test.Assert.assertInstanceOf;
+ import static org.junit.jupiter.api.Assertions.*;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.metadata.citation.Responsibility;
 +// Specific to the main branch:
 +import org.opengis.metadata.citation.ResponsibleParty;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java
index 2697121021,2efa62e0c6..0b8293bd1c
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java
@@@ -87,9 -90,17 +87,17 @@@ public final class AxisDirections exten
       * 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).
       */
 -    @UML(identifier="starboard", obligation=CONDITIONAL, 
specification=ISO_19162)
 +    @UML(identifier="starboard", obligation=CONDITIONAL, 
specification=UNSPECIFIED)
      public static final AxisDirection STARBOARD = 
AxisDirection.valueOf("STARBOARD");
  
+     /**
+      * Port direction.
+      * For an observer at the centre of the object this will be towards its 
left.
+      * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+      */
 -    @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19162)
++    @UML(identifier="port", obligation=CONDITIONAL, specification=UNSPECIFIED)
+     public static final AxisDirection PORT = AxisDirection.valueOf("PORT");
+ 
      /**
       * Direction of geographic angles (bearing).
       * Added in ISO 19111:2019 (was not in ISO 19111:2007).
diff --cc 
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
index 8a0a0420ff,4d22dfe327..e69005946b
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java
@@@ -172,14 -171,10 +171,10 @@@ abstract class RasterStore extends PRJD
              builder.addFormatName(formatName);
              listeners.warning(e);
          }
 -        builder.addResourceScope(ScopeCode.COVERAGE, null);
 +        builder.addResourceScope(ScopeCode.valueOf("COVERAGE"), null);
          builder.addLanguage(Locale.ENGLISH, encoding, 
MetadataBuilder.Scope.METADATA);
          builder.addSpatialRepresentation(null, gridGeometry, true);
-         try {
-             builder.addExtent(gridGeometry.getEnvelope());
-         } catch (TransformException e) {
-             listeners.warning(e);
-         }
+         builder.addExtent(gridGeometry.getEnvelope(), listeners);
          /*
           * Do not invoke `getSampleDimensions()` because computing sample 
dimensions without statistics
           * may cause the loading of the full image. Even if 
`GridCoverage.getSampleDimensions()` exists
diff --cc 
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
index b5b714edcb,6da2247bb3..19acd4ed95
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java
@@@ -536,10 -534,10 +534,10 @@@ loop:   for (int convention=0;; convent
                  }
              }
              builder.addFormatName(format);                          // Does 
nothing if `format` is null.
 -            builder.addResourceScope(ScopeCode.COVERAGE, null);
 +            builder.addResourceScope(ScopeCode.valueOf("COVERAGE"), null);
              builder.addSpatialRepresentation(null, 
getGridGeometry(MAIN_IMAGE), true);
              if (gridGeometry.isDefined(GridGeometry.ENVELOPE)) {
-                 builder.addExtent(gridGeometry.getEnvelope());
+                 builder.addExtent(gridGeometry.getEnvelope(), listeners);
              }
              addTitleOrIdentifier(builder);
              builder.setISOStandards(false);
diff --cc 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java
index 0000000000,c3f55427fd..c165bf1df0
mode 000000,100644..100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java
@@@ -1,0 -1,82 +1,79 @@@
+ /*
+  * 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.storage.shapefile;
+ 
+ import java.util.Collection;
 -import org.opengis.util.CodeList;
+ import org.apache.sis.filter.internal.FunctionNames;
+ import org.apache.sis.filter.internal.Visitor;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.filter.BetweenComparisonOperator;
 -import org.opengis.filter.Filter;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.ValueReference;
 -import org.opengis.filter.ComparisonOperatorName;
 -import org.opengis.filter.LikeOperator;
 -import org.opengis.filter.LogicalOperator;
++// Specific to the main branch:
++import org.apache.sis.filter.Filter;
++import org.apache.sis.filter.Expression;
++import org.apache.sis.pending.geoapi.filter.BetweenComparisonOperator;
++import org.apache.sis.pending.geoapi.filter.ValueReference;
++import org.apache.sis.pending.geoapi.filter.ComparisonOperatorName;
++import org.apache.sis.pending.geoapi.filter.LogicalOperator;
+ 
+ 
+ /**
+  * Expression visitor that returns a list of all Feature attributs requiered 
by this expression.
+  *
+  * @author Johann Sorel (Geomatys)
+  */
+ final class ListingPropertyVisitor extends Visitor<Object,Collection<String>> 
{
+ 
+     public static final ListingPropertyVisitor VISITOR = new 
ListingPropertyVisitor();
+ 
+     protected ListingPropertyVisitor() {
+         setLogicalHandlers((f, names) -> {
+             final LogicalOperator<Object> filter = (LogicalOperator<Object>) 
f;
+             for (Filter<Object> child : filter.getOperands()) {
+                 visit(child, names);
+             }
+         });
+         
setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_BETWEEN),
 (f, names) -> {
+             final BetweenComparisonOperator<Object> filter = 
(BetweenComparisonOperator<Object>) f;
+             visit(filter.getExpression(),    names);
+             visit(filter.getLowerBoundary(), names);
+             visit(filter.getUpperBoundary(), names);
+         });
+         
setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_LIKE),
 (f, names) -> {
 -            final LikeOperator<Object> filter = (LikeOperator<Object>) f;
 -            visit(filter.getExpressions().get(0), names);
++            visit(f.getExpressions().get(0), names);
+         });
+         setExpressionHandler(FunctionNames.ValueReference, (e, names) -> {
+             final ValueReference<Object,?> expression = 
(ValueReference<Object,?>) e;
+             final String propName = expression.getXPath();
+             if (!propName.trim().isEmpty()) {
+                 names.add(propName);
+             }
+         });
+     }
+ 
+     @Override
 -    protected void typeNotFound(final CodeList<?> type, final Filter<Object> 
filter, final Collection<String> names) {
++    protected void typeNotFound(final Enum<?> type, final Filter<Object> 
filter, final Collection<String> names) {
+         for (final Expression<? super Object, ?> f : filter.getExpressions()) 
{
+             visit(f, names);
+         }
+     }
+ 
+     @Override
+     protected void typeNotFound(final String type, final Expression<Object, 
?> expression, final Collection<String> names) {
+         for (final Expression<? super Object, ?> p : 
expression.getParameters()) {
+             visit(p, names);
+         }
+     }
+ }
diff --cc 
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
index 04774dfc66,f17560575a..68984f7c48
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
@@@ -34,8 -44,12 +44,11 @@@ import java.util.concurrent.locks.Reent
  import java.util.function.Consumer;
  import java.util.function.Predicate;
  import java.util.function.UnaryOperator;
+ import java.util.logging.Level;
+ import java.util.logging.Logger;
  import java.util.stream.Stream;
  import java.util.stream.StreamSupport;
+ 
 -import org.opengis.util.CodeList;
  import org.opengis.geometry.Envelope;
  import org.opengis.metadata.Metadata;
  import org.opengis.parameter.ParameterValueGroup;
@@@ -67,11 -91,30 +90,30 @@@ import org.apache.sis.storage.shapefile
  import org.apache.sis.storage.shapefile.shp.ShapeHeader;
  import org.apache.sis.storage.shapefile.shp.ShapeReader;
  import org.apache.sis.storage.shapefile.shp.ShapeRecord;
+ import org.apache.sis.storage.shapefile.shp.ShapeType;
+ import org.apache.sis.storage.shapefile.shp.ShapeWriter;
+ import org.apache.sis.storage.shapefile.shx.IndexWriter;
  import org.apache.sis.util.collection.BackingStoreException;
+ import org.locationtech.jts.geom.Geometry;
+ import org.locationtech.jts.geom.LineString;
+ import org.locationtech.jts.geom.MultiLineString;
+ import org.locationtech.jts.geom.MultiPoint;
+ import org.locationtech.jts.geom.MultiPolygon;
+ import org.locationtech.jts.geom.Point;
+ import org.locationtech.jts.geom.Polygon;
  
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.feature.Feature;
 -import org.opengis.feature.FeatureType;
 -import org.opengis.feature.PropertyType;
 -import org.opengis.feature.AttributeType;
 -import org.opengis.filter.Expression;
 -import org.opengis.filter.Filter;
 -import org.opengis.filter.Literal;
 -import org.opengis.filter.LogicalOperator;
 -import org.opengis.filter.LogicalOperatorName;
 -import org.opengis.filter.SpatialOperatorName;
 -import org.opengis.filter.ValueReference;
 +// Specific to the main branch:
 +import org.apache.sis.feature.AbstractFeature;
 +import org.apache.sis.feature.DefaultFeatureType;
++import org.apache.sis.feature.AbstractIdentifiedType;
++import org.apache.sis.feature.DefaultAttributeType;
++import org.apache.sis.filter.Expression;
++import org.apache.sis.filter.Filter;
++import org.apache.sis.pending.geoapi.filter.Literal;
++import org.apache.sis.pending.geoapi.filter.LogicalOperator;
++import org.apache.sis.pending.geoapi.filter.LogicalOperatorName;
++import org.apache.sis.pending.geoapi.filter.SpatialOperatorName;
++import org.apache.sis.pending.geoapi.filter.ValueReference;
  
  
  /**
@@@ -147,10 -190,61 +189,61 @@@ public final class ShapefileStore exten
          return featureSetView.getEnvelope();
      }
  
+     @Override
 -    public void updateType(FeatureType featureType) throws DataStoreException 
{
++    public void updateType(DefaultFeatureType featureType) throws 
DataStoreException {
+         featureSetView.updateType(featureType);
+     }
+ 
+     @Override
 -    public void add(Iterator<? extends Feature> iterator) throws 
DataStoreException {
++    public void add(Iterator<? extends AbstractFeature> iterator) throws 
DataStoreException {
+         featureSetView.add(iterator);
+     }
+ 
+     @Override
 -    public void removeIf(Predicate<? super Feature> predicate) throws 
DataStoreException {
++    public void removeIf(Predicate<? super AbstractFeature> predicate) throws 
DataStoreException {
+         featureSetView.removeIf(predicate);
+     }
+ 
+     @Override
 -    public void replaceIf(Predicate<? super Feature> predicate, 
UnaryOperator<Feature> unaryOperator) throws DataStoreException {
++    public void replaceIf(Predicate<? super AbstractFeature> predicate, 
UnaryOperator<AbstractFeature> unaryOperator) throws DataStoreException {
+         featureSetView.replaceIf(predicate, unaryOperator);
+     }
+ 
      private class AsFeatureSet extends AbstractFeatureSet implements 
WritableFeatureSet {
  
-         private AsFeatureSet() {
+         private final Rectangle2D.Double filter;
+         private final Set<String> dbfProperties;
+         private final boolean readShp;
+ 
+         /**
+          * Extracted informations
+          */
+         private int[] dbfPropertiesIndex;
+         private ShapeHeader shpHeader;
+         private DBFHeader dbfHeader;
+         /**
+          * Name of the field used as identifier, may be null.
+          */
+         private String idField;
+         private CoordinateReferenceSystem crs;
 -        private FeatureType type;
++        private DefaultFeatureType type;
+ 
+         /**
+          * @param filter optional shape filter, must be in data CRS
+          * @param properties dbf properties to read, null for all properties
+          */
+         private AsFeatureSet(Rectangle2D.Double filter, boolean readShp, 
Set<String> properties) {
              super(null);
+             this.readShp = readShp;
+             this.filter = filter;
+             this.dbfProperties = properties;
+         }
+ 
+         /**
+          * @return true if this view reads all data without any filter.
+          */
+         private boolean isDefaultView() {
+             return filter == null && dbfProperties == null && readShp;
          }
  
          @Override
@@@ -227,9 -344,34 +343,34 @@@
              return type;
          }
  
+         @Override
+         public Optional<Envelope> getEnvelope() throws DataStoreException {
+             getType();//force loading headers
+             if (shpHeader != null && filter == null) {
+                 final GeneralEnvelope env = new GeneralEnvelope(crs);
+                 env.setRange(0, shpHeader.bbox.getMinimum(0), 
shpHeader.bbox.getMaximum(0));
+                 env.setRange(1, shpHeader.bbox.getMinimum(1), 
shpHeader.bbox.getMaximum(1));
+                 return Optional.of(env);
+             }
+             return super.getEnvelope();
+         }
+ 
+         @Override
+         public OptionalLong getFeatureCount() {
+             try {
+                 getType();//force loading headers
+                 if (dbfHeader != null && filter == null) {
+                     return OptionalLong.of(dbfHeader.nbRecord);
+                 }
+             } catch (DataStoreException ex) {
+                 //do nothing
+             }
+             return super.getFeatureCount();
+         }
+ 
          @Override
 -        public Stream<Feature> features(boolean parallel) throws 
DataStoreException {
 -            final FeatureType type = getType();
 +        public Stream<AbstractFeature> features(boolean parallel) throws 
DataStoreException {
 +            final DefaultFeatureType type = getType();
              final ShapeReader shpreader;
              final DBFReader dbfreader;
              try {
@@@ -238,28 -380,73 +379,73 @@@
              } catch (IOException ex) {
                  throw new DataStoreException("Faild to open shp and dbf 
files.", ex);
              }
-             final DBFHeader header = dbfreader.getHeader();
  
-             final Spliterator spliterator = new 
Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
-                 @Override
-                 public boolean tryAdvance(Consumer action) {
-                     try {
-                         final ShapeRecord shpRecord = shpreader.next();
-                         if (shpRecord == null) return false;
-                         final DBFRecord dbfRecord = dbfreader.next();
-                         final AbstractFeature next = type.newInstance();
-                         next.setPropertyValue(GEOMETRY_NAME, 
shpRecord.geometry);
-                         for (int i = 0; i < header.fields.length; i++) {
-                             next.setPropertyValue(header.fields[i].fieldName, 
dbfRecord.fields[i]);
+             final Spliterator spliterator;
+             if (readShp && dbfPropertiesIndex.length > 0) {
+                 //read both shp and dbf
+                 final DBFHeader header = dbfreader.getHeader();
+ 
+                 spliterator = new 
Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
+                     @Override
+                     public boolean tryAdvance(Consumer action) {
+                         try {
+                             final ShapeRecord shpRecord = shpreader.next();
+                             if (shpRecord == null) return false;
+                             //move dbf to record offset, some shp record 
might have been skipped because of filter
+                             dbfreader.moveToOffset(header.headerSize + 
(shpRecord.recordNumber-1) * header.recordSize);
+                             final DBFRecord dbfRecord = dbfreader.next();
 -                            final Feature next = type.newInstance();
++                            final AbstractFeature next = type.newInstance();
+                             next.setPropertyValue(GEOMETRY_NAME, 
shpRecord.geometry);
+                             for (int i = 0; i < dbfPropertiesIndex.length; 
i++) {
+                                 
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord.fields[i]);
+                             }
+                             action.accept(next);
+                             return true;
+                         } catch (IOException ex) {
+                             throw new BackingStoreException(ex.getMessage(), 
ex);
                          }
-                         action.accept(next);
-                         return true;
-                     } catch (IOException ex) {
-                         throw new BackingStoreException(ex.getMessage(), ex);
                      }
-                 }
-             };
+                 };
+             } else if (readShp) {
+                 //read only the shp
+                 spliterator = new 
Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
+                     @Override
+                     public boolean tryAdvance(Consumer action) {
+                         try {
+                             final ShapeRecord shpRecord = shpreader.next();
+                             if (shpRecord == null) return false;
 -                            final Feature next = type.newInstance();
++                            final AbstractFeature next = type.newInstance();
+                             next.setPropertyValue(GEOMETRY_NAME, 
shpRecord.geometry);
+                             action.accept(next);
+                             return true;
+                         } catch (IOException ex) {
+                             throw new BackingStoreException(ex.getMessage(), 
ex);
+                         }
+                     }
+                 };
+             } else {
+                 //read only dbf
+                 final DBFHeader header = dbfreader.getHeader();
+                 spliterator = new 
Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
+                     @Override
+                     public boolean tryAdvance(Consumer action) {
+                         try {
+                             final DBFRecord dbfRecord = dbfreader.next();
+                             if (dbfRecord == null) return false;
 -                            final Feature next = type.newInstance();
++                            final AbstractFeature next = type.newInstance();
+                             for (int i = 0; i < dbfPropertiesIndex.length; 
i++) {
+                                 
next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, 
dbfRecord.fields[i]);
+                             }
+                             action.accept(next);
+                             return true;
+                         } catch (IOException ex) {
+                             throw new BackingStoreException(ex.getMessage(), 
ex);
+                         }
+                     }
+                 };
+             }
+ 
 -            final Stream<Feature> stream = StreamSupport.stream(spliterator, 
false);
 +            final Stream<AbstractFeature> stream = 
StreamSupport.stream(spliterator, false);
              return stream.onClose(new Runnable() {
                  @Override
                  public void run() {
@@@ -274,23 -461,210 +460,212 @@@
  
          }
  
+         @Override
+         public FeatureSet subset(Query query) throws 
UnsupportedQueryException, DataStoreException {
+             //try to optimise the query for common cases
+             opti:
+             if (query instanceof FeatureQuery) {
+                 final FeatureQuery fq = (FeatureQuery) query;
+                 FeatureQuery.NamedExpression[] projection = 
fq.getProjection();
 -                Filter<? super Feature> selection = fq.getSelection();
++                Filter<? super AbstractFeature> selection = fq.getSelection();
+ 
+                 if (selection == null && projection == null) {
+                     //no optimisation
+                     break opti;
+                 }
+ 
+                 //force loading
 -                final FeatureType type = getType();
++                final DefaultFeatureType type = getType();
+ 
+                 //extract bbox
+                 Envelope bbox = null;
+                 if (selection != null) {
+                     //run optimizations
+                     final Optimization optimization = new Optimization();
+                     optimization.setFeatureType(type);
+                     selection = optimization.apply(selection);
+                     final Entry<Envelope, Filter> split = 
extractBbox(selection);
+                     bbox = split.getKey();
+                     selection = split.getValue();
+                 }
+ 
+                 //extract field names
+                 boolean simpleSelection = true; //true if there are no alias 
and all expressions are ValueReference
+                 Set<String> properties = null;
+                 if (projection != null) {
+                     properties = new HashSet<>();
+                     if (selection!=null) 
ListingPropertyVisitor.VISITOR.visit((Filter) selection, properties);
+                     for (FeatureQuery.NamedExpression ne : projection) {
+                         ListingPropertyVisitor.VISITOR.visit((Expression) 
ne.expression, properties);
+                         simpleSelection &= (ne.alias == null);
+                         simpleSelection &= 
(ne.expression.getFunctionName().tip().toString().equals(FunctionNames.ValueReference));
+                     }
+ 
+                     //if link fields are referenced, add target fields
+                     if (properties.contains(AttributeConvention.IDENTIFIER)) 
simpleSelection &= !properties.add(idField);
+                     if (properties.contains(AttributeConvention.GEOMETRY)) 
simpleSelection &= !properties.add(GEOMETRY_NAME);
+                     if (properties.contains(AttributeConvention.ENVELOPE)) 
simpleSelection &= !properties.add(GEOMETRY_NAME);
+                 }
+ 
+                 final boolean readShp = projection == null || 
properties.contains(GEOMETRY_NAME);
+                 Rectangle2D.Double area = null;
+                 if (bbox != null) {
+                     try {
+                         bbox = Envelopes.transform(bbox, crs);
+                     } catch (TransformException ex) {
+                         throw new DataStoreException("Failed to transform 
bbox filter", ex);
+                     }
+                     area = new Rectangle2D.Double(bbox.getMinimum(0), 
bbox.getMinimum(1), bbox.getSpan(0), bbox.getSpan(1));
+ 
+                     //combine this area with the one we already have since 
this is a subset
+                     if (filter != null) {
+                         area = (Rectangle2D.Double) 
area.createIntersection(filter);
+                     }
+                 }
+ 
+                 final AsFeatureSet fs = new AsFeatureSet(area, readShp, 
properties);
+                 //see if there are elements we could not handle
+                 final FeatureQuery subQuery = new FeatureQuery();
+                 boolean needSubProcessing = false;
+                 if (fq.getLimit().isPresent()){
+                     needSubProcessing = true;
+                     subQuery.setLimit(fq.getLimit().getAsLong());
+                 }
+                 if (fq.getLinearResolution() != null) {
+                     needSubProcessing = true;
+                     subQuery.setLinearResolution(fq.getLinearResolution());
+                 }
+                 if (fq.getOffset() != 0) {
+                     needSubProcessing = true;
+                     subQuery.setOffset(fq.getOffset());
+                 }
++                /* Unsupported on the main branch.
+                 if (fq.getSortBy() != null) {
+                     needSubProcessing = true;
+                     subQuery.setSortBy(fq.getSortBy());
+                 }
++                */
+                 if (selection != null) {
+                     needSubProcessing = true;
+                     subQuery.setSelection(selection);
+                 }
+                 if (!simpleSelection) {
+                     needSubProcessing = true;
+                     subQuery.setProjection(projection);
+                 }
+ 
+                 return needSubProcessing ? fs.parentSubSet(subQuery) : fs;
+             }
+ 
+             return super.subset(query);
+         }
+ 
+         private FeatureSet parentSubSet(Query query) throws 
DataStoreException {
+             return super.subset(query);
+         }
+ 
          @Override
 -        public void updateType(FeatureType newType) throws DataStoreException 
{
 +        public void updateType(DefaultFeatureType newType) throws 
DataStoreException {
-             throw new UnsupportedOperationException("Not supported yet.");
+             if (true) throw new UnsupportedOperationException("Not supported 
yet.");
+ 
+             if (!isDefaultView()) throw new DataStoreException("Resource not 
writable in current filter state");
+             if (Files.exists(shpPath)) {
+                 throw new DataStoreException("Update type is possible only 
when files do not exist. It can be used to create a new shapefile but not to 
update one.");
+             }
+ 
+             final ShapeHeader shpHeader = new ShapeHeader();
+             final DBFHeader dbfHeader = new DBFHeader();
+             Charset charset = StandardCharsets.UTF_8;
+             CoordinateReferenceSystem crs = 
CommonCRS.WGS84.normalizedGeographic();
+ 
 -            for (PropertyType pt : newType.getProperties(true)) {
 -                if (pt instanceof AttributeType) {
 -                    final AttributeType at = (AttributeType) pt;
++            for (AbstractIdentifiedType pt : newType.getProperties(true)) {
++                if (pt instanceof DefaultAttributeType) {
++                    final DefaultAttributeType at = (DefaultAttributeType) pt;
+                     final Class valueClass = at.getValueClass();
+                     if (Geometry.class.isAssignableFrom(valueClass)) {
+                         if (shpHeader.shapeType != 0) {
+                             throw new DataStoreException("Shapefile format 
can only contain one geometry");
+                         }
+                         if (Point.class.isAssignableFrom(valueClass)) 
shpHeader.shapeType = ShapeType.VALUE_POINT;
+                         else if 
(MultiPoint.class.isAssignableFrom(valueClass)) shpHeader.shapeType = 
ShapeType.VALUE_MULTIPOINT;
+                         else if 
(LineString.class.isAssignableFrom(valueClass) || 
MultiLineString.class.isAssignableFrom(valueClass)) shpHeader.shapeType = 
ShapeType.VALUE_POLYLINE;
+                         else if (Polygon.class.isAssignableFrom(valueClass) 
|| MultiPolygon.class.isAssignableFrom(valueClass)) shpHeader.shapeType = 
ShapeType.VALUE_POLYGON;
+                         else throw new DataStoreException("Unsupported 
geometry type " + valueClass);
+ 
+                         Object cdt = 
at.characteristics().get(AttributeConvention.CRS_CHARACTERISTIC);
+                         if (cdt instanceof CoordinateReferenceSystem) {
+                             crs = (CoordinateReferenceSystem) cdt;
+                         }
+ 
+                     } else if (String.class.isAssignableFrom(valueClass)) {
+ 
+                     } else if (Integer.class.isAssignableFrom(valueClass)) {
+ 
+                     } else if (Long.class.isAssignableFrom(valueClass)) {
+ 
+                     } else if (Float.class.isAssignableFrom(valueClass)) {
+ 
+                     } else if (Double.class.isAssignableFrom(valueClass)) {
+ 
+                     } else if (LocalDate.class.isAssignableFrom(valueClass)) {
+ 
+                     } else {
+                         LOGGER.log(Level.WARNING, "Shapefile writing, field 
{0} is not supported", pt.getName());
+                     }
+                 } else {
+                     LOGGER.log(Level.WARNING, "Shapefile writing, field {0} 
is not supported", pt.getName());
+                 }
+             }
+ 
+             //write shapefile
+             try (ShapeWriter writer = new 
ShapeWriter(ShpFiles.openWriteChannel(files.shpFile))) {
+                 writer.write(shpHeader);
+             } catch (IOException ex){
+                 throw new DataStoreException("Failed to create shapefile 
(shp).", ex);
+             }
+ 
+             //write shx
+             try (IndexWriter writer = new 
IndexWriter(ShpFiles.openWriteChannel(files.shxFile))) {
+                 writer.write(shpHeader);
+             } catch (IOException ex){
+                 throw new DataStoreException("Failed to create shapefile 
(shx).", ex);
+             }
+ 
+             //write dbf
+             try (DBFWriter writer = new 
DBFWriter(ShpFiles.openWriteChannel(files.dbfFile))) {
+                 writer.write(dbfHeader);
+             } catch (IOException ex){
+                 throw new DataStoreException("Failed to create shapefile 
(dbf).", ex);
+             }
+ 
+             //write cpg
+             try {
+                 CpgFiles.write(charset, files.cpgFile);
+             } catch (IOException ex) {
+                 throw new DataStoreException("Failed to create shapefile 
(cpg).", ex);
+             }
+ 
+             //write prj
+             //todo
+ 
+ 
          }
  
          @Override
 -        public void add(Iterator<? extends Feature> features) throws 
DataStoreException {
 +        public void add(Iterator<? extends AbstractFeature> features) throws 
DataStoreException {
+             if (!isDefaultView()) throw new DataStoreException("Resource not 
writable in current filter state");
              throw new UnsupportedOperationException("Not supported yet.");
          }
  
          @Override
 -        public void removeIf(Predicate<? super Feature> filter) throws 
DataStoreException {
 +        public void removeIf(Predicate<? super AbstractFeature> filter) 
throws DataStoreException {
+             if (!isDefaultView()) throw new DataStoreException("Resource not 
writable in current filter state");
              throw new UnsupportedOperationException("Not supported yet.");
          }
  
          @Override
 -        public void replaceIf(Predicate<? super Feature> filter, 
UnaryOperator<Feature> updater) throws DataStoreException {
 +        public void replaceIf(Predicate<? super AbstractFeature> filter, 
UnaryOperator<AbstractFeature> updater) throws DataStoreException {
+             if (!isDefaultView()) throw new DataStoreException("Resource not 
writable in current filter state");
              throw new UnsupportedOperationException("Not supported yet.");
          }
      }
@@@ -382,4 -756,98 +757,98 @@@
          }
      }
  
+ 
+     /**
+      * Will only split bbox with direct value reference to the geometry.
+      *
+      * @param filter to split, not null
+      * @return entry with key is a BBox filter and value what remains of the 
filter.
+      *        each filter can be null but not both.
+      */
+     private static Entry<Envelope,Filter> extractBbox(Filter<?> filter) {
+ 
 -        final CodeList operatorType = filter.getOperatorType();
++        final Enum operatorType = filter.getOperatorType();
+ 
+         if (SpatialOperatorName.BBOX.equals(operatorType)) {
+             Envelope env = isDirectBbox(filter);
+             if (env != null) {
+                 return new AbstractMap.SimpleImmutableEntry<>(env, null);
+             } else {
+                 return new AbstractMap.SimpleImmutableEntry<>(null, filter);
+             }
+ 
+         } else if (LogicalOperatorName.AND.equals(operatorType)) {
+ 
+             boolean rebuildAnd = false;
+             List<Filter<?>> lst = (List<Filter<?>>) 
((LogicalOperator<?>)filter).getOperands();
+             Envelope bbox = null;
+             for (int i = 0; i < lst.size(); i++) {
+                 final Filter<?> f = lst.get(i);
+                 final Entry<Envelope, Filter> split = extractBbox(f);
+                 Envelope cdtBbox = split.getKey();
+                 Filter cdtOther = split.getValue();
+                 if (cdtBbox != null) {
+                     if (bbox == null) {
+                         bbox = cdtBbox;
+                     } else {
+                         throw new RuntimeException("Combine bbox");
+                     }
+ 
+                     //see if we need to rebuild the AND filter
+                     if (cdtOther != f) {
+                         if (!rebuildAnd) {
+                             rebuildAnd = true;
+                             lst = new ArrayList<>(lst);
+                         }
+                         //replace in list
+                         if (cdtOther != null) {
+                             lst.set(i, cdtOther);
+                         } else {
+                             lst.remove(i);
+                             i--;
+                         }
+                     }
+                 }
+             }
+ 
+             if (rebuildAnd) {
+                 if (lst.isEmpty()) {
+                     filter = null;
+                 } else if (lst.size() == 1) {
+                     filter = lst.get(0);
+                 } else {
+                     filter = 
DefaultFilterFactory.forFeatures().and((List)lst);
+                 }
+             }
+ 
+             return new AbstractMap.SimpleImmutableEntry<>(bbox, filter);
+         } else {
+             //can do nothing
+             return new AbstractMap.SimpleImmutableEntry<>(null, filter);
+         }
+     }
+ 
+     /**
+      * Returns envelope if the other expression is a direct value reference.
+      * @param bbox
+      * @return filter envelope
+      */
+     private static Envelope isDirectBbox(Filter<?> bbox) {
+         Envelope env = null;
+         for (Expression exp : bbox.getExpressions()) {
+             if (exp instanceof ValueReference) {
+                 final ValueReference<Object,?> expression = 
(ValueReference<Object,?>) exp;
+                 final String propName = expression.getXPath();
+                 if ( !(GEOMETRY_NAME.equals(propName) || 
AttributeConvention.GEOMETRY.equals(propName))) {
+                     return null;
+                 }
+             } else if (exp instanceof Literal) {
+                 Object value = ((Literal) exp).getValue();
+                 env = Geometries.wrap(value).get().getEnvelope();
+             }
+         }
+         return env;
+     }
+ 
+ 
  }
diff --cc 
incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
index 9eb5e07bea,cfa9d2645e..5dbc92b826
--- 
a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
@@@ -87,4 -94,107 +93,107 @@@ public class ShapefileStoreTest 
              assertFalse(iterator.hasNext());
          }
      }
+ 
+     /**
+      * Test optimized envelope filter.
+      */
+     @Test
+     public void testEnvelopeFilter() throws URISyntaxException, 
DataStoreException {
+         final URL url = 
ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp");
+         final ShapefileStore store = new 
ShapefileStore(Paths.get(url.toURI()));
+ 
 -        final FilterFactory<Feature, Object, Object> ff = 
DefaultFilterFactory.forFeatures();
++        final DefaultFilterFactory<AbstractFeature, Object, Object> ff = 
DefaultFilterFactory.forFeatures();
+ 
+         final GeneralEnvelope env = new 
GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic());
+         env.setRange(0, 2, 3);
+         env.setRange(1, 42, 43);
+ 
+         final FeatureQuery query = new FeatureQuery();
+         query.setSelection(ff.bbox(ff.property("geometry"), env));
+         FeatureSet featureset = store.subset(query);
+         //ensure we obtained an optimized version
+         
assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", 
featureset.getClass().getName());
+ 
 -        try (Stream<Feature> stream = featureset.features(false)) {
 -            Iterator<Feature> iterator = stream.iterator();
++        try (Stream<AbstractFeature> stream = featureset.features(false)) {
++            Iterator<AbstractFeature> iterator = stream.iterator();
+             assertTrue(iterator.hasNext());
 -            Feature feature = iterator.next();
++            AbstractFeature feature = iterator.next();
+             assertEquals(2L, feature.getPropertyValue("id"));
+             assertEquals("text2", feature.getPropertyValue("text"));
+             assertEquals(40L, feature.getPropertyValue("integer"));
+             assertEquals(60.0, feature.getPropertyValue("float"));
+             assertEquals(LocalDate.of(2023, 10, 28), 
feature.getPropertyValue("date"));
+             Point pt2 = (Point) feature.getPropertyValue("geometry");
+ 
+             assertFalse(iterator.hasNext());
+         }
+     }
+ 
+     /**
+      * Test optimized field selection.
+      */
+     @Test
+     public void testFieldFilter() throws URISyntaxException, 
DataStoreException {
+         final URL url = 
ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp");
+         final ShapefileStore store = new 
ShapefileStore(Paths.get(url.toURI()));
+ 
+ 
+         final FeatureQuery query = new FeatureQuery();
+         query.setProjection("text", "float");
+         FeatureSet featureset = store.subset(query);
+         //ensure we obtained an optimized version
+         
assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", 
featureset.getClass().getName());
+ 
 -        try (Stream<Feature> stream = featureset.features(false)) {
 -            Iterator<Feature> iterator = stream.iterator();
++        try (Stream<AbstractFeature> stream = featureset.features(false)) {
++            Iterator<AbstractFeature> iterator = stream.iterator();
+             assertTrue(iterator.hasNext());
 -            Feature feature1 = iterator.next();
++            AbstractFeature feature1 = iterator.next();
+             assertEquals("text1", feature1.getPropertyValue("text"));
+             assertEquals(20.0, feature1.getPropertyValue("float"));
+ 
+             assertTrue(iterator.hasNext());
 -            Feature feature2 = iterator.next();
++            AbstractFeature feature2 = iterator.next();
+             assertEquals("text2", feature2.getPropertyValue("text"));
+             assertEquals(60.0, feature2.getPropertyValue("float"));
+ 
+             assertFalse(iterator.hasNext());
+         }
+     }
+ 
+     /**
+      * Test creating a new shapefile.
+      */
+     @Ignore
+     @Test
+     public void testCreate() throws URISyntaxException, DataStoreException {
+         //todo
+     }
+ 
+     /**
+      * Test adding features to a shapefile.
+      */
+     @Ignore
+     @Test
+     public void testAddFeatures() throws URISyntaxException, 
DataStoreException {
+         //todo
+     }
+ 
+     /**
+      * Test remove features from a shapefile.
+      */
+     @Ignore
+     @Test
+     public void testRemoveFeatures() throws URISyntaxException, 
DataStoreException {
+         //todo
+     }
+ 
+     /**
+      * Test replacing features in a shapefile.
+      */
+     @Ignore
+     @Test
+     public void testReplaceFeatures() throws URISyntaxException, 
DataStoreException {
+         //todo
+     }
+ 
  }
diff --cc netbeans-project/ivy.xml
index 6dfb805b1a,536f6fffc6..fcc8fc0de2
--- a/netbeans-project/ivy.xml
+++ b/netbeans-project/ivy.xml
@@@ -11,10 -11,8 +11,10 @@@
  <ivy-module version="2.0">
      <info organisation="org.apache" module="sis"/>
      <dependencies defaultconf="default">
 +        <dependency org="org.opengis"            name="geoapi"                
  rev="3.0.2"/>
 +        <dependency org="org.opengis"            name="geoapi-conformance"    
  rev="3.0.2"/>
          <dependency org="javax.measure"          name="unit-api"              
  rev="2.1.3"/>
-         <dependency org="org.glassfish.jaxb"     name="jaxb-runtime"          
  rev="4.0.3"/>
+         <dependency org="org.glassfish.jaxb"     name="jaxb-runtime"          
  rev="4.0.4"/>
          <dependency org="org.eclipse"            name="yasson"                
  rev="3.0.3"/>
          <dependency org="com.esri.geometry"      name="esri-geometry-api"     
  rev="2.2.4"/>
          <dependency org="org.locationtech.jts"   name="jts-core"              
  rev="1.19.0"/>
diff --cc settings.gradle.kts
index d26ccc2bee,60f13a1608..6c83faf896
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@@ -21,6 -21,8 +21,7 @@@ val geoapiVersion = "3.0.2
   * The sub-projects to include in the build.
   * They are directory names relative to this file.
   */
 -include("geoapi")
+ include("parent")
  include("endorsed")
  include("incubator")
  if (System.getenv("PATH_TO_FX") != null) {
@@@ -56,10 -59,10 +57,10 @@@ dependencyResolutionManagement 
       */
      versionCatalogs {
          create("libs") {
 -            library("geoapi",        "org.opengis",            
"geoapi-pending")      .version {strictly(geoapiVersion)}
 +            library("geoapi",        "org.opengis",            "geoapi")      
        .version {strictly(geoapiVersion)}
              library("units",         "javax.measure",          "unit-api")    
        .version {strictly("[2.1, 3.0[");  prefer("2.1.3")}
              library("jaxb.api",      "jakarta.xml.bind",       
"jakarta.xml.bind-api").version {strictly("[4.0, 5.0[");  prefer("4.0.1")}
-             library("jaxb.impl",     "org.glassfish.jaxb",     
"jaxb-runtime")        .version {strictly("[4.0, 5.0[");  prefer("4.0.3")}
+             library("jaxb.impl",     "org.glassfish.jaxb",     
"jaxb-runtime")        .version {strictly("[4.0, 5.0[");  prefer("4.0.4")}
              library("yasson",        "org.eclipse",            "yasson")      
        .version {strictly("[3.0, 4.0[");  prefer("3.0.3")}
              library("jts.core",      "org.locationtech.jts",   "jts-core")    
        .version {strictly("[1.15, 2.0["); prefer("1.19.0")}
              library("esri.geometry", "com.esri.geometry",      
"esri-geometry-api")   .version {strictly("[2.0, 3.0[");  prefer("2.2.4")}

Reply via email to