UPD 2013-11-16: обновил список программ, добавил информацию об автоматической обрезке рамок, добавил информацию о создании многослойного JNX. Убрал текст помеченный как удалённый. За остальными подробностями - в историю изменения страницы. Вики всё же :)
UPD 2024-11-15:
- по растру ещё можно поглядеть цикл статей на этом блоге:
https://blog.lexa.ru/tags/jnx
- в частности референс на следующие тулы:
- JNXCustomizer
- jnxmerge
- jnx2img
- в частности референс на следующие тулы:
- https://www.the-thorns.org.uk/mapping/help/jnx.html
- https://github.com/apr2504/jnxutil
- уточнил информацию по map2jnx
- поправил форматирование текста/блоков кода
Задача: сделать растровую карту для навигатора 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, что бы не конфликтовать по имени, из комплекта QMapShack переименован в
qmt_map2jnx
(QMT - QMapTool - внешний тул из комплекта QMapShack для привязки карт). Текущая версия - 1.17.1 (ноябрь 2024).
- 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 я писать не буду в виду простоты оной, а так же её неактуальности.
Про это я уже упомянул в своей прошлой заметке, теперь немного развернём, в частности, как исправить что бы проекция стала корректной для всех остальных программ. Буду приводить на примере конкретных имен файлов.
Конвертируем карту в GeoTIFF:
gdal_translate -of GTiff 200k--n57-21_ozf.map 200k--n57-21_ozf.tiff
Карты внутри OZFX3 зачастую имеют индексированную палитру, поэтому сделаем из них RGB, что бы потом добиться прозрачности ненужных областей:
pct2rgb.py 200k--n57-21_ozf.tiff 200k--n57-21_ozf-rgb.tiff
Теперь исправим систему координат в нужную (датум Пулково 1942, элепсоид Красовского, проекция Гаусса-Крюгера (разновидность поперечно-цилиндрической проекции (Transverse Mercator)) для нужной зоны), при преобразовании укажем какой цвет использовать в результирующем изображении как nodata:
gdalwarp -t_srs "EPSG:28427" -dstnodata "255" 200k--n57-21_ozf-rgb.tiff 200k--n57-21_ozf-rgb-fixed.tiff
Дам немного пояснений: параметр EPSG, точнее номер, смотрится в базе EPSG, например тут или тут, для простоты же можно принять, что последние две цифры в номере и есть номер зоны листа, т.е. “EPSG:284##”, где вместо “##” подставляется номер зоны, который, если нужно, дополняется лидирующим нулём (т.е. не “2”, а “02”). Значение EPSG можно полностью заменить описанием проекции, как её считать, можно посмотреть в вышеприведённой статье про привязке генштабовских карт, или брать из тех же базах EPSG((Например тут: http://spatialreference.org/ref/epsg/284##/proj4/, где вместо ## ставим номер зоны - получаем параметры системы координат в виде набора параметров для proj4)). Отмечу только, что номер зоны так же легко узнаётся из номенклатурного листа, просто из номера вычитается 30, т.е. в нашем случае лист “n57”, значит номер зоны:
Nз = 57 - 30 = 27
Кроме того номер зоны легко узнаётся из километровой сетки: у полгого значения в километрах отбрасывается три последние цифты и остаётся номер зоны (на листе линии нумеруются двухзначным числом, но в левом верхнем углу, обычно, стоит небольшими цифрами что-то вроде 274, т.е. полное значение будет 27444, если на вертикальной линии стоит подпись 44, а номер зоны отсюда - 27). И всё-таки вынесу формулы расчёта центрального меридиана и номера зоны по прочим данным:
lon_0 = Nз * 6 - 3 # центральный меридиан по известной зоне
Nз = floor(lon / 6) + 1 # номер зоны по любому значению долготы на листе, floor - взятие целой части от деления
Nз = (lon_0 + 3) / 6 # номер зоны по центральному меридиану (частный случай)
x_0 = Nз * 1000000 + 500000 # мнимый восток (false easting)
А теперь сделаем всё пространство, которое стало после предыдущего преобразования nodata полупрозрачным:
gdalwarp -srcnodata "255" -dstalpha 200k--n57-21_ozf-rgb-fixed.tiff 200k--n57-21_ozf-rgb-result.tiff
Сразу сохраним (на будущее) геоданные из файла:
listgeo -no_norm 200k--n57-21_ozf-rgb-result.tiff > 200k--n57-21_ozf-rgb-result.geo
Все эти шаги объеденены в маленьком скрипте:
#!/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.