Skip to content

Commit

Permalink
GH-162 handle empty point / shape collection in WKT, GeoJSON (#183)
Browse files Browse the repository at this point in the history
* GH-162 handle empty point / shape collection in WKT

Signed-off-by: Jeen Broekstra <jeen.broekstra@gmail.com>

* Fix GeoJson too.  Test round-trip.

Co-authored-by: David Smiley <david.w.smiley@gmail.com>
  • Loading branch information
abrokenjester and dsmiley committed Mar 20, 2020
1 parent 5d8bac2 commit 6a43828
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 18 deletions.
5 changes: 4 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ DATE: unreleased
equidistant from the center. Before the change, there was potentially a large inaccuracy.
(Hrishi Bakshi)

* \#163: EMPTY points in JTS are now convertible to a Spatial4j Shape instead of throwing an exception.
* \#163: "Empty" points in JTS are now convertible to a Spatial4j Shape instead of throwing an exception.
(David Smiley)

* \#162: Fixed WKT & GeoJSON \[de\]serialization of "empty" points and geometrycollections.
(Jeen Broekstra, David Smiley)

## VERSION 0.7

DATE: 27 December 2017
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,6 @@ protected Shape readShape(JSONParser parser) throws IOException, ParseException
}
sub = parser.nextEvent();
}
if (shapes.isEmpty()) {
throw new ParseException("Shape Collection with no geometries!",
(int) parser.getPosition());
}
return ctx.makeCollection(shapes);
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ public String getFormatName() {
protected void write(Writer output, NumberFormat nf, double... coords) throws IOException {
output.write('[');
for (int i = 0; i < coords.length; i++) {
if (Double.isNaN(coords[i])) {
break; // empty point or no more coordinates
}
if (i > 0) {
output.append(',');
}
Expand Down
27 changes: 17 additions & 10 deletions src/main/java/org/locationtech/spatial4j/io/WKTWriter.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,14 @@

package org.locationtech.spatial4j.io;

import org.locationtech.spatial4j.shape.Circle;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.Rectangle;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.ShapeCollection;
import org.locationtech.spatial4j.shape.impl.BufferedLine;
import org.locationtech.spatial4j.shape.impl.BufferedLineString;

import java.io.IOException;
import java.io.Writer;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Iterator;
import org.locationtech.spatial4j.shape.*;
import org.locationtech.spatial4j.shape.impl.BufferedLine;
import org.locationtech.spatial4j.shape.impl.BufferedLineString;

public class WKTWriter implements ShapeWriter {

Expand All @@ -42,8 +37,13 @@ protected NumberFormat getNumberFormat() {
public String toString(Shape shape) {
NumberFormat nf = getNumberFormat();
if (shape instanceof Point) {
Point point = (Point)shape;
if (point.isEmpty()) {
return "POINT EMPTY";
}

StringBuilder buffer = new StringBuilder();
return append(buffer.append("POINT ("),(Point)shape,nf).append(")").toString();
return append(buffer.append("POINT ("), point, nf).append(")").toString();
}
if (shape instanceof Rectangle) {
NumberFormat nfMIN = nf;
Expand Down Expand Up @@ -103,10 +103,17 @@ public String toString(Shape shape) {
return str.toString();
}
if(shape instanceof ShapeCollection) {
@SuppressWarnings("unchecked")
ShapeCollection<? extends Shape> collection = (ShapeCollection<? extends Shape>) shape;

if (collection.isEmpty()) {
return "GEOMETRYCOLLECTION EMPTY";
}

StringBuilder buffer = new StringBuilder();
buffer.append("GEOMETRYCOLLECTION (");
boolean first = true;
for(Shape sub : ((ShapeCollection<? extends Shape>)shape).getShapes()) {
for (Shape sub : collection.getShapes()) {
if(!first) {
buffer.append(",");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ public class ShapeAsGeoJSONSerializer extends JsonSerializer<Shape>

protected void write(JsonGenerator gen, double... coords) throws IOException {
gen.writeStartArray();
for (int i = 0; i < coords.length; i++) {
gen.writeNumber(coords[i]);
for (double coord : coords) {
if (Double.isNaN(coord)) {
break; // empty
}
gen.writeNumber(coord);
}
gen.writeEndArray();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public ShapeDeserializer(SpatialContext ctx) {
}

public Point readPoint(ArrayNode arr, ShapeFactory factory) {
if(arr.size()==0) {
return factory.pointXY(Double.NaN, Double.NaN);
}
double x = arr.get(0).asDouble();
double y = arr.get(1).asDouble();
if(arr.size()==3) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;

public class GeneralPolyshapeTest extends GeneralReadWriteShapeTest {

Expand Down Expand Up @@ -61,4 +62,9 @@ public boolean shouldBeEqualAfterRoundTrip() {
return false; // the polyline values will be off by a small fraction -- everything is rounded to: Math.round(value * 1e5)
}

@Override
@Ignore
public void testEmptyGeometryCollection() throws Exception {
assumeTrue(false); // not supported
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.locationtech.spatial4j.util.GeomBuilder;

import java.util.Arrays;
import java.util.Collections;

public abstract class GeneralReadWriteShapeTest extends BaseRoundTripTest<JtsSpatialContext> {

Expand Down Expand Up @@ -70,7 +71,17 @@ protected void assertRoundTrip(Shape shape, boolean andEquals) throws Exception
Assert.assertEquals(shape, out);
}
}


@Test
public void testEmptyPoint() throws Exception {
assertRoundTrip(ctx.getShapeFactory().pointXY(Double.NaN, Double.NaN));
}

@Test
public void testEmptyGeometryCollection() throws Exception {
assertRoundTrip(ctx.getShapeFactory().multiShape(Collections.<Shape>emptyList()));
}

@Test
public void testWriteThenReadPoint() throws Exception {
assertRoundTrip(point());
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/org/locationtech/spatial4j/io/WKTWriterTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.locationtech.spatial4j.io;

import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import org.junit.Test;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.Point;
import org.locationtech.spatial4j.shape.ShapeCollection;

public class WKTWriterTest {

private SpatialContext ctx;

protected WKTWriterTest(SpatialContext ctx) {
this.ctx = ctx;
}

public WKTWriterTest() {
this(SpatialContext.GEO);
}

@Test
public void testToStringOnEmptyPoint() throws Exception {
ShapeWriter writer = ctx.getFormats().getWktWriter();
Point emptyPoint = ctx.makePoint(Double.NaN, Double.NaN);

assertEquals("POINT EMPTY", writer.toString(emptyPoint));
}

@Test
public void testToStringOnEmptyShapeCollection() throws Exception {
ShapeWriter writer = ctx.getFormats().getWktWriter();
ShapeCollection<Point> emptyCollection = ctx.makeCollection(new ArrayList<Point>());

assertEquals("GEOMETRYCOLLECTION EMPTY", writer.toString(emptyCollection));
}
}

0 comments on commit 6a43828

Please sign in to comment.