Minggu, 10 Mei 2026
Mengapa Cartopy untuk Peta Meteorologi

Data cuaca pada dasarnya hanyalah angka — array suhu, tekanan, kelembapan yang tersusun dalam grid lat/lon. Tapi angka-angka itu baru bermakna ketika kita bisa melihatnya di atas peta yang benar. Pola tekanan, jalur siklon, distribusi curah hujan — semua itu tersembunyi dalam array NumPy sampai kita memetakannya dengan proyeksi yang tepat.
Cartopy adalah library Python untuk geospatial data processing yang menjadi standar de-facto visualisasi meteorologi. Awalnya dikembangkan di UK Met Office agar para ilmuwan bisa memvisualisasikan data mereka di atas peta dengan cepat, mudah, dan akurat. Cartopy dibangun di atas PROJ, NumPy, dan Shapely. Library ini menyediakan interface yang bersih ke matplotlib untuk menghasilkan peta berkualitas publikasi.
Untuk pembaca yang sudah familiar dengan Python dan NumPy, tutorial ini menunjukkan cara kerja Cartopy dari dasar — mulai dari CRS object, GeoAxes, hingga mereprojeksi array data ERA5 ke peta yang siap digunakan. Integrasi Cartopy dengan xarray dan workflow ERA5 menjadikannya pilihan utama di komunitas meteorologi global maupun lokal.
Memahami Coordinate Reference System dan Proyeksi
Konsep terpenting dalam Cartopy adalah perbedaan antara data CRS dan display projection. Keduanya sama-sama objek CRS, tapi punya peran yang berbeda.
Data CRS adalah sistem koordinat tempat data kita disimpan. Hampir semua output model NWP dan reanalysis seperti ERA5 menggunakan PlateCarree — lat/lon biasa dengan derajat yang spasi-nya merata. Ketika kita buka file NetCDF ERA5, koordinatnya sudah dalam format ini.
Display projection adalah proyeksi yang kita pilih untuk merender peta. Cartopy menyediakan lebih dari 40 proyeksi melalui modul cartopy.crs. Beberapa yang paling relevan untuk meteorologi:
ccrs.PlateCarree()— equirectangular, standar untuk wilayah tropis dan ekuatorialccrs.LambertConformal()— conic conformal, banyak dipakai dalam meteorologi operasional NWS/NOAAccrs.Mercator()— silindris, populer untuk peta laut dan web map
Proyeksi lain seperti Stereographic (polar) dan Geostationary (data Himawari-9) juga tersedia untuk kebutuhan spesifik. Untuk Indonesia, ccrs.PlateCarree() sudah cukup sebagai display projection karena kita berada di kawasan ekuatorial. Satu hal penting: set_extent() selalu menggunakan koordinat PlateCarree terlepas dari display projection yang dipilih, dan Cartopy melakukan reprojeksi secara internal.
Mari kita inspect beberapa CRS object:
import cartopy.crs as ccrs
projections = [
("PlateCarree", ccrs.PlateCarree()),
("LambertConformal", ccrs.LambertConformal()),
("Mercator", ccrs.Mercator()),
("Orthographic (Indonesia)", ccrs.Orthographic(central_longitude=120, central_latitude=-2)),
]
for name, proj in projections:
class_name = type(proj).__name__
try:
proj4 = proj.proj4_init
print(f"{name}: class={class_name}, proj4={proj4}")
except AttributeError:
try:
proj4 = proj.proj4_params
print(f"{name}: class={class_name}, proj4_params={proj4}")
except Exception as e:
print(f"{name}: class={class_name}, proj4 not available ({e})")
PlateCarree: class=PlateCarree, proj4=+ellps=WGS84 +a=6378137.0 +proj=eqc +lon_0=0.0 +to_meter=111319.4907932736 +vto_meter=1 +no_defs
LambertConformal: class=LambertConformal, proj4=+ellps=WGS84 +proj=lcc +lon_0=-96.0 +lat_0=39.0 +x_0=0.0 +y_0=0.0 +lat_1=33 +lat_2=45 +no_defs
Mercator: class=Mercator, proj4=+ellps=WGS84 +proj=merc +lon_0=0.0 +x_0=0.0 +y_0=0.0 +units=m +no_defs
Orthographic (Indonesia): class=Orthographic, proj4=+a=6378137.0 +proj=ortho +lon_0=120 +lat_0=-2 +alpha=0.0 +no_defs
Membuat GeoAxes dan Menambahkan Fitur Peta
Ketika kita pass parameter projection=ccrs.PlateCarree() ke plt.axes(), matplotlib mengembalikan objek GeoAxes — bukan Axes biasa. GeoAxes inilah yang membuka akses ke method-method kartografis seperti set_extent(), add_feature(), dan coastlines().
Dua pendekatan untuk menambahkan garis pantai: method cepat ax.coastlines() yang pakai default resolution, atau ax.add_feature(cfeature.COASTLINE, linewidth=0.5) yang lebih fleksibel. Untuk peta meteorologi yang serius, add_feature() lebih direkomendasikan karena kita bisa mengatur resolution, warna, dan style.
Cartopy mengambil shapefile Natural Earth dari naturalearthdata.com pada pertama kali dijalankan, dalam tiga resolusi: '10m' (detail tinggi), '50m' (menengah), dan '110m' (kasar). Untuk render awal atau preview, '110m' sudah cukup dan lebih cepat. Perlu dicatat bahwa proses download shapefile ini membutuhkan koneksi internet — jalankan di environment lokal, bukan di sandbox yang offline.
Snippet berikut mencetak extent yang akan kita gunakan dan daftar feature constants yang tersedia:
import cartopy.crs as ccrs
import cartopy.feature as cfeature
crs = ccrs.PlateCarree()
extent = [90, 145, -15, 15] # Indonesia: [lonW, lonE, latS, latN]
print(f"Data CRS: {type(crs).__name__}")
print(f"Extent Indonesia: lonW={extent[0]}, lonE={extent[1]}, latS={extent[2]}, latN={extent[3]}")
print(f"Lebar domain: {extent[1]-extent[0]}° lon x {extent[3]-extent[2]}° lat")
print()
feature_names = [
("COASTLINE", cfeature.COASTLINE),
("BORDERS", cfeature.BORDERS),
("LAND", cfeature.LAND),
("OCEAN", cfeature.OCEAN),
("LAKES", cfeature.LAKES),
("RIVERS", cfeature.RIVERS),
]
print("Cartopy feature constants:")
for name, feat in feature_names:
print(f" cfeature.{name}: {type(feat).__name__}")
Data CRS: PlateCarree
Extent Indonesia: lonW=90, lonE=145, latS=-15, latN=15
Lebar domain: 55° lon x 30° lat
Cartopy feature constants:
cfeature.COASTLINE: NaturalEarthFeature
cfeature.BORDERS: NaturalEarthFeature
cfeature.LAND: NaturalEarthFeature
cfeature.OCEAN: NaturalEarthFeature
cfeature.LAKES: NaturalEarthFeature
cfeature.RIVERS: NaturalEarthFeature
Berikut ilustrasi alur kerja pembuatan peta dengan Cartopy:
Mereprojeksi Data Cuaca ke Peta
Ini adalah sumber kesalahan paling umum bagi pemula: lupa menyertakan parameter transform. Ketika kita punya array suhu ERA5 dalam grid lat/lon dan ingin memetakannya ke display projection apa pun, kita harus memberi tahu Cartopy bahwa data aslinya dalam PlateCarree.
Caranya: ax.pcolormesh(lon, lat, T, transform=ccrs.PlateCarree()). Tanpa transform, Cartopy menganggap koordinat data sudah dalam native projection — hasilnya data akan terplot di posisi geografis yang salah.
Parameter transform ini berlaku untuk semua fungsi plot 2D: pcolormesh(), contourf(), contour(), maupun scatter(). Prinsipnya sama: data CRS selalu PlateCarree untuk gridded model data, display projection boleh apa saja.
Untuk memahami cara kerja reprojeksi secara numerik, kita bisa gunakan transform_point(). Method ini mengkonversi satu titik (lon, lat) dari satu CRS ke CRS lain — berguna untuk debugging atau memverifikasi transformasi koordinat:
import cartopy.crs as ccrs
cities = [
("Jakarta", 106.85, -6.21),
("Medan", 98.67, 3.59),
("Denpasar", 115.22, -8.65),
]
src_crs = ccrs.PlateCarree()
tgt_crs = ccrs.Orthographic(central_longitude=120, central_latitude=-2)
print("Transformasi koordinat: PlateCarree → Orthographic (center: 120°E, 2°S)")
print(f"{'Kota':<12} {'Lon':>8} {'Lat':>7} → {'X (m)':>14} {'Y (m)':>14}")
print("-" * 60)
for city, lon, lat in cities:
x, y = tgt_crs.transform_point(lon, lat, src_crs)
print(f"{city:<12} {lon:>8.2f}° {lat:>6.2f}° → {x:>14.0f} {y:>14.0f}")
Transformasi koordinat: PlateCarree → Orthographic (center: 120°E, 2°S)
Kota Lon Lat → X (m) Y (m)
------------------------------------------------------------
Jakarta 106.85° -6.21° → -1442519 -474036
Medan 98.67° 3.59° → -2315425 606072
Denpasar 115.22° -8.65° → -525445 -739379
Hasil transform_point() dalam satuan meter — itulah native coordinate system dari Orthographic projection. Jakarta yang ada di barat-daya dari pusat proyeksi (120°E, 2°S) akan punya nilai X negatif dan Y negatif.
Contoh Lengkap Peta Suhu ERA5

