Stars: Does stars have an eqivalent way to create RGB rasters from arrays like raster::brick?

Created on 1 Jul 2020  路  18Comments  路  Source: r-spatial/stars

Here's my reprex:

img <- structure(c(143, 143, 128, 49, 8, 5, 25, 89, 143, 143, 143, 143, 
143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 137, 143, 
143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, 133, 133, 
133, 133, 133, 136, 137, 143, 143, 143, 143, 143, 143, 143, 143, 
143, 143, 143, 154, 172, 172, 172, 154, 137, 133, 133, 136, 143, 
143, 143, 143, 143, 143, 143, 143, 143, 145, 145, 145, 145, 145, 
145, 125, 100, 79, 100, 137, 143, 143, 143, 143, 143, 143, 143, 
82, 82, 82, 82, 82, 82, 82, 82, 74, 88, 133, 137, 143, 143, 143, 
143, 143, 143, 232, 232, 232, 232, 232, 232, 232, 232, 232, 230, 
163, 133, 137, 143, 143, 143, 143, 143, 232, 232, 230, 230, 230, 
230, 230, 230, 230, 230, 230, 158, 133, 137, 143, 143, 143, 143, 
74, 158, 220, 230, 230, 230, 230, 230, 230, 230, 230, 230, 145, 
133, 143, 143, 143, 143, 133, 49, 33, 100, 172, 189, 203, 203, 
220, 230, 230, 230, 211, 133, 136, 143, 143, 143, 163, 128, 89, 
53, 18, 65, 137, 189, 230, 247, 238, 230, 230, 158, 133, 143, 
143, 143, 128, 125, 124, 158, 178, 107, 33, 33, 121, 220, 238, 
230, 230, 203, 133, 137, 143, 143, 125, 124, 133, 195, 195, 195, 
158, 44, 21, 143, 247, 232, 230, 230, 136, 136, 143, 143, 133, 
145, 136, 189, 125, 53, 29, 100, 203, 247, 247, 232, 230, 230, 
152, 133, 143, 143, 178, 163, 59, 25, 59, 133, 189, 195, 232, 
247, 247, 232, 230, 230, 163, 133, 143, 143, 65, 33, 74, 158, 
195, 195, 195, 195, 238, 247, 247, 230, 230, 230, 172, 133, 143, 
143, 152, 220, 230, 203, 172, 178, 195, 195, 247, 238, 230, 230, 
230, 232, 172, 133, 143, 143, 232, 230, 220, 172, 158, 178, 195, 
211, 247, 238, 230, 230, 230, 232, 154, 133, 143, 143, 189, 203, 
121, 107, 107, 107, 114, 178, 203, 211, 230, 230, 232, 232, 137, 
136, 143, 143, 8, 14, 2, 2, 2, 2, 5, 21, 21, 107, 232, 232, 232, 
203, 133, 137, 143, 143, 121, 133, 121, 121, 121, 124, 195, 220, 
220, 220, 232, 232, 232, 158, 133, 143, 143, 143, 124, 124, 133, 
133, 137, 203, 232, 232, 232, 232, 232, 232, 211, 136, 136, 143, 
143, 143, 145, 143, 154, 189, 230, 232, 232, 232, 232, 232, 232, 
230, 145, 133, 143, 143, 143, 143, 230, 230, 232, 232, 232, 232, 
232, 232, 232, 232, 232, 163, 133, 137, 143, 143, 143, 143, 230, 
230, 230, 230, 230, 230, 232, 232, 232, 230, 163, 133, 136, 143, 
143, 143, 143, 143, 230, 230, 230, 230, 230, 230, 230, 232, 211, 
145, 133, 136, 143, 143, 143, 143, 143, 143, 142, 142, 127, 49, 
8, 5, 25, 89, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 
142, 142, 142, 142, 142, 137, 142, 142, 142, 142, 142, 142, 142, 
142, 142, 142, 142, 142, 132, 132, 132, 132, 132, 136, 137, 142, 
142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 154, 171, 171, 
171, 154, 137, 132, 132, 136, 142, 142, 142, 142, 142, 142, 142, 
142, 142, 145, 145, 145, 145, 145, 145, 125, 100, 79, 100, 137, 
142, 142, 142, 142, 142, 142, 142, 82, 82, 82, 82, 82, 82, 82, 
82, 74, 88, 132, 137, 142, 142, 142, 142, 142, 142, 232, 232, 
232, 232, 232, 232, 232, 232, 232, 230, 163, 132, 137, 142, 142, 
142, 142, 142, 232, 232, 230, 230, 230, 230, 230, 230, 230, 230, 
230, 158, 132, 137, 142, 142, 142, 142, 74, 158, 220, 230, 230, 
230, 230, 230, 230, 230, 230, 230, 145, 132, 142, 142, 142, 142, 
132, 49, 33, 100, 171, 189, 203, 203, 220, 230, 230, 230, 211, 
132, 136, 142, 142, 142, 163, 127, 89, 53, 18, 65, 137, 189, 
230, 247, 238, 230, 230, 158, 132, 142, 142, 142, 127, 125, 123, 
158, 178, 107, 33, 33, 121, 220, 238, 230, 230, 203, 132, 137, 
142, 142, 125, 123, 132, 195, 195, 195, 158, 44, 21, 142, 247, 
232, 230, 230, 136, 136, 142, 142, 132, 145, 136, 189, 125, 53, 
29, 100, 203, 247, 247, 232, 230, 230, 151, 132, 142, 142, 178, 
163, 59, 25, 59, 132, 189, 195, 232, 247, 247, 232, 230, 230, 
163, 132, 142, 142, 65, 33, 74, 158, 195, 195, 195, 195, 238, 
247, 247, 230, 230, 230, 171, 132, 142, 142, 151, 220, 230, 203, 
171, 178, 195, 195, 247, 238, 230, 230, 230, 232, 171, 132, 142, 
142, 232, 230, 220, 171, 158, 178, 195, 211, 247, 238, 230, 230, 
230, 232, 154, 132, 142, 142, 189, 203, 121, 107, 107, 107, 114, 
178, 203, 211, 230, 230, 232, 232, 137, 136, 142, 142, 8, 14, 
2, 2, 2, 2, 5, 21, 21, 107, 232, 232, 232, 203, 132, 137, 142, 
142, 121, 132, 121, 121, 121, 123, 195, 220, 220, 220, 232, 232, 
232, 158, 132, 142, 142, 142, 123, 123, 132, 132, 137, 203, 232, 
232, 232, 232, 232, 232, 211, 136, 136, 142, 142, 142, 145, 142, 
154, 189, 230, 232, 232, 232, 232, 232, 232, 230, 145, 132, 142, 
142, 142, 142, 230, 230, 232, 232, 232, 232, 232, 232, 232, 232, 
232, 163, 132, 137, 142, 142, 142, 142, 230, 230, 230, 230, 230, 
230, 232, 232, 232, 230, 163, 132, 136, 142, 142, 142, 142, 142, 
230, 230, 230, 230, 230, 230, 230, 232, 211, 145, 132, 136, 142, 
142, 142, 142, 142, 142, 142, 142, 127, 49, 8, 5, 25, 89, 142, 
142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 
142, 137, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 142, 
142, 132, 132, 132, 132, 132, 135, 137, 142, 142, 142, 142, 142, 
142, 142, 142, 142, 142, 142, 153, 171, 171, 171, 153, 137, 132, 
132, 135, 142, 142, 142, 142, 142, 142, 142, 142, 142, 145, 145, 
145, 145, 145, 145, 125, 100, 79, 100, 137, 142, 142, 142, 142, 
142, 142, 142, 82, 82, 82, 82, 82, 82, 82, 82, 73, 87, 132, 137, 
142, 142, 142, 142, 142, 142, 232, 232, 232, 231, 231, 231, 231, 
231, 231, 230, 163, 132, 137, 142, 142, 142, 142, 142, 231, 231, 
230, 230, 230, 230, 230, 230, 230, 230, 230, 158, 132, 137, 142, 
142, 142, 142, 73, 158, 220, 230, 230, 230, 230, 230, 230, 230, 
230, 230, 145, 132, 142, 142, 142, 142, 132, 49, 33, 100, 171, 
189, 203, 203, 220, 230, 230, 230, 211, 132, 135, 142, 142, 142, 
163, 127, 89, 53, 18, 65, 137, 189, 230, 247, 238, 230, 230, 
158, 132, 142, 142, 142, 127, 125, 123, 158, 178, 107, 33, 33, 
121, 220, 238, 230, 230, 203, 132, 137, 142, 142, 125, 123, 132, 
195, 195, 195, 158, 44, 21, 142, 247, 231, 230, 230, 135, 135, 
142, 142, 132, 145, 135, 189, 125, 53, 29, 100, 203, 247, 247, 
232, 230, 230, 151, 132, 142, 142, 178, 163, 59, 25, 59, 132, 
189, 195, 232, 247, 247, 232, 230, 230, 163, 132, 142, 142, 65, 
33, 73, 158, 195, 195, 195, 195, 238, 247, 247, 230, 230, 230, 
171, 132, 142, 142, 151, 220, 230, 203, 171, 178, 195, 195, 247, 
238, 230, 230, 230, 231, 171, 132, 142, 142, 231, 230, 220, 171, 
158, 178, 195, 211, 247, 238, 230, 230, 230, 231, 153, 132, 142, 
142, 189, 203, 121, 107, 107, 107, 114, 178, 203, 211, 230, 230, 
231, 231, 137, 135, 142, 142, 8, 14, 2, 2, 2, 2, 5, 21, 21, 107, 
231, 231, 231, 203, 132, 137, 142, 142, 121, 132, 121, 121, 121, 
123, 195, 220, 220, 220, 231, 231, 231, 158, 132, 142, 142, 142, 
123, 123, 132, 132, 137, 203, 231, 231, 231, 231, 231, 232, 211, 
135, 135, 142, 142, 142, 145, 142, 153, 189, 230, 232, 232, 232, 
232, 232, 232, 230, 145, 132, 142, 142, 142, 142, 230, 230, 231, 
232, 232, 232, 232, 232, 232, 232, 231, 163, 132, 137, 142, 142, 
142, 142, 230, 230, 230, 230, 230, 230, 232, 232, 232, 230, 163, 
132, 135, 142, 142, 142, 142, 142, 230, 230, 230, 230, 230, 230, 
230, 232, 211, 145, 132, 135, 142, 142, 142, 142, 142, 142), .Dim = c(18L, 
                                                                      26L, 3L))

