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 2023/11/06 11:38:09 UTC

(sis) 01/01: Merge branch 'geoapi-3.1'

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 fafbe8a7b643a4994eb5fe2917721e380d381af2
Merge: ed9308d56c 2bffeac5a6
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Mon Nov 6 12:37:23 2023 +0100

    Merge branch 'geoapi-3.1'

 endorsed/build.gradle.kts                          |   3 +-
 .../org.apache.sis.feature/main/module-info.java   |   1 +
 .../grid/BandAggregateGridCoverageTest.java        |   4 +-
 .../coverage/grid/ConvertedGridCoverageTest.java   |   6 +-
 .../coverage/grid/DimensionalityReductionTest.java |   2 +-
 .../sis/coverage/grid/GridCoverage2DTest.java      |   4 +-
 .../sis/coverage/grid/GridCoverageBuilderTest.java |   2 +-
 .../sis/coverage/grid/GridDerivationTest.java      |   8 +-
 .../apache/sis/coverage/grid/GridExtentTest.java   |   2 +-
 .../apache/sis/coverage/grid/GridGeometryTest.java |   8 +-
 .../coverage/grid/ResampledGridCoverageTest.java   |   8 +-
 .../sis/coverage/grid/ReshapedImageTest.java       |   6 +-
 .../coverage/grid/TranslatedGridCoverageTest.java  |   2 +-
 .../apache/sis/feature/EnvelopeOperationTest.java  |   2 +-
 .../apache/sis/feature/FeatureOperationsTest.java  |   6 +-
 .../feature/builder/FeatureTypeBuilderTest.java    |   8 +-
 .../feature/internal/AttributeConventionTest.java  |   2 +-
 .../sis/filter/BinarySpatialFilterTestCase.java    |   4 +-
 .../apache/sis/filter/sqlmm/RegistryTestCase.java  |   4 +-
 .../org/apache/sis/filter/sqlmm/SQLMMTest.java     |   2 +-
 .../sis/geometry/wrapper/GeometriesTestCase.java   |   4 +-
 .../org/apache/sis/image/ResamplingGridTest.java   |   4 +-
 .../apache/sis/metadata/MetadataCopierTest.java    |   4 +-
 .../apache/sis/metadata/MetadataStandardTest.java  |   6 +-
 .../apache/sis/metadata/PropertyAccessorTest.java  |  14 +-
 .../sis/metadata/PropertyInformationTest.java      |   2 +-
 .../org/apache/sis/metadata/SpecialCasesTest.java  |   6 +-
 .../sis/metadata/sql/IdentifierGeneratorTest.java  |   1 -
 .../sis/metadata/sql/MetadataWriterTest.java       |   4 +-
 .../xml/bind/fra/DirectReferenceSystemTest.java    |   4 +-
 .../apache/sis/geometry/AbstractEnvelopeTest.java  |   6 +-
 .../org/apache/sis/geometry/ArrayEnvelopeTest.java |   4 +-
 .../apache/sis/geometry/CoordinateFormatTest.java  |   6 +-
 .../org/apache/sis/geometry/Envelope2DTest.java    |   2 +-
 .../org/apache/sis/geometry/EnvelopesTest.java     |   2 +-
 .../sis/geometry/GeneralDirectPositionTest.java    |   2 +-
 .../apache/sis/geometry/GeneralEnvelopeTest.java   |   4 +-
 .../apache/sis/geometry/ImmutableEnvelopeTest.java |   2 +-
 .../org/apache/sis/geometry/SubEnvelopeTest.java   |   2 +-
 .../org/apache/sis/geometry/TransformTestCase.java |   8 +-
 .../sis/geometry/WraparoundAdjustmentTest.java     |   4 +-
 .../sis/referencing/AuthorityFactoriesTest.java    |   4 +-
 .../test/org/apache/sis/referencing/CRSTest.java   |   6 +-
 .../sis/referencing/GeodesicsOnEllipsoidTest.java  |   2 +-
 .../sis/referencing/GeodeticCalculatorTest.java    |   2 +-
 .../sis/referencing/IdentifiedObjectsTest.java     |   2 +-
 .../sis/referencing/StandardDefinitionsTest.java   |  10 +-
 .../sis/referencing/crs/AbstractCRSTest.java       |   4 +-
 .../referencing/crs/DefaultCompoundCRSTest.java    |   4 +-
 .../sis/referencing/crs/DefaultDerivedCRSTest.java |   4 +-
 .../referencing/crs/DefaultEngineeringCRSTest.java |   2 +-
 .../sis/referencing/crs/DefaultImageCRSTest.java   |   4 +-
 .../referencing/crs/DefaultProjectedCRSTest.java   |   2 +-
 .../referencing/crs/DefaultTemporalCRSTest.java    |   2 +-
 .../apache/sis/referencing/crs/HardCodedCRS.java   |   4 +-
 .../sis/referencing/crs/HardCodedCRSTest.java      |   2 +-
 .../datum/DefaultTemporalDatumTest.java            |   2 +-
 .../sis/referencing/datum/HardCodedDatum.java      |   4 +-
 .../referencing/factory/AuthorityFactoryMock.java  |   2 +
 .../factory/MultiAuthoritiesFactoryTest.java       |   6 +-
 .../internal/ServicesForMetadataTest.java          |   2 +-
 .../operation/CoordinateOperationFinderTest.java   |   4 +-
 .../DefaultConcatenatedOperationTest.java          |   4 +-
 .../operation/DefaultConversionTest.java           |  12 +-
 .../operation/DefaultTransformationTest.java       |   8 +-
 .../operation/HardCodedConversions.java            |   2 +
 .../builder/LinearTransformBuilderTest.java        |   6 +-
 .../operation/builder/LinearizerTest.java          |   4 +-
 .../operation/provider/GeographicOffsetsTest.java  |   2 +-
 .../transform/AbridgedMolodenskyTransformTest.java |   4 +-
 .../transform/CoordinateSystemTransformTest.java   |   2 +-
 .../transform/DefaultMathTransformFactoryTest.java |   8 +-
 .../InterpolatedGeocentricTransformTest.java       |   2 +-
 .../transform/MathTransformFactoryBase.java        |   2 +
 .../transform/MolodenskyTransformTest.java         |   4 +-
 .../transform/TransformSeparatorTest.java          |   6 +-
 .../transform/WraparoundTransformTest.java         |   2 +-
 .../sis/referencing/util/AxesMapperTest.java       |   2 +-
 .../sis/referencing/util/AxisDirectionsTest.java   |   4 +-
 .../referencing/util/CoordinateOperationsTest.java |  10 +-
 .../referencing/util/DefinitionVerifierTest.java   |  10 +-
 .../util/EllipsoidalHeightCombinerTest.java        |   8 +-
 .../apache/sis/referencing/util/FormulasTest.java  |   2 +-
 .../referencing/util/ReferencingUtilitiesTest.java |   4 +-
 .../sis/referencing/util/WKTUtilitiesTest.java     |   4 +-
 .../referencing/util/WraparoundApplicatorTest.java |   3 +-
 .../main/module-info.java                          |   2 +-
 .../apache/sis/storage/geotiff/Compression.java    | 241 ++++++++
 .../{GeoTiffOption.java => FormatModifier.java}    |  39 +-
 .../apache/sis/storage/geotiff/GeoTiffStore.java   |  66 +-
 .../sis/storage/geotiff/GeoTiffStoreProvider.java  |  16 +-
 .../org/apache/sis/storage/geotiff/IOBase.java     |   6 +-
 .../org/apache/sis/storage/geotiff/Reader.java     |   6 +-
 .../org/apache/sis/storage/geotiff/Writer.java     |  80 ++-
 .../sis/storage/geotiff/base/Compression.java      |  15 +-
 .../apache/sis/storage/geotiff/base/Predictor.java |  41 +-
 .../storage/geotiff/inflater/CopyFromBytes.java    |  91 ++-
 .../geotiff/inflater/HorizontalPredictor.java      |   4 +-
 .../sis/storage/geotiff/inflater/Inflater.java     |   2 +-
 .../storage/geotiff/inflater/PredictorChannel.java |   2 +
 .../storage/geotiff/writer/CompressionChannel.java | 116 ++++
 .../geotiff/writer/HorizontalPredictor.java        | 390 ++++++++++++
 .../sis/storage/geotiff/writer/PixelChannel.java   |  60 ++
 .../storage/geotiff/writer/PredictorChannel.java   |  86 +++
 .../sis/storage/geotiff/writer/TileMatrix.java     | 185 ++++--
 .../org/apache/sis/storage/geotiff/writer/ZIP.java | 120 ++++
 .../org/apache/sis/storage/geotiff/WriterTest.java |  76 +--
 .../storage/sql/feature/GeometryGetterTest.java    |   2 +-
 .../sql/feature/SelectionClauseWriterTest.java     |   2 +-
 .../sql/feature/TemporalValueGetterTest.java       |   4 +-
 .../sis/storage/sql/postgis/PostgresTest.java      |   2 +-
 .../org.apache.sis.storage/main/module-info.java   |   1 +
 .../main/org/apache/sis/io/stream/ChannelData.java |   4 +
 .../apache/sis/io/stream/ChannelDataOutput.java    |  10 +
 .../sis/io/stream/ChannelImageOutputStream.java    |  14 +
 .../apache/sis/io/stream/FileCacheByteChannel.java |   5 +-
 .../apache/sis/io/stream/HyperRectangleWriter.java | 361 ++++++++---
 .../apache/sis/io/stream/RewindableLineReader.java |   2 +-
 .../org/apache/sis/storage/StorageConnector.java   | 141 +++--
 .../main/org/apache/sis/storage/csv/Store.java     |   6 +-
 .../apache/sis/storage/wkt/FirstKeywordPeek.java   |   4 +-
 .../org/apache/sis/storage/CoverageQueryTest.java  |   2 +-
 .../org/apache/sis/storage/CoverageSubsetTest.java |   2 +-
 .../aggregate/BandAggregateGridResourceTest.java   |   2 +-
 .../sis/storage/base/MemoryGridResourceTest.java   |   2 +-
 .../sis/storage/esri/BSQConsistencyTest.java       |   2 +-
 .../apache/sis/storage/esri/WritableStoreTest.java |   2 +-
 .../src/org.apache.sis.util/main/module-info.java  |   1 +
 .../main/org/apache/sis/util/ArgumentChecks.java   |  22 +-
 .../org/apache/sis/util/resources/Vocabulary.java  |   5 +
 .../sis/util/resources/Vocabulary.properties       |   1 +
 .../sis/util/resources/Vocabulary_fr.properties    |   1 +
 .../main/module-info.java                          |   2 +
 .../sis/storage/shapefile/ShapefileProvider.java   |  82 +++
 .../sis/storage/shapefile/ShapefileStore.java      | 385 ++++++++++++
 .../apache/sis/storage/shapefile/dbf/DBFField.java | 120 ++++
 .../sis/storage/shapefile/dbf/DBFFieldEncoder.java | 206 +++++++
 .../sis/storage/shapefile/dbf/DBFHeader.java       |  78 +++
 .../sis/storage/shapefile/dbf/DBFReader.java       |  87 +++
 .../sis/storage/shapefile/dbf/DBFRecord.java}      |  20 +-
 .../shapefile/shp/ShapeGeometryEncoder.java        | 677 +++++++++++++++++++++
 .../sis/storage/shapefile/shp/ShapeHeader.java     | 113 ++++
 .../sis/storage/shapefile/shp/ShapeReader.java     |  66 ++
 .../sis/storage/shapefile/shp/ShapeRecord.java     |  78 +++
 .../sis/storage/shapefile/shp/ShapeType.java       |  89 +++
 .../sis/storage/shapefile/shp/ShapeWriter.java     |  65 ++
 .../sis/storage/shapefile/shx/IndexReader.java     |  66 ++
 .../sis/storage/shapefile/ShapefileStoreTest.java  |  90 +++
 .../sis/storage/shapefile/dbf/DBFIOTest.java       | 104 ++++
 .../apache/sis/storage/shapefile/multipoint.cpg    |   1 +
 .../apache/sis/storage/shapefile/multipoint.dbf    | Bin 0 -> 88 bytes
 .../apache/sis/storage/shapefile/multipoint.prj    |   1 +
 .../apache/sis/storage/shapefile/multipoint.shp    | Bin 0 -> 260 bytes
 .../apache/sis/storage/shapefile/multipoint.shx    | Bin 0 -> 116 bytes
 .../org/apache/sis/storage/shapefile/point.cpg     |   1 +
 .../org/apache/sis/storage/shapefile/point.dbf     | Bin 0 -> 434 bytes
 .../org/apache/sis/storage/shapefile/point.prj     |   1 +
 .../org/apache/sis/storage/shapefile/point.shp     | Bin 0 -> 156 bytes
 .../org/apache/sis/storage/shapefile/point.shx     | Bin 0 -> 116 bytes
 .../org/apache/sis/storage/shapefile/polygon.cpg   |   1 +
 .../org/apache/sis/storage/shapefile/polygon.dbf   | Bin 0 -> 88 bytes
 .../org/apache/sis/storage/shapefile/polygon.prj   |   1 +
 .../org/apache/sis/storage/shapefile/polygon.shp   | Bin 0 -> 456 bytes
 .../org/apache/sis/storage/shapefile/polygon.shx   | Bin 0 -> 116 bytes
 .../org/apache/sis/storage/shapefile/polyline.cpg  |   1 +
 .../org/apache/sis/storage/shapefile/polyline.dbf  | Bin 0 -> 88 bytes
 .../org/apache/sis/storage/shapefile/polyline.prj  |   1 +
 .../org/apache/sis/storage/shapefile/polyline.shp  | Bin 0 -> 328 bytes
 .../org/apache/sis/storage/shapefile/polyline.shx  | Bin 0 -> 116 bytes
 .../sis/storage/shapefile/shp/ShapeIOTest.java     | 319 ++++++++++
 .../apache/sis/gui/coverage/CoverageCanvasApp.java |   4 +-
 .../org/apache/sis/gui/coverage/GridViewApp.java   |   4 +-
 172 files changed, 4715 insertions(+), 556 deletions(-)

diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
index 7495875e05,4b14655648..57d0622432
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/geometry/AbstractEnvelopeTest.java
@@@ -32,10 -31,8 +31,11 @@@ import static org.junit.Assert.*
  import static org.opengis.test.Validators.validate;
  import static org.apache.sis.referencing.Assertions.assertContains;
  import static org.apache.sis.referencing.Assertions.assertDisjoint;
+ import static org.apache.sis.referencing.crs.HardCodedCRS.WGS84;
  
 +// Specific to the main branch:
 +import static org.apache.sis.test.GeoapiAssert.PENDING_NEXT_GEOAPI_RELEASE;
 +
  
  /**
   * Tests the methods defined in the {@link AbstractEnvelope} class.
diff --cc endorsed/src/org.apache.sis.util/main/module-info.java
index 3394402ebf,afebada7be..29dc1fd8dd
--- a/endorsed/src/org.apache.sis.util/main/module-info.java
+++ b/endorsed/src/org.apache.sis.util/main/module-info.java
@@@ -127,6 -127,8 +127,7 @@@ module org.apache.sis.util 
              org.apache.sis.storage.netcdf,
              org.apache.sis.storage.geotiff,
              org.apache.sis.storage.earthobservation,
+             org.apache.sis.storage.shapefile,           // In the "incubator" sub-project.
 -            org.apache.sis.cql,                         // In the "incubator" sub-project.
              org.apache.sis.portrayal,
              org.apache.sis.cloud.aws,
              org.apache.sis.console,
diff --cc incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java
index 0000000000,33c46f120d..04774dfc66
mode 000000,100644..100644
--- 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
@@@ -1,0 -1,385 +1,385 @@@
+ /*
+  * 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.io.IOException;
+ import java.nio.ByteBuffer;
+ import java.nio.channels.SeekableByteChannel;
+ import java.nio.channels.WritableByteChannel;
+ import java.nio.charset.Charset;
+ import java.nio.charset.StandardCharsets;
+ import java.nio.file.Files;
+ import java.nio.file.Path;
+ import java.nio.file.StandardOpenOption;
+ import java.util.Iterator;
+ import java.util.Optional;
+ import java.util.Spliterator;
+ import java.util.Spliterators;
+ import java.util.concurrent.locks.ReadWriteLock;
+ import java.util.concurrent.locks.ReentrantReadWriteLock;
+ import java.util.function.Consumer;
+ import java.util.function.Predicate;
+ import java.util.function.UnaryOperator;
+ import java.util.stream.Stream;
+ import java.util.stream.StreamSupport;
+ import org.opengis.geometry.Envelope;
+ import org.opengis.metadata.Metadata;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.opengis.util.FactoryException;
+ import org.opengis.util.GenericName;
+ import org.apache.sis.feature.builder.AttributeRole;
+ import org.apache.sis.feature.builder.AttributeTypeBuilder;
+ import org.apache.sis.feature.builder.FeatureTypeBuilder;
+ import org.apache.sis.io.stream.ChannelDataInput;
+ import org.apache.sis.io.stream.ChannelDataOutput;
+ import org.apache.sis.io.stream.IOUtilities;
+ import org.apache.sis.parameter.Parameters;
+ import org.apache.sis.referencing.CRS;
+ import org.apache.sis.referencing.CommonCRS;
+ import org.apache.sis.storage.AbstractFeatureSet;
+ import org.apache.sis.storage.DataStore;
+ import org.apache.sis.storage.DataStoreException;
+ import org.apache.sis.storage.FeatureSet;
+ import org.apache.sis.storage.Query;
+ import org.apache.sis.storage.UnsupportedQueryException;
+ import org.apache.sis.storage.WritableFeatureSet;
+ import org.apache.sis.storage.shapefile.cpg.CpgFiles;
+ import org.apache.sis.storage.shapefile.dbf.DBFField;
+ import org.apache.sis.storage.shapefile.dbf.DBFHeader;
+ import org.apache.sis.storage.shapefile.dbf.DBFReader;
+ import org.apache.sis.storage.shapefile.dbf.DBFRecord;
+ import org.apache.sis.storage.shapefile.shp.ShapeGeometryEncoder;
+ 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.util.collection.BackingStoreException;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.feature.Feature;
 -import org.opengis.feature.FeatureType;
++// Specific to the main branch:
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.feature.DefaultFeatureType;
+ 
+ 
+ /**
+  * Shapefile datastore.
+  *
+  * @author Johann Sorel (Geomatys)
+  */
+ public final class ShapefileStore extends DataStore implements FeatureSet {
+ 
+     private static final String GEOMETRY_NAME = "geometry";
+ 
+     private final Path shpPath;
+     private final ShpFiles files;
+     /**
+      * Internal class to inherit AbstractFeatureSet.
+      */
+     private final AsFeatureSet featureSetView = new AsFeatureSet();
 -    private FeatureType type;
++    private DefaultFeatureType type;
+     private Charset charset;
+ 
+     /**
+      * Lock to control read and write operations.
+      */
+     private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ 
+     public ShapefileStore(Path path) {
+         this.shpPath = path;
+         this.files = new ShpFiles(shpPath);
+     }
+ 
+     @Override
+     public Optional<ParameterValueGroup> getOpenParameters() {
+         final Parameters parameters = Parameters.castOrWrap(ShapefileProvider.PARAMETERS_DESCRIPTOR.createValue());
+         parameters.parameter(ShapefileProvider.LOCATION).setValue(shpPath.toUri());
+         return Optional.of(parameters);
+     }
+ 
+     @Override
+     public void close() throws DataStoreException {
+     }
+ 
+ 
+     /*
+     Redirect FeatureSet interface to View
+     */
+     @Override
+     public Optional<GenericName> getIdentifier() throws DataStoreException {
+         return featureSetView.getIdentifier();
+     }
+ 
+     @Override
+     public Metadata getMetadata() throws DataStoreException {
+         return featureSetView.getMetadata();
+     }
+ 
+     @Override
 -    public FeatureType getType() throws DataStoreException {
++    public DefaultFeatureType getType() throws DataStoreException {
+         return featureSetView.getType();
+     }
+ 
+     @Override
+     public FeatureSet subset(Query query) throws UnsupportedQueryException, DataStoreException {
+         return featureSetView.subset(query);
+     }
+ 
+     @Override
 -    public Stream<Feature> features(boolean parallel) throws DataStoreException {
++    public Stream<AbstractFeature> features(boolean parallel) throws DataStoreException {
+         return featureSetView.features(parallel);
+     }
+ 
+     @Override
+     public Optional<Envelope> getEnvelope() throws DataStoreException {
+         return featureSetView.getEnvelope();
+     }
+ 
+     private class AsFeatureSet extends AbstractFeatureSet implements WritableFeatureSet {
+ 
+         private AsFeatureSet() {
+             super(null);
+         }
+ 
+         @Override
 -        public synchronized FeatureType getType() throws DataStoreException {
++        public synchronized DefaultFeatureType getType() throws DataStoreException {
+             if (type == null) {
+                 if (!Files.isRegularFile(shpPath)) {
+                     throw new DataStoreException("Shape files do not exist. Update FeatureType first to initialize this empty datastore");
+                 }
+ 
+                 final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
+                 ftb.setName(files.baseName);
+ 
+                 //read shp header to obtain geometry type
+                 final Class geometryClass;
+                 try (final ShapeReader reader = new ShapeReader(ShpFiles.openReadChannel(shpPath))) {
+                     final ShapeHeader header = reader.getHeader();
+                     geometryClass = ShapeGeometryEncoder.getEncoder(header.shapeType).getValueClass();
+                 } catch (IOException ex) {
+                     throw new DataStoreException("Failed to parse shape file header.", ex);
+                 }
+ 
+                 //read prj file for projection
+                 final Path prjFile = files.getPrj(false);
+                 final CoordinateReferenceSystem crs;
+                 if (prjFile != null) {
+                     try {
+                         crs = CRS.fromWKT(Files.readString(prjFile, StandardCharsets.UTF_8));
+                     } catch (IOException | FactoryException ex) {
+                         throw new DataStoreException("Failed to parse prj file.", ex);
+                     }
+                 } else {
+                     //shapefile often do not have a .prj, mostly those are in CRS:84.
+                     //we do not raise an error otherwise we would not be able to read a lot of data.
+                     crs = CommonCRS.WGS84.normalizedGeographic();
+                 }
+ 
+                 ftb.addAttribute(geometryClass).setName(GEOMETRY_NAME).setCRS(crs).addRole(AttributeRole.DEFAULT_GEOMETRY);
+ 
+                 //read cpg for dbf file charset
+                 final Path cpgFile = files.getCpg(false);
+                 if (cpgFile != null) {
+                     try (final SeekableByteChannel channel = Files.newByteChannel(cpgFile, StandardOpenOption.READ)) {
+                         charset = CpgFiles.read(channel);
+                     } catch (IOException ex) {
+                         throw new DataStoreException("Failed to parse cpg file.", ex);
+                     }
+                 } else {
+                     charset = StandardCharsets.UTF_8;
+                 }
+ 
+                 //read dbf for attributes
+                 final Path dbfFile = files.getDbf(false);
+                 if (dbfFile != null) {
+                     try (DBFReader reader = new DBFReader(ShpFiles.openReadChannel(dbfFile), charset)) {
+                         final DBFHeader header = reader.getHeader();
+                         boolean hasId = false;
+                         for (DBFField field : header.fields) {
+                             final AttributeTypeBuilder atb = ftb.addAttribute(field.getEncoder().getValueClass()).setName(field.fieldName);
+                             //no official but 'id' field is common
+                             if (!hasId && "id".equalsIgnoreCase(field.fieldName) || "identifier".equalsIgnoreCase(field.fieldName)) {
+                                 atb.addRole(AttributeRole.IDENTIFIER_COMPONENT);
+                                 hasId = true;
+                             }
+                         }
+                     } catch (IOException ex) {
+                         throw new DataStoreException("Failed to parse dbf file header.", ex);
+                     }
+                 } else {
+                     throw new DataStoreException("DBF file is missing.");
+                 }
+ 
+                 type = ftb.build();
+             }
+             return type;
+         }
+ 
+         @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 {
+                 shpreader = new ShapeReader(ShpFiles.openReadChannel(files.shpFile));
+                 dbfreader = new DBFReader(ShpFiles.openReadChannel(files.getDbf(false)), charset);
+             } 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 Feature next = type.newInstance();
++                        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]);
+                         }
+                         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() {
+                     try {
+                         shpreader.close();
+                         dbfreader.close();
+                     } catch (IOException ex) {
+                         throw new BackingStoreException(ex.getMessage(), ex);
+                     }
+                 }
+             });
+ 
+         }
+ 
+         @Override
 -        public void updateType(FeatureType newType) throws DataStoreException {
++        public void updateType(DefaultFeatureType newType) throws DataStoreException {
+             throw new UnsupportedOperationException("Not supported yet.");
+         }
+ 
+         @Override
 -        public void add(Iterator<? extends Feature> features) throws DataStoreException {
++        public void add(Iterator<? extends AbstractFeature> features) throws DataStoreException {
+             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 {
+             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 {
+             throw new UnsupportedOperationException("Not supported yet.");
+         }
+     }
+ 
+     /**
+      * Manipulate the different shape files.
+      */
+     private static class ShpFiles {
+ 
+         private final String baseName;
+         private final boolean baseUpper;
+         private final Path shpFile;
+         private Path shxFile;
+         private Path dbfFile;
+         private Path prjFile;
+         private Path cpgFile;
+ 
+         public ShpFiles(Path shpFile) {
+             this.shpFile = shpFile;
+             final String fileName = shpFile.getFileName().toString();
+             baseUpper = Character.isUpperCase(fileName.codePointAt(fileName.length()-1));
+             this.baseName = IOUtilities.filenameWithoutExtension(fileName);
+             shxFile = findSibling("shx");
+             dbfFile = findSibling("dbf");
+             prjFile = findSibling("prj");
+             cpgFile = findSibling("cpg");
+         }
+ 
+         /**
+          * @param create true to create the path even if file do not exist.
+          * @return file if it exist or create is true, null otherwise
+          */
+         public Path getShx(boolean create) {
+             if (create && shxFile == null) {
+                 return shpFile.getParent().resolve(baseName + "." + (baseUpper ? "SHX" : "shx"));
+             }
+             return shxFile;
+         }
+ 
+         /**
+          * @param create true to create the path even if file do not exist.
+          * @return file if it exist or create is true, null otherwise
+          */
+         public Path getDbf(boolean create) {
+             if (create && dbfFile == null) {
+                 return shpFile.getParent().resolve(baseName + "." + (baseUpper ? "DBF" : "dbf"));
+             }
+             return dbfFile;
+         }
+ 
+         /**
+          * @param create true to create the path even if file do not exist.
+          * @return file if it exist or create is true, null otherwise
+          */
+         public Path getPrj(boolean create) {
+             if (create && prjFile == null) {
+                 return shpFile.getParent().resolve(baseName + "." + (baseUpper ? "PRJ" : "prj"));
+             }
+             return prjFile;
+         }
+ 
+         /**
+          * @param create true to create the path even if file do not exist.
+          * @return file if it exist or create is true, null otherwise
+          */
+         public Path getCpg(boolean create) {
+             if (create && cpgFile == null) {
+                 return shpFile.getParent().resolve(baseName + "." + (baseUpper ? "CPG" : "cpg"));
+             }
+             return cpgFile;
+         }
+ 
+         private Path findSibling(String extension) {
+             Path candidate = shpFile.getParent().resolve(baseName + "." + extension);
+             if (java.nio.file.Files.isRegularFile(candidate)) return candidate;
+             candidate = shpFile.getParent().resolve(baseName + "." + extension.toUpperCase());
+             if (java.nio.file.Files.isRegularFile(candidate)) return candidate;
+             return null;
+         }
+ 
+         private static ChannelDataInput openReadChannel(Path path) throws IOException {
+             final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ);
+             return new ChannelDataInput(path.getFileName().toString(), channel, ByteBuffer.allocate(8192), false);
+         }
+ 
+         private static ChannelDataOutput openWriteChannel(Path path) throws IOException, IllegalArgumentException, DataStoreException {
+             final WritableByteChannel wbc = Files.newByteChannel(path, StandardOpenOption.WRITE);
+             return new ChannelDataOutput(path.getFileName().toString(), wbc, ByteBuffer.allocate(8000));
+         }
+     }
+ 
+ }
diff --cc incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java
index 0000000000,0cdb00b890..9eb5e07bea
mode 000000,100644..100644
--- 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
@@@ -1,0 -1,90 +1,90 @@@
+ /*
+  * 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.net.URISyntaxException;
+ import java.net.URL;
+ import java.nio.file.Paths;
+ import java.time.LocalDate;
+ import java.util.Iterator;
+ import java.util.stream.Stream;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.storage.DataStoreException;
+ import org.junit.Test;
+ import org.locationtech.jts.geom.Point;
+ 
 -// Specific to the geoapi-3.1 and geoapi-4.0 branches:
 -import org.opengis.feature.AttributeType;
 -import org.opengis.feature.Feature;
 -import org.opengis.feature.FeatureType;
++// Specific to the main branch:
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.feature.DefaultFeatureType;
++import org.apache.sis.feature.DefaultAttributeType;
+ 
+ 
+ /**
+  *
+  * @author Johann Sorel (Geomatys)
+  */
+ public class ShapefileStoreTest {
+ 
+     @Test
+     public void testStream() 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()));
+ 
+         //check feature type
 -        final FeatureType type = store.getType();
