Алгоритм знаходження нерегулярного центроїда багатокутника (мітка точки)


13

Мені потрібно знайти центроїд (або мітку) для багатокутників неправильної форми на Картах Google. Я показую InfoWindows для посилок і мені потрібно місце для закріплення InfoWindow, яке гарантовано знаходиться на поверхні. Дивіться зображення нижче.

alt текст alt текст

Насправді мені не потрібно нічого конкретного Google Maps, я просто шукаю ідею, як автоматично знайти цю точку.

Моя перша ідея полягала в тому, щоб знайти "помилковий" центроїд, взявши середній лат і lngs, і випадково розміщуючи точки звідти, поки я не знайду той, який перетинає полігон. У мене вже є код «багатокутник». Це мені здається жахливо "хакітним".

Слід зазначити, що я не маю доступу до жодного з кодів сервера, що виводить геометрію, тому я не можу зробити нічого, як ST_PointOnSurface (the_geom).

Відповіді:


6

Швидкий і брудний: Якщо "помилкового" центроїда немає в полігоні, використовуйте найближчу вершину до цієї точки.


Я не думав про це. В ідеалі мені хотілося б, щоб ця точка була на полігоні, а не на краю, але це може бути те, до чого я впадаю.
Джейсон

Виявивши крайню точку, ви можете перетинати невеликий квадрат, центрований у цій точці з багатокутником, а потім вибирати центроїд перетину. Коли площа буде досить маленькою, це гарантовано буде внутрішньою точкою (хоча, звичайно, це буде дуже близько до краю).
whuber

@Jason Якщо ви користуєтесь справжнім центроїдом, ви, можливо, менше зіткнетеся з цією проблемою. Не повинно бути занадто важким, щоб щось швидко перекласти на JavaScript: github.com/cartesiananalytics/Pipra/blob/master/src/…
Денді,

Хоча моє рішення (промені від помилкового центру) буде працювати більшу частину часу, я думаю, що це рішення, мабуть, найкраще спрацює через його простоту та той факт, що ви гарантовано знайдете точку хоча б на межі, і можете легко змінити він повинен знаходитися всередині полігону з дуже невеликими зусиллями.
Джейсон

3

Ви можете подивитися на це: http://github.com/tparkin/Google-Maps-Point-in-Polygon

Схоже, використовується алгоритм Ray Casting, який повинен відповідати випадку, який ви представили.

Тут про це є повідомлення в блозі. http://appdelegateinc.com/blog/2010/05/16/point-in-polygon-checking/


Якщо ви хотіли реалізувати це на стороні сервера, і JTS (Java), і Geos (C) реалізують цю функціональність.
DavidF

Так, я, мабуть, мав би додати, що я вже маю код, щоб визначити, чи мій "обчислений" центроїд знаходиться в полігоні чи ні. Те, що я насправді хочу, - це певний спосіб створити центроїд, який знаходиться в полігоні.
Джейсон

3

Алгоритм ESRI (старший) обчислює центр маси і, випробувавши його на включення в полігон, переміщує його по горизонталі, якщо це необхідно, поки він не опиниться в межах багатокутника. (Це може бути зроблено багатьма способами залежно від того, які основні операції доступні у вашому середовищі програмування.) Це, як правило, створює мітки, досить близькі до візуального центру полігону: спробуйте це на ілюстрації.


1

Я вирішив свою проблему, поширивши популярний код epoly з http://econym.org.uk/gmap . В основному те, що я в кінцевому підсумку робив:

  • Створіть ряд променів, які починаються з "помилкового центроїда" і поширюються на кожен кут і збоку (8 усього)
  • Поступово створіть точку на 10,20,30 ... відсотків вниз від кожного променя і подивіться, чи ця точка знаходиться в нашому початковому багатокутнику

Розширений епольний код нижче:

