You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@baremaps.apache.org by bc...@apache.org on 2023/04/07 22:18:34 UTC
[incubator-baremaps] 01/06: Improve support for geometries in collections
This is an automated email from the ASF dual-hosted git repository.
bchapuis pushed a commit to branch 619-transformation-workflow
in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
commit 9b59aefad6472fcfa8ac3dd71025954ffa8f2c3b
Author: Bertil Chapuis <bc...@gmail.com>
AuthorDate: Fri Apr 7 23:46:05 2023 +0200
Improve support for geometries in collections
---
.../{GeometryDataType.java => WKBDataType.java} | 5 +-
.../type/geometry/CoordinateArrayDataType.java | 62 +++++++
.../type/geometry/GeometryCollectionDataType.java | 96 +++++++++++
.../collection/type/geometry/GeometryDataType.java | 185 +++++++++++++++++++++
.../type/geometry/LineStringDataType.java | 67 ++++++++
.../type/geometry/MultiLineStringDataType.java | 85 ++++++++++
.../type/geometry/MultiPointDataType.java | 67 ++++++++
.../type/geometry/MultiPolygonDataType.java | 83 +++++++++
.../collection/type/geometry/PointDataType.java | 76 +++++++++
.../collection/type/geometry/PolygonDataType.java | 111 +++++++++++++
10 files changed, 835 insertions(+), 2 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..d3980ee0
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/CoordinateArrayDataType.java
@@ -0,0 +1,62 @@
+package org.apache.baremaps.collection.type.geometry;
+
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.Coordinate;
+
+import java.nio.ByteBuffer;
+
+/**
+ * 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..6d06b187
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryCollectionDataType.java
@@ -0,0 +1,96 @@
+package org.apache.baremaps.collection.type.geometry;
+
+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;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * 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 = buffer.getInt(position);
+ position += Integer.BYTES;
+ var geometries = new ArrayList<Geometry>();
+ while (position < size) {
+ 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..5713e734
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/GeometryDataType.java
@@ -0,0 +1,185 @@
+package org.apache.baremaps.collection.type.geometry;
+
+
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.*;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A {@code DataType} for {@code MultiPolygon} objects.
+ */
+public class GeometryDataType implements DataType<Geometry> {
+
+ private final GeometryFactory geometryFactory;
+
+ 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.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 instanceof Point) {
+ buffer.put(position, (byte) 1);
+ position += Byte.BYTES;
+ pointDataType.write(buffer, position, (Point) value);
+ } else if (value instanceof LineString) {
+ buffer.put(position, (byte) 2);
+ position += Byte.BYTES;
+ lineStringDataType.write(buffer, position, (LineString) value);
+ } else if (value instanceof Polygon) {
+ buffer.put(position, (byte) 3);
+ position += Byte.BYTES;
+ polygonDataType.write(buffer, position, (Polygon) value);
+ } else if (value instanceof MultiPoint) {
+ buffer.put(position, (byte) 4);
+ position += Byte.BYTES;
+ multiPointDataType.write(buffer, position, (MultiPoint) value);
+ } else if (value instanceof MultiLineString) {
+ buffer.put(position, (byte) 5);
+ position += Byte.BYTES;
+ multiLineStringDataType.write(buffer, position, (MultiLineString) value);
+ } else if (value instanceof MultiPolygon) {
+ buffer.put(position, (byte) 6);
+ position += Byte.BYTES;
+ multiPolygonDataType.write(buffer, position, (MultiPolygon) value);
+ } else if (value instanceof GeometryCollection) {
+ buffer.put(position, (byte) 7);
+ position += Byte.BYTES;
+ geometryCollectionDataType.write(buffer, position, (GeometryCollection) value);
+ } 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 == 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..fcf8ff99
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/LineStringDataType.java
@@ -0,0 +1,67 @@
+package org.apache.baremaps.collection.type.geometry;
+
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+
+import java.nio.ByteBuffer;
+
+/**
+ * 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..3c7c7846
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiLineStringDataType.java
@@ -0,0 +1,85 @@
+package org.apache.baremaps.collection.type.geometry;
+
+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;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * 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) {
+ int size = buffer.getInt(position);
+ position += Integer.BYTES;
+ var lineStrings = new ArrayList<LineString>();
+ while (position < size) {
+ 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..f53fbfcf
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPointDataType.java
@@ -0,0 +1,67 @@
+package org.apache.baremaps.collection.type.geometry;
+
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.MultiPoint;
+
+import java.nio.ByteBuffer;
+
+/**
+ * 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..f2c4141e
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/MultiPolygonDataType.java
@@ -0,0 +1,83 @@
+package org.apache.baremaps.collection.type.geometry;
+
+import org.apache.baremaps.collection.type.DataType;
+import org.locationtech.jts.geom.*;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * 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) {
+ int size = buffer.getInt(position);
+ position += Integer.BYTES;
+ var polygons = new ArrayList<Polygon>();
+ while (position < size) {
+ 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..97a38407
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PointDataType.java
@@ -0,0 +1,76 @@
+package org.apache.baremaps.collection.type.geometry;
+
+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;
+
+import java.nio.ByteBuffer;
+
+/**
+ * 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..a9bf0c9b
--- /dev/null
+++ b/baremaps-core/src/main/java/org/apache/baremaps/collection/type/geometry/PolygonDataType.java
@@ -0,0 +1,111 @@
+package org.apache.baremaps.collection.type.geometry;
+
+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;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+
+/**
+ * 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 = buffer.getInt(position);
+ 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 < size) {
+ 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));
+ }
+}