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

Материал из GIS-Lab
Перейти к навигации Перейти к поиску
Эта страница является черновиком статьи.


Рассмотрен процесс подготовки и подключения изображений без привязки в картографический JavaScript-движок Leaflet

Введение

Картографические JavaScript-движки, такие как OpenLayers или Leaflet, порой находят своё применение в таких областях, для которых они изначально вроде бы и не предназначались. Так, например, международное агентство Рейтер продемонстрировало использование Leaflet для интерактивного взаимодействия с фотографиями из зала вручения кинопремии «Оскар»:

Пример №1 нестандартного использования картографического движка

Еще один необычный пример - интерактивный тур по городу на базе его вымышленной карты:

Пример №2 нестандартного использования картографического движка

Таких примеров можно привести множество. Но есть один технический момент, который объединяет все эти «карты» - это то, что все они построены на базе изображений, которые не имеют абсолютно никакой географической привязки. Использование картографических движков для таких изображений добавляет возможность навигации по ним, а при небольшой предварительной обработке - возможность их масштабирования.

Именно вопросу такой предварительной подготовки и посвящена основная часть данной статьи. В качестве языка программирования будем использовать Python, в качестве JavaScript-движка - Leaflet.

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

Обработка исходного изображения

Постановка задачи

Исходное изображение в принципе можно подключить "как есть", в OpenLayers для этого есть класс OpenLayers.Layer.Image, а в Leaflet - L.ImageOverlay, однако в общем случае это плохая идея, так как изображение может быть большим и пользователю придется очень долго ждать, пока оно загрузится, поэтому первое, что нужно сделать с изображением - это разбить его на тайлы. А так как мы хотим иметь возможность не только двигать наше изображение, но и изменять его масштаб, то тайлы должны быть построены для нескольких масштабных уровней.

Выбор исходного изображения

В качестве исходного изображения возьмем изображение, представляющее собой визуализацию землетрясений, произошедших, начиная с 1898 года. Подробнее об изображении тут. Размер изображения - 3410 x 2058.

Исходное изображение (не оригинал, размер уменьшен)

Разбивка на тайлы

Переходим к написанию скрипта, который будет разбивать на тайлы наше изображение. Мы будем использовать инструменты стандартной библиотеки Python, а также одну стороннюю библиотеку - библиотеку для работы с изображениями - PIL, поэтому прежде чем переходить далее, убедитесь, что у вас установлен этот самый PIL.

Сразу оговоримся, что используемая в дальнейшем схема разбивки на тайлы - как в OpenStreetMap (то есть нумерация строк и столбцов тайлов начинается с левого верхнего угла). Алгоритм разбивки изображения на тайлы достаточно прост, но имеет некоторую особенность. Итак, представьте, что мы взяли наше изображение размера 3410 x 2058 и пытаемся разбить его на тайлы 256 x 256. Очевидно, что 3410 и 2058 не делятся без остатка на 256, а это означает, что тайлы последнего столбца и последней строки получатся не квадратными (82 x 256 и 256 x 10 соответственно). Поэтому прежде чем переходить к нарезке тайлов, изображение должно быть приведено к такому виду, что его ширина и высота будут кратны размеру тайла. Размеры поправок вычисляются следующим образом (src_width, src_height - ширина и высота исходного изображения, tile_size - размер тайла, % - операция получения остатка от деления). В Python это записывается так:

dw = tile_size - src_width % tile_size
dh = tile_size - src_height % tile_size

Области расширения за счет поправок следует сделать прозрачными, в нашем коде за эту операцию будет отвечать функция adjustBounds.

Одним из важных параметров процедуры разбивки на тайлы - это максимальный масштабный уровень. Рассчитывается он как логарифм по основанию 2 числа max(w,h), округленный до ближайшего большего целого. В Python это записывается так:

max_zoom = int(math.ceil(math.log((max(w, h) / tile_size), 2)))

Подключение тайлов в Leaflet

{{#widget:Iframe |url=http://gis-lab.info/share/DR/public_html/non-geographical-imagery-ex.html |width=767 |height=367 |border=0 }}

Заключение

В ходе данной статьи была описана процедура разбивки изображения без привязки на тайлы, а также подключение их в Leaflet. Отметим, что операцию по тайлированию изображения можно было выполнить, используя утилиту gdal2tiles, которая входит в состав библиотеки GDAL:

gdal2tiles.py -p raster -z 0-4 earthquakes.jpg

Описанный в статье вариант решения задачи не требует использования GDAL, плюс (что самое, наверное, важное) код довольно простой и наглядно дает понимание того, как вычисляются масштабные уровни и как выполняется непосредственно сама разбивка на тайлы. По коду gdal2tiles разбираться в этих вопросах гораздо сложнее, так как это более универсальный инструмент. И еще, gdal2tiles помимо генерирования тайлов также создает html-файлы с включенными в них движками OpenLayers и Google (но не Leaflet) и подключает на карту набор созданных тайлов, то есть по-сути делает все то же самое о чем идет речь в данной статье, но как-бы за кадром, мы же попытались представить этот процесс более наглядно.

Полезные ссылки

  1. Using leaflet.js with non-geographic imagery
  2. Zoomable image with Leaflet
  3. Images as Maps