google.maps.Polygon.prototype.Centroid = function() {
var p = this;
var b = this.Bounds();
var c = new google.maps.LatLng((b.getSouthWest().lat()+b.getNorthEast().lat())/2,(b.getSouthWest().lng()+b.getNorthEast().lng())/2);
if (!p.Contains(c)){
    var fc = c; //False Centroid
    var percentages = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]; //We'll check every 10% down each ray and see if we're inside our polygon
    var rays = [
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getSouthWest().lng())]}),
        new google.maps.Polyline({path:[fc,b.getNorthEast()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,b.getSouthWest()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),b.getSouthWest().lng())]})
    ];
    var lp;
    for (var i=0;i<percentages.length;i++){
        var percent = percentages[i];
        for (var j=0;j<rays.length;j++){
            var ray = rays[j];
            var tp = ray.GetPointAtDistance(percent*ray.Distance()); //Test Point i% down the ray
            if (p.Contains(tp)){
                lp = tp; //It worked, store it
                break;
            }
        }
        if (lp){
            c = lp;
            break;
        }
    }
}
return c;}

Досі трохи hacky але це здається працює.


Цей метод виявиться невдалим для деяких звивистих багатокутників. Наприклад, буфер поліліній {{0, 9}, {10, 20}, {9, 9}, {20, 10}, {9, 0}, {20, -10}, {9, -9} , {10, -20}, {0, -9}, {-10, -20}, {-9, -9}, {-20, -10}, {-9, 0}, {-20, 10}, {-9, 9}, {-10, 20}, {0,9}} менше ніж на 1/2. Наприклад, він також неефективний порівняно з методом QAD Денді, наприклад.
whuber

1

Ще один "брудний" алгоритм для цього:

  • Візьміть обмежувальну коробку геометрії (Xmax, Ymax, Xmin, Ymin)

  • Цикл, поки в ( Xmin+rand*(Xmax-Xmin), Ymin+rand*(Ymax-Ymin) ) геометрії не буде знайдена випадкова точка (за допомогою Google-Maps-Point-in-Polygon )


+1, оскільки це може мати шанси на удар вдруге. Поки ваш "випадковий" відтворюється кожен раз, щоб не дратувати користувача, це також є правильним рішенням. Шанси на те, що незабаром не потрапить на дійсну точку, невеликі, особливо якщо ви почнете з точки зору хорошого здогаду.
Денді

1
@Dandy: Насправді, в деяких випадках це може бути дуже поганий алгоритм. Розглянемо, наприклад, вузьку діагональну повзунку. Вони існують на практиці (наприклад, довгі посилки дорожнього фронту) і можуть легко зайняти менше 0,1% обмежувальної коробки (іноді набагато менше). Щоб бути абсолютно впевненим (95% впевненим), щоб вдарити такий полігон за допомогою цієї методики, знадобиться близько 3000 ітерацій.
whuber

@Whuber: Якщо ви вибрали неправильне початкове місце, так, це може зайняти деякий час, щоб запустити його до завершення. Якщо ви також вважаєте, що гіпотетично 95% клацань будуть на більш бажаних геометріях, це може бути проблемою лише 5% часу. Як і в іншому питанні GIS.se, якщо ціль є ефективністю, ніколи не існує єдиного рішення, найкраще змінити тактику, засновану на евристиці. Немає підстав запускати це за 3000 ітерацій. Ви завжди можете покласти на мій QAD після 10. Я думаю, що варто було б спробувати цей на кілька ітерацій, оскільки місце розташування може бути більш бажаним.
Денді

@Dandy: Але що з вашим рішенням QAD? Ви можете навіть трохи змінити його, перемістившись від початкової пробної мітки до найближчої вершини в якомусь внутрішньому буфері полігону: все ще QAD, але тепер гарантовано приземлиться на внутрішнє розташування оригінальної функції. До речі, ваша стратегія скорочення коштів є хорошою. Щоразу, коли я кодую такий випадковий зонд, я попередньо обчислюю відношення площі функції до площі її обмежувального поля, використовуючи це, щоб знайти очікуваний час для успіху, і негайно попереджую користувача, чи може це бути довгим.
whuber

@Wurber співвідношення площі евристичний - це відмінна ідея, тому що ви майже обчислюєте центроїд, коли обчислюєте площу. Щодо проблеми з моїм рішенням QAD: воно знаходиться на краю. Якщо я виберу цю точку і буфер її, як ви кажете, той "малий" радіус може бути більшим, ніж довжина на цьому вузькому перерізі. Завжди є кутовий корпус. Стільки врахувати, просто зробити повітряну кулю, яка буде захаращувати інтерфейс користувача і все-таки затемнювати геометрію. Напевно, краще вибрати найвищу чи найнижчу вершину.
Денді

