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

jiayu pushed a commit to branch geojson-m
in repository https://gitbox.apache.org/repos/asf/sedona.git

commit 6b251a596958a603341d002196ceced12b95a69d
Author: Jia Yu <ji...@apache.org>
AuthorDate: Sat Aug 16 14:50:08 2025 -0700

    Add geojson m support in Java
---
 common/pom.xml                                     |   4 -
 .../src/main/java/org/wololo/geojson/Feature.java  |  64 +++
 .../java/org/wololo/geojson/FeatureCollection.java |  38 ++
 .../src/main/java/org/wololo/geojson/GeoJSON.java  |  75 +++
 .../java/org/wololo/geojson/GeoJSONFactory.java    |  73 +++
 .../src/main/java/org/wololo/geojson/Geometry.java |  49 ++
 .../org/wololo/geojson/GeometryCollection.java     |  38 ++
 .../main/java/org/wololo/geojson/LineString.java   |  45 ++
 .../java/org/wololo/geojson/MultiLineString.java   |  45 ++
 .../main/java/org/wololo/geojson/MultiPoint.java   |  45 ++
 .../main/java/org/wololo/geojson/MultiPolygon.java |  45 ++
 common/src/main/java/org/wololo/geojson/Point.java |  45 ++
 .../src/main/java/org/wololo/geojson/Polygon.java  |  45 ++
 .../java/org/wololo/jts2geojson/GeoJSONReader.java | 129 +++++
 .../java/org/wololo/jts2geojson/GeoJSONWriter.java | 122 ++++
 .../src/test/java/org/wololo/jts2geojson/Data.java |  26 +
 .../java/org/wololo/jts2geojson/DataFactory.java   |  35 ++
 .../DeserializeGeometryCollectionTest.java         |  70 +++
 .../org/wololo/jts2geojson/GeoJSONFactoryTest.java | 140 +++++
 .../org/wololo/jts2geojson/GeoJSONReaderTest.java  | 291 ++++++++++
 .../org/wololo/jts2geojson/GeoJSONWriterTest.java  | 636 +++++++++++++++++++++
 pom.xml                                            |  12 -
 spark/common/pom.xml                               |  10 -
 spark/spark-3.4/pom.xml                            |  10 -
 spark/spark-3.5/pom.xml                            |  10 -
 spark/spark-4.0/pom.xml                            |  10 -
 26 files changed, 2056 insertions(+), 56 deletions(-)

diff --git a/common/pom.xml b/common/pom.xml
index 3108ecf99c..336ad44b97 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -73,10 +73,6 @@
             <groupId>org.locationtech.jts</groupId>
             <artifactId>jts-core</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-        </dependency>
         <dependency>
             <groupId>org.locationtech.spatial4j</groupId>
             <artifactId>spatial4j</artifactId>
