以下测试均基于mongo v4.0 win10
一、GeoJSON
GeoJSON是一种基于json的经纬度描述数据格式。在这里主要服务于2dsphere索引查询。
基本格式 <type:"",coordinates:[]> ,type 对应的类型有Point、LineString(老版本有写Line的)、Polygon、MultiPoint、MultiLineString、MultiPolygon。coordinates对应格式可参照【表1】。
更详细的请自行百度。
二、2dsphere索引。
基于地球的经纬度。举例如下
存在点 【28,2】,则以下区域是否与该点存在交集($geoIntersects)的情况为
type | coordinates | 是否包含 | |
1 | Point | [28,2] | 是 |
2 | LineString | [[28,1],[28,3]] | 是 |
3 | LineString | [[28,3],[28,1]] | 是 |
4 | LineString | [[27,2],[29,2]] | 否 |
5 | LineString | [[29,2],[27,2]] | 否 |
6 | Polygon | [[[27,1],[27,3],[29,3],[29,1],[27,1]]] | 是 |
7 | Polygon | [[[27,3],[27,1],[29,1],[29,3],[27,3]]] | 是 |
8 | Polygon | [[[29,1],[29,3],[27,3],[27,1],[29,1]]] | 是 |
9 | Polygon | [[[29,3],[29,1],[27,1],[27,3],[29,3]]] | 是 |
从LineString的四个例子中我们可以得出:getIntersects对LineString的支持性并不高,不过考虑到现实场景中的查询,都是以一个面积为参考的,所以LineString的测验只了解一个结果,不再深入研究。
从Polygon的四个例子中我们可以暂时得出:GeoJSON的数组是不在乎顺序的,为了验证这个观点,我们再补做方向性的测试数据。
三、geoIntersects的方向性研究
type | coordinates | 是否包含 | |
1 | Polygon | [[[27,1],[29,1],[29,3],[27,3],[27,1]]] | 是 |
2 | Polygon | [[[27,3],[29,3],[29,1],[27,1],[27,3]]] | 是 |
3 | Polygon | [[[29,3],[27,3],[27,1],[29,1],[29,3]]] | 是 |
4 | Polygon | [[[29,1],[27,1],[27,3],[29,3],[29,1]]] | 是 |
5 | Polygon | [[[29,1],[-27,3],[-27,3],[29,3],[29,1]]] | 是 |
6 | |||
7 | Polygon | [[[-179,1],[27,1],[27,3],[-179,3],[-179,1]]] | 是 |
8 | Polygon | [[[0,90],[180,90],[180,-90],[0,-90],[0,90]]] | 否 |
9 | Polygon | [[[0,90],[0,-90],[180,-90],[180,90],[0,90]]] | 是 |
10 | Polygon | [[[0,-90],[0,90],[180,90],[180,-90],[0,-90]]] | 否 |
11 | Polygon | [[[0,-90],[180,-90],[180,90],[0,90],[0,-90]]] | 是 |
12 | Polygon | [[[180,-90],[0,-90],[0,90],[180,90],[180,-90]]] | 否 |
13 | Polygon | [[[180,90],[0,90],[0,-90],[180,-90],[180,90]]] | 是 |
14 | Polygon | [[[180,90],[180,-90],[0,-90],[0,90],[180,90]]] | 否 |
16 | Polygon | [[[180,-90],[180,90],[0,90],[0,-90],[180,-90]]] | 是 |
根据【表2编号1-5】到验证了以上的猜想,GeoJSON数组代表的区域是 : 数组中的点连接成线,将经纬度网划分成两块,取面积小的那一块作为选定区域,进行筛选判定。
但是如果所选范围扩大到整个半球甚至更大,GeoJSON的数组该如何选择判定区域?从【表2编号7-16】中,我们什么也不能得出来,整个查询呈现出一种混乱的状态。又进行大量的对比实验,做了各种统计分析图后,结论依旧混乱,我甚至还去文具店买了一个地球仪。。。
网上进行了大量查询,最后还是在官网找到了一些信息。3.0版本以后$within(表示包含)、$geoIntersects的Polygon查询支持新的参数:crs。可以指定特殊的索引范围划分方式。如EPSG:4326,判定区域时具有严格的逆时针顺序。更多更全的知识请移步 https://docs.mongodb.com/manual/reference/operator/query/geoIntersects/#geointersects-big-poly
但是经过测验发现,4.0版本默认了EPSG:4326,加与不加,影响不大,测验如下($within与$geoIntersects结果一致):
1 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,1],[29,1],[29,3],[27,3],[27,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); 2 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } 3 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[29,1],[29,3],[27,3],[27,1],[29,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); 4 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } 5 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[29,3],[27,3],[27,1],[29,1],[29,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); 6 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } 7 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,3],[27,1],[29,1],[29,3],[27,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); 8 { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } 9 > db.people.find({"add":{"$within":{"$geometry":{"type":"Polygon","coordinates":[[[27,1],[27,3],[29,3],[29,1],[27,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
甚至会出现这种莫名其妙的结果:
> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[79,1],[79,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); { "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } } { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } { "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } } > db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[124,1],[124,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); { "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } } { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } > db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,1],[120,1],[120,3],[0,3],[0,1]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); { "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } } { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } { "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } }
可以看出,mongo对于2ds索引的支持度并不高。那么我们完全按照官方文档的格式进行测试,看能不能得出一个有规律的结果。
官方文档的格式:
db.places.find( { loc: { $geoIntersects: { $geometry: { type : "Polygon", coordinates: [ [ [ -100, 60 ], [ -100, 0 ], [ -100, -60 ], [ 100, -60 ], [ 100, 60 ], [ -100, 60 ] ] ], crs: { type: "name", properties: { name: "urn:x-mongodb:crs:strictwinding:EPSG:4326" } } } } } } )
格式定义如下:
1、首尾相接(以前测试的数组都是满足的)
2、逆时针方向书写
> db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,3],[0,-1],[179,-1],[179,3],[0,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}}); { "_id" : ObjectId("5bcafe07377b37ef8e160bc7"), "add" : { "type" : "Point", "coordinates" : [ 2, 2 ] } } { "_id" : ObjectId("5bcb0145377b37ef8e160bcb"), "add" : { "type" : "Point", "coordinates" : [ 2, 4 ] } } { "_id" : ObjectId("5bb4c46d0c2287bbf737427e"), "name" : "王五", "age" : 26, "sex" : "男", "add" : { "type" : "Point", "coordinates" : [ 28, 2 ] } } { "_id" : ObjectId("5bcb0158377b37ef8e160bcc"), "add" : { "type" : "Point", "coordinates" : [ 2, -2 ] } } { "_id" : ObjectId("5bb4290d0c2287bbf737427d"), "name" : "ww wu", "age" : 40, "add" : { "type" : "Point", "coordinates" : [ 50, 2 ] } } > db.people.find({"add":{"$geoIntersects":{"$geometry":{"type":"Polygon","coordinates":[[[0,3],[0,1],[179,1],[179,3],[0,3]]],crs:{type:"name",properties:{name:"urn:x-mongodb:crs:strictwinding:EPSG:4326"}}}}}});
讲道理,完全没有道理。
总结,2ds对于小范围的within(包含),geoIntersects(相交)的支持是正常的,超过半球或者接近半球时,判定方式让人摸不着头脑。如果有懂的大神看见了。麻烦指教以下。