Skip to content

Commit 08f1d8f

Browse files
authored
Merge pull request #269 from JuliaDataCubes/lina_examples
Lina examples
2 parents f4f7f4c + a8fb690 commit 08f1d8f

14 files changed

+999
-15
lines changed

.github/workflows/Documenter.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ jobs:
1919
env:
2020
DISPLAY: ':0'
2121
steps:
22+
- name: Install matplotlib
23+
run: if [ "$RUNNER_OS" = "Linux" ]; then sudo apt-get install -y python3-matplotlib; fi
24+
shell: bash
2225
- uses: actions/checkout@v3
2326
- uses: julia-actions/setup-julia@v1
2427
- uses: julia-actions/cache@v1

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ build
77
.vscode
88
.DS_Store
99
*.zarr
10+
*.nc
1011

1112
docs/docs
1213
docs/site

docs/Project.toml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
[deps]
22
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
3-
ClusterManagers = "34f1f09b-3a8b-5176-ab39-66d58a4d544e"
3+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
44
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
55
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
6-
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
6+
DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3"
77
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
88
DocumenterMarkdown = "997ab1e6-3595-5248-9280-8efb232c3433"
99
Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
10+
EarthDataLab = "359177bc-a543-11e8-11b7-bb015dba3358"
1011
GLMakie = "e9467ef8-e4e7-5192-8a1a-b1aee30e663a"
12+
GeoInterface = "cf35fbd7-0cd7-5166-be24-54bfbe79505f"
13+
GeoJSON = "61d90e0f-e114-555e-ac52-39dfb47a3ef9"
1114
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"
12-
JSServe = "824d6782-a2ef-11e9-3a09-e5662e0c26f9"
15+
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
16+
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
1317
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
18+
MakieTeX = "6d554a22-29e7-47bd-aee5-0c5f06619414"
19+
MultivariateStats = "6f286f6a-111f-5878-ab1e-185364afe411"
1420
NetCDF = "30363a11-5582-574a-97bb-aa9a979735b9"
21+
PlotUtils = "995b91a9-d308-5afd-9ec6-746e21dbc043"
22+
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
23+
PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee"
24+
SkipNan = "aed68c70-c8b0-4309-8cd1-d392a74f991a"
1525
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
16-
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
26+
WeightedOnlineStats = "bbac0a1f-7c9d-5672-960b-c6ca726e5d5d"
1727
YAXArrayBase = "90b8fcef-0c2d-428d-9c56-5f86629e9d14"
1828
YAXArrays = "c21b50f5-aa40-41ea-b809-c0f5e47bfa5c"
1929
Zarr = "0a941bbe-ad1d-11e8-39d9-ab76183a1d99"

docs/examples/Gallery/simplemaps.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Zarr, YAXArrays, Dates
22
using GLMakie, GeoMakie
3-
using CairoMakie.GeometryBasics
3+
using GLMakie.GeometryBasics
44

55
store ="gs://cmip6/CMIP6/ScenarioMIP/DKRZ/MPI-ESM1-2-HR/ssp585/r1i1p1f1/3hr/tas/gn/v20190710/"
66
g = open_dataset(zopen(store, consolidated=true))
@@ -26,7 +26,7 @@ fig
2626
nlon = lon .- 180 .+ δlon
2727
ndata = circshift(data, (192,1))
2828

29-
GLMakie.activate!()
29+
3030
fig = Figure(resolution = (1200,600))
3131
ax = GeoAxis(fig[1,1])
3232
surface!(ax, nlon, lat, ndata; colormap = :seaborn_icefire_gradient, shading=false)

docs/examples/HowdoI/howdoi.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# !!! question
55

6-
# ## extract the axes names from a Cube?
6+
# ## Extract the axes names from a Cube
77

88
using YAXArrays
99
c = YAXArray(rand(10,10,5))
@@ -12,7 +12,20 @@ caxes(c)
1212

1313
# !!! question
1414

