Skip to content

Instantly share code, notes, and snippets.

@defremov
Last active June 1, 2022 18:46
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save defremov/e4f0ed47e1402ba65edd9e2d4c2b210c to your computer and use it in GitHub Desktop.
Convert raster images of Slazav's map series (https://slazav.xyz/maps/podm.htm) to Garmin JNX format files
.SUFFIXES:
map_files := $(wildcard *.map)
jnx_files := $(map_files:%.map=%.jnx)
.PHONY: all
all: $(jnx_files)
%.jnx: %.png %.map
bash slazav2jnx.sh $^ $@
clean:
rm -f $(jnx_files)
#! /usr/bin/env awk -f
# Copyright (C) 2020 Dmitry Efremov <defremov аt aha dоt ru>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
function mins_to_dec_degs(degs, mins, hemi) {
sign = (hemi == "S" || hemi == "W") ? -1 : 1
return sign * (degs + mins / 60.)
}
function expand_bounds(lat, lon) {
if (lat < minlat) minlat = lat
if (lon < minlon) minlon = lon
if (lat > maxlat) maxlat = lat
if (lon > maxlon) maxlon = lon
}
BEGIN {
_abort = 0
FS = "\\s*,\\s*"
CONVFMT = "%.8f"
minlat = 90.
maxlat = -90.
minlon = 180.
maxlon = -180.
if (!gcp_cmd) {
gcp_cmd = "cat"
}
i = 0
}
/^Point[0-9]{2},xy,\s*[0-9]+,/ {
xy[i++]= $3 " " $4
lat = mins_to_dec_degs($7, $8, $9)
lon = mins_to_dec_degs($10, $11, $12)
print lon " " lat |& gcp_cmd
expand_bounds(lat, lon)
}
END {
if (_abort) {
exit _abort
}
if (!extents_file) {
extents_file = "/dev/stdout"
}
print minlon, minlat >extents_file
print maxlon, maxlat >extents_file
if (!gcp_file) {
gcp_file = "/dev/stdout"
}
close(gcp_cmd, "to")
i = 0
while ((gcp_cmd |& getline lonlat) > 0) {
sub(/\s+0$/, "", lonlat)
print xy[i++] " " lonlat >gcp_file
}
close(gcp_cmd)
}
#! /usr/bin/env bash
# This script converts raster images of Slazav's map series
# (https://slazav.xyz/maps/podm.htm) to Garmin JNX format
# files.
#
# Depends on: gawk, map2jnx tool and GDAL utilities.
#
# Example: to convert N55aE037 sheet to JNX format download
# N55aE037.png and N55aE037.map files then run:
# slazav2jnx.sh N55aE037.{png,map} N55aE037.jnx
#
# Copyright (C) 2020 Dmitry Efremov &lt;defremov аt aha dоt ru&gt;
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
set -Eeuo pipefail
PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH
progname="${0##*/}"
slazav_map="$(dirname "$0")/slazav-map.awk"
declare -a tmpfiles
map2jnx=qmt_map2jnx
# Suitable for a 160×240 display
declare -a jnx_scale_values=(31250 7813 5209 2084 1303)
slazav_map_image=''
slazav_map_data_file=''
jnx_file=''
jnx_title=''
newtmp() {
while (( $# )) ; do
tmpfiles=($(mktemp) ${tmpfiles[@]})
eval "${1}=${tmpfiles}"
shift
done
}
term_handler() {
local signal="${1}"
>&2 printf "\n%s: caught %s signal, exiting.\n" \
"${progname}" "${signal}"
exit 1
}
set_signal_handlers() {
trap 'set +e ; kill $(jobs -p) 2>/dev/null ; \
rm -f ${tmpfiles[@]}' EXIT
for signal in INT QUIT TERM ; do
trap 'term_handler "${signal}"' ${signal}
done
}
usage() {
cat <<EOF
${progname} script converts raster images of Slazav's map series
(http://slazav.mccme.ru/maps/podm/index.htm) to Garmin JNX format
files.
Usage: ${progname} [options] map_image map_data_file jnx_file [jnx_title]
Options:
-h show this help message and exit
Depends on: gawk, map2jnx tool and GDAL utilities.
Example: to convert N55aE037 sheet to JNX format download N55aE037.png
and N55aE037.map files then run:
${progname} N55aE037.{png,map} N55aE037.jnx 'Slazav N55aE037'
EOF
}
parse_parameters() {
while getopts "hn" OPT; do
case "${OPT}" in
h)
usage
exit 0
;;
\?)
echo
>&2 usage
exit 1
;;
esac
done
shift $((${OPTIND} - 1))
if [[ $# -lt 3 ]] ; then
>&2 printf \
"${progname}: script requires 3 arguments.\n\n"
>&2 usage
exit 1
fi
slazav_map_image="${1}"
slazav_map_data_file="${2}"
jnx_file="${3}"
jnx_title="${4:-${jnx_file%.*}}"
}
slazav2jnx() {
# Extract ground control points data and the map extents from
# the supplied map data file (.map).
# The GCPs are reprojected from WGS 84 to Pulkovo 1942 /
# Gauss-Kruger zone 7 coordinate system
local pulkovo_42_gcps
local cmd='gdaltransform -s_srs EPSG:4326 -t_srs EPSG:28407'
newtmp pulkovo_42_gcps
local wgs_84_extents=$(awk -f "${slazav_map}" \
-v gcp_cmd="${cmd}" -v gcp_file="${pulkovo_42_gcps}" \
"${slazav_map_data_file}")
# Convert the supplied source map image to 24-bit RGB GeoTIFF
# format and make it georeferenced by adding the GCPs from the
# previous step
local pulkovo_42_gcp_image
newtmp pulkovo_42_gcp_image
gdal_translate -of GTiff -expand rgb\
$(awk '{print " -gcp", $0}' "${pulkovo_42_gcps}") \
-a_srs EPSG:28407 \
"${slazav_map_image}" "${pulkovo_42_gcp_image}"
# Reproject the extents of the source map from WGS 84 to the
# World Mercator coordinate system
local world_mercator_extents=$(gdaltransform -s_srs EPSG:4326 \
-t_srs EPSG:3395 <<<"${wgs_84_extents}" | \
awk '{print $1, $2}')
# Reproject the georeferenced image to the World Mercator CS
local world_mercator_prj_image
newtmp world_mercator_prj_image
gdalwarp -t_srs EPSG:3395 \
-te ${world_mercator_extents} -of GTiff -overwrite \
"${pulkovo_42_gcp_image}" "${world_mercator_prj_image}"
# Get the width and height in pixels of the World Mercator
# image
declare -a pixel_size=($(gdalinfo \
"${world_mercator_prj_image}" | awk -F',\\s*' \
'/^Size is / {sub(/^[^0-9]*/, ""); print $1, $2}'))
# Reproject the georeferenced image from Pulkovo 42 to WGS 84
# CS and downscale it to a set of level of detail images using
# the width and height in pixels from the previous step
local level_image
declare -a level_images
for n in "${!jnx_scale_values[@]}" ; do
newtmp level_image
level_images=(${level_image} ${level_images[@]})
# Halve the resolution of each successive level
gdalwarp -t_srs EPSG:4326 -r cubic \
-te ${wgs_84_extents} \
-ts $(( (${pixel_size[0]} + $n) >> $n )) \
$(( (${pixel_size[1]} + $n) >> $n )) \
-of GTiff -overwrite \
"${pulkovo_42_gcp_image}" "${level_image}"
done
# Convert the set of level of detail images to a JNX file
local x_param=${jnx_scale_values[@]}
"${map2jnx}" -q 90 -s 444 -x ${x_param// /,} \
-p 90 -m "${jnx_title}" "${level_images[@]}" \
"${jnx_file}"
}
main() {
set_signal_handlers
parse_parameters "${@}"
slazav2jnx
exit 0
}
main "${@}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment