Getting Weather Data

There are two main entry paths in PlantMeteo:

  • start with get_weather if you have coordinates and dates but no cleaned file yet
  • start with read_weather if you already have weather data from stations, archives, or project pipelines

Path 1: Get Weather From An API

This is the fastest route when downloading weather is the hard part.

using PlantMeteo
using Dates

period = Date(2025, 7, 1):Day(1):Date(2025, 7, 2)
api_weather = get_weather(48.8566, 2.3522, period; api = PlantMeteo.DemoAPI())
api_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)`

PlantMeteo.DemoAPI() is only for tests, demos, and documentation. It is not an actual weather service. The default real backend is OpenMeteo().

This function can return any type you want via the sink keyword, which is a function that transforms the full weather table into a derived object. By default, sink is the identity function, but you could use DataFrame instead.

You can also use it to get a summary or a custom struct instead of the full table. For example, you could use sink to get a summary of the weather data like this:

summary = get_weather(
    48.8566,
    2.3522,
    period;
    api = PlantMeteo.DemoAPI(),
    sink = x -> (
        nsteps = length(x),
        first_date = x[1].date,
        mean_temperature = round(sum(x.T) / length(x), digits = 2),
    )
)
(nsteps = 48, first_date = DateTime("2025-07-01T00:00:00"), mean_temperature = 20.4)
summary.nsteps
48
summary.first_date
2025-07-01T00:00:00
summary.mean_temperature
20.4

Using The Built-In Open-Meteo Backend

In normal use, replace PlantMeteo.DemoAPI() with OpenMeteo():

using PlantMeteo, Dates

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

Use this path first when:

  • you do not already have trusted local weather files
  • you need a quick forecast or a recent historical series
  • you want a consistent hourly schema without writing provider-specific glue code

See Open-Meteo Guide for why Open-Meteo is a good default and when to validate against station data.

Path 2: Read And Standardize Local Files

Use read_weather when you already have weather data but need to map source-specific names and units into PlantMeteo variables.

file = joinpath(dirname(dirname(pathof(PlantMeteo))), "test", "data", "meteo.csv")

file_weather = read_weather(
    file,
    :temperature => :T,
    :relativeHumidity => (x -> x ./ 100) => :Rh,
    :wind => :Wind,
    :atmosphereCO2_ppm => :Cₐ,
    date_format = DateFormat("yyyy/mm/dd")
)
TimeStepTable{Atmosphere{(:date, :duration,...}(3 x 29):
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 hour_start hour_end temperature relativeHumidity Re_SW_f wind atmosphereCO2_ppm
DateTime Dates.CompoundPeriod Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Time Time Float64 Float64 Float64 Float64 Float64
1 2016-06-12T12:00:00 30 minutes 25.0 1.0 101.325 0.6 0.0 380.0 1.90812 3.1802 1.27208 1.18389 2.44188e6 0.0675791 0.602345 0.190095 0.75 Inf Inf Inf Inf Inf 12:00:00 12:30:00 25.0 60.0 500.0 1.0 380.0
2 2016-06-12T12:30:00 30 minutes 26.0 1.5 101.325 0.62 0.0 380.0 2.09238 3.37481 1.28243 1.17993 2.43951e6 0.0676446 0.610038 0.200215 0.75 Inf Inf Inf Inf Inf 12:30:00 13:00:00 26.0 62.0 500.0 1.5 380.0
3 2016-06-12T13:00:00 30 minutes 25.3 1.5 101.325 0.58 0.0 380.0 1.87776 3.23752 1.35976 1.1827 2.44117e6 0.0675987 0.60088 0.193085 0.75 Inf Inf Inf Inf Inf 13:00:00 13:30:00 25.3 58.0 500.0 1.5 380.0
Metadata: `Dict{String, Any}("name" => "Aquiares", "latitude" => 15.0, "altitude" => 100.0, "use" => [:clearness], "file" => "/home/runner/work/PlantMeteo.jl/PlantMeteo.jl/test/data/meteo.csv")`
length(file_weather)
3
file_weather[1].date
2016-06-12T12:00:00
file_weather[1].T
25.0

PlantMeteo also handles legacy date encodings where only the first row contains the date and later rows must be forward-filled:

legacy = mktempdir() do tmp
    path = joinpath(tmp, "legacy_meteo.csv")
    write(path, """
date;hour_start;hour_end;temperature;relativeHumidity;wind;clearness
2016/06/12;08:30:00;09:00:00;25;60;1.0;0.6
;09:00:00;09:30:00;25;60;1.0;0.6
""")

    read_weather(
        path,
        :temperature => :T,
        :relativeHumidity => (x -> x ./ 100) => :Rh,
        :wind => :Wind,
        date_formats = (DateFormat("yyyy/mm/dd"), DateFormat("yyyy-mm-dd")),
        forward_fill_date = true,
    )
end
TimeStepTable{Atmosphere{(:date, :duration,...}(2 x 27):
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 hour_start hour_end temperature relativeHumidity wind
DateTime Dates.CompoundPeriod Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Time Time Int64 Int64 Float64
1 2016-06-12T08:30:00 30 minutes 25.0 1.0 101.325 0.6 0.0 400.0 1.90812 3.1802 1.27208 1.18389 2.44188e6 0.0675791 0.602345 0.190095 0.6 Inf Inf Inf Inf Inf 08:30:00 09:00:00 25 60 1.0
2 2016-06-12T09:00:00 30 minutes 25.0 1.0 101.325 0.6 0.0 400.0 1.90812 3.1802 1.27208 1.18389 2.44188e6 0.0675791 0.602345 0.190095 0.6 Inf Inf Inf Inf Inf 09:00:00 09:30:00 25 60 1.0
Metadata: `Dict("file" => "/tmp/jl_5HdxlA/legacy_meteo.csv")`
legacy.date[1]
2016-06-12T08:30:00
legacy.date[2]
2016-06-12T09:00:00

Use this path first when:

  • you already have station or archive files
  • you need to preserve curated local data instead of re-downloading
  • the main challenge is column naming, units, or date parsing rather than data acquisition

Choosing Between The Two

  • Start with get_weather if your problem is getting weather data in the first place.
  • Start with read_weather if your problem is standardizing weather data you already own.
  • After either path, the downstream workflow is the same: inspect the table, aggregate with to_daily, sample with sample_weather, or export with write_weather.