Если вы любите кататься на горных лыжах или сноуборде по некатанным склонам, или же вам просто надоели скучные выглаженные склоны Сорочан и Волена с их безумными ценами на подъемники – то эта статья для вас.
В феврале 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
В итоге задача разбивается на следующие шаги:
- Закачиваем данные из файла в таблицу, содержащую высоту, № ряда и столбца в исходной матрице и высоту.
- Запускаем SQL запрос на выборку соседних фрагментов, для которых перепад высот укладывается в интересующие нас критерии поиска.
- Запускаем запрос на поиск цепочек из полученных в пункте 2 фрагментов.
- Экспортируем цепочки нужной длины в 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-файлов
→ Инсталлятор
Автор: ikanel