mongoDB地理位置查询
Mongodb的特色:MongoDB地理空间查询可以解释平面或球体上的几何图形。2dsphere索引只支持球形查询(即在球形表面上解释几何图形的查询)。2d索引支持平面查询(即在平面上解释几何图形的查询)和一些球形查询。虽然2d索引支持一些球形查询,但是对这些球形查询使用2d索引可能会导致错误。如果可能,对球形查询使用2dsphere索引。MongoDB提供了以下地理空间查询操作:NameDesc
目录
给出经纬度和半径构建圆形范围,从mongodb中查询是否与其他相交,重叠。
- MongoDB地理空间查询可以解释平面或球体上的几何图形。
- 2dsphere索引只支持球形查询(即在球形表面上解释几何图形的查询)。
- 2d索引支持平面查询(即在平面上解释几何图形的查询)和一些球形查询。虽然2d索引支持一些球形查询,但是对这些球形查询使用2d索引可能会导致错误。如果可能,对球形查询使用2dsphere索引。
地理空间查询运算符:
几何操作符说明:
Name | Description |
$box | 使用传统坐标对指定一个用于$geoWithin查询的矩形框 。2D索引支撑 $box。 |
$center | 使用坐标(经纬度)和距离第一 一个圆进行范围查询。2D索引支撑$center。 |
$centerSphere | 在使用球形几何图形时,使用传统坐标或GeoJSON格式指定一个圆来进行$geoWithin查询(在球面上定义一个园,进行范围查询)。2dsphere和 2D索引支持$centerSphere。 |
$geometry | 地理空间几何查询符, 指定GeoJSON格式的类型后。可以配合用以下地理空间查询操作符 $geoWithin,$geoIntersects,$near, $nearSphere。$geometry,进行地理查询. |
$maxDistance | 指定最大距离以限制$near 和$nearSphere查询的结果。2dsphere和2D索引支持 $maxDistance。 |
$minDistance | 指定最小距离以限制$near 和$nearSphere查询的结果。2dsphere 仅与索引一起使用。 |
$polygon | 指定要使用旧式坐标对进行 $geoWithin查询的面。2D索引支撑 $center。 |
$uniqueDocs | 不推荐使用。修改$geoWithin和$near查询以确保即使文档多次匹配查询,查询也会返回一次文档。 |
查询操作符说明:
$geoIntersects | 查询与指定的GeoJSON几何形状相交的几何形状。2dsphere索引支持 $geoIntersects。 |
$geoWithin | 查询与指定的GeoJSON几何重叠的几何。该2dsphere和2D指标支持 $geoWithin。 |
$near | (平面上定圆形,查询该圆形内的数据)返回点附近的地理空间对象。需要地理空间索引。该2dsphere和2D指标支持 $near。 |
$nearSphere | (球面上定圆形,查询该圆形内的数据)返回球体上某个点附近的地理空间对象。需要地理空间索引。该2dsphere和2D指标支持 $nearSphere。 |
地理空间聚集管道符:
Stage | Stage |
$geoNear | 根据与地理空间点的接近程度返回有序的文档流。为地理空间数据合并了$match、$sort和$limit功能。输出文档包含一个额外的距离字段,并且可以包含一个位置标识符字段。 $geoNear需要一个地理空间索引。 |
准备工作:
1. mongodb中先插入几条数据:
db.ZHXCDIS.insert({
"ZXDJDWD" : [ 69.3, 89.2 ], //经纬度
"BJ" : 10.0, //半径
"name": "面馆1"
})
db.ZHXCDIS.insert({
"ZXDJDWD" : [ 69.4, 89.3 ], //经纬度
"BJ" : 20.0, //半径
"name": "面馆2"
})
db.ZHXCDIS.insert({
"ZXDJDWD" : [ 69.5, 89.4 ], //经纬度
"BJ" : 30.0, //半径
"name": "面馆3"
})
2. 先给经纬度字段建立2dsphere索引。
▼ 建立索引的字段一定要是数组或者对象,不然创建不了 # 2d不能写成2D
- 2d索引,用于存储和查找平面上的点。(二维平面)
- 2dsphere索引,用于存储和查找球面上的点,同时它支持数据存储为GeoJSON 和传统坐标。(查询地球上真实的距离用该索引)
▼创建索引的例子:
- db.你的Collection名字.createIndex({ 改成你要建立索引的字段名字 : "索引名字" })
- db.ZHXCDIS.createIndex({ZXDJDWD : "2dsphere"})
地理位置查询案例:
-
给出一个经纬度,查询mongodb中与其他经纬度的距离
▼3种距离单位:
- 米(meters)
- 平面单位(flat units,可以理解为经纬度的“一度” )
- 弧度(radians) :地球表面1弧度距离约为6378137米, 0.001弧度距离为6378米
mongodb的查询语句
- ZHXCDIS是Collection的名字,其他都是固定写法
- 若需要距离单位为米 则指定 distanceMultiplier: 6378137,
- 千米 distanceMultiplier: 6378 (不指定该属性,则返回两点相差的弧度)
db.ZHXCDIS.aggregate({
$geoNear:{
near:[0,0], //中括号中写经度纬度
spherical:true, //表示使用是否采用球面几何计算 (false则返回平面单位)
distanceMultiplier: 6378137, //要和spherical:true同时使用,
distanceField:"distance" //给返回结果起一个叫distance的别名
}})/**
查询的结果,会根据距离大小自动排序{
"_id" : ObjectId("6204c76ef73f965f1d3e5542"),
"ZXDJDWD" : [
69.3,
89.2
],
"BJ" : 10.0,
"name" : "面馆1",
"distance" : 5617637.110091394 //相差的距离
}
{
"_id" : ObjectId("6204c76ef73f965f1d3e5543"),
"ZXDJDWD" : [
69.4,
89.3
],
"BJ" : 20.0,
"name" : "面馆2",
"distance" : 5624962.261152434 //相差的距离
}
{
"_id" : ObjectId("6204c76ef73f965f1d3e5544"),
"ZXDJDWD" : [
69.5,
89.4
],
"BJ" : 30.0,
"name" : "面馆3",
"distance" : 5632325.494106238 //相差的距离
}
**/
Java代码实现:
Aggregation aggregation = Aggregation.newAggregation(Aggregation.geoNear(
NearQuery.near(ZXDJD, ZXDWD) //经纬度
.spherical(true) //是否采用球面几何计算
.distanceMultiplier(6378137D), "distance")); //是别名distance
/*参数一:构造的条件;参数二:表示你要查询mongodb的哪个Collection;参数三:查询的结果封装到实体类*/
List<DisasterOtherDTO> DisasterOtherDTOs = mongoTemplate.aggregate(aggregation, "ZHXCDIS",DisasterOtherDTO.class ).getMappedResults();
点面关系
-
给出经纬度和半径,从mongodb中查询该范围内的所有数
mongodb查询语句
//ZHXCDIS是collection的名字,ZXDJDWD是添加了2dsphere的字段,其他固定写法
//查询最远12500米半径,最近200米半径圆形范围内的所有数据
db.ZHXCDIS.find({
ZXDJDWD:{
$nearSphere:{
type: "point", //指定GeoJson类型为一个点,
coordinates: [117.0,39.0] //经纬度(坐标)
},
$maxDistance: 12500, //最远距离 ,查询坐标12500米内(单位:米)
$minDistance:200 //最近距离,查询坐标200米外的数据(单位:米)
}
})
Java代码实现
Point point = new Point(117.0, 39.0);
Query query = new Query(Criteria.where("ZXDJDWD") //经纬度字段
.nearSphere(point) //经纬度
.maxDistance(12500d / 6378137d) //最大距离 (这里一定要除以地球的半径(6378137),得出地弧度)
.minDistance(200d / 6378137d)); //最近距离 (这里一定要除以地球的半径(6378137))
List<DisasterEntity> disasterEntities = mongoTemplate.find(query, DisasterEntity.class);
-
给出经纬度和半径构建圆形范围,从mongodb中查询是否与其他相交,重叠。
ps:Mongodb中的点面关系,没有直接判断是否相交的功能,只能曲线救国。拿两个圆形范围的半径和,在得到两个圆心的距离,相比即可得出是否相交
逻辑:
- 计算出经纬度和其他经纬度距离
- 计算出半径和
- 筛选出半径和比距离大的数据
mongodb实现
db.getCollection("ZHXCDIS").aggregate([
{$geoNear:{ //第一段聚合,先查询出距离,赋值给distance
near:[69.3,89.2],
spherical:true,
distanceMultiplier: 6378137,
distanceField:"distance",
}},
{"$addFields":{ //第二段聚合,计算出半径和赋值给num
"num": {$add:["$BJ",20]}, //这个相当于我们给半径加20,然后将结果赋值给num
}},
{
"$redact": { //第三阶段,筛选出半径和大于等于距离的数据,证明有相交
"$cond": [
{ "$gte": [ "$num","$distance"] }, //$num和$distance等同于上面的
"$$KEEP",
"$$PRUNE"
]
}
}
])//发布博客后格式乱了,看这里
$redact:
根据字段所处的document结构的级别,对文档进行“修剪”,它通常和“判断语句if-else”结合使用即$cond。
$redact可选值有3个:
1)$$DESCEND:包含当前document级别的所有fields。当前级别字段的内嵌文档将会被继续检测。
2)$$PRUNE:不包含当前文档或者内嵌文档级别的所有字段,不会继续检测此级别的其他字段,即使这些字段的内嵌文档持有相同的访问级别。
3)$$KEEP:包含当前文档或内嵌文档级别的所有字段,不再继续检测此级别的其他字段,即使这些字段的内嵌文档中持有不同的访问级别。
java代码实现
/**
* 判断新建灾害现场和未结束的是否相交
* @param ZXDJD 经度
* @param ZXDWD 维度
* @param BJ 半径
* @return 返回相交距离最近的数据
*/
private DisasterOtherDTO crossDisasterRange(Double ZXDJD, Double ZXDWD, Double BJ){
//计算出半径和,赋值给num属性
ArithmeticOperators.Add add = ArithmeticOperators.Add.valueOf("$BJ").add(BJ);
AddFieldsOperation build =
Aggregation.addFields().addFieldWithValue("num",add).build();
//先查询出距离,赋值给distance
GeoNearOperation distance1 = Aggregation.geoNear(
NearQuery.near(ZXDJD,ZXDWD)
.spherical(true)
.distanceMultiplier(6378137d)), "distance");
//筛选出半径和比距离大的数据,证明有相交
Criteria condition = Criteria.where("num").gte("$distance");
AggregationExpression conditionExpression = ConditionalOperators.Cond.when(condition).thenValueOf("$$KEEP")
.otherwise("$$PRUNE");
RedactOperation redact = Aggregation.redact(conditionExpression);
//管道聚合查询,返回有相交的数据
Aggregation aggregation = Aggregation.newAggregation(distance1,build,redact);
List<DisasterOtherDTO> results = mongoTemplate.aggregate(aggregation, "ZHXCDIS", DisasterOtherDTO.class).getMappedResults();
return results;
}
多个经纬度构建的几何范围查询
插入测试数据
ps: 最后的一个经纬度一定要和第一个相同,这样才能闭合
db.demo.insertMany([{
"name":"测试地点1",
jw:{ //这是一个geoJson对象
"type":"Polygon", // 表示多边形几何
"coordinates":[[ //结构必须是 [[[经度,维度],[经度,维度]]],三个中括号不可变
[112.67097473144531,33.338559712732525], //首位必须一样才能闭合
[112.78495788574217,33.338559712732525],
[112.78495788574217,33.40679724595547],
[112.67097473144531,33.40679724595547],
[112.67097473144531,33.338559712732525] //首位必须一样才能闭合
]]
}
},{
"name":"测试地点2",
jw:{
"type":"Polygon",
"coordinates":[[
[112.79869079589844,33.25476662931654],
[112.92915344238281,33.25476662931654],
[112.92915344238281,33.4039312002347],
[112.79869079589844,33.4039312002347],
[112.79869079589844,33.25476662931654]
]]
}
},
{
"name":"测试地点3",
jw:{
"type":"Polygon",
"coordinates":[[
[112.66273498535156,33.246153192679756],
[112.7911376953125,33.246153192679756],
[112.7911376953125,33.31388929028309],
[112.66273498535156,33.31388929028309],
[112.66273498535156,33.246153192679756]
]]
}
},
{
"name":"测试地点4",
jw:{
"type":"Polygon",
"coordinates":[[
[112.59613037109375,33.34085428063472],
[112.65380859375,33.34085428063472],
[112.65380859375,33.40622404437544],
[112.59613037109375,33.40622404437544],
[112.59613037109375,33.34085428063472]
]]
}
}
])db.demo.renameCollection('coordinates')
建索引
db.demo.createIndex(jw.coordinates,"2dsphere")
判断几何图形面积是否相交
mongodb案例:
db.demo.find({
jw: { // coordinates是保存经纬度的字段
$geoIntersects: { //$geoIntersects: 查询相交的几何形状,$geoWithin:查询重叠
$geometry: { // 为地理空间查询运算符指定GeoJSON格式的几何
type: "Polygon" , // 指定GeoJSON格式的几何类型为:Polygon,表示查询多边形
coordinates: [[ //经纬度构建的多半形,
[112.76195526123045,33.23064686752627],
[112.91267395019531,33.23064686752627],
[112.91267395019531,33.37411872061922],
[112.76195526123045, 33.37411872061922],
[112.76195526123045,33.23064686752627]
]]
}
}
}
}
)注意:
- 在插入数据的时候coordinates(经纬度)的结构必须是 [[[经度,维度],[经度,维度]]],假如说结构写成[[经度,维度],[经度,维度]],会出现以下的问题。
- 当你给出经纬度构建一个几何图形,查询相交的时候,必须像图里红色四边形一样,与图中黑色四边形的角(经纬度点)相交才能查询出相交的数据。
- 如果只是像蓝色四边形一样,与黑色多边形的边相交,mongodb查询不到相交的数据。
Java代码实现
如有问题还请大佬们多多指正
更多推荐
所有评论(0)