This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new d27bba9b07 [GH-2294] Introduce XYZM support in GeoJSON reader and
writer (#2295)
d27bba9b07 is described below
commit d27bba9b07319f6b9050242e0b7d010b21e0f300
Author: Jia Yu <[email protected]>
AuthorDate: Sat Aug 16 21:30:16 2025 -0700
[GH-2294] Introduce XYZM support in GeoJSON reader and writer (#2295)
---
.../java/org/apache/sedona/common/Functions.java | 2 +-
.../sedona/common/jts2geojson/GeoJSONReader.java | 129 ++++
.../sedona/common/jts2geojson/GeoJSONWriter.java | 122 ++++
.../apache/sedona/common/utils/FormatUtils.java | 2 +-
.../common/jts2geojson/GeoJSONReaderTest.java | 303 ++++++++++
.../common/jts2geojson/GeoJSONWriterTest.java | 663 +++++++++++++++++++++
.../org/apache/sedona/flink/ConstructorTest.java | 2 +-
.../java/org/apache/sedona/flink/TestBase.java | 2 +-
.../apache/sedona/core/spatialRDD/SpatialRDD.java | 2 +-
.../org/apache/sedona/core/utils/testGeoJSON.java | 2 +-
10 files changed, 1223 insertions(+), 6 deletions(-)
diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java
b/common/src/main/java/org/apache/sedona/common/Functions.java
index 83522080d3..0469683fb2 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.sedona.common.S2Geography.Geography;
import org.apache.sedona.common.geometryObjects.Circle;
+import org.apache.sedona.common.jts2geojson.GeoJSONWriter;
import org.apache.sedona.common.sphere.Spheroid;
import org.apache.sedona.common.subDivide.GeometrySubDivider;
import org.apache.sedona.common.utils.*;
@@ -70,7 +71,6 @@ import
org.locationtech.jts.triangulate.DelaunayTriangulationBuilder;
import
org.locationtech.jts.triangulate.polygon.ConstrainedDelaunayTriangulator;
import org.wololo.geojson.Feature;
import org.wololo.geojson.FeatureCollection;
-import org.wololo.jts2geojson.GeoJSONWriter;
public class Functions {
private static final double DEFAULT_TOLERANCE = 1e-6;
diff --git
a/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONReader.java
b/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONReader.java
new file mode 100644
index 0000000000..92b802baf4
--- /dev/null
+++
b/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONReader.java
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.sedona.common.jts2geojson;
+
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.CoordinateXYZM;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.PrecisionModel;
+import org.wololo.geojson.*;
+
+public class GeoJSONReader {
+ static final GeometryFactory FACTORY =
+ new GeometryFactory(new PrecisionModel(PrecisionModel.FLOATING));
+
+ public Geometry read(String json) {
+ return read(json, null);
+ }
+
+ public Geometry read(String json, GeometryFactory geomFactory) {
+ return read(GeoJSONFactory.create(json), geomFactory);
+ }
+
+ public Geometry read(GeoJSON geoJSON) {
+ return read(geoJSON, null);
+ }
+
+ public Geometry read(GeoJSON geoJSON, GeometryFactory geomFactory) {
+ var factory = geomFactory != null ? geomFactory : FACTORY;
+ if (geoJSON instanceof Point) return convert((Point) geoJSON, factory);
+ else if (geoJSON instanceof LineString) return convert((LineString)
geoJSON, factory);
+ else if (geoJSON instanceof Polygon) return convert((Polygon) geoJSON,
factory);
+ else if (geoJSON instanceof MultiPoint) return convert((MultiPoint)
geoJSON, factory);
+ else if (geoJSON instanceof MultiLineString) return
convert((MultiLineString) geoJSON, factory);
+ else if (geoJSON instanceof MultiPolygon) return convert((MultiPolygon)
geoJSON, factory);
+ else if (geoJSON instanceof GeometryCollection)
+ return convert((GeometryCollection) geoJSON, factory);
+ else throw new UnsupportedOperationException();
+ }
+
+ Geometry convert(Point point, GeometryFactory factory) {
+ return factory.createPoint(convert(point.getCoordinates()));
+ }
+
+ Geometry convert(MultiPoint multiPoint, GeometryFactory factory) {
+ return
factory.createMultiPointFromCoords(convert(multiPoint.getCoordinates()));
+ }
+
+ Geometry convert(LineString lineString, GeometryFactory factory) {
+ return factory.createLineString(convert(lineString.getCoordinates()));
+ }
+
+ Geometry convert(MultiLineString multiLineString, GeometryFactory factory) {
+ var size = multiLineString.getCoordinates().length;
+ var lineStrings = new org.locationtech.jts.geom.LineString[size];
+ for (int i = 0; i < size; i++)
+ lineStrings[i] =
factory.createLineString(convert(multiLineString.getCoordinates()[i]));
+ return factory.createMultiLineString(lineStrings);
+ }
+
+ Geometry convert(Polygon polygon, GeometryFactory factory) {
+ return convertToPolygon(polygon.getCoordinates(), factory);
+ }
+
+ org.locationtech.jts.geom.Polygon convertToPolygon(
+ double[][][] coordinates, GeometryFactory factory) {
+ var shell = factory.createLinearRing(convert(coordinates[0]));
+ if (coordinates.length > 1) {
+ var size = coordinates.length - 1;
+ var holes = new LinearRing[size];
+ for (var i = 0; i < size; i++)
+ holes[i] = factory.createLinearRing(convert(coordinates[i + 1]));
+ return factory.createPolygon(shell, holes);
+ } else {
+ return factory.createPolygon(shell);
+ }
+ }
+
+ Geometry convert(MultiPolygon multiPolygon, GeometryFactory factory) {
+ var size = multiPolygon.getCoordinates().length;
+ var polygons = new org.locationtech.jts.geom.Polygon[size];
+ for (int i = 0; i < size; i++)
+ polygons[i] = convertToPolygon(multiPolygon.getCoordinates()[i],
factory);
+ return factory.createMultiPolygon(polygons);
+ }
+
+ Geometry convert(GeometryCollection gc, GeometryFactory factory) {
+ var size = gc.getGeometries().length;
+ var geometries = new Geometry[size];
+ for (var i = 0; i < size; i++) geometries[i] = read(gc.getGeometries()[i],
factory);
+ return factory.createGeometryCollection(geometries);
+ }
+
+ Coordinate convert(double[] c) {
+ if (c.length == 2) {
+ return new Coordinate(c[0], c[1]);
+ } else if (c.length == 3) {
+ return new Coordinate(c[0], c[1], c[2]);
+ } else if (c.length == 4) {
+ // Handle XYZM coordinates (4 values)
+ return new CoordinateXYZM(c[0], c[1], c[2], c[3]);
+ } else {
+ return new Coordinate(c[0], c[1]);
+ }
+ }
+
+ Coordinate[] convert(double[][] ca) {
+ var coordinates = new Coordinate[ca.length];
+ for (int i = 0; i < ca.length; i++) coordinates[i] = convert(ca[i]);
+ return coordinates;
+ }
+}
diff --git
a/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONWriter.java
b/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONWriter.java
new file mode 100644
index 0000000000..f9bd189b2f
--- /dev/null
+++
b/common/src/main/java/org/apache/sedona/common/jts2geojson/GeoJSONWriter.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.sedona.common.jts2geojson;
+
+import java.util.List;
+import org.locationtech.jts.geom.*;
+import org.wololo.geojson.Feature;
+
+public class GeoJSONWriter {
+
+ static final GeoJSONReader reader = new GeoJSONReader();
+
+ public org.wololo.geojson.Geometry write(Geometry geometry) {
+ Class<? extends Geometry> c = geometry.getClass();
+ if (c.equals(Point.class)) return convert((Point) geometry);
+ else if (c.equals(LineString.class)) return convert((LineString) geometry);
+ else if (c.equals(LinearRing.class)) return convert((LinearRing) geometry);
+ else if (c.equals(Polygon.class)) return convert((Polygon) geometry);
+ else if (c.equals(MultiPoint.class)) return convert((MultiPoint) geometry);
+ else if (c.equals(MultiLineString.class)) return convert((MultiLineString)
geometry);
+ else if (c.equals(MultiPolygon.class)) return convert((MultiPolygon)
geometry);
+ else if (c.equals(GeometryCollection.class)) return
convert((GeometryCollection) geometry);
+ else throw new UnsupportedOperationException();
+ }
+
+ public org.wololo.geojson.FeatureCollection write(List<Feature> features) {
+ var size = features.size();
+ var featuresJson = new Feature[size];
+ for (var i = 0; i < size; i++) featuresJson[i] = features.get(i);
+ return new org.wololo.geojson.FeatureCollection(featuresJson);
+ }
+
+ org.wololo.geojson.Point convert(Point point) {
+ return new org.wololo.geojson.Point(convert(point.getCoordinate()));
+ }
+
+ org.wololo.geojson.MultiPoint convert(MultiPoint multiPoint) {
+ return new
org.wololo.geojson.MultiPoint(convert(multiPoint.getCoordinates()));
+ }
+
+ org.wololo.geojson.LineString convert(LineString lineString) {
+ return new
org.wololo.geojson.LineString(convert(lineString.getCoordinates()));
+ }
+
+ org.wololo.geojson.LineString convert(LinearRing ringString) {
+ return new
org.wololo.geojson.LineString(convert(ringString.getCoordinates()));
+ }
+
+ org.wololo.geojson.MultiLineString convert(MultiLineString multiLineString) {
+ var size = multiLineString.getNumGeometries();
+ var lineStrings = new double[size][][];
+ for (int i = 0; i < size; i++)
+ lineStrings[i] =
convert(multiLineString.getGeometryN(i).getCoordinates());
+ return new org.wololo.geojson.MultiLineString(lineStrings);
+ }
+
+ org.wololo.geojson.Polygon convert(Polygon polygon) {
+ var size = polygon.getNumInteriorRing() + 1;
+ var rings = new double[size][][];
+ rings[0] = convert(polygon.getExteriorRing().getCoordinates());
+ for (int i = 0; i < size - 1; i++)
+ rings[i + 1] = convert(polygon.getInteriorRingN(i).getCoordinates());
+ return new org.wololo.geojson.Polygon(rings);
+ }
+
+ org.wololo.geojson.MultiPolygon convert(MultiPolygon multiPolygon) {
+ var size = multiPolygon.getNumGeometries();
+ var polygons = new double[size][][][];
+ for (int i = 0; i < size; i++)
+ polygons[i] = convert((Polygon)
multiPolygon.getGeometryN(i)).getCoordinates();
+ return new org.wololo.geojson.MultiPolygon(polygons);
+ }
+
+ org.wololo.geojson.GeometryCollection convert(GeometryCollection gc) {
+ var size = gc.getNumGeometries();
+ var geometries = new org.wololo.geojson.Geometry[size];
+ for (int i = 0; i < size; i++) geometries[i] = write((Geometry)
gc.getGeometryN(i));
+ return new org.wololo.geojson.GeometryCollection(geometries);
+ }
+
+ double[] convert(Coordinate coordinate) {
+ boolean hasZ = !Double.isNaN(coordinate.getZ());
+ boolean hasM = !Double.isNaN(coordinate.getM());
+
+ if (!hasZ && !hasM) {
+ // XY case - only 2D coordinates
+ return new double[] {coordinate.x, coordinate.y};
+ } else if (hasZ && !hasM) {
+ // XYZ case - 3D coordinates without measure
+ return new double[] {coordinate.x, coordinate.y, coordinate.getZ()};
+ } else if (hasZ && hasM) {
+ // XYZM case - 3D coordinates with measure
+ return new double[] {coordinate.x, coordinate.y, coordinate.getZ(),
coordinate.getM()};
+ } else {
+ // XYM case - We don't support this directly
+ throw new UnsupportedOperationException(
+ "XYM coordinates are not supported. Please convert to XYZM
coordinates by adding a Z value.");
+ }
+ }
+
+ double[][] convert(Coordinate[] coordinates) {
+ var array = new double[coordinates.length][];
+ for (int i = 0; i < coordinates.length; i++) array[i] =
convert(coordinates[i]);
+ return array;
+ }
+}
diff --git
a/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
b/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
index dcbf1cfb2a..e37d05f050 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/FormatUtils.java
@@ -25,6 +25,7 @@ import java.util.*;
import org.apache.sedona.common.Functions;
import org.apache.sedona.common.enums.FileDataSplitter;
import org.apache.sedona.common.enums.GeometryType;
+import org.apache.sedona.common.jts2geojson.GeoJSONReader;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKBReader;
@@ -34,7 +35,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wololo.geojson.Feature;
import org.wololo.geojson.GeoJSONFactory;
-import org.wololo.jts2geojson.GeoJSONReader;
/** This format mapper is isolated on purpose for the sake of sharing across
Spark and Flink */
public class FormatUtils<T extends Geometry> implements Serializable {
diff --git
a/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONReaderTest.java
b/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONReaderTest.java
new file mode 100644
index 0000000000..6b6ee05ab5
--- /dev/null
+++
b/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONReaderTest.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.sedona.common.jts2geojson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.locationtech.jts.geom.CoordinateXYZM;
+import org.locationtech.jts.geom.Geometry;
+import org.wololo.geojson.Point;
+
+public class GeoJSONReaderTest {
+
+ @Test
+ public void testPointConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[] coords = new double[] {1.0, 2.0};
+ Point point = new Point(coords);
+ Geometry geometry = reader.read(point);
+ assertEquals(1.0, geometry.getCoordinate().x, 0.0);
+ assertEquals(2.0, geometry.getCoordinate().y, 0.0);
+ }
+
+ @Test
+ public void testMultiPointConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][] coords = new double[][] {{1.0, 2.0}, {3.0, 4.0}};
+ org.wololo.geojson.MultiPoint multiPoint = new
org.wololo.geojson.MultiPoint(coords);
+ Geometry geometry = reader.read(multiPoint);
+ assertEquals(1.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(3.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(4.0, geometry.getCoordinates()[1].y, 0.0);
+ }
+
+ @Test
+ public void testPointWithZConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[] coords = new double[] {1.0, 2.0, 3.0};
+ Point point = new Point(coords);
+ Geometry geometry = reader.read(point);
+ assertEquals(1.0, geometry.getCoordinate().x, 0.0);
+ assertEquals(2.0, geometry.getCoordinate().y, 0.0);
+ assertEquals(3.0, geometry.getCoordinate().getZ(), 0.0);
+ }
+
+ @Test
+ public void testMultiPointWithZConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][] coords = new double[][] {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0}};
+ org.wololo.geojson.MultiPoint multiPoint = new
org.wololo.geojson.MultiPoint(coords);
+ Geometry geometry = reader.read(multiPoint);
+ assertEquals(1.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(3.0, geometry.getCoordinates()[0].getZ(), 0.0);
+ assertEquals(4.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(5.0, geometry.getCoordinates()[1].y, 0.0);
+ assertEquals(6.0, geometry.getCoordinates()[1].getZ(), 0.0);
+ }
+
+ @Test
+ public void testPointWithXYZMConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[] coords = new double[] {1.0, 2.0, 3.0, 4.0};
+ Point point = new Point(coords);
+ Geometry geometry = reader.read(point);
+
+ assertEquals(1.0, geometry.getCoordinate().x, 0.0);
+ assertEquals(2.0, geometry.getCoordinate().y, 0.0);
+ assertEquals(3.0, geometry.getCoordinate().getZ(), 0.0);
+ assertEquals(4.0, geometry.getCoordinate().getM(), 0.0);
+
+ // Verify the coordinate is an XYZM coordinate
+ assertTrue(geometry.getCoordinate() instanceof CoordinateXYZM);
+ }
+
+ @Test
+ public void testMultiPointWithXYZMConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][] coords = new double[][] {{1.0, 2.0, 3.0, 4.0}, {5.0, 6.0, 7.0,
8.0}};
+ org.wololo.geojson.MultiPoint multiPoint = new
org.wololo.geojson.MultiPoint(coords);
+ Geometry geometry = reader.read(multiPoint);
+
+ assertEquals(1.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(3.0, geometry.getCoordinates()[0].getZ(), 0.0);
+ assertEquals(4.0, geometry.getCoordinates()[0].getM(), 0.0);
+
+ assertEquals(5.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(6.0, geometry.getCoordinates()[1].y, 0.0);
+ assertEquals(7.0, geometry.getCoordinates()[1].getZ(), 0.0);
+ assertEquals(8.0, geometry.getCoordinates()[1].getM(), 0.0);
+
+ // Verify the coordinates are XYZM coordinates
+ assertTrue(geometry.getCoordinates()[0] instanceof CoordinateXYZM);
+ assertTrue(geometry.getCoordinates()[1] instanceof CoordinateXYZM);
+ }
+
+ @Test
+ public void testLineStringConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][] coords = new double[][] {{1.0, 2.0}, {3.0, 4.0}, {5.0, 6.0}};
+ org.wololo.geojson.LineString lineString = new
org.wololo.geojson.LineString(coords);
+ Geometry geometry = reader.read(lineString);
+
+ assertEquals("LineString", geometry.getGeometryType());
+ assertEquals(3, geometry.getCoordinates().length);
+ assertEquals(1.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(3.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(4.0, geometry.getCoordinates()[1].y, 0.0);
+ assertEquals(5.0, geometry.getCoordinates()[2].x, 0.0);
+ assertEquals(6.0, geometry.getCoordinates()[2].y, 0.0);
+ }
+
+ @Test
+ public void testLineStringWithZConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][] coords = new double[][] {{1.0, 2.0, 3.0}, {4.0, 5.0, 6.0},
{7.0, 8.0, 9.0}};
+ org.wololo.geojson.LineString lineString = new
org.wololo.geojson.LineString(coords);
+ Geometry geometry = reader.read(lineString);
+
+ assertEquals("LineString", geometry.getGeometryType());
+ assertEquals(3, geometry.getCoordinates().length);
+ assertEquals(1.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(3.0, geometry.getCoordinates()[0].getZ(), 0.0);
+ assertEquals(4.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(5.0, geometry.getCoordinates()[1].y, 0.0);
+ assertEquals(6.0, geometry.getCoordinates()[1].getZ(), 0.0);
+ assertEquals(7.0, geometry.getCoordinates()[2].x, 0.0);
+ assertEquals(8.0, geometry.getCoordinates()[2].y, 0.0);
+ assertEquals(9.0, geometry.getCoordinates()[2].getZ(), 0.0);
+ }
+
+ @Test
+ public void testPolygonConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ // Polygon with one exterior ring
+ double[][][] coords =
+ new double[][][] {{{0.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}, {10.0, 0.0},
{0.0, 0.0}}};
+ org.wololo.geojson.Polygon polygon = new
org.wololo.geojson.Polygon(coords);
+ Geometry geometry = reader.read(polygon);
+
+ assertEquals("Polygon", geometry.getGeometryType());
+ assertEquals(5, geometry.getCoordinates().length);
+ assertEquals(0.0, geometry.getCoordinates()[0].x, 0.0);
+ assertEquals(0.0, geometry.getCoordinates()[0].y, 0.0);
+ assertEquals(0.0, geometry.getCoordinates()[1].x, 0.0);
+ assertEquals(10.0, geometry.getCoordinates()[1].y, 0.0);
+ }
+
+ @Test
+ public void testPolygonWithHoleConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ // Polygon with exterior ring and one hole
+ double[][][] coords =
+ new double[][][] {
+ {{0.0, 0.0}, {0.0, 10.0}, {10.0, 10.0}, {10.0, 0.0}, {0.0, 0.0}},
+ {{2.0, 2.0}, {2.0, 8.0}, {8.0, 8.0}, {8.0, 2.0}, {2.0, 2.0}}
+ };
+ org.wololo.geojson.Polygon polygon = new
org.wololo.geojson.Polygon(coords);
+ Geometry geometry = reader.read(polygon);
+
+ assertEquals("Polygon", geometry.getGeometryType());
+ assertEquals(1, geometry.getNumGeometries());
+ assertEquals(1, ((org.locationtech.jts.geom.Polygon)
geometry).getNumInteriorRing());
+
+ // Check exterior ring
+ assertEquals(
+ 5,
+ ((org.locationtech.jts.geom.Polygon)
geometry).getExteriorRing().getCoordinates().length);
+
+ // Check interior ring
+ assertEquals(
+ 5,
+ ((org.locationtech.jts.geom.Polygon)
geometry).getInteriorRingN(0).getCoordinates().length);
+ assertEquals(
+ 2.0,
+ ((org.locationtech.jts.geom.Polygon)
geometry).getInteriorRingN(0).getCoordinates()[0].x,
+ 0.0);
+ assertEquals(
+ 2.0,
+ ((org.locationtech.jts.geom.Polygon)
geometry).getInteriorRingN(0).getCoordinates()[0].y,
+ 0.0);
+ }
+
+ @Test
+ public void testMultiLineStringConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][][] coords =
+ new double[][][] {
+ {{1.0, 2.0}, {3.0, 4.0}},
+ {{5.0, 6.0}, {7.0, 8.0}}
+ };
+ org.wololo.geojson.MultiLineString multiLineString =
+ new org.wololo.geojson.MultiLineString(coords);
+ Geometry geometry = reader.read(multiLineString);
+
+ assertEquals("MultiLineString", geometry.getGeometryType());
+ assertEquals(2, geometry.getNumGeometries());
+
+ // Check first linestring
+ assertEquals(2, geometry.getGeometryN(0).getCoordinates().length);
+ assertEquals(1.0, geometry.getGeometryN(0).getCoordinates()[0].x, 0.0);
+ assertEquals(2.0, geometry.getGeometryN(0).getCoordinates()[0].y, 0.0);
+
+ // Check second linestring
+ assertEquals(2, geometry.getGeometryN(1).getCoordinates().length);
+ assertEquals(5.0, geometry.getGeometryN(1).getCoordinates()[0].x, 0.0);
+ assertEquals(6.0, geometry.getGeometryN(1).getCoordinates()[0].y, 0.0);
+ }
+
+ @Test
+ public void testMultiPolygonConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ double[][][][] coords =
+ new double[][][][] {
+ // First polygon
+ {{{0.0, 0.0}, {0.0, 5.0}, {5.0, 5.0}, {5.0, 0.0}, {0.0, 0.0}}},
+ // Second polygon
+ {{{10.0, 10.0}, {10.0, 15.0}, {15.0, 15.0}, {15.0, 10.0}, {10.0,
10.0}}}
+ };
+ org.wololo.geojson.MultiPolygon multiPolygon = new
org.wololo.geojson.MultiPolygon(coords);
+ Geometry geometry = reader.read(multiPolygon);
+
+ assertEquals("MultiPolygon", geometry.getGeometryType());
+ assertEquals(2, geometry.getNumGeometries());
+
+ // Check first polygon
+ assertEquals(5, geometry.getGeometryN(0).getCoordinates().length);
+ assertEquals(0.0, geometry.getGeometryN(0).getCoordinates()[0].x, 0.0);
+ assertEquals(0.0, geometry.getGeometryN(0).getCoordinates()[0].y, 0.0);
+
+ // Check second polygon
+ assertEquals(5, geometry.getGeometryN(1).getCoordinates().length);
+ assertEquals(10.0, geometry.getGeometryN(1).getCoordinates()[0].x, 0.0);
+ assertEquals(10.0, geometry.getGeometryN(1).getCoordinates()[0].y, 0.0);
+ }
+
+ @Test
+ public void testGeometryCollectionConversion() {
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader = new
GeoJSONReader();
+
+ // Create a Point
+ double[] pointCoords = new double[] {1.0, 2.0};
+ org.wololo.geojson.Point point = new org.wololo.geojson.Point(pointCoords);
+
+ // Create a LineString
+ double[][] lineCoords = new double[][] {{3.0, 4.0}, {5.0, 6.0}};
+ org.wololo.geojson.LineString lineString = new
org.wololo.geojson.LineString(lineCoords);
+
+ // Create a GeometryCollection with these geometries
+ org.wololo.geojson.Geometry[] geometries =
+ new org.wololo.geojson.Geometry[] {point, lineString};
+ org.wololo.geojson.GeometryCollection geometryCollection =
+ new org.wololo.geojson.GeometryCollection(geometries);
+
+ Geometry geometry = reader.read(geometryCollection);
+
+ assertEquals("GeometryCollection", geometry.getGeometryType());
+ assertEquals(2, geometry.getNumGeometries());
+
+ // Check first geometry (point)
+ assertEquals("Point", geometry.getGeometryN(0).getGeometryType());
+ assertEquals(1.0, geometry.getGeometryN(0).getCoordinate().x, 0.0);
+ assertEquals(2.0, geometry.getGeometryN(0).getCoordinate().y, 0.0);
+
+ // Check second geometry (linestring)
+ assertEquals("LineString", geometry.getGeometryN(1).getGeometryType());
+ assertEquals(3.0, geometry.getGeometryN(1).getCoordinates()[0].x, 0.0);
+ assertEquals(4.0, geometry.getGeometryN(1).getCoordinates()[0].y, 0.0);
+ }
+}
diff --git
a/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONWriterTest.java
b/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONWriterTest.java
new file mode 100644
index 0000000000..3d9f2ee995
--- /dev/null
+++
b/common/src/test/java/org/apache/sedona/common/jts2geojson/GeoJSONWriterTest.java
@@ -0,0 +1,663 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.sedona.common.jts2geojson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.CoordinateXYM;
+import org.locationtech.jts.geom.CoordinateXYZM;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineString;
+import org.locationtech.jts.geom.LinearRing;
+import org.locationtech.jts.geom.MultiLineString;
+import org.locationtech.jts.geom.MultiPoint;
+import org.locationtech.jts.geom.MultiPolygon;
+import org.locationtech.jts.geom.Point;
+import org.locationtech.jts.geom.Polygon;
+import org.wololo.geojson.Feature;
+import org.wololo.geojson.FeatureCollection;
+
+public class GeoJSONWriterTest {
+
+ @Test
+ public void testPointConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new Coordinate(1, 1));
+ String expected = "{\"type\":\"Point\",\"coordinates\":[1.0,1.0]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(point);
+ assertEquals("Point should be correctly converted to GeoJSON", expected,
json.toString());
+
+ org.locationtech.jts.geom.Geometry geometry = reader.read(json);
+ assertEquals(
+ "GeoJSON should be correctly converted back to geometry",
+ "POINT (1 1)",
+ geometry.toString());
+ }
+
+ @Test
+ public void testLineStringConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader =
+ new org.apache.sedona.common.jts2geojson.GeoJSONReader();
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ String expected =
+
"{\"type\":\"LineString\",\"coordinates\":[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(lineString);
+ assertEquals("LineString should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testLineStringWithIdConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ String expected =
+
"{\"type\":\"LineString\",\"coordinates\":[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(lineString);
+ assertEquals(
+ "LineString with ID should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testLinearRingConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LinearRing ring = factory.createLinearRing(coordinates);
+ String expected =
+
"{\"type\":\"LineString\",\"coordinates\":[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(ring);
+ assertEquals("LinearRing should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testPolygonConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ Polygon polygon = factory.createPolygon(coordinates);
+ String expected =
+
"{\"type\":\"Polygon\",\"coordinates\":[[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(polygon);
+ assertEquals("Polygon should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testMultiPointConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ MultiPoint multiPoint =
factory.createMultiPointFromCoords(lineString.getCoordinates());
+ String expected =
+
"{\"type\":\"MultiPoint\",\"coordinates\":[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPoint);
+ assertEquals("MultiPoint should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testMultiLineStringConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ MultiLineString multiLineString =
+ factory.createMultiLineString(new LineString[] {lineString,
lineString});
+ String expected =
+
"{\"type\":\"MultiLineString\",\"coordinates\":[[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]],[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiLineString);
+ assertEquals(
+ "MultiLineString should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testMultiPolygonConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1), new Coordinate(1, 2), new Coordinate(2, 2),
new Coordinate(1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ Polygon polygon = factory.createPolygon(lineString.getCoordinates());
+ MultiPolygon multiPolygon = factory.createMultiPolygon(new Polygon[]
{polygon, polygon});
+ String expected =
+
"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]],[[[1.0,1.0],[1.0,2.0],[2.0,2.0],[1.0,1.0]]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPolygon);
+ assertEquals(
+ "MultiPolygon should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testFeatureCollectionConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new Coordinate(1, 1));
+ org.wololo.geojson.Geometry json = writer.write(point);
+ Feature feature1 = new Feature(json, null);
+ Feature feature2 = new Feature(json, null);
+ FeatureCollection featureCollection = new FeatureCollection(new Feature[]
{feature1, feature2});
+ String expected =
+
"{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":null},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":null}]}";
+
+ // Test
+ assertEquals(
+ "FeatureCollection should be correctly converted to GeoJSON",
+ expected,
+ featureCollection.toString());
+ }
+
+ // 3D Tests
+
+ @Test
+ public void testPointWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONReader reader = new
GeoJSONReader();
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new Coordinate(1, 1, 1));
+ String expected = "{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(point);
+ assertEquals("3D Point should be correctly converted to GeoJSON",
expected, json.toString());
+
+ org.locationtech.jts.geom.Geometry geometry = reader.read(json);
+ assertEquals(
+ "GeoJSON should be correctly converted back to geometry",
+ "POINT (1 1)",
+ geometry.toString());
+ }
+
+ @Test
+ public void testLineStringWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1, 1),
+ new Coordinate(1, 2, 1),
+ new Coordinate(2, 2, 2),
+ new Coordinate(1, 1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ String expected =
+
"{\"type\":\"LineString\",\"coordinates\":[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(lineString);
+ assertEquals(
+ "3D LineString should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testPolygonWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1, 1),
+ new Coordinate(1, 2, 1),
+ new Coordinate(2, 2, 2),
+ new Coordinate(1, 1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ Polygon polygon = factory.createPolygon(lineString.getCoordinates());
+ String expected =
+
"{\"type\":\"Polygon\",\"coordinates\":[[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(polygon);
+ assertEquals("3D Polygon should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testMultiPointWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1, 1),
+ new Coordinate(1, 2, 1),
+ new Coordinate(2, 2, 2),
+ new Coordinate(1, 1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ MultiPoint multiPoint =
factory.createMultiPointFromCoords(lineString.getCoordinates());
+ String expected =
+
"{\"type\":\"MultiPoint\",\"coordinates\":[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPoint);
+ assertEquals(
+ "3D MultiPoint should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testMultiLineStringWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1, 1),
+ new Coordinate(1, 2, 1),
+ new Coordinate(2, 2, 2),
+ new Coordinate(1, 1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ MultiLineString multiLineString =
+ factory.createMultiLineString(new LineString[] {lineString,
lineString});
+ String expected =
+
"{\"type\":\"MultiLineString\",\"coordinates\":[[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]],[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiLineString);
+ assertEquals(
+ "3D MultiLineString should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testMultiPolygonWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new Coordinate(1, 1, 1),
+ new Coordinate(1, 2, 1),
+ new Coordinate(2, 2, 2),
+ new Coordinate(1, 1, 1)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ Polygon polygon = factory.createPolygon(lineString.getCoordinates());
+ MultiPolygon multiPolygon = factory.createMultiPolygon(new Polygon[]
{polygon, polygon});
+ String expected =
+
"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]],[[[1.0,1.0,1.0],[1.0,2.0,1.0],[2.0,2.0,2.0],[1.0,1.0,1.0]]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPolygon);
+ assertEquals(
+ "3D MultiPolygon should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testFeatureCollectionWithZConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new Coordinate(1, 1, 1));
+ org.wololo.geojson.Geometry json = writer.write(point);
+ Feature feature1 = new Feature(json, null);
+ Feature feature2 = new Feature(json, null);
+ FeatureCollection featureCollection = new FeatureCollection(new Feature[]
{feature1, feature2});
+ String expected =
+
"{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0]},\"properties\":null},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0]},\"properties\":null}]}";
+
+ // Test
+ assertEquals(
+ "3D FeatureCollection should be correctly converted to GeoJSON",
+ expected,
+ featureCollection.toString());
+ }
+
+ // XYZM Tests
+
+ @Test
+ public void testPointWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new CoordinateXYZM(1, 1, 1, 2));
+ String expected = "{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0,2.0]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(point);
+ assertEquals("XYZM Point should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testLineStringWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYZM(1, 1, 1, 10),
+ new CoordinateXYZM(1, 2, 1, 20),
+ new CoordinateXYZM(2, 2, 2, 30),
+ new CoordinateXYZM(1, 1, 1, 10)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+ String expected =
+
"{\"type\":\"LineString\",\"coordinates\":[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0],[2.0,2.0,2.0,30.0],[1.0,1.0,1.0,10.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(lineString);
+ assertEquals(
+ "XYZM LineString should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testPolygonWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYZM(1, 1, 1, 10),
+ new CoordinateXYZM(1, 2, 1, 20),
+ new CoordinateXYZM(2, 2, 2, 30),
+ new CoordinateXYZM(1, 1, 1, 10)
+ };
+ Polygon polygon = factory.createPolygon(coordinates);
+ String expected =
+
"{\"type\":\"Polygon\",\"coordinates\":[[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0],[2.0,2.0,2.0,30.0],[1.0,1.0,1.0,10.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(polygon);
+ assertEquals(
+ "XYZM Polygon should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testMultiPointWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYZM(1, 1, 1, 10),
+ new CoordinateXYZM(1, 2, 1, 20),
+ new CoordinateXYZM(2, 2, 2, 30)
+ };
+ MultiPoint multiPoint = factory.createMultiPointFromCoords(coordinates);
+ String expected =
+
"{\"type\":\"MultiPoint\",\"coordinates\":[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0],[2.0,2.0,2.0,30.0]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPoint);
+ assertEquals(
+ "XYZM MultiPoint should be correctly converted to GeoJSON", expected,
json.toString());
+ }
+
+ @Test
+ public void testMultiLineStringWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates1 =
+ new Coordinate[] {new CoordinateXYZM(1, 1, 1, 10), new
CoordinateXYZM(1, 2, 1, 20)};
+ Coordinate[] coordinates2 =
+ new Coordinate[] {new CoordinateXYZM(2, 2, 2, 30), new
CoordinateXYZM(3, 3, 3, 40)};
+ LineString lineString1 = factory.createLineString(coordinates1);
+ LineString lineString2 = factory.createLineString(coordinates2);
+ MultiLineString multiLineString =
+ factory.createMultiLineString(new LineString[] {lineString1,
lineString2});
+ String expected =
+
"{\"type\":\"MultiLineString\",\"coordinates\":[[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0]],[[2.0,2.0,2.0,30.0],[3.0,3.0,3.0,40.0]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiLineString);
+ assertEquals(
+ "XYZM MultiLineString should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testMultiPolygonWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYZM(1, 1, 1, 10),
+ new CoordinateXYZM(1, 2, 1, 20),
+ new CoordinateXYZM(2, 2, 2, 30),
+ new CoordinateXYZM(1, 1, 1, 10)
+ };
+ Polygon polygon = factory.createPolygon(coordinates);
+ MultiPolygon multiPolygon = factory.createMultiPolygon(new Polygon[]
{polygon, polygon});
+ String expected =
+
"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0],[2.0,2.0,2.0,30.0],[1.0,1.0,1.0,10.0]]],[[[1.0,1.0,1.0,10.0],[1.0,2.0,1.0,20.0],[2.0,2.0,2.0,30.0],[1.0,1.0,1.0,10.0]]]]}";
+
+ // Test
+ org.wololo.geojson.Geometry json = writer.write(multiPolygon);
+ assertEquals(
+ "XYZM MultiPolygon should be correctly converted to GeoJSON",
expected, json.toString());
+ }
+
+ @Test
+ public void testFeatureCollectionWithZMConversion() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data
+ Point point = factory.createPoint(new CoordinateXYZM(1, 1, 1, 10));
+ org.wololo.geojson.Geometry json = writer.write(point);
+ Feature feature1 = new Feature(json, null);
+ Feature feature2 = new Feature(json, null);
+ FeatureCollection featureCollection = new FeatureCollection(new Feature[]
{feature1, feature2});
+ String expected =
+
"{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0,10.0]},\"properties\":null},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0,1.0,10.0]},\"properties\":null}]}";
+
+ // Test
+ assertEquals(
+ "XYZM FeatureCollection should be correctly converted to GeoJSON",
+ expected,
+ featureCollection.toString());
+ }
+
+ // XYM Tests - These should fail as XYM is not supported
+
+ @Test
+ public void testPointWithMConversionFails() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data - XYM coordinate
+ Point point = factory.createPoint(new CoordinateXYM(1, 1, 10));
+
+ // Test - should throw UnsupportedOperationException
+ try {
+ writer.write(point);
+ fail("XYM coordinates should not be supported");
+ } catch (UnsupportedOperationException e) {
+ assertEquals(
+ "XYM coordinates are not supported. Please convert to XYZM
coordinates by adding a Z value.",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void testLineStringWithMConversionFails() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer =
+ new org.apache.sedona.common.jts2geojson.GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data - XYM coordinates
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYM(1, 1, 10), new CoordinateXYM(1, 2, 20), new
CoordinateXYM(2, 2, 30)
+ };
+ LineString lineString = factory.createLineString(coordinates);
+
+ // Test - should throw UnsupportedOperationException
+ try {
+ writer.write(lineString);
+ fail("XYM coordinates should not be supported");
+ } catch (UnsupportedOperationException e) {
+ assertEquals(
+ "XYM coordinates are not supported. Please convert to XYZM
coordinates by adding a Z value.",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void testPolygonWithMConversionFails() {
+ // Setup
+ org.apache.sedona.common.jts2geojson.GeoJSONWriter writer = new
GeoJSONWriter();
+ GeometryFactory factory = new GeometryFactory();
+
+ // Test data - XYM coordinates
+ Coordinate[] coordinates =
+ new Coordinate[] {
+ new CoordinateXYM(1, 1, 10),
+ new CoordinateXYM(1, 2, 20),
+ new CoordinateXYM(2, 2, 30),
+ new CoordinateXYM(1, 1, 10)
+ };
+ Polygon polygon = factory.createPolygon(coordinates);
+
+ // Test - should throw UnsupportedOperationException
+ try {
+ writer.write(polygon);
+ fail("XYM coordinates should not be supported");
+ } catch (UnsupportedOperationException e) {
+ assertEquals(
+ "XYM coordinates are not supported. Please convert to XYZM
coordinates by adding a Z value.",
+ e.getMessage());
+ }
+ }
+}
diff --git a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
index 5f4242b8d2..9732e4ba8e 100644
--- a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
@@ -34,6 +34,7 @@ import org.apache.flink.api.java.typeutils.RowTypeInfo;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.table.api.Table;
import org.apache.flink.types.Row;
+import org.apache.sedona.common.jts2geojson.GeoJSONReader;
import org.apache.sedona.flink.expressions.Constructors;
import org.apache.sedona.flink.expressions.Functions;
import org.junit.BeforeClass;
@@ -43,7 +44,6 @@ import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
-import org.wololo.jts2geojson.GeoJSONReader;
public class ConstructorTest extends TestBase {
diff --git a/flink/src/test/java/org/apache/sedona/flink/TestBase.java
b/flink/src/test/java/org/apache/sedona/flink/TestBase.java
index 4506d798e1..0ba5503f90 100644
--- a/flink/src/test/java/org/apache/sedona/flink/TestBase.java
+++ b/flink/src/test/java/org/apache/sedona/flink/TestBase.java
@@ -38,10 +38,10 @@ import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.types.Row;
import org.apache.flink.util.CloseableIterator;
+import org.apache.sedona.common.jts2geojson.GeoJSONWriter;
import org.apache.sedona.flink.expressions.Constructors;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
-import org.wololo.jts2geojson.GeoJSONWriter;
public class TestBase {
protected static StreamExecutionEnvironment env;
diff --git
a/spark/common/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
b/spark/common/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
index 8b8f3c96f3..2cfc661a64 100644
---
a/spark/common/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
+++
b/spark/common/src/main/java/org/apache/sedona/core/spatialRDD/SpatialRDD.java
@@ -28,6 +28,7 @@ import java.util.Map;
import org.apache.commons.lang.NullArgumentException;
import org.apache.log4j.Logger;
import org.apache.sedona.common.FunctionsGeoTools;
+import org.apache.sedona.common.jts2geojson.GeoJSONWriter;
import org.apache.sedona.common.utils.GeomUtils;
import org.apache.sedona.core.enums.GridType;
import org.apache.sedona.core.enums.IndexType;
@@ -56,7 +57,6 @@ import org.locationtech.jts.index.strtree.STRtree;
import org.locationtech.jts.io.WKBWriter;
import org.locationtech.jts.io.WKTWriter;
import org.wololo.geojson.Feature;
-import org.wololo.jts2geojson.GeoJSONWriter;
import scala.Tuple2;
// TODO: Auto-generated Javadoc
diff --git
a/spark/common/src/test/java/org/apache/sedona/core/utils/testGeoJSON.java
b/spark/common/src/test/java/org/apache/sedona/core/utils/testGeoJSON.java
index bc4be60830..2d80e7d983 100644
--- a/spark/common/src/test/java/org/apache/sedona/core/utils/testGeoJSON.java
+++ b/spark/common/src/test/java/org/apache/sedona/core/utils/testGeoJSON.java
@@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
+import org.apache.sedona.common.jts2geojson.GeoJSONWriter;
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaSparkContext;
import org.junit.AfterClass;
@@ -31,7 +32,6 @@ import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.wololo.geojson.Feature;
-import org.wololo.jts2geojson.GeoJSONWriter;
/**
* @author Arizona State University DataSystems Lab