Точка в алгоритме Polygon иногда дает неправильные результаты

Я видел в StackOverflow алгоритм ratracing, называемый точкой в ​​полигоне, который я реализовал в своем PHP-коде. В большинстве случаев это работает хорошо, но в некоторых сложных случаях с сложными полигонами и порочными точками он терпит неудачу, и он говорит, что точка не в полигоне, когда она есть.

Например:
Здесь вы найдете мои классы Polygon и Point: метод pointInPolygon находится в классе Polygon. В конце файла есть две точки, которые должны находиться внутри заданного многоугольника (True на Google Earth). Второй работает хорошо, но первый из них глючит:(.

Вы можете легко проверить многоугольник на Google Earth, используя этот файл KML.

Ответ 1

Были там:-) Я также путешествовал по stackoverflows PiP-предложениям, включая вашу ссылку и эту тему. К сожалению, ни одно из предложений (по крайней мере, те, что я пробовал) было безупречным и достаточным для сценария реальной жизни: например, пользователи, планирующие сложный многоугольник на карте google в свободном доступе, "злобные" права против левых проблем, отрицательные числа и т.д.

PiP-алгоритм должен работать во всех случаях, даже если многоугольник состоит из сотен тысяч точек (например, приграничный, природный парк и т.д.) - независимо от того, насколько "сумасшедшим" является многоугольник.

Итак, я закончил создание нового алгоритма, основанного на каком-то источнике астрономии-приложения:

//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 not in the poly.
    return $c%2!=0;
}

Иллюстративный тест:

$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);

Выходы:

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

Сейчас уже больше года, так как я переключился на вышеупомянутый алгоритм на нескольких сайтах. В отличие от "SO-алгоритмов" до сих пор не было никаких жалоб. См. Его в действии здесь (национальная микологическая база данных, извините за датскую). Вы можете построить многоугольник или выбрать "kommune" (графство) - в конечном счете сравнить многоугольник с тысячами точек до тысяч записей).

Обновление Обратите внимание: этот алгоритм нацелен на геоданные /lat, lngs, которые могут быть очень точными (n-й десятичный), поэтому рассматривая "в полигоне" как внутри многоугольника, а не на границе многоугольника. 1,1 считается вне, так как он находится на границе. 1.0000000001,1.01 нет.