Dalam workflow nyata, kita load data ERA5 dari file NetCDF menggunakan xarray — ds = xr.open_dataset("era5_temperature.nc") — lalu ambil array suhu, koordinat lat/lon-nya, dan plot dengan Cartopy. Untuk tutorial ini kita mock data tersebut dengan NumPy agar snippet bisa diverifikasi di sandbox.
Data ERA5 suhu permukaan untuk kawasan Indonesia umumnya berkisar 25–30 °C dengan gradien yang dipengaruhi topografi dan konveksi. Snippet berikut membangun mock array yang bentuknya realistis:
import numpy as np
# Grid Indonesia: 21 titik lat (-15 s.d. 15), 31 titik lon (90 s.d. 145)
lat = np.linspace(-15, 15, 21)
lon = np.linspace(90, 145, 31)
LON, LAT = np.meshgrid(lon, lat)
# Mock suhu permukaan (°C): hangat di ekuator, sedikit modulasi longitudinal
T = 28 - 0.05 * np.abs(LAT) ** 2 + 0.5 * np.sin(np.deg2rad(LON * 2))
print(f"Shape array T: {T.shape} (lat x lon)")
print(f"Extent: lon {lon.min():.1f}°–{lon.max():.1f}°E, lat {lat.min():.1f}°–{lat.max():.1f}°")
print()
print("Statistik suhu permukaan mock ERA5 (°C):")
print(f" Min : {T.min():.2f}")
print(f" Max : {T.max():.2f}")
print(f" Mean : {T.mean():.2f}")
print(f" Std : {T.std():.2f}")
print()
Shape array T: (21, 31) (lat x lon)
Extent: lon 90.0°–145.0°E, lat -15.0°–15.0°
Statistik suhu permukaan mock ERA5 (°C):
Min : 16.25
Max : 28.00
Mean : 23.53
Std : 3.68
Untuk merender peta lokal, gunakan matplotlib + Cartopy seperti berikut:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()})
ax.set_extent([90, 145, -15, 15], crs=ccrs.PlateCarree())
mesh = ax.pcolormesh(LON, LAT, T, transform=ccrs.PlateCarree(), cmap='RdYlBu_r')
ax.add_feature(cfeature.COASTLINE, linewidth=0.5)
ax.add_feature(cfeature.BORDERS, linewidth=0.3)
gl = ax.gridlines(draw_labels=True)
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
plt.colorbar(mesh, ax=ax, label='Suhu (°C)')
plt.show()
Untuk workflow ERA5 yang sesungguhnya menggunakan Cartopy dan xarray, kita juga perlu menambahkan gridlines berlabel. Pertama, import LONGITUDE_FORMATTER dan LATITUDE_FORMATTER dari cartopy.mpl.gridliner. Selanjutnya, panggil gl = ax.gridlines(draw_labels=True) lalu set gl.xformatter = LONGITUDE_FORMATTER dan gl.yformatter = LATITUDE_FORMATTER. Cartopy 0.25 sudah mendukung label gridline untuk hampir semua proyeksi.
Satu hal yang perlu diperhatikan: untuk display projection non-PlateCarree seperti ccrs.LambertConformal() — yang banyak dipakai untuk peta meteorologi operasional — pastikan setiap call pcolormesh() atau contourf() selalu menyertakan transform=ccrs.PlateCarree(). Ini adalah aturan yang sama, berlaku konsisten.
Langkah Selanjutnya
Setelah memahami fondasi CRS, GeoAxes, dan parameter transform, langkah selanjutnya adalah mengaplikasikannya ke data riil. Untuk download ERA5, gunakan cdsapi dari ECMWF — request data dalam format NetCDF, lalu buka dengan xr.open_dataset(). Untuk data GFS real-time, xarray.open_dataset() via URL OPeNDAP dari NOMADS juga bekerja langsung.
Untuk analisis meteorologis yang lebih dalam, pertimbangkan integrasi MetPy dari Unidata. MetPy bekerja di atas Cartopy untuk handle proyeksi dan transform, sehingga kita bisa fokus pada kalkulasi meteorologi — dewpoint, CAPE, vorticity — tanpa harus mengurus CRS secara manual.
Beberapa proyeksi yang layak dieksplorasi berikutnya: ccrs.Geostationary(central_longitude=140.7) untuk data Himawari-9, ccrs.TransverseMercator() untuk peta lokal resolusi tinggi, dan ccrs.NorthPolarStereo() untuk analisis polar. Referensi lengkap semua 40+ proyeksi ada di Cartopy projection list.
Untuk tutorial yang lebih mendalam, Project Pythia's Cartopy tutorial adalah referensi komunitas yang sangat baik — mencakup gridlines, quiver plots untuk angin, dan overlaying station data di atas model output.
Eksplorasi artikel meteorologi lainnya di meteo.my.id — kunjungi https://meteo.my.id untuk telusur lebih lanjut.
Referensi
- Cartopy Documentation — SciTools — dokumentasi resmi Cartopy, library Python geospatial mapping yang dikembangkan oleh UK Met Office.
- Introduction to Cartopy — Project Pythia — tutorial komunitas geosains yang komprehensif mencakup GeoAxes, proyeksi, dan feature overlay.
- Cartopy Projection Reference — referensi lengkap 40+ proyeksi kartografis yang tersedia di
cartopy.crs. - Tutorial on Climatologies using C3S Climate Data — ECMWF — tutorial resmi ECMWF/C3S menggunakan Cartopy dan xarray untuk visualisasi data ERA5.
- Getting Started with MetPy — Unidata — panduan MetPy menjelaskan integrasi dengan Cartopy untuk pembuatan peta meteorologi operasional.
Mengapa Reanalysis Penting untuk Riset Iklim
Jaringan observasi meteorologi tidak pernah benar-benar lengkap. Stasiun cuaca terkonsentrasi di daratan berpenduduk, radiosonde cuma diluncurkan dua kali sehari di lokasi terbatas, dan samudra yang luas hampir sepenuhnya tidak terpantau secara langsung. Akibatnya, setiap analisis iklim yang mengandalkan data observasi mentah selalu menyisakan gap spasial dan temporal yang besar.
Di sinilah reanalysis berperan. Pendekatan ini menggabungkan model NWP dengan observasi historis lewat data assimilation, menghasilkan rekaman state atmosfer global yang lengkap, konsisten secara fisis, dan homogen sepanjang waktu — bukan observasi langsung, melainkan estimasi terbaik kondisi atmosfer dari semua data yang tersedia pada setiap saat.
ERA5 adalah reanalysis generasi kelima dari ECMWF, mencakup Januari 1940 sampai sekarang pada resolusi temporal satu jam dan grid ~31 km global. Buat peneliti data atmosfer Indonesia — pola monsun, extreme weather events, tren suhu jangka panjang — ERA5 hampir wajib jadi titik awal.
Tutorial ini memandu kita dari nol: cara akses ERA5, memahami struktur datanya, dan menguasai operasi xarray yang paling sering dipakai di workflow iklim sehari-hari.
Apa Itu ERA5 dan Bagaimana Cara Kerjanya
ERA5 dibangun di atas Integrated Forecast System (IFS CY41R2) milik ECMWF dengan metode 4D-Var data assimilation. Pada 4D-Var, selisih antara prediksi model dan observasi diminimalkan selama time window 12 jam, sehingga kondisi awal atmosfer yang dihasilkan seoptimal mungkin secara fisis.
Jumlah observasi yang di-assimilate terus naik seiring waktu: dari sekitar 750.000 data per hari pada 1979 jadi sekitar 24 juta data per hari pada 2018. Sumber observasinya mencakup radiosonde, pesawat (AMDAR), satelit cuaca, ocean buoy, sampai GPS radio occultation. Semua jenis observasi ini di-merge jadi satu produk yang koheren.
Beberapa fakta teknis utama ERA5 yang perlu diketahui:
- Resolusi horizontal: ~31 km (0,25° × 0,25° pada grid interpolasi atmosfer, 0,5° × 0,5° untuk ocean wave) — tersedia di Copernicus CDS.
- Resolusi vertikal: 137 level dari permukaan sampai ~80 km.
- Temporal: data per jam, tersedia dari Januari 1940.
- Lisensi: Creative Commons Attribution 4.0 (CC-BY 4.0) — bebas dipakai untuk riset selama ada atribusi.
- Format distribusi: GRIB asli, atau NetCDF lewat antarmuka CDS.
Selain ERA5 utama, ada dua varian: ERA5-Land dengan resolusi ~9 km khusus variabel permukaan tanah, dan ERA5T (near-real-time) dengan latensi ~5 hari namun masih sementara. ERA5 menggantikan ERA-Interim yang di-discontinue pada 31 Agustus 2019. Paper referensi utamanya adalah Hersbach et al. (2020).
Pilihan Akses Data ERA5
Ada tiga jalur utama untuk akses ERA5:
1. Portal web Copernicus CDS — kunjungi cds.climate.copernicus.eu, pilih variabel, rentang waktu, dan area, lalu download manual. Cocok untuk eksplorasi awal tanpa nulis kode.
2. CDS API via cdsapi — jalur programatik buat workflow otomatis. Daftar akun CDS, terima lisensi data, simpan API key di ~/.cdsapirc. Detail di How to download ERA5.
3. Cloud mirror dan arsip — beberapa institusi menyediakan ERA5 di platform cloud (misalnya Google Cloud Storage untuk era5 zarr), jadi akses tanpa download file besar dulu.
Snippet berikut menunjukkan bentuk request cdsapi yang khas — versi multi-hari yang lebih besar dari yang nanti benar-benar kita jalankan di bagian akhir artikel ini. Snippet versi pendek (1 hari, 4 timestep) yang beneran dieksekusi muncul di bagian "Mengunduh Data ERA5 Asli" di bawah:
import cdsapi
c = cdsapi.Client()
c.retrieve(
"reanalysis-era5-single-levels",
{
"product_type": "reanalysis",
"variable": ["2m_temperature"],
"year": "2024",
"month": "01",
"day": [f"{d:02d}" for d in range(1, 8)],
"time": ["00:00", "06:00", "12:00", "18:00"],
"area": [6, 95, -11, 141], # North, West, South, East — Indonesia
"format": "netcdf",
},
"era5_t2m_indonesia_202401.nc",
)
Dataset identifier utamanya adalah reanalysis-era5-single-levels untuk surface variable dan reanalysis-era5-pressure-levels untuk variabel pada pressure level.
Menyiapkan Lingkungan Python
Sebelum mulai, pastikan xarray, NumPy, dan pandas sudah ter-install. Snippet berikut import ketiganya dan print versinya masing-masing sebagai sanity check:
import xarray as xr
import numpy as np
import pandas as pd
print(f"xarray : {xr.__version__}")
print(f"NumPy : {np.__version__}")
print(f"pandas : {pd.__version__}")
xarray : 2026.4.0
NumPy : 2.4.4
pandas : 3.0.2
Kalau versi xarray yang ter-install masih 0.x, pertimbangkan upgrade ke versi 2.x — API selection dan indexing sudah jauh diperbaiki di sana.
Memuat Dataset Latihan untuk Eksplorasi
Sebelum menyentuh data sebenarnya, kita mulai dulu dari Dataset sintetis yang bentuknya mirip ERA5. Pendekatan ini berguna karena snippet-snippet sintetis di sini reproducible — bisa dijalankan offline, di mesin manapun, tanpa API key. Setelah pola-nya kebayang, kita lanjut ke real download ERA5 di bagian "Mengunduh Data ERA5 Asli dengan cdsapi".
xarray menyediakan dua data structure utama: DataArray (labeled N-dim array, mirip pandas.Series) dan Dataset (container mirip dict yang menyimpan beberapa DataArray dengan dimensi yang aligned, mirip pandas.DataFrame). Data model ini diturunkan langsung dari format NetCDF yang dipakai luas di earth science.
np.random.seed(42)
# Koordinat — menyerupai ERA5 area Indonesia
time = pd.date_range("2024-01-01", periods=24, freq="6h")
latitude = np.linspace(-10.0, 10.0, 11) # 11 titik, berpusat di ekuator
longitude = np.linspace(95.0, 141.0, 24) # 24 titik, batas barat-timur Indonesia
# Suhu 2 m: gradien lintang tropis + gangguan acak kecil
lat_grid = latitude[np.newaxis, :, np.newaxis] # shape (1, 11, 1)
t2m_data = (
273.15 + 28.0
- 2.0 * np.abs(lat_grid) # lebih hangat di ekuator
+ np.random.normal(0, 0.5, (24, 11, 24)) # time, lat, lon
).astype("float32")
ds = xr.Dataset(
{
"t2m": xr.DataArray(
t2m_data,
dims=["time", "latitude", "longitude"],
attrs={
"long_name": "2 metre temperature",
"units": "K",
"source": "synthetic ERA5-like tutorial data",
},
)
},
coords={
"time": time,
"latitude": latitude,
"longitude": longitude,
},
attrs={"title": "Synthetic ERA5-like dataset for tutorial purposes"},
)
print(ds)
<xarray.Dataset> Size: 26kB
Dimensions: (time: 24, latitude: 11, longitude: 24)
Coordinates:
* time (time) datetime64[us] 192B 2024-01-01 ... 2024-01-06T18:00:00
* latitude (latitude) float64 88B -10.0 -8.0 -6.0 -4.0 ... 4.0 6.0 8.0 10.0
* longitude (longitude) float64 192B 95.0 97.0 99.0 ... 137.0 139.0 141.0
Data variables:
t2m (time, latitude, longitude) float32 25kB 281.4 281.1 ... 281.2
Attributes:
title: Synthetic ERA5-like dataset for tutorial purposes
Dataset yang dihasilkan punya satu variabel t2m (suhu 2 meter dalam Kelvin) dengan tiga dimensi: time (24 timestep × 6 jam = 6 hari), latitude (11 titik), dan longitude (24 titik). Persis bentuk yang akan kita temui pas buka file ERA5 NetCDF aslinya nanti.
Menjelajahi Dimensi, Koordinat, dan Variabel
Sebelum analisis, selalu wise untuk inspect struktur Dataset dulu. Data model xarray membedakan tiga konsep penting: dims (nama dimensi dan ukurannya), coords (nilai koordinat yang menempel pada dimensi), dan data_vars (N-dimensional data variable).
print("=== Dimensi ===")
print(dict(ds.dims))
print("\n=== Koordinat ===")
print("time :", ds.coords["time"].values[:4], "...")
print("lat :", ds.coords["latitude"].values)
print("lon :", ds.coords["longitude"].values[[0, -1]], "(first & last)")
print("\n=== Variabel data ===")
print(list(ds.data_vars))
print("\n=== Atribut variabel t2m ===")
print(ds["t2m"].attrs)
print("\n=== Bentuk array t2m ===")
print("shape:", ds["t2m"].shape, " dtype:", ds["t2m"].dtype)
=== Dimensi ===
{'time': 24, 'latitude': 11, 'longitude': 24}
=== Koordinat ===
time : ['2024-01-01T00:00:00.000000' '2024-01-01T06:00:00.000000'
'2024-01-01T12:00:00.000000' '2024-01-01T18:00:00.000000'] ...
lat : [-10. -8. -6. -4. -2. 0. 2. 4. 6. 8. 10.]
lon : [ 95. 141.] (first & last)
=== Variabel data ===
['t2m']
=== Atribut variabel t2m ===
{'long_name': '2 metre temperature', 'units': 'K', 'source': 'synthetic ERA5-like tutorial data'}
=== Bentuk array t2m ===
shape: (24, 11, 24) dtype: float32
Perhatikan ds["t2m"] mengembalikan DataArray, bukan array NumPy biasa. Bedanya yang penting: DataArray membawa metadata dimensi dan koordinat, jadi operasi seperti sel() dan resample() bisa bekerja berdasarkan label, bukan integer index.
Operasi Inti — Selection, Aggregation, dan Resampling
Selection dan Spatial Mean
sel() membiarkan kita pilih data langsung pakai nilai koordinat. Untuk pilih area Indonesia bagian barat (Sumatra dan sekitarnya), kita batasi lintang antara -6° sampai 6° dan bujur 95° sampai 108°, lalu hitung mean spasial dan temporalnya:
# Seleksi sub-area Sumatra
sumatera = ds["t2m"].sel(
latitude=slice(-6.0, 6.0),
longitude=slice(95.0, 108.0),
)
print("Shape setelah seleksi:", sumatera.shape)
# Rata-rata spasial (mean atas lat dan lon)
spatial_mean = sumatera.mean(dim=["latitude", "longitude"])
print("\nRata-rata spasial per langkah waktu (K):")
print(spatial_mean.values.round(3))
# Rata-rata temporal keseluruhan (skalar)
overall_mean = sumatera.mean().item()
print(f"\nRata-rata keseluruhan: {overall_mean:.3f} K ({overall_mean - 273.15:.2f} °C)")
# Nilai min dan maks dalam seleksi
print(f"Min: {sumatera.min().item():.3f} K Max: {sumatera.max().item():.3f} K")
Shape setelah seleksi: (24, 7, 7)
Rata-rata spasial per langkah waktu (K):
[294.243 294.346 294.346 294.46 294.362 294.363 294.325 294.282 294.274
294.364 294.393 294.399 294.281 294.294 294.207 294.329 294.041 294.219
294.247 294.239 294.182 294.246 294.257 294.29 ]
Rata-rata keseluruhan: 294.291 K (21.14 °C)
Min: 287.848 K Max: 302.437 K
Resampling dan Monthly Aggregation
Reanalysis ERA5 tersedia per jam; di praktiknya kita sering aggregate ke frekuensi yang lebih kasar. resample() bekerja mirip pandas.resample() tapi di atas dimensi waktu xarray:
# Resample dari 6-jam ke harian (rata-rata)
t2m_daily = ds["t2m"].resample(time="1D").mean()
print("Shape harian:", t2m_daily.shape)
print("Rata-rata harian (rata-rata spasial per hari, K):")
daily_spatial = t2m_daily.mean(dim=["latitude", "longitude"])
for t, v in zip(t2m_daily.time.values, daily_spatial.values):
print(f" {str(t)[:10]} : {v:.3f} K")
# Groupby bulan — satu bulan di dataset ini (Januari)
print("\nStatistik groupby bulan:")
monthly = ds["t2m"].groupby("time.month").mean()
for m, v in zip(monthly.month.values, monthly.mean(dim=["latitude", "longitude"]).values):
print(f" Bulan {m:02d} : {v:.3f} K")
Shape harian: (6, 11, 24)
Rata-rata harian (rata-rata spasial per hari, K):
2024-01-01 : 290.257 K
2024-01-02 : 290.262 K
2024-01-03 : 290.238 K
2024-01-04 : 290.232 K
2024-01-05 : 290.232 K
2024-01-06 : 290.222 K
Statistik groupby bulan:
Bulan 01 : 290.240 K
Pola resample().mean() dan groupby().mean() ini persis sama dengan yang nanti kita pakai di file ERA5 asli berukuran gigabyte — xarray meng-handle time indexing otomatis tanpa kode tambahan.
Mengunduh Data ERA5 Asli dengan cdsapi
Sekarang giliran the real thing: request data ERA5 langsung dari CDS, download ke file lokal, lalu apply workflow yang sama yang baru kita pelajari di atas. Pastikan API key sudah ada di ~/.cdsapirc sebelum menjalankan snippet ini — sandbox tutorial ini sudah menganggap credential-nya valid.
Snippet pertama kirim request ke CDS untuk variabel 2m_temperature selama satu hari (1 Januari 2024) dengan empat timestep, dibatasi ke wilayah Indonesia. Request bisa nge-queue di server CDS selama puluhan detik sampai beberapa menit; makanya snippet ini diberi timeout=600 biar cukup waktu buat queue + download.
import os
import cdsapi
OUT = "era5_t2m_indonesia_20240101.nc"
if not os.path.exists(OUT):
client = cdsapi.Client(quiet=True)
client.retrieve(
"reanalysis-era5-single-levels",
{
"product_type": "reanalysis",
"variable": ["2m_temperature"],
"year": "2024",
"month": "01",
"day": "01",
"time": ["00:00", "06:00", "12:00", "18:00"],
"area": [6, 95, -11, 141], # N, W, S, E — kotak Indonesia
"format": "netcdf",
},
OUT,
)
size_kb = os.path.getsize(OUT) / 1024
print(f"Downloaded : {OUT}")
print(f"Size : {size_kb:.1f} KB")
Downloaded : era5_t2m_indonesia_20240101.nc
Size : 105.2 KB
Setelah file tersedia, kita buka pakai xarray dan apply operasi yang sama persis dengan yang kita pakai di Dataset sintetis sebelumnya. Satu hal penting: di file ERA5 modern, dimensi latitude di-order dari positif ke negatif (utara ke selatan), jadi argumen slice() ditulis dari nilai lebih besar ke lebih kecil. Snippet di bawah mendeteksi order-nya otomatis biar sama-sama bekerja di konvensi mana pun.
import xarray as xr
ds_era5 = xr.open_dataset(OUT)
print(ds_era5)
# Pilih nama variabel suhu (file ERA5 baru kadang menamainya "t2m",
# kadang prefix lain — kita ambil variabel data pertama jika "t2m" tidak ada).
var_name = "t2m" if "t2m" in ds_era5 else next(iter(ds_era5.data_vars))
t2m = ds_era5[var_name]
# Deteksi urutan latitude
lat = ds_era5["latitude"].values
lat_slice = slice(6.0, -6.0) if lat[0] > lat[-1] else slice(-6.0, 6.0)
sumatera = t2m.sel(latitude=lat_slice, longitude=slice(95.0, 108.0))
mean_K = float(sumatera.mean())
print(f"\nShape setelah seleksi : {sumatera.shape}")
print(f"Rata-rata Sumatra (K) : {mean_K:.3f}")
print(f"Rata-rata Sumatra (°C): {mean_K - 273.15:.2f}")
print(f"Min : {float(sumatera.min()):.3f} K Max : {float(sumatera.max()):.3f} K")
<xarray.Dataset> Size: 206kB
Dimensions: (valid_time: 4, latitude: 69, longitude: 185)
Coordinates:
* valid_time (valid_time) datetime64[ns] 32B 2024-01-01 ... 2024-01-01T18:...
expver (valid_time) <U4 64B ...
* latitude (latitude) float64 552B 6.0 5.75 5.5 5.25 ... -10.5 -10.75 -11.0
* longitude (longitude) float64 1kB 95.0 95.25 95.5 ... 140.5 140.8 141.0
number int64 8B ...
Data variables:
t2m (valid_time, latitude, longitude) float32 204kB ...
Attributes:
GRIB_centre: ecmf
GRIB_centreDescription: European Centre for Medium-Range Weather Forecasts
GRIB_subCentre: 0
Conventions: CF-1.7
institution: European Centre for Medium-Range Weather Forecasts
history: 2026-05-09T17:41 GRIB to CDM+CF via cfgrib-0.9.1...
Shape setelah seleksi : (4, 49, 53)
Rata-rata Sumatra (K) : 300.024
Rata-rata Sumatra (°C): 26.87
Min : 289.247 K Max : 305.995 K
Inilah indahnya xarray: kode untuk file ERA5 asli hampir identik dengan kode untuk Dataset sintetis. Cuma satu check kecil soal order dimensi latitude yang bedain keduanya — sisanya pola yang sama (sel, mean, resample, groupby) yang sudah kita pelajari.
Untuk dataset ERA5 berskala besar (bulanan atau tahunan), pertimbangkan xr.open_mfdataset() yang membuka banyak file NetCDF sekaligus dan meng-concatenate-nya secara lazy di sepanjang dimensi waktu.
Langkah Lanjut dan Sumber Belajar
Tutorial ini sudah mencakup workflow dasar yang menutupi hampir semua kebutuhan analisis ERA5 sehari-hari: memahami struktur Dataset, lakukan spatial dan temporal selection, dan aggregate data ke frekuensi yang diinginkan.
Langkah alami berikutnya:
- Download data ERA5 pertama kita — daftar di cds.climate.copernicus.eu, pasang API key, jalankan snippet cdsapi di bagian akses di atas.
- Pelajari
xr.open_mfdataset()— untuk analisis multi-tahun, jauh lebih efisien daripada buka file satu-satu. - Eksplorasi API xarray lebih dalam —
interp()untuk interpolasi,where()untuk conditional masking,apply_ufunc()untuk custom function NumPy. - Analisis climate anomaly — hitung temperature anomaly dengan
groupby("time.month") - climatology. - Baca dokumentasi resmi — xarray Data Structures dan xarray.tutorial.open_dataset.
Eksplorasi artikel meteorologi lainnya di meteo.my.id — ada panduan ERA5, GFS, analisis BMKG, dan teknik visualisasi atmosfer di https://meteo.my.id.
Referensi
- Hersbach et al. (2020): The ERA5 global reanalysis — Paper komprehensif yang mendeskripsikan seluruh sistem ERA5, terbit di Quarterly Journal of the Royal Meteorological Society.
- ERA5: data documentation — Copernicus Knowledge Base — Dokumentasi teknis resmi ECMWF tentang resolusi, level, variabel, dan metode data assimilation ERA5.
- ERA5 hourly data on single levels from 1940 to present — Copernicus CDS — Halaman dataset CDS yang mencantumkan variabel tersedia, lisensi CC-BY 4.0, dan link download.
- How to download ERA5 — Copernicus Knowledge Base — Panduan resmi install cdsapi dan contoh request download programatik.
- Data Structures — xarray documentation — Penjelasan mendalam soal DataArray dan Dataset sebagai fondasi data model xarray.
Sabtu, 09 Mei 2026
Apa itu ENSO
ENSO — singkatan dari El Niño–Southern Oscillation — adalah sistem interaksi antara lautan dan atmosfer di Pasifik tropis yang menjadi penentu iklim paling berpengaruh di Bumi (NOAA Climate.gov). Sistem ini bergerak antara dua fase berlawanan: El Niño dan La Niña.
El Niño adalah fase hangat ENSO. Anomali suhu permukaan laut (SST) di Pasifik tropis tengah dan timur mencapai \(+0{,}5°C\) atau lebih di atas rata-rata, dan kondisi ini harus bertahan minimal lima periode tiga-bulan berurutan agar dinyatakan sebagai peristiwa resmi (NOAA Climate.gov).
La Niña adalah kebalikannya — fase dingin ENSO. Sebagian besar Pasifik tropis lebih dingin dari rata-rata, angin perdagangan (trade winds) menguat, dan curah hujan meningkat di atas Indonesia sekaligus berkurang di Pasifik tengah (NOAA Climate.gov).
Kedua fase ini muncul secara tidak teratur setiap dua hingga tujuh tahun (NOAA Climate.gov — ONI), mengubah sirkulasi atmosfer secara global dan berdampak langsung pada jutaan orang di kawasan Asia Tenggara, termasuk Indonesia.
Mekanisme Sirkulasi Walker dan Perubahan Angin Perdagangan
Untuk memahami El Niño dan La Niña, Kita perlu memahami lebih dulu kondisi normal Pasifik tropis.
Dalam kondisi normal, trade winds bertiup dari timur ke barat di sepanjang khatulistiwa, mendorong massa air hangat ke arah barat. Akibatnya, air hangat terakumulasi di sekitar Indonesia dan Filipina, sementara di Amerika Selatan terjadi upwelling — naiknya air dingin dari kedalaman laut. Perbedaan suhu ini menggerakkan Sirkulasi Walker: udara hangat dan lembap di Pasifik barat naik membentuk awan konvektif, bergerak ke timur di lapisan atas atmosfer, turun di Pasifik timur yang lebih dingin, lalu kembali ke barat sebagai trade winds di permukaan (NASA Science).
Saat El Niño, trade winds melemah — bahkan kadang berbalik arah menjadi angin barat. Massa air hangat yang biasanya terkonsentrasi di Pasifik barat pun bergerak ke arah timur. Awan konvektif dan curah hujan ikut berpindah, meninggalkan Pasifik barat — termasuk Indonesia — dalam kondisi kering (NASA Science). Sirkulasi Walker pun melemah atau bahkan terdistorsi, memicu anomali cuaca di berbagai penjuru dunia.
Saat La Niña, trade winds menguat lebih dari biasanya. Air hangat semakin terdorong ke Pasifik barat, konveksi di atas Indonesia intensif, dan curah hujan meningkat tajam (NOAA Climate.gov).

