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

bchapuis pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git


The following commit(s) were added to refs/heads/main by this push:
     new c3791173 Improve the transformation workflow (#620)
c3791173 is described below

commit c37911738bc14c30986e00d68a9d1edefc19666f
Author: Bertil Chapuis <[email protected]>
AuthorDate: Sun Apr 9 21:46:33 2023 +0200

    Improve the transformation workflow (#620)
    
    * Improve support for geometries in collections
    
    * Fix index out of bounds exception
    
    * Improve flatgeobuf serialization
    
    * Import empty geometry instead of null
---
 .../{GeometryDataType.java => WKBDataType.java}    |   5 +-
 .../type/geometry/CoordinateArrayDataType.java     |  73 ++++++++
 .../type/geometry/GeometryCollectionDataType.java  | 110 ++++++++++++
 .../collection/type/geometry/GeometryDataType.java | 197 +++++++++++++++++++++
 .../type/geometry/LineStringDataType.java          |  78 ++++++++
 .../type/geometry/MultiLineStringDataType.java     |  97 ++++++++++
 .../type/geometry/MultiPointDataType.java          |  77 ++++++++
 .../type/geometry/MultiPolygonDataType.java        |  95 ++++++++++
 .../collection/type/geometry/PointDataType.java    |  87 +++++++++
 .../collection/type/geometry/PolygonDataType.java  | 124 +++++++++++++
 .../function/RelationGeometryBuilder.java          |   1 +
 .../openstreetmap/function/WayGeometryBuilder.java |   1 +
 .../main/java/org/apache/baremaps/storage/Row.java |   2 +-
 .../storage/flatgeobuf/FlatGeoBufStore.java        |   8 +-
 .../storage/flatgeobuf/FlatGeoBufTable.java        |  53 +++---
 .../flatgeobuf/internal/TableConversions.java      |   4 +-
 .../baremaps/workflow/tasks/EntityDataType.java    |   4 +-
 .../baremaps/collection/AppendOnlyBufferTest.java  |   4 +-
 .../baremaps/collection/type/DataTypeProvider.java | 145 +++++++++++++--
 .../workflow.json                                  |   6 +-
 20 files changed, 1122 insertions(+), 49 deletions(-)

diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/GeometryDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/WKBDataType.java
similarity index 91%
rename from 
baremaps-core/src/main/java/org/apache/baremaps/collection/type/GeometryDataType.java
rename to 
baremaps-core/src/main/java/org/apache/baremaps/collection/type/WKBDataType.java
index 395a74cf..4bf89729 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/GeometryDataType.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/WKBDataType.java
@@ -19,12 +19,13 @@ import 
org.apache.baremaps.openstreetmap.utils.GeometryUtils;
 import org.locationtech.jts.geom.Geometry;
 
 /** A {@link DataType} for reading and writing {@link Geometry} in {@link 
ByteBuffer}s. */
-public class GeometryDataType implements DataType<Geometry> {
+public class WKBDataType implements DataType<Geometry> {
 
   /** {@inheritDoc} */
   @Override
   public int size(Geometry value) {
-    return Integer.BYTES + GeometryUtils.serialize(value).length;
+    byte[] bytes = GeometryUtils.serialize(value);
+    return Integer.BYTES + bytes.length;
   }
 
   /** {@inheritDoc} */
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/CoordinateArrayDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/CoordinateArrayDataType.java
new file mode 100644
index 00000000..1bc483c8
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/CoordinateArrayDataType.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.Coordinate;
+
+/**
+ * A data type for {@link Coordinate} arrays.
+ */
+public class CoordinateArrayDataType implements DataType<Coordinate[]> {
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(Coordinate[] value) {
+    return Integer.BYTES + Double.BYTES * 2 * value.length;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return buffer.getInt(position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, Coordinate[] value) {
+    buffer.putInt(position, size(value));
+    position += Integer.BYTES;
+    for (int i = 0; i < value.length; i++) {
+      Coordinate coordinate = value[i];
+      buffer.putDouble(position, coordinate.x);
+      position += Double.BYTES;
+      buffer.putDouble(position, coordinate.y);
+      position += Double.BYTES;
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Coordinate[] read(ByteBuffer buffer, int position) {
+    int size = buffer.getInt(position);
+    int numPoints = (size - Integer.BYTES) / (Double.BYTES * 2);
+    position += Integer.BYTES;
+    Coordinate[] coordinates = new Coordinate[numPoints];
+    for (int i = 0; i < numPoints; i++) {
+      double x = buffer.getDouble(position);
+      double y = buffer.getDouble(position + Double.BYTES);
+      coordinates[i] = new Coordinate(x, y);
+      position += Double.BYTES * 2;
+    }
+    return coordinates;
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryCollectionDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryCollectionDataType.java
new file mode 100644
index 00000000..256c75e7
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryCollectionDataType.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryCollection;
+import org.locationtech.jts.geom.GeometryFactory;
+
+/**
+ * A data type for {@link GeometryCollection} objects.
+ */
+public class GeometryCollectionDataType implements 
DataType<GeometryCollection> {
+
+  private final GeometryFactory geometryFactory;
+
+  private GeometryDataType geometryDataType;
+
+  /**
+   * Constructs a {@code GeometryCollectionDataType} with a default {@code 
GeometryFactory}.
+   */
+  public GeometryCollectionDataType() {
+    this(new GeometryFactory(), new GeometryDataType());
+  }
+
+  /**
+   * Constructs a {@code GeometryCollectionDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public GeometryCollectionDataType(GeometryFactory geometryFactory) {
+    this(geometryFactory, new GeometryDataType());
+  }
+
+  /**
+   * Constructs a {@code GeometryCollectionDataType} with a specified {@code 
GeometryFactory} and
+   * {@code GeometryDataType}.
+   *
+   * @param geometryFactory the geometry factory
+   * @param geometryDataType the geometry data type
+   */
+  public GeometryCollectionDataType(GeometryFactory geometryFactory,
+      GeometryDataType geometryDataType) {
+    this.geometryFactory = geometryFactory;
+    this.geometryDataType = geometryDataType;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(GeometryCollection value) {
+    int size = Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      size += geometryDataType.size(value.getGeometryN(i));
+    }
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return buffer.getInt(position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, GeometryCollection value) 
{
+    buffer.putInt(position, size(value));
+    position += Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      var geometry = value.getGeometryN(i);
+      geometryDataType.write(buffer, position, geometry);
+      position += geometryDataType.size(buffer, position);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public GeometryCollection read(ByteBuffer buffer, int position) {
+    var size = size(buffer, position);
+    var limit = position + size;
+    position += Integer.BYTES;
+    var geometries = new ArrayList<Geometry>();
+    while (position < limit) {
+      var geometry = geometryDataType.read(buffer, position);
+      geometries.add(geometry);
+      position += geometryDataType.size(geometry);
+    }
+    return 
geometryFactory.createGeometryCollection(geometries.toArray(Geometry[]::new));
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryDataType.java
new file mode 100644
index 00000000..376e82de
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryDataType.java
@@ -0,0 +1,197 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+
+import java.nio.ByteBuffer;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.*;
+
+/**
+ * A {@code DataType} for {@code MultiPolygon} objects.
+ */
+public class GeometryDataType implements DataType<Geometry> {
+
+  private final PointDataType pointDataType;
+
+  private final LineStringDataType lineStringDataType;
+
+  private final PolygonDataType polygonDataType;
+
+  private final MultiPointDataType multiPointDataType;
+
+  private final MultiLineStringDataType multiLineStringDataType;
+
+  private final MultiPolygonDataType multiPolygonDataType;
+
+  private final GeometryCollectionDataType geometryCollectionDataType;
+
+  /**
+   * Constructs a {@code GeometryDataType} with a default {@code 
GeometryFactory}.
+   */
+  public GeometryDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code GeometryDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory
+   */
+  public GeometryDataType(GeometryFactory geometryFactory) {
+    this.pointDataType = new PointDataType(geometryFactory);
+    this.lineStringDataType = new LineStringDataType(geometryFactory);
+    this.polygonDataType = new PolygonDataType(geometryFactory);
+    this.multiPointDataType = new MultiPointDataType(geometryFactory);
+    this.multiLineStringDataType = new 
MultiLineStringDataType(geometryFactory);
+    this.multiPolygonDataType = new MultiPolygonDataType(geometryFactory);
+    this.geometryCollectionDataType = new 
GeometryCollectionDataType(geometryFactory, this);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(Geometry value) {
+    var size = 0;
+
+    // Geometry type
+    size += Byte.BYTES;
+
+    // Size of the geometry
+    if (value instanceof Point point) {
+      size += pointDataType.size(point);
+    } else if (value instanceof LineString lineString) {
+      size += lineStringDataType.size(lineString);
+    } else if (value instanceof Polygon polygon) {
+      size += polygonDataType.size(polygon);
+    } else if (value instanceof MultiPoint multiPoint) {
+      size += multiPointDataType.size(multiPoint);
+    } else if (value instanceof MultiLineString multiLineString) {
+      size += multiLineStringDataType.size(multiLineString);
+    } else if (value instanceof MultiPolygon multiPolygon) {
+      size += multiPolygonDataType.size(multiPolygon);
+    } else if (value instanceof GeometryCollection geometryCollection) {
+      size += geometryCollectionDataType.size(geometryCollection);
+    } else {
+      throw new IllegalArgumentException("Unsupported geometry type: " + 
value.getClass());
+    }
+
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    var size = 0;
+
+    // Geometry type
+    var type = buffer.get(position);
+    size += Byte.BYTES;
+
+    // Size of the geometry
+    if (type == 1) {
+      size += pointDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 2) {
+      size += lineStringDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 3) {
+      size += polygonDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 4) {
+      size += multiPointDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 5) {
+      size += multiLineStringDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 6) {
+      size += multiPolygonDataType.size(buffer, position + Byte.BYTES);
+    } else if (type == 7) {
+      size += geometryCollectionDataType.size(buffer, position + Byte.BYTES);
+    } else {
+      throw new IllegalArgumentException("Unsupported geometry type: " + type);
+    }
+
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, Geometry value) {
+    // Write the geometry
+    if (value == null) {
+      buffer.put(position, (byte) 0);
+    } else if (value instanceof Point point) {
+      buffer.put(position, (byte) 1);
+      position += Byte.BYTES;
+      pointDataType.write(buffer, position, point);
+    } else if (value instanceof LineString lineString) {
+      buffer.put(position, (byte) 2);
+      position += Byte.BYTES;
+      lineStringDataType.write(buffer, position, lineString);
+    } else if (value instanceof Polygon polygon) {
+      buffer.put(position, (byte) 3);
+      position += Byte.BYTES;
+      polygonDataType.write(buffer, position, polygon);
+    } else if (value instanceof MultiPoint multiPoint) {
+      buffer.put(position, (byte) 4);
+      position += Byte.BYTES;
+      multiPointDataType.write(buffer, position, multiPoint);
+    } else if (value instanceof MultiLineString multiLineString) {
+      buffer.put(position, (byte) 5);
+      position += Byte.BYTES;
+      multiLineStringDataType.write(buffer, position, multiLineString);
+    } else if (value instanceof MultiPolygon multiPolygon) {
+      buffer.put(position, (byte) 6);
+      position += Byte.BYTES;
+      multiPolygonDataType.write(buffer, position, multiPolygon);
+    } else if (value instanceof GeometryCollection geometryCollection) {
+      buffer.put(position, (byte) 7);
+      position += Byte.BYTES;
+      geometryCollectionDataType.write(buffer, position, geometryCollection);
+    } else {
+      throw new IllegalArgumentException("Unsupported geometry type: " + 
value.getClass());
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Geometry read(ByteBuffer buffer, int position) {
+    // Read the geometry type
+    var type = buffer.get(position);
+    position += Byte.BYTES;
+
+    // Read the geometry
+    if (type == 0) {
+      return null;
+    } else if (type == 1) {
+      return pointDataType.read(buffer, position);
+    } else if (type == 2) {
+      return lineStringDataType.read(buffer, position);
+    } else if (type == 3) {
+      return polygonDataType.read(buffer, position);
+    } else if (type == 4) {
+      return multiPointDataType.read(buffer, position);
+    } else if (type == 5) {
+      return multiLineStringDataType.read(buffer, position);
+    } else if (type == 6) {
+      return multiPolygonDataType.read(buffer, position);
+    } else if (type == 7) {
+      return geometryCollectionDataType.read(buffer, position);
+    } else {
+      throw new IllegalArgumentException("Unsupported geometry type: " + type);
+    }
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/LineStringDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/LineStringDataType.java
new file mode 100644
index 00000000..9e3f397b
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/LineStringDataType.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+
+/**
+ * A data type for {@link LineString} objects.
+ */
+public class LineStringDataType implements DataType<LineString> {
+
+  private final GeometryFactory geometryFactory;
+
+  private final CoordinateArrayDataType coordinateArrayDataType;
+
+  /**
+   * Constructs a {@code LineStringDataType} with a default {@code 
GeometryFactory}.
+   */
+  public LineStringDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code LineStringDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public LineStringDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+    this.coordinateArrayDataType = new CoordinateArrayDataType();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(LineString value) {
+    return coordinateArrayDataType.size(value.getCoordinates());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return coordinateArrayDataType.size(buffer, position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, LineString value) {
+    coordinateArrayDataType.write(buffer, position, value.getCoordinates());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public LineString read(ByteBuffer buffer, int position) {
+    var coordinates = coordinateArrayDataType.read(buffer, position);
+    return geometryFactory.createLineString(coordinates);
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiLineStringDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiLineStringDataType.java
new file mode 100644
index 00000000..f78e4f45
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiLineStringDataType.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.MultiLineString;
+
+/**
+ * A data type for {@link MultiLineString} objects.
+ */
+public class MultiLineStringDataType implements DataType<MultiLineString> {
+
+  private final LineStringDataType lineStringDataType;
+
+  private final GeometryFactory geometryFactory;
+
+  /**
+   * Constructs a {@code MultiLineStringDataType} with a default {@code 
GeometryFactory}.
+   */
+  public MultiLineStringDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code MultiLineStringDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public MultiLineStringDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+    this.lineStringDataType = new LineStringDataType(geometryFactory);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(MultiLineString value) {
+    int size = Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      size += lineStringDataType.size((LineString) value.getGeometryN(i));
+    }
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return buffer.getInt(position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, MultiLineString value) {
+    buffer.putInt(position, size(value));
+    position += Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      lineStringDataType.write(buffer, position, (LineString) 
value.getGeometryN(i));
+      position += buffer.getInt(position);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public MultiLineString read(ByteBuffer buffer, int position) {
+    var size = size(buffer, position);
+    var limit = position + size;
+    position += Integer.BYTES;
+    var lineStrings = new ArrayList<LineString>();
+    while (position < limit) {
+      var lineString = lineStringDataType.read(buffer, position);
+      lineStrings.add(lineString);
+      position += lineStringDataType.size(buffer, position);
+    }
+    return 
geometryFactory.createMultiLineString(lineStrings.toArray(LineString[]::new));
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPointDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPointDataType.java
new file mode 100644
index 00000000..0e41c101
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPointDataType.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.MultiPoint;
+
+/**
+ * A data type for {@link MultiPoint} objects.
+ */
+public class MultiPointDataType implements DataType<MultiPoint> {
+
+  private final CoordinateArrayDataType coordinateArrayDataType = new 
CoordinateArrayDataType();
+
+  private final GeometryFactory geometryFactory;
+
+  /**
+   * Constructs a {@code MultiPointDataType} with a default {@code 
GeometryFactory}.
+   */
+  public MultiPointDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code MultiPointDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public MultiPointDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(MultiPoint value) {
+    return coordinateArrayDataType.size(value.getCoordinates());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return coordinateArrayDataType.size(buffer, position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, MultiPoint value) {
+    coordinateArrayDataType.write(buffer, position, value.getCoordinates());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public MultiPoint read(ByteBuffer buffer, int position) {
+    var coordinates = coordinateArrayDataType.read(buffer, position);
+    return geometryFactory.createMultiPoint(coordinates);
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPolygonDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPolygonDataType.java
new file mode 100644
index 00000000..f8b84cdc
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPolygonDataType.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.*;
+
+/**
+ * A data type for {@link GeometryCollection} objects.
+ */
+public class MultiPolygonDataType implements DataType<MultiPolygon> {
+
+  private final GeometryFactory geometryFactory;
+
+  private final PolygonDataType polygonDataType;
+
+  /**
+   * Constructs a {@code MultiPolygonDataType} with a default {@code 
GeometryFactory}.
+   */
+  public MultiPolygonDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code MultiPolygonDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public MultiPolygonDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+    this.polygonDataType = new PolygonDataType(geometryFactory);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(MultiPolygon value) {
+    int size = Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      size += polygonDataType.size((Polygon) value.getGeometryN(i));
+    }
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return buffer.getInt(position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, MultiPolygon value) {
+    buffer.putInt(position, size(value));
+    position += Integer.BYTES;
+    for (int i = 0; i < value.getNumGeometries(); i++) {
+      polygonDataType.write(buffer, position, (Polygon) value.getGeometryN(i));
+      position += buffer.getInt(position);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public MultiPolygon read(ByteBuffer buffer, int position) {
+    var size = size(buffer, position);
+    var limit = position + size;
+    position += Integer.BYTES;
+    var polygons = new ArrayList<Polygon>();
+    while (position < limit) {
+      var polygon = polygonDataType.read(buffer, position);
+      polygons.add(polygon);
+      position += polygonDataType.size(buffer, position);
+    }
+    return 
geometryFactory.createMultiPolygon(polygons.toArray(Polygon[]::new));
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PointDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PointDataType.java
new file mode 100644
index 00000000..3059340f
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PointDataType.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+
+/**
+ * A data type for {@link Point} objects.
+ */
+public class PointDataType implements DataType<Point> {
+
+  private final GeometryFactory geometryFactory;
+
+  /**
+   * Constructs a {@code PointDataType} with a default {@code GeometryFactory}.
+   */
+  public PointDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code PointDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public PointDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(Point value) {
+    return Double.BYTES * 2;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return Double.BYTES * 2;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, Point value) {
+    if (value.isEmpty()) {
+      buffer.putDouble(position, Double.NaN);
+      buffer.putDouble(position + Double.BYTES, Double.NaN);
+    } else {
+      buffer.putDouble(position, value.getX());
+      buffer.putDouble(position + Double.BYTES, value.getY());
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Point read(ByteBuffer buffer, int position) {
+    double x = buffer.getDouble(position);
+    double y = buffer.getDouble(position + Double.BYTES);
+    if (Double.isNaN(x) || Double.isNaN(y)) {
+      return geometryFactory.createPoint();
+    } else {
+      return geometryFactory.createPoint(new Coordinate(x, y));
+    }
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PolygonDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PolygonDataType.java
new file mode 100644
index 00000000..8497dc04
--- /dev/null
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PolygonDataType.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed 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.baremaps.collection.type.geometry;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.Polygon;
+
+/**
+ * A data type for {@link Polygon} objects.
+ */
+public class PolygonDataType implements DataType<Polygon> {
+
+  private final CoordinateArrayDataType coordinateArrayDataType = new 
CoordinateArrayDataType();
+
+  private final GeometryFactory geometryFactory;
+
+  /**
+   * Constructs a {@code PolygonDataType} with a default {@code 
GeometryFactory}.
+   */
+  public PolygonDataType() {
+    this(new GeometryFactory());
+  }
+
+  /**
+   * Constructs a {@code PolygonDataType} with a specified {@code 
GeometryFactory}.
+   *
+   * @param geometryFactory the geometry factory
+   */
+  public PolygonDataType(GeometryFactory geometryFactory) {
+    this.geometryFactory = geometryFactory;
+  }
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(Polygon value) {
+    int size = Integer.BYTES;
+
+    // Add the size of the exterior ring
+    var exteriorRing = value.getExteriorRing();
+    size += coordinateArrayDataType.size(exteriorRing.getCoordinates());
+
+    // Add the size of the interior rings
+    for (int i = 0; i < value.getNumInteriorRing(); i++) {
+      var interiorRing = value.getInteriorRingN(i);
+      size += coordinateArrayDataType.size(interiorRing.getCoordinates());
+    }
+
+    return size;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int size(ByteBuffer buffer, int position) {
+    return coordinateArrayDataType.size(buffer, position);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void write(ByteBuffer buffer, int position, Polygon value) {
+    buffer.putInt(position, size(value));
+    position += Integer.BYTES;
+
+    // Write the exterior ring
+    var exteriorRing = value.getExteriorRing();
+    coordinateArrayDataType.write(buffer, position, 
exteriorRing.getCoordinates());
+    position += coordinateArrayDataType.size(exteriorRing.getCoordinates());
+
+    // Write the interior rings
+    for (int i = 0; i < value.getNumInteriorRing(); i++) {
+      var interiorRing = value.getInteriorRingN(i);
+      coordinateArrayDataType.write(buffer, position, 
interiorRing.getCoordinates());
+      position += coordinateArrayDataType.size(interiorRing.getCoordinates());
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Polygon read(ByteBuffer buffer, int position) {
+    var size = size(buffer, position);
+    var limit = position + size;
+    position += Integer.BYTES;
+
+
+    // Read the exterior ring
+    var exteriorRingCoordinates = coordinateArrayDataType.read(buffer, 
position);
+    var exteriorRing = 
geometryFactory.createLinearRing(exteriorRingCoordinates);
+    position += coordinateArrayDataType.size(buffer, position);
+
+    // Read the interior rings
+    var interiorRings = new ArrayList<LineString>();
+    while (position < limit) {
+      var interiorRingCoordinates = coordinateArrayDataType.read(buffer, 
position);
+      var interiorRing = 
geometryFactory.createLinearRing(interiorRingCoordinates);
+      interiorRings.add(interiorRing);
+      position += coordinateArrayDataType.size(buffer, position);
+    }
+
+    return geometryFactory.createPolygon(exteriorRing, 
interiorRings.toArray(LinearRing[]::new));
+  }
+}
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
index 35867faa..35370f09 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/RelationGeometryBuilder.java
@@ -96,6 +96,7 @@ public class RelationGeometryBuilder implements 
Consumer<Relation> {
       }
     } catch (Exception e) {
       logger.warn("Unable to build the geometry for relation #" + 
relation.id(), e);
+      relation.setGeometry(GEOMETRY_FACTORY_WGS84.createEmpty(0));
     }
   }
 
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/WayGeometryBuilder.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/WayGeometryBuilder.java
index 4ffeec3a..9a57fcbf 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/WayGeometryBuilder.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/openstreetmap/function/WayGeometryBuilder.java
@@ -62,6 +62,7 @@ public class WayGeometryBuilder implements Consumer<Way> {
       }
     } catch (Exception e) {
       logger.warn("Unable to build the geometry for way #" + way.id(), e);
+      way.setGeometry(GEOMETRY_FACTORY_WGS84.createEmpty(0));
     }
   }
 }
diff --git a/baremaps-core/src/main/java/org/apache/baremaps/storage/Row.java 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/Row.java
index 2af38e3f..3de90fbf 100644
--- a/baremaps-core/src/main/java/org/apache/baremaps/storage/Row.java
+++ b/baremaps-core/src/main/java/org/apache/baremaps/storage/Row.java
@@ -31,7 +31,7 @@ public interface Row {
    * 
    * @return the values of the columns in the row
    */
-  List values();
+  List<?> values();
 
   /**
    * Returns the value of the specified column.
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufStore.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufStore.java
index 2b0459ec..950597bd 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufStore.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufStore.java
@@ -60,12 +60,14 @@ public class FlatGeoBufStore implements Store {
    */
   @Override
   public void add(Table table) throws TableException {
-    var path = directory.resolve(table.schema().name());
+    var filename = table.schema().name();
+    filename = filename.endsWith(".fgb") ? filename : filename + ".fgb";
+    var path = directory.resolve(filename);
     try {
-      Files.delete(path);
+      Files.deleteIfExists(path);
       Files.createFile(path);
       var flatGeoBufTable = new FlatGeoBufTable(path, table.schema());
-      table.forEach(flatGeoBufTable::add);
+      flatGeoBufTable.write(table);
     } catch (IOException e) {
       throw new TableException(e);
     }
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufTable.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufTable.java
index 5a13eeef..dedae110 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufTable.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/FlatGeoBufTable.java
@@ -27,7 +27,7 @@ import org.apache.baremaps.storage.AbstractTable;
 import org.apache.baremaps.storage.Row;
 import org.apache.baremaps.storage.Schema;
 import org.apache.baremaps.storage.flatgeobuf.internal.TableConversions;
-import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.*;
 import org.wololo.flatgeobuf.Constants;
 import org.wololo.flatgeobuf.GeometryConversions;
 import org.wololo.flatgeobuf.HeaderMeta;
@@ -160,6 +160,7 @@ public class FlatGeoBufTable extends AbstractTable {
       var headerMeta = new HeaderMeta();
       headerMeta.geometryType = GeometryType.Unknown;
       headerMeta.indexNodeSize = 16;
+      headerMeta.srid = 3857;
       headerMeta.featuresCount =
           features instanceof AbstractDataCollection<Row>c ? c.sizeAsLong() : 
features.size();
       headerMeta.name = schema.name();
@@ -175,38 +176,44 @@ public class FlatGeoBufTable extends AbstractTable {
 
       var iterator = features.iterator();
       while (iterator.hasNext()) {
+        var featureBuilder = new FlatBufferBuilder(4096);
+
         var row = iterator.next();
-        var featureBuilder = new FlatBufferBuilder();
-        var geometryOffset = 0;
-        var propertiesOffset = 0;
+
         var propertiesBuffer = ByteBuffer.allocate(1 << 
20).order(ByteOrder.LITTLE_ENDIAN);
-        var i = 0;
-        for (Object value : row.values()) {
-          if (value instanceof Geometry geometry) {
-            try {
-              geometryOffset =
-                  GeometryConversions.serialize(featureBuilder, geometry, 
headerMeta.geometryType);
-            } catch (IOException e) {
-              throw new RuntimeException(e);
-            }
-          } else {
-            var column = headerMeta.columns.get(i);
-            propertiesBuffer.putShort((short) i);
-            TableConversions.writeValue(propertiesBuffer, column, value);
-            i++;
-          }
+        var properties = row.values().stream()
+            .filter(v -> !(v instanceof Geometry))
+            .toList();
+        for (int i = 0; i < properties.size(); i++) {
+          var column = headerMeta.columns.get(i);
+          var value = properties.get(i);
+          propertiesBuffer.putShort((short) i);
+          TableConversions.writeValue(propertiesBuffer, column, value);
         }
-        propertiesBuffer.flip();
-        propertiesOffset = org.wololo.flatgeobuf.generated.Feature
+        if (propertiesBuffer.position() > 0) {
+          propertiesBuffer.flip();
+        }
+        var propertiesOffset = org.wololo.flatgeobuf.generated.Feature
             .createPropertiesVector(featureBuilder, propertiesBuffer);
 
+        var geometry = row.values().stream()
+            .filter(v -> v instanceof Geometry)
+            .map(Geometry.class::cast)
+            .findFirst();
+
+        var geometryOffset = geometry.isPresent()
+            ? GeometryConversions.serialize(featureBuilder, geometry.get(), 
headerMeta.geometryType)
+            : 0;
+
         var featureOffset =
             
org.wololo.flatgeobuf.generated.Feature.createFeature(featureBuilder, 
geometryOffset,
                 propertiesOffset, 0);
-
         featureBuilder.finishSizePrefixed(featureOffset);
 
-        channel.write(featureBuilder.dataBuffer());
+        ByteBuffer data = featureBuilder.dataBuffer();
+        while (data.hasRemaining()) {
+          channel.write(data);
+        }
       }
     }
   }
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/internal/TableConversions.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/internal/TableConversions.java
index 817dc6d0..5bc3fff9 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/internal/TableConversions.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/storage/flatgeobuf/internal/TableConversions.java
@@ -21,6 +21,7 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 import org.apache.baremaps.storage.*;
 import org.wololo.flatgeobuf.ColumnMeta;
@@ -191,13 +192,14 @@ public class TableConversions {
   public static List<ColumnMeta> asColumns(List<Column> columns) {
     return columns.stream()
         .map(TableConversions::asColumn)
+        .filter(Objects::nonNull)
         .collect(Collectors.toList());
   }
 
   public static ColumnMeta asColumn(Column column) {
     var type = types.get(column.type());
     if (type == null) {
-      throw new IllegalArgumentException("Unsupported type " + type);
+      return null;
     }
     var columnMeta = new ColumnMeta();
     columnMeta.name = column.name();
diff --git 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/EntityDataType.java
 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/EntityDataType.java
index f9c9a2de..027762d3 100644
--- 
a/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/EntityDataType.java
+++ 
b/baremaps-core/src/main/java/org/apache/baremaps/workflow/tasks/EntityDataType.java
@@ -15,6 +15,7 @@ package org.apache.baremaps.workflow.tasks;
 import java.nio.ByteBuffer;
 import java.util.Map;
 import org.apache.baremaps.collection.type.*;
+import org.apache.baremaps.collection.type.geometry.GeometryDataType;
 import org.locationtech.jts.geom.Geometry;
 
 public class EntityDataType implements DataType<Entity> {
@@ -29,7 +30,8 @@ public class EntityDataType implements DataType<Entity> {
   @Override
   public int size(Entity value) {
     int size = 0;
-    size += idType.size();
+    size += Integer.BYTES;
+    size += idType.size(value.getId());
     size += tagsType.size(value.getTags());
     size += geometryType.size(value.getGeometry());
     return size;
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/collection/AppendOnlyBufferTest.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/collection/AppendOnlyBufferTest.java
index 5340fd3b..d1e6bebc 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/collection/AppendOnlyBufferTest.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/collection/AppendOnlyBufferTest.java
@@ -61,8 +61,8 @@ class AppendOnlyBufferTest {
   @ParameterizedTest
   
@MethodSource("org.apache.baremaps.collection.type.DataTypeProvider#dataTypes")
   void testAllDataTypes(DataType dataType, Object value) {
-    var num = 1 << 10;
-    var collection = new AppendOnlyBuffer<>(dataType, new OffHeapMemory(1 << 
8));
+    var num = 1000;
+    var collection = new AppendOnlyBuffer<>(dataType, new OffHeapMemory(1 << 
22));
 
     // write values
     for (int i = 0; i < num; i++) {
diff --git 
a/baremaps-core/src/test/java/org/apache/baremaps/collection/type/DataTypeProvider.java
 
b/baremaps-core/src/test/java/org/apache/baremaps/collection/type/DataTypeProvider.java
index 76d32457..0b8d15f8 100644
--- 
a/baremaps-core/src/test/java/org/apache/baremaps/collection/type/DataTypeProvider.java
+++ 
b/baremaps-core/src/test/java/org/apache/baremaps/collection/type/DataTypeProvider.java
@@ -13,13 +13,12 @@
 package org.apache.baremaps.collection.type;
 
 
-
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Stream;
+import org.apache.baremaps.collection.type.geometry.*;
 import org.junit.jupiter.params.provider.Arguments;
-import org.locationtech.jts.geom.Coordinate;
-import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.*;
 
 public class DataTypeProvider {
 
@@ -62,15 +61,6 @@ public class DataTypeProvider {
         Arguments.of(new FloatListDataType(), List.of()),
         Arguments.of(new FloatListDataType(), List.of((float) 1, (float) 2, 
(float) 3)),
 
-        // Geometry
-        Arguments.of(new GeometryDataType(),
-            new GeometryFactory().createEmpty(0)),
-        Arguments.of(new GeometryDataType(),
-            new GeometryFactory().createPoint(new Coordinate(1, 1))),
-        Arguments.of(new GeometryDataType(),
-            new GeometryFactory()
-                .createLineString(new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)})),
-
         // Integer
         Arguments.of(new IntegerDataType(), Integer.MIN_VALUE),
         Arguments.of(new IntegerDataType(), Integer.MAX_VALUE),
@@ -147,6 +137,135 @@ public class DataTypeProvider {
         Arguments.of(new SmallLongDataType(8), Long.MIN_VALUE),
         Arguments.of(
             new PairDataType<>(new LongDataType(), new LongDataType()),
-            new PairDataType.Pair<>(1l, 2l)));
+            new PairDataType.Pair<>(1l, 2l)),
+
+        // WKB (Well Known Binary)
+        Arguments.of(new WKBDataType(),
+            new GeometryFactory().createEmpty(0)),
+        Arguments.of(new WKBDataType(),
+            new GeometryFactory().createPoint(new Coordinate(1, 1))),
+        Arguments.of(new WKBDataType(),
+            new GeometryFactory()
+                .createLineString(new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)})),
+
+        // Point
+        Arguments.of(new PointDataType(),
+            new GeometryFactory().createPoint()),
+        Arguments.of(new PointDataType(),
+            new GeometryFactory()
+                .createPoint(new Coordinate(1, 1))),
+
+        // MultiPoint
+        Arguments.of(new MultiPointDataType(),
+            new GeometryFactory().createMultiPoint()),
+        Arguments.of(new MultiPointDataType(),
+            new GeometryFactory()
+                .createMultiPoint(new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)})),
+
+        // LineString
+        Arguments.of(new LineStringDataType(),
+            new GeometryFactory().createLineString()),
+        Arguments.of(new LineStringDataType(),
+            new GeometryFactory()
+                .createLineString(new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)})),
+
+        // MultiLineString
+        Arguments.of(new MultiLineStringDataType(),
+            new GeometryFactory().createMultiLineString()),
+        Arguments.of(new MultiLineStringDataType(),
+            new GeometryFactory()
+                .createMultiLineString(
+                    new LineString[] {
+                        new GeometryFactory()
+                            .createLineString(
+                                new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)}),
+                        new GeometryFactory()
+                            .createLineString(
+                                new Coordinate[] {new Coordinate(3, 3), new 
Coordinate(4, 4)})})),
+
+        // Polygon
+        Arguments.of(new PolygonDataType(),
+            new GeometryFactory().createPolygon()),
+        Arguments.of(new PolygonDataType(),
+            new GeometryFactory()
+                .createPolygon(
+                    new GeometryFactory()
+                        .createLinearRing(
+                            new Coordinate[] {new Coordinate(0, 0), new 
Coordinate(0, 3),
+                                new Coordinate(5, 3), new Coordinate(5, 0), 
new Coordinate(0, 0)}),
+                    new LinearRing[] {
+                        new GeometryFactory()
+                            .createLinearRing(new Coordinate[] {new 
Coordinate(1, 1),
+                                new Coordinate(1, 2), new Coordinate(2, 2), 
new Coordinate(2, 1),
+                                new Coordinate(1, 1)}),
+                        new GeometryFactory()
+                            .createLinearRing(new Coordinate[] {new 
Coordinate(3, 1),
+                                new Coordinate(3, 2), new Coordinate(4, 2), 
new Coordinate(4, 1),
+                                new Coordinate(3, 1)})})),
+
+        // MultiPolygon
+        Arguments.of(new MultiPolygonDataType(),
+            new GeometryFactory().createMultiPolygon()),
+        Arguments.of(new MultiPolygonDataType(),
+            new GeometryFactory()
+                .createMultiPolygon(
+                    new Polygon[] {
+                        new GeometryFactory()
+                            .createPolygon(
+                                new GeometryFactory()
+                                    .createLinearRing(new Coordinate[] {new 
Coordinate(0, 0),
+                                        new Coordinate(0, 3), new 
Coordinate(5, 3),
+                                        new Coordinate(5, 0), new 
Coordinate(0, 0)}),
+                                new LinearRing[] {
+                                    new GeometryFactory()
+                                        .createLinearRing(new Coordinate[] 
{new Coordinate(1, 1),
+                                            new Coordinate(1, 2), new 
Coordinate(2, 2),
+                                            new Coordinate(2, 1), new 
Coordinate(1, 1)}),
+                                    new GeometryFactory()
+                                        .createLinearRing(new Coordinate[] 
{new Coordinate(3, 1),
+                                            new Coordinate(3, 2), new 
Coordinate(4, 2),
+                                            new Coordinate(4, 1), new 
Coordinate(3, 1)})}),
+                        new GeometryFactory()
+                            .createPolygon(
+                                new GeometryFactory()
+                                    .createLinearRing(new Coordinate[] {new 
Coordinate(1, 4),
+                                        new Coordinate(1, 5), new 
Coordinate(2, 5),
+                                        new Coordinate(2, 4), new 
Coordinate(1, 4)}))})),
+
+        // GeometryCollection
+        Arguments.of(new GeometryCollectionDataType(),
+            new GeometryFactory().createGeometryCollection()),
+        Arguments.of(new GeometryCollectionDataType(),
+            new GeometryFactory()
+                .createGeometryCollection(
+                    new Geometry[] {
+                        new GeometryFactory()
+                            .createPoint(new Coordinate(1, 1)),
+                        new GeometryFactory()
+                            .createLineString(
+                                new Coordinate[] {new Coordinate(1, 1), new 
Coordinate(2, 2)}),
+                        new GeometryFactory()
+                            .createPolygon(
+                                new GeometryFactory()
+                                    .createLinearRing(new Coordinate[] {new 
Coordinate(0, 0),
+                                        new Coordinate(0, 3), new 
Coordinate(5, 3),
+                                        new Coordinate(5, 0), new 
Coordinate(0, 0)}),
+                                new LinearRing[] {
+                                    new GeometryFactory()
+                                        .createLinearRing(new Coordinate[] 
{new Coordinate(1, 1),
+                                            new Coordinate(1, 2), new 
Coordinate(2, 2),
+                                            new Coordinate(2, 1), new 
Coordinate(1, 1)}),
+                                    new GeometryFactory()
+                                        .createLinearRing(new Coordinate[] 
{new Coordinate(3, 1),
+                                            new Coordinate(3, 2), new 
Coordinate(4, 2),
+                                            new Coordinate(4, 1), new 
Coordinate(3, 1)})})})),
+
+        // Geometry
+        Arguments.of(new GeometryDataType(),
+            new GeometryFactory().createEmpty(0)),
+        Arguments.of(new GeometryDataType(),
+            new GeometryFactory()
+                .createPoint(new Coordinate(1, 1))));
+
   }
 }
diff --git a/examples/simplification/workflow.json 
b/examples/transformation/workflow.json
similarity index 91%
rename from examples/simplification/workflow.json
rename to examples/transformation/workflow.json
index d1028ce9..a89c4560 100644
--- a/examples/simplification/workflow.json
+++ b/examples/transformation/workflow.json
@@ -6,8 +6,8 @@
       "tasks": [
         {
           "type": "DownloadUrl",
-          "url": 
"https://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf";,
-          "path": "liechtenstein-latest.osm.pbf"
+          "url": 
"https://download.geofabrik.de/europe/switzerland-latest.osm.pbf";,
+          "path": "data.osm.pbf"
         }
       ]
     },
@@ -19,7 +19,7 @@
       "tasks": [
         {
           "type": "CreateEntityCollection",
-          "file": "liechtenstein-latest.osm.pbf",
+          "file": "data.osm.pbf",
           "collection": "collection",
           "srid": 3857
         }

Reply via email to