Hatred's Log Place

DON'T PANIC!

Jun 26, 2011 - 9 minute read - Comments - linux туризм maps

Готовим растровую карту для навигатора Garmin GPSMap 62s

UPD 2013-11-16: обновил список программ, добавил информацию об автоматической обрезке рамок, добавил информацию о создании многослойного JNX. Убрал текст помеченный как удалённый. За остальными подробностями - в историю изменения страницы. Вики всё же :)

Задача: сделать растровую карту для навигатора Garmin GPSMap 62s.

Инструменты: * GDAL 1.8.0. С версии 1.10.0 научился конвертировать привязки OZI Explorer в виде .map + растр (.png, .gif и т.п.) в geotiff, рекоменду обновляться. * map2jnx 1.7.8. В последних версиях QLandkarteGT идёт в комплекте + уже с моими патчами для задания различного scale-factor для разных слоёв. Скачёк версии с 0.2.4 до 1.7 (или даже 1.6) случился как раз из-за включения в состав QLandkarteGT. * QLandkarteGT задепрекейчена в пользу новой программы от автора - QMapShack * Соответственно map2jnx теперь можно разжиться тут * QLandKarteGT или QMapShack - нужны для просмотра. * NEW: geocrop * NEW: ozi2map, опционально, если у вас GDAL младше 1.10.x и есть куча растров привязанных в OZI Explorer (.gif, .png, .jpg и т.д.)

Исходные карты в формате OZFX3 (Ozi Explorer) с готовой привязкой. Если нет привязанных карт, можно почитать тут как это сделать самому. Теперь можно брать любые озиковские привязки (кроме OZFX2 и новых шифрованных OZFX3), но только если у вас версия gdal больше 1.10.0, иначе можно воспользоваться утилитой ozi2map.

Конвертируем карту из OZFX3 в GeoTIFF

