centos mongodb 第十六节课 地理空间查询 地理空间索引 2d索引

centos mongodb 第十六节课   地理空间查询  地理空间索引  2d索引

 

地理空间查询

MongoDB 支持地理空间查询,允许用户存储地理空间数据并对这些数据进行查询。MongoDB 使用两种数据类型来存储地理空间数据:GeoJSON 对象和传统坐标对。
在地理空间查询中,MongoDB 支持两种索引:2dsphere (球面)和 2d (平面)索引。
 

 GeoJSON 对象

GeoJSON 数据可以用来指定点、线和多边形等。它使用内嵌文档来指定,包含 type 和 coordinates 两个字段。type 指定 GeoJSON 对象类型,coordinates 指定对象的坐标。格式如下:
<field>: { type: <GeoJSON type>, coordinates: <coordinates> }
注意:有效经度值介于 - 180~180,有效纬度值介于 - 90~90。
 
MongoDB 支持的 GeoJSON 对象的类型如表 5.5 所示。
表 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]
}
MongoDB 在使用 GeoJSON 对象进行地理空间查询时,是基于球面来计算的。
 

传统坐标对

MongoDB 在使用传统坐标对进行距离计算时,是基于几何平面来计算的。
这种计算方式支持的索引是 2d 索引。如果手动将数据转换为 GeoJSON Point 类型,MongoDB 通过使用 2dsphere 索引支持在传统坐标对上进行球面计算。
传统坐标对有两种方式存储经纬度,即数组和嵌入文档。
数组形式格式如下:
<field>: [ <x>, <y> ]


如果是经纬度数据,那么格式如下:

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

相对应的内嵌文档格式如下:

<field>: { <field1>: <x>, <field2>: <y> }
<field>: { <field1>: <longitude>, <field2>: <latitude> }

 

2dsphere 索引

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>
            ]
        }
    }
})
需要注意,radius 参数代表的是带弧度的半径。因此,需要将参数转换为球面操作符的半径。转换时一般涉及英里与千米两种距离单位。
  • 英里:使用英里除以 3953.2。
  • 千米:使用千米除以 6378.1。
在前面插入 places 集合的数据基础上,查询距离点[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 索引

2d 索引用于非球面的二维平面。在使用 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
    }
})
 
可以看出,在二维平面上,只能按照坐标系进行直线距离计算。



 

 
 
 
 
 
posted @ 2025-07-31 23:00  huangchaolilli  阅读(15)  评论(0)    收藏  举报