Skip to content

Commit 6c98e99

Browse files
committed
Update plotting functions to use masks
This commit also fixes the issue of masking not affecting the colorbar. This commit also changes `Visualize.oceanmask` to return the geometry instead of a FeatureCollection.
1 parent 7115e78 commit 6c98e99

File tree

5 files changed

+113
-17
lines changed

5 files changed

+113
-17
lines changed

NEWS.md

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ var_no_land = ClimaAnalysis.apply_landmask(var)
2020
var_no_ocean = ClimaAnalysis.apply_oceanmask(var)
2121
```
2222

23+
## Bug fixes
24+
- Masking now affects the colorbar.
25+
2326
v0.5.10
2427
-------
2528

172 KB
Loading

docs/src/visualize.md

+26-4
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ In this example, we plotted `var` on the globe and overplotted a blue ocean.
3939
`ca_kwargs` (`Utils.kwargs`) is a convenience function to pass keyword arguments
4040
more easily.
4141

42-
!!! note Masking does not affect the colorbar. If you have values defined
43-
beneath the map, they can still affect the colorbar.
44-
4542
The output might look something like:
4643

4744
![oceanmask](./assets/oceanmask.png)
@@ -70,4 +67,29 @@ CairoMakie.save("myfigure.pdf", fig)
7067

7168
The output produces something like:
7269

73-
![biasplot](./assets/bias_plot.png)
70+
![biasplot](./assets/bias_plot.png)
71+
72+
We can also plot the bias using an ocean mask. This also means we compute the bias only
73+
over land.
74+
75+
```julia
76+
import ClimaAnalysis
77+
import ClimaAnalysis.Visualize: plot_bias_on_globe!, oceanmask
78+
import GeoMakie
79+
import CairoMakie
80+
81+
obs_var = ClimaAnalysis.OutputVar("ta_1d_average.nc")
82+
sim_var = ClimaAnalysis.get(ClimaAnalysis.simdir("simulation_output"), "ta")
83+
84+
fig = CairoMakie.Figure()
85+
plot_bias_on_globe!(fig,
86+
var,
87+
mask = oceanmask(),
88+
more_kwargs = Dict(:mask => ca_kwargs(color = :blue)),
89+
)
90+
CairoMakie.save("myfigure.pdf", fig)
91+
```
92+
93+
The output produces something like:
94+
95+
![biasplot_oceanmask](./assets/bias_plot_oceanmask.png)

ext/ClimaAnalysisGeoMakieExt.jl

+34-13
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Plot with `Makie.poly`.
1616
"""
1717
function Visualize.oceanmask()
1818
elevation = 0
19-
return GeoMakie.NaturalEarth.bathymetry(elevation)
19+
return GeoMakie.NaturalEarth.bathymetry(elevation).geometry
2020
end
2121

2222
"""
@@ -48,6 +48,9 @@ function _geomakie_plot_on_globe!(
4848
)
4949
length(var.dims) == 2 || error("Can only plot 2D variables")
5050

51+
apply_mask = _find_mask_to_apply(mask)
52+
!isnothing(apply_mask) && (var = apply_mask(var))
53+
5154
lon_name = ""
5255
lat_name = ""
5356

@@ -130,9 +133,6 @@ The dimensions have to be longitude and latitude.
130133
mask. `ClimaAnalysis` comes with predefined masks, check out [`Visualize.oceanmask`](@ref) and
131134
[`Visualize.landmask`](@ref).
132135
133-
!!! note Masking does not affect the colorbar. If you have values defined beneath the map,
134-
they can still affect the colorbar.
135-
136136
Additional arguments to the plotting and axis functions
137137
=======================================================
138138
@@ -209,9 +209,6 @@ The dimensions have to be longitude and latitude.
209209
mask. `ClimaAnalysis` comes with predefined masks, check out [`Visualize.oceanmask`](@ref) and
210210
[`Visualize.landmask`](@ref).
211211
212-
!!! note Masking does not affect the colorbar. If you have values defined beneath the map,
213-
they can still affect the colorbar.
214-
215212
Additional arguments to the plotting and axis functions
216213
=======================================================
217214
@@ -287,10 +284,9 @@ The dimensions have to be longitude and latitude.
287284
288285
`mask` has to be an object that can be plotted by `Makie.poly`. `ClimaAnalysis` comes with
289286
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.
287+
Also, the corresponding mask is applied to the `OutputVar`s. For instance, using
288+
`Visualize.landmask` means `ClimaAnalysis.apply_landmask` is applied to the `OutputVar`s
289+
when computing the bias.
294290
295291
Additional arguments to the plotting and axis functions
296292
=======================================================
@@ -324,9 +320,14 @@ function Visualize.plot_bias_on_globe!(
324320
:mask => Dict(),
325321
),
326322
)
327-
bias_var = ClimaAnalysis.bias(sim, obs)
323+
apply_mask = _find_mask_to_apply(mask)
324+
325+
bias_var = ClimaAnalysis.bias(sim, obs, mask = apply_mask)
328326
global_bias = round(bias_var.attributes["global_bias"], sigdigits = 3)
329-
rmse = round(ClimaAnalysis.global_rmse(sim, obs), sigdigits = 3)
327+
rmse = round(
328+
ClimaAnalysis.global_rmse(sim, obs, mask = apply_mask),
329+
sigdigits = 3,
330+
)
330331
units = ClimaAnalysis.units(bias_var)
331332

332333
bias_var.attributes["long_name"] *= " (RMSE: $rmse $units, Global bias: $global_bias $units)"
@@ -375,4 +376,24 @@ function Visualize.plot_bias_on_globe!(
375376
)
376377
end
377378

379+
"""
380+
Return the appropriate mask to apply to a `OutputVar` from the plotting `mask`.
381+
382+
If `mask` is `Visualize.landmask()`, return `ClimaAnalysis.apply_landmask`. If `mask` is
383+
`Visualize.oceanmask()`, return `ClimaAnalysis.apply_oceanmask`. In all other cases, return
384+
nothing.
385+
"""
386+
function _find_mask_to_apply(mask)
387+
if isnothing(mask)
388+
return nothing
389+
elseif mask == Visualize.landmask()
390+
return ClimaAnalysis.apply_landmask
391+
elseif mask == Visualize.oceanmask()
392+
return ClimaAnalysis.apply_oceanmask
393+
else
394+
@warn "Mask not recognized, overplotting it. The colorbar will not be correct"
395+
return nothing
396+
end
397+
end
398+
378399
end

test/test_GeoMakieExt.jl

+50
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,54 @@ using OrderedCollections
161161
ClimaAnalysis.Visualize.heatmap2D_on_globe!(fig9, ocean_var)
162162
output_name = joinpath(tmp_dir, "plot_apply_ocean_mask.png")
163163
Makie.save(output_name, fig9)
164+
165+
# Test bias plots with apply_landmask and apply_oceanmask
166+
fig10 = Makie.Figure()
167+
168+
lon = collect(range(-179.5, 179.5, 360))
169+
lat = collect(range(-89.5, 89.5, 180))
170+
data = collect(reshape(-32400:32399, (360, 180))) ./ (32399.0 / 5.0)
171+
dims = OrderedDict(["lon" => lon, "lat" => lat])
172+
attribs = Dict("long_name" => "idk", "short_name" => "ta", "units" => "K")
173+
dim_attribs = OrderedDict([
174+
"lon" => Dict("units" => "deg"),
175+
"lat" => Dict("units" => "deg"),
176+
])
177+
var = ClimaAnalysis.OutputVar(attribs, dims, dim_attribs, data)
178+
179+
data_zero = zeros(length(lon), length(lat))
180+
var_zero = ClimaAnalysis.OutputVar(attribs, dims, dim_attribs, data_zero)
181+
ClimaAnalysis.Visualize.plot_bias_on_globe!(
182+
fig10,
183+
var,
184+
var_zero;
185+
mask = ClimaAnalysis.Visualize.landmask(),
186+
more_kwargs = Dict(:mask => ClimaAnalysis.Utils.kwargs(color = :red)),
187+
)
188+
output_name = joinpath(tmp_dir, "plot_bias_landmask.png")
189+
Makie.save(output_name, fig10)
190+
191+
fig11 = Makie.Figure()
192+
ClimaAnalysis.Visualize.plot_bias_on_globe!(
193+
fig11,
194+
var,
195+
var_zero;
196+
mask = ClimaAnalysis.Visualize.oceanmask(),
197+
more_kwargs = Dict(:mask => ClimaAnalysis.Utils.kwargs(color = :blue)),
198+
)
199+
output_name = joinpath(tmp_dir, "plot_bias_oceanmask.png")
200+
Makie.save(output_name, fig11)
201+
202+
# Test bias plots with a mask that is not landmask() or oceanmask()
203+
fig12 = Makie.Figure()
204+
land_mask_modified = first(ClimaAnalysis.Visualize.landmask(), 50)
205+
206+
ClimaAnalysis.Visualize.heatmap2D_on_globe!(
207+
fig12,
208+
var2D,
209+
mask = land_mask_modified,
210+
more_kwargs = Dict(:mask => ClimaAnalysis.Utils.kwargs(color = :red)),
211+
)
212+
output_name = joinpath(tmp_dir, "plot_modified_mask.png")
213+
Makie.save(output_name, fig12)
164214
end

0 commit comments

Comments
 (0)