centos mongodb 第十六节课 地理空间查询 地理空间索引 2d索引
centos mongodb 第十六节课 地理空间查询 地理空间索引 2d索引
地理空间查询
GeoJSON 对象
<field>: { type: <GeoJSON type>, coordinates: <coordinates> }
表 5.5 MongoDB 支持的 GeoJSON 对象的类型
| 类型名称 | 说明 | 索引 |
|---|---|---|
| Point | 点 | 2d、2dsphere |
| LineString | 线 | 2d、2dsphere |
| Polygon | 多边形 | 2d、2dsphere |
| MultiPoint | 多点,点集合 | 2dsphere |
| MultiLineString | 多线,线集合 | 2dsphere |
| MultiPolygon | 多个多边形 | 2dsphere |
| GeometryCollection | 混合类型 | 2dsphere |
location: { type: "Point", coordinates: [-73.856077, 40.848447] }
传统坐标对
这种计算方式支持的索引是 2d 索引。如果手动将数据转换为 GeoJSON Point 类型,MongoDB 通过使用 2dsphere 索引支持在传统坐标对上进行球面计算。
传统坐标对有两种方式存储经纬度,即数组和嵌入文档。
数组形式格式如下:
<field>: [ <x>, <y> ]
如果是经纬度数据,那么格式如下:
<field>: [ <longitude>, <latitude> ]
相对应的内嵌文档格式如下:
<field>: { <field1>: <x>, <field2>: <y> }
<field>: { <field1>: <longitude>, <field2>: <latitude> }
2dsphere 索引
1. 创建索引
db.collection.createIndex( { <location field>: "2dsphere" } )
<location field>字段对应的值为 GeoJSON 对象或传统坐标对。
例如,在数据库中创建一个地点集合,在集合中存入一些地点数据,如下所示:
db.places.insertMany([ { loc: { type: "Point", coordinates: [116.45, 39.92] }, name: "Ritan Park", category: "Park" }, { loc: { type: "Point", coordinates: [116.61, 40.08] }, name: "Beijing Airport", category: "Airport" }, { loc: { type: "Point", coordinates: [116.42, 39.92] }, name: "Xiehe Hospital", category: "Hospital" } ])
接下来在地点数据下创建 2dsphere 索引,如下所示:
db.places.createIndex({ loc: "2dsphere" })
使用 2dsphere 可以进行多种类型的查询,例如查询多边形内的位置、查询球面位置、查询与 GeoJSON 对象相交的位置、查询球面上圆内的位置。
2. 查询多边形内的位置
db.<collection>.find({ <location field>: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ <coordinates> ] } } } })
使用的操作符为$geoWithin,该操作符对索引无要求,但索引会提高查询性能。location field 对应的字段必须为 GeoJSON 格式的数据。coordinates 为多边形的坐标对数据,且第一个点与最后一个点的坐标必须相同。
例如,查询在如下多边形范围内的数据:
db.places.find({ loc: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [116.44, 39.96], [116.49, 39.96], [116.48, 39.82], [116.43, 39.90], [116.44, 39.96] ] ] } } } })
3. 查询球面上某个点附近的位置
db.<collection>.find({ <location field>: { $near: { $geometry: { type: "Point", coordinates: [ <longitude>, <latitude> ] }, $maxDistance: <distance in meters> } } })
使用的操作符为$near。type 为 Point,coordinates 为点的经纬度,$maxDistance为查询的距离范围。
下面来看一个示例,查询在点[116.42,39.92]附近 500 米范围内的位置。
// Entering editor mode (ctrl+o to finish, ctrl+c to cancel) db.places.find({ loc: { $near: { $geometry: { type: "Point", coordinates: [116.42, 39.92] }, $maxDistance: 500 } } }) { "_id": ObjectId("65e81987f9fd14b8a063f14"), "loc": { "type": "Point", "coordinates": [116.42, 39.92] }, "name": "Xiehe Hospital", "category": "Hospital" }
如果有多个结果,则查询结果会由近到远排列。
4. 查询与 GeoJSON 对象相交的位置
db.<collection>.find({ <location field>: { $geoIntersects: { $geometry: { type: "<GeoJSON object type>", coordinates: [ <coordinates> ] } } } })
在上面的语句中,$geoIntersects操作符对索引没有强制要求,但使用索引可以提高查询性能。type 为 GeoJSON 对象的数据类型,而 coordinates 为 GeoJSON 对象的数据值。
下面来看一个使用场景的示例:在路线上寻找加油站。
使用数据格式为 LineString 的 GeoJSON 对象存储路线,然后查询该路线上的加油站。
首先,插入加油站数据,命令如下:
test> use locations switched to db locations locations> .editor // Entering editor mode (ctrl+o to finish, ctrl+c to cancel) db.gasStations.insertMany([ { loc: { type: "Point", coordinates: [116.43, 39.91] }, province: "Beijing", city: "Beijing", name: "Sinopec Gas Station" }, { loc: { type: "Point", coordinates: [116.68, 39.55] }, province: "Hebei", city: "Langfang", name: "CNPC Gas Station" }, { loc: { type: "Point", coordinates: [117.97, 37.43] }, province: "Shandong", city: "Binzhou", name: "Chambroad Gas Station" } ]) acknowledged: true, insertedIds: { "0": ObjectId("65e8752623ee6f7457256ee"), "1": ObjectId("65e8752623ee6f7457256ef"), "2": ObjectId("65e8752623ee6f7457256f0") }
接下来查询路线上的加油站,命令如下:
// Entering editor mode (ctrl+o to finish, ctrl+c to cancel) db.gasStations.find({ loc: { $geoIntersects: { $geometry: { type: "LineString", coordinates: [ [116.42, 39.91], [116.43, 39.91], [116.44, 39.91], [116.45, 39.91] ] } } } }) { "_id": ObjectId("65e8752623ee6f7457256ee"), "loc": { "type": "Point", "coordinates": [116.43, 39.91] }, "province": "Beijing", "city": "Beijing", "name": "Sinopec Gas Station" }
coordinates 为路线的经纬度数据。
该路线上有一个加油站数据被查出。
5. 查询球面上圆内的位置
$geoWithin结合$centerSphere操作符。在$centerSphere参数对象中指定圆的经纬度(圆心)和半径。语法如下:db.<collection>.find({ <location field>: { $geoWithin: { $centerSphere: [ [ <longitude>, <latitude> ], <radius> ] } } })
- 英里:使用英里除以 3953.2。
- 千米:使用千米除以 6378.1。
[116.42,39.92]附近 5 千米以内的地点。 查询球面上圆内的位置// Entering editor mode (ctrl+o to finish, ctrl+c to cancel) db.places.find({ loc: { $geoWithin: { $centerSphere: [ [116.42, 39.92], 5 / 6378.1 ] } } }) { "_id": ObjectId("65e81987f9fd14b8a063f12"), "loc": { "type": "Point", "coordinates": [116.45, 39.92] }, "name": "Ritan Park", "category": "Park" }, { "_id": ObjectId("65e81987f9fd14b8a063f14"), "loc": { "type": "Point", "coordinates": [116.42, 39.92] }, "name": "Xiehe Hospital", "category": "Hospital" }
2d 索引
$nearSphere查询来实现球面计算,但是在进行球面查询时,还是推荐使用 2dsphere 索引进行替换。创建 2d 索引使用如下语法:
db.collection.createIndex( { <location field>: "2d" } )
<location field>字段对应的值为合法的坐标对。
在创建 2d 索引前,先在集合中插入数据,如下所示:
db.contacts.insertMany([ { name: "Tencent", phone: "400-700-700", address: [113.93, 22.54] }, { name: "Baidu", phone: "400-800-8888", address: [116.31, 40.06] }, { name: "Alibaba", phone: "400-800-1688", address: [116.49, 40.03] } ])
其中,address 数据为坐标对数据,在 address 字段上创建 2d 索引,如下所示:
db.contacts.createIndex({ address: "2d" })
接下来,使用$near操作符对坐标进行查询。查询语法如下:
db.<collection>.find({ <location field>: { $near: [ <longitude>, <latitude> ], $maxDistance: <distance in meters> } })
longitude:代表经度,范围为 -180-180。latitude:代表纬度,范围为 -90-90。
$maxDistance 代表距离范围,即距离上述坐标在该范围内的数据,距离单位为米。查询结果将由近到远排列。[116.48, 40.02] 附近 5 米以内的地点,可以使用如下语句:db.contacts.find({ address: { $near: [116.48, 40.02], $maxDistance: 5 } })

浙公网安备 33010602011771号