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 e818303189 feat(Shapefile): update shapefile header on close, some
work on store writing support
e818303189 is described below
commit e818303189ecd21a5e271cdcca5664bf510fb362
Author: jsorel <[email protected]>
AuthorDate: Thu Nov 23 08:57:02 2023 +0100
feat(Shapefile): update shapefile header on close, some work on store
writing support
---
.../sis/storage/shapefile/ShapefileStore.java | 96 +++++++++++++++++++++-
.../sis/storage/shapefile/shp/ShapeHeader.java | 11 ++-
.../sis/storage/shapefile/shp/ShapeWriter.java | 51 ++++++++++--
.../{shp/ShapeWriter.java => shx/IndexWriter.java} | 39 +++++----
4 files changed, 173 insertions(+), 24 deletions(-)
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 eed006d9bd..ec68d29f9d 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
@@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
+import java.time.LocalDate;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
@@ -47,6 +48,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import org.apache.sis.feature.Features;
import org.apache.sis.geometry.wrapper.Geometries;
import org.opengis.geometry.Envelope;
@@ -83,15 +85,28 @@ 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.dbf.DBFWriter;
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.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;
+import org.opengis.feature.AttributeType;
// 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.filter.Expression;
import org.opengis.filter.Filter;
import org.opengis.filter.Literal;
@@ -111,6 +126,7 @@ import org.opengis.util.CodeList;
public final class ShapefileStore extends DataStore implements
WritableFeatureSet {
private static final String GEOMETRY_NAME = "geometry";
+ private static final Logger LOGGER =
Logger.getLogger("org.apache.sis.storage.shapefile");
private final Path shpPath;
private final ShpFiles files;
@@ -550,11 +566,89 @@ public final class ShapefileStore extends DataStore
implements WritableFeatureSe
@Override
public void updateType(FeatureType newType) throws DataStoreException {
+ 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.");
}
- throw new UnsupportedOperationException("Not supported yet.");
+
+ 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;
+ 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
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 1f61d614b4..f81a55dc7c 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
@@ -52,8 +52,17 @@ public final class ShapeHeader {
* Shapefile bounding box without CRS.
* Ordinates are in X,Y,Z,M order.
*/
- public Envelope bbox;
+ public ImmutableEnvelope bbox;
+ public ShapeHeader() {
+
+ }
+
+ public ShapeHeader(ShapeHeader toCopy) {
+ this.fileLength = toCopy.fileLength;
+ this.shapeType = toCopy.shapeType;
+ this.bbox = toCopy.bbox;
+ }
/**
* Read shapefile header.
* @param channel input channel, not null
diff --git
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
index 54e3953a7d..f66e489970 100644
---
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
+++
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
@@ -19,7 +19,8 @@ package org.apache.sis.storage.shapefile.shp;
import org.apache.sis.io.stream.ChannelDataOutput;
import java.io.IOException;
-import java.nio.ByteOrder;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.ImmutableEnvelope;
/**
* Shape file writer.
@@ -30,19 +31,38 @@ public final class ShapeWriter implements AutoCloseable{
private final ChannelDataOutput channel;
+ private ShapeHeader header;
+ private GeneralEnvelope bbox;
private ShapeGeometryEncoder io;
public ShapeWriter(ChannelDataOutput channel) throws IOException {
this.channel = channel;
}
+ public ShapeHeader getHeader() {
+ return header;
+ }
+
+ /**
+ * Header will be copied and modified.
+ * Use getHeader to obtain the new header.
+ */
public void write(ShapeHeader header) throws IOException {
+ this.header = new ShapeHeader(header);
+ this.header.fileLength = 0;
+ this.header.bbox = new ImmutableEnvelope(new GeneralEnvelope(4));
header.write(channel);
io = ShapeGeometryEncoder.getEncoder(header.shapeType);
}
public void write(ShapeRecord record) throws IOException {
record.write(channel, io);
+ if (bbox == null) {
+ bbox = new GeneralEnvelope(record.bbox.getDimension());
+ bbox.setEnvelope(record.bbox);
+ } else {
+ bbox.add(record.bbox);
+ }
}
public void flush() throws IOException {
@@ -51,13 +71,30 @@ public final class ShapeWriter implements AutoCloseable{
@Override
public void close() throws IOException {
- channel.flush();
+ flush();
+
+ //update header and rewrite it
+ //update the file length
+ header.fileLength = (int) channel.getStreamPosition();
- //update the file length in the header
- final long fileLength = channel.getStreamPosition();
- channel.seek(24);
- channel.buffer.order(ByteOrder.BIG_ENDIAN);
- channel.writeInt((int) fileLength);
+ //update bbox, size must be 4
+ if (bbox != null) {
+ if (bbox.getDimension() == 4) {
+ header.bbox = new ImmutableEnvelope(bbox);
+ } else {
+ final GeneralEnvelope e = new GeneralEnvelope(4);
+ for (int i = 0, n = bbox.getDimension(); i < n; i++) {
+ e.setRange(i, bbox.getMinimum(i), bbox.getMaximum(i));
+ }
+ header.bbox = new ImmutableEnvelope(e);
+ }
+ } else {
+ header.bbox = new ImmutableEnvelope(new GeneralEnvelope(4));
+ }
+
+ channel.seek(0);
+ header.write(channel);
+ channel.flush();
channel.channel.close();
}
diff --git
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
similarity index 61%
copy from
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
copy to
incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
index 54e3953a7d..8a3528fa49 100644
---
a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shp/ShapeWriter.java
+++
b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/shx/IndexWriter.java
@@ -14,35 +14,43 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.storage.shapefile.shp;
+package org.apache.sis.storage.shapefile.shx;
+import org.apache.sis.storage.shapefile.shp.ShapeHeader;
import org.apache.sis.io.stream.ChannelDataOutput;
-
import java.io.IOException;
-import java.nio.ByteOrder;
/**
* Shape file writer.
*
* @author Johann Sorel (Geomatys)
*/
-public final class ShapeWriter implements AutoCloseable{
+public final class IndexWriter implements AutoCloseable{
private final ChannelDataOutput channel;
- private ShapeGeometryEncoder io;
+ private ShapeHeader header;
- public ShapeWriter(ChannelDataOutput channel) throws IOException {
+ public IndexWriter(ChannelDataOutput channel) throws IOException {
this.channel = channel;
}
+ public ShapeHeader getHeader() {
+ return header;
+ }
+ /**
+ * Header will be copied and modified.
+ * Use getHeader to obtain the new header.
+ */
public void write(ShapeHeader header) throws IOException {
+ this.header = new ShapeHeader(header);
+ this.header.fileLength = 0;
header.write(channel);
- io = ShapeGeometryEncoder.getEncoder(header.shapeType);
}
- public void write(ShapeRecord record) throws IOException {
- record.write(channel, io);
+ public void write(int offset, int length) throws IOException {
+ channel.writeInt(offset);
+ channel.writeInt(length);
}
public void flush() throws IOException {
@@ -51,13 +59,14 @@ public final class ShapeWriter implements AutoCloseable{
@Override
public void close() throws IOException {
- channel.flush();
+ flush();
- //update the file length in the header
- final long fileLength = channel.getStreamPosition();
- channel.seek(24);
- channel.buffer.order(ByteOrder.BIG_ENDIAN);
- channel.writeInt((int) fileLength);
+ //update header and rewrite it
+ //update the file length
+ header.fileLength = (int) channel.getStreamPosition();
+ channel.seek(0);
+ header.write(channel);
+ channel.flush();
channel.channel.close();
}