Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-162 handle empty point / shape collection in toString #183

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
}
}