package org.mongodb.morphia.geo;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.junit.Test;
import org.mongodb.morphia.TestBase;
import org.mongodb.morphia.testutil.JSONMatcher;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mongodb.morphia.geo.GeoJson.lineString;
import static org.mongodb.morphia.geo.GeoJson.multiPolygon;
import static org.mongodb.morphia.geo.GeoJson.point;
import static org.mongodb.morphia.geo.GeoJson.polygon;
/**
* Test driving features for Issue 643 - add support for saving entities with GeoJSON.
*/
public class GeoEntitiesTest extends TestBase {
@Test
public void shouldConvertPointCorrectlyToDBObject() {
// given
City city = new City("New City", point(3.0, 7.0));
// when
DBObject dbObject = getMorphia().toDBObject(city);
assertThat(dbObject, is(notNullValue()));
assertThat(dbObject.toString(), JSONMatcher.jsonEqual(" {"
+ " name: 'New City',"
+ " className: 'org.mongodb.morphia.geo.City',"
+ " location: "
+ " {"
+ " type: 'Point', "
+ " coordinates: [7.0, 3.0]"
+ " }"
+ "}"));
}
@Test
public void shouldRetrieveGeoCollectionType() {
// given
String name = "What, everything?";
Point point = point(3.0, 7.0);
LineString lineString = lineString(point(1, 2), point(3, 5), point(19, 13));
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8), point(2.2, 2.1)));
MultiPoint multiPoint = GeoJson.multiPoint(point(1, 2), point(3, 5), point(19, 13));
MultiLineString multiLineString = GeoJson.multiLineString(lineString(point(1, 2), point(3, 5), point(19, 13)),
lineString(point(1.5, 2.0),
point(1.9, 2.0),
point(1.9, 1.8),
point(1.5, 2.0)));
MultiPolygon multiPolygon = multiPolygon(polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
polygon(lineString(point(1.2, 3.0), point(2.5, 4.5), point(6.7, 1.9), point(1.2, 3.0)),
lineString(point(3.5, 2.4), point(1.7, 2.8), point(3.5, 2.4))));
GeometryCollection geometryCollection = GeoJson.geometryCollection(point, lineString, polygonWithHoles, multiPoint,
multiLineString, multiPolygon);
AllTheThings allTheThings = new AllTheThings(name, geometryCollection);
getDs().save(allTheThings);
// when
AllTheThings found = getDs().find(AllTheThings.class).field("name").equal(name).get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(allTheThings));
}
@Test
public void shouldRetrieveGeoJsonLineString() {
// given
Route route = new Route("My Route", lineString(point(1, 2), point(3, 5), point(19, 13)));
getDs().save(route);
// when
Route found = getDs().find(Route.class).field("name").equal("My Route").get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(route));
}
@Test
public void shouldRetrieveGeoJsonMultiLineString() {
// given
String name = "Many Paths";
Paths paths = new Paths(name, GeoJson.multiLineString(lineString(point(1, 2), point(3, 5), point(19, 13)),
lineString(point(1.5, 2.0),
point(1.9, 2.0),
point(1.9, 1.8),
point(1.5, 2.0))));
getDs().save(paths);
// when
Paths found = getDs().find(Paths.class).field("name").equal(name).get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(paths));
}
@Test
public void shouldRetrieveGeoJsonMultiPoint() {
// given
String name = "My stores";
Stores stores = new Stores(name, GeoJson.multiPoint(point(1, 2), point(3, 5), point(19, 13)));
getDs().save(stores);
// when
Stores found = getDs().find(Stores.class).field("name").equal(name).get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(stores));
}
@Test
public void shouldRetrieveGeoJsonMultiPolygon() {
// given
String name = "All these shapes";
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8), point(2.2, 2.1)));
Regions regions = new Regions(name, multiPolygon(polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
polygonWithHoles));
getDs().save(regions);
// when
Regions found = getDs().find(Regions.class).field("name").equal(name).get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(regions));
}
@Test
public void shouldRetrieveGeoJsonMultiRingPolygon() {
// given
String polygonName = "A polygon with holes";
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8), point(2.2, 2.1)));
Area area = new Area(polygonName, polygonWithHoles);
getDs().save(area);
// when
Area found = getDs().find(Area.class).field("name").equal(polygonName).get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(area));
}
@Test
public void shouldRetrieveGeoJsonPoint() {
// given
City city = new City("New City", point(3.0, 7.0));
getDs().save(city);
// when
City found = getDs().find(City.class).field("name").equal("New City").get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(city));
}
@Test
public void shouldRetrieveGeoJsonPolygon() {
// given
Area area = new Area("The Area", polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)));
getDs().save(area);
// when
Area found = getDs().find(Area.class).field("name").equal("The Area").get();
// then
assertThat(found, is(notNullValue()));
assertThat(found, is(area));
}
@Test
public void shouldSaveAnEntityWithAGeoCollectionType() {
// given
String name = "What, everything?";
Point point = point(3.0, 7.0);
LineString lineString = lineString(point(1, 2), point(3, 5), point(19, 13));
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8), point(2.2, 2.1)));
MultiPoint multiPoint = GeoJson.multiPoint(point(1, 2), point(3, 5), point(19, 13));
MultiLineString multiLineString = GeoJson.multiLineString(lineString(point(1, 2), point(3, 5), point(19, 13)),
lineString(point(1.5, 2.0),
point(1.9, 2.0),
point(1.9, 1.8),
point(1.5, 2.0)));
MultiPolygon multiPolygon = multiPolygon(polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
polygon(lineString(point(1.2, 3.0), point(2.5, 4.5), point(6.7, 1.9), point(1.2, 3.0)),
lineString(point(3.5, 2.4), point(1.7, 2.8), point(3.5, 2.4))));
GeometryCollection geometryCollection = GeoJson.geometryCollection(point, lineString, polygonWithHoles, multiPoint, multiLineString,
multiPolygon);
AllTheThings allTheThings = new AllTheThings(name, geometryCollection);
// when
getDs().save(allTheThings);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedArea = getDs().getCollection(AllTheThings.class).findOne(new BasicDBObject("name", name),
new BasicDBObject("_id", 0)
.append("className", 0));
assertThat(storedArea, is(notNullValue()));
assertThat(storedArea.toString(), JSONMatcher.jsonEqual(" {"
+ " name: '" + name + "',"
+ " everything: "
+ " {"
+ " type: 'GeometryCollection', "
+ " geometries: "
+ " ["
+ " {"
+ " type: 'Point', "
+ " coordinates: [7.0, 3.0]"
+ " }, "
+ " {"
+ " type: 'LineString', "
+ " coordinates: [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] ]"
+ " },"
+ " {"
+ " type: 'Polygon', "
+ " coordinates: "
+ " [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1] "
+ " ],"
+ " [ [ 2.0, 1.5],"
+ " [ 2.0, 1.9],"
+ " [ 1.8, 1.9],"
+ " [ 2.0, 1.5] "
+ " ],"
+ " [ [ 2.1, 2.2],"
+ " [ 1.9, 2.4],"
+ " [ 1.7, 2.4],"
+ " [ 1.8, 2.1],"
+ " [ 2.1, 2.2] "
+ " ]"
+ " ]"
+ " },"
+ " {"
+ " type: 'MultiPoint', "
+ " coordinates: [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] ]"
+ " },"
+ " {"
+ " type: 'MultiLineString', "
+ " coordinates: "
+ " [ [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] "
+ " ], "
+ " [ [ 2.0, 1.5],"
+ " [ 2.0, 1.9],"
+ " [ 1.8, 1.9],"
+ " [ 2.0, 1.5] "
+ " ]"
+ " ]"
+ " },"
+ " {"
+ " type: 'MultiPolygon', "
+ " coordinates: [ [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1],"
+ " ]"
+ " ],"
+ " [ [ [ 3.0, 1.2],"
+ " [ 4.5, 2.5],"
+ " [ 1.9, 6.7],"
+ " [ 3.0, 1.2] "
+ " ],"
+ " [ [ 2.4, 3.5],"
+ " [ 2.8, 1.7],"
+ " [ 2.4, 3.5] "
+ " ],"
+ " ]"
+ " ]"
+ " }"
+ " ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithALineStringGeoJsonType() {
// given
Route route = new Route("My Route", lineString(point(1, 2), point(3, 5), point(19, 13)));
// when
getDs().save(route);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedRoute = getDs().getCollection(Route.class).findOne(new BasicDBObject("name", "My Route"),
new BasicDBObject("_id", 0).append("className", 0));
assertThat(storedRoute, is(notNullValue()));
// lat/long is always long/lat on the server
assertThat(storedRoute.toString(), JSONMatcher.jsonEqual(" {"
+ " name: 'My Route',"
+ " route:"
+ " {"
+ " type: 'LineString', "
+ " coordinates: [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithALocationStoredAsAMultiPoint() {
// given
String name = "My stores";
Stores stores = new Stores(name, GeoJson.multiPoint(point(1, 2), point(3, 5), point(19, 13)));
// when
getDs().save(stores);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedObject = getDs().getCollection(Stores.class).findOne(new BasicDBObject("name", name),
new BasicDBObject("_id", 0).append("className", 0));
assertThat(storedObject, is(notNullValue()));
assertThat(storedObject.toString(), JSONMatcher.jsonEqual(" {"
+ " name: " + name + ","
+ " locations: "
+ " {"
+ " type: 'MultiPoint', "
+ " coordinates: [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithALocationStoredAsAPoint() {
// given
City city = new City("New City", point(3.0, 7.0));
// when
getDs().save(city);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedCity = getDs().getCollection(City.class).findOne(new BasicDBObject("name", "New City"),
new BasicDBObject("_id", 0).append("className", 0));
assertThat(storedCity, is(notNullValue()));
assertThat(storedCity.toString(), JSONMatcher.jsonEqual(" {"
+ " name: 'New City',"
+ " location: "
+ " {"
+ " type: 'Point', "
+ " coordinates: [7.0, 3.0]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithAMultiLineStringGeoJsonType() {
// given
String name = "Many Paths";
Paths paths = new Paths(name, GeoJson.multiLineString(lineString(point(1, 2), point(3, 5), point(19, 13)),
lineString(point(1.5, 2.0),
point(1.9, 2.0),
point(1.9, 1.8),
point(1.5, 2.0))));
// when
getDs().save(paths);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedPaths = getDs().getCollection(Paths.class).findOne(new BasicDBObject("name", name),
new BasicDBObject("_id", 0).append("className", 0));
assertThat(storedPaths, is(notNullValue()));
// lat/long is always long/lat on the server
assertThat(storedPaths.toString(), JSONMatcher.jsonEqual(" {"
+ " name: '" + name + "',"
+ " paths:"
+ " {"
+ " type: 'MultiLineString', "
+ " coordinates: "
+ " [ [ [ 2.0, 1.0],"
+ " [ 5.0, 3.0],"
+ " [13.0, 19.0] "
+ " ], "
+ " [ [ 2.0, 1.5],"
+ " [ 2.0, 1.9],"
+ " [ 1.8, 1.9],"
+ " [ 2.0, 1.5] "
+ " ]"
+ " ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithAMultiPolygonGeoJsonType() {
// given
String name = "All these shapes";
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8),
point(2.2, 2.1)));
Regions regions = new Regions(name, multiPolygon(polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
polygonWithHoles));
// when
getDs().save(regions);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedRegions = getDs().getCollection(Regions.class).findOne(new BasicDBObject("name", name),
new BasicDBObject("_id", 0)
.append("className", 0));
assertThat(storedRegions, is(notNullValue()));
assertThat(storedRegions.toString(), JSONMatcher.jsonEqual(" {"
+ " name: '" + name + "',"
+ " regions: "
+ " {"
+ " type: 'MultiPolygon', "
+ " coordinates: [ [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1],"
+ " ]"
+ " ],"
+ " [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1] "
+ " ],"
+ " [ [ 2.0, 1.5],"
+ " [ 2.0, 1.9],"
+ " [ 1.8, 1.9],"
+ " [ 2.0, 1.5] "
+ " ],"
+ " [ [ 2.1, 2.2],"
+ " [ 1.9, 2.4],"
+ " [ 1.7, 2.4],"
+ " [ 1.8, 2.1],"
+ " [ 2.1, 2.2] "
+ " ]"
+ " ]"
+ " ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithAPolygonContainingInteriorRings() {
// given
String polygonName = "A polygon with holes";
Polygon polygonWithHoles = polygon(lineString(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)),
lineString(point(1.5, 2.0), point(1.9, 2.0), point(1.9, 1.8), point(1.5, 2.0)),
lineString(point(2.2, 2.1), point(2.4, 1.9), point(2.4, 1.7), point(2.1, 1.8), point(2.2, 2.1)));
Area area = new Area(polygonName, polygonWithHoles);
// when
getDs().save(area);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedArea = getDs().getCollection(Area.class).findOne(new BasicDBObject("name", polygonName),
new BasicDBObject("_id", 0)
.append("className", 0)
.append("area.className", 0));
assertThat(storedArea, is(notNullValue()));
assertThat(storedArea.toString(), JSONMatcher.jsonEqual(" {"
+ " name: " + polygonName + ","
+ " area: "
+ " {"
+ " type: 'Polygon', "
+ " coordinates: "
+ " [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1] "
+ " ],"
+ " [ [ 2.0, 1.5],"
+ " [ 2.0, 1.9],"
+ " [ 1.8, 1.9],"
+ " [ 2.0, 1.5] "
+ " ],"
+ " [ [ 2.1, 2.2],"
+ " [ 1.9, 2.4],"
+ " [ 1.7, 2.4],"
+ " [ 1.8, 2.1],"
+ " [ 2.1, 2.2] "
+ " ]"
+ " ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithAPolygonGeoJsonType() {
// given
Area area = new Area("The Area", polygon(point(1.1, 2.0), point(2.3, 3.5), point(3.7, 1.0), point(1.1, 2.0)));
// when
getDs().save(area);
// then use the underlying driver to ensure it was persisted correctly to the database
DBObject storedArea = getDs().getCollection(Area.class).findOne(new BasicDBObject("name", "The Area"),
new BasicDBObject("_id", 0)
.append("className", 0)
.append("area.className", 0));
assertThat(storedArea, is(notNullValue()));
assertThat(storedArea.toString(), JSONMatcher.jsonEqual(" {"
+ " name: 'The Area',"
+ " area: "
+ " {"
+ " type: 'Polygon', "
+ " coordinates: [ [ [ 2.0, 1.1],"
+ " [ 3.5, 2.3],"
+ " [ 1.0, 3.7],"
+ " [ 2.0, 1.1] ] ]"
+ " }"
+ "}"));
}
@Test
public void shouldSaveAnEntityWithNullPoints() {
getDs().save(new City("New City", null));
DBObject storedCity = getDs().getCollection(City.class)
.findOne(new BasicDBObject("name", "New City"),
new BasicDBObject("_id", 0)
.append("className", 0));
assertThat(storedCity, is(notNullValue()));
assertThat(storedCity.toString(), JSONMatcher.jsonEqual("{ name: 'New City'}"));
}
@SuppressWarnings("UnusedDeclaration")
private static final class Paths {
private String name;
private MultiLineString paths;
private Paths() {
}
private Paths(final String name, final MultiLineString paths) {
this.name = name;
this.paths = paths;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + paths.hashCode();
return result;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Paths paths1 = (Paths) o;
if (!name.equals(paths1.name)) {
return false;
}
if (!paths.equals(paths1.paths)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Paths{"
+ "name='" + name + '\''
+ ", paths=" + paths
+ '}';
}
}
@SuppressWarnings("UnusedDeclaration")
private static final class AllTheThings {
private GeometryCollection everything;
private String name;
private AllTheThings() {
}
private AllTheThings(final String name, final GeometryCollection everything) {
this.name = name;
this.everything = everything;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AllTheThings that = (AllTheThings) o;
if (!everything.equals(that.everything)) {
return false;
}
if (!name.equals(that.name)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = everything.hashCode();
result = 31 * result + name.hashCode();
return result;
}
@Override
public String toString() {
return "AllTheThings{"
+ "everything=" + everything
+ ", name='" + name + '\''
+ '}';
}
}
}