import matplotlib.pyplot as plt
import pandas as pd
#from cartopy import crs as ccrs
#from cartopy import feature as cfeature
from datetime import datetime,timedelta
#from dateutil.relativedelta import relativedelta
import geopandas as gpd
from lonboard import viz, Map, ScatterplotLayer, HeatmapLayer
import duckdbBy default, set the date and time to one hour prior to the current time. Or, specify a past date and hour.
Call time by querying
# Use the current time, or set your own for a past time.
# Set current to False if you want to specify a past time.
nowTime = datetime.now()
current = True
current = False
if (current):
validTime = datetime.now()
year = validTime.year
month = validTime.month
day = validTime.day
hour = validTime.hour
validTime = datetime(year, month, day, hour)
offset = timedelta(hours = 1)
validTime = validTime - offset
else:
year = 2026
month = 1
day = 1
hour = 0
time_1 = datetime(year, month, day, hour)time_0 = time_1 - timedelta(hours=1)
YYYY_0 = time_0.strftime("%Y")
YYYY_1 = time_1.strftime("%Y")
print(time_0, time_1)
# Handle edge case when the two hours straddle the end/beginning of a yearw
if (YYYY_0 == YYYY_1):
URLs = [f'https://data.source.coop/dynamical/asos-parquet/year={YYYY_1}/data.parquet']
else:
URLs = [f'https://data.source.coop/dynamical/asos-parquet/year={YYYY_0}/data.parquet',
f'https://data.source.coop/dynamical/asos-parquet/year={YYYY_1}/data.parquet']
2025-12-31 23:00:00 2026-01-01 00:00:00
URLs['https://data.source.coop/dynamical/asos-parquet/year=2025/data.parquet',
'https://data.source.coop/dynamical/asos-parquet/year=2026/data.parquet']df = duckdb.execute("""
SELECT *
FROM read_parquet($1, hive_partitioning=true)
WHERE
--- country = 'FR' AND
valid BETWEEN $2 AND $3
ORDER BY country
""", [URLs, time_0, time_1]).fetchdf()Loading...
dfLoading...
tmpc: Temperature in degrees Celsius
from numpy.random import default_rng
from pandas import Series, MultiIndex
rng = default_rng(0)
country = [ 'ZA', 'MX', 'CA', 'RU', 'KR', 'DE', 'BR', 'CN', 'GB', 'US', 'AU', 'IN', 'JP', 'FR']
years = (df['valid'])
index = MultiIndex.from_product([country, years], names=['country', 'valid'])
s = Series(rng.integers(20, 100, size=len(index)), index=index, name='count')
scountry valid
ZA 2025-12-31 23:00:00+00:00 88
2025-12-31 23:30:00+00:00 70
2025-12-31 23:00:00+00:00 60
2025-12-31 23:26:00+00:00 41
2025-12-31 23:00:00+00:00 44
..
FR 2025-12-31 23:00:00+00:00 25
2025-12-31 23:00:00+00:00 84
2025-12-31 23:00:00+00:00 67
2025-12-31 23:00:00+00:00 59
2025-12-31 23:47:00+00:00 57
Name: count, Length: 109018, dtype: int64The Data
from numpy.random import default_rng
from pandas import Series, MultiIndex
#rng = default_rng(0)
country = ['ZA', 'MX', 'CA', 'RU', 'KR', 'DE', 'BR', 'CN', 'GB', 'US', 'AU', 'IN', 'JP', 'FR']
valid = [2000, 2005, 2010, 2015, 2020, 2025]
index = MultiIndex.from_product([country, valid], names=['country', 'valid'])
s = Series(rng.integers(20, 100, size=len(index)), index=index, name='count')
scountry valid
ZA 2000 80
2005 97
2010 38
2015 97
2020 31
..
FR 2005 39
2010 48
2015 75
2020 22
2025 53
Name: count, Length: 84, dtype: int64Bar charts of counting the readings every 5 years from country between 2000 to 2025¶
from seaborn import barplot
fig, ax = plt.subplots(figsize=(12, 6))
barplot(
data=s.reset_index(),
x='country', y='count', hue='valid',
hue_order=years, palette='viridis_r',
ax=ax
)
#bars = ax.barh(df['country'], df['tmpc'], color='#4682b4')
ax.legend(ncol=3, title='Year', loc='lower right', bbox_to_anchor=(1, 1))
ax.spines[['top', 'right', 'left']].set_visible(False)
ax.yaxis.grid(color=ax.get_facecolor())
ax.set_xticklabels([t.get_text().title() for t in ax.get_xticklabels()]);
ax.set_xlabel('');
plt.tight_layout()
#plt.savefig('Temp2.pdf', bbox_inches='tight')
# 5. Show the plot
plt.show()/tmp/ipykernel_4862/3797966388.py:16: UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator.
ax.set_xticklabels([t.get_text().title() for t in ax.get_xticklabels()]);

