Raw File
layer.Raster.R
# Purpose        : Write a raster layer to KML;
# Maintainer     : Pierre Roudier (pierre.roudier@landcare.nz);
# Contributions  : Tomislav Hengl (tom.hengl@wur.nl); Dylan Beaudette (debeaudette@ucdavis.edu); 
# Status         : pre-alpha
# Note           : Rasters can also be written as polygons; see "?grid2poly";

kml_layer.Raster <- function(
  obj,  
  plot.legend = TRUE,
  metadata = NULL,
  raster_name,
  png.width = ncol(obj), 
  png.height = nrow(obj),
  ...
  ){

  # get our invisible file connection from custom environment
  kml.out <- get("kml.out", envir=plotKML.fileIO)

  # Checking the projection 
  prj.check <- check_projection(obj, control = TRUE)

  # Parsing the call:
  call <- substitute(list(...))
  call <- as.list(call)[-1]

  # Check if any attribute has been selected:
  if (is.na(charmatch("colour", names(call)))){
    stop("No attribute selected. Please use the colour = ... option.")
  }

  if(is.call(call[["colour"]])|is.name(call[["colour"]])){
    x <- data.frame(getValues(obj))
    names(x) <- names(obj)
    x <- eval(call[["colour"]], x)
    obj <- raster(obj)
    values(obj) <- x
  } else { 
  if(is.numeric(call[["colour"]])) {
    i_layer <- call[["colour"]]
    if (nlayers(obj) > 1) {
      obj <- raster(obj, layer = i_layer)
    }
  } else { 
  if(is.character(call[["colour"]])) {
    i_layer <- which(names(obj) == call[["colour"]])
    if (nlayers(obj) > 1) {
      obj <- raster(obj, layer = i_layer)
    }
  }}}

  # TH: this needs to be fixed
  altitude <- charmatch("altitude", names(call))
  if(!is.na(altitude)){
    altitude <- eval(call[["altitude"]], nlayers(obj))
  } else {
    altitude <- rep(.all_kml_aesthetics[["altitude"]], length.out = nlayers(obj))
  }
  altitudeMode <- kml_altitude_mode(altitude, GroundOverlay=TRUE) 

  # prepare the palette:
  if (!is.na(charmatch("colour_scale", names(call)))){
    pal <- eval(call[["colour_scale"]])
  } else {
  ## default colour palettes
    if (!is.factor(obj@data[,1])){
      pal <- get("colour_scale_numeric", envir = plotKML.opts)
    } else {
      pal <- get("colour_scale_factor", envir = plotKML.opts)
    }
  }

  # Trying to reproject data if the check was not successful
  if(!prj.check) {  
    obj <- reproject(obj) 
  }

  if(is.factor(obj)){
    if(length(labels(obj))==0){
      warning("RasterLayer of type factor missing labels")
      colour_scale <- colorRampPalette(pal)(length(levels(as.factor(getValues(obj)))))      
    } else {
      colour_scale <- colorRampPalette(pal)(length(labels(obj)[[1]]))
    }
  } else {
    colour_scale <- colorRampPalette(pal)(100)  
  }

  # Transparency
  alpha <- charmatch("alpha", names(call))
  if (!is.na(alpha)) {
    # - constant transparency
    # - raster index if a Stack
    # - name of a layer if a Stack
    colour_scale <- kml_alpha(obj, alpha = eval(call[["alpha"]], obj@data), colours = colour_scale, RGBA = TRUE)
  }

  # Creating the PNG file
  if(missing(raster_name)){
    raster_name <- set.file.extension(as.character(call[["colour"]]), ".png")
  }

  # Plotting the image
  png(filename = raster_name, bg = "transparent", type="cairo-png", width=png.width, height=png.height)
  par(mar = c(0, 0, 0, 0), xaxs = "i", yaxs = "i")
  if(!is.na(charmatch("z.lim", names(call)))){ 
    z.lim <- eval(call[["z.lim"]])
    obj <- calc(obj, fun=function(x){ x[x < z.lim[1]] <- z.lim[1]; return(x)}) 
    obj <- calc(obj, fun=function(x){ x[x > z.lim[2]] <- z.lim[2]; return(x)})
    raster::image(obj, col = colour_scale, zlim = z.lim, frame.plot = FALSE, main="") 
  } else {
    raster::image(obj, col = colour_scale, frame.plot = FALSE, main="")
  }
  dev.off()

  ## There is a bug in Google Earth that does not allow transparency of PNGs:
  # http://groups.google.com/group/earth-free/browse_thread/thread/1cd6bc29a2b6eb76/62724be63547fab7
  # Solution: add transparency using ImageMagick:
  convert <- get("convert", envir = plotKML.opts)
  if(nchar(convert)==0){
    plotKML.env(silent = FALSE, show.env = FALSE)
    convert <- get("convert", envir = plotKML.opts)
  } else {
    # if it does manages to find ImageMagick:
    if(!nchar(convert)==0){
      system(paste(convert, ' ', raster_name, ' -matte -transparent "#FFFFFF" ', raster_name, sep=""))
    } else {
    warning("PNG transparency possibly ineffective. Install ImageMagick and add to PATH. See ?kml_layer.Raster for more info.")
    }
  }

  # plot the legend (PNG)
  if(plot.legend == TRUE){
    if(missing(raster_name)){
      legend_name <- paste(as.character(call[["colour"]]), "legend.png", sep="_")
    } else {
      legend_name <- paste(strsplit(raster_name, "\\.")[[1]][1], "legend.png", sep="_")      
    }
    if(is.factor(obj)){
      x <- as.factor(getValues(obj))
      if(length(labels(obj))==0){
        levels(x) <- levels(as.factor(getValues(obj)))
      } else {
        levels(x) = labels(obj)[[1]]
      }      
      kml_legend.bar(x = x, legend.file = legend_name, legend.pal = colour_scale)
    } else {
      if(!is.na(charmatch("z.lim", names(call)))){
        kml_legend.bar(x = getValues(obj), legend.file = legend_name, legend.pal = colour_scale, z.lim = eval(call[["z.lim"]]))
       } else {
        kml_legend.bar(x = getValues(obj), legend.file = legend_name, legend.pal = colour_scale)
       } 
    }
  }

  message("Writing to KML...")
  # Folder name
  pl1 = newXMLNode("Folder", parent=kml.out[["Document"]])
  pl2 <- newXMLNode("name", paste(class(obj)), parent = pl1)

  # Insert metadata:
  if(!is.null(metadata)){
    md.txt <- kml_metadata(metadata, asText = TRUE)
    txt <- sprintf('<description><![CDATA[%s]]></description>', md.txt)
    parseXMLAndAdd(txt, parent=pl1)
  }

  # Ground overlay
  # =====================
  pl2b <- newXMLNode("GroundOverlay", parent = pl1)
  # Creating a SpatialPixelsDataFrame object to be plotted
  pl3 <- newXMLNode("name", deparse(call[["colour"]]), parent = pl2b)
  pl3b <- newXMLNode("altitude", signif(altitude, 4), parent = pl2b)
  pl3b <- newXMLNode("altitudeMode", altitudeMode, parent = pl2b)
  pl3c <- newXMLNode("Icon", parent = pl2b)
  pl4 <- newXMLNode("href", raster_name, parent = pl3c)
  pl3d <- newXMLNode("LatLonBox", parent = pl2b)
  pl4b <- newXMLNode("north", bbox(extent(obj))[2, 2], parent = pl3d)
  pl4c <- newXMLNode("south", bbox(extent(obj))[2, 1], parent = pl3d)
  pl4d <- newXMLNode("east", bbox(extent(obj))[1, 2], parent = pl3d)
  pl4e <- newXMLNode("west", bbox(extent(obj))[1, 1], parent = pl3d)
  
  # Legend
  # ======================
  if(plot.legend == TRUE){
    txtso <- sprintf('<ScreenOverlay><name>Legend</name><Icon><href>%s</href></Icon><overlayXY x="0" y="1" xunits="fraction" yunits="fraction"/><screenXY x="0" y="1" xunits="fraction" yunits="fraction"/></ScreenOverlay>', legend_name)
    parseXMLAndAdd(txtso, parent=kml.out[["Document"]])
  }
  
  # save results: 
  assign("kml.out", kml.out, envir=plotKML.fileIO)
  
}

setMethod("kml_layer", "RasterLayer", kml_layer.Raster)
setMethod("kml_layer", "RasterStack", kml_layer.Raster)

# end of script;
back to top