16

I saw on StackOverflow a "point in polygon" raytracing algorithm that I implemented in my PHP Code. Most of the time, it works well, but in some complicated cases, with complex polygons and vicious points, it fails and it says that point in not in polygon when it is.

For example:
You will find here my Polygon and Point classes: pointInPolygon method is in Polygon class. At the end of the file, there are two points that are supposed to lie inside the given polygon (True on Google Earth). The second one works well, but the first one is buggy :( .

You can easily check the polygon on Google Earth using this KML file.

TylerH
  • 20,799
  • 66
  • 75
  • 101
user1527491
  • 895
  • 10
  • 22

1 Answers1

57

Have been there :-) I also travelled through Stackoverflow's PiP-suggestions, including your reference and this thread. Unfortunately, none of the suggestions (at least those I tried) were flawless and sufficient for a real-life scenario: like users plotting complex polygons on a Google map in freehand, "vicious" right vs left issues, negative numbers and so on.

The PiP-algorithm must work in all cases, even if the polygon consists of hundreds of thousands of points (like a county-border, nature park and so on) - no matter how "crazy" the polygon is.

So I ended up building a new algorithm, based on some source from an astronomy-app:

//Point class, storage of lat/long-pairs
class Point {
    public $lat;
    public $long;
    function Point($lat, $long) {
        $this->lat = $lat;
        $this->long = $long;
    }
}

//the Point in Polygon function
function pointInPolygon($p, $polygon) {
    //if you operates with (hundred)thousands of points
    set_time_limit(60);
    $c = 0;
    $p1 = $polygon[0];
    $n = count($polygon);

    for ($i=1; $i<=$n; $i++) {
        $p2 = $polygon[$i % $n];
        if ($p->long > min($p1->long, $p2->long)
            && $p->long <= max($p1->long, $p2->long)
            && $p->lat <= max($p1->lat, $p2->lat)
            && $p1->long != $p2->long) {
                $xinters = ($p->long - $p1->long) * ($p2->lat - $p1->lat) / ($p2->long - $p1->long) + $p1->lat;
                if ($p1->lat == $p2->lat || $p->lat <= $xinters) {
                    $c++;
                }
        }
        $p1 = $p2;
    }
    // if the number of edges we passed through is even, then it's not in the poly.
    return $c%2!=0;
}

Illustrative test :

$polygon = array(
    new Point(1,1), 
    new Point(1,4),
    new Point(4,4),
    new Point(4,1)
);

function test($lat, $long) {
    global $polygon;
    $ll=$lat.','.$long;
    echo (pointInPolygon(new Point($lat,$long), $polygon)) ? $ll .' is inside polygon<br>' : $ll.' is outside<br>';
}

test(2, 2);
test(1, 1);
test(1.5333, 2.3434);
test(400, -100);
test(1.01, 1.01);

Outputs :

2,2 is inside polygon 
1,1 is outside
1.5333,2.3434 is inside polygon 
400,-100 is outside
1.01,1.01 is inside polygon

It is now more than a year since I switched to the above algorithm on several sites. Unlike the "SO-algorithms" there have not been any complaints so far. See it in action here (national mycological database, sorry for the Danish). You can plot a polygon, or select a "kommune" (a county) - ultimately compare a polygon with thousands of points to thousands of records).

Update Note, this algorithm is targeting geodata / lat,lngs which can be very precise (n'th decimal), therefore considering "in polygon" as inside polygon - not on border of polygon. 1,1 is considered outside, since it is on the border. 1.0000000001,1.01 is not.

mike65535
  • 483
  • 2
  • 10
  • 21
davidkonrad
  • 83,997
  • 17
  • 205
  • 265
  • 3
    Yes! i've tried 3 different algorithms for PiP, and this is the best. One didn't work at all, another gave inconsistent results, but this one seems solid! Nice job. – italiansoda Mar 07 '16 at 17:31
  • Hi Can you please check this - https://stackoverflow.com/questions/61302366/point-in-polygon-algorithm-giving-wrong-results-for-negative-points –  Apr 19 '20 at 17:08
  • 1
    This example is the best! I have tried these two: https://stackoverflow.com/questions/5065039/find-point-in-polygon-php, https://assemblysys.com/php-point-in-polygon-algorithm/ and none of them was accurate. – blues911 Sep 18 '20 at 07:22
  • why did you use `set_time_limit()`. Is the logic time consuming? – CodeToLife Jan 03 '21 at 10:03
  • @CodeToLife, I dont think you will ever need it, but depending on the hardware and how many points the polygon is made of, you can theoretically exceed the time limit. I have used this with very complex nature reservations areas, municipalities and so on, and never seen the time limit exceeded. But I will rather change the time limit than to see an error message, if and when ... – davidkonrad Jan 03 '21 at 13:23
  • hi i tried to reproduce the same in VB.NET and it's not working! would you care to [read my question?](https://stackoverflow.com/questions/69524103/vb-net-point-in-polygon-from-kml-file) – Carlo Prato Oct 12 '21 at 09:30
  • As the answer is probably in an older version of PHP, you may notice if you are using PHP 7 or 8 that the `Point` function doesn't work and needs to be called `__construct` instead. – Mr U. Jun 21 '22 at 08:14