Quickstart

This quickstart starts with the most common pain point: getting usable weather from coordinates and dates. The examples below use PlantMeteo.DemoAPI(), a built-in offline backend for tests and documentation, so the code stays reproducible. It is not a real weather provider. For actual downloads, use the built-in OpenMeteo backend (which is the default).

1. Download Weather Into A Typed Table

We can use get_weather to download weather for a location and period:

using PlantMeteo
using Dates
using Statistics

period = Date(2025, 7, 1):Day(1):Date(2025, 7, 2)
weather = get_weather(48.8566, 2.3522, period; api = PlantMeteo.DemoAPI())
weather[1:4]
TimeStepTable{Atmosphere{(:date, :duration,...}(4 x 22):
date duration T Wind P Rh Precipitations Cₐ e eₛ VPD ρ λ γ ε Δ clearness Ri_SW_f Ri_PAR_f Ri_NIR_f Ri_TIR_f Ri_custom_f
DateTime Hour Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64
1 2025-07-01T00:00:00 1 hour 20.0 1.3 101.3 0.65 0.0 415.0 1.52555 2.347 0.821448 1.20379 2.4537e6 0.0672368 0.584806 0.145734 Inf 0.0 Inf Inf Inf Inf
2 2025-07-01T01:00:00 1 hour 21.2941 1.29659 101.3 0.611177 0.0 415.0 1.5535 2.54181 0.988315 1.19849 2.45064e6 0.0673208 0.585956 0.156273 Inf 0.0 Inf Inf Inf Inf
3 2025-07-01T02:00:00 1 hour 22.5 1.2866 101.3 0.575 0.0 415.0 1.57317 2.73596 1.16278 1.19361 2.44779e6 0.0673992 0.586667 0.166668 Inf 0.0 Inf Inf Inf Inf
4 2025-07-01T03:00:00 1 hour 23.5355 1.27071 101.3 0.543934 0.0 415.0 1.58443 2.9129 1.32847 1.18944 2.44534e6 0.0674667 0.586972 0.176056 Inf 0.0 Inf Inf Inf Inf
Metadata: `Dict{String, Any}("latitude" => 48.8566, "source" => "demo-api", "longitude" => 2.3522)`

get_weather returns a Weather-like TimeStepTable that you can inspect row-wise or column-wise.

2. Inspect The Variables You Will Feed To The Model

TimeStepTable is a custom table type that is optimized for weather sampling. It has a row-oriented design, so it is efficient to access rows of weather data for sampling. Each row corresponds to a timestep in the original data, and the columns correspond to the variables at that timestep.

We can inspect the variables in the table by indexing rows like a vector, e.g. weather[1] is the first row, which is a TimeStepRow struct with fields for each variable:

weather[1]
╭──── TimeStepRow ─────────────────────────────────────────────────────────────╮
  Step 1: date=2025-07-01T00:00:00, duration=1 hour, T=20.0, Wind=1.3,        
  P=101.3, Rh=0.65, Precipitations=0.0, Cₐ=415.0, e=1.5255470730405223,       
  eₛ=2.3469954969854188 ...                                                   
╰──────────────────────────────────────────────────────────────────────────────╯

You can then inspect the variables in that row by field access, e.g. weather[1].date is the date at the first timestep:

weather[1].date
2025-07-01T00:00:00

Similarly, you can access any variable in that row:

weather[1].T
20.0

You can also use a column-oriented design to access variables across all rows like a DataFrame, e.g.:

weather[1:3, :T]
3-element Vector{Float64}:
 20.0
 21.294095225512603
 22.5

The table also has metadata keys that describe the site and the source of the data:

metadatakeys(weather)
("latitude", "longitude", "source")

3. Aggregate To One Row Per Day

You can use to_daily when you want to aggregate infra-day data into a standard daily weather table with one row per civil day.

daily = to_daily(weather)
TimeStepTable{Atmosphere{(:date, :duration,...}(2 x 26):
date duration T Wind P Rh Precipitations Cₐ e eₛ VPD ρ λ γ ε Δ clearness Ri_SW_f Ri_PAR_f Ri_NIR_f Ri_TIR_f Ri_custom_f year dayofyear Tmin Tmax
Date Day Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Int64 Int64 Float64 Float64
1 2025-07-01 1 day 20.0 1.2 101.3 0.65 1.2 415.0 1.50274 2.39642 0.893674 1.20396 2.4537e6 0.0672376 0.583441 0.148012 Inf 17.7741 Inf Inf Inf Inf 2025 182 15.0 25.0
2 2025-07-02 1 day 20.8 1.2 101.3 0.65 1.2 415.0 1.57876 2.51709 0.938332 1.20068 2.45181e6 0.0672895 0.587345 0.154521 Inf 17.7741 Inf Inf Inf Inf 2025 183 15.8 25.8
Metadata: `Dict{String, Any}("latitude" => 48.8566, "source" => "demo-api", "longitude" => 2.3522)`
length(daily)
2
daily[1]
╭──── TimeStepRow ─────────────────────────────────────────────────────────────╮
  Step 1: date=2025-07-01, duration=1 day, T=19.999999999999996, Wind=1.2,    
  P=101.30000000000003, Rh=0.6500000000000001, Precipitations=1.20000000000   
  00002, Cₐ=415.0, e=1.5027425702003174, eₛ=2.3964165011914402 ...            
╰──────────────────────────────────────────────────────────────────────────────╯

4. Sample Weather For A Model Time Window

We can use sample_weather to sample weather for a model time window. This is useful when you have a model that runs on a different time step than the original data, or when you want to apply a custom reducer over a rolling window.

Let's say we have a model that runs on 24-hour time steps, and we want to sample weather for each 24-hour window. We can use sample_weather with a RollingWindow to do this:

prepared = prepare_weather_sampler(weather)
window = RollingWindow(24.0)
sampled = sample_weather(prepared, 24; window = window)
Atmosphere(date = DateTime("2025-07-01T23:00:00"), duration = Millisecond(86400000), T = 20.0, Wind = 1.1999999999999997, P = 101.3, Rh = 0.6500000000000001, Precipitations = 1.2000000000000002, Cₐ = 415.0, e = 1.502742570200317, eₛ = 2.396416501191441, VPD = 0.8936739309911232, ρ = 1.203960293363366, λ = 2.453700000000001e6, γ = 0.0672375819269494, ε = 0.5834413910269151, Δ = 0.1480115968281549, clearness = Inf, Ri_SW_f = 205.71834055297288, Ri_PAR_f = Inf, Ri_NIR_f = Inf, Ri_TIR_f = Inf, Ri_custom_f = Inf, Rhmax = 0.8, Rhmin = 0.5, Ri_NIR_q = Inf, Ri_PAR_q = Inf, Ri_SW_q = 17.774064623776855, Ri_TIR_q = Inf, Ri_custom_q = Inf, Tmax = 25.0, Tmin = 15.0)

The underlying data structure of sampled is the same as weather, but the values are now the result of applying the reducer (by default, mean) over each 24-hour window. Each row in sampled corresponds to a 24-hour window in the original data, and the variables are the aggregated values for that window. You can inspect the variables in sampled just like before, e.g. sampled[1] is the first sampled row, and sampled[1].T is the mean temperature over the first 24-hour window:

sampled
Atmosphere(date = DateTime("2025-07-01T23:00:00"), duration = Millisecond(86400000), T = 20.0, Wind = 1.1999999999999997, P = 101.3, Rh = 0.6500000000000001, Precipitations = 1.2000000000000002, Cₐ = 415.0, e = 1.502742570200317, eₛ = 2.396416501191441, VPD = 0.8936739309911232, ρ = 1.203960293363366, λ = 2.453700000000001e6, γ = 0.0672375819269494, ε = 0.5834413910269151, Δ = 0.1480115968281549, clearness = Inf, Ri_SW_f = 205.71834055297288, Ri_PAR_f = Inf, Ri_NIR_f = Inf, Ri_TIR_f = Inf, Ri_custom_f = Inf, Rhmax = 0.8, Rhmin = 0.5, Ri_NIR_q = Inf, Ri_PAR_q = Inf, Ri_SW_q = 17.774064623776855, Ri_TIR_q = Inf, Ri_custom_q = Inf, Tmax = 25.0, Tmin = 15.0)

This sampled row is the aggregation of the previous 24 hourly timesteps:

mean(weather[i].T for i in 1:24)
20.0

5. Switch To The Real Open-Meteo Backend

The runnable examples above stay offline on purpose. In real use, replace PlantMeteo.DemoAPI() with OpenMeteo() (which is the default):

using PlantMeteo, Dates

period = Date(2025, 7, 1):Day(1):Date(2025, 7, 3)
weather = get_weather(48.8566, 2.3522, period; api=OpenMeteo())

Next Steps