Як згрупувати біля пунктів GPS позиції?


10

Я ІТ-людина, тому я не знаю надто багато про прогнози тощо, сподіваюся, ви можете мені допомогти.

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

Моя перша ідея (щоб трохи більше пояснити проблему) полягала у використанні децималів широти та довготи для групування; наприклад, однією групою будуть позиції з широтою між 35.123 та 35.124 та довготою між 60.101 та 60.102. Тож якщо я отримаю позицію типу lat = 35.1235647 і lon = 60.1012254598, ця точка перейде до цієї групи.

Це рішення було б добре для декартового 2D-репрезентації, оскільки я мав би ділянки шириною і висотою 0,001 одиниць; однак, оскільки розмір довготи в 1 градус різний на різних широтах, я не можу використовувати цей підхід.

Будь-яка ідея?


Чому ви не можете зберігати позицію як у, а потім обробляти пізніше? Також на екваторі 1 градус довготи становить приблизно 111 км, тому 0,001 градус буде трохи більше 1 км. Ви дійсно хочете, щоб ваші кошики були такими великими?
Devdatta Tengshe

Ступінь 0,001 був лише прикладом моєї ідеї. Звичайно, мені доведеться адаптувати це до вимог. Я не можу виконувати обробку пошти, оскільки планується це програма в режимі реального часу, і я не можу сказати своїм користувачам "тримайтесь до завтра, тому що я повинен згрупувати бали" Спасибі за ідеї все одно;)
Куу,

Відповіді:


6

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

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

(Чи сказав я розщеплення "площини"? Так - якщо моделювати земну поверхню як сферу і використовувати геоцентричні координати (x, y, z), то більшість наших обчислень відбувається в трьох вимірах, де сторони трикутників - це шматки перетину сфери з площинами через її походження. Це робить обчислення швидкими та легкими.)


Я проілюструю, показуючи процедуру на одному октанті сфери; інші сім октантів обробляються таким же чином. Такий октант - трикутник 90-90-90. У своїй графіці я намалюю евклідові трикутники, що охоплюють однакові кути: вони виглядають не дуже добре, поки не стануть маленькими, але їх можна легко та швидко намалювати. Ось евклідовий трикутник, відповідний октанту: це початок процедури.

Фігура 1

Оскільки всі сторони мають однакову довжину, один вибирається випадковим чином як "найдовший" і підрозділяється:

Малюнок 2

Повторіть це для кожного нового трикутника:

Малюнок 3

Після n кроків у нас буде 2 ^ n трикутників. Ось ситуація після 10 кроків, що показують 1024 трикутники в октанті (і 8192 на загальній кулі):

Малюнок 4

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

Малюнок 5

Між іншим, щоб звузити розташування точки на одну ступінь широти (приблизно), ви зауважите, що це приблизно 1/60 радіана і так охоплює навколо (1/60) ^ 2 / (Pi / 2) = 1/6000 загальна поверхня. Оскільки кожен підрозділ приблизно вдвічі зменшує розмір трикутника, трюк виконає приблизно 13-14 підрозділів октанта. Це не так вже й багато обчислень - як ми побачимо нижче - що дозволяє не зберігати дерево взагалі, а виконувати підрозділ на льоту. На початку ви зазначили, в якому октанті лежить точка - це визначається знаками трьох його координат, які можна записати як трицифрове двійкове число - і на кожному кроці ви хочете пам'ятати, чи лежить точка ліворуч (0) або праворуч (1) трикутника. Це дає ще 14-значне двійкове число. Ви можете використовувати ці коди для групування довільних точок.

(Як правило, коли два коди близькі як фактичні двійкові числа, відповідні точки є близькими; однак, точки все одно можуть бути близькими і мають надзвичайно різні коди. Розглянемо, наприклад, два точки на метр один від одного, відокремлений екватор, наприклад: їхні коди повинні відрізнятися навіть перед двійковою точкою, оскільки вони знаходяться в різних октантах. Така річ неминуча при будь-якому фіксованому розділенні простору.)


Я використовував Mathematica 8 для цього: ви можете прийняти це як є або як псевдокод для реалізації у вашому улюбленому середовищі програмування.

Визначте, на якій стороні площини лежить 0-ab точка p :