15-
# ## concatenate cubes?
15+
# ## Obtain values from axes and data from the cube
16+
17+
# There are two options to collect values from axes. In this examples the axis ranges from 1 to 10. Later we will see that axes can be `RangeAxis` such as latitude and longitude values, or `CategoricalAxis` which are strings such as variable names.
18+
19+
## this two examples bring the same result
20+
collect(getAxis("Dim_1", c).values)
21+
collect(c.axes[1].values)
22+
23+
## to collect data from a cube works exactly the same as doing it from an array
24+
c[:,:,1]
25+
26+
27+
28+
# ## Concatenate cubes
1629

1730
# It is possible to concatenate several cubes that shared the same dimensions using the [`concatenatecubes`]@ref function.
1831

docs/examples/UserGuide/applyfunctions.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ function mean_seasonal_cycle(c; ndays = 365)
112112
return msc
113113
end
114114

115-
msc = mean_seasonal_cycle(c)
115+
msc = mean_seasonal_cycle(c);
116116

117117
# ## Plot results: mean seasonal cycle
118118

docs/examples/UserGuide/create_from_func.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ gen_cube = mapCube(g, (lon, lat, time);
5555
# !!! info "slicing dim"
5656
# Note that now the broadcasted dimension is `lon`.
5757

58-
gen_cube.data[:,:,1]
58+
gen_cube.data[:, :, 1]

docs/examples/UserGuide/examples_from_esdl.jl

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# # Examples from the ESDL paper
2+
# ## Earth Syst. Dynam., 11, 201–234, 2020 [doi](https://doi.org/10.5194/esd-11-201-2020)
3+
4+
# **NOTE:** This section is based on the case studies from the paper "Earth system data cubes unravel global multivariate dynamics" by Mahecha, Gans et al. (2019). Original scripts are available at https://github.com/esa-esdl/ESDLPaperCode.jl.
5+
# - We have slightly adjusted the scripts. A few differences are that these new scripts are updated to Julia 1.9, and the YAXArrays.jl package is used.
6+
# - The dataset has been updated but it has less available variables. Therefore the results might differ.
7+
# - The calculations are performed with a very coarse spatial (2.5°) and temporal resolution (monthly).
8+
# - These are examples for illustrative purposes of the packages and do not intend any deeper scientific interpretation. For scientific analysis use the higher spatio-temporal resolution datasets.
9+
10+
# ## Case study 1: Seasonal dynamics on the land surface
11+
# ### Based on simple seasonal statistics
12+
13+
# * The code is written based on Julia 1.9
14+
15+
# * Normal text are explanations referring to notation and equations in the paper
16+
17+
# * `# comments in the code are itended explain specific aspects of the coding`
18+
19+
# * **New steps in workflows are introduced with bold headers**
20+
21+
# Load requiered packages
22+
23+
using Pkg
24+
25+
## for plotting later on (need to be loaded first, to avoid conflicts)
26+
using PyCall, PyPlot, PlotUtils
27+
28+
## for operating data cubes
29+
using Zarr, YAXArrays
30+
using EarthDataLab
31+
32+
## for data analysis
33+
using Statistics, Dates, SkipNan
34+
35+
36+
# Next we get a handle to the Earth System Data Cube we want to use, which provides a description of the cube:
37+
cube_handle = esdc(res="tiny")
38+
39+
## if we want the names of the variables:
40+
println(getAxis("Var", cube_handle).values)
41+
42+
# We decide which variables to plot:
43+
vars = ["gross_primary_productivity", "air_temperature_2m", "surface_moisture"]
44+
45+
## time based on data span availability
46+
time_overlap = Date("2001-01-01")..Date("2020-12-31")
47+
48+
# So we "virtually get" the cube data virtually:
49+
cube_subset = subsetcube(cube_handle, variable=vars, time=time_overlap)
50+
51+
# The next function estimates the median seasonal cycle. This changes the dimension of the cube, as the time domain is replaced by day of year (doy); Eq. 9 in the manuscript:
52+
# $$
53+
# f_{\{time\}}^{\{doy\}} : \mathcal{C}(\{lat, lon, time, var\}) \rightarrow \mathcal{C}(\{lat, lon, doy, var\})
54+
# $$
55+
56+
# median seasonal cycle built-in function
57+
cube_msc = getMedSC(cube_subset)
58+
59+
# The resulting cube `cube_msc` has is of the form $\mathcal{C}(\{lat, lon, doy, var\})$. On this cube we want to apply function `nan_med` (see below) to estimate latitudinal averages for all variables. The atomic function (Eq. 10) needs to
60+
# have the form, i.e. expecting a longitude and returning a scalar:
61+
# $$
62+
# f_{\{lon\}}^{\{\}} : \mathcal{C}(\{lat, lon, doy, var\}) \rightarrow \mathcal{C}(\{lat, doy, var\})
63+
# $$
64+
65+
import Statistics.median
66+
cube_msc_lat = mapslices(median skipmissing, cube_msc, dims = "Lon")
67+
68+
# The result of each operation on a data cube is a data cube. Here the resulting cube has the form $\mathcal{C}(\{doy, lat, var\})$ as expected but in different order, which is, irrelevant as axes have no natural order.
69+
70+
# ### Visualization
71+
72+
# At this point we leave the `Earth Data Lab` and and go for visualizations. Using PyPlot we can generate fig. 3 of the paper, the data can be exatracted from the resutls cube via array-access `A[:, :]`.
73+
74+
75+
## Create a plot and make it a polar plot
76+
function zonal_polar_plot(d_msc_lat, sbp, it, vari, lab)
77+
78+
ax = subplot(sbp, polar = "true")
79+
ourcmap = ColorMap(get_cmap("Spectral_r", 72))
80+
81+
## set polar ticks and labels
82+
month_ang = 0:11
83+
month_ang = month_ang .* (360/12)
84+
month_lab = ["Jan"; "Feb"; "Mar"; "Apr"; "May"; "Jun"; "Jul"; "Aug"; "Sep"; "Oct"; "Nov"; "Dec"]
85+
86+
## tuning
87+
ax.set_yticklabels([])
88+
ax.set_thetagrids(angles = month_ang,
89+
labels = month_lab,
90+
#rotation = month_ang
91+
)
92+
93+
## set Jan to the top of the plot
94+
ax.set_theta_zero_location("N")
95+
96+
## switch to clockwise
97+
ax.set_theta_direction(-1)
98+
99+
## color setup
100+
if isequal(vari, "gross_primary_productivity")
101+
N_min = 0
102+
N_max = 8
103+
N_var = 8
104+
elseif isequal(vari, "air_temperature_2m")
105+
N_min = -35
106+
N_max = 35
107+
N_var = 15
108+
elseif isequal(vari, "surface_moisture")
109+
N_min = 0
110+
N_max = 50
111+
N_var = 10
112+
end
113+
114+
## background setup
115+
116+
## define time (all could be done more elegantly in julia I guess... )
117+
time_ang = range(0, stop = 12, step = 0.25)
118+
N_time = length(time_ang)
119+
time_ang = time_ang .* (360/12)
120+
time_rad = time_ang .* (pi / 180)
121+
122+
## create a continous var for the background
123+
N_div = N_time*10
124+
y = range(N_min, stop = N_max, length = N_div)
125+
126+
## grid to fill the polar plot
127+
xgrid = repeat(time_rad', N_div, 1)
128+
ygrid = repeat(y, 1, N_time)
129+
130+
## a grid of NaNs to make an extra colorbar later
131+
nangrid = zeros(size(ygrid)).*NaN
132+
levels = range(-90, stop = 90, step = 10)
133+
ticks = range(-80, stop = 80, step = 20)
134+
135+
axsurf2 = ax.contourf(xgrid, ygrid, ygrid.*NaN, N_max,
136+
cmap = ourcmap,
137+
levels = levels,
138+
ticks = ticks)
139+
140+
## background to the range of values
141+
if isequal(vari, "gross_primary_productivity")
142+
axsurf = ax.contourf(xgrid, ygrid, ygrid, N_max,
143+
cmap = ColorMap("gray_r"),
144+
extend = "max")
145+
elseif isequal(vari, "air_temperature_2m")
146+
axsurf = ax.contourf(xgrid, ygrid, ygrid, N_max,
147+
cmap = ColorMap("gray_r"),
148+
extend = "both")
149+
elseif isequal(vari, "surface_moisture")
150+
axsurf = ax.contourf(xgrid, ygrid, ygrid, N_max,
151+
cmap = ColorMap("gray_r"),
152+
extend = "max")
153+
end
154+
155+
## colorbar setup
156+
if isodd(sbp)
157+
## add forground colorbar
158+
cban = colorbar(axsurf2, fraction = 0.05, shrink = 0.5, pad = 0.18)
159+
cban.ax.set_title(label = "Latitude")
160+
cban.set_ticks([-80, -60, -40, -20, 0, 20, 40, 60, 80])
161+
cban.set_ticklabels(["80°N", "60°N", "40°N", "20°N", "", "20°S", "40°S", "60°S", "80°S"])
162+
cban.ax.invert_yaxis()
163+
else
164+
## add background colorbar
165+
cbsurf = colorbar(axsurf, fraction = 0.05, shrink = 0.5, pad = 0.18)
166+
if vari == "gross_primary_productivity"
167+
cbsurf.ax.set_title(label = "GPP")
168+
cbsurf.set_label(label = "g C / (m2 d)", rotation=270, labelpad=+20)
169+
elseif vari == "air_temperature_2m"
170+
cbsurf.ax.set_title(label = "Tair")
171+
cbsurf.set_label(label = "°C", rotation = 270, labelpad=+20)
172+
elseif vari == "surface_moisture"
173+
cbsurf.ax.set_title(label = "Surf. Moist.")
174+
cbsurf.set_label(label = "[]", rotation = 270, labelpad=+20)
175+
cbsurf.set_ticks([0, 10, 20, 30, 40, 50])
176+
cbsurf.set_ticklabels(["0", "0.1", "0.2", "0.3", "0.4", "0.5"])
177+
end
178+
end
179+
180+
## forground setup
181+
182+
## plot the real data
183+
N_msc = size(d_msc_lat)[1]
184+
time_ang_dat = range(1/N_msc, step = 1/N_msc, length = N_msc)
185+
time_ang_dat = time_ang_dat .* (360)
186+
time_rad_dat = time_ang_dat .* (pi / 180)
187+
time_rad_dat = [time_rad_dat; time_rad_dat[1]]
188+
189+
## add your data
190+
for j = it
191+
jj = convert(Int, j)
192+
try
193+
var_idx = findall(vari .== getAxis("Variable", d_msc_lat).values)[1]
194+
ts = d_msc_lat[:, jj, var_idx]
195+
va = [ts; ts[1]]
196+
## correction for temperature
197+
if isequal(vari, "air_temperature_2m")
198+
va = va.-273.15
199+
elseif isequal(vari, "surface_moisture")
200+
va = va.*100
201+
end
202+
p = ax.plot(time_rad_dat, va,
203+
color = ourcmap(jj),
204+
linewidth = 0.8)
205+
catch
206+
end
207+
end
208+
209+
end
210+
211+
# create a new figure
212+
f1 = figure("polar_lineplot_new", figsize = (10, 15))
213+
214+
# get the latitude values for which we have data
215+
L = collect(getAxis("lat", caxes(cube_msc_lat)).values)
216+
217+
sbps = 321:2:332
218+
labtoshow = ["a)", "b)", "c)", "d)", "e)", "f)"]
219+
vari = getAxis("Variable", caxes(cube_msc_lat)).values
220+
221+
for (sbp, lab, vari) in zip(sbps, labtoshow, vari)
222+
it1 = range(72/2, stop = 1, step = -2)
223+
it2 = range(72/2+1, stop = 72, step = 2)
224+
zonal_polar_plot(cube_msc_lat, sbp, it1, vari, lab)
225+
zonal_polar_plot(cube_msc_lat, sbp+1, it2, vari, lab)
226+
end
227+
228+
f1

0 commit comments

Comments
 (0)