I have a 3GB raster layer, and I'd like to export it to KML for use with Google Earth.

When I use the "Layer to KML" conversion tool in ArcMap, I end up with a low-resolution overlay: looks like one 2000x2000 pixel image (give or take). I've tried messing with the "layer output scale" parameter, but it isn't having any effect.


4 Answers 4


Save your money, buy nothing. Associate this script with a toolbox tool. Set it up with 4 parameters (Input Raster, Output Folder, Draw Order and Output Prefix). Run. Tried the SuperOverlay thing from GDAL and didn't think much of that horrible data structure. I never cleaned this up so it does processing it doesn't need to if I remember correctly, but it ran and created a working output. The kmz will even work on a Garmin (*MAP model or newer) as long as you make sure it complies with the models limitations for "Custom Maps".

#First crack at writing something to tile out a source image to
#conform to the Garmin Custom Maps KML format. For now I am not
#concerning myself with limitations of this format on various devices
#and just working on getting the output to always function in
#Google Earth
#To use the output kmz on most Garmin *MAP models open the kmz file in
#Google Earth. Delete tiles until there are only 100 remaining (maximum)
#Resave the resultant kmz to a new file and copy this to the "\Garmin\CustomMaps"
#folder on the GPS
#This image will display on the unit at different zoom levels based on the
#resolution of the image. If you want to use the imagery at a coarser scale
#you will have to resample the input image to a larger cell size and rerun
#this tool.
#Michael Stead, August 29, 2014

# Import arcpy module
import arcpy, arcgis, os, zipfile, shutil

# Get input parameters
source = arcpy.GetParameterAsText(0)
outfolder = arcpy.GetParameterAsText(1)
level = arcpy.GetParameterAsText(2)
outname = arcpy.GetParameterAsText(3)
kmzfile = outfolder + "\\" + outname + ".kmz"
workfile = "tmplayer.tif"

# Get projection of input raster... set output to wgs84
desc1 = arcpy.Describe(source)
sproj = arcpy.SpatialReference(4326)

#make temporary raster layer, ensuring 3 bands only
arcpy.env.workspace = outfolder
arcpy.MakeRasterLayer_management(source, workfile, "#", "#", "1;2;3")

#make sure 8 bit input as other bit depth creates empty output from Split Raster
spath = desc1.Path
if ":" in str(source):
    ddesc = arcpy.Describe(source+"\\Band_1")
if not ":" in str(source):
    ddesc = arcpy.Describe(spath+"\\"+source+"\\Band_1")
sourcedepth = ddesc.pixelType
if sourcedepth <> "U8":
    arcpy.AddMessage("Convert to 8 bit unsigned")
    arcpy.CopyRaster_management(workfile, "t_raster.tif","DEFAULTS","","","","","8_BIT_UNSIGNED")
    workfile = "t_raster.tif"
    arcpy.AddMessage("Already 8 bit unsigned") #but couldn't see how else to pass this as a variable
    #arcpy.CopyRaster_management ("tmplayer.tif", "t_raster.tif","DEFAULTS","","","","","8_BIT_UNSIGNED")

#check if the input is wgs84 already, if not reproject
if desc1.spatialReference.name == arcpy.SpatialReference(4326).name:
    arcpy.AddMessage("Already WGS84")
    #arcpy.CopyRaster_management ("t_raster.tif", "p_raster.tif","DEFAULTS","","","","","8_BIT_UNSIGNED")

    # Reproject to WGS84
    arcpy.ProjectRaster_management(workfile, "p_raster.tif", sproj, "BILINEAR", "#","#", "#", "#")
    workfile = "p_raster.tif"

#Figure out size for tiles (must be less than 1024x1024)
if workfile == "tmplayer.tif":
    workfile = source
desc2 = arcpy.Describe(workfile+"\\Band_1")
if ":" in str(source):
    arcpy.AddMessage("Full Path")
    desc4 = arcpy.Describe(source+"\\Band_1")
if not ":" in str(source):
    arcpy.AddMessage("TOC Layer")
    desc4 = arcpy.Describe(spath+"\\"+source+"\\Band_1")

cellsize = desc4.MeanCellHeight
columnfactor = int((desc2.width)/1024)+1
rowfactor = int((desc2.height)/1024)+1
columnwidth = int(desc2.width/columnfactor)+1
rowheight = int(desc2.height/rowfactor)+1
arcpy.AddMessage("Height: %d" % rowheight)
arcpy.AddMessage("Width: %d" % columnwidth)
tilesize = "%d %d" % (columnwidth, rowheight)

# Create "files" folder for tiled images
arcpy.AddMessage("Creating Folder")
tilefolder =  (outfolder +'\\files\\')   

# Split unprojected raster into a folder of 1024 by 1024 (or less to minimize "no data" tile area) jpg tiles
arcpy.AddMessage("Tiling Input")
arcpy.SplitRaster_management(workfile, tilefolder, outname,"SIZE_OF_TILE","JPG","CUBIC","1 1",tilesize,"0","PIXELS","#","#")

#Create empty doc.kml file
arcpy.AddMessage("Create KML")
kml_file = (outfolder +'\\doc.kml')
f = open(kml_file,"a+")

#Add header to empty doc.kml
arcpy.AddMessage("Build KML Header")
text_file = open(kml_file, "a+")
text_file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
text_file.write('<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">\n')
text_file.write('  <Document>\n')
text_file.write('    <Name>MikesKMZs</Name>\n')