Sumber: NOAA Climate.gov
Bagaimana ENSO Diukur
Para ilmuwan tidak hanya mengandalkan satu indeks untuk memantau ENSO, karena fenomena ini bersifat multifaceted — melibatkan berbagai aspek lautan dan atmosfer sekaligus (NOAA Climate.gov — Indices).
Oceanic Niño Index (ONI) adalah indeks utama yang digunakan NOAA. ONI dihitung dari rata-rata bergulir tiga-bulan anomali SST di wilayah Niño 3.4 (120°W–170°W, di Pasifik tropis tengah). El Niño dinyatakan ketika ONI mencapai \(+0{,}5°C\) atau lebih; La Niña ketika ONI mencapai \(-0{,}5°C\) atau lebih rendah (NOAA Climate.gov — ONI).
Southern Oscillation Index (SOI) adalah indikator ENSO tertua, diturunkan dari perbedaan tekanan permukaan laut yang distandardisasi antara Tahiti dan Darwin, Australia (NOAA Climate.gov — Indices). Saat El Niño, SOI bernilai negatif; saat La Niña, SOI bernilai positif. Rekaman SOI membentang hingga akhir 1800-an, menjadikannya alat berharga untuk kajian iklim historis.
Penggunaan beberapa indeks sekaligus memberikan gambaran yang lebih lengkap. Seperti yang dicatat NOAA, "Kita tidak bisa mengukur satu aspek dari seluruh Pasifik tropis secara sempurna, sehingga gambaran yang lebih baik diperoleh dengan mempertimbangkan beberapa ukuran terkait" (NOAA Climate.gov — Indices).

