Check if a polygon point is inside another in leaflet
Asked Answered
B

3

10

I'm having two sets of polygon coordinates selected from leaflet geoJSON map. The parent and child coordinates are coordinates are:

var parentCoordinates=[
    [
        32.05898221582174,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.31004731142091
    ]
]
var childCoordinates=[
  [
    32.059904895722866,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.30970726909422
  ]
]

The child is drawn inside the parent area as shown in the picture: enter image description here

Using Ray Casting algorithm to determine if the point lies inside polygon I 'm not able to determine as the result I'm getting is false. Please let me know where I'm doing wrong or any other way to determine the solution.Thanks

Barthold answered 14/12, 2016 at 8:35 Comment(1)
Note:For polyline inside polygon its working fine .Not working for polygon inside polygon(as shown in the image)Barthold
G
4

I tried with your algorithm and another found here https://rosettacode.org/wiki/Ray-casting_algorithm and both return the right value.

Maybe this fiddle can help you in the implementation :

https://jsfiddle.net/4psL2hoo/1/

Your algo

// Data
var parentCoordinates=[
    [
        32.05898221582174,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.31004731142091
    ]
]
var childCoordinates=[
  [
    32.059904895722866,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.30970726909422
  ]
]

// Other algo
function test(point, vs) {
    // ray-casting algorithm based on
    // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    var x = point[0], y = point[1];

    var inside = false;
    for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
        var xi = vs[i][0], yi = vs[i][1];
        var xj = vs[j][0], yj = vs[j][1];

        var intersect = ((yi > y) != (yj > y))
            && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }

    return inside;
};

for (var i = 0; i < childCoordinates.length; i++) {
     var testPoint = childCoordinates[i];
     console.log(JSON.stringify(testPoint) + '\tin parentCoordinate\t' + test(testPoint, parentCoordinates));
}

Rosetta code algo

//https://rosettacode.org/wiki/Ray-casting_algorithm
function contains(bounds, lat, lng) {
    //https://rosettacode.org/wiki/Ray-casting_algorithm
    var count = 0;
    for (var b = 0; b < bounds.length; b++) {
        var vertex1 = bounds[b];
        var vertex2 = bounds[(b + 1) % bounds.length];
        if (west(vertex1, vertex2, lng, lat))
            ++count;
    }
    return count % 2;

    /**
     * @return {boolean} true if (x,y) is west of the line segment connecting A and B
     */
    function west(A, B, x, y) {
        if (A.y <= B.y) {
            if (y <= A.y || y > B.y ||
                x >= A.x && x >= B.x) {
                return false;
            } else if (x < A.x && x < B.x) {
                return true;
            } else {
                return (y - A.y) / (x - A.x) > (B.y - A.y) / (B.x - A.x);
            }
        } else {
            return west(B, A, x, y);
        }
    }
}

var square = {name: 'square', bounds: [{x: 32.05898221582174, y: -28.31004731142091}, {x: 32.05898221582174, y: -28.308044824292978}, {x: 32.06134255975485, y: -28.308044824292978}, {x: 32.06134255975485, y: -28.31004731142091}]};

var shapes = [square];
var testPoints = [{lng: 32.059904895722866, lat: -28.30970726909422}, {lng: 32.059904895722866, lat: -28.308743809931784}, {lng: 32.06089194864035, lat: -28.308743809931784},
    {lng: 32.06089194864035, lat: -28.30970726909422}];

for (var s = 0; s < shapes.length; s++) {
    var shape = shapes[s];
    for (var tp = 0; tp < testPoints.length; tp++) {
        var testPoint = testPoints[tp];
        console.log(JSON.stringify(testPoint) + '\tin ' + shape.name + '\t' + contains(shape.bounds, testPoint.lat, testPoint.lng));
    }
}
Galvanotropism answered 14/12, 2016 at 9:3 Comment(0)
S
6

I've had good experience with Turf. It works well, is well documented and the examples are already shown with leaflet.

For your problem, you could use turf.within with parentCoordinates as turf.polygon and childCoordinates as an array of turf.point :

var parentPolygon = turf.polygon([parentCoordinates]);

