如何修复geojson以满足mongodb 2dsphere索引的需求

dan*_*man 5 postgis mongodb geojson turfjs mapshaper

我在mongo集合中有~400K文档,所有文档都具有几何type:Polygon.2dsphere由于几何体显然具有自交叉,因此无法像当前所示那样为数据添加索引.

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

到目前为止,我尝试使用草皮如下(这是一个函数的主体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;
Run Code Online (Sandbox Code Playgroud)

最重要的功能是unkinkPolygons我希望它能完全符合我的要求,即使几何体足够好以便进行索引.这simplify可能没什么用,但我把它添加进去了.那clean是因为unkink抱怨它的输入,并且combine是将Polygons 数组转换成单个数组MultiPolygon.实际上,unkink仍然不满意它的输入,所以我不得不编写一个hacky函数,如下所示,抖动重复的顶点,这会修改geom之前传递给unkink:

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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然而,即使在所有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 ] ] ] }
Run Code Online (Sandbox Code Playgroud)

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

{ 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 ] ] ] ] }
Run Code Online (Sandbox Code Playgroud)

这是由索引创建吐出的错误的相关位:

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"
Run Code Online (Sandbox Code Playgroud)

我的问题是:turf在保持mongo快乐的方面,是否存在错误,或者它没有做我需要的东西?还有关于2dshpere"修复"指数究竟需要什么的任何文档吗?此外,是否有人建议我可以使用哪些其他工具来修复数据,例如mapshaper或PostGIS的ST_MakeValid.

请注意,一旦现有数据得到修复,我还需要一个解决方案来动态修复新数据(理想情况下,这对节点很有用).

Mongo版本:3.4.14(或任何后来的3.x)

Zoh*_*uda 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)
Run Code Online (Sandbox Code Playgroud)