side[p_, {a_, b_}] := If[Det[{p, a, b}] >=  0, left, right];

Уточнити трикутник abc на основі точки p.

refine[p_, {a_, b_, c_}] := Block[{sides, x, y, z, m},
  sides = Norm /@ {b - c, c - a, a - b} // N;
  {x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
  m = Normalize[Mean[{y, z}]];
  If[side[p, {x, m}] === right, {y, m, x}, {x, m, z}] 
  ]

Останню цифру було намальовано відображенням октанта, а поверх цього шляхом подання наступного списку у вигляді набору багатокутників:

p = Normalize@RandomReal[NormalDistribution[0, 1], 3]        (* Random point *)
{a, b, c} = IdentityMatrix[3] . DiagonalMatrix[Sign[p]] // N (* First octant *)
NestWhileList[refine[p, #] &, {a, b, c}, Norm[#[[1]] - #[[2]]] >= 0.05 &, 1, 16]

NestWhileListнеодноразово застосовує операцію ( refine), коли дотримується умова (трикутник великий) або до досягнення максимальної кількості операцій (16).

Щоб відобразити повну тріангуляцію октанта, я почав з першого октанта і десять разів повторив уточнення. Це починається з незначної модифікації refine:

split[{a_, b_, c_}] := Module[{sides, x, y, z, m},
  sides = Norm /@ {b - c, c - a, a - b} // N;
  {x, y, z} = RotateLeft[{a, b, c}, First[Position[sides, Max[sides]]] - 1];
  m = Normalize[Mean[{y, z}]];
  {{y, m, x}, {x, m, z}}
  ]

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

triangles = NestList[Flatten[split /@ #, 1] &, {IdentityMatrix[3] // N}, 10];

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

Through[{Min, Max}[Map[Round[Det[#], 0.00001] &, triangles[[10]] // N, {1}]]]

{0,00523, 0,00739}

Таким чином, розміри змінюються вгору або вниз приблизно на 25% від їх середнього: це здається розумним для досягнення приблизно рівномірного способу групування балів.

При перегляді цього коду ви не помітите тригонометрії : єдине місце, яке воно знадобиться, - це перетворення вперед і назад між сферичними та декартовими координатами. Код також не проектує земну поверхню на будь-яку карту, тим самим уникаючи супутніх спотворень. В іншому випадку він використовує лише усереднення ( Mean), теорему Піфагора ( Norm) та детермінант 3 на 3 ( Det) для виконання всієї роботи. (Існують декілька простих команд для маніпуляції зі списком, як-от, RotateLeftі Flatten, поряд з пошуком найдовшої сторони кожного трикутника.)


1

Це складно, оскільки проекції вносять спотворення при переході від географічної системи координат 3D WGS84 до плоскої 2D проекції. У глобальному масштабі, ви повинні перекрутити десь у системі.

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

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


1
UTM не є "глобальною проекцією": демонстрація полягає в тому, що майже будь-яка пара дійсних координат, наприклад (500000, 5000000), відповідає щонайменше 120 чітко розмежованих точок в системі UTM. І, на жаль, алгоритми кластеризації не відповідають потребам ОП у можливості групування точок у режимі реального часу, виходячи лише з місця їх розташування (а не на їх близькості до інших точок).
whuber

@whuber re: "глобальна проекція" - правильно. Ось чому я сказав: "Найближчі ви можете дістатися до глобальної проекції". Якщо ви знаєте про кращу проекційну систему, яка підходить, залиште її в коментарях, і я відредагую свою відповідь. Крім того, ОП не вимагала реального часу вимоги в початковій посаді. Я відредагую свою відповідь, щоб врахувати це.
Шон Барбо

Шон, (1) Моє рішення глобальної проблеми прогнозування полягає не в тому, щоб використовувати її. Там не існує НЕ глобальна проекція без особливостей. (2) Щоправда, уточнення в реальному часі з'явилося в коментарі. Текст, що слідує за "моєю першою ідеєю", робить непогану роботу, хоча, підкреслюючи, що ця проблема є одним із розділення земної поверхні, а не згрупування набору локацій. Це суть, яку я (не дуже ефективно) намагався натрапити на свій попередній коментар до вас.
whuber
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.