OpenStreetMap

Tile Stitching...

Posted by mkarau on 24 October 2013 in English (English)

Below is a shell script using graphicsmagick (can be easily adapted to imagemagick by simply removing gm from the two image-generation lines below) for stitching together downloaded tile sets into one large PNG. It is tested on MacOS 10.8.5 with graphicsmagick installed via homebrew and should work on most Linux distros. This script is adapted from a script posted by HannesHH.

The script can be useful when producing large printed maps at a zoom level unavailable from most online services. I use JTileDownloader to grab tiles. Be sure to obey the terms of use of your tile server (or bake your own).

#!/bin/bash
# Original credit to OSM user HannesHH
# Updated by OSM user mkarau
# will search for tiles in your current directory ($z/$x/$y.png) and stitch them all together
# will fail horribly if you do not have a rectangular area with tiles
# this is super i/o intense if you use it for a larger amount of tiles so do not do that
# that is because i am lazy and just needed something for small areas
# uses graphicsmagick's gm tool, just remove "gm" if you use imagemagick
# needs bash 3 i guess
# pass the wanted zoomlevel as argument, eg "bash ./tilestitch.sh 14"

blankFileName="./blank.png"
stitchedFileName="./stitchedtiles.png"
#montageOptions="-verbose +frame +shadow +label"
montageOptions="+frame +shadow +label"

runScript=0

if [ $# -gt 0 ];
then
    zoomlevel=$1
    if [ ${zoomlevel} -ge 0 ];
    then
        if [ ${zoomlevel} -le 18 ];
        then
            if [ -d "${zoomlevel}" ];
            then
                runScript=1
            fi
        fi
    fi
fi


if [ ${runScript} -le 0 ];
then
    echo "usage: $0 ZOOMLEVEL"
    echo "ZOOMLEVEL: 0-18"
    echo "Files must be stored in the directory structure below"
    echo "./ZOOMLEVEL/XXXXXX/YYYYYY.png"
    exit    
fi

rows=$(ls -1 $zoomlevel/*/*.png | grep -Eo '/.*/' | sort| uniq | wc -l)
rows=$(echo ${rows} | tr -d ' ')

columns=$(ls -1 $zoomlevel/*/*.png | grep -Eo '/[^/]*png' | sort | uniq | wc -l)
columns=$(echo ${columns} | tr -d ' ')

min_x=$(ls -1 $zoomlevel/*/*.png | grep -Eo '/.*/' | sed 's/\///g' | sort -n  | head -1)
max_x=$(ls -1 $zoomlevel/*/*.png | grep -Eo '/.*/' | sed 's/\///g' | sort -n | tail -1)
min_y=$(ls -1 $zoomlevel/*/*.png | grep -Eo '[^/]*png' | sed 's/\.png//g' | sort -n | head -1)
max_y=$(ls -1 $zoomlevel/*/*.png | grep -Eo '[^/]*png' | sed 's/\.png//g' | sort -n | tail -1)
# so sorry...

echo "zoomlevel: $zoomlevel"
echo "rows: $rows"
echo "columns: $columns"
totalTiles=$(( rows * columns ))
echo "Total Tiles: ${totalTiles}"

echo "min_x: $min_x"
echo "max_x: $max_x"
echo "min_y: $min_y"
echo "max_y: $max_y"

missingTiles=0
tileNo=0
xIndex=0
yIndex=0

echo -n "Ordering filenames for stitching "
# gm montage needs them ordered by column ($y).
for y in $(eval echo {${min_y}..${max_y}})
do
    for x in $(eval echo {${min_x}..${max_x}})
    do  
        xIndex=$((x - min_x))
        yIndex=$((y - min_y))
        tileNo=$(( ((yIndex) * (rows)) + (xIndex) ))
#       echo "Count: ${tileNo} Missing: ${missingTiles} X: ${x}  Y: ${y} xIndx: ${xIndex} yIndx: ${yIndex}" 
        if [ -f $zoomlevel/$x/$y.png ];
        then
            filenames="${filenames} ${zoomlevel}/${x}/${y}.png"
            echo -n "."
        else
            echo -n "B"
#           echo "$zoomlevel/$x/$y.png does not exist, using blank tile"
            filenames="${filenames} ${blankFileName}"
            missingTiles=$(( missingTiles + 1 ))            
        fi
    done
done
echo " DONE"

if [ ${missingTiles} -gt 0 ];
then
    echo -n "Creating blank tile: "
    gm convert -format png -geometry 256x256+0+0 -colorspace RGB xc:none ${blankFileName}
    echo "DONE"
fi

echo -n "Creating Montage (${stitchedFileName})... "
tileSize="${rows}x${columns}"
gmCommand="gm montage ${montageOptions} -tile ${tileSize} -geometry 256x256+0+0 ${filenames} ${stitchedFileName}"
#echo "${gmCommand}"
${gmCommand}
echo "DONE"


if [ ${missingTiles} -gt 0 ];
then
    if [ -f ${blankFileName} ];
    then
        echo -n "Removing blank tile (${blankFileName}): "
        rm ${blankFileName}
        echo "DONE"
    fi
fi
Location: 30th Street, Al Dhafra, United Arab Emirates

Comment from 51114u9 on 30 October 2013 at 06:45

Works great on Slackware / Linux and imagemagick.

Thanks!

Hide this comment

Comment from aharvey on 31 October 2013 at 09:44

I also have a similar script in Perl at https://gist.github.com/andrewharvey/3736925

It will take a lat/lon and zoom and image width/height download tiles and paste these together into a single map.

But if your script works for you, no reason not to use it!

Hide this comment

Comment from mkarau on 1 November 2013 at 06:54

Thanks for the link, aharvey. This works well for me in conjunction with a tile downloader - nice to see you've wrapped the process all together.

Hide this comment

Comment from Harry Wood on 20 November 2013 at 12:42

I created a wiki page, mainly so I could make this findable in the Tile Stitching category. Quite a few different tools do this. Maybe we should make a more general page 'Tile Stitching'

Hide this comment

Comment from kannix on 18 April 2014 at 15:44

Thanks a lot, just what I needed :-)

Hide this comment

Comment from kannix on 20 April 2014 at 07:29

Now I understand "will fail on non rectangular area" ;-)

I am fiddling around seacharts (OpenSeaMap): OSeaM-tiles are non existing in case there is nothing to show.... Could you extend your script, checking for missing y-directories (and missing x-files in missing y-directories)?

Thanks a lot, Dirk http://maps.grade.de/

P.S. https://code.google.com/p/gmapcatcher is a great tool, fetching tiles along a path. Perfect small footprint downloader in conjunction with your script :-)

Hide this comment

Leave a comment

Parsed with Markdown

  • Headings

    # Heading
    ## Subheading

  • Unordered list

    * First item
    * Second item

  • Ordered list

    1. First item
    2. Second item

  • Link

    [Text](URL)
  • Image

    ![Alt text](URL)

Login to leave a comment