var inside = true;
childCoordinates.forEach(function(coordinates) {
    point = turf.point(coordinates);
    if (!turf.inside(point, parentPolygon)){
      alert("Oh no! "+ coordinates + " isn't in polygon");
      inside = false;
    }
});

alert("Child polygon inside parent polygon ? " + inside);

Here's a Fiddle example.

Sapphera answered 14/12, 2016 at 9:3 Comment(0)
G
4

I tried with your algorithm and another found here https://rosettacode.org/wiki/Ray-casting_algorithm and both return the right value.

Maybe this fiddle can help you in the implementation :

https://jsfiddle.net/4psL2hoo/1/

Your algo

// Data
var parentCoordinates=[
    [
        32.05898221582174,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.308044824292978
    ],
    [
        32.06134255975485,
        -28.31004731142091
    ],
    [
        32.05898221582174,
        -28.31004731142091
    ]
]
var childCoordinates=[
  [
    32.059904895722866,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.308743809931784
  ],
  [
    32.06089194864035,
    -28.30970726909422
  ],
  [
    32.059904895722866,
    -28.30970726909422
  ]
]

// Other algo
function test(point, vs) {
    // ray-casting algorithm based on
    // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

    var x = point[0], y = point[1];

    var inside = false;
    for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
        var xi = vs[i][0], yi = vs[i][1];
        var xj = vs[j][0], yj = vs[j][1];

        var intersect = ((yi > y) != (yj > y))
            && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }

    return inside;
};

for (var i = 0; i < childCoordinates.length; i++) {
     var testPoint = childCoordinates[i];
     console.log(JSON.stringify(testPoint) + '\tin parentCoordinate\t' + test(testPoint, parentCoordinates));
}

Rosetta code algo

//https://rosettacode.org/wiki/Ray-casting_algorithm
function contains(bounds, lat, lng) {
    //https://rosettacode.org/wiki/Ray-casting_algorithm
    var count = 0;
    for (var b = 0; b < bounds.length; b++) {
        var vertex1 = bounds[b];
        var vertex2 = bounds[(b + 1) % bounds.length];
        if (west(vertex1, vertex2, lng, lat))
            ++count;
    }
    return count % 2;

    /**
     * @return {boolean} true if (x,y) is west of the line segment connecting A and B
     */
    function west(A, B, x, y) {
        if (A.y <= B.y) {
            if (y <= A.y || y > B.y ||
                x >= A.x && x >= B.x) {
                return false;
            } else if (x < A.x && x < B.x) {
                return true;
            } else {
                return (y - A.y) / (x - A.x) > (B.y - A.y) / (B.x - A.x);
            }
        } else {
            return west(B, A, x, y);
        }
    }
}

var square = {name: 'square', bounds: [{x: 32.05898221582174, y: -28.31004731142091}, {x: 32.05898221582174, y: -28.308044824292978}, {x: 32.06134255975485, y: -28.308044824292978}, {x: 32.06134255975485, y: -28.31004731142091}]};

var shapes = [square];
var testPoints = [{lng: 32.059904895722866, lat: -28.30970726909422}, {lng: 32.059904895722866, lat: -28.308743809931784}, {lng: 32.06089194864035, lat: -28.308743809931784},
    {lng: 32.06089194864035, lat: -28.30970726909422}];

for (var s = 0; s < shapes.length; s++) {
    var shape = shapes[s];
    for (var tp = 0; tp < testPoints.length; tp++) {
        var testPoint = testPoints[tp];
        console.log(JSON.stringify(testPoint) + '\tin ' + shape.name + '\t' + contains(shape.bounds, testPoint.lat, testPoint.lng));
    }
}
Galvanotropism answered 14/12, 2016 at 9:3 Comment(0)
D
0

You can try Leaflet's api for that - contains . You create a parent polygon with LatLngBounds and then child too.

parentPolygon.contains(childPolygon)
Derek answered 14/12, 2016 at 14:46 Comment(1)
LatLngBounds creates a rectangle; the poster needs general polygons. Downvoted.Feer

© 2022 - 2024 — McMap. All rights reserved.