DATAMONGO-1466 - Add embedded typeinformation-based reading GeoJSON converter.

Original pull request: #561.
This commit is contained in:
Christoph Strobl
2018-05-14 09:11:03 +02:00
committed by Mark Paluch
parent 489d637a00
commit ae18958955
2 changed files with 117 additions and 47 deletions

View File

@@ -41,6 +41,7 @@ import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.geo.Sphere; import org.springframework.data.mongodb.core.geo.Sphere;
import org.springframework.data.mongodb.core.query.GeoCommand; import org.springframework.data.mongodb.core.query.GeoCommand;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@@ -91,7 +92,8 @@ abstract class GeoConverters {
, DocumentToGeoJsonMultiLineStringConverter.INSTANCE // , DocumentToGeoJsonMultiLineStringConverter.INSTANCE //
, DocumentToGeoJsonMultiPointConverter.INSTANCE // , DocumentToGeoJsonMultiPointConverter.INSTANCE //
, DocumentToGeoJsonMultiPolygonConverter.INSTANCE // , DocumentToGeoJsonMultiPolygonConverter.INSTANCE //
, DocumentToGeoJsonGeometryCollectionConverter.INSTANCE); , DocumentToGeoJsonGeometryCollectionConverter.INSTANCE //
, DocumentToGeoJsonConverter.INSTANCE);
} }
/** /**
@@ -756,7 +758,7 @@ abstract class GeoConverters {
* @author Christoph Strobl * @author Christoph Strobl
* @since 1.7 * @since 1.7
*/ */
static enum DocumentToGeoJsonGeometryCollectionConverter implements Converter<Document, GeoJsonGeometryCollection> { enum DocumentToGeoJsonGeometryCollectionConverter implements Converter<Document, GeoJsonGeometryCollection> {
INSTANCE; INSTANCE;
@@ -775,41 +777,12 @@ abstract class GeoConverters {
Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"), Assert.isTrue(ObjectUtils.nullSafeEquals(source.get("type"), "GeometryCollection"),
String.format("Cannot convert type '%s' to GeometryCollection.", source.get("type"))); String.format("Cannot convert type '%s' to GeometryCollection.", source.get("type")));
List<GeoJson<?>> geometries = new ArrayList<GeoJson<?>>(); List<GeoJson<?>> geometries = new ArrayList<>();
for (Object o : (List) source.get("geometries")) { for (Object o : (List) source.get("geometries")) {
geometries.add(convertGeometries((Document) o)); geometries.add(toGenericGeoJson((Document) o));
} }
return new GeoJsonGeometryCollection(geometries); return new GeoJsonGeometryCollection(geometries);
}
private static GeoJson<?> convertGeometries(Document source) {
Object type = source.get("type");
if (ObjectUtils.nullSafeEquals(type, "Point")) {
return DocumentToGeoJsonPointConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiPoint")) {
return DocumentToGeoJsonMultiPointConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "LineString")) {
return DocumentToGeoJsonLineStringConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiLineString")) {
return DocumentToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "Polygon")) {
return DocumentToGeoJsonPolygonConverter.INSTANCE.convert(source);
}
if (ObjectUtils.nullSafeEquals(type, "MultiPolygon")) {
return DocumentToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
}
throw new IllegalArgumentException(String.format("Cannot convert unknown GeoJson type %s", type));
} }
} }
@@ -852,6 +825,64 @@ abstract class GeoConverters {
return new GeoJsonPolygon(toListOfPoint((List) dbList.get(0))); return new GeoJsonPolygon(toListOfPoint((List) dbList.get(0)));
} }
/**
* Converter implementation transforming a {@link Document} into a concrete {@link GeoJson} based on the embedded
* {@literal type} information.
*
* @since 2.1
* @author Christoph Strobl
*/
@ReadingConverter
enum DocumentToGeoJsonConverter implements Converter<Document, GeoJson> {
INSTANCE;
/*
* (non-Javadoc)
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
@Nullable
@Override
public GeoJson convert(Document source) {
return toGenericGeoJson(source);
}
}
private static GeoJson<?> toGenericGeoJson(Document source) {
String type = source.get("type", String.class);
if ("point".equalsIgnoreCase(type)) {
return DocumentToGeoJsonPointConverter.INSTANCE.convert(source);
}
if ("multipoint".equalsIgnoreCase(type)) {
return DocumentToGeoJsonMultiPointConverter.INSTANCE.convert(source);
}
if ("linestring".equalsIgnoreCase(type)) {
return DocumentToGeoJsonLineStringConverter.INSTANCE.convert(source);
}
if ("multilinestring".equalsIgnoreCase(type)) {
return DocumentToGeoJsonMultiLineStringConverter.INSTANCE.convert(source);
}
if ("polygon".equalsIgnoreCase(type)) {
return DocumentToGeoJsonPolygonConverter.INSTANCE.convert(source);
}
if ("multipolygon".equalsIgnoreCase(type)) {
return DocumentToGeoJsonMultiPolygonConverter.INSTANCE.convert(source);
}
if ("geometrycollection".equalsIgnoreCase(type)) {
return DocumentToGeoJsonGeometryCollectionConverter.INSTANCE.convert(source);
}
throw new IllegalArgumentException(
String.format("No converter found capable of converting GeoJson type " + "%s.", type));
}
private static double toPrimitiveDoubleValue(Object value) { private static double toPrimitiveDoubleValue(Object value) {
Assert.isInstanceOf(Number.class, value, "Argument must be a Number."); Assert.isInstanceOf(Number.class, value, "Argument must be a Number.");

View File

@@ -20,10 +20,11 @@ import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.core.query.Query.*; import static org.springframework.data.mongodb.core.query.Query.*;
import lombok.Data;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.mongodb.client.MongoCollection;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -43,16 +44,17 @@ import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType; import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed; import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.GeospatialIndex; import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.NearQuery; import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder; import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.mongodb.Mongo;
import com.mongodb.MongoClient; import com.mongodb.MongoClient;
import com.mongodb.MongoException; import com.mongodb.MongoException;
import com.mongodb.WriteConcern; import com.mongodb.WriteConcern;
import com.mongodb.client.MongoCollection;
/** /**
* @author Christoph Strobl * @author Christoph Strobl
@@ -190,8 +192,9 @@ public class GeoJsonTests {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonMultiLineString"; obj.id = "geoJsonMultiLineString";
obj.geoJsonMultiLineString = new GeoJsonMultiLineString(Arrays.asList(new GeoJsonLineString(new Point(0, 0), obj.geoJsonMultiLineString = new GeoJsonMultiLineString(
new Point(0, 1), new Point(1, 1)), new GeoJsonLineString(new Point(199, 0), new Point(2, 3)))); Arrays.asList(new GeoJsonLineString(new Point(0, 0), new Point(0, 1), new Point(1, 1)),
new GeoJsonLineString(new Point(199, 0), new Point(2, 3))));
template.save(obj); template.save(obj);
@@ -221,8 +224,8 @@ public class GeoJsonTests {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonMultiPolygon"; obj.id = "geoJsonMultiPolygon";
obj.geoJsonMultiPolygon = new GeoJsonMultiPolygon(Arrays.asList(new GeoJsonPolygon(new Point(0, 0), obj.geoJsonMultiPolygon = new GeoJsonMultiPolygon(
new Point(0, 1), new Point(1, 1), new Point(0, 0)))); Arrays.asList(new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(0, 0))));
template.save(obj); template.save(obj);
@@ -237,9 +240,8 @@ public class GeoJsonTests {
DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType(); DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
obj.id = "geoJsonGeometryCollection"; obj.id = "geoJsonGeometryCollection";
obj.geoJsonGeometryCollection = new GeoJsonGeometryCollection(Arrays.<GeoJson<?>> asList( obj.geoJsonGeometryCollection = new GeoJsonGeometryCollection(Arrays.<GeoJson<?>> asList(new GeoJsonPoint(100, 200),
new GeoJsonPoint(100, 200), new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, new GeoJsonPolygon(new Point(0, 0), new Point(0, 1), new Point(1, 1), new Point(1, 0), new Point(0, 0))));
0), new Point(0, 0))));
template.save(obj); template.save(obj);
@@ -286,7 +288,8 @@ public class GeoJsonTests {
new CollectionCallback<Object>() { new CollectionCallback<Object>() {
@Override @Override
public Object doInCollection(MongoCollection<org.bson.Document> collection) throws MongoException, DataAccessException { public Object doInCollection(MongoCollection<org.bson.Document> collection)
throws MongoException, DataAccessException {
org.bson.Document pointRepresentation = new org.bson.Document(); org.bson.Document pointRepresentation = new org.bson.Document();
pointRepresentation.put("type", "Point"); pointRepresentation.put("type", "Point");
@@ -313,7 +316,8 @@ public class GeoJsonTests {
new CollectionCallback<Object>() { new CollectionCallback<Object>() {
@Override @Override
public Object doInCollection(MongoCollection<org.bson.Document> collection) throws MongoException, DataAccessException { public Object doInCollection(MongoCollection<org.bson.Document> collection)
throws MongoException, DataAccessException {
org.bson.Document lineStringRepresentation = new org.bson.Document(); org.bson.Document lineStringRepresentation = new org.bson.Document();
lineStringRepresentation.put("type", "LineString"); lineStringRepresentation.put("type", "LineString");
@@ -337,6 +341,27 @@ public class GeoJsonTests {
is(equalTo(new GeoJsonLineString(new Point(0D, 0D), new Point(1, 1))))); is(equalTo(new GeoJsonLineString(new Point(0D, 0D), new Point(1, 1)))));
} }
@Test // DATAMONGO-1466
public void readGeoJsonBasedOnEmbeddedTypeInformation() {
Point first = new Point(-73.99756, 40.73083);
Point second = new Point(-73.99756, 40.741404);
Point third = new Point(-73.988135, 40.741404);
Point fourth = new Point(-73.988135, 40.73083);
GeoJsonPolygon polygon = new GeoJsonPolygon(first, second, third, fourth, first);
ConcreteGeoJson source = new ConcreteGeoJson();
source.shape = polygon;
source.id = "id-1";
template.save(source);
OpenGeoJson target = template.findOne(query(where("id").is(source.id)), OpenGeoJson.class);
assertThat(target.shape, is(equalTo(source.shape)));
}
private void addVenues() { private void addVenues() {
template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057)); template.insert(new Venue2DSphere("Penn Station", -73.99408, 40.75057));
@@ -355,8 +380,8 @@ public class GeoJsonTests {
protected void createIndex() { protected void createIndex() {
dropIndex(); dropIndex();
template.indexOps(Venue2DSphere.class).ensureIndex( template.indexOps(Venue2DSphere.class)
new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE)); .ensureIndex(new GeospatialIndex("location").typed(GeoSpatialIndexType.GEO_2DSPHERE));
} }
protected void dropIndex() { protected void dropIndex() {
@@ -416,4 +441,18 @@ public class GeoJsonTests {
GeoJsonGeometryCollection geoJsonGeometryCollection; GeoJsonGeometryCollection geoJsonGeometryCollection;
} }
@Data
@Document(collection = "geo-json-shapes")
static class ConcreteGeoJson {
String id;
GeoJsonPolygon shape;
}
@Data
@Document(collection = "geo-json-shapes")
static class OpenGeoJson {
String id;
GeoJson shape;
}
} }