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 类型。