Как стать автором
Поиск
Написать публикацию
Обновить

Группировка гео координат

Время на прочтение2 мин
Количество просмотров4.1K

Здравствуйте!

Хочу рассказать об кластеризации (группировке) геокоординат на стороне БД. Хотелось бы все это сделать в максимально простом виде, используя школьную математику. На работе появилась задача "отображение тепловой карты пожаров в МО". Пожары представлены виде географических координат. Нужно сгруппировать пожары, которые находятся в определенном радиусе, и на основе этих данных построить тепловую карту.

Готовые решения

Можно использовать стандартный тип geography Sql Server и с помощью distance процедуры рассчитать расстояния между точками и группировать их. Этот способ дорогой по времени, больше 10000 записей занимает примерно 5 минут на core i5 9600к.

Также можно использовать Tile38 (https://tile38.com/) не было времени внедрять, скорость вычисления идеальная. Но нет интеграции с SqlServer и для этого придеться синхронизировать координаты, и обеспечить отказаустойчивость для обеих систем.

Решение

Для решения задачи будем использовать округление координат, так как округление нам нужно в метрах, а координаты представлены в градусах. Долгота зависит от широты и это надо учитывать, когда будем преобразовывать метры в градусы.

Широту можно округлить не зависимо, например:

один градус широты = 2 * Pi * "Полярный радиус земли" / 360 ~ 111 км.

И так как по широте максимум может быть 90 градусов, получается ~ 9985 км. Это число нужно для того чтобы определить коэффициент разбиения по группам. Например Требуется округлить в радиусе 100 км, получается по широте может поместится ~ 100 групп. Итак, нам нужно еще получить до какой степени нужно округлить, - это мы получим следующей формулой.

6 - lg "масштаб группировки в метрах" - оставляем только целую часть

например 6 - lg 100000м = 1 и так нужно округлять первую цифру после запятой.

Итого формула для широты

declare @metersLatDegree float = round(9985238.0901698 / @maxMeters, 0);

declare @latituteCenter float = Round(Round(@latitude * @metersLatDegree / 90.0, 0) * (90.0 /@metersLatDegree), @precsion);

где @maxMeters это максимальная разница между координатами в метрах, @latitude широта, @precsion - степень округления.

Переходим к долготе и она будет зависеть от широты, так как при приближении к полюсу его периметр будет уменьшаться от ~ 40000 км до 0. и для того чтобы определить периметр нужно использовать cos(широты) по след формуле:

один градус долготы = 2 * Pi * "экваториальный радиус земли" * COS(широты) / 360 ~ 111 км. на экваторе когда широта равна 0 и когда широта равна 60 ~ 55 км.

Итоговая формула для долготы почти такая же

declare @metersLatDegree float= round(20037077.9445957 * cos(radians(@latitude)) / @maxMeters, 0);

declare @longitudeCenter float = Round(Round(@longitude * @metersLatDegree / 90.0, 0) * (90.0 /@metersLatDegree), @precsion);

@longitude - долгота

Чтобы получить @precsion формула одинаковая

@precsion int = 6 - round(log(@maxMeters), 10), 0)

Визуализируем результат для случайных 1000 точек по земле

на полюсах погрешность хромает, но для моего проекта подошло.

Теги:
Хабы:
Всего голосов 4: ↑4 и ↓0+4
Комментарии8

Публикации

Ближайшие события