++        final DefaultFeatureType type = store.getType();
+         assertEquals("point", type.getName().toString());
+         assertEquals(9, type.getProperties(true).size());
+         assertNotNull(type.getProperty("sis:identifier"));
+         assertNotNull(type.getProperty("sis:envelope"));
+         assertNotNull(type.getProperty("sis:geometry"));
 -        final var geomProp    = (AttributeType) type.getProperty("geometry");
 -        final var idProp      = (AttributeType) type.getProperty("id");
 -        final var textProp    = (AttributeType) type.getProperty("text");
 -        final var integerProp = (AttributeType) type.getProperty("integer");
 -        final var floatProp   = (AttributeType) type.getProperty("float");
 -        final var dateProp    = (AttributeType) type.getProperty("date");
++        final var geomProp    = (DefaultAttributeType) type.getProperty("geometry");
++        final var idProp      = (DefaultAttributeType) type.getProperty("id");
++        final var textProp    = (DefaultAttributeType) type.getProperty("text");
++        final var integerProp = (DefaultAttributeType) type.getProperty("integer");
++        final var floatProp   = (DefaultAttributeType) type.getProperty("float");
++        final var dateProp    = (DefaultAttributeType) type.getProperty("date");
+         assertEquals(Point.class, geomProp.getValueClass());
+         assertEquals(Long.class, idProp.getValueClass());
+         assertEquals(String.class, textProp.getValueClass());
+         assertEquals(Long.class, integerProp.getValueClass());
+         assertEquals(Double.class, floatProp.getValueClass());
+         assertEquals(LocalDate.class, dateProp.getValueClass());
+ 
 -        try (Stream<Feature> stream = store.features(false)) {
 -            Iterator<Feature> iterator = stream.iterator();
++        try (Stream<AbstractFeature> stream = store.features(false)) {
++            Iterator<AbstractFeature> iterator = stream.iterator();
+             assertTrue(iterator.hasNext());
 -            Feature feature1 = iterator.next();
++            AbstractFeature feature1 = iterator.next();
+             assertEquals(1L, feature1.getPropertyValue("id"));
+             assertEquals("text1", feature1.getPropertyValue("text"));
+             assertEquals(10L, feature1.getPropertyValue("integer"));
+             assertEquals(20.0, feature1.getPropertyValue("float"));
+             assertEquals(LocalDate.of(2023, 10, 27), feature1.getPropertyValue("date"));
+             Point pt1 = (Point) feature1.getPropertyValue("geometry");
+ 
+             assertTrue(iterator.hasNext());
 -            Feature feature2 = iterator.next();
++            AbstractFeature feature2 = iterator.next();
+             assertEquals(2L, feature2.getPropertyValue("id"));
+             assertEquals("text2", feature2.getPropertyValue("text"));
+             assertEquals(40L, feature2.getPropertyValue("integer"));
+             assertEquals(60.0, feature2.getPropertyValue("float"));
+             assertEquals(LocalDate.of(2023, 10, 28), feature2.getPropertyValue("date"));
+             Point pt2 = (Point) feature2.getPropertyValue("geometry");
+ 
+             assertFalse(iterator.hasNext());
+         }
+     }
+ }