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 <[email protected]>
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