1

У світлі вашого недавнього уточнення, що ви віддаєте перевагу суворому розташуванню в інтер'єрі, ви можете вибрати будь-яку точку на Медіальній трансформації осі, яка також не знаходиться на межі полігону. (Якщо у вас немає коду для MAT, ви можете наблизити його негативним буферуванням полігону. Двійковий або сектантний пошук швидко створить невеликий внутрішній багатокутник, який наближає частину MAT; використовуйте будь-яку точку на його межі.)


Я розумію, що ви говорили про використання краю геометрії таким чином, щоб край знаходився у внутрішній частині цікавить багатокутника. Я не розумію, як би ви пішли про створення цього краю / вершини. Єдине, про що я можу придумати - це зробити віртуальний трикутник, перетинаючи перпендикулярний промінь від точки, що цікавить, до відрізка, протилежного відрізку вибраної точки. Середина між цими двома точками може бути вершиною цього віртуального трикутника.
Денді

@Dandy: Це досягає серця. Існує багато способів вирішити це в залежності від того, що робить ваш ГІС спочатку. Наприклад, щойно ви знайшли промінь, який перетинає оригінальну функцію набір позитивної довжини, це перетин буде нерозривним об'єднанням відрізків рядків. Використовуйте центр будь-якого з цих сегментів. Інший спосіб - почати з будь-якої точки на функції (бажано біля її середини, що і було досягнуто вашим методом QED), створіть невеликий простий багатокутник (наприклад, квадрат), по центру якого перетинаєте його, виберіть унікальну підключену компонент ...
whuber

(продовження) ..., що містить початкову точку, та рекурсивно вибирають центр для цього компонента. Існує безліч методів, коли ваш ГІС дозволить вам перебирати послідовності вершин, що описують межу функції. Якщо підтримуються негативні буфери, ви можете ітераційно знаходити набір внутрішніх точок максимальної відстані ("скелет", який є підмножиною MAT). Це трохи дорого, але програмувати досить просто і дає чудові позначки.
whuber

0

Чому б не використовувати центроїд тільки для вертикального (широтного) положення? Потім ви можете розмістити мітку горизонтально, вибравши середню довготу на цій широті . (Для цього вам потрібно буде знайти значення довготи для ребра багатокутника на певній широті, що не повинно створювати проблем.)

Також будьте уважні до фігур U і більш складних. :) Можливо, для них виберіть середнє значення найправішої пари довжин (кожна пара відповідала б фрагменту багатокутника), оскільки інформаційне вікно орієнтоване саме так?

Це дає вам трохи більше контролю над позиціонуванням; наприклад, може бути непогано розмістити інформаційне вікно на 66 або 75% вертикально, щоб залишити більше видимого багатокутника. (Або це не може! Але у вас є ручка, щоб підкрутити.)


0

Як щодо просто використання точки, яку користувач натиснув, щоб вибрати її, якщо її обрав користувач, який є.


Його можна вибрати клацанням миші або непросторовим запитом, тому це не завжди спрацює.
Джейсон

0

Я також намагаюся вирішити це. Я встановив умову для своїх полігонів, що вони не можуть мати пересічні лінії, що входить в те, що я опишу.

Отже, мій підхід використовує тріангуляцію. Візьміть випадкову вершину (можливо, візьміть вершину в крайніх N, E, W або S, можливо, спростіть).

З цієї вершини намалюйте лінії до вершини однією вершиною, тобто якщо ваша вершина вершина 3, подивіться на вершину 3 + 2.

Побудуйте рядок від початкової вершини до цієї вершини. Якщо побудована лінія:

  1. не перетинає жодної іншої лінії та
  2. його середина не знаходиться поза полігоном

Потім ви побудували трикутник, який знаходиться в межах багатокутника. Якщо успішною вершиною було n + 2, то ваш трикутник дорівнює {n, n + 1, n + 2}, який ми будемо називати {v, v1, v2}. Якщо ні, спробуйте наступну вершину та продовжуйте, поки всі вершини не будуть спробувані.

Знайшовши трикутник, знайдіть його центр, взявши пряму від вершини v до середини v1 та v2. Середина цієї лінії гарантовано знаходиться всередині трикутника та всередині багатокутника.

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


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.