Sumber: NOAA Climate.gov
Dampak ENSO terhadap Cuaca dan Iklim Indonesia
Indonesia berada di jantung Pasifik barat, sehingga menjadi salah satu wilayah yang paling terdampak oleh ENSO di seluruh dunia.
Saat El Niño, curah hujan di hampir seluruh Indonesia turun lebih dari 40% pada musim Juni–Agustus (JJA) dan September–November (SON) (BMKG CEWS). Dampak paling parah dirasakan di Nusa Tenggara, Jawa, dan Sulawesi. Musim kemarau menjadi lebih panjang dan lebih kering, meningkatkan risiko kekeringan dan kebakaran hutan secara drastis (BMKG CEWS).
Contoh paling dramatis adalah El Niño 2015–2016. Kondisi kering ekstrem memicu kebakaran hutan dan lahan di Sumatera dan Kalimantan yang luar biasa. Penelitian dari ilmuwan Harvard University memperkirakan kebakaran-kebakaran tersebut berkontribusi pada sebanyak 100.000 kematian (NASA Science).
Sebaliknya, saat La Niña, sebagian besar Indonesia mengalami peningkatan curah hujan sebesar 20–40% pada periode JJA dan SON, dengan peningkatan yang berlanjut di Indonesia timur hingga musim Desember–Februari dan Maret–Mei (BMKG CEWS). La Niña 2010 membawa curah hujan sangat tinggi di Sumatera selatan, Jawa, Bali, Nusa Tenggara, Sulawesi, Maluku, dan sebagian Kalimantan — memperpanjang musim hujan dan meningkatkan risiko banjir serta tanah longsor.

