Quantum GIS: геометрия

ГИС Оставить комментарий

Заключительный текст про создание карт в Quantum GIS как-то всё откладывался, но, кажется, откладывать дальше нельзя. На этот раз речь пойдёт про, вероятно, самую сложную с технической точки зрения карту.

О том, откуда брать данные для таких карт, я, пожалуй, почти ничего говорить не буду. Необходимая статистика часто имеется, когда речь идёт о миграциях населения, но я даже не проверял, есть ли такие данные. Почему-то мне хотелось отразить на карте именно пропускные способности внутренних авиационных линий Новой Зеландии. Желание было настолько сильным, что его не остановил даже механизм, защищающий сайт Air New Zealand от автоматического скачивания. Не пытайтесь проделать это дома 🙂 Вся информация с расписаний внутренних рейсов была скопирована вручную. О том, что делать после того, как информация собрана — этот пост.

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

Подготовка данных

Итогом моих взаимодействий с сайтом Air New Zealand стала вот такая таблица:

Столбцы в ней: AIR1 и AIR2 — аэропорты (все рейсы считались «двусторонними», поэтому то, какой аэропорт оказался первым, а какой — вторым, было несущественно), TYPE — тип самолёта, FLIGHTS_PER_WEEK — число рейсов в неделю, SEATS — число мест в самолёте, TOTAL — суммарное число мест на всех рейсах самолётов этого типа за неделю на данном направлении.

Скопировав из этой таблицы на новый лист столбцы AIR1 и AIR2 один под другой, я после сортировки получил список всех используемых аэропортов. Правда, многие аэропорты в нём повторялись:

В новых версиях Excel есть специальная кнопка для убирания не уникальных значений. Если же такой кнопки нет, с этой задачей вполне справляются «Сводные таблицы», имеющиеся как в Excel, так и в OpenOffice и его вариантах. Если вкратце, то сводные таблицы позволяют делать из одних таблиц другие (обычно, более компактные), группируя какую-то часть информации исходной таблицы.

Например, выбирая все ячейки своего списка дублирующихся городов, и вызывая функицию Сводные таблицы на этом выделении, я могу сформировать такую таблицу:

В строках у неё будут названия городов с аэропортами, а напротив них — количество раз, которое город встретился в списке. Информация о количестве, конечно, нам не очень нужна, зато теперь каждый город встречается в списке только один раз:

Этот список можно, например, сверить со списком городов, которые у нас есть в слое с городами (предварительно выгрузив их в CSV) и убедиться, что большинство городов там присутствует, хотя нескольких всё-таки нет. Впрочем, всем этим мы займёмся чуть позже.

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

суммирует количество мест по каждому направлению по всем типам самолётов. В итоге мы получаем:

В этой таблице каждая пара аэропортов встречается только один раз, и напротив неё написано суммарное число мест на рейсах данного направления. С этим уже можно работать. Теперь нам нужны координаты.

Экспорт координат

Вернёмся теперь к нашему проекту про Новую Зеландию и списку аэропортов. Координаты аэропортов естественно взять из координат городов, т.е. из слоя с городами, который у нас есть в группе «Обзорная карта». В атрибутивной таблице этого слоя есть столбцы latitude и longitude, в которых хранятся широта и долгота:

Если бы таких столбцов не было, их надо было бы создать (с помощью функций $x и $y в калькуляторе, которые мы использовали, когда делали настраиваемые подписи в административном делении). Теперь этот слой можно сохранить как CSV и открыть в электронных таблицах.

Выкидывая ненужные столбцы получим что-то вроде:

Эти данные можно перенести в файл, где хранятся данные по авиалиниям и сопоставить по названиям городов координаты аэропортам из списка, который мы сделали с помощью Сводных таблиц.

Используя функцию ВПР (соответственно, VLOOKUP, если по-английски), напротив каждого города пишем координаты:

Как видно, для большинства аэропортов нашлись города с координатами. Немногочисленные исключения можно найти на Google Maps и взять координаты оттуда. Делается это так: сначала вбиваем в поиск нужный город, а потом, когда он найден, нажимаем на него правой кнопкой и выбираем пункт «What is here?». В строке поиска появятся координаты города:

Формирование геометрии линий

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

В итоге таблица с аэропортами будет выглядеть так:

Теперь координаты и номера аэропортов надо приписать к таблице, где перечислены маршурты. Сейчас она у нас в виде сводной таблицы, но удобнее, если это станет обычная таблица, в которой в каждой строчке записано откуда, куда и сколько пассажирских мест имеется. Сначала скопируем данные сводной таблицу на лист как обычную таблицу, а потом добавим к ней формулу, которая заполнит пробелы в первом столбце:

Функция =IF(ISBLANK(B2);E1;B2) (по-русски это =ЕСЛИ(ЕПУСТО(B2);E1;B2) ) формирует новый первый столбец, подставляя везде, где в первом столбце пусто, значения из строчки выше. Второй и третий столбец сводной таблицы можно скопировать, чтобы всё было совсем по порядку:

Теперь напротив каждого пункта с помощью ВПР/VLOOKUP из уже составленной ранее таблицы с номерами и координатами городов соответствующие данные про пункты FROM и TO.

Из этих данных мы сформируем теперь два файла. В одном будут координаты начала и конца всех линий, которые мы хотим нарисовать, а в другом — данные о числе пассажирских мест на данном направлении. Чтобы их связывать, нам будет удобно иметь идентификатор линии. Его мы сформируем как «номер FROM»-нижнее подчеркивание-«номер TO». Для этого заведём специальный столбец и впишем в него формулу:
=H2&"_"&K2
После заполнения таблицы этой формулой она будет выглядеть так:

Теперь на отдельных листах составляем новые таблицы. Первую — из столбцов LINE_ID и SEATS:

Копируются, естетственно, только значения. Результат можно сохранить в файл, например, nz_air_lines.csv.

Построение таблицы с координатами линий делается в два этапа. Сначала сформируем таблицу в которой будут такие столбцы:
LINE_ID, столбец заполненный единицами, FROM_LAT, FROM_LNG, снова LINE_ID, столбец заполненный двойками, TO_LAT, TO_LNG:

Если десятичный разделитель в координатах — запятая, то сейчас её имеет смысл заменить на точку. После этого можно формировать новую таблицу. Для этого надо содержимое второй четвёрки столбцов переместить под первую четвёрку столбцов, а затем отсортировать по возрастанию по первому и второму столбцу. Результатом станет такая таблица:

Её надо сохранить в CSV-файл, например, nz_air_lines_coords.csv.

Импорт координат

Для того, чтобы создавать в QGIS объектны на основе данных из электронных таблицы, нам потребуется дополнительный модуль для QGIS. Управление модулями осуществляется через меню «Модули», в котором надо сначала выбрать пункт «Загрузить модули». Откроется окно, в котором сначала будет идти соединение с сервером QGIS (предполагается, что есть работающее соединение с интернетом), а затем появится список доступных модулей:

Количество модулей достаточно велико, и разбираться в их возможностях — отдельная задача. Но пока нам нужен только один: его название — mmqgis — можно ввести в строку фильтра:

После выбора этого модуля и нажатия кнопки «Установить», он станет доступен в меню «Модули» в качестве подпункта «mmqgis». Функционал этого модуля довольно широкий, но нас будет интересовать прежде всего преобразование координат объектов из CSV-файла в формат shape. Соответствующий пункты находится в подменю «Transfer» и называется «Geometry import from CSV File»:

Важный момент: модуль mmqgis не умеет работать с русскими именами файлов и папок. Для того, чтобы нормально с ним работать, я рекомендую создать прямо в корне диска С (или какого-то другого) папку с простым названием латинскими буквами, например tmp и всё взаимодействие с этим модулем вести через эту папку.

