Google/OSM to TMS tile renamer

About

This is a simple BASH script to rename map tiles from Google Maps/OpenStreetMap format to Tile Map Service format.
Click here to skip the background info and go straight to the script.
There are several standards for serving slippy map tiles from a standard web server. Two common ones are Google Maps format (also used by Yahoo Maps, OpenStreetMap, and OpenAerialMap) and Tile Map Service (used by OpenLayers, TileCache, and applications such as MyTrails; the Web Map Tiling Service specification is also very similar). A third, related, format called QuadTree is used by Microsoft Virtual Earth and uses the same projection and file format but has a different naming scheme; this format is not covered here.
The Google and TMS formats are very similar. Both store the tiles in /ROOT/Z/X/Y format where Z is the zoom level (0..23), and X and Y are the tile coordinates. The naming scheme is the same for both except that the Y coordinate uses a different origin. The following equation converts a Y-coordinate from Google to TMS format: YTMS = 2zoom - YGoogle - 1
Comments are welcome, see the email link at the bottom of the page.

Using the script

Download the script, make it executable (chmod +x google_to_tms.sh), and copy it to somewhere in your PATH (e.g. to /usr/local/bin/). Basic usage is google_to_tms.sh /path/to/tiles/root (change the path as appropriate)
By default the script displays the name of each directory as it is processed, give the -q flag (quiet) to suppress this.
Give the -v flag (verbose) to have the rename commands displayed as they are run.
Give the -n flag (no-act) to not actually do any renaming; this is useful as a sanity check.
Note that all flags must appear at the end of the commandline.

Source

#!/bin/bash

# google_to_tms.sh
# Copyright 2015 Alexander Hajnal
# http://alephnull.net/software/gis/google_to_tms.shtml

# Usage
# ------------------------------------------------------------------------------
# google_to_tms.sh /path/to/tiles/root [ -q ] [ -v ] [ -n ]
# 
# -q -> Quieter output
# -v -> Verbose output
# -n -> Don't actually do any renaming, instead just say what would be done
# 
# Root directory should contain the numbered level directories (0..23).
# Note that this script does _not_ check which file naming convention is 
# currently being used.

# Warranty
# ------------------------------------------------------------------------------
# I make no warranty or representation, either express or implied, with respect 
# the behavior of this script, its quality, performance, accuracy, merchantability, 
# or fitness for a particular purpose. This script is provided 'as is', and 
# you, by making use thereof, are assuming the entire risk.  That said, 
# I hope you this script useful.  Have fun!

Usage() {
  echo "Usage: $0 /path/to/tiles/root [ -q ] [ -v ] [ -n ]"
  echo "Rename tiles from Google Maps/OpenStreetMap format to Tile Map Service format"
  echo "Copyright 2015 Alexander Hajnal"
  echo
  echo "-q -> Quieter output"
  echo "-v -> Verbose output"
  echo "-n -> Don't actually do any renaming, instead just say what would be done"
  echo
  echo "Root directory should contain the numbered level directories (0..23)."
  echo "Note that this script does _not_ check which file naming convention is "
  echo "currently being used."
}

if [ "$#" -eq 0 ]; then
  Usage
  exit
fi

ROOT=`echo "$1"|sed 's/\/*$//'`

VERBOSE=0
QUIET=0
NO_ACT=0

if [[ $2 == '-v' || $3 == '-v' || $4 == '-v' ]] ; then
  VERBOSE=1
fi

if [[ $2 == '-q' || $3 == '-q' || $4 == '-q' ]] ; then
  QUIET=1
fi

if [[ $2 == '-n' || $3 == '-n' || $4 == '-n' ]] ; then
  NO_ACT=1
fi

if [[ ! -d "$ROOT" ]]; then
  echo "FATAL: \`$ROOT' is not a directory"
  echo
  Usage
  exit
fi


ProcessImageLevel() {
  SUBLEVEL_PATH=$1
  if [[ $QUIET == 0 ]] ; then
    echo "$SUBLEVEL_PATH"
  fi
  
  ls "$SUBLEVEL_PATH" | while IFS= read entry; do
    if [ -f "$SUBLEVEL_PATH/$entry" ]; then
      # Only process files...
      image_regex='^([0-9]+)(\..+)?$'
      if [[ $entry =~ $image_regex ]] ; then
        # ...that look like valid tiles ( NUMBERS or NUMBERS.EXTENSION )
        y=${BASH_REMATCH[1]}
        ext=${BASH_REMATCH[2]}
        new_y=$(((1<<zoom) - y - 1))
        if [[ $NO_ACT == 1 || $VERBOSE == 1 ]] ; then
          echo mv \""$ROOT"/$zoom/$x/$y$ext\" \""$ROOT"/$zoom/$x/$new_y$ext\" 
        fi
        if [[ $NO_ACT == 0 ]] ; then
          mv "$ROOT/$zoom/$x/$y$ext" "$ROOT/$zoom/$x/$new_y$ext" 
        fi
      fi
    fi
  done
}


ProcessTopLevel() {
  LEVEL_PATH=$1
  ls "$LEVEL_PATH" | while IFS= read x; do
    if [ -d "$LEVEL_PATH/$x" ]; then
      # Only process directories...
      numbers_regex='^[0-9]+$'
      if [[ $x =~ $numbers_regex ]] ; then
        # ...consisting of all numbers
        ProcessImageLevel "$LEVEL_PATH/$x"
      fi
    fi
  done
}


if [[ $QUIET == 0 ]] ; then
  echo "Renaming tiles in $ROOT"
fi

ls "$ROOT" | while IFS= read zoom; do
  if [ -d "$ROOT/$zoom" ]; then
    # Only process directories...
    regex='^([0-9])|([1-9][0-9]))$'
    if [[ $zoom =~ $regex ]] ; then
      # ...numbered from 0 through 99
      ProcessTopLevel "$ROOT/$zoom"
    fi
  fi
done

Did you find this page useful? Want to say thanks? Consider leaving a tip via PayPal:

Other GIS pages on this site

I make no warranty or representation, either express or implied, with respect the behavior of this script, its quality, performance, accuracy, merchantability, or fitness for a particular purpose. This script is provided 'as is', and you, by making use thereof, are assuming the entire risk. That said, I hope you this script useful. Have fun!