Sumber: NASA Earth Observatory / MODIS
Memantau ENSO Saat Ini
Memantau fase ENSO yang sedang berlangsung sangat penting untuk menyiapkan diri menghadapi potensi anomali cuaca musiman. Beberapa sumber data yang dapat Kita gunakan secara langsung:
NOAA Climate Prediction Center (CPC) menyediakan data ONI dan SOI terkini, termasuk pembaruan rutin status El Niño atau La Niña serta prospek iklim musiman global. Data dapat diakses di https://www.cpc.ncep.noaa.gov/.
NOAA Climate.gov menerbitkan ringkasan status ENSO bulanan dalam format naratif yang mudah dipahami, dilengkapi grafik ONI historis dan proyeksi. Kunjungi bagian "ENSO: Recent Evolution, Current Status, and Predictions" untuk pembaruan terkini.
BMKG CEWS (Climate Early Warning System) menyediakan prakiraan dan peringatan dini iklim berbasis ENSO yang dikhususkan untuk wilayah Indonesia. Portal ini menampilkan analisis dampak ENSO per wilayah dan musim, berguna langsung bagi pengelola pertanian, kehutanan, dan penanggulangan bencana. Akses di https://cews.bmkg.go.id/.
Untuk interpretasi: ketika ONI berada di bawah \(-0{,}5°C\) selama beberapa periode berurutan, Indonesia sebaiknya bersiap menghadapi curah hujan di atas normal — terutama di luar musim hujan biasa. Sebaliknya, ketika ONI di atas \(+0{,}5°C\), waspadai potensi kekeringan, terutama di Jawa, Nusa Tenggara, dan Sulawesi.
Eksplorasi artikel meteorologi lainnya di meteo.my.id untuk memperdalam pemahaman Kita tentang fenomena cuaca dan iklim yang memengaruhi kehidupan sehari-hari. Kunjungi https://meteo.my.id untuk selengkapnya.
Referensi
- El Niño and La Niña: Frequently asked questions | NOAA Climate.gov — Panduan komprehensif NOAA tentang definisi, mekanisme fisik, dan dampak global El Niño dan La Niña, termasuk pengaruhnya terhadap curah hujan Indonesia.
- Climate Variability: Oceanic Niño Index | NOAA Climate.gov — Penjelasan mendalam tentang ONI sebagai indeks pemantauan utama ENSO, cara perhitungan, dan ambang batas \(\pm0{,}5°C\) yang digunakan untuk menetapkan fase El Niño atau La Niña.
- Why are there so many ENSO indexes, instead of just one? | NOAA Climate.gov — Penjelasan tentang SOI, wilayah Niño 3.4, dan alasan ilmuwan menggunakan beberapa indeks sekaligus untuk mendapatkan gambaran ENSO yang lebih lengkap.
- El Niño | NASA Science — Ulasan NASA tentang mekanisme Walker circulation, peran trade winds, dan dampak regional El Niño termasuk kebakaran hutan di Indonesia pada 2015–2016.
- La Nina, El Nino — Tentang ENSO | BMKG CEWS — Data kuantitatif BMKG tentang perubahan curah hujan Indonesia akibat ENSO per musim dan per wilayah, termasuk studi kasus El Niño 1997 dan La Niña 2010.

