如何让ggplot2为自定义美学使用用户创建的刻度?扩展包开发疑问
geom_arrow() Dependency on scale_mag() Hey there! Let's break down why your geom_arrow() fails without scale_mag() and how to fix it.
The Root Cause
ggplot2 doesn't automatically handle custom aesthetic attributes like mag the way it does built-ins (e.g., x, colour). When you skip scale_mag(), ggplot has no idea how to translate your raw mag values into usable arrow lengths—so it can't render anything.
Solution 1: Register a Default Scale for mag
The cleanest fix is to tell ggplot to use scale_mag() as the default scale for the mag aesthetic. Add this to your package's .onLoad() function (it runs when your package is loaded):
.onLoad <- function(libname, pkgname) { # Register scale_mag as the default for the 'mag' aesthetic ggplot2::register_scale("mag", function() scale_mag()) }
This way, whenever a user maps to mag without explicitly adding scale_mag(), ggplot will automatically apply your scale behind the scenes.
Solution 2: Auto-Add scale_mag() in geom_arrow()
If you prefer not to rely on package load events, you can modify your GeomArrow ggproto class to check for an existing mag scale and add it if missing. Use the setup_layer() method to tweak the plot object before rendering:
GeomArrow <- ggplot2::ggproto( "GeomArrow", ggplot2::Geom, required_aes = c("x", "y", "mag", "angle"), default_aes = ggplot2::aes(colour = "black", size = 1, linetype = 1, alpha = 1), draw_panel = function(data, panel_params, coord) { # Your existing arrow-drawing logic here # (e.g., converting scaled mag values to arrow lengths) }, setup_layer = function(self, plot) { # Check if any existing scale is a ScaleMag instance has_mag_scale <- any(vapply( plot$scales$scales, function(s) inherits(s, "ScaleMag"), logical(1) )) # Add default scale_mag if none exists if (!has_mag_scale) { plot$scales$add(scale_mag()) } plot } )
Verify Your scale_mag() Definition
Make sure your scale_mag() is a valid continuous scale (either extending ggplot's built-in scale or using a custom ggproto class). For example:
# Basic continuous scale for mag scale_mag <- function(name = waiver(), breaks = waiver(), labels = waiver(), limits = NULL, trans = "identity", guide = "none") { ggplot2::scale_continuous( name = name, breaks = breaks, labels = labels, limits = limits, trans = trans, guide = guide, aesthetics = "mag" ) } # OR custom scaled scale with range control ScaleMag <- ggplot2::ggproto( "ScaleMag", ggplot2::ScaleContinuous, aesthetics = "mag", transform = function(self, x) { # Scale mag values to a usable range (e.g., 0 to 0.5 for plot units) scaled <- (x - min(x)) / (max(x) - min(x)) * 0.5 scaled[is.na(scaled)] <- 0 scaled } ) scale_mag <- function(...) { ggplot2::ggproto(NULL, ScaleMag, ...) }
Test It Out
Now both of these should work:
# With explicit scale_mag (still works as before) ggplot(geo, aes(lon, lat)) + geom_arrow(aes(mag = mag, angle = angle)) + scale_mag() # Without scale_mag (now works automatically) ggplot(geo, aes(lon, lat)) + geom_arrow(aes(mag = mag, angle = angle))
内容的提问来源于stack exchange,提问作者Elio Campitelli

