Перевірте, чи точка знаходиться всередині багатокутника


79

Я хочу перевірити, чи знаходиться точка в межах певного багатокутника. Багатокутник:

 polygon=   [ [-73.89632720118, 40.8515320489962],
              [-73.8964878416508, 40.8512476593594],
              [-73.8968799791431, 40.851375925454],
              [-73.8967188588015, 40.851660158514],
              [-73.89632720118, 40.8515320489962] ]

Я хочу перевірити такі пункти:

1 = [40.8515320489962,-73.89632720118]
2 = [40.8512476593594,-73.8964878416508]
3 = [40.851375925454,-73.8968799791431]
4 = [40.851660158514,-73.8967188588015]
5 = [40.8515320489962,-73.89632720118]

Як я можу зрозуміти, чи кожна з цих точок лежить у межах цього багатокутника?

Алгоритм нижче не працює. Не знаю чому.

pt[lat,long]

function isPointInPoly(poly, pt){
    for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
        ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1] < poly[i].y))
        && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
        && (c = !c);
    return c;
}

Я не хочу використовувати сторонні рішення, такі як API Google Maps або цей https://github.com/mattwilliamson/Google-Maps-Point-in-Polygon .

Моя спроба тут: http://jsfiddle.net/nvNNF/2/


5
Виберіть точку поза перевіркою багатокутника та перевірте, чи перетинає лінія від цієї точки до вашої точки непарну кількість ліній, що визначають периметр багатокутника.
— Хоган,

Ви можете перевірити код у прямому ефірі тут: я ставлю крапку в полігоні jsfiddle.net/nvNNF/2, і він повертає "False"
— user3378649


poly[i].yмає бути poly[i][1]в кінці рядка 3. Також ця функція перевіряє, чи точка знаходиться всередині багатокутника, а не чи належить точка до багатокутника.
— Бен

Немає жодної причини, щоб у циклі for так багато змінних, що зменшує читабельність. Ви можете протестувати кожну умову окремо, а потім попередити, і як тільки ви дійдете до кінця коду, тоді ви об’єднаєте всі свої умови та перевірите ще раз.
— майже новачок

Відповіді:


118

Існує проект на Github з кодом: https://github.com/substack/point-in-polygon (ліцензія MIT):

function inside(point, vs) {
    // ray-casting algorithm based on
    // https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html/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;
};

Використання:

// array of coordinates of each vertex of the polygon
var polygon = [ [ 1, 1 ], [ 1, 2 ], [ 2, 2 ], [ 2, 1 ] ];
inside([ 1.5, 1.5 ], polygon); // true

Тестова функція знаходиться тут: https://github.com/substack/point-in-polygon/blob/master/index.js

Примітка: Цей код не працює надійно, коли точка - це кут багатокутника або на ребрі. Тут є вдосконалена версія: https://github.com/mikolalysenko/robust-point-in-polygon


Я думаю, що це нормально, скопіюйте та вставте код, враховуючи те, що ви включили посилання. Це надасть заслугу оригінальному автору.
— Чатуранга Чандрасекара

1
@AaronDigulla - це не зовсім вірно. спробуйте цю просту річ у javascript: 0,3 - 0,2 та console.log вихід
— Педро Сільва

2
@PedroSilva Дозвольте сказати це так: він працює або на C / C ++, Java та JavaScript, або не працює на жодному з трьох. Всі вони використовують абсолютно однаковий метод для представлення чисел. Використання компілятора не надає мовам програмування міфічних особливостей. Бібліотеки C / C ++ та Java лише трохи кращі в округленні результату під час його друку.
— Аарон Дігулла

1
спасибі, чудово працював, але щойно зрозумів, що він дає неоднозначні результати для точок, які лежать по краях чи кутах багатокутника ... як можна відредагувати, щоб прийняти їх як завжди всередині?
— treeseal7,