Sumber: NASA Earth Observatory / ISS66E-037532 — domain publik.
Laju Adiabatik Kering dan Konsepnya
Ketika sebuah parcel udara terangkat ke atas — baik oleh orografi, konvergensi permukaan, maupun gaya apung — tekanan di sekelilingnya turun. Parcel meluas secara adiabatik, tanpa bertukar panas dengan lingkungan, sehingga suhunya menurun. Laju penurunan suhu ini, yang disebut dry adiabatic lapse rate (DALR), menjadi patokan dasar dalam termodinamika atmosfer.
Proses ini bersifat adiabatik karena perpindahan parcel berlangsung jauh lebih cepat dibandingkan laju difusi panas antara parcel dan lingkungan. Dalam konteks meteorologi sinoptik, asumsi adiabatik berlaku baik untuk parcel yang bergerak secara vertikal dalam skala waktu menit hingga jam. Akibatnya, satu-satunya sumber perubahan suhu parcel adalah kerja ekspansi atau kompresi — bukan pertukaran panas langsung.
Nilai DALR diturunkan langsung dari dua konstanta fisika: gravitational acceleration \(g\) dan specific heat at constant pressure \(C_p\) untuk udara kering. Hubungannya sederhana:
$$\Gamma_d \;=\; \frac{g}{C_p} \;\approx\; 9{,}8\ \text{K/km}$$
Artinya, setiap kali parcel udara kering naik sejauh 1 km, suhunya turun sekitar \(9{,}8\ \text{K}\). Nilai ini bersifat konstan selama udara belum jenuh — belum ada kondensasi, belum ada pelepasan latent heat. Konstanta \(C_p\) untuk udara kering bernilai \(1004\ \text{J/kg/K}\), sedangkan \(g\) bernilai \(9{,}80665\ \text{m/s}^2\). Pembagian keduanya langsung memberikan DALR dalam satuan K/m, yang kemudian kita konversi ke K/km untuk kemudahan interpretasi.
Mengapa hal ini penting? Apabila kita membandingkan DALR dengan environmental lapse rate (laju penurunan suhu lingkungan yang sebenarnya), kita dapat menentukan stability kolom atmosfer: apakah parcel yang terangkat akan terus naik (tidak stabil) atau kembali ke posisi awal (stabil). Lingkungan dengan lapse rate melebihi DALR disebut absolutely unstable; lapse rate di bawah SALR disebut absolutely stable; dan di antara keduanya berada dalam kondisi conditionally unstable — tidak stabil hanya jika udara sudah jenuh. Pemahaman ini mendasari analisis parcel ascent yang digunakan dalam pembuatan prognosis konvektif, pembacaan radiosonde, dan perhitungan CAPE.
Snippet berikut menghitung DALR secara langsung dari konstanta fisika menggunakan NumPy.
import numpy as np
# Konstanta fisika udara kering
g = 9.80665 # gravitational acceleration (m/s²)
Cp = 1004.0 # specific heat at constant pressure (J/kg/K)
# Dry adiabatic lapse rate
DALR = g / Cp # K/m
DALR_per_km = DALR * 1000 # K/km
print(f"g = {g} m/s²")
print(f"Cp = {Cp} J/kg/K")
print(f"DALR = g/Cp = {DALR:.5f} K/m")
print(f"DALR = {DALR_per_km:.2f} K/km")
g = 9.80665 m/s²
Cp = 1004.0 J/kg/K
DALR = g/Cp = 0.00977 K/m
DALR = 9.77 K/km
Hasil di atas mengkonfirmasi bahwa \(\Gamma_d \approx 9{,}77\ \text{K/km}\), yang sering dibulatkan menjadi \({\sim}9{,}8\ \text{K/km}\) dalam literatur meteorologi. Konstanta DALR ini akan kita gunakan ulang pada semua snippet berikutnya.
Simulasi Profil Suhu Parcel dan Lingkungan
Dengan DALR yang sudah kita hitung, langkah berikutnya adalah membangun profil vertikal suhu dari permukaan hingga ketinggian 6 km. Kita butuh dua profil: profil suhu parcel yang naik mengikuti DALR, dan profil suhu lingkungan dengan lapse rate yang lebih kecil — mencerminkan kondisi atmosfer stabil secara kondisional.
Profil parcel dihitung sebagai:
$$T_{\text{parcel}}(z) \;=\; T_{\text{surface}} - \Gamma_d \cdot z$$
Sedangkan profil lingkungan menggunakan environmental lapse rate misalnya sebesar \(6{,}5\ \text{K/km}\) — nilai mendekati rata-rata troposfer standar (International Standard Atmosphere):
$$T_{\text{env}}(z) \;=\; T_{\text{surface}} - \Gamma_e \cdot z$$
Perbedaan antara keduanya menentukan apakah parcel lebih hangat (dan lebih ringan) dari lingkungan, sehingga cenderung terus naik.
import numpy as np
# Reuse DALR dari snippet-1
g = 9.80665
Cp = 1004.0
DALR = g / Cp * 1000 # K/km
# Parameter permukaan
T_surface = 300.0 # suhu permukaan (K)
ELR = 6.5 # environmental lapse rate (K/km)
# Grid ketinggian 0–6 km, interval 500 m
z_km = np.arange(0, 6.5, 0.5) # km
# Profil suhu (K)
T_parcel = T_surface - DALR * z_km
T_env = T_surface - ELR * z_km
# Header tabel
print(f"{'z (km)':>7} | {'T_parcel (K)':>12} | {'T_env (K)':>10} | {'ΔT (K)':>8}")
print("-" * 48)
for z, tp, te in zip(z_km, T_parcel, T_env):
delta = tp - te
print(f"{z:7.1f} | {tp:12.2f} | {te:10.2f} | {delta:8.2f}")
z (km) | T_parcel (K) | T_env (K) | ΔT (K)
------------------------------------------------
0.0 | 300.00 | 300.00 | 0.00
0.5 | 295.12 | 296.75 | -1.63
1.0 | 290.23 | 293.50 | -3.27
1.5 | 285.35 | 290.25 | -4.90
2.0 | 280.46 | 287.00 | -6.54
2.5 | 275.58 | 283.75 | -8.17
3.0 | 270.70 | 280.50 | -9.80
3.5 | 265.81 | 277.25 | -11.44
4.0 | 260.93 | 274.00 | -13.07
4.5 | 256.05 | 270.75 | -14.70
5.0 | 251.16 | 267.50 | -16.34
5.5 | 246.28 | 264.25 | -17.97
6.0 | 241.39 | 261.00 | -19.61
Kolom \(\Delta T = T_{\text{parcel}} - T_{\text{env}}\) menunjukkan selisih suhu parcel terhadap lingkungan. Nilai positif berarti parcel lebih hangat dari lingkungan — parcel bersifat buoyant dan akan terus naik. Nilai negatif berarti parcel lebih dingin, menandakan kondisi stabil yang menghambat ascent lebih lanjut. Dari profil ini, kita juga dapat mulai melihat di ketinggian berapa parcel mulai lebih dingin dari lingkungan — yang menjadi dasar deteksi level keseimbangan (equilibrium level).
Perlu diperhatikan bahwa profil lingkungan (T_env) ini adalah profil sintetis dengan ELR konstan 6,5 K/km. Pada kondisi nyata, profil lingkungan berasal dari pengukuran radiosonde atau output model NWP, dan tidak selalu monotonik — bisa ada lapisan inversi suhu di mana suhu justru meningkat dengan ketinggian. Namun untuk membangun intuisi numeris mengenai hubungan DALR dan environmental lapse rate, profil sintetis ini sudah memadai.
Deteksi Level Kondensasi Angkat Numeris
Parcel yang naik secara kering (tanpa kondensasi) tidak selalu tetap kering. Seiring ketinggian bertambah, suhu parcel mendekati dewpoint — titik di mana uap air mulai terkondensasi menjadi droplet awan. Ketinggian di mana suhu parcel bertemu dengan dewpoint disebut lifting condensation level (LCL).
Untuk mendeteksinya secara numeris, kita bangun profil dewpoint yang menurun lebih lambat dari profil suhu parcel. Pendekatan umum: dewpoint menurun sekitar \(1{,}8\ \text{K/km}\) seiring kenaikan parcel (mengikuti aturan praktis untuk udara yang sedikit basah). Kemudian kita cari titik persilangan kedua profil dengan linear interpolation di antara dua titik grid yang mengapitnya.
import numpy as np
# Reuse DALR dan profil parcel dari snippet-2
g = 9.80665
Cp = 1004.0
DALR = g / Cp * 1000 # K/km
T_surface = 300.0
Td_surface = 294.0 # dewpoint di permukaan (K), RH ~70 %
ELR = 6.5 # environmental lapse rate (K/km)
DDR = 1.8 # dewpoint depression rate (K/km)
z_km = np.arange(0, 6.5, 0.5)
T_parcel = T_surface - DALR * z_km
T_dewpt = Td_surface - DDR * z_km
# Selisih: positif = parcel masih lebih hangat dari dewpoint (kering)
diff = T_parcel - T_dewpt
# Deteksi persilangan dengan interpolasi linear
lcl_km = None
for i in range(len(diff) - 1):
if diff[i] > 0 and diff[i + 1] <= 0:
# Interpolasi linear antar titik i dan i+1
frac = diff[i] / (diff[i] - diff[i + 1])
lcl_km = z_km[i] + frac * (z_km[i + 1] - z_km[i])
T_lcl = T_parcel[i] + frac * (T_parcel[i + 1] - T_parcel[i])
break
if lcl_km is not None:
print(f"Lifting Condensation Level (LCL) terdeteksi:")
print(f" Ketinggian LCL = {lcl_km:.2f} km")
print(f" Suhu parcel di LCL = {T_lcl:.2f} K ({T_lcl - 273.15:.2f} °C)")
else:
print("LCL tidak terdeteksi dalam rentang ketinggian yang dihitung.")
Lifting Condensation Level (LCL) terdeteksi:
Ketinggian LCL = 0.75 km
Suhu parcel di LCL = 292.64 K (19.49 °C)
Interpolasi linear antara dua titik grid memberikan estimasi LCL yang cukup akurat untuk keperluan analisis sinoptik. Semakin rapat grid ketinggian, semakin tepat estimasi LCL yang diperoleh. Untuk keperluan operasional yang membutuhkan presisi lebih tinggi, kita dapat menggunakan formula analitik Bolton (1980) yang menurunkan LCL langsung dari suhu permukaan dan dewpoint tanpa perlu grid ketinggian.
Pada kasus ini, dengan dewpoint permukaan sekitar \(294\ \text{K}\) (sekitar \(21\ ^\circ\text{C}\)) dan suhu permukaan \(300\ \text{K}\) (\(27\ ^\circ\text{C}\)), dewpoint depression di permukaan adalah \(6\ \text{K}\). Aturan praktis menyatakan bahwa LCL kira-kira berada pada ketinggian \(125\ \text{m} \times \text{dewpoint depression}\) dalam \(^\circ\text{C}\) — sehingga estimasi kasar kita sekitar \(750\ \text{m}\), yang konsisten dengan hasil interpolasi numeris. Cloud base yang berada di ketinggian \(1\text{–}2\ \text{km}\) adalah tipikal untuk awan kumulus di daerah tropis dengan kelembapan permukaan tinggi.
Perbandingan dengan Laju Adiabatik Jenuh
Setelah LCL terlampaui, kondensasi aktif terjadi dan latent heat dilepaskan ke dalam parcel. Pelepasan panas ini memperlambat laju pendinginan parcel. Kondisi ini dikenal sebagai pendinginan adiabatik jenuh, dengan saturated adiabatic lapse rate (\(\Gamma_s\)) berkisar antara \(5\text{–}6\ \text{K/km}\), jauh lebih kecil dari \(\Gamma_d\).
\(\Gamma_s\) bervariasi tergantung suhu dan tekanan — lebih kecil di troposfer bawah yang hangat karena kapasitas uap air lebih besar (sehingga lebih banyak latent heat yang dilepaskan per kilometer), dan mendekati \(\Gamma_d\) di troposfer atas yang dingin dan kering. Di daerah tropis dekat permukaan, \(\Gamma_s\) bisa serendah \(4\text{–}5\ \text{K/km}\), sementara di lapisan atas troposfer bisa mencapai \(7\text{–}8\ \text{K/km}\). Untuk analisis numeris sederhana ini, kita gunakan nilai representatif \(\Gamma_s = 5{,}5\ \text{K/km}\).
import numpy as np
# Reuse konstanta
g = 9.80665
Cp = 1004.0
DALR = g / Cp * 1000 # K/km
SALR = 5.5 # saturated adiabatic lapse rate (K/km)
T_surface = 300.0
Td_surface = 294.0
DDR = 1.8
z_km = np.arange(0, 6.5, 0.5)
T_parcel_dry = T_surface - DALR * z_km
T_dewpt = Td_surface - DDR * z_km
# Hitung LCL (interpolasi linear)
diff = T_parcel_dry - T_dewpt
lcl_km, T_lcl = None, None
for i in range(len(diff) - 1):
if diff[i] > 0 and diff[i + 1] <= 0:
frac = diff[i] / (diff[i] - diff[i + 1])
lcl_km = z_km[i] + frac * (z_km[i + 1] - z_km[i])
T_lcl = T_parcel_dry[i] + frac * (T_parcel_dry[i + 1] - T_parcel_dry[i])
break
# Profil parcel gabungan: kering di bawah LCL, jenuh di atas LCL
T_parcel_full = np.where(
z_km <= lcl_km,
T_surface - DALR * z_km,
T_lcl - SALR * (z_km - lcl_km)
)
print(f"LCL = {lcl_km:.2f} km | T_LCL = {T_lcl:.2f} K")
print()
print(f"{'z (km)':>7} | {'T_dry (K)':>9} | {'T_full (K)':>10} | {'Fase':>12} | {'Beda T (K)':>10}")
print("-" * 60)
for z, td, tf in zip(z_km, T_parcel_dry, T_parcel_full):
fase = "Kering" if z <= lcl_km else "Jenuh"
beda = td - tf
print(f"{z:7.1f} | {td:9.2f} | {tf:10.2f} | {fase:>12} | {beda:10.2f}")
LCL = 0.75 km | T_LCL = 292.64 K
z (km) | T_dry (K) | T_full (K) | Fase | Beda T (K)
------------------------------------------------------------
0.0 | 300.00 | 300.00 | Kering | 0.00
0.5 | 295.12 | 295.12 | Kering | 0.00
1.0 | 290.23 | 291.29 | Jenuh | -1.05
1.5 | 285.35 | 288.54 | Jenuh | -3.19
2.0 | 280.46 | 285.79 | Jenuh | -5.32
2.5 | 275.58 | 283.04 | Jenuh | -7.46
3.0 | 270.70 | 280.29 | Jenuh | -9.59
3.5 | 265.81 | 277.54 | Jenuh | -11.72
4.0 | 260.93 | 274.79 | Jenuh | -13.86
4.5 | 256.05 | 272.04 | Jenuh | -15.99
5.0 | 251.16 | 269.29 | Jenuh | -18.12
5.5 | 246.28 | 266.54 | Jenuh | -20.26
6.0 | 241.39 | 263.79 | Jenuh | -22.39
Kolom "Beda T" menunjukkan seberapa besar perbedaan antara profil kering dan profil gabungan (kering + jenuh). Di bawah LCL, kedua profil identik. Di atas LCL, profil jenuh lebih hangat karena latent heat memperlambat pendinginan — perbedaan ini semakin besar seiring ketinggian bertambah. Inilah inti dari mengapa awan konvektif yang dalam dapat mencapai troposfer atas: latent heat terus menyuplai energi ke parcel yang naik.
Langkah Selanjutnya dan Kesimpulan
Dari empat snippet di atas, kita telah menunjukkan secara numeris tiga konsep inti parcel ascent:
- \(\Gamma_d \approx 9{,}8\ \text{K/km}\) — laju pendinginan parcel udara kering, diturunkan langsung dari \(g/C_p\).
- LCL — ketinggian di mana suhu parcel menyentuh dewpoint, menandai awal pembentukan awan. Kita deteksi dengan interpolasi linear antara titik-titik grid NumPy.
- \(\Gamma_s \approx 5\text{–}6\ \text{K/km}\) — laju pendinginan jenuh di atas LCL, lebih lambat karena latent heat memperlambat penurunan suhu parcel.
Pendekatan numeris ini sengaja dibuat minimal — hanya NumPy, tanpa dependensi meteorologi khusus — agar mudah direplikasi dan dipahami secara konseptual. Dari sini, ada beberapa arah yang bisa kita eksplorasi selanjutnya: menghitung stability index seperti Lifted Index atau Showalter Index dari profil yang sama, menerapkan metode ini pada data radiosonde nyata (misalnya dari BMKG atau Wyoming Upper Air), atau memperluas simulasi untuk menghitung convective available potential energy (CAPE) sebagai ukuran potensi badai konvektif.
Eksplorasi artikel meteorologi lainnya di meteo.my.id. Kunjungi https://meteo.my.id untuk topik lebih lanjut seputar analisis atmosfer, NWP, dan data science cuaca.