MongoDB - 两个多边形的地理空间交集

Vir*_*raj 8 javascript geospatial mongodb mongodb-query aggregation-framework

有没有什么方法可以使用符合以下条件的mongodb地理空间查询来查询和获取位置数据?

  • 获取两个框之间或两个多边形之间交叉点的所有位置.

例如,下面我们可以在查询输出中只获得黄色区域内的那些位置,这些位置实际上是紫色和红色几何对象[多边形]的公共区域?

在此输入图像描述

我到目前为止对mongodb文档的研究

用例

    db.places.find( {
   loc: { $geoWithin: { $box:  [ [ 0, 0 ], [ 100, 100 ] ] } }
} )
Run Code Online (Sandbox Code Playgroud)

上面的查询提供了一个矩形几何区域内的结果[我正在寻找两个这样的单个查询共有的位置]

    db.places.find( {
   loc: { $geoWithin: { $box:  [ [ 0, 0 ], [ 100, 100 ] ] } }
} )

    db.places.find( {
   loc: { $geoWithin: { $box:  [ [ 50, 50 ], [ 90, 120 ] ] } }
} )
Run Code Online (Sandbox Code Playgroud)

Nei*_*unn 6

因此,以一种清新的头脑看待这个问题,答案就是盯着我.您已经说过的关键是您希望在单个响应中找到两个查询的"交集".

另一种看待这种情况的方法是,您希望第一个查询绑定的所有点然后为第二个查询"输入",依此类推.这实际上是一个交集所做的,但逻辑实际上是文字的.

因此,只需使用聚合框架链接匹配的查询.举一个简单的例子,请考虑以下文档:

{ "loc" : { "type" : "Point", "coordinates" : [ 4, 4 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 8, 8 ] } }
{ "loc" : { "type" : "Point", "coordinates" : [ 12, 12 ] } }
Run Code Online (Sandbox Code Playgroud)

和链式聚合管道,只有两个查询:

db.geotest.aggregate([
    { "$match": {
        "loc": {
            "$geoWithin": {
                "$box": [ [0,0], [10,10] ]
            }
        }
    }},
    { "$match": {
        "loc": {
            "$geoWithin": {
                "$box": [ [5,5], [20,20] ]
            }
        }
    }}
])
Run Code Online (Sandbox Code Playgroud)

因此,如果您从逻辑上考虑,第一个结果将找到落在初始框或前两个项的范围内的点.然后,第二个查询对这些结果起作用,并且由于新的框边界开始,[5,5]因此排除了第一个点.第三点已被排除,但如果方框限制被颠倒,那么结果将只是相同的中间文件.

$geoWithin与其他各种地理函数相比,它对查询运算符的工作方式非常独特:

$ geoWithin不需要地理空间索引.但是,地理空间索引将提高查询性能.无论2dsphere二维地理空间索引支持$ geoWithin.

所以结果既好又坏.很好,你可以在没有索引的情况下进行这种类型的操作,但是很糟糕,因为一旦聚合管道在第一次查询操作之后改变了收集结果,就不能再使用索引了.因此,在支持初始Polygon/MultiPolygon之后合并"set"结果时,索引的任何性能优势都会丢失.


出于这个原因,我仍然建议你计算发给MongoDB的查询的"外部"的交集边界.尽管聚合框架可以通过管道的"链式"特性来实现这一点,即使生成的交叉点会变得越来越小,但最佳性能是具有正确边界的单个查询,可以使用所有索引优势.

有各种方法可以做到这一点,但这里的参考是使用JSTS库的实现,JSTS库是流行的Java JTS库的JavaScript端口.可能有其他语言或其他语言端口,但这有简单的GeoJSON解析和内置方法,例如获取交集边界:

var async = require('async');
    util = require('util'),
    jsts = require('jsts'),
    mongo = require('mongodb'),
    MongoClient = mongo.MongoClient;

var parser = new jsts.io.GeoJSONParser();

var polys= [
  {
    type: 'Polygon',
    coordinates: [[
      [ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 10, 0 ], [ 0, 0 ]
    ]]
  },
  {
    type: 'Polygon',
    coordinates: [[
      [ 5, 5 ], [ 5, 20 ], [ 20, 20 ], [ 20, 5 ], [ 5, 5 ]
    ]]
  }
];

var points = [
  { type: 'Point', coordinates: [ 4, 4 ]  },
  { type: 'Point', coordinates: [ 8, 8 ]  },
  { type: 'Point', coordinates: [ 12, 12 ] }
];

MongoClient.connect('mongodb://localhost/test',function(err,db) {

  db.collection('geotest',function(err,geo) {

    if (err) throw err;

    async.series(
      [
        // Insert some data
        function(callback) {
          var bulk = geo.initializeOrderedBulkOp();
          bulk.find({}).remove();
          async.each(points,function(point,callback) {
            bulk.insert({ "loc": point });
            callback();
          },function(err) {
            bulk.execute(callback);
          });
        },

        // Run each version of the query
        function(callback) {
          async.parallel(
            [
              // Aggregation
              function(callback) {
                var pipeline = [];
                polys.forEach(function(poly) {
                  pipeline.push({
                    "$match": {
                      "loc": {
                        "$geoWithin": {
                          "$geometry": poly
                        }
                      }
                    }
                  });
                });

                geo.aggregate(pipeline,callback);
              },

              // Using external set resolution
              function(callback) {
                var geos = polys.map(function(poly) {
                  return parser.read( poly );
                });

                var bounds = geos[0];

                for ( var x=1; x<geos.length; x++ ) {
                  bounds = bounds.intersection( geos[x] );
                }

                var coords = parser.write( bounds );

                geo.find({
                  "loc": {
                    "$geoWithin": {
                      "$geometry": coords
                    }
                  }
                }).toArray(callback);
              }
            ],
            callback
          );
        }
      ],
      function(err,results) {
        if (err) throw err;
        console.log(
          util.inspect( results.slice(-1), false, 12, true ) );
        db.close();
      }
    );

  });

});
Run Code Online (Sandbox Code Playgroud)

使用完整的GeoJSON"Polygon"表示,这可以转换为JTS可以理解和使用的内容.对于真实应用程序可能会收到的任何输入都可能是这种格式,而不是应用诸如此类的便利$box.

因此,可以使用聚合框架,甚至是并行查询来合并结果的"集合".但是虽然聚合框架可能比在外部合并结果集更好,但最好的结果总是来自计算边界.