diff --git a/common/src/main/java/org/wololo/geojson/Feature.java 
b/common/src/main/java/org/wololo/geojson/Feature.java
new file mode 100644
index 0000000000..86f6de6114
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/Feature.java
@@ -0,0 +1,64 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import java.util.Map;
+
+@JsonPropertyOrder({"type", "id", "geometry", "properties"})
+public class Feature extends GeoJSON {
+  @JsonInclude(Include.NON_EMPTY)
+  private final Object id;
+
+  private final Geometry geometry;
+  private final Map<String, Object> properties;
+
+  public Feature(
+      @JsonProperty("geometry") Geometry geometry,
+      @JsonProperty("properties") Map<String, Object> properties) {
+    this(null, geometry, properties);
+  }
+
+  @JsonCreator
+  public Feature(
+      @JsonProperty("id") Object id,
+      @JsonProperty("geometry") Geometry geometry,
+      @JsonProperty("properties") Map<String, Object> properties) {
+    super();
+    this.id = id;
+    this.geometry = geometry;
+    this.properties = properties;
+  }
+
+  public Object getId() {
+    return id;
+  }
+
+  public Geometry getGeometry() {
+    return geometry;
+  }
+
+  public Map<String, Object> getProperties() {
+    return properties;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/FeatureCollection.java 
b/common/src/main/java/org/wololo/geojson/FeatureCollection.java
new file mode 100644
index 0000000000..df5d58c641
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/FeatureCollection.java
@@ -0,0 +1,38 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonPropertyOrder({"type", "features"})
+public class FeatureCollection extends GeoJSON {
+  private final Feature[] features;
+
+  @JsonCreator
+  public FeatureCollection(@JsonProperty("features") Feature[] features) {
+    super();
+    this.features = features;
+  }
+
+  public Feature[] getFeatures() {
+    return features;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/GeoJSON.java 
b/common/src/main/java/org/wololo/geojson/GeoJSON.java
new file mode 100644
index 0000000000..db5a30746f
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/GeoJSON.java
@@ -0,0 +1,75 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    include = JsonTypeInfo.As.EXISTING_PROPERTY,
+    property = "type")
+@JsonSubTypes({
+  @JsonSubTypes.Type(value = Point.class, name = "Point"),
+  @JsonSubTypes.Type(value = LineString.class, name = "LineString"),
+  @JsonSubTypes.Type(value = Polygon.class, name = "Polygon"),
+  @JsonSubTypes.Type(value = MultiPoint.class, name = "MultiPoint"),
+  @JsonSubTypes.Type(value = MultiLineString.class, name = "MultiLineString"),
+  @JsonSubTypes.Type(value = MultiPolygon.class, name = "MultiPolygon"),
+  @JsonSubTypes.Type(value = Feature.class, name = "Feature"),
+  @JsonSubTypes.Type(value = FeatureCollection.class, name = 
"FeatureCollection"),
+  @JsonSubTypes.Type(value = GeometryCollection.class, name = 
"GeometryCollection")
+})
+public abstract class GeoJSON {
+  private static final ObjectMapper mapper = new ObjectMapper();
+
+  @JsonProperty("type")
+  private String type;
+
+  @JsonCreator
+  public GeoJSON() {
+    setType(getClass().getSimpleName());
+  }
+
+  public String toString() {
+    try {
+      return mapper.writeValueAsString(this);
+    } catch (JsonGenerationException e) {
+      return "Unhandled exception occurred when serializing this instance";
+    } catch (JsonMappingException e) {
+      return "Unhandled exception occurred when serializing this instance";
+    } catch (IOException e) {
+      return "Unhandled exception occurred when serializing this instance";
+    }
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/GeoJSONFactory.java 
b/common/src/main/java/org/wololo/geojson/GeoJSONFactory.java
new file mode 100644
index 0000000000..bb0bc8acdb
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/GeoJSONFactory.java
@@ -0,0 +1,73 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Map;
+
+public class GeoJSONFactory {
+  private static final ObjectMapper mapper = new ObjectMapper();
+
+  public static GeoJSON create(String json) {
+    try {
+      var node = mapper.readTree(json);
+      var type = node.get("type").asText();
+      if (type.equals("FeatureCollection")) return readFeatureCollection(node);
+      else if (type.equals("Feature")) return readFeature(node);
+      else return readGeometry(node, type);
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  private static FeatureCollection readFeatureCollection(JsonNode node)
+      throws JsonParseException, JsonMappingException, IOException, 
ClassNotFoundException {
+    var it = node.get("features").iterator();
+    var features = new ArrayList<Feature>();
+    while (it.hasNext()) features.add(readFeature(it.next()));
+    return new FeatureCollection(features.toArray(new 
Feature[features.size()]));
+  }
+
+  private static Feature readFeature(JsonNode node)
+      throws JsonParseException, JsonMappingException, IOException, 
ClassNotFoundException {
+    var geometryNode = node.get("geometry");
+    var javaType = mapper.getTypeFactory().constructMapType(Map.class, 
String.class, Object.class);
+    var id = node.get("id");
+    Map<String, Object> properties = 
mapper.readValue(node.get("properties").traverse(), javaType);
+    var geometry = readGeometry(geometryNode);
+    return new Feature(id, geometry, properties);
+  }
+
+  private static Geometry readGeometry(JsonNode node)
+      throws JsonParseException, JsonMappingException, IOException, 
ClassNotFoundException {
+    if (node != null && !node.isNull()) return readGeometry(node, 
node.get("type").asText());
+    else return null;
+  }
+
+  private static Geometry readGeometry(JsonNode node, String type)
+      throws JsonParseException, JsonMappingException, IOException, 
ClassNotFoundException {
+    return (Geometry)
+        mapper.readValue(node.traverse(), Class.forName("org.wololo.geojson." 
+ type));
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/Geometry.java 
b/common/src/main/java/org/wololo/geojson/Geometry.java
new file mode 100644
index 0000000000..070ec8d23f
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/Geometry.java
@@ -0,0 +1,49 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+@JsonTypeInfo(
+    use = JsonTypeInfo.Id.NAME,
+    include = JsonTypeInfo.As.EXISTING_PROPERTY,
+    property = "type")
+@JsonSubTypes({
+  @JsonSubTypes.Type(value = Point.class, name = "Point"),
+  @JsonSubTypes.Type(value = LineString.class, name = "LineString"),
+  @JsonSubTypes.Type(value = Polygon.class, name = "Polygon"),
+  @JsonSubTypes.Type(value = MultiPoint.class, name = "MultiPoint"),
+  @JsonSubTypes.Type(value = MultiLineString.class, name = "MultiLineString"),
+  @JsonSubTypes.Type(value = MultiPolygon.class, name = "MultiPolygon"),
+  @JsonSubTypes.Type(value = Feature.class, name = "Feature"),
+  @JsonSubTypes.Type(value = FeatureCollection.class, name = 
"FeatureCollection"),
+  @JsonSubTypes.Type(value = GeometryCollection.class, name = 
"GeometryCollection")
+})
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({"type", "coordinates", "bbox"})
+public abstract class Geometry extends GeoJSON {
+  @JsonCreator
+  public Geometry() {
+    super();
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/GeometryCollection.java 
b/common/src/main/java/org/wololo/geojson/GeometryCollection.java
new file mode 100644
index 0000000000..f22e6c114b
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/GeometryCollection.java
@@ -0,0 +1,38 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+
+@JsonPropertyOrder({"type", "geometries"})
+public class GeometryCollection extends Geometry {
+  private final Geometry[] geometries;
+
+  @JsonCreator
+  public GeometryCollection(@JsonProperty("geometries") Geometry[] geometries) 
{
+    super();
+    this.geometries = geometries;
+  }
+
+  public Geometry[] getGeometries() {
+    return geometries;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/LineString.java 
b/common/src/main/java/org/wololo/geojson/LineString.java
new file mode 100644
index 0000000000..d798a47cb5
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/LineString.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class LineString extends Geometry {
+  private final double[][] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public LineString(@JsonProperty("coordinates") double[][] coordinates) {
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[][] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/MultiLineString.java 
b/common/src/main/java/org/wololo/geojson/MultiLineString.java
new file mode 100644
index 0000000000..1c89ad406c
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/MultiLineString.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class MultiLineString extends Geometry {
+  private final double[][][] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public MultiLineString(@JsonProperty("coordinates") double[][][] 
coordinates) {
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[][][] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/MultiPoint.java 
b/common/src/main/java/org/wololo/geojson/MultiPoint.java
new file mode 100644
index 0000000000..495e77cf70
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/MultiPoint.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class MultiPoint extends Geometry {
+  private final double[][] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public MultiPoint(@JsonProperty("coordinates") double[][] coordinates) {
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[][] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/MultiPolygon.java 
b/common/src/main/java/org/wololo/geojson/MultiPolygon.java
new file mode 100644
index 0000000000..3513ff95a2
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/MultiPolygon.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class MultiPolygon extends Geometry {
+  private final double[][][][] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public MultiPolygon(@JsonProperty("coordinates") double[][][][] coordinates) 
{
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[][][][] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/Point.java 
b/common/src/main/java/org/wololo/geojson/Point.java
new file mode 100644
index 0000000000..b2cf6b75b3
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/Point.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class Point extends Geometry {
+  private final double[] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public Point(@JsonProperty("coordinates") double[] coordinates) {
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/geojson/Polygon.java 
b/common/src/main/java/org/wololo/geojson/Polygon.java
new file mode 100644
index 0000000000..2ee5660868
--- /dev/null
+++ b/common/src/main/java/org/wololo/geojson/Polygon.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wololo.geojson;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonInclude(Include.NON_NULL)
+public class Polygon extends Geometry {
+  private final double[][][] coordinates;
+  private final double[] bbox;
+
+  @JsonCreator
+  public Polygon(@JsonProperty("coordinates") double[][][] coordinates) {
+    super();
+    this.coordinates = coordinates;
+    this.bbox = null;
+  }
+
+  public double[][][] getCoordinates() {
+    return coordinates;
+  }
+
+  public double[] getBbox() {
+    return bbox;
+  }
+}
diff --git a/common/src/main/java/org/wololo/jts2geojson/GeoJSONReader.java 
b/common/src/main/java/org/wololo/jts2geojson/GeoJSONReader.java
new file mode 100644
index 0000000000..b7a6b7a8d2
--- /dev/null
+++ b/common/src/main/java/org/wololo/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.wololo.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/wololo/jts2geojson/GeoJSONWriter.java 
b/common/src/main/java/org/wololo/jts2geojson/GeoJSONWriter.java
new file mode 100644
index 0000000000..a407f7ec32
--- /dev/null
+++ b/common/src/main/java/org/wololo/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.wololo.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/test/java/org/wololo/jts2geojson/Data.java 
b/common/src/test/java/org/wololo/jts2geojson/Data.java
new file mode 100644
index 0000000000..396067ddcc
--- /dev/null
+++ b/common/src/test/java/org/wololo/jts2geojson/Data.java
@@ -0,0 +1,26 @@
+/*
+ * 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.wololo.jts2geojson;
+
+import org.wololo.geojson.GeoJSON;
+
+class Data {
+
+  public GeoJSON geometry;
+}
diff --git a/common/src/test/java/org/wololo/jts2geojson/DataFactory.java 
b/common/src/test/java/org/wololo/jts2geojson/DataFactory.java
new file mode 100644
index 0000000000..357f56ebdc
--- /dev/null
+++ b/common/src/test/java/org/wololo/jts2geojson/DataFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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.wololo.jts2geojson;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+class DataFactory {
+
+  private static final ObjectMapper mapper = new ObjectMapper();
+
+  public static Data fromJson(String json) {
+    try {
+      return mapper.readerFor(Data.class).readValue(json);
+    } catch (JsonProcessingException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git 
a/common/src/test/java/org/wololo/jts2geojson/DeserializeGeometryCollectionTest.java
 
b/common/src/test/java/org/wololo/jts2geojson/DeserializeGeometryCollectionTest.java
new file mode 100644
index 0000000000..4c18a060fe
--- /dev/null
+++ 
b/common/src/test/java/org/wololo/jts2geojson/DeserializeGeometryCollectionTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.wololo.jts2geojson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.wololo.geojson.GeoJSON;
+import org.wololo.geojson.GeoJSONFactory;
+import org.wololo.geojson.Geometry;
+import org.wololo.geojson.GeometryCollection;
+import org.wololo.geojson.Point;
+
+public class DeserializeGeometryCollectionTest {
+
+  @Test
+  public void testDeserializeGeometryCollectionSuccessfully() {
+    // Setup test data
+    String geoJSON =
+        "{\"type\": \"GeometryCollection\", \"geometries\": [{\"type\": 
\"Point\", \"coordinates\": [1.1, 2.2]}]}";
+
+    // Execute test
+    GeoJSON json = GeoJSONFactory.create(geoJSON);
+
+    // Verify results
+    assertTrue("Should be a GeometryCollection", json instanceof 
GeometryCollection);
+    GeometryCollection gc = (GeometryCollection) json;
+    assertTrue("Should have geometries", gc.getGeometries().length > 0);
+
+    Geometry g = gc.getGeometries()[0];
+    assertNotNull("Geometry should not be null", g);
+    assertTrue("Should be a Point", g instanceof Point);
+
+    Point point = (Point) g;
+    double[] coordinates = point.getCoordinates();
+    assertEquals("X coordinate should match", 1.1, coordinates[0], 0.000001);
+    assertEquals("Y coordinate should match", 2.2, coordinates[1], 0.000001);
+  }
+
+  @Test
+  public void 
testDeserializeGeometryCollectionSuccessfullyIfUsingObjectMapper() {
+    // Setup test data
+    String geoJSON =
+        "{ \"geometry\" : {\"type\": \"GeometryCollection\", \"geometries\": 
[{\"type\": \"Point\", \"coordinates\": [1.1, 2.2]}]}}";
+
+    // Execute test
+    Data value = DataFactory.fromJson(geoJSON);
+
+    // Verify results
+    assertTrue("Should be a Data object", value instanceof Data);
+  }
+}
diff --git 
a/common/src/test/java/org/wololo/jts2geojson/GeoJSONFactoryTest.java 
b/common/src/test/java/org/wololo/jts2geojson/GeoJSONFactoryTest.java
new file mode 100644
index 0000000000..b1dd3676c9
--- /dev/null
+++ b/common/src/test/java/org/wololo/jts2geojson/GeoJSONFactoryTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.wololo.jts2geojson;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import org.junit.Test;
+import org.wololo.geojson.Feature;
+import org.wololo.geojson.FeatureCollection;
+import org.wololo.geojson.GeoJSON;
+import org.wololo.geojson.GeoJSONFactory;
+import org.wololo.geojson.Geometry;
+import org.wololo.geojson.Point;
+
+public class GeoJSONFactoryTest {
+
+  @Test
+  public void testReadMissingGeometryFieldAsNull() {
+    String json = "{\"type\":\"Feature\",\"properties\":{\"test\":1}}";
+    String expected = 
"{\"type\":\"Feature\",\"geometry\":null,\"properties\":{\"test\":1}}";
+
+    GeoJSON geojson = GeoJSONFactory.create(json);
+    assertEquals(expected, geojson.toString());
+  }
+
+  @Test
+  public void testIdenticalToProgrammaticallyCreatedFeature() {
+    Geometry geometry = new Point(new double[] {1, 1});
+    HashMap<String, Object> properties = new HashMap<>();
+    properties.put("test", 1);
+    Feature feature = new Feature(geometry, properties);
+
+    String expected =
+        
"{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":{\"test\":1}}";
+
+    String json = feature.toString();
+    assertEquals(expected, json);
+
+    GeoJSON geoJSON = GeoJSONFactory.create(json);
+    assertEquals(expected, geoJSON.toString());
+  }
+
+  @Test
+  public void testIdenticalToProgrammaticallyCreatedFeatureWithId() {
+    Geometry geometry = new Point(new double[] {1, 1});
+    HashMap<String, Object> properties = new HashMap<>();
+    properties.put("test", 1);
+    Feature feature = new Feature(1, geometry, properties);
+
+    String expected =
+        
"{\"type\":\"Feature\",\"id\":1,\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":{\"test\":1}}";
+
+    String json = feature.toString();
+    assertEquals(expected, json);
+
+    GeoJSON geoJSON = GeoJSONFactory.create(json);
+    assertEquals(expected, geoJSON.toString());
+  }
+
+  @Test
+  public void testIdenticalToProgrammaticallyCreatedFeatureWithoutGeometry() {
+    HashMap<String, Object> properties = new HashMap<>();
+    properties.put("test", 1);
+    Feature feature = new Feature(null, properties);
+
+    String expected = 
"{\"type\":\"Feature\",\"geometry\":null,\"properties\":{\"test\":1}}";
+
+    String json = feature.toString();
+    assertEquals(expected, json);
+
+    GeoJSON geoJSON = GeoJSONFactory.create(json);
+    assertEquals(expected, geoJSON.toString());
+  }
+
+  @Test
+  public void testIdenticalToProgrammaticallyCreatedFeatureCollection() {
+    Geometry geometry = new Point(new double[] {1, 1});
+    HashMap<String, Object> properties = new HashMap<>();
+    properties.put("test", 1);
+    Feature feature = new Feature(geometry, properties);
+
+    String expected =
+        
"{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":{\"test\":1}},{\"type\":\"Feature\",\"geometry\":{\"type\":\"Point\",\"coordinates\":[1.0,1.0]},\"properties\":{\"test\":1}}]}";
+
+    FeatureCollection fc = new FeatureCollection(new Feature[] {feature, 
feature});
+    assertEquals(expected, fc.toString());
+  }
+
+  @Test
+  public void testTakeCareOfBboxProperty() {
+    String geoJSON =
+        "{\"type\": \"FeatureCollection\", \"features\": [{\"type\": 
\"Feature\", \"id\": 1, \"geometry\": {\"type\": \"Point\", \"coordinates\": 
[-8.311419016226296, 53.894485921596285], \"bbox\": [-8.311419016226296, 
53.894485921596285, -8.311419016226296, 53.894485921596285] }, \"properties\": 
{\"FID\": 335, \"PLAN_REF\": \"151\", \"APP_TYPE\": \"RETENTION\", 
\"LOCATION\": \"Knockglass, Ballinameen, Co. Roscommon.\", \"REC_DATE\": 
\"05/01/2015\", \"DESCRIPT\": \"Of entrance to existin [...]
+    GeoJSON json = GeoJSONFactory.create(geoJSON);
+    assertTrue(json instanceof FeatureCollection);
+    FeatureCollection fc = (FeatureCollection) json;
+    assertTrue(fc.getFeatures().length > 0);
+    Feature f = fc.getFeatures()[0];
+    assertNotNull(f.getGeometry());
+    assertTrue(f.getGeometry() instanceof Point);
+    Point point = (Point) f.getGeometry();
+    assertNotNull(point.getBbox());
+
+    double[] expected = {
+      -8.311419016226296, 53.894485921596285, -8.311419016226296, 
53.894485921596285
+    };
+    double[] actual = point.getBbox();
+    assertEquals(expected.length, actual.length);
+    for (int i = 0; i < expected.length; i++) {
+      assertEquals(expected[i], actual[i], 1e-10);
+    }
+  }
+
+  @Test
+  public void testTakeCareOfMultiPointBboxProperty() {
+    String geoJSON =
+        "{\"type\": \"FeatureCollection\", \"features\": [{\"type\": 
\"Feature\", \"id\": 1, \"geometry\": {\"type\": \"MultiPoint\", 
\"coordinates\": [[-8.311419016226296, 53.894485921596285]], \"bbox\": 
[-8.311419016226296, 53.894485921596285, -8.311419016226296, 
53.894485921596285] }, \"properties\": {\"FID\": 335, \"PLAN_REF\": \"151\", 
\"APP_TYPE\": \"RETENTION\", \"LOCATION\": \"Knockglass, Ballinameen, Co. 
Roscommon.\", \"REC_DATE\": \"05/01/2015\", \"DESCRIPT\": \"Of entrance to  
[...]
+    GeoJSON json = GeoJSONFactory.create(geoJSON);
+    assertTrue(json instanceof FeatureCollection);
+    FeatureCollection fc = (FeatureCollection) json;
+  }
+}
diff --git a/common/src/test/java/org/wololo/jts2geojson/GeoJSONReaderTest.java 
b/common/src/test/java/org/wololo/jts2geojson/GeoJSONReaderTest.java
new file mode 100644
index 0000000000..a4cd594227
--- /dev/null
+++ b/common/src/test/java/org/wololo/jts2geojson/GeoJSONReaderTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.wololo.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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    GeoJSONReader reader = new 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() {
+    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/wololo/jts2geojson/GeoJSONWriterTest.java 
b/common/src/test/java/org/wololo/jts2geojson/GeoJSONWriterTest.java
new file mode 100644
index 0000000000..28e8038582
--- /dev/null
+++ b/common/src/test/java/org/wololo/jts2geojson/GeoJSONWriterTest.java
@@ -0,0 +1,636 @@
+/*
+ * 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.wololo.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
+    GeoJSONReader reader = new GeoJSONReader();
+    GeoJSONWriter writer = new 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
+    GeoJSONReader reader = new GeoJSONReader();
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONReader reader = new GeoJSONReader();
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    GeoJSONWriter writer = new 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
+    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)
+        };
+    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
+    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/pom.xml b/pom.xml
index 7865db02ed..f9bcc088d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -72,7 +72,6 @@
         <hadoop.version>3.2.4</hadoop.version>
         <jackson.version>2.13.4</jackson.version>
         <jts.version>1.20.0</jts.version>
-        <jts2geojson.version>0.16.1</jts2geojson.version>
         <spatial4j.version>0.8</spatial4j.version>
 
         <jt-jiffle.version>1.1.24</jt-jiffle.version>
@@ -139,17 +138,6 @@
 
     <dependencyManagement>
         <dependencies>
-            <dependency>
-                <groupId>org.wololo</groupId>
-                <artifactId>jts2geojson</artifactId>
-                <version>${jts2geojson.version}</version>
-                <exclusions>
-                    <exclusion>
-                        <groupId>org.locationtech.jts</groupId>
-                        <artifactId>jts-core</artifactId>
-                    </exclusion>
-                </exclusions>
-            </dependency>
             <dependency>
                 <groupId>org.locationtech.jts</groupId>
                 <artifactId>jts-core</artifactId>
diff --git a/spark/common/pom.xml b/spark/common/pom.xml
index a910d74480..d02fbacb5a 100644
--- a/spark/common/pom.xml
+++ b/spark/common/pom.xml
@@ -174,16 +174,6 @@
             <groupId>org.locationtech.jts</groupId>
             <artifactId>jts-core</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.fasterxml.jackson.core</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>io.graphframes</groupId>
             
<artifactId>graphframes-spark${spark.major.version}_${scala.compat.version}</artifactId>
diff --git a/spark/spark-3.4/pom.xml b/spark/spark-3.4/pom.xml
index 6b5fe78a4d..c809010258 100644
--- a/spark/spark-3.4/pom.xml
+++ b/spark/spark-3.4/pom.xml
@@ -106,16 +106,6 @@
             <groupId>org.locationtech.jts</groupId>
             <artifactId>jts-core</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.fasterxml.jackson.core</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>org.scala-lang</groupId>
             <artifactId>scala-library</artifactId>
diff --git a/spark/spark-3.5/pom.xml b/spark/spark-3.5/pom.xml
index f9478f1d96..6aafda49b5 100644
--- a/spark/spark-3.5/pom.xml
+++ b/spark/spark-3.5/pom.xml
@@ -106,16 +106,6 @@
             <groupId>org.locationtech.jts</groupId>
             <artifactId>jts-core</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.fasterxml.jackson.core</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>org.scala-lang</groupId>
             <artifactId>scala-library</artifactId>
diff --git a/spark/spark-4.0/pom.xml b/spark/spark-4.0/pom.xml
index ee4b30724c..d2635f0976 100644
--- a/spark/spark-4.0/pom.xml
+++ b/spark/spark-4.0/pom.xml
@@ -106,16 +106,6 @@
             <groupId>org.locationtech.jts</groupId>
             <artifactId>jts-core</artifactId>
         </dependency>
-        <dependency>
-            <groupId>org.wololo</groupId>
-            <artifactId>jts2geojson</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>com.fasterxml.jackson.core</groupId>
-                    <artifactId>*</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
         <dependency>
             <groupId>org.scala-lang</groupId>
             <artifactId>scala-library</artifactId>


Reply via email to