library(raster)
library(stars)
library(ggspatial)
library(mapview)
library(ggplot2)
library(palr)

## raster
r_tile <- raster::brick(img)
raster::crs(r_tile) <- "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"

ggplot() +
  layer_spatial(r_tile)
## correct

mapview::viewRGB(r_tile)
## yes

palr::image_raster(r_tile)
## works(?)

## stars

s_tile <- st_as_stars(img)
st_crs(s_tile) <- 3857

image(s_tile)
## fails

ggplot() +
  layer_spatial(s_tile)
## applying a colour scale I don't want

mapview::viewRGB(s_tile)
## fails

palr::image_stars(s_tile)
## fails

Most helpful comment

st_rgb()?

All 18 comments

edit: added ggplot2 and palr to reprex.

edit: accidently wrote image_raster instead of image_stars for the last one. Same result anyhow.

I don't think that raster::brick creates an RGB raster, just try plot(r_tile) or image(r_tile). It interprets your array as a set (stack? brick?) of raster layers, and plots these. That ggspatial::layer_spatial() makes a different assumption is outside the scope of raster, and in addition it interpolates your cells, which is usually a no-go when showing raster data (where colors often correspond to categories).

Since your data seem not to be spatially referenced, I'd suggest to try your luck with one of the R packages dedicated to image analysis or image handling. Otherwise, plot(s_tile, rgb = 1:3) seems to do what you want (not stated anywhere, but this started here: https://twitter.com/MilesMcBain/status/1278293526766211072 ). I'll make a note that stars::geom_stars still lacks an rgb argument.

