9

我在 mongo 集合中有约 400K 文档,所有文档的几何形状均为type:Polygon. 2dsphere由于几何显然具有自相交,因此无法像当前那样向数据添加索引。

过去,我们有一个 hacky 解决方法,即在 mongoose 保存钩子上计算几何体的边界框,然后索引它而不是几何体本身,但我们想简化事情并只使用实际的几何体。

到目前为止,我已经尝试使用 turf如下(这是一个名为 的函数的主体fix):

let geom = turf.polygon(geometry.coordinates);
geom = turf.simplify(geom, { tolerance: 1e-7 }); 
geom = turf.cleanCoords(geom); 
geom = turf.unkinkPolygon(geom);
geom = turf.combine(geom);
return geom.features[0].geometry;

最重要的功能是unkinkPolygons我希望它完全符合我的要求,即使几何图形足够好以便被索引。这simplify可能没有帮助,但我添加它是为了很好的衡量标准。clean那里是因为unkink抱怨它的输入,并且那里是为了将一个scombine数组变成一个 s 。实际上,仍然对它的输入不满意,所以我不得不编写一个如下的 hacky 函数来抖动重复的顶点,这会修改传递给之前的:PolygonMultiPolygonunkinkgeomunkink

function jitterDups(geom) {
  let coords = geom.geometry.coordinates;
  let points = new Set();
  for (let ii = 0; ii < coords.length; ii++) {
    // last coords is allowed to match first, not sure if it must match.
    let endsMatch = coords[ii][0].join(",") === coords[ii][coords[ii].length - 1].join(",");

    for (let jj = 0; jj < coords[ii].length - (endsMatch ? 1 : 0); jj++) {
      let str = coords[ii][jj].join(",");

      while (points.has(str)) {
        coords[ii][jj][0] += 1e-8; // if you make this too small it doesn't do the job
        if (jj === 0 && endsMatch) {
          coords[ii][coords[ii].length - 1][0] = coords[ii][jj][0];
        }
        str = coords[ii][jj].join(",");
      }
      points.add(str);
    }
  }
}

然而,即使在所有这些 mongo 之后仍然抱怨。

这是一些示例原始Polygon输入:

{ type: "Polygon", coordinates: [ [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027535925691804, 51.5122814221859 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027638484531731, 51.5122996934574 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027672409315982, 51.5123868001613 ], [ -0.027667905522642, 51.5123866344944 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.02764931654289, 51.512375566682 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.027542009179339, 51.5122867222457 ] ], [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ], [ -0.027542009179339, 51.5122867222457 ] ] ] }

通过上述修复管道后的相同数据:

{ type: "MultiPolygon", coordinates: [ [ [ [ -0.027560309178214, 51.5123001412876 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.02754202884162257, 51.51228674398443 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ] ] ], [ [ [ -0.02754202884162257, 51.51228674398443 ], [ -0.02754202882236209, 51.51228674396312 ], [ -0.027541999179339, 51.5122867222457 ], [ -0.02754202884162257, 51.51228674398443 ] ] ] ] }

以下是索引创建时引发的错误的相关位:

Edges 0 and 9 cross.
Edge locations in degrees: [-0.0275603, 51.5123001]-[-0.0275420, 51.5122867] and [-0.0275420, 51.5122867]-[-0.0275579, 51.5122984]
"code" : 16755,
"codeName" : "Location16755"

我的问题是:中是否有错误turf,或者在保持 mongo 快乐方面它没有做我需要的事情?还有关于2dshpere索引在“修复”方面的确切需求的任何文档吗?此外,是否有人对我可以使用哪些其他工具来修复数据有建议,例如mapshaper或 PostGIS 的ST_MakeValid

请注意,一旦现有数据被修复,我还需要一个用于即时修复新数据的解决方案(理想情况下与节点一起工作的东西)。

Mongo 版本:3.4.14(或任何更高版本的 3.x)

4

1 回答 1

2

这里的问题不是多边形与自身相交,而是多边形中有一个(小)洞,由 4 个点组成,与外部共享一个点。所以这个洞“接触”了外部,而不是与之相交,但这是不允许的。您可以使用具有微小值的 Shapely 缓冲区来修复此类情况,例如:

shp = shapely.geometry.shape({ "type": "Polygon", "coordinates": [ [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027535822940572, 51.512281465421 ], [ -0.027535925691804, 51.5122814221859 ], [ -0.027589474043984, 51.5122605515771 ], [ -0.027638484531731, 51.5122996934574 ], [ -0.027682911101528, 51.5123351881505 ], [ -0.027689915350493, 51.5123872384419 ], [ -0.027672409315982, 51.5123868001613 ], [ -0.027667905522642, 51.5123866344944 ], [ -0.027663068941865, 51.5123864992013 ], [ -0.02764931654289, 51.512375566682 ], [ -0.027552504539425, 51.5122983194123 ], [ -0.027542009179339, 51.5122867222457 ] ], [ [ -0.027542009179339, 51.5122867222457 ], [ -0.027557948301911, 51.5122984109658 ], [ -0.027560309178214, 51.5123001412876 ], [ -0.027542009179339, 51.5122867222457 ] ] ] })
shp = shp.buffer(1e-12, resolution=0)
geojson = shapely.geometry.mapping(shp)
于 2018-10-02T08:19:47.260 回答