AnyMath 文档
Notebook

让我们建立一个温度,降水和天气事件的地图。

在这个例子中,我们想展示使用俄罗斯地图和天气服务的可能性。

让我们从安装几个库并配置环境后的示例开始。

In [ ]:
]add Shapefile, GeoJSON
In [ ]:
gr()
Out[0]:
Plots.GRBackend()

使用OpenMeteo API上传数据

首先,我们将下载由纬度和经度设置的坐标网格的每个点的数据。

收集天气数据可能需要相当长的时间。 从OpenMeteo开放和免费的来源,收集地形和天气数据需要大约4分钟的地图分辨率为4度(请求之间的延迟为0.1秒)和7分钟的分辨率为2度。 这对于培训示例是可以接受的,但对于更严肃的工作,最好使用更高效的api进行商业服务。

In [ ]:
# 取消注释以下载最新数据。
using Dates
target_date = Dates.Date(2026, 03, 08);
target_precision = 4;
target_hour = 12;

# include("get_meteo_data.jl")
# df_final = get_meteo_data( target_date, target_hour, target_precision );
# first( df_final, 5 )

如果你打开下载的数据,并建立一个简单的"热图"使用 heatmap,我们可以构造如下图:

In [ ]:
using CSV, DataFrames

df = CSV.read("天气已经满了。csv档案源", DataFrame, types=[Int64, Int64, Float32, String, Float32, Float32, Float32, Float32], missingstring=["NA", "N/A", ""])
df=过滤器(r->!ismissing(r.温度),df)
m=Matrix(permutedims(unstack(df,:longitude,:latitude,:temperature)[!,2:结束]))
热图(sort(unique(df.经度)),排序(唯一(df。纬度)),m,大小=(1200,400))
Out[0]:
No description has been provided for this image

过滤土地面积

由于我们还上传了有关矩阵的每个点落在哪里的信息—在水面上或不在地球表面上,我们可以输出这样的图形:

In [ ]:
m_temp=Matrix(permutedims(unstack(df,:longitude,:latitude,:temperature)[!,2:结束]))
m_land=Matrix(permutedims(unstack(df,:longitude,:latitude,:surface type)[!,2:结束]))
m_filtered = ifelse.(m_land .== "土地", m_temp, NaN)
热图(sort(unique(df.经度)),排序(唯一(df。纬度)),m_filtered,大小=(1200,400))
Out[0]:
No description has been provided for this image

从文件加载地图边框的小函数 f 这将使我们能够规划进一步的工作。:

In [ ]:
# 从GeoJSON文件上传多边形的函数
load_borders(f) = [ [(p[1],p[2]) for p in poly] for f in JSON.parsefile(f)["features"] for geom in [f["geometry"]] for g in (geom["type"]=="MultiPolygon" ? geom["coordinates"] : [geom["coordinates"]]) for poly in g ];

现在你可以把一张地图放在矩阵的顶部。:

In [ ]:
borders = load_borders("俄罗斯0.01。戈伊森")

lons=sort(unique(df.经度));lats=sort(unique(df。纬度));
# 创建热图
p = heatmap(lons, lats, m_filtered[end:-1:1, :],
             yflip=true, size=(1200,400), xlims=(18,190), ylims=(35,85), xlabel="经度", ylabel="阔度",
             color=:thermal, clims=(-40,30), colorbar_title="温度(°C)")
# 添加边框
for poly in borders
    xs = [point[1] for point in poly]; ys = [point[2] for point in poly]
    push!(xs, xs[1]); push!(ys, ys[1]) # 关闭多边形
    ys_flipped = [minimum(lats) + maximum(lats) - y .+ 9 for y in ys] # 我们考虑到yflip
    plot!(p, xs .- 3, ys_flipped, linecolor=:red, linewidth=1.5, alpha=0.7, label="")
end

display(p)
No description has been provided for this image

