[Из песочницы] Поиск горнолыжных трасс из космоса


Если вы любите кататься на горных лыжах или сноуборде по некатанным склонам, или же вам просто надоели скучные выглаженные склоны Сорочан и Волена с их безумными ценами на подъемники — то эта статья для вас.
image

В феврале 2000 года шатл Endeavour 11 дней снимал топологию земли с помощью Spaceborne Imaging Radar-C/X-band Synthetic Aperture Radar. См. подробности тут

Результатом этого проекта явилась публично доступная база с данными о топологии земли за исключением ее полярных областей.

Полный массив данных имеет огромный размер и оперировать с ним в рамках ПК довольно проблематично. К счастью на сайте проекта Consortium for Spatial Information можно скачать топологиечкий файл для интересующей вас области.

Данные доступны в формате ESRI GRID (ARC ASCII), которые представляют собой простую матрицу высот. Каждая ячейка матрицы имеет одинаковую широту и долготу. Файл состоит из метаинформации и матрицы высот в 6001 строк и 6001 столбцов.

Скорее всего наиболее подходящим инструментом для решения такой задачи является Mathlab, но за неимением оного — я воспользовался подручными средствами: MSSQL, Dapper, и консольное приложение на .Net

В итоге задача разбивается на следующие шаги:

  1. Закачиваем данные из файла в таблицу, содержащую высоту, № ряда и столбца в исходной матрице и высоту.
  2. Запускаем SQL запрос на выборку соседних фрагментов, для которых перепад высот укладывается в интересующие нас критерии поиска.
  3. Запускаем запрос на поиск цепочек из полученных в пункте 2 фрагментов.
  4. Экспортируем цепочки нужной длины в KML.

Чтобы загрузка данных не заняла целый день — я воспользовался пакетным загрузчиком — SqlClient.SqlBulkCopy.

Наиболее ресурсоемким оказался шаг 2. Первой идеей было заджойнить таблицу саму на себя примерно таким образом:

select * from Points p1 JOIN Points p2 ON p1.row<>p2.row and p1.[Column]<>p2.[Column] AND  p2.row between p1.row-1 and p1.Row+1 and p2.[Column] between p1.[Column]-1 and p1.[Column]+1

Однако на таблице из 36 000 000 записей такой запрос работает невыносимо медленно — много-много часов. Разные эксперименты с индексами не далм существенной прибавки производительности.

Как всегда это бывает — самое простое решение оказалось самым эффективным: если добавть в таблицу с точками поле со счетчиком — и далее, зная кол-во столбоцов в каждом ряде, можно можно расписать табличку для поиска соседних ID:

ID-6002 ID-6001 ID-6000
ID-1 ID ID+1
ID+6000 ID+6001 ID+6002

Что позволяет существенно упростить JOIN:
select * from Points  p1 join Points p2 on (p2.id in (p1.id-6000-2,p1.id-6000-1,p1.id-6000,p1.id-1,p1.id+1,p1.id+6000 ,p1.id+6000+1,p1.id+6000+2)) WHERE  p1.Elevation-p2.Elevation>X

При наличии индекса по полю ID такой запрос довольно эффективен и отрабатывается менее чем за 15 минут.

Для построения цепочек из найденных ранее фрагментов наиболее эффективным оказался рекурсивный CTE-запрос примерно такого вида:

with chains (id,id1,id2,level,parentID,alt)
AS
(
select c1.id,c1.id1,c1.id2,0 as level,c1.id as parentID, c1.alt1 as alt from candidates c1 where not exists (select * from  candidates c2 where c2.id2=c1.id1) and exists(select * from  candidates c2 where c2.id1=c1.id2)
UNION ALL
select c1.id,c1.id1,c1.id2, level+1 as level,c2.id as parentID,c1.alt2 as alt from candidates c1 join chains c2 on c1.id1=c2.id2
)
select distinct p.*,level,ParentID  from chains c1 join candidates c2 on c1.id=c2.id
join Points p on p.ID=c2.id1 or p.id=c2.id2

Экспорт в KML с использованием библиотеки SharpKml предельно прост. Создается объект Placemark, который имеет в своем составе объект LineString содержащий посчитанные ранее координаты и высоту.

Условия поиска


Чтобы лыжи или борд куда-то ехали, а не просто застревали в снегу, уклон должен быть не менее 10–12 градусов, а чтобы был хоть какой-то интерес длина склона должна быть не менее 200–300 метров.

С другой стороны — уклон более 35 градусов уже не подразумевает расслабленного катания — оставим такие кручи профессионалам.

В качестве примера можно привести характеристики тех-же Сорочан с сайта.

  • Протяженность трасс: от 500 до 1000 метров.
  • Ширина трасс: от 30 до 70 метров.
  • Перепад высот: от 70 до 90 метров.
  • Высота над уровнем моря: 230 метров.
  • Средний уклон:10–12 градусов.
  • Максимальный уклон: 25 градусов.

Результаты


Самая высока точка в московском регионе — 317 метров, находится в районе станции 147 км. Можайского направления (карта). Самая низкая точка -75 метров (карта), находится в черте Рыбинска в русле Волги.

Склон с самым большим перепадом в 60 м и длиной 250 м находится опять же на берегу Волги в районе деревни Чукавино Тверской области (карта).

К сожалению этот склон, как и большинство остальных с хорошим перепадом и уклоном находится по берегу реки и покрыт лесом, однако мне удалось найти несколько подходящих теоретически катабельных вариантов:

Перепад Длина склона Координаты Комментарий
51 300 56°16'4.70''С; 38°20'34.20''В Водопад «гремячий» очень красивое место
43 200 56°26'22.08''С; 37°50'37.35''В Деревня Старово, Дмитровского района
51 200 55°38'21.04''С; 37°51'37.99''В Дзержинский карьер
42 200 56°28'55.13''С; 38°11'55.16''В Напротив Загорской ГАЭС.Д. Выпуклово
59 250 55°17'28.08''С; 38°43'5.33''В Белая гора. Воскресенск

Само собой в выборку попали известные Гонолыжные курорты:
Курорт Высота, м Длина склона, м
Волен 42 200
Сорочаны 40 200
Чулково 46 200
Лоза 42 200
Парамоново 50 250

Следует принять во внимание, что указанные цифры неточные из-за дескретности данных: каждый «квадрат» в матрице высотот имеет сторону 92.7 м.

В качестве POC я выбран склон в окрестностях деревни Старово Дмитровского района.
Местные бабки рассказали, что тут, во времена царя гороха, работал горнолыжный подъемник. Склон пологий, зато довольно длинный, в низовьях заросший кустарником. Здесь же, в деревне, если верить wikimapia есть база дельтапланеристов.

Видео с тестированием этого склона можно посмотреть тут.

→ Адрес проекта на гитхабе
→ Примеры сгенерированных KML-файлов
→ Инсталлятор

Комментарии (0)

© Habrahabr.ru