@@ -254,4 +254,125 @@ function Visualize.contour2D_on_globe!(
254
254
)
255
255
end
256
256
257
+ """
258
+ plot_bias_on_globe!(fig::Makie.Figure,
259
+ sim::ClimaAnalysis.OutputVar,
260
+ obs::ClimaAnalysis.OutputVar;
261
+ cmap_extrema = extrema(ClimaAnalysis.bias(sim, obs).data),
262
+ p_loc = (1, 1),
263
+ plot_coastline = true,
264
+ plot_colorbar = true,
265
+ mask = nothing,
266
+ more_kwargs)
267
+ plot_bias_on_globe!(grid_layout::Makie.GridLayout,
268
+ sim::ClimaAnalysis.OutputVar,
269
+ obs::ClimaAnalysis.OutputVar;
270
+ cmap_extrema = extrema(ClimaAnalysis.bias(sim, obs).data),
271
+ p_loc = (1, 1),
272
+ plot_coastline = true,
273
+ plot_colorbar = true,
274
+ mask = nothing,
275
+ more_kwargs)
276
+
277
+
278
+ Plot the bias (`sim.data - var.data`) on a projected geoid. The gloal bias and root mean
279
+ squared error (RMSE) are computed and can be found in the title of the plot. This function
280
+ plots the returned `OutputVar` of `ClimaAnalysis.bias(sim, obs)`. See also
281
+ [`ClimaAnalysis.bias`](@ref).
282
+
283
+ The plot comes with labels, units, and a colorbar. This function uses a constrained colormap
284
+ based on the values of `cmap_extrema`.
285
+
286
+ The dimensions have to be longitude and latitude.
287
+
288
+ `mask` has to be an object that can be plotted by `Makie.poly`. `ClimaAnalysis` comes with
289
+ predefined masks, check out [`Visualize.oceanmask`](@ref) and [`Visualize.landmask`](@ref).
290
+
291
+ !!! note
292
+ Masking does not affect the colorbar. If you have values defined beneath the map, they
293
+ can still affect the colorbar.
294
+
295
+ Additional arguments to the plotting and axis functions
296
+ =======================================================
297
+
298
+ `more_kwargs` can be a dictionary that maps symbols to additional options for:
299
+ - the axis (`:axis`)
300
+ - the plotting function (`:plot`)
301
+ - the colorbar (`:cb`)
302
+ - the coastline (`:coast`)
303
+ - the mask (`:mask`)
304
+
305
+ The coastline is plotted from `GeoMakie.coastline` using the `lines!` plotting function.
306
+
307
+ The values are splatted in the relevant functions. Populate them with a
308
+ Dictionary of `Symbol`s => values to pass additional options.
309
+ """
310
+ function Visualize. plot_bias_on_globe! (
311
+ place:: MakiePlace ,
312
+ sim:: ClimaAnalysis.OutputVar ,
313
+ obs:: ClimaAnalysis.OutputVar ;
314
+ cmap_extrema = extrema (ClimaAnalysis. bias (sim, obs). data),
315
+ p_loc = (1 , 1 ),
316
+ plot_coastline = true ,
317
+ plot_colorbar = true ,
318
+ mask = nothing ,
319
+ more_kwargs = Dict (
320
+ :plot => Dict (),
321
+ :cb => Dict (),
322
+ :axis => Dict (),
323
+ :coast => Dict (:color => :black ),
324
+ :mask => Dict (),
325
+ ),
326
+ )
327
+ bias_var = ClimaAnalysis. bias (sim, obs)
328
+ global_bias = round (bias_var. attributes[" global_bias" ], sigdigits = 3 )
329
+ rmse = round (ClimaAnalysis. global_rmse (sim, obs), sigdigits = 3 )
330
+ units = ClimaAnalysis. units (bias_var)
331
+
332
+ bias_var. attributes[" long_name" ] *= " (RMSE: $rmse $units , Global bias: $global_bias $units )"
333
+ min_level, max_level = cmap_extrema
334
+
335
+ # Make sure that 0 is at the center
336
+ cmap = Visualize. _constrained_cmap (
337
+ Makie. cgrad (:vik ). colors,
338
+ min_level,
339
+ max_level;
340
+ categorical = true ,
341
+ )
342
+ nlevels = 11
343
+ # Offset so that it covers 0
344
+ levels = collect (range (min_level, max_level, length = nlevels))
345
+ offset = levels[argmin (abs .(levels))]
346
+ levels = levels .- offset
347
+ ticklabels = map (x -> string (round (x; digits = 0 )), levels)
348
+ ticks = (levels, ticklabels)
349
+
350
+ default_kwargs = Dict (
351
+ :plot => Dict (
352
+ :colormap => cmap,
353
+ :levels => levels,
354
+ :extendhigh => :auto ,
355
+ :extendlow => :auto ,
356
+ ),
357
+ :cb => Dict (:ticks => ticks),
358
+ )
359
+
360
+ # Function for recursively merging two dictionaries if the values of the dictionaries
361
+ # are dictionaries and the values of those are also dictionaries and so on
362
+ # See: https://discourse.julialang.org/t/multi-layer-dict-merge/27261/6
363
+ recursive_merge (x:: AbstractDict... ) = merge (recursive_merge, x... )
364
+ recursive_merge (x... ) = x[end ]
365
+ default_and_more_kwargs = recursive_merge (default_kwargs, more_kwargs)
366
+
367
+ return Visualize. contour2D_on_globe! (
368
+ place,
369
+ bias_var;
370
+ p_loc = p_loc,
371
+ plot_coastline = plot_coastline,
372
+ plot_colorbar = plot_colorbar,
373
+ mask = mask,
374
+ more_kwargs = default_and_more_kwargs,
375
+ )
376
+ end
377
+
257
378
end
0 commit comments