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

jsorel pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 4c9052a8f1 feat(Shapefile): implement featureset bounding box and 
count methods, several small fix in reader iterator
4c9052a8f1 is described below

commit 4c9052a8f111b1d811803798d2083eb75edf898c
Author: jsorel <johann.so...@geomatys.com>
AuthorDate: Tue Nov 14 17:14:00 2023 +0100

    feat(Shapefile): implement featureset bounding box and count methods, 
several small fix in reader iterator
---
 .../sis/storage/shapefile/ShapefileProvider.java   |  4 +-
 .../sis/storage/shapefile/ShapefileStore.java      | 56 +++++++++++++++++++---
 .../sis/storage/shapefile/shp/ShapeHeader.java     |  7 ++-
 .../sis/storage/shapefile/shp/ShapeReader.java     | 11 ++++-
 4 files changed, 65 insertions(+), 13 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
index 1420cef0e8..4a77845225 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileProvider.java
@@ -36,7 +36,7 @@ import org.apache.sis.storage.StorageConnector;
  */
 public final class ShapefileProvider extends DataStoreProvider {
 
-    public static final String NAME = "Shapefile";
+    public static final String NAME = "esri shapefile";
 
     public static final String MIME_TYPE = "application/x-shapefile";
 
@@ -49,7 +49,7 @@ public final class ShapefileProvider extends 
DataStoreProvider {
             .create(URI.class, null);
 
     public static final ParameterDescriptorGroup PARAMETERS_DESCRIPTOR =
-            new 
ParameterBuilder().addName(NAME).addName("ShapefileParameters").createGroup(
+            new 
ParameterBuilder().addName(NAME).addName("EsriShapefileParameters").createGroup(
                 PATH);
 
     public ShapefileProvider() {
diff --git 
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
index 52dafdd7de..37c24a9615 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
@@ -28,11 +28,13 @@ import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
 import java.util.AbstractMap;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Optional;
+import java.util.OptionalLong;
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
@@ -61,6 +63,7 @@ import org.apache.sis.filter.DefaultFilterFactory;
 import org.apache.sis.filter.Optimization;
 import org.apache.sis.filter.internal.FunctionNames;
 import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.io.stream.ChannelDataInput;
 import org.apache.sis.io.stream.ChannelDataOutput;
 import org.apache.sis.io.stream.IOUtilities;
@@ -176,8 +179,14 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
 
         private final Rectangle2D.Double filter;
         private final Set<String> dbfProperties;
-        private int[] dbfPropertiesIndex;
         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.
          */
@@ -211,6 +220,7 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                     final Class geometryClass;
                     try (final ShapeReader reader = new 
ShapeReader(ShpFiles.openReadChannel(shpPath), filter)) {
                         final ShapeHeader header = reader.getHeader();
+                        this.shpHeader = header;
                         geometryClass = 
ShapeGeometryEncoder.getEncoder(header.shapeType).getValueClass();
                     } catch (IOException ex) {
                         throw new DataStoreException("Failed to parse shape 
file header.", ex);
@@ -250,6 +260,7 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                 if (dbfFile != null) {
                     try (DBFReader reader = new 
DBFReader(ShpFiles.openReadChannel(dbfFile), charset, null)) {
                         final DBFHeader header = reader.getHeader();
+                        this.dbfHeader = header;
                         boolean hasId = false;
 
                         if (dbfProperties == null) {
@@ -258,7 +269,8 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                             dbfPropertiesIndex = new int[dbfProperties.size()];
                         }
 
-                        for (int i = 0,idx=0; i < header.fields.length; i++) {
+                        int idx=0;
+                        for (int i = 0; i < header.fields.length; i++) {
                             final DBFField field = header.fields[i];
                             if (dbfProperties != null && 
!dbfProperties.contains(field.fieldName)) {
                                 //skip unwanted fields
@@ -275,6 +287,9 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                                 hasId = true;
                             }
                         }
+                        //the properties collection may have contain other 
names, for links or geometry, trim those
+                        dbfPropertiesIndex = Arrays.copyOf(dbfPropertiesIndex, 
idx);
+
                     } catch (IOException ex) {
                         throw new DataStoreException("Failed to parse dbf file 
header.", ex);
                     }
@@ -287,6 +302,31 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
             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();
@@ -442,7 +482,7 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                     }
                 }
 
-                FeatureSet fs = new AsFeatureSet(area, readShp, properties);
+                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;
@@ -471,13 +511,15 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
                     subQuery.setProjection(projection);
                 }
 
-                return needSubProcessing ? fs.subset(subQuery) : fs;
+                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 {
@@ -612,7 +654,7 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
             boolean rebuildAnd = false;
             List<Filter<?>> lst = (List<Filter<?>>) 
((LogicalOperator<?>)filter).getOperands();
             Envelope bbox = null;
-            for (int i = 0, n = lst.size(); i < n; i++) {
+            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();
@@ -644,6 +686,8 @@ public final class ShapefileStore extends DataStore 
implements FeatureSet {
             if (rebuildAnd) {
                 if (lst.isEmpty()) {
                     filter = null;
+                } else if (lst.size() == 1) {
+                    filter = lst.get(0);
                 } else {
                     filter = DefaultFilterFactory.forFeatures().and((List)lst);
                 }
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
index b0413d7150..1f61d614b4 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeHeader.java
@@ -26,7 +26,6 @@ import java.nio.ByteOrder;
 
 /**
  * Shapefile header.
- *
  * @author Johann Sorel (Geomatys)
  * @see <a 
href="http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf";>ESRI 
Shapefile Specification</a>
  */
@@ -42,7 +41,7 @@ public final class ShapeHeader {
     public static final int SIGNATURE = 9994;
 
     /**
-     * File size.
+     * File size, in bytes.
      */
     public int fileLength;
     /**
@@ -69,7 +68,7 @@ public final class ShapeHeader {
         }
         //skip unused datas
         channel.skipBytes(5*4);
-        fileLength = channel.readInt();
+        fileLength = channel.readInt() * 2; //in 16bits words
 
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         final int version = channel.readInt();
@@ -95,7 +94,7 @@ public final class ShapeHeader {
         channel.buffer.order(ByteOrder.BIG_ENDIAN);
         channel.writeInt(SIGNATURE);
         channel.write(new byte[5*4]);
-        channel.writeInt(fileLength);
+        channel.writeInt(fileLength/2);
         channel.buffer.order(ByteOrder.LITTLE_ENDIAN);
         channel.writeInt(1000);
         channel.writeInt(shapeType);
diff --git 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
index 9e4b325112..5d713d62bd 100644
--- 
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
+++ 
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeReader.java
@@ -21,6 +21,7 @@ import org.apache.sis.io.stream.ChannelDataInput;
 
 import java.io.EOFException;
 import java.io.IOException;
+import java.util.Objects;
 import org.apache.sis.geometry.Envelope2D;
 
 /**
@@ -36,6 +37,7 @@ public final class ShapeReader implements AutoCloseable{
     private final Rectangle2D.Double filter;
 
     public ShapeReader(ChannelDataInput channel, Rectangle2D.Double filter) 
throws IOException {
+        Objects.nonNull(channel);
         this.channel = channel;
         this.filter = filter;
         header = new ShapeHeader();
@@ -55,7 +57,14 @@ public final class ShapeReader implements AutoCloseable{
         final ShapeRecord record = new ShapeRecord();
         try {
             //read until we find a record matching the filter or EOF exception
-            while (!record.read(channel, geomParser, filter)) {}
+            //we do not trust EOF exception, some channel implementations with 
buffers may continue to say they have datas
+            //but they are picking in an obsolete buffer.
+            for (;;) {
+                if (header.fileLength <= channel.getStreamPosition()) {
+                    return null;
+                }
+                if (record.read(channel, geomParser, filter)) break;
+            }
             return record;
         } catch (EOFException ex) {
             //no more records

Reply via email to