R - ggplot2 - использовать растр в качестве базовой карты в оттенках серого

У меня есть два растровых слоя, один представляет собой изображение в оттенках серого, а другой результат анализа. Я хотел бы использовать один растр в качестве фона в оттенках серого в ggplot и наложить второй растр с помощью цветовой шкалы. Вот пример того, как это могло бы выглядеть (но лучше):

введите здесь описание изображения

Я не могу назначать растрам отдельные цвета. Вы знаете способ добиться этого? Я долго гуглил, и у меня сложилось впечатление, что это невозможно. Но я не могу (и не хочу) в это поверить.

Пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

Пример

создать тестовые данные

xy.1 <- expand.grid(1:10, 1:10)
df.1 <- data.frame(Longitude=xy.1[,1], Latitude=xy.1[,2], Value=xy.1[,1]+xy.1[,2])
xy.2 <- expand.grid(3:5, 3:5)
df.2 <- data.frame(Longitude=xy.2[,1], Latitude=xy.2[,2], Value=rnorm(9))

участок

p1 <- ggplot() +
  geom_raster(data=df.1, aes(x=Longitude, y=Latitude, fill=Value)) +
  scale_fill_gradientn(colours = grey(seq(0,1,l=20))) +
  coord_equal() +
  theme(legend.position="none")

p1 + geom_raster(data=df.2, aes(x=Longitude, y=Latitude, fill=Value)) +
  scale_fill_gradient(low="red", high="white")

Результат:

введите здесь описание изображения


person bask0    schedule 30.03.2016    source источник
comment
Я смутно припоминаю, что пытался сделать это и погуглил, что этого удалось избежать по замыслу: github.com /hadley/ggplot2/issues/578   -  person fanli    schedule 30.03.2016
comment
Спасибо, фанли, за ответ. Я проверил ссылку, и в последнем комментарии говорится, что ggvis поддерживает это. Я попытаюсь сделать это с помощью ggvis и опубликую ответ, если он сработает.   -  person bask0    schedule 31.03.2016
comment
Я думаю, что это неправильный подход после того, как взглянул на ggvis.   -  person bask0    schedule 31.03.2016
comment
из любопытства, как ggvis справляется с этим? мне бы тоже пригодилось   -  person fanli    schedule 31.03.2016
comment
Я не очень хорошо знаю ggvis. Это для создания интерактивных сюжетов. Я просто подумал, что это не похоже на правильный инструмент для этого.   -  person bask0    schedule 01.04.2016


Ответы (1)


Я написал функцию, которая позволяет добавить фон и наложить растр переднего плана с помощью ggplot2.

Редактировать: я добавил лучшее решение в конце

Вот как это работает:

Я объединяю два растра и сдвигаю значения так, чтобы значения не перекрывались. Затем я рисую растровый слой с линейкой масштаба, содержащей цветовую шкалу для переднего плана (например, красный -> зеленый), где цветовая шкала фона жестко запрограммирована (черный -> белый). Количество цветов не ограничено.

Легенда растрового слоя не отображается. Чтобы получить легенду, которая не содержит всю масштабную линейку (которая была бы черной -> белой -> красной -> зеленой), я вставляю две фиктивные точки на заднем плане. Один с наименьшим значением данных переднего плана, другой с наибольшим. Это дает легенду только для данных переднего плана.

Если кто-то знает более удобный способ масштабирования и создания цветовой шкалы, я был бы рад добавить это в функцию.

Я добавил возможность масштабировать данные переднего плана с помощью квантилей; Аргумент fg.quant принимает вектор из двух целых чисел, используемый для «вырезания» данных. bw.scale позволяет сделать фоновый растр темнее/светлее: bw.scale=c(0, 0.5) означает, что фоновое изображение имеет цветовую шкалу от черного до серого (0,5), например.

Я знаю, что это не идеальная функция. Но это очень полезно для меня, и я улучшу его и постараюсь устранить некрасивые части, как только у меня будет свободное время.

тестовые данные

# 'background'
r.1 <- raster(x=matrix(rowSums(expand.grid(1:10, 1:10)), nrow=10),
              xmn=0, xmx=10, ymn=0, ymx=10)
# 'foreground'
r.2 <- raster(x=matrix(rnorm(16), nrow=4),
              xmn=3, xmx=7, ymn=3, ymx=7)

графическая функция

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# BGPlot() -- plot data with background raster using ggplot2
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