Temperature reading between 2025-12-31 23:00:00 and 2026-01-01 00:00:00 across country stations¶
fig, ax = plt.subplots(figsize=(10, 6))
bars = ax.barh(df['country'], df['tmpc'], color='#4682b4')
ax.set_title('Temp in Celsius for 2025-12-31 23:10:00 to 2026-01-01 00:00:00', fontsize=14, pad=15)
ax.set_xlabel('Temp ($^\circ$C)', fontsize=12)
ax.set_xlim(-50, 50)
ax.set_ylabel('Country', fontsize=12)
plt.tight_layout()
# 5. Show the plot
plt.show()<>:6: SyntaxWarning: "\c" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\c"? A raw string is also an option.
<>:6: SyntaxWarning: "\c" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\c"? A raw string is also an option.
/tmp/ipykernel_4862/3216971234.py:6: SyntaxWarning: "\c" is an invalid escape sequence. Such sequences will not work in the future. Did you mean "\\c"? A raw string is also an option.
ax.set_xlabel('Temp ($^\circ$C)', fontsize=12)

import pandas as pd
import fsspec
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# 1. Load Parquet Data from URL
# parquet_url = "https://data.source.coop/dynamical/asos-parquet/year=2025/data.parquet" # Replace with your actual URL
# with fsspec.open(parquet_url) as file:
# df = pd.read_parquet(file)
# 2. Set up Matplotlib Figure with Plate Carree projection
fig = plt.figure(figsize=(14, 7))
ax = plt.axes(projection=ccrs.PlateCarree())
# 3. Plot the temperature data (assuming columns: 'longitude', 'latitude', 'temperature')
scatter = ax.scatter(
df['longitude'],
df['latitude'],
c=df['tmpc'], # The temperature parameter
cmap='coolwarm', # Colormap for temperature (blue = cold, red = hot)
s=15, # Marker size
alpha=0.8,
transform=ccrs.PlateCarree()
)
# 4. Add map features for context
ax.coastlines(linewidth=0.8)
ax.add_feature(cfeature.BORDERS, linestyle=':', alpha=0.5)
ax.add_feature(cfeature.LAND, facecolor='lightgray', alpha=0.3)
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.OCEAN, color='lightblue')
# 4. Add geographic visual anchors
ax.coastlines(resolution="110m", color="black", linewidth=1)
ax.gridlines(draw_labels=True, dms=True, xlocs=[-180, -90, 0, 90, 180])
# 5. Add a colorbar and title
cbar = plt.colorbar(scatter, ax=ax, orientation='horizontal', pad=0.05, shrink=0.7)
cbar.set_label('Temperature (°C)', fontsize=12)
plt.title('Global distribution of stations', fontsize=16, pad=15)
# Display the map
plt.show()/home/runner/micromamba/envs/METAR-archive-cookbook/lib/python3.14/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_physical/ne_110m_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/METAR-archive-cookbook/lib/python3.14/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_physical/ne_110m_ocean.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/METAR-archive-cookbook/lib/python3.14/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_physical/ne_110m_coastline.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)
/home/runner/micromamba/envs/METAR-archive-cookbook/lib/python3.14/site-packages/cartopy/io/__init__.py:242: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_cultural/ne_110m_admin_0_boundary_lines_land.zip
warnings.warn(f'Downloading: {url}', DownloadWarning)

import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np
# 1. Load data from Parquet
# Ensure your Parquet file has columns named 'lat', 'lon', and 'temperature'
#df = pd.read_csv('global_temperature_data.parquet')
# 2. Setup the figure and map projection (Flat Globe / Robinson)
fig = plt.figure(figsize=(12, 6))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.Robinson(central_longitude=0))
# 3. Plot the temperature data
# We use a scatter plot, mapping longitude/latitude to the globe
scatter = ax.scatter(
df['longitude'], df['latitude'],
c=df['tmpc'],
cmap='coolwarm',
s=5, # Marker size
alpha=0.8,
transform=ccrs.PlateCarree() # Specifies that the data's original coordinates are lat/lon
)
# 4. Add geographic context and formatting
ax.coastlines(color='black', linewidth=1)
ax.gridlines(draw_labels=True, linestyle='--', color='lightgray')
ax.gridlines(draw_labels=True, dms=True, xlocs=[-180, -120, -60, 0, 60, 120, 180])
ax.add_feature(cfeature.BORDERS, linestyle=':')
ax.add_feature(cfeature.LAND, facecolor='lightgray', alpha=0.5)
ax.add_feature(cfeature.OCEAN, color='lightblue')
# 5. Add a colorbar and title
cbar = plt.colorbar(scatter, ax=ax, orientation='horizontal', pad=0.05, shrink=0.7)
cbar.set_label('Temperature (°C)')
plt.title('Global distribution of stations')
# Show the plot
plt.show()
