Open rsbivand opened 1 year ago
Hi Roger, Great idea. Will have some further look at get back to you.
I am not sure how useful but I have a repo for mapping Luxembourg population per municipality or using the EU ref grid (1km). I believe it can bring 2 discussion points:
Besides you’ll see I am basically using ggplot for mapping with a function I have named ggplot.themap() (see R folder and its application to the Lux case in ggplot.themap.lux). My idea was originally to use the dots to pass parameters to classInt but I retracted because of some bugs I could not understand with the “fixed” method.
Some output with varying discretisation methods here: https://github.com/geocaruso/cartolux/blob/main/output/Lux_map_grid_pop.pdf
In general it may also just show how a rather basic user (not a proper developer) makes use of classint in cartographic context with public data only.
Best Geoffrey
On 17 Aug 2023, at 13:46, Roger Bivand @.***> wrote:
@rCarto https://github.com/rCarto @HuguesPecout https://github.com/HuguesPecout @geocaruso https://github.com/geocaruso @dieghernan https://github.com/dieghernan I've put a very preliminary presentation draft up on https://rsbivand.github.io/eqc23_talk/, repo https://github.com/rsbivand/eqc23_talk (here). I'd be very grateful for comments.
The presentation is only a first approach, but I'd like to develop this towards a vignette, and would also like to bring in package maintainers of packages using class intervals to see how to provide support to users.
— Reply to this email directly, view it on GitHub https://github.com/rsbivand/eqc23_talk/issues/1, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASTDGZRUSPUKQOX6QXLUOETXVYAAHANCNFSM6AAAAAA3T6FBXY. You are receiving this because you were mentioned.
@geocaruso Thanks, yes, plenty of useful points to consider. I'll update the draft closer to the meeting.
As I needed to do something similar today, I attempted to use classInt for a spatraster with ggplot2 (following the line of what I do with sf's). Well I believe it is quite a painful experience. tidyterra helps a lot with continuous values mapping but as far as I could see does not work with discrete values
Here my code using 1km pop for Luxembourg.
`
Grid1km_LAU2_Pop2021EU<-read.csv("https://github.com/geocaruso/cartolux/raw/main/data/Grid1km_LAU2_Pop2021EU.csv")
GrdPop<-Grid1km_LAU2_Pop2021EU[,c(2,6)] #extract cell code and pop only
x_centre<-as.numeric(substr(GrdPop$CELLCODE,5,8))1000+500
y_centre<-as.numeric(substr(GrdPop$CELLCODE,10,13))1000+500
xyz0<-cbind(x_centre,y_centre,GrdPop$Pop2021EU) xyz<-xyz0[xyz0[,3]>0,] r<-terra::rast(xyz, type="xyz") terra::crs(r) <- "epsg:3035"
library(ggplot2) p<-ggplot()+ tidyterra::geom_spatraster(data = r, aes(fill = X))+ scale_fill_viridis_c(option="magma",na.value = "white",direction = -1) p
cl.intvl<-classInt::classIntervals(xyz[,3], n=5, style="fisher") n.classes<-length(cl.intvl$brks)-1 cl.value<-factor(classInt::findCols(cl.intvl))
xyz2_df<-as.data.frame(cbind(xyz[,1:2],cl.value))
p2<-ggplot()+ geom_raster(data = xyz2_df, aes(x=x_centre, y=y_centre,fill = factor(cl.value)))+ tidyterra::geom_spatraster(data = r, aes(fill = factor(cl.value)))+ scale_fill_brewer(palette="Spectral", direction=-1) p2
Conclusion: Quite ugly ;-)
I think we'd need to look at terra::plot
as well as support in tmap3
and forthcoming tmap4
, maybe others. I see that mapsf
has some support too: https://riatelab.github.io/mapsf/reference/mf_raster.html. I stay away from ggplot2
unless forced; the analysis in How does ggplot do this? in https://rsbivand.github.io/BAN422_V20/ban422_v20_tues.html, which takes functions calling functions to extremities IMO.
I think you are right about tmap vs gglot. I can’t really remember why I quit tmap, especially since the leaflet mode is very practical.
As to the SpatRaster I think the logical way to go is to feed the rcl argument of terra::classify with a from to and new value matrix or with the breaks from the classInt object
Geoffrey
Le mar. 22 août 2023 à 21:19, Roger Bivand @.***> a écrit :
I think we'd need to look at terra::plot as well as support in tmap3 and forthcoming tmap4, maybe others. I see that mapsf has some support too: https://riatelab.github.io/mapsf/reference/mf_raster.html. I stay away from ggplot2 unless forced; the analysis in How does ggplot do this? in https://rsbivand.github.io/BAN422_V20/ban422_v20_tues.html, which takes functions calling functions to extremities IMO.
— Reply to this email directly, view it on GitHub https://github.com/rsbivand/eqc23_talk/issues/1#issuecomment-1688788879, or unsubscribe https://github.com/notifications/unsubscribe-auth/ASTDGZV5RAFE4Q52KKMOMC3XWUA5TANCNFSM6AAAAAA3T6FBXY . You are receiving this because you were mentioned.Message ID: @.***>
Very late on this, as contributor to classInt didn't have any comment but as developer of tidyterra...
As I needed to do something similar today, I attempted to use classInt for a spatraster with ggplot2 (following the line of what I do with sf's). Well I believe it is quite a painful experience. tidyterra helps a lot with continuous values mapping but as far as I could see does not work with discrete values @geocaruso
I think plotting SpatRasters
with discrete values is also very straighforward, see:
Grid1km_LAU2_Pop2021EU <- read.csv("https://github.com/geocaruso/cartolux/raw/main/data/Grid1km_LAU2_Pop2021EU.csv")
GrdPop <- Grid1km_LAU2_Pop2021EU[, c(2, 6)] # extract cell code and pop only
# extract lower left corner, then factor 1000 for meters, and add 500m to get to centre
x_centre <- as.numeric(substr(GrdPop$CELLCODE, 5, 8)) * 1000 + 500
y_centre <- as.numeric(substr(GrdPop$CELLCODE, 10, 13)) * 1000 + 500
xyz0 <- cbind(x_centre, y_centre, GrdPop$Pop2021EU)
xyz <- xyz0[xyz0[, 3] > 0, ]
r <- terra::rast(xyz, type = "xyz")
terra::crs(r) <- "epsg:3035"
# 3. Map pop SpatRaster with ggplot and continuous scale
# EASY with tidyterra!
library(ggplot2)
p <- ggplot() +
tidyterra::geom_spatraster(data = r) +
scale_fill_viridis_c(option = "magma", na.value = "white", direction = -1)
p
For making a SpatRaster
discrete, use terra::classify()
with the breaks of classInt::classIntervals()
so you can create a categorical layer.
# 4. Map SpatRaster with ggplot and discretized scale
cl.intvl <- classInt::classIntervals(xyz[, 3], n = 5, style = "fisher")
# Classify the SpatRaster into factors
r$classified <- terra::classify(r, cl.intvl$brks,
include.lowest = TRUE,
brackets = TRUE
)
When we are done, just include in aes(fill=)
the name of the new layer with the factors.
# Plot SpatRaster with ggplot2
p2 <- ggplot() +
tidyterra::geom_spatraster(data = r, aes(fill = classified)) + # Note the layer we use
scale_fill_brewer(palette = "Spectral", direction = -1, na.translate = FALSE) + # na.translate remove NA for legend
labs(fill = "Using factors")
p2
If we prefer numeric factors...
# New layer
integs <- terra::values(r$classified)
r$num_as_fact <- as.factor(as.integer(integs) + 1)
p3 <- ggplot() +
tidyterra::geom_spatraster(data = r, aes(fill = num_as_fact)) +
scale_fill_brewer(palette = "Spectral", direction = -1, na.translate = FALSE) +
labs(fill = "A number as factor")
p3
Created on 2024-02-26 with reprex v2.1.0
@dieghernan thanks!
Thanks @dieghernan. I see now I overlooked terra::classify which can make us the classint breaks quickly indeed. And through a factoring of terra::values for integers rather than breaks. Nice! Much better than getting back to xyz.
@rCarto @HuguesPecout @geocaruso @dieghernan
I've put a very preliminary presentation draft up on https://rsbivand.github.io/eqc23_talk/, repo https://github.com/rsbivand/eqc23_talk (here). I'd be very grateful for comments.
The presentation is only a first approach, but I'd like to develop this towards a vignette, and would also like to bring in package maintainers of packages using class intervals to see how to provide support to users.