Созданный нами файл nz_air_lines_coords.csv надо поместить в эту папку. После этого в меню «Модули» добираемся до пункта «Geometry Import from CSV File» и попадаем в диалоговое окно:

После выбора файла, по идее, появится возможность выбирать, что из какого столбца файла должно браться. В нашем случае Longitude Column назыается FROM_LNG, Latitude Column — FROM_LAT, ShapeID Column — LINE_ID, а в Geometry Type надо указать Polyline. В строчке с результирующим shape-файлом стоит проследить, чтобы он попадал в туже папку tmp без русских букв в названии:

Если всё произошло удачно, то на карте появится слой с линиями, соединяющими нужные нам аэропорты:

В атрибутивной таблице новоприобретённого слоя нет ничего кроме LINE_ID:

Чтобы это исправить, надо добавить в качестве слоя CSV-файл с данными о числе пассажиромест (создав для него предварительно правильный csvt), и связать его с нашим shape-файлом линий по полю LINE_ID:

Эту связку можно сохранить в новый shape-файл, и дальше уже работать с ним.

В частности, в нём можно настроить отображение толщины линий пропорционально количеству пассажиро-мест:

Если вы делаете свою карту, и у вас после этого всё выглядит приличнее, чем в моём случае, на этом, скорее всего можно остановиться (ну, разве что сделать линиям скруглённые концы в настройках стиля). Но на карте авиалиний пока что разобрать ничего нельзя:

Глядя на итоговую карту, которую я привёл в начале, можно понять, что линии разной мощности отображаются не только разной толщиной, но и разным цветом. Настроить это несложно, однако, одного цвета будет явно недостаточно для того, чтобы карта нормально читалась. Потребуется изменение самой геометрии, а именно — сделать линии не прямыми. И для этого нам придётся вернуться в электронные таблицы.

Генерация ломаных линий

У линий, которые мы сгенерировали, всего два узла — начало и конец. Для того, чтобы карта лучше читалась, вместо прямых линий лучше использовать ломаные (внешне достаточно похожие на кривые). Естественно, при большом количестве линий хочется хотя бы частично автоматизировать процесс. Идея, которую я буду реализовывать ниже, заключается в следующем.

Каждую прямую линию мы разобьём на 9 сегментов (можно было бы и больше) десятью точками, и будем отклонять концы этих сегментов от прямой линии, превращая наши линии в ломаные. Большинство перелётов в НЗ имеют скорее меридиональное направление, поэтому я буду отклоненять долготу вершин по синусоидальному закону вправо или влево. То есть, долгота вершин будет вычисляться по формуле:

долгота_новая = долгота_старая +/- амплитуда * sin(п * номер_точки_в_линии/9)

Изначальная амплитуда отклонения будет 1 градус (у нас всё сейчас в градусах), т.е. около 100 км, но мы её будем для некоторых линий менять.

Для начала заведём столбец с порядоковым номером для всех линий.

У меня получилось 53 линии. Напротив каждой линии заготовим поля для знака перед синусом, показывающим направление отклонения («плюс» вправо, «минус» влево), и ещё одно поле для амплитуды.

Помимо этих двух полей нам ещё будет удобно иметь координаты вектора, соединяющего начальную точку, с конечной (получаются вычитанием широты начала из широты конца и долготы начала из долготы конца, соответственно):

Теперь можно перейти непосредственно к созданию таблицы с геометрией на отдельном листе. Всего линий у меня 53, в каждой по 10 точек, значит в таблице будет 530 строк (не считая заголовка).
Из каждого номера строки я делаю номер линии по формуле =INT(A2/10)+1 (по-русски это =ЦЕЛОЕ(A2/10)+1) и номер точки по формуле =MOD(A2;10) (или по-русски — =ОСТАТОК(A2;10) ):

Теперь с помощью VLOOKUP (соответственно, ВПР) можно каждой точке приписать LINE_ID, а также сформировать её широту и долготу. С широтой всё несколько проще. Она вычисляется по формуле:

широта = широта_начала + широта_вектор * номер_точки_в_линии/9.

в долготе надо к слагаемым долгота_начала и долгота_вектор * номер_точки_в_линии/9 добавить ещё слагаемое с синусоидальным отклонением. В итоге таблица будет выглядеть так:

В новое окно как значения достаточно скопировать лишь столбцы с LINE_ID, LATIT и LONGIT:

Импорт этой геометрии из CSV-файла в QGIS даёт уже неплохие результаты:

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

(внизу окна меню «Концы», в нём надо выбрать «круглые»). После этого всё выглядит не очень плохо, но становится понятно, что надо добавить цвет:

Я решил разделить линии на 3 категории по количеству пассажиромест в неделю — менее 500, от 500 до 9000, и свыше 9000. Каждая из них получается на карте путём добавления одного и того же слоя только с разными параметрами запроса. К первой категории (менее 500) масштабирование не применяем, оставляя постоянную ширину линии в 0,26мм. Две других категории — с масштабированием по полю SEATS и изначальной толщиной линии 0,0005мм. Результат выглядит так:

На первый взгляд,уже стало совсем хорошо, только не хватает аэропортов. Но потом замечаешь, что некоторые линии хорошо бы подправить — например, попытка выгнуть «горизонтальную» линию Веллингтон—Нельсон даёт странный выброс на запад, от которого хорошо бы избавиться. И это значит, что надо вернуться в таблицу с линиями, которую мы сформировали самой первой в этом разделе, и подправить там амплитуды и знаки. Работа эта довольно муторная, потому что чтобы визуализировать исправление, надо вновь пройти всю цепочку (формирование CSV, импорт геометрии, привязка данных о местах, отображение). Возможно, есть варианты делать это быстрее (или, возможно, стоит подумать о поиске/написании подходящего плагина). Но пока получилось только так.

Подобранные мной значения (линия, знак, амплитуда): (3,-1,1.5), (24,1,1), (25,-1,1.5), (33,1,1.5), (36,1,1), (40,1,1.5), (42,-1,0), (45,-1,1), (46,-1,1.5), (49,1,1.5), (53,-1,1.5), остальные — как изначально. (Уже доделав всё это, я обнаружил, что на демонстрационной версии карты в начале поста амплитуда по умолчанию была 0.5 градуса, но переделывать теперь всё ещё раз уже не хотелось). В итоге, без аэропортов (и с выключенными городами) карта выглядит так:

Загрузка точек из CSV

Добавление на карту точек из CSV файла бывает нужно гораздо чаще, чем добавление линий. Для этого можно было бы использовать тот же импорт геометрии, которым мы уже пользовались, но он имеет большие ограничения на использование русских символов, поэтому гораздо удобнее использовать инструмент для создания слоя точек из CSV-файла, доступный в качестве модуля QGIS. Скорее всего, он уже включён, но если нет, то его надо включить в меню «Модули» — «Управление модулями». Если он включён, то в меню «Слой» есть пункт «Добавить слой из текста с раделителями», а также, возможно, соответствующая кнопка на панели:

Собственно, список аэропортов с координатами у нас уже есть. Достаточно сохранить его как CSV-файл:

После нажатия кнопки (или выбора пункта меню) открывается окно для создания слоя из файла с разделителями:

Модуль отличается достаточно большой «интеллектуальностью» и многое может угадать сам, однако при необходимости можно настроить не только разделитель полей в файле, но десятичный разделитель в числах.

После добавления аэропорты сразу появятся на карте:

В принципе, им не хватает только оформления, но для дальнейшей работы лучше всё-таки пересохранить данные из CSV-файла в SHP-файл, а потом уже приделать к нему красивые значки.

Подходящий для аэропорта символ есть среди SVG-маркеров, прилагающихся к QGIS:

С включёнными подписями названий это выглядит так:

Оформление макета — картинка-врезка