事实证明,将两个图关联起来相当困难,此外,我们看到海岸上的许多点都没有包含在离散化中。 但我们计划在不需要非常准确的会计时建立这样的时间表。

让我们比较确定矩阵中某个点是否属于文件中显示的地图的两个功能:

In [ ]:
include("geo_poly_functions.jl")

lats = 36:target_precision:87
lons = 14:target_precision:200

crude_matrix = [point_in_russia(lon, lat, borders) for lat in lats, lon in lons]
smooth_matrix = [land_weight(lon, lat, borders, 1.0) for lat in lats, lon in lons]

plot(
    heatmap(lons, lats, crude_matrix[end:-1:1, :], yflip = true, aspect_ratio=1.5),
    heatmap(lons, lats, smooth_matrix[end:-1:1, :] .> 0.001, yflip = true, aspect_ratio=1.5),
    size=(1200,250)
)
Out[0]:
No description has been provided for this image

右边的地图更适合我们,特别是因为在为右边的地图构建蒙版时,通过更改阈值,我们可以控制更精细细节的渲染,并准确地构建最能反映视觉意图的地图。

现在我们终于可以建立一个伟大的天气事件地图。:

In [ ]:
using CSV, DataFrames

# 将WMO的天气代码转换为emojis的函数
function weathercode_to_emoji(code)
    if code == 0                 return "☀️"  # 显然
    elseif code in [1, 2, 3]     return "☁️"  # 多云
    elseif code in [45, 48]      return "🌫️"  # 雾
    elseif code in [51, 53, 55]  return "🌧️"  # 毛毛雨
    elseif code in [56, 57]      return "🌨️"  # 冰冷的细雨
    elseif code in [61, 63, 65]  return "💧"  # 雨
    elseif code in [66, 67]      return "🌨️"  # 冻雨
    elseif code in [71, 73, 75]  return "❄️"  # 雪
    elseif code == 77            return "🌨️"  # 雪槽/雪槽
    elseif code in [80, 81, 82]  return "💧"  # 雨量
    elseif code in [85, 86]      return "☃️"  # 降雪
    elseif code == 95            return "⛈️"  # 雷暴雨
    elseif code in [96, 99]
        return "⛈️"  # 雷暴与冰雹
    else
        return "❓"
    end
end

# 上传数据
df = CSV.read("天气已经满了。csv档案源", DataFrame, types=[Int64, Int64, Float32, String, Float32, Float32, Float32, Float32], missingstring=["NA", "N/A", ""])
df1=滤波器(r->!ismissing(r.weather_code),df)
df1=filter([:latitude,:longitude]=>(lat,lon)->land_weight(lon,lat,borders,1.0).>0.001,df1)
lats,lons=sort(unique(df1.纬度)),排序(唯一(df1.经度))
emoji_matrix = fill("🟩", length(lats), length(lons))
emoji_dict=Dict((r.latitude,r.longitude)=>weathercode_to_emoji(round(Int32,r.weather_code))for r in eachrow(df1))
emoji_matrix = [get(emoji_dict, (lat, lon), "🟩") for lat in lats, lon in lons]

# 我们逐行输出矩阵
for i in length(lats):-1:1
    println( join(emoji_matrix[i, :]) )
    println( join(emoji_matrix[i, :]) ) # 让我们重复每行两次。