Для простых привязок OZI Explorer почти всё аналогично. Про использование ozi2map я писать не буду в виду простоты оной, а так же её неактуальности.

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

  1. Конвертируем карту в GeoTIFF:```bash gdal_translate -of GTiff 200k–n57-21_ozf.map 200k–n57-21_ozf.tiff

    1. Карты внутри OZFX3 зачастую имеют индексированную палитру, поэтому сделаем из них RGB, что бы потом добиться прозрачности ненужных областей:```bash
    pct2rgb.py 200k--n57-21_ozf.tiff 200k--n57-21_ozf-rgb.tiff
  2. Теперь исправим систему координат в нужную (датум Пулково 1942, элепсоид Красовского, проекция Гаусса-Крюгера (разновидность поперечно-цилиндрической проекции (Transverse Mercator)) для нужной зоны), при преобразовании укажем какой цвет использовать в результирующем изображении как nodata:```bash gdalwarp -t_srs “EPSG:28427” -dstnodata “255” 200k–n57-21_ozf-rgb.tiff 200k–n57-21_ozf-rgb-fixed.tiff

    Nз = 57 - 30 = 27
    ``` Кроме того номер зоны легко узнаётся из километровой сетки: у полгого значения в километрах отбрасывается три последние цифты и остаётся номер зоны (на листе линии нумеруются двухзначным числом, но в левом верхнем углу, обычно, стоит небольшими цифрами что-то вроде 274, т.е. полное значение будет 27444, если на вертикальной линии стоит подпись 44, а номер зоны отсюда - 27). И всё-таки вынесу формулы расчёта центрального меридиана и номера зоны по прочим данным:```scilab
    lon_0 = Nз * 6 - 3            # центральный меридиан по известной зоне
    Nз    = floor(lon / 6) + 1    # номер зоны по любому значению долготы на листе, floor - взятие целой части от деления
    Nз    = (lon_0 + 3) / 6       # номер зоны по центральному меридиану (частный случай)
    x_0   = Nз * 1000000 + 500000 # мнимый восток (false easting)
    1. А теперь сделаем всё пространство, которое стало после предыдущего преобразования nodata полупрозрачным:bash gdalwarp -srcnodata "255" -dstalpha 200k--n57-21_ozf-rgb-fixed.tiff 200k--n57-21_ozf-rgb-result.tiff
  3. Сразу сохраним (на будущее) геоданные из файла:```bash listgeo -no_norm 200k–n57-21_ozf-rgb-result.tiff > 200k–n57-21_ozf-rgb-result.geo

    Все эти шаги объеденены в маленьком скрипте:
    ```bash
    #!/bin/bash
    
    
    use()
    {
    echo "Use: $0 <OziExplorer MAP file> [Gauss-Kruger Zone]"
    }
    
    if [ -z "$1" ]; then
    use
    exit
    fi
    
    inf="$1"
    name=`basename "$inf" .map`
    
    
    # convert ozfx to geotiff
    tmp1=`mktemp`
    gdal_translate -of GTiff "$inf" "$tmp1"
    
    # convert pallete to RGB
    tmp2=`mktemp`
    pct2rgb.py "$tmp1" "$tmp2"
    
    # Check zone or calculate it
    if [ -z "$2" ]; then
    central_meridian=`gdalinfo "$tmp2" | grep "central_meridian" | sed 's|.*PARAMETER<br/>["central_meridian",<br/>([0-9.]<br/>+<br/>)<br/>].*|<br/>1|'`
    zone=`expr $central_meridian / 6 + 1`
    else
    zone="$2"
    fi
    
    if [ -z "$zone" ]; then
    echo "Gauss-Kruger Zone for map don't set and I can't take it from source map. Please point it manually and try again."
    exit
    fi
    
    # format zone to two digit
    zone=`printf "%.2d" $zone`
    
    # EPSG data
    # See: http://gisgeek.pdx.edu/webmapping/epsg.html
    # See: http://nautilus.baruch.sc.edu/resources/mapserver/epsg
    EPSG="EPSG:284$zone"
    
    # Fix projection and coordinate systems
    tmp3=`mktemp`
    gdalwarp -t_srs "$EPSG" -dstnodata "255" "$tmp2" "$tmp3"
    
    # Make transparency and produce map, also, save geodata to TXT
    gdalwarp -srcnodata "255" -dstalpha "$tmp3" "$name.tiff"
    listgeo -no_norm "$name.tiff" > "$name.geo"
    
    rm -f "$tmp1" "$tmp2" "$tmp3"

Использовать очень просто:

./genshtab-ozfx3-to-geotiff.sh 200k--n57-21_ozf.map

В результате работы в этом же каталоге появятся два новых файла: * 200k–n57-21_ozf.tiff - привязанный GeoTIFF * 200k–n57-21_ozf.geo - геоданные из карты, для будущих преобразований.

Если же карту привязывали сами в QLandKarteGT, то для полученного в ней результата потребуется выполнить только шиги 4 и 5

Обрезка рамки

UPD 2013-11-16: тест ниже оставлен только для ознакомительных целей. Сразу переходите к разделу “Автоматическая обрезка рамки”.

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

Тут небольшое лирическое отступление: все рассмотренные мной инструменты, которые работают напрямую с GeoTIFF не позволяют вырезать произвольные области, а с gdalwarp и его опциями -cutline и -crop_to_cutline я пока так и не понял как работать: всё время получается пустой (полностью прозрачный) результирующий файл. По этой причине, Я решил, что нужно сохранить геоданные в файл, редактировать его в любом растровом редакторе (ГЛАВНОЕ НЕ МЕНЯТЬ РАЗМЕРА В ПИКСЕЛЯХ!!!) и потом копировать в результат сохраненные данные о привязке.

Итак, растровый редактор - GIMP и его инструмент “Лассо” (для произвольного выделения). Выделяем нужную область, раставляя точки в углах (этого, по большей части, достаточно). Когда область выделения будет замкнута, нужно инвертировать выделение (Ctrl+I) и нажать Del. После чего экспортировать картинку в тот же файл, в параметрах экспорта выбрать сжатие LZW и обязательно снять галочку с параметра “Сохранять значения цвета прозрачных точек”.

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

geotifcp -g 200k--n57-21_ozf.geo 200k--n57-21_ozf.tiff 200k--n57-21_ozf-croped.tiff

Всё, на этом шаге имеем привязанную карту с обрезанными полями.

Автоматическая обрезка рамки

Да, меня достала ручная обрезка и около года назад (на момент обновления статьи), при подготовке картографического материала для предстоящего похода по Кодару я написал маленькую утилиту geocrop, которая, используя номенклатурную информацию о листе карты, производит автоматическое определение нужной области и обрезку лишнего. Про саму программу можно почитать тут: post/2012/06/22/dve_utility_dlja_raboty_s_kartami_ozi2map_i_geocrop#geocrop. Там же примеры использования.

Склейка карт

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

Интуитивно просится для этой операции программа gdal_merge.py. Вот только в ходе своей работы она перезаписывает прозрачным цветом соседние изображения, как результат - слитые изображения разделены прозрачными полосами, причем, достаточно широкими. И тут приходят на помощь списки рассылок, достаточно добавить параметр “-n 0” и всё становится на свои места :) в результате команда для объединения выглядит примерно так:

gdal_merge.py -n 0 -o kamchatka-elizovo-rgb.tiff -co COMPRESS=LZW -co TILED=YES *-croped.tiff

В QLandKarteGT, при сборке атласа все карты тоже отлично сошлись, но с таким атласом можно работать только в самом QLandKarteGT (в можно прямо из него экспортировать в JNX, за подробностями - в документацию).

Всё, на этом шаге мы получаем большой склеенный GeoTIFF.

Генерация JNX

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

У нас есть большой TIFF с привязкой, теперь его можно преобразовать в формат JNX, для этого потребуется утилита map2jnx, до недавних пор она пряталась в svn проекта QLandkarteGT, не имела официальной странички. Теперь же её версия скаканула с 0.2.4 до 1.7.8 и поставляется вместе с QLandkarteGT. Пользователям современных дистрибутивов и пользователям Windows теперь даже собирать ничего не нужно.

Собрается карта не просто, а очень просто:

map2jnx -n "Kamchatka. Elizovo" -m "Kamchatka. Elizovo" -c "Alexander Drozdov <hatred@inbox.ru>" kamchatka-elizovo-rgb.tiff kamchatka-elizovo.jnx

где: * “-n” задаёт имя карты * “-m” имя продукта (будет выводится в навигаторе при выборе карты) * “-c” данные о копирайте, предлагаю записывать сюда создателя карты, что бы иметь возможность к нему обратиться, вдруг если что. * kamchatka-elizovo-rgb.tiff - мой склеенный GeoTIFF * kamchatka-elizovo.jnx - результирующий файл. Скачать его можно тут((на 2013-11-16 ссылка битая, если кому срочно нужно: пишите на почту или в комментарии))

Просмотреть карту на компе можно в программе QLandKarteGT, правда отображается она там малость убого, но для общего представления вполне себе сгодится. В приборе отображается корректно.

Что бы карта увиделать в приборе, надо её забросить в каталог /Garmin/BirdsEye во внутренней памяти или на флешке.

Многослойный JNX

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

Логично, что для панорамного обзора лучше использовать карду 1:1 000 000, и делать масштам карты больше, по мере увеличения.

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

Итак, какие входные данные нам нужны? А нужно повторить все шаги до раздела (включительно) “Склейка карт” для карт разных масштабов, к примеру: * 1:1 000 000 * 1:500 000 * 1:200 000 * 1:100 000 * 1:50 000

При наличии автоматической резалки границ карт, это не должно занять много времени (только время на обработку карт).

Количество слоёв JNX ограничено 5, поэтому, возможно, придётся чем-то жертвовать.

Далее нужно выдать примерно такую команду: map2jnx -p 1 -c ‘Alexander Drozdov hatred@inbox.ru’ -m Kodar -n ‘Kodar 1M, 500k, 200k, 100k, 50k’ -z 25 -x 78125,20834,7813,3125,1303 1M/merge/kodar-1M-pct.tif 500k/merge/kodar-500k-pct.tif 200k/merge/kodar-200k-pct.tif 100k/merge/kodar-100k-pct.tif 50k/merge/kodar-50k-pct.tif kodar-1M-50k.jnx

Она нам создаст многослойный kodar-1M-50k.jnx((если кому нужно - тоже могу поделиться)) из набора склееных карт для разных масштабов. Интересна опция -x и цифры - это, собственно, и есть коэффициенты масштаба, выведены эмпирическим путём, хорошо подходят для прибора Garmin GPSMAP 62s. Более подробно в справке к map2jnx:

  -x Override levels scale. Default: autodetect

...

Scale levels must be pass in same order as level files pointed.
Empty and zero values equal to autodetect. We can point only needed
levels, like:
  -x 45356,,,75; -x ,,,,75
Calculated levels table can be found:
  English: http://whiter.brinkster.net/en/JNX.shtml
  Russian: http://whiter.brinkster.net/JNX.shtml
Most common values for different map scales:
  JNX scale              Map scale
  -------------          ---------
  78125-31250            1:1 000 000
  20834-7813             1:500 000
  7813-3125              1:200 000
  3125-2084              1:100 000
  2084-782               1:50 000
  782-32                 1:25 000
  32-21                  1:10 000
  21-14                  1:5000, 1:2000

Остальные опции описаны выше.

Выводы и результаты

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

Кроме того, чувствуется необходимость в фронт-энде к этим утилитам для более комфортного проведения операций обрезки и склейки карт.

UPD 2013-11-16: больше 2х лет прошло с момента публикации статьи, графических утилит так и не появилось, но появились инструменты для автоматического кропа и, как следствие, для практически полного скриптинга процесса конвертации и получения готового рузультата. Теперь, при наличии хороших источников привязанных карт, подготовка пятислойного JNX практически для любого района занимает не больше 1 часа времени на достаточно слабом нетбуке EeePC.