The data are spatially referenced. I know the extent and CRS, I just didn't include that for the reprex. Full context is here:https://github.com/anthonynorth/snapbox/blob/master/R/static_map.R#L27

Ultimately what I want to do is ggplot over the top of this thing. So right now I can't use geom_stars with 3 channel arrays of RGB values?

Looks like my autodetection for "RGB" for stars images is not working...I think I based it off of what one gets when reading using read_stars() on a very specific image.

https://github.com/paleolimbot/ggspatial/blob/master/R/layer-spatial-stars.R#L23-L24

If you set the dimension names, it works but is incorrect...

s_tile <- st_as_stars(img) %>% 
  st_set_dimensions(names = c("x", "y", "band"))

It's not standalone, but you could rig your own Geom based on what I use to plot OSM tiles:

https://github.com/paleolimbot/ggspatial/blob/master/R/annotation-map-tile.R#L157-L199

But band is way too generic! I can imagine that we make this default if the (or a) dimension is called rgb and has cardinality 3.

Yes so if I write my image to PNG and read it back with read_stars, it's in a format that works nicely with ggspatial::layer_spatial.

stars object with 3 dimensions and 1 attribute
attribute(s):
    foo.png     
 Min.   :  2.0  
 1st Qu.:137.0  
 Median :143.0  
 Mean   :162.6  
 3rd Qu.:230.0  
 Max.   :247.0  