end
println("图例:它的清晰|每天都会多云|🌧每天都会下雨|作每天都会下雪|☃每天都会让它雪|⛈每天都会雷|🌫每天都会雾|❓没有数据")
🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩☀️☁️☀️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☁️🟩🟩🟩☀️☁️☀️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩🟩🟩☁️☁️🟩🟩🟩🟩🟩☁️☁️☁️☁️☁️☁️❄️❄️❄️☁️🟩☁️🟩🟩☁️☁️🟩☁️🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩🟩🟩☁️☁️🟩🟩🟩🟩🟩☁️☁️☁️☁️☁️☁️❄️❄️❄️☁️🟩☁️🟩🟩☁️☁️🟩☁️🟩🟩🟩🟩🟩🟩🟩
🟩❄️❄️🟩🟩🟩🟩☁️❄️☁️☁️☁️☁️☁️☀️☀️☀️☀️☀️☀️☁️☀️☀️☀️☁️❄️☁️☁️☀️☀️☀️☀️☁️☀️☁️☁️☁️☀️☀️
🟩❄️❄️🟩🟩🟩🟩☁️❄️☁️☁️☁️☁️☁️☀️☀️☀️☀️☀️☀️☁️☀️☀️☀️☁️❄️☁️☁️☀️☀️☀️☀️☁️☀️☁️☁️☁️☀️☀️
🟩❄️❄️☁️☁️☁️☁️☁️☀️☁️☀️☁️☁️☁️❄️❄️☁️☁️☁️☁️☁️☁️☁️☀️☀️❄️☁️☁️☁️☀️☀️☀️☁️☁️☁️☁️☀️☀️☀️
🟩❄️❄️☁️☁️☁️☁️☁️☀️☁️☀️☁️☁️☁️❄️❄️☁️☁️☁️☁️☁️☁️☁️☀️☀️❄️☁️☁️☁️☀️☀️☀️☁️☁️☁️☁️☀️☀️☀️
🟩☁️❄️☁️☀️☀️☀️☀️☁️☁️❄️☁️☁️☁️☁️☁️☁️☁️❄️❄️☁️☁️☁️☁️☀️☀️☁️☁️☁️☀️☀️☀️☀️☀️☁️☀️☁️☀️☁️
🟩☁️❄️☁️☀️☀️☀️☀️☁️☁️❄️☁️☁️☁️☁️☁️☁️☁️❄️❄️☁️☁️☁️☁️☀️☀️☁️☁️☁️☀️☀️☀️☀️☀️☁️☀️☁️☀️☁️
🟩☁️☁️❄️☁️☁️☁️☁️❄️☁️❄️❄️☁️☁️☁️☁️☁️❄️❄️❄️☁️☁️☁️☁️☀️☁️☁️☁️☁️☁️🟩🟩🟩☁️☁️🟩🟩🟩🟩
🟩☁️☁️❄️☁️☁️☁️☁️❄️☁️❄️❄️☁️☁️☁️☁️☁️❄️❄️❄️☁️☁️☁️☁️☀️☁️☁️☁️☁️☁️🟩🟩🟩☁️☁️🟩🟩🟩🟩
☀️🟩☁️☁️❄️☁️☁️❄️❄️☁️☁️🟩❄️❄️❄️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️🟩🟩🟩☁️🟩🟩🟩🟩🟩
☀️🟩☁️☁️❄️☁️☁️❄️❄️☁️☁️🟩❄️❄️❄️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️🟩🟩🟩☁️🟩🟩🟩🟩🟩
🟩🟩🟩☁️☁️☁️🟩🟩🟩🟩🟩🟩🟩🟩☁️☁️☁️☁️☁️🟩☀️☀️☀️☀️🟩🟩☁️☁️☁️❄️🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩☁️☁️☁️🟩🟩🟩🟩🟩🟩🟩🟩☁️☁️☁️☁️☁️🟩☀️☀️☀️☀️🟩🟩☁️☁️☁️❄️🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩☁️☁️☁️☁️☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☀️☀️❄️🟩☁️🟩🟩🟩🟩🟩🟩🟩
🟩🟩☁️☁️☁️☁️☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☀️☀️❄️🟩☁️🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☀️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
🟩🟩🟩🟩🟩☁️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩☀️🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
Легенда: ☀️ ясно | ☁️ облачно | 🌧️ дождь | ❄️ снег | ☃️ Снегопад | ⛈️ гроза | 🌫️ туман | ❓ нет данных

结论

我们执行了一个小型制图练习,上传了一个GeoJSON文件,并使用CSV表中的标量值,从而获得了一个有趣的可视化结果。