当前位置:   article > 正文

MongoDB系列五(地理空间索引与查询)._mongodb $geometry

mongodb $geometry

一、经纬度表示方式
MongoDB 中对经纬度的存储有着自己的一套规范(主要是为了可以在该字段上建立地理空间索引)。包括两种方式,分别是 Legacy Coordinate Pairs (这个词实在不知道怎么翻译…) 和 GeoJSON 。

Legacy Coordinate Pairs
Legacy Coordinate Pairs 又有两种方式可以存储经纬度,可以使用数组(首选)或嵌入式文档。

数组:

<field>: [<longitude>, <latitude> ]
  • 1

嵌入式文档:

<field>: { <field1>: <longitude>, <field2>: <latitude> }
  • 1

tips:有效经度值介于-180和180之间。有效纬度值介于-90和90之间。

GeoJSON
GeoJson 比 Legacy Coordinate Pairs 要强大的多,Legacy Coordinate Pairs 仅仅用来保存一个经纬度,而 GeoJson 可以用来指定点、线和多边形。

点可以用形如[longitude, latitude]([经度,纬度])的两个元素的数组表示:

{
"geometry": {
        "type": "Point",
        "coordinates": [125.6, 10.1]
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

线可以用一个由点组成的数组来表示:

{
"geometry": {
        "type": "LineString",
         "coordinates": [[125.6, 10.1],[125.6,10.2],[125.6,10.3]]
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

多边形的表示方式与线一样(都是一个由点组成的数组),但是"type"不同:

{
"geometry": {
        "type": "Polygon",
         "coordinates": [[125.6, 10.1],[125.5,10.2],[125.7,10.3]]
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

type 除了 Point(点)、LineString(线)、Polygon(多边形),还有 MultiPoint(多点)、MultiLineString(多个线) 和 MultiPolygon(多个多边形)。

"geometry"字段的名字可以是任意的,但是其中的子对象是由GeoJSON指定的,不能改变。

二、地理空间索引
2dsphere索引
2dsphere索引用于地球表面类型的地图,允许使用在 Legacy Coordinate Pairs 保存的经纬度字段上和使用GeoJSON格式保存的点、线和多边形字段上。

  db.world.ensureIndex({"geometry" : "2dsphere"})
2d索引
 对于非球面地图(游戏地图、时间连续的数据等),可以使用"2d"索引代替"2dsphere"。

 2d 索引 仅允许使用在 Legacy Coordinate Pairs 保存的经纬度字段上。

  db.world.ensureIndex({"geometry" : "2d"})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

区别:
2dsphere索引只支持球形查询(即球面上几何图形的查询)。

2d索引支持平面查询(即在平面上几何图形的查询)和一些球形查询。虽然2d索引支持一些球形查询,但是对这些球形查询使用2d索引可能会导致错误,例如极点附近会出现大量的扭曲变形。
2d索引只能对点进行索引。可以保存一个由点组成的数组,但是它只会被保存为由点组成的数组,不会被当成线。特别是对于"$geoWithin"查询来说,这是一项重要的区别。如果将街道保存为由点组成的数组,那么如果其中的某个点位于给定的形状之内,这个文档就会与$geoWithin相匹配。但是,由这些点组成的线并不一定完全包含在这个形状之内。
  • 1
  • 2

三、地理空间查询
可以使用多种不同类型的地理空间查询:交集(intersection)、包含(within)以及接近(nearness)。

$geoIntersects
定义:指出与查询位置相交的文档。

支持的索引:2dsphere

几何操作符:

$geometry (仅支持 2dsphere 索引,指定GeoJSON格式的几何图形)
  • 1
  • 2
  • 3
  • 4
  • 5

$geoWithin
定义:指出完全包含在某个区域的文档。

支持的索引:2dsphere、2d
  • 1

几何操作符:

$box(仅支持 2d 索引,查询出矩形范围内的所有文档)
$center(仅支持 2d 索引,查询出圆形范围内的所有文档)
$polygon (仅支持 2d 索引,查询出多边形范围内的所有文档)
$centerSphere(支持 2d 索引和 2dsphere 索引,查询出球面圆形范围内的所有文档)
$geometry (仅支持 2dsphere 索引,指定GeoJSON格式的几何图形)
  • 1
  • 2
  • 3
  • 4
  • 5

$near
定义:指出与查询位置从最近到最远的文档。

支持的索引:2dsphere、2d
  • 1

几何操作符:

$maxDistance (支持 2dsphere 索引和 2d 索引,指定查询结果的最大距离)
$minDistance (仅支持 2dsphere 索引,指定查询结果的最小距离)
$geometry (仅支持 2dsphere 索引,指定GeoJSON格式的点)
备注:$minDistance 官方文档说仅支持 2dsphere 索引,但是我实践证明 $minDistance 也支持 2d 索引,大家可以试试看,这里保留争议。
  • 1
  • 2
  • 3
  • 4

$nearSphere
定义:使用球面几何计算近球面的距离,指出与查询位置从最近到最远的文档。

支持的索引:2dsphere、2d
  • 1

几何操作符:

$maxDistance (支持 2dsphere 索引和 2d 索引,指定查询结果的最大距离)
$minDistance (仅支持 2dsphere 索引,指定查询结果的最小距离)
$geometry (仅支持 2dsphere 索引,指定GeoJSON格式的点)
备注:$minDistance 官方文档说仅支持 2dsphere 索引,但是我实践证明 $minDistance 也支持 2d 索引,大家可以试试看,这里保留争议。
  • 1
  • 2
  • 3
  • 4

四、实践
“$geoIntersects” 操作符找出与查询位置相交的文档 ?

db.driverPoint.find(
   {
     coordinate: {
       $geoIntersects: {
          $geometry: {
             type: "Polygon" ,
             coordinates: [
               [ [ 118.193828, 24.492242 ], [ 118.193953, 24.702114 ], [ 118.19387, 24.592242 ],[ 118.193828, 24.492242 ]]
             ]
          }
       }
     }
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

View Code
tips:coordinates 表示多边形,第一个点 和 最后一个点 必须相同,因为这样才能拼成一个多边形呀!

"$geoWithin"操作符找出完全包含在某个区域的文档?

db.driverPoint.find(
   {
     coordinate: {
       $geoWithin: {
          $geometry: {
             type: "Polygon" ,
             coordinates: [
               [ [ 118.193828, 24.492242 ], [ 118.193953, 24.702114 ], [ 119.19387, 28.792242 ],[ 118.193828, 24.492242 ]]
             ]
          }
       }
     }
   }
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

View Code
"$geoWithin"操作符找出矩形范围内的文档?

db.driverPoint.find(
{
  coordinate: {
     $geoWithin: {
        $box: [
          [ 118.0,24.0 ],
          [ 120.0,30.0 ]
        ]
     }
  }
}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

View Code
tips:"$box"接受一个两元素的数组:第一个元素指定左下角的坐标,第二个元素指定右上角的坐标。

"$geoWithin"操作符找出圆形范围内的文档?

db.driverPoint.find(
{
  coordinate: {
     $geoWithin: {
         $center: [ [ 118.067678, 24.444373] , 10 ] 
     }
  }
}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

View Code
tips:"$center"接受一个两元素数组作为参数:第一个元素是一个点,用于指定圆心;第二个参数用于指定半径。

"$geoWithin"操作符找出多边形范围内的文档?

db.driverPoint.find(
{
  coordinate: {
     $geoWithin: {
         $polygon: [ [ 118.067678 , 24.444373 ], [ 119.067678 , 25.444373 ], [ 120.067678 , 26.444373 ] ]
     }
  }
}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

View Code
tips:"$polygon" 列表中的最后一个点会被连接到第一个点,以便组成多边形。

"$geoWithin"操作符找出球面圆形范围内的文档?

db.driverPoint.find(
{
  coordinate: {
     $geoWithin: {
         $centerSphere: [ [ 118.067678, 24.444373 ], 10/3963.2 ]
     }
  }
}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

View Code
tips:该例子表示 距离 [118.067678, 24.444373] 中心点10 英里范围内的所有文档,查询通过除以地球的大约赤道半径(3963.2英里)将距离转换为弧度。

$near 找出距离一个点相应距离内的文档?
geoJson 格式(仅支持 2dsphere 索引):

db.driverPoint.find(
{
   coordinate: {
     $near: {
       $geometry: {
          type: "Point" ,
          coordinates: [ 118.067678 , 24.444373 ]
       },
       $maxDistance: 3000,
       $minDistance: 0
     }
   }
}
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

View Code
Legacy Coordinate Pairs 格式(仅支持 2d 索引):

db.driverPoint.find(
{
coordinate: {
$near: [ 118.193828 , 24.492242 ],
KaTeX parse error: Expected 'EOF', got '}' at position 24: …nce: 0.10 }̲ } ) View Code …near 当用 geoJson 格式表示时, 距离单位是米(meter)。

     2、$near 当用 Legacy Coordinate Pairs 格式表示时,距离单位是弧度(radian)。  

     3、"$near"是唯一一个会对查询结果进行自动排序的地理空间操作符:"$near"的返回结果是按照距离由近及远排序的。
  • 1
  • 2
  • 3

五、结语
怎么说呢?学习这方面的知识老是给我一种特别乱的感觉。稍微总结下吧!MongoDB 对于地理空间的查询 是基于 它对 地理空间的索引(即2dsphere 和 2d)来实现的。所以,我们只要搞清楚什么时候 该建立 2dsphere 索引,什么时候该建立 2d 索引,然后再找适用于该索引的操作符就很清晰明了了!总之,geoJSON 格式保存的经纬度一定 建立 2dsphere 索引。Legacy Coordinate Pairs 格式保存的经纬度 仅在表示 平面地图的时候才考虑建立 2d 索引,其他情况还是选择 2dsphere 索引。

Spring Data MongoDB 中对地理位置的查询可参考 https://github.com/JMCuixy/SpringDataMongoDB 中单元测试的 Test03.java。
  • 1
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/504035
推荐阅读
相关标签
  

闽ICP备14008679号