Как сгруппировать близкие точки с позициями GPS?

10

Я айтишник, поэтому не слишком много знаю о прогнозах и так далее, надеюсь, вы мне поможете.

Я сделал приложение для Android, которое собирает координаты GPS, поэтому у меня есть широта и долгота в данный момент времени. Я хочу сохранить вместе те элементы, которые находятся рядом друг с другом, в группах областей местности одинакового физического размера; проблема в том, что я заранее не знаю точек, и они могут прийти из любой точки мира .

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

Это решение было бы хорошо для декартового 2D-представления, поскольку у меня были бы области шириной 0,001 единицы и высотой; однако, поскольку размер 1 градуса долготы отличается на разных широтах, я не могу использовать этот подход.

Есть идеи?

Kuu
источник
Почему вы не можете сохранить позицию как в, а затем выполнить обработку позже? Также на экваторе 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, вместе с поиском самой длинной стороны каждого треугольника.)

Whuber
источник
1

Это сложный вопрос, поскольку проекции вносят искажения при переходе от географической системы координат 3D WGS84 к плоской 2D-проекции. В глобальном масштабе вы обязательно будете иметь искажения где-то в системе.

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

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

Шон Барбо
источник
1
UTM не является «глобальной проекцией»: демонстрация состоит в том, что почти любая пара действительных координат, например (500000, 5000000), соответствует по меньшей мере 120 различным широко разнесенным точкам в системе UTM. И, к сожалению, алгоритмы кластеризации не удовлетворяют потребности ОП в способности группировать точки в режиме реального времени исключительно на основании их местоположения (а не их близости к другим точкам).
whuber
@whuber re: "глобальная проекция" - верно. Вот почему я сказал: «Как можно ближе к глобальной проекции». Если вам известна лучшая проекционная система, более подходящая, пожалуйста, оставьте ее в комментариях, и я отредактирую свой ответ. Кроме того, у ОП не было требования в реальном времени в первоначальном посте. Я отредактирую свой ответ, чтобы учесть это.
Шон Барбо
Шон, (1) Мое решение проблемы глобальной проекции - не использовать ее. Там не существует не глобальная проекция без особенностей. (2) Правда, пояснение в реальном времени появилось в комментарии. Тем не менее, текст, следующий за «моей первой идеей», делает хорошую работу, подчеркивая, что эта проблема заключается в разделении земной поверхности, а не в кластеризации набора местоположений. Именно об этом я и пытался донести (не очень эффективно) в своем предыдущем комментарии к вам.
whuber