使用JSTS缓冲区来识别自相交的多边形

时间:2016-03-20 20:08:30

标签: javascript geometry

我期望能够通过JSTS无法构造它们来测试自交叉多边形,或者通过添加缓冲区并在缓冲后测试它们是否是MultiPolygons但是对于某种形状这是不起作用的并且很好地超过了我的几何学能力

min-height
//a self-intersecting shape
var poly = [[0, 3], [1, 5], [3, 1], [5, 5], [6, 3], [0, 3]];

var geomFactory = new jsts.geom.GeometryFactory();

var jstsCoordinates = poly.map(function(pt) {
  return new jsts.geom.Coordinate(pt[0], pt[1]);
});

var linearRing = geomFactory.createLinearRing(jstsCoordinates);

var jstsPolygon = geomFactory.createPolygon(linearRing).buffer(1);

console.log(jstsPolygon.getGeometryType()); //this will be polygon but I thought should be MultiPolygon

var bufferedPoly = (jstsPolygon.shell.points.coordinates.map(function(pr) {
  return [pr.x, pr.y]
}))

var svg = d3.select('svg');

//add the first shape (maginified for display)
svg.selectAll('.original').data([poly]).enter().append("polygon")
    .attr("points",function(d) { 
        return d.map(function(d) {
            return [d[0] * 10 + 10, d[1]*10].join(",");
        }).join(" ");
    })
    .attr("fill", "yellow");

//add the buffered shape below it
svg.selectAll('.buffered').data([bufferedPoly]).enter().append("polygon")
    .attr("points",function(d) { 
        return d.map(function(d) {
            return [d[0] * 10 + 10, d[1]*10 + 40].join(",");
        }).join(" ");
    })
    .attr("fill", "yellow");
svg {background-color:blue}

2 个答案:

答案 0 :(得分:3)

基于使用JTS(Is there a way to convert a self intersecting polygon to a multipolygon in JTS?)的链接答案,我使用JSTS重新实现了它

只是想为寻求相同答案/功能的其他人发布代码。

通过运行代码,使用JSTS Geometry,您将返回一个有效的Geometry,无论是Polygon还是Multipolygon



/**
 * Get / create a valid version of the geometry given. If the geometry is a polygon or multi polygon, self intersections /
 * inconsistencies are fixed. Otherwise the geometry is returned.
 *
 * @param geom
 * @return a geometry
 */
var jsts_validate = function(geom) {
  if (geom instanceof jsts.geom.Polygon) {
    if (geom.isValid()) {
      geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
      return geom; // If the polygon is valid just return it
    }
    var polygonizer = new jsts.operation.polygonize.Polygonizer();
    jsts_addPolygon(geom, polygonizer);
    return jsts_toPolygonGeometry(polygonizer.getPolygons(), geom.getFactory());
  } else if (geom instanceof jsts.geom.MultiPolygon) {
    if (geom.isValid()) {
      geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
      return geom; // If the multipolygon is valid just return it
    }
    var polygonizer = new jsts.operation.polygonize.Polygonizer();

    for (var n = geom.getNumGeometries(); n > 0; n--) {
      jsts_addPolygon(geom.getGeometryN(n - 1), polygonizer);
    }
    return jsts_toPolygonGeometry(polygonizer.getPolygons(), geom.getFactory());
  } else {
    return geom; // In my case, I only care about polygon / multipolygon geometries
  }
};

/**
 * Add all line strings from the polygon given to the polygonizer given
 *
 * @param polygon polygon from which to extract line strings
 * @param polygonizer polygonizer
 */
var jsts_addPolygon = function(polygon, polygonizer) {
  jsts_addLineString(polygon.getExteriorRing(), polygonizer);

  for (var n = polygon.getNumInteriorRing(); n > 0; n--) {
    jsts_addLineString(polygon.getInteriorRingN(n), polygonizer);
  }
};

/**
 * Add the linestring given to the polygonizer
 *
 * @param linestring line string
 * @param polygonizer polygonizer
 */
var jsts_addLineString = function(lineString, polygonizer) {

  if (lineString instanceof jsts.geom.LinearRing) {
    // LinearRings are treated differently to line strings : we need a LineString NOT a LinearRing
    lineString = lineString.getFactory().createLineString(lineString.getCoordinateSequence());
  }

  // unioning the linestring with the point makes any self intersections explicit.
  var point = lineString.getFactory().createPoint(lineString.getCoordinateN(0));
  var toAdd = lineString.union(point); //geometry

  //Add result to polygonizer
  polygonizer.add(toAdd);
}

/**
 * Get a geometry from a collection of polygons.
 *
 * @param polygons collection
 * @param factory factory to generate MultiPolygon if required
 * @return null if there were no polygons, the polygon if there was only one, or a MultiPolygon containing all polygons otherwise
 */
var jsts_toPolygonGeometry = function(polygons, factory) {
  switch (polygons.size()) {
    case 0:
      return null; // No valid polygons!
    case 1:
      return polygons.iterator().next(); // single polygon - no need to wrap
    default:
      //polygons may still overlap! Need to sym difference them
      var iter = polygons.iterator();
      var ret = iter.next();
      while (iter.hasNext()) {
        ret = ret.symDifference(iter.next());
      }
      return ret;
  }
}




答案 1 :(得分:2)

让孩子们上床让我的大脑休息得足够......

这里有两种解决方案(我认为 - 这种几何形状在我的舒适区之外)

我发现Splitting self-intersecting polygon only returned one polygon in shapely in PythonIs there a way to convert a self intersecting polygon to a multipolygon in JTS?实际上包含解决方案,因为jts,jsts和shapely都是密切相关的。

第一个是,在构建线性环(在这种情况下并不简单)之后,我可以致电isSimple()来接收false

而我第二次打电话给buffer(1)误解了我给出的建议。要解决的问题是buffer(0)

//a self-intersecting shape
var poly = [
  [0, 3],
  [1, 5],
  [3, 1],
  [5, 5],
  [6, 3],
  [0, 3]
];

var geomFactory = new jsts.geom.GeometryFactory();

var jstsCoordinates = poly.map(function(pt) {
  return new jsts.geom.Coordinate(pt[0], pt[1]);
});

var linearRing = geomFactory.createLinearRing(jstsCoordinates);
// turns out you can just ask if it is simple... i.e. does not have any self intersections.
console.log(linearRing.isSimple()); //so this is false

//Ah! To split it and find out if it is self intersecting use buffer(0)
var jstsPolygon = geomFactory.createPolygon(linearRing).buffer(0);

console.log(jstsPolygon.getGeometryType()); //this will now be MultiPolygon

var svg = d3.select('svg');

if (jstsPolygon.getGeometryType() !== 'MultiPolygon') {
var bufferedPoly = (jstsPolygon.shell.points.coordinates.map(function(pr) {
  return [pr.x, pr.y];
}));

//add the buffered shape below it
svg.selectAll('.buffered').data([bufferedPoly]).enter().append("polygon")
    .attr("points",function(d) { 
        return d.map(function(d) {
            return [d[0] * 10 + 10, d[1]*10 + 40].join(",");
        }).join(" ");
    })
    .attr("fill", "yellow");
}

//add the first shape (magnified for display)
svg.selectAll('.original')
   .data([poly]).enter()
   .append("polygon")
   .attr("points",function(d) { 
        return d.map(function(d) {
            return [
              d[0] * 10 + 10, 
              d[1]*10].join(",");
        }).join(" ");
    })
    .attr("fill", "yellow");
svg {
  background-color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.0.2/jsts.min.js"></script>
<svg width=200 height=200>
  <svg>