@@ -206,7 +206,9 @@ epsg3857 <- "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y
206
206
# ' @param x a \code{\link[terra]{SpatRaster}} or a \code{RasterLayer} object--see \code{\link[raster]{raster}}
207
207
# ' @param colors the color palette (see \code{\link{colorNumeric}}) or function
208
208
# ' to use to color the raster values (hint: if providing a function, set
209
- # ' \code{na.color} to \code{"#00000000"} to make \code{NA} areas transparent)
209
+ # ' \code{na.color} to \code{"#00000000"} to make \code{NA} areas transparent).
210
+ # ' The palette is ignored if \code{x} is a SpatRaster with a color table or if
211
+ # ' it has RGB channels.
210
212
# ' @param opacity the base opacity of the raster, expressed from 0 to 1
211
213
# ' @param attribution the HTML string to show as the attribution for this layer
212
214
# ' @param layerId the layer id
@@ -225,15 +227,20 @@ epsg3857 <- "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y
225
227
# ' (before base64 encoding); defaults to 4MB.
226
228
# ' @template data-getMapData
227
229
# '
230
+ # ' @seealso \code{\link{addRasterLegend}} for an easy way to add a legend for a
231
+ # ' SpatRaster with a color table.
232
+ # '
228
233
# ' @examples
229
234
# ' \donttest{library(raster)
230
235
# '
231
236
# ' r <- raster(xmn = -2.8, xmx = -2.79, ymn = 54.04, ymx = 54.05, nrows = 30, ncols = 30)
232
237
# ' values(r) <- matrix(1:900, nrow(r), ncol(r), byrow = TRUE)
233
238
# ' crs(r) <- CRS("+init=epsg:4326")
234
239
# '
240
+ # ' pal <- colorNumeric("Spectral", domain = c(0, 1000))
235
241
# ' leaflet() %>% addTiles() %>%
236
- # ' addRasterImage(r, colors = "Spectral", opacity = 0.8)
242
+ # ' addRasterImage(r, colors = pal, opacity = 0.8) %>%
243
+ # ' addLegend(pal = pal, values = c(0, 1000))
237
244
# ' }
238
245
# ' @export
239
246
addRasterImage <- function (
@@ -283,6 +290,93 @@ addRasterImage <- function(
283
290
}
284
291
285
292
293
+ # ' Add a color legend for a SpatRaster to a map
294
+ # '
295
+ # ' A function for adding a [legend][addLegend()] that is specifically designed
296
+ # ' for [terra::SpatRaster] objects, with categorical values, that carry their
297
+ # ' own [color table][terra::coltab()].
298
+ # '
299
+ # ' @param map a map widget object
300
+ # ' @param x a [SpatRaster][terra::SpatRaster] object with a color table
301
+ # ' @param layer the layer of the raster to target
302
+ # ' @param ... additional arguments to pass through to [addLegend()]
303
+ # ' @seealso [addRasterImage()]
304
+ # ' @examples
305
+ # '
306
+ # ' library(terra)
307
+ # '
308
+ # ' r <- rast("/vsicurl/https://geodata.ucdavis.edu/test/pr_nlcd.tif")
309
+ # ' leaflet() %>%
310
+ # ' addTiles() %>%
311
+ # ' addRasterImage(r, opacity = 0.75) %>%
312
+ # ' addRasterLegend(r, opacity = 0.75)
313
+ # '
314
+ # ' plot.new() # pause in interactive mode
315
+ # '
316
+ # ' rr <- r
317
+ # ' levels(rr) <- NULL
318
+ # ' leaflet() %>%
319
+ # ' addTiles() %>%
320
+ # ' addRasterImage(rr, opacity = 0.75) %>%
321
+ # ' addRasterLegend(rr, opacity = 0.75)
322
+ # '
323
+ # ' @md
324
+ # ' @export
325
+ addRasterLegend <- function (map , x , layer = 1 , ... ) {
326
+ stopifnot(inherits(x , " SpatRaster" ))
327
+ stopifnot(length(layer ) == 1 && layer > 0 && layer < = terra :: nlyr(x ))
328
+
329
+ # # might as well do this here and only once. Subsetting would otherwise have been necessary in
330
+ # # color_info <- base::subset(color_info, value %in% terra::values(x))
331
+ x <- x [[layer ]]
332
+
333
+ # Retrieve the color table from the layer. If one doesn't exist, that means
334
+ # the raster was colored some other way, like using colorFactor or something,
335
+ # and the regular addLegend() is designed for those cases.
336
+ ct <- terra :: coltab(x )[[1 ]]
337
+ if (is.null(ct )) {
338
+ stop(" addRasterLegend() can only be used on layers with color tables (see ?terra::coltab). Otherwise, use addLegend()." )
339
+ }
340
+
341
+ # Create a data frame that has value and color columns
342
+ # Extract the colors in #RRGGBBAA format
343
+ color_info <- data.frame (
344
+ value = ct [[1 ]],
345
+ color = grDevices :: rgb(ct $ red / 255 , ct $ green / 255 , ct $ blue / 255 , ct $ alpha / 255 )
346
+ )
347
+
348
+ lvls <- terra :: levels(x )[[1 ]]
349
+
350
+ # Drop values that aren't part of the layer
351
+ # # unlike "values", "unique" is memory-safe; it does not load all values
352
+ # # into memory if the raster is large. So instead of:
353
+
354
+ # color_info <- base::subset(color_info, value %in% terra::values(x))
355
+
356
+ # # remove the levels to get the raw cell values
357
+ levels(x ) <- NULL
358
+ color_info <- base :: subset(color_info , value %in% terra :: unique(x )[[1 ]])
359
+
360
+ res <- if (is.data.frame(lvls )) {
361
+ # Use the labels from levels(x), and look up the matching colors in the
362
+ # color table
363
+
364
+ # The levels data frame can have varying colnames, just normalize them
365
+ colnames(lvls ) <- c(" value" , " label" )
366
+ base :: merge(color_info , lvls , by.x = " value" , by.y = 1 )
367
+ } else {
368
+ # No level labels provided; use the values as labels
369
+ cbind(color_info , label = color_info $ value )
370
+ }
371
+
372
+ # At this point, res is a data frame with `value`, `color`, and `label` cols,
373
+ # and values/colors not present in the raster layer have been dropped
374
+
375
+ addLegend(map , colors = res [[" color" ]], labels = res [[" label" ]], ... )
376
+ }
377
+
378
+
379
+
286
380
addRasterImage_RasterLayer <- function (
287
381
map ,
288
382
x ,
@@ -370,50 +464,70 @@ addRasterImage_SpatRaster <- function(
370
464
data = getMapData(map )
371
465
) {
372
466
373
- if (terra :: nlyr(x ) > 1 ) {
467
+ # terra 1.5-50 has terra::has.RGB()
468
+ if (has.RGB(x )) {
469
+ # RGB(A) channels to color table
470
+ x <- terra :: colorize(x , " col" )
471
+ } else if (terra :: nlyr(x ) > 1 ) {
374
472
x <- x [[1 ]]
375
473
warning(" using the first layer in 'x'" , call. = FALSE )
376
474
}
377
475
378
476
raster_is_factor <- terra :: is.factor(x )
477
+
478
+ # there 1.5-50 has terra::has.colors(x)
479
+ ctab <- terra :: coltab(x )[[1 ]]
480
+ has_colors <- ! is.null(ctab )
481
+
379
482
method <- match.arg(method )
380
483
if (method == " ngb" ) method = " near"
381
484
if (method == " auto" ) {
382
- if (raster_is_factor ) {
485
+ if (raster_is_factor || has_colors ) {
383
486
method <- " near"
384
487
} else {
385
488
method <- " bilinear"
386
489
}
387
490
}
388
491
389
- if (project ) {
390
- # if we should project the data
391
- projected <- projectRasterForLeaflet(x , method )
392
- } else {
393
- # do not project data
394
- projected <- x
395
- }
396
-
397
492
bounds <- terra :: ext(
398
493
terra :: project(
399
494
terra :: project(
400
495
terra :: as.points(terra :: ext(x ), crs = terra :: crs(x )),
401
496
epsg3857 ),
402
497
epsg4326 )
403
498
)
499
+ # # can't the above be simplified to this?
500
+ # bounds <- terra::ext(
501
+ # terra::project(
502
+ # terra::as.points(terra::ext(x), crs=terra::crs(x)),
503
+ # epsg4326)
504
+ # )
505
+
506
+ if (project ) {
507
+ # if we should project the data
508
+ x <- projectRasterForLeaflet(x , method )
509
+ if (method == " bilinear" ) {
510
+ has_colors <- FALSE
511
+ }
512
+ }
404
513
405
514
if (! is.function(colors )) {
406
- if (method == " near" ) {
515
+ if (method == " near" || has_colors ) {
407
516
# 'factors'
408
- colors <- colorFactor(colors , domain = NULL , na.color = " #00000000" , alpha = TRUE )
517
+ domain <- NULL
518
+ if (has_colors ) {
519
+ colors <- rgb(ctab [,2 ], ctab [,3 ], ctab [,4 ], ctab [,5 ], maxColorValue = 255 )
520
+ domain <- ctab [,1 ]
521
+ }
522
+ colors <- colorFactor(colors , domain = domain , na.color = " #00000000" , alpha = TRUE )
409
523
} else {
410
524
# 'numeric'
411
525
colors <- colorNumeric(colors , domain = NULL , na.color = " #00000000" , alpha = TRUE )
412
526
}
413
527
}
414
528
415
- tileData <- terra :: values(projected ) %> % as.vector() %> % colors() %> % col2rgb(alpha = TRUE ) %> % as.raw()
416
- dim(tileData ) <- c(4 , ncol(projected ), nrow(projected ))
529
+ tileData <- terra :: values(x ) %> % as.vector() %> % colors() %> % col2rgb(alpha = TRUE ) %> % as.raw()
530
+ dim(tileData ) <- c(4 , ncol(x ), nrow(x ))
417
531
pngData <- png :: writePNG(tileData )
418
532
if (length(pngData ) > maxBytes ) {
419
533
stop(
0 commit comments