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


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.