#Loop through folder of tiled rasters and write the extents to the doc.kml file
arcpy.AddMessage("Create KML Tile Extent")
arcpy.env.workspace = tilefolder
rasterList = arcpy.ListRasters("*", "JPG")
for raster in rasterList:
    loopath = tilefolder+'\\'+raster
    describeimg = arcpy.Describe(loopath)
    ##### QUESTION - the west and east values might need to get swapped for eastern hemisphere, and the north and south for the southern hemisphere
    North = describeimg.extent.YMax
    South = describeimg.extent.YMin
    West = describeimg.extent.XMin
    East = describeimg.extent.XMax
    text_file = open(kml_file, "a+")
    text_file.write('    <GroundOverlay>\n')
    text_file.write('      <name>'+raster+'</name>\n')
    text_file.write('      <drawOrder>'+str(level)+'</drawOrder>\n')
    text_file.write('      <color>ffffffff</color>\n')
    text_file.write('      <Description>\n')
    text_file.write('      </Description>\n')
    text_file.write('      <Icon>\n')
    text_file.write('        <href>files/'+str(raster)+'</href>\n')
    text_file.write('      </Icon>\n')
    text_file.write('      <LatLonBox>\n')
    text_file.write('        <north>'+str(North)+'</north>\n')
    text_file.write('        <south>'+str(South)+'</south>\n')
    text_file.write('        <east>'+str(East)+'</east>\n')
    text_file.write('        <west>'+str(West)+'</west>\n')
    text_file.write('        <rotation>0</rotation>\n')    
    text_file.write('      </LatLonBox>\n')
    text_file.write('    </GroundOverlay>\n')

#Add footer to doc.kml file\
arcpy.AddMessage("Add KML Footer")
text_file = open(kml_file, "a+")
text_file.write('  </Document>\n')

#Create kmz and compress kml and jpgs
zip = zipfile.ZipFile(kmzfile,'w')
JPGList = arcpy.ListFiles("*.JPG")
for JPGs in JPGList:

#Clean up working file debris
arcpy.env.workspace = outfolder
if arcpy.Exists("p_raster.tif"):
if arcpy.Exists("t_raster.tif"):
if arcpy.Exists("tmplayer.tif"):

Edit - In 10.2 the line:

arcpy.SplitRaster_management(workfile, tilefolder, outname,"SIZE_OF_TILE","JPG","CUBIC","1 1",tilesize,"0","PIXELS","#","#")

Must be modified to:

arcpy.SplitRaster_management(workfile, tilefolder, outname,"SIZE_OF_TILE","JPEG","CUBIC","1 1",tilesize,"0","PIXELS","#","#")

I realized recently that you can do a find and replace for JPG to PNG in the script and it will support transparencies in Google Earth. The data I converted had 0 as "NoData" and it worked great. I haven't tested if it still works on a Garmin GPS, but their documentation says it wouldn't.

  • Works AWESOME! Can't believe this answer does not have any votes. May 26, 2015 at 15:55
  • Thanks Jakub... Canada rocks eh?... If you use it for Garmin backgrounds you will soon figure out some limitations that Garmin have placed on their format (mainly the lack of a pyramid type rendering for different display levels). That's the catch with Garmins... All the proprietary formats. They want to sell you jnx capability.... That comes with a subscription to their imagery. It does work though. I have created files at different resolutions and stuck them each on a separate SD card. Some are different, but with some you can have multiple on the same card and select one at a time.
    – Mike
    May 30, 2015 at 23:08
  • What does the parameter 'level' do?
    – mmann1123
    Feb 10, 2016 at 20:34
  • The level is meant to control drawing order... I don't think it worked all that well... if at all. It is a Garmin thing. In theory all the layers on the GPS will draw in a certain order. Level 51 should be over top of all the base data but under any waypoints or tracks on the GPS. If you had multiple layers it might be important, but I try and keep my GPS data as simple as possible. I usually overlay my vector data on my raster before I convert it to kmz instead of tying to order layers on a gps. Just leaving waypoint or tracks I might use for navigation as vector.
    – Mike
    Feb 11, 2016 at 0:59
  • On ... I forgot to mention... Google Earth has a drawing order too.... This has nothing to do with that as far as I can remember.
    – Mike
    Feb 11, 2016 at 1:41

I see two possible options:

  1. Use Google Earth Pro to directly load the raster assuming it is in a supported file format. See Importing Geographic Information Systems (GIS) data in Google Earth

  2. Google Earth supports "super overlays" through the use of Region and NetworkLink elements which allows you to display a multi-megapixel image by subsetting it into tiles. KMLer Pro ($50) is an ArcGIS extension that purports to be able to create super overlays directly from ArcGIS maps. There are also several non-ArcGIS tools such as MapTiler, TileCache, and SuperOverlay (related to KMLer).

  1. Download Shape2Earth and apply it to your add-in manager.
  2. Convert your raster to a feature.
  3. Work with Shape2Earth
  4. Save your money.

There is a ArcGIS Tool for converting Maps into Garmin Custom Maps:


Its quite similar to your script... i discoverd this after i finished making this Tool :O

  • Looks like the workflow is pretty much all that is the same... its the workflow that makes sense in my head... but I haven't seen any other versions. At least the coding isn't hacked together.. all that error handling...so many more lines... so much time... haven't tried his version but it sure looks like it should be less problematic for users. I have a "hack" script for localizing web imagery... wish he would work that one up for me :)
    – Mike
    Jun 23, 2016 at 18:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.