MongoDB 支持 GeoJSON 和用于地理空间数据的简单(遗留)坐标对。这些格式既可以用于存储数据,也可以用于查询数据。
在领域类中使用 GeoJSON 类型很简单。org.springframework.data.mongodb.core.geo 包中包含了 GeoJsonPoint、GeoJsonPolygon 等类型。这些类型是现有org.springframework.data.geo 类型的扩展。以下示例展示了 GeoJsonPoint 的应用:
public class Store {
String id;
/**
* location is stored in GeoJSON format.
* {
* "type" : "Point",
* "coordinates" : [ x, y ]
* }
*/
GeoJsonPoint location;
}提示:如果一个 GeoJSON 对象的坐标表示纬度和经度对,那么经度在前面,纬度在后。因此,GeoJsonPoint 将 getX() 视为经度,将 getY() 视为纬度。
使用 GeoJSON 类型作为存储库的查询参数,在创建查询时必须使用 $geometry 操作符,如下例所示。
public interface StoreRepository extends CrudRepository<Store, String> {
// 存储库方法的定义使用通用类型,允许用 GeoJSON 和传统格式调用它
List<Store> findByLocationWithin(Polygon polygon);
}
/*
* {
* "location": {
* "$geoWithin": {
* "$geometry": {
* "type": "Polygon",
* "coordinates": [
* [
* [-73.992514,40.758934],
* [-73.961138,40.760348],
* [-73.991658,40.730006],
* [-73.992514,40.758934]
* ]
* ]
* }
* }
* }
* }
*/
// 使用 GeoJSON 类型可以使用 $geometry运算符
repo.findByLocationWithin(
new GeoJsonPolygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006),
// 注意,GeoJSON 多边形需要定义一个闭合环,因此第一个和最后一个 point 的坐标一致
new Point(-73.992514, 40.758934)));
/*
* {
* "location" : {
* "$geoWithin" : {
* "$polygon" : [ [-73.992514,40.758934] , [-73.961138,40.760348] , [-73.991658,40.730006] ]
* }
* }
* }
*/
// 使用传统格式 $polygon 运算符
repo.findByLocationWithin(
new Polygon(
new Point(-73.992514, 40.758934),
new Point(-73.961138, 40.760348),
new Point(-73.991658, 40.730006)));MongoDB 的 $geoNear 操作符允许使用 GeoJSON 点或传统坐标对。
(1)传统坐标对,如下:
NearQuery.near(new Point(-73.99171, 40.738868))
对应的JSON:
{
"$geoNear": {
//...
"near": [-73.99171, 40.738868]
}
}(2)GeoJSON 点方式,如下:
NearQuery.near(new GeoJsonPoint(-73.99171, 40.738868))
对应的JSON:
{
"$geoNear": {
//...
"near": { "type": "Point", "coordinates": [-73.99171, 40.738868] }
}
}虽然在语法上不同,但无论集合中的目标文档使用什么格式,服务器都能接受这两种格式。
警告:GeoJSON 和传统坐标在距离计算方面有很大的不同。使用传统的格式是在类似地球的球体上操作弧度,而 GeoJSON 格式则使用 Meters。
为了避免严重的麻烦,请确保将 Metric(公制)设置为所需的度量单位,以确保正确计算距离。
换句话说:
假设您有5份文件,如下所示:
{
"_id" : ObjectId("5c10f3735d38908db52796a5"),
"name" : "Penn Station",
"location" : { "type" : "Point", "coordinates" : [ -73.99408, 40.75057 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
}
{
"_id" : ObjectId("5c10f3735d38908db52796ab"),
"name" : "Momofuku Milk Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.985839, 40.731698 ] }
}使用 GeoJSON 从 [73.99171,40.738868] 获取 400 米半径内的所有文档如下所示:
{
"$geoNear": {
"maxDistance": 400, // (1) 距中心点的最大距离,单位为米
"num": 10,
"near": { type: "Point", coordinates: [-73.99171, 40.738868] },
"spherical":true, // (2) GeoJSON 始终在球体上运行,spherical 表示球形的,球状的
"key": "location",
"distanceField": "distance"
}
}返回以下3份文件:
{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
"distance" : 0.0 // (3) 距中心点的距离,单位为米
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 69.3582262492474 // (3) 距中心点的距离,单位为米
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 69.3582262492474 // (3) 距中心点的距离,单位为米
}现在,当使用传统的坐标对时,正如之前讨论的那样,我们对弧度进行操作。所以我们在构建 $geoNear 命令时使用 Metrics#KILOMETERS。Metric 可确保距离乘数被正确设置。
{
"$geoNear": {
"maxDistance": 0.0000627142377, // (1) 距中心点的最大距离(弧度)
"distanceMultiplier": 6378.137, // (2) 距离的乘数,所以我们得到公里作为结果距离
"num": 10,
"near": [-73.99171, 40.738868],
"spherical":true, // (3) 确保对 2d_sphere 索引进行操作
"key": "location",
"distanceField": "distance"
}
}像 GeoJSON 变体一样返回3个文档:
{
"_id" : ObjectId("5c10f3735d38908db52796a6"),
"name" : "10gen Office",
"location" : { "type" : "Point", "coordinates" : [ -73.99171, 40.738868 ] }
"distance" : 0.0 // (4) 与中心点的距离,以公里为单位。除以 1000 则匹配 GeoJSON 的单位米
}
{
"_id" : ObjectId("5c10f3735d38908db52796a9"),
"name" : "City Bakery ",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 0.0693586286032982 // (4) 与中心点的距离,以公里为单位。除以 1000 则匹配 GeoJSON 的单位米
}
{
"_id" : ObjectId("5c10f3735d38908db52796aa"),
"name" : "Splash Bar",
"location" : { "type" : "Point", "coordinates" : [ -73.992491, 40.738673 ] }
"distance" : 0.0693586286032982 // (4) 与中心点的距离,以公里为单位。除以 1000 则匹配 GeoJSON 的单位米
}为了支持 Web,Spring Data 向 ObjectMapper 注册了额外的 Jackson 模块,用于反序列化/序列化常见的 Spring Data 领域类型。
MongoDB 模块还通过 GeoJsonConfiguration 公开 GeoJsonModule 为以下 GeoJSON 类型注册了 Jsondeserializer。
org.springframework.data.mongodb.core.geo.GeoJsonPoint org.springframework.data.mongodb.core.geo.GeoJsonMultiPoint org.springframework.data.mongodb.core.geo.GeoJsonLineString org.springframework.data.mongodb.core.geo.GeoJsonMultiLineString org.springframework.data.mongodb.core.geo.GeoJsonPolygon org.springframework.data.mongodb.core.geo.GeoJsonMultiPolygon
注意:
GeoJsonModule 仅注册 JsonDeserializer!
要为 ObjectMapper 配备一组对称的 JsonSerializers,您需要手动为 ObjectMapper 配置这些 JsonSerializers,或者提供一个自定义的 SpringDataJacksonModules 配置,将GeoJsonModule.serializers() 公开为 Spring Bean。
class GeoJsonConfiguration implements SpringDataJacksonModules {
@Bean
public Module geoJsonSerializers() {
return GeoJsonModule.serializers();
}
}警告:下一个主要版本(4.0)将同时注册 JsonDeserializers 和 JsonSerializers,默认为 GeoJSON 类型。