Exporting 3GB ArcGIS Raster to KML without losing resolution?
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)
arcpy.AddMessage(str(desc1.spatialReference.name))
arcpy.AddMessage(str(sproj.name))
#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
arcpy.AddMessage(sourcedepth)
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"
else:
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")
else:
# Reproject to WGS84
arcpy.AddMessage("Projecting")
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")
arcpy.CreateFolder_management(outfolder,"files")
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+")
f.close()
#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')
text_file.close()
#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
arcpy.AddMessage(loopath)
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')
text_file.close()
#Add footer to doc.kml file\
arcpy.AddMessage("Add KML Footer")
text_file = open(kml_file, "a+")
text_file.write(' </Document>\n')
text_file.write('</kml>')
text_file.close()
#Create kmz and compress kml and jpgs
zip = zipfile.ZipFile(kmzfile,'w')
zip.write(kml_file,'doc.kml',zipfile.ZIP_DEFLATED)
JPGList = arcpy.ListFiles("*.JPG")
for JPGs in JPGList:
zip.write(tilefolder+'\\'+JPGs,'files\\'+JPGs,zipfile.ZIP_DEFLATED)
#Clean up working file debris
arcpy.env.workspace = outfolder
os.remove(outfolder+'\\doc.kml')
shutil.rmtree(tilefolder)
if arcpy.Exists("p_raster.tif"):
arcpy.Delete_management("p_raster.tif")
if arcpy.Exists("t_raster.tif"):
arcpy.Delete_management("t_raster.tif")
if arcpy.Exists("tmplayer.tif"):
arcpy.Delete_management("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 see two possible options:
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
Google Earth supports "super overlays" through the use of
Region
andNetworkLink
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).