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 <[email protected]> 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)); + } +}
