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();
     }

Reply via email to