Search code examples
javascriptgoogle-maps-api-3polygonshapesself-intersection

Google Maps Polygons self intersecting detection


I'm trying to implement a polygon self intersection algorithm from Google Maps API V3 polygons.
The goal is just to detect if yes or no, a simple polygon drawn by the user is self crossing.

I have found this very interesting link, but it assumes that coordinates of the polygon's vertices are given on geoJSON format. However, this isn't my case ; I'm only able to retrieve polygons coordinates using polygon.getPath() into a polygoncomplete event.

This is how i retrieve the coordinates :

google.maps.event.addDomListener(drawingManager, 'polygoncomplete', function(polygon)
{
    var polygonBounds = polygon.getPath();
    var coordinates = [];

    for(var i = 0 ; i < polygonBounds.length ; i++)
    {            
        vertice = {
                      "Latitude" : polygonBounds.getAt(i).lat(),
                      "Longitude" : polygonBounds.getAt(i).lng()
                  }

        coordinates.push(vertice );
    }
}

How can I transform these coordinates, given by polygon.getpath() into geoJSON format ?
Is there any better way to detect if a Google Maps polygon is self-intersecting ? If so, could you please share some code sample and not just a mathematical explaination ?

PS : I've seen this link but without any code sample, I'm a little bit lost.


Solution

  • You don't need to convert them to GeoJSON to use the jsts library, you need to convert them from google.maps.LatLng objects to jsts.geom.Coordinates. Instead of using this:

    var geoJSON2JTS = function(boundaries) {
      var coordinates = [];
      for (var i = 0; i < boundaries.length; i++) {
        coordinates.push(new jsts.geom.Coordinate(
            boundaries[i][1], boundaries[i][0]));
      }
      return coordinates;
    };
    

    Use this, which will convert coordinates in a google.maps.Polygon path to the JTS format:

    var googleMaps2JTS = function(boundaries) {
      var coordinates = [];
      for (var i = 0; i < boundaries.getLength(); i++) {
        coordinates.push(new jsts.geom.Coordinate(
            boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
      }
      return coordinates;
    };
    

    then change "findSelfIntersects" like this:

    /**
     * findSelfIntersects
     *
     * Detect self-intersections in a polygon.
     *
     * @param {object} google.maps.Polygon path co-ordinates.
     * @return {array} array of points of intersections.
     */
    var findSelfIntersects = function(googlePolygonPath) {
      var coordinates = googleMaps2JTS(googlePolygonPath);
      var geometryFactory = new jsts.geom.GeometryFactory();
      var shell = geometryFactory.createLinearRing(coordinates);
      var jstsPolygon = geometryFactory.createPolygon(shell);
     
      // if the geometry is aleady a simple linear ring, do not
      // try to find self intersection points.
      var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
      if (validator.isSimpleLinearGeometry(jstsPolygon)) {
        return;
      }
     
      var res = [];
      var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
      var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
      var r = cat.isNodeConsistentArea();
      if (!r) {
        var pt = cat.getInvalidPoint();
        res.push([pt.x, pt.y]);
      }
      return res;
    };
    

    proof of concept fiddle (credit to HoffZ)

    code snippet:

    var mapOptions = {
      zoom: 16,
      center: new google.maps.LatLng(62.1482, 6.0696)
    };
    
    var drawingManager = new google.maps.drawing.DrawingManager({
      drawingControl: false,
      polygonOptions: {
        editable: true
      }
    });
    
    var googleMaps2JTS = function(boundaries) {
      var coordinates = [];
      for (var i = 0; i < boundaries.getLength(); i++) {
        coordinates.push(new jsts.geom.Coordinate(
          boundaries.getAt(i).lat(), boundaries.getAt(i).lng()));
      }
      coordinates.push(coordinates[0]);
      console.log(coordinates);
      return coordinates;
    };
    
    /**
     * findSelfIntersects
     *
     * Detect self-intersections in a polygon.
     *
     * @param {object} google.maps.Polygon path co-ordinates.
     * @return {array} array of points of intersections.
     */
    var findSelfIntersects = function(googlePolygonPath) {
      var coordinates = googleMaps2JTS(googlePolygonPath);
      var geometryFactory = new jsts.geom.GeometryFactory();
      var shell = geometryFactory.createLinearRing(coordinates);
      var jstsPolygon = geometryFactory.createPolygon(shell);
    
      // if the geometry is aleady a simple linear ring, do not
      // try to find self intersection points.
      var validator = new jsts.operation.IsSimpleOp(jstsPolygon);
      if (validator.isSimpleLinearGeometry(jstsPolygon)) {
        return;
      }
    
      var res = [];
      var graph = new jsts.geomgraph.GeometryGraph(0, jstsPolygon);
      var cat = new jsts.operation.valid.ConsistentAreaTester(graph);
      var r = cat.isNodeConsistentArea();
      if (!r) {
        var pt = cat.getInvalidPoint();
        res.push([pt.x, pt.y]);
      }
      return res;
    };
    
    
    var map = new google.maps.Map(document.getElementById("map"), mapOptions);
    drawingManager.setDrawingMode(google.maps.drawing.OverlayType.POLYGON);
    drawingManager.setMap(map);
    google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
      //var polyPath = event.overlay.getPath();
      var intersects = findSelfIntersects(polygon.getPath());
      console.log(intersects);
      if (intersects && intersects.length) {
        alert('Polygon intersects itself');
      } else {
        alert('Polygon does not intersect itself');
      }
    });
    #map {
      width: 500px;
      height: 400px;
    }
    <script src="https://maps.google.com/maps/api/js?libraries=drawing&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
    <script src="https://cdn.rawgit.com/bjornharrtell/jsts/gh-pages/1.4.0/jsts.min.js"></script>
    <p>
      Draw a polygon on the map
    </p>
    
    <div id="map">
    
    </div>