1
Якщо ви передасте цій функції координати багатокутника, щоб перевірити, чи вони знаходяться всередині нього, ви отримаєте істину на половині з них.
— Олександр Правдін,

15

Ваш масив багатокутників виглядає як coordinatesмасив у структурі полігонів GeoJSON (докладніше читайте на https://macwright.org/2015/03/23/geojson-second-bite.html та http://geojson.org ). То, можливо, ви можете використовувати бібліотеки, які працюють з даними geoJSON? Подивіться на відповідь та коментарі до OP у Чи можна визначити, чи знаходиться точка GeoJSON всередині багатокутника GeoJSON за допомогою JavasScript?

Словом, мій день врятував turf( https://github.com/turfjs/turf ) Також є d3( https://github.com/d3/d3-geo#geoContains ), але у мене з цим були проблеми.

UPD: Я помітив turf, що дає суперечливі результати, коли точка знаходиться на 'краю' багатокутника. Я створив випуск і чекаю відповіді від розробників.

UPD2: Проблема з "Граничними точками" вирішена за допомогою останньої версії turf(я використовував 3.0.14 замість 4.6.1). Зараз все гаразд.


12

Ось функція, яку я нарешті запрацював. Я отримав його, прийнявши код C на javascript звідси (з поясненнями).

function checkcheck (x, y, cornersX, cornersY) {

    var i, j=cornersX.length-1 ;
    var odd = false;

    var pX = cornersX;
    var pY = cornersY;

    for (i=0; i<cornersX.length; i++) {
        if ((pY[i]< y && pY[j]>=y ||  pY[j]< y && pY[i]>=y)
            && (pX[i]<=x || pX[j]<=x)) {
              odd ^= (pX[i] + (y-pY[i])*(pX[j]-pX[i])/(pY[j]-pY[i])) < x; 
        }

        j=i; 
    }

return odd;
}

Де cornersX= масив з масивом x або широта вершин, cornersY= масив з масивом y або довгота. X, Y - широта та довгота досліджуваної точки.


1
Дякую! Чудово працює. Я зміг це адаптувати. Значення, що повертаються, є false, 0 або 1. Я змінив значення var oddNodes = 0 і return oddNodes == 1, щоб отримати остаточне логічне значення.
— Турбо

Це не працює для мене. Я завжди отримую 1. будь-яка точка всередині або зовні. Будь ласка, допоможи мені.
— Крішна

це не працює; у будь-якому випадку, чи існує функція відкритого джерела для заповнення області кольором? Якщо це станеться, це буде просто питання порівняння всіх заповнених точок із заданими координатами, набагато надійнішими для складних багатокутників.
— jumpjack

2

У моєму випадку я зробив наступне, що мені добре працює

function isLatLngInZone(latLngs,lat,lng){
  // latlngs = [{"lat":22.281610498720003,"lng":70.77577162868579},{"lat":22.28065743343672,"lng":70.77624369747241},{"lat":22.280860953131217,"lng":70.77672113067706},{"lat":22.281863655593973,"lng":70.7762061465462}];
  vertices_y = new Array();
  vertices_x = new Array();
  longitude_x = lng;
  latitude_y = lat;
  latLngs = JSON.parse(latLngs);
  var r = 0;
  var i = 0;
  var j = 0;
  var c = 0;
  var point = 0;

  for(r=0; r<latLngs.length; r++){
   vertices_y.push(latLngs[r].lat);
   vertices_x.push(latLngs[r].lng);
  }
  points_polygon = vertices_x.length;
  for(i = 0, j = points_polygon; i < points_polygon; j = i++){
   point = i;
   if(point == points_polygon)
    point = 0;
   if ( ((vertices_y[point]  >  latitude_y != (vertices_y[j] > latitude_y)) && (longitude_x < (vertices_x[j] - vertices_x[point]) * (latitude_y - vertices_y[point]) / (vertices_y[j] - vertices_y[point]) + vertices_x[point]) ) )
    c = !c;
  }
return c;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.