BGPlot <- function(fg,
                   bg,
                   cols=c('red', 'green'),
                   fg.quant=c(0, 1),
                   bw.scale=c(0, 1),
                   plot.title='',
                   leg.name='Value') {
  # plot data with background raster using ggplot2
  #
  # Args
  #   fg: foreground raster layer
  #   bg: background raster layer
  #   cols: colors to use for fg
  #   fg.quant: scaling fg by quantiles
  #   bw.scale: makes bg darker / brighter:
  #     E.g. c(0, 0.9) -> darker / c(0.2, 1) -> brighter
  #   plot.title: title
  #   leg.name: name to be displyed at legend
  #
  # Returns
  #   ggplot object

  # load libraries
  library(ggplot2)

  # get min / max of foreground raster
  fg.q <- quantile(fg, fg.quant)
  fg.min <- fg.q[1]
  fg.max <- fg.q[2]

  # rescale fg
  fg.sc <- (fg-fg.q[1]) / (fg.q[2]-fg.q[1])
  fg.sc[fg.sc<0] <- 0
  fg.sc[fg.sc>1] <- 1
  fg.sc <- fg.sc + 0.1

  # get scale (fg values 0.1, 10, 1000 range?)
  ifelse((fg.max-fg.min)/10>=1, n.dgts <- 0, n.dgts <- 1)

  # create fg legend breaks / labels
  fg.breaks <- round(seq(fg.min, fg.max, l=5), n.dgts)
  fg.breaks[1] <- ceiling(fg.min*(10^n.dgts))/(10^n.dgts)
  fg.breaks[5] <- floor(fg.max*(10^n.dgts))/(10^n.dgts)

  fg.labs <- paste0(c(paste0(round(fg.min, n.dgts+1), '-'),'','','',''),
                    fg.breaks,
                    c('','','','',paste0('-', round(fg.max, n.dgts+1)))
  )


  # rescale bg
  bg.sc <- (bg-minValue(bg)) /
    (maxValue(bg)-minValue(bg)) *
    (bw.scale[2]-bw.scale[1]) + bw.scale[1] -1.1


  # merge rasters, fg over bg
  r <- merge(fg.sc, bg.sc)

  # convert raster to data.frame
  r.df <- as.data.frame(rasterToPoints(r))
  names(r.df) <- c('Longitude', 'Latitude', 'Value')

  # get center of r
  mid.Lon <- mean(r.df$Longitude)
  mid.Lat <- mean(r.df$Latitude)

  # set scale positions
  vals <-c(-1.1,-0.1, seq(0.1,1.1,l=length(cols)))

  # set dummy-point values
  dp <-seq(fg.min,fg.max,l=length(cols))


  # plotting; 
  p <-
    ggplot() +
    # dummy points: points not visible, needed to display custom scale-bar
    geom_point(data=data.frame(x = rep(mid.Lon, length(cols)),
                               y = rep(mid.Lat, length(cols)),
                               c = dp),
               aes(x, y, color=c)) +
    scale_color_gradientn(colours = cols,
                          breaks=fg.breaks,
                          labels=fg.labs,
                          name=leg.name) +
    # raster; no scale-bar plotted
    geom_raster(data=r.df, aes(x=Longitude, y=Latitude, fill=Value)) +
    scale_fill_gradientn(colours  = c('black', 'white', cols),
                         values   = vals,
                         rescaler = function(x,...) x,
                         oob      = identity,
                         guide    = "none") +
    ggtitle(label=plot.title) +
    theme_light() +
    labs(list(x='Lon', y='Lat')) +
    theme(axis.text.y=element_text(angle=90, hjust=0.5)) +
    coord_equal()

  return(p)
}

вызов функции

BGPlot(fg=r.2, bg=r.1, cols=c('red', 'green'), fg.quant=c(0.01, 0.99), bw.scale=c(0, 0.8), plot.title='Chlorophyll Concentration', leg.name='CHL')

введите здесь описание изображения

Пример "реального мира":

введите здесь описание изображения

EDIT: Лучшее решение с использованием пакета R "RStoolbox"

Это очень простое и идеальное рабочее решение с использованием пакета RStoolbox. Функция ggR создает фоновое изображение в оттенках серого, функция ggRGB — фон RGB.

library(ggplot2)
library(RStoolbox)
ggR(BACKGROUND_IMAGE, geom_raster=FALSE) +
geom_raster(...) # here comes standard raster plot
person bask0    schedule 01.04.2016