Skip to content

Commit

Permalink
Added support for parsing geoJson
Browse files Browse the repository at this point in the history
  • Loading branch information
BrynCooke committed Jan 30, 2015
1 parent cab73fe commit a2b4755
Show file tree
Hide file tree
Showing 2 changed files with 239 additions and 12 deletions.
@@ -1,6 +1,7 @@
package com.thinkaurelius.titan.core.attribute;

import com.google.common.base.Preconditions;
import com.google.common.primitives.Doubles;
import com.spatial4j.core.context.SpatialContext;
import com.spatial4j.core.distance.DistanceUtils;
import com.spatial4j.core.shape.Shape;
Expand All @@ -11,8 +12,7 @@
import org.apache.commons.lang.builder.HashCodeBuilder;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -356,15 +356,13 @@ public void verifyAttribute(Geoshape value) {

@Override
public Geoshape convert(Object value) {

if(value instanceof Map) {
return convertGeoJson(value);
}

if(value instanceof Collection) {
Collection<Object> c = (Collection) value;
List<Double> numbers = c.stream().map(o -> {
if (!(o instanceof Number)) {
throw new IllegalArgumentException("Collections may only contain numbers to create a Geoshape");
}
return ((Number)o).doubleValue();
}).collect(Collectors.toList());
value = numbers.toArray(new Double[numbers.size()]);
value = convertCollection((Collection<Object>) value);
}

if (value.getClass().isArray() && (value.getClass().getComponentType().isPrimitive() ||
Expand Down Expand Up @@ -398,6 +396,84 @@ public Geoshape convert(Object value) {
} else return null;
}


private double[] convertCollection(Collection<Object> c) {

List<Double> numbers = c.stream().map(o -> {
if (!(o instanceof Number)) {
throw new IllegalArgumentException("Collections may only contain numbers to create a Geoshape");
}
return ((Number) o).doubleValue();
}).collect(Collectors.toList());
return Doubles.toArray(numbers);
}

private Geoshape convertGeoJson(Object value) {
//Note that geoJson is long,lat
try {
Map<String, Object> map = (Map) value;
String type = (String) map.get("type");
if("Point".equals(type) || "Circle".equals(type) || "Polygon".equals(type)) {
return convertGeometry(map);
}
else if("Feature".equals(type)) {
Map<String, Object> geometry = (Map) map.get("geometry");
return convertGeometry(geometry);
}
throw new IllegalArgumentException("Only Point, Circle, Polygon or feature types are supported");
} catch (ClassCastException e) {
throw new IllegalArgumentException("GeoJSON was unparsable");
}

}

private Geoshape convertGeometry(Map<String, Object> geometry) {
String type = (String) geometry.get("type");
List<Object> coordinates = (List) geometry.get("coordinates");
//Either this is a single point or a collection of points

if ("Point".equals(type)) {
double[] parsedCoordinates = convertCollection(coordinates);
return point(parsedCoordinates[1], parsedCoordinates[0]);
} else if ("Circle".equals(type)) {
Number radius = (Number) geometry.get("radius");
if (radius == null) {
throw new IllegalArgumentException("GeoJSON circles require a radius");
}
double[] parsedCoordinates = convertCollection(coordinates);
return circle(parsedCoordinates[1], parsedCoordinates[0], radius.doubleValue());
} else if ("Polygon".equals(type)) {
if (coordinates.size() != 4) {
throw new IllegalArgumentException("GeoJSON polygons are only supported if they form a box");
}
List<double[]> polygon = (List<double[]>) coordinates.stream().map(o -> convertCollection((Collection) o)).collect(Collectors.toList());

double[] p0 = polygon.get(0);
double[] p1 = polygon.get(1);
double[] p2 = polygon.get(2);
double[] p3 = polygon.get(3);

//This may be a clockwise or counterclockwise polygon, we have to verify that it is a box
if ((p0[0] == p1[0] && p1[1] == p2[1] && p2[0] == p3[0] && p3[1] == p0[1]) ||
(p0[1] == p1[1] && p1[0] == p2[0] && p2[1] == p3[1] && p3[0] == p0[0])) {
return box(min(p0[1], p1[1], p2[1], p3[1]), min(p0[0], p1[0], p2[0], p3[0]), max(p0[1], p1[1], p2[1], p3[1]), max(p0[0], p1[0], p2[0], p3[0]));
}

throw new IllegalArgumentException("GeoJSON polygons are only supported if they form a box");
} else {
throw new IllegalArgumentException("GeoJSON support is restricted to Point, Circle or Polygon.");
}
}

private double min(double... numbers) {
return Arrays.stream(numbers).min().getAsDouble();
}

private double max(double... numbers) {
return Arrays.stream(numbers).max().getAsDouble();
}


@Override
public Geoshape read(ScanBuffer buffer) {
long l = VariableLong.readPositive(buffer);
Expand Down
@@ -1,9 +1,14 @@
package com.thinkaurelius.titan.graphdb.attribute;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.thinkaurelius.titan.core.attribute.Geoshape;
import org.junit.Test;

import java.util.Arrays;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.*;

Expand All @@ -15,8 +20,8 @@ public class GeoshapeTest {

@Test
public void testDistance() {
Geoshape p1 = Geoshape.point(37.759,-122.536);
Geoshape p2 = Geoshape.point(35.714,-105.938);
Geoshape p1 = Geoshape.point(37.759, -122.536);
Geoshape p2 = Geoshape.point(35.714, -105.938);

double distance = 1496;
assertEquals(distance,p1.getPoint().distance(p2.getPoint()),5.0);
Expand Down Expand Up @@ -51,6 +56,7 @@ public void testEquality() {
System.out.println(b);
}


@Test
public void testParseCollection() {
Geoshape.GeoshapeSerializer serializer = new Geoshape.GeoshapeSerializer();
Expand All @@ -65,4 +71,149 @@ public void testFailParseCollection() {
serializer.convert(Arrays.asList(10, "Foo"));
}


@Test
public void testGeoJsonPoint() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Point\",\n" +
" \"coordinates\": [20.5, 10.5]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
assertEquals(Geoshape.point(10.5, 20.5), s.convert(json));
}


@Test(expected = IllegalArgumentException.class)
public void testGeoJsonPointUnparseable() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Point\",\n" +
" \"coordinates\": [20.5, \"10.5\"]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
s.convert(json);
}


@Test
public void testGeoJsonCircle() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Circle\",\n" +
" \"radius\": 30.5, " +
" \"coordinates\": [20.5, 10.5]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
assertEquals(Geoshape.circle(10.5, 20.5, 30.5), s.convert(json));
}

@Test(expected = IllegalArgumentException.class)
public void testGeoJsonCircleMissingRadius() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Circle\",\n" +
" \"coordinates\": [20.5, 10.5]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
s.convert(json);
}

@Test
public void testGeoJsonPolygon() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Polygon\",\n" +
" \"coordinates\": [[20.5, 10.5],[22.5, 10.5],[22.5, 12.5],[20.5, 12.5]]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
assertEquals(Geoshape.box(10.5, 20.5, 12.5, 22.5), s.convert(json));

//Try the reverse order points
json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Polygon\",\n" +
" \"coordinates\": [[20.5, 12.5],[22.5, 12.5],[22.5, 10.5],[20.5, 10.5]]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
assertEquals(Geoshape.box(10.5, 20.5, 12.5, 22.5), s.convert(json));
}

@Test(expected = IllegalArgumentException.class)
public void testGeoJsonPolygonNotBox1() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Polygon\",\n" +
" \"coordinates\": [[20.5, 12.5],[22.5, 12.5],[22.5, 10.5],[20.5, 10.6]]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
s.convert(json);

}

@Test(expected = IllegalArgumentException.class)
public void testGeoJsonPolygonNotBox2() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Feature\",\n" +
" \"geometry\": {\n" +
" \"type\": \"Polygon\",\n" +
" \"coordinates\": [[20.5, 10.5],[22.5, 10.5],[22.5, 12.5]]\n" +
" },\n" +
" \"properties\": {\n" +
" \"name\": \"Dinagat Islands\"\n" +
" }\n" +
"}", HashMap.class);
s.convert(json);

}

@Test
public void testGeoJsonGeometry() throws IOException {
Geoshape.GeoshapeSerializer s = new Geoshape.GeoshapeSerializer();
Map json = new ObjectMapper().readValue("{\n" +
" \"type\": \"Point\",\n" +
" \"coordinates\": [20.5, 10.5]\n" +
"}", HashMap.class);
assertEquals(Geoshape.point(10.5, 20.5), s.convert(json));

}




}

0 comments on commit a2b4755

Please sign in to comment.