dimension(s):
     from to offset delta    refsys point values    
x       1 26      0     1 EPSG:3857    NA   NULL [x]
y       1 18     18    -1 EPSG:3857    NA   NULL [y]
band    1  3     NA    NA        NA    NA   NULL    

Is this an RGB raster?

No, it is a three-band raster dataset. The semantics that band 1, 2 and 3 refer to R, G and B values in 0-255 is lost.

How do you btw get read_stars to assign a crs to a PNG?

Oh sorry. I assigned the crs after reading from png file. :)

Instead of moving the rgb=... stuff to geom_stars, I propose to introduce a function that creates color rasters from multi-band rasters, so that we can handle there things like alpha, maxColorValue, and leave other dimensions untouched e.g.

library(stars)
s = st_as_stars(img)
r = st_apply(s, 1:2, function(x) rgb(x[1], x[2], x[3], maxColorValue=255))

has the colors in the proper places; supporting this by plot.stars and geom_stars is pretty straightforward.

st_rgb()?

This already works:

ggplot() + geom_stars(data=r)+ scale_fill_identity()

x

Here is an example how things look for a 4-dimensional object, using plot:

library(stars)
# Loading required package: abind
# Loading required package: sf
# Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 7.0.0
tif = system.file("tif/L7_ETMs.tif", package = "stars")
x = read_stars(tif)
x1 = x[,,,1:3]
x2 = x[,,,4:6]
st_dimensions(x1) = st_dimensions(x2)
(xx = c(x1, x2, along = "foo"))
# stars object with 4 dimensions and 1 attribute
# attribute(s):
#   L7_ETMs.tif    
#  Min.   :  1.00  
#  1st Qu.: 54.00  
#  Median : 69.00  
#  Mean   : 68.91  
#  3rd Qu.: 86.00  
#  Max.   :255.00  
# dimension(s):
#      from  to  offset delta                       refsys point values    
# x       1 349  288776  28.5 UTM Zone 25, Southern Hem... FALSE   NULL [x]
# y       1 352 9120761 -28.5 UTM Zone 25, Southern Hem... FALSE   NULL [y]
# band    4   6      NA    NA                           NA    NA   NULL    
# foo     1   2      NA    NA                           NA    NA   NULL    
r = st_rgb(xx, 3)
plot(r)

x

This already works:

ggplot() + geom_stars(data=r)+ scale_fill_identity()

Sort of. This appears transposed from what I expect and get when using layer_spatial on a raster brick:

image

It's an M. For either Mapbox or openstreetMap.

fortunes::fortune("illogical")

Indeed, it matters how your matrix (or array) should be interpreted in terms of a (spatially oriented) image. You didn't tell.

@MilesMcBain : swapping dimensions 1 and 2 and then flipping the second (y) gives you

r = st_as_stars(aperm(img, c(2,1,3))[,18:1,])
ggplot()+geom_stars(data=st_rgb(r))+scale_fill_identity()

x

Feel free to re-open for follow-up issues!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

michaeldorman picture michaeldorman  路  11Comments

dblodgett-usgs picture dblodgett-usgs  路  13Comments

agila5 picture agila5  路  8Comments

kendonB picture kendonB  路  11Comments

kendonB picture kendonB  路  6Comments