一、經緯度表示方式 MongoDB 中對經緯度的存儲有著自己的一套規範(主要是為了可以在該欄位上建立地理空間索引)。包括兩種方式,分別是 Legacy Coordinate Pairs (這個詞實在不知道怎麼翻譯...) 和 GeoJSON 。 Legacy Coordinate Pairs Leg ...
一、經緯度表示方式
MongoDB 中對經緯度的存儲有著自己的一套規範(主要是為了可以在該欄位上建立地理空間索引)。包括兩種方式,分別是 Legacy Coordinate Pairs (這個詞實在不知道怎麼翻譯...) 和 GeoJSON 。
- Legacy Coordinate Pairs
Legacy Coordinate Pairs 又有兩種方式可以存儲經緯度,可以使用數組(首選)或嵌入式文檔。
數組:
<field>: [<longitude>, <latitude> ]
嵌入式文檔:
<field>: { <field1>: <longitude>, <field2>: <latitude> }
tips:有效經度值介於-180和180之間。有效緯度值介於-90和90之間。
- GeoJSON
GeoJson 比 Legacy Coordinate Pairs 要強大的多,Legacy Coordinate Pairs 僅僅用來保存一個經緯度,而 GeoJson 可以用來指定點、線和多邊形。
點可以用形如[longitude, latitude]([經度,緯度])的兩個元素的數組表示:
{
"geometry": {
"type": "Point",
"coordinates": [125.6, 10.1]
}
}
線可以用一個由點組成的數組來表示:
{
"geometry": {
"type": "LineString",
"coordinates": [[125.6, 10.1],[125.6,10.2],[125.6,10.3]]
}
}
多邊形的表示方式與線一樣(都是一個由點組成的數組),但是"type"不同:
{
"geometry": {
"type": "Polygon",
"coordinates": [[125.6, 10.1],[125.5,10.2],[125.7,10.3]]
}
}
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"})
- 區別:
2dsphere索引只支持球形查詢(即球面上幾何圖形的查詢)。
2d索引支持平面查詢(即在平面上幾何圖形的查詢)和一些球形查詢。雖然2d索引支持一些球形查詢,但是對這些球形查詢使用2d索引可能會導致錯誤,例如極點附近會出現大量的扭曲變形。
2d索引只能對點進行索引。可以保存一個由點組成的數組,但是它只會被保存為由點組成的數組,不會被當成線。特別是對於"$geoWithin"查詢來說,這是一項重要的區別。如果將街道保存為由點組成的數組,那麼如果其中的某個點位於給定的形狀之內,這個文檔就會與$geoWithin相匹配。但是,由這些點組成的線並不一定完全包含在這個形狀之內。
三、地理空間查詢
可以使用多種不同類型的地理空間查詢:交集(intersection)、包含(within)以及接近(nearness)。
- $geoIntersects
定義:指出與查詢位置相交的文檔。
支持的索引:2dsphere
幾何操作符:
- $geometry (僅支持 2dsphere 索引,指定GeoJSON格式的幾何圖形)
- $geoWithin
定義:指出完全包含在某個區域的文檔。
支持的索引:2dsphere、2d
幾何操作符:
- $box(僅支持 2d 索引,查詢出矩形範圍內的所有文檔)
- $center(僅支持 2d 索引,查詢出圓形範圍內的所有文檔)
- $polygon (僅支持 2d 索引,查詢出多邊形範圍內的所有文檔)
- $centerSphere(支持 2d 索引和 2dsphere 索引,查詢出球面圓形範圍內的所有文檔)
- $geometry (僅支持 2dsphere 索引,指定GeoJSON格式的幾何圖形)
- $near
定義:指出與查詢位置從最近到最遠的文檔。
支持的索引:2dsphere、2d
幾何操作符:
- $maxDistance (支持 2dsphere 索引和 2d 索引,指定查詢結果的最大距離)
- $minDistance (僅支持 2dsphere 索引,指定查詢結果的最小距離)
- $geometry (僅支持 2dsphere 索引,指定GeoJSON格式的點)
備註:$minDistance 官方文檔說僅支持 2dsphere 索引,但是我實踐證明 $minDistance 也支持 2d 索引,大家可以試試看,這裡保留爭議。
- $nearSphere
定義:使用球面幾何計算近球面的距離,指出與查詢位置從最近到最遠的文檔。
支持的索引:2dsphere、2d
幾何操作符:
- $maxDistance (支持 2dsphere 索引和 2d 索引,指定查詢結果的最大距離)
- $minDistance (僅支持 2dsphere 索引,指定查詢結果的最小距離)
- $geometry (僅支持 2dsphere 索引,指定GeoJSON格式的點)
備註:$minDistance 官方文檔說僅支持 2dsphere 索引,但是我實踐證明 $minDistance 也支持 2d 索引,大家可以試試看,這裡保留爭議。
四、實踐
- "$geoIntersects" 操作符找出與查詢位置相交的文檔 ?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ]] ] } } } } )View Code
tips:coordinates 表示多邊形,第一個點 和 最後一個點 必須相同,因為這樣才能拼成一個對邊形呀!
- "$geoWithin"操作符找出完全包含在某個區域的文檔?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 ]] ] } } } } )View Code
- "$geoWithin"操作符找出矩形範圍內的文檔?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $geoWithin: { $box: [ [ 118.0,24.0 ], [ 120.0,30.0 ] ] } } } )View Code
tips:"$box"接受一個兩元素的數組:第一個元素指定左下角的坐標,第二個元素指定右上角的坐標。
- "$geoWithin"操作符找出圓形範圍內的文檔?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $geoWithin: { $center: [ [ 118.067678, 24.444373] , 10 ] } } } )View Code
tips:"$center"接受一個兩元素數組作為參數:第一個元素是一個點,用於指定圓心;第二個參數用於指定半徑。
- "$geoWithin"操作符找出多邊形範圍內的文檔?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $geoWithin: { $polygon: [ [ 118.067678 , 24.444373 ], [ 119.067678 , 25.444373 ], [ 120.067678 , 26.444373 ] ] } } } )View Code
tips:"$polygon" 列表中的最後一個點會被連接到第一個點,以便組成多邊形。
- "$geoWithin"操作符找出球面圓形範圍內的文檔?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $geoWithin: { $centerSphere: [ [ 118.067678, 24.444373 ], 10/3963.2 ] } } } )View Code
tips:該例子表示 距離 [118.067678, 24.444373] 中心點10 英里範圍內的所有文檔,查詢通過除以地球的大約赤道半徑(3963.2英里)將距離轉換為弧度。
- $near 找出距離一個點相應距離內的文檔?
geoJson 格式(僅支持 2dsphere 索引):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $near: { $geometry: { type: "Point" , coordinates: [ 118.067678 , 24.444373 ] }, $maxDistance: 3000, $minDistance: 0 } } } )View Code
Legacy Coordinate Pairs 格式(僅支持 2d 索引):
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
db.driverPoint.find( { coordinate: { $near: [ 118.193828 , 24.492242 ], $maxDistance: 0.10 } } )View Code
tips:1、$near 當用 geoJson 格式表示時打完, 距離單位是米(meter)。
2、$near 當用 Legacy Coordinate Pairs 格式表示時,距離單位是弧度(radian)。
3、"$near"是唯一一個會對查詢結果進行自動排序的地理空間操作符:"$near"的返回結果是按照距離由近及遠排序的。
五、結語
怎麼說呢?學習這方面的知識老是給我一種特別亂的感覺。稍微總結下吧!MongoDB 對於地理空間的查詢 是基於 它對 地理空間的索引(即2dsphere 和 2d)來實現的。所以,我們只要搞清楚什麼時候 該建立 2dsphere 索引,什麼時候該建立 2d 索引,然後再找適用於該索引的操作符就很清晰明瞭了!總之,geoJSON 格式保存的經緯度一定 建立 2dsphere 索引。Legacy Coordinate Pairs 格式保存的經緯度 僅在表示 平面地圖的時候才考慮建立 2d 索引,其他情況還是選擇 2dsphere 索引。
Spring Data MongoDB 中對地理位置的查詢可參考 https://github.com/JMCuixy/SpringDataMongoDB 中單元測試的 Test03.java。
參考資料:
1、《MongoDB 權威指南第二版》
2、https://docs.mongodb.com/manual/reference/operator/query-geospatial/