Остаётся лишь создать пустой слой с линиями, чтобы включить его потом в легенду — он создаётся так же, как пустой слой с точками, только в качестве типа объектов надо указать «Линия». После «уборки» и оформления пустого слоя проект выглядит так:

Остальное оформление макета не должно уже вызывать никаких сложностей. Единственное, чего мы ещё не делали — это добавление картинки. Чтобы нарисовать прямоугольник-рамку в редакторе макета есть специальная кнопка (которая, кстати, позволяет рисовать и другие фигуры тоже), а картинка (взятая мной из википедии), добавляется кнопкой .

После нажатия на кнопку, добавляющую изображение, надо ткнуть в карту и там появится рамочка, а сбоку откроются свойства изображения. В них есть непреметное поле для загрузки изображений с кнопкой «...» справа:

Прямоугольник с помощью соответствующего инструмента рисуется естественным образом, после чего его можно, используя кнопку «опустить» ниже картинки с самолётом, а затем, с помощью кнопки — отцентрировать.

Добавление надписи уже не должно вызывать проблем. Итоговая карта выглядит так:

13 комментариев к “Quantum GIS: геометрия”

  • Евгений Ключников:

    Спасибо за полезные статьи. Вот еще бы ссылку на Excel файлы для этого занятия, если они, конечно, сохранились...

  • Алексей, спасибо огромное за статьи по QGIS, довольно полезные рецепты!
    Единственный возникший вопрос по геометрии: как создать окружность с заданным радиусом в метрах вокруг точки с известными координатами(допустим, полученной при экспорте из Excel?
    Заранее спасибо за возможную подсказку )

    • Алексей Яшунский:

      Здравствуйте, Нина! Если речь идёт о создании небольшого числа окружностей прямо в интерфейсе программы, то может пригодиться модуль, который называется "Rectangles Ovals Digitizing". Если же надо создать много окружностей, то, видимо, лучше сгенерировать их поточечно в экселе (по формуле x²+y²=R²) в подходящей системе координат (чтобы они не были "сплюснутыми"), а потом загрузить в QGIS.

  • Огромное спасибо за статьи) Это прям спасение для студента...

    • Алексей Яшунский:

      Пожалуйста! Рад, что оно всё ещё кому-то полезно, хотя уже много новых версий вышло и пошаговые инструкции стали не такими ценными.

  • Полина:

    А как вы связали csv файл с данными о числе пассажиромест c shape-файлом линий по полю LINE_ID?
    Можно поподробнее этот момент?
    Я не смогла.
    Спасибо.

    • Алексей Яшунский:

      К слою с линиями надо привязывать (через свойства слоя->"Связи") файл nz_air_lines.csv, содержащий только LINE_ID и количество мест. Про то, как его сформировать, написано в тексте. Или вопрос не в этом?

  • Полина:

    Алексей, именно в этом и был вопрос. Всё сделала как Вы написали, всё получилось! Спасибо Вам огромное! У Вас отличные статьи, побольше бы таких!!!

    • Алексей Яшунский:

      Пожалуйста! Рад, что они всё ещё востребованы, спустя столько времени.

  • Герман:

    Можно ли выслать таблицу, которую удалось создать по количеству самолетов и мест на внутренних рейсах?

  • Аноним:

    Алексей, при создании точечного слоя из файла CSV в названиях столбцов атрибутивной таблицы обрезается текст. Стоит какое-то ограничение на количество символов? Причем эта проблема возникает только после пересохранения слоя в шейпфайл. Не подскажете решение? И спасибо Вам за статьи!

    • Алексей Яшунский:

      Наверняка есть ограничение на длину названия поля в SHP, но можно ли его снять, я не знаю. Мне почему-то кажется, что нет. Возможно, я ошибаюсь.

  • Аноним:

    Я тоже к этому склоняюсь. Видимо эта проблема возникает из за кириллицы. Спасибо Вам за ответ!

Оставить комментарий

Тема WordPress и иконки разработаны N.Design Studio
© 2018 Страница Алексея Яшунского RSS записей RSS комментариев Войти