@aeroswarm/weather-layer 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -0
- package/dist/index.js +30 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -232,6 +232,7 @@ declare class WeatherController {
|
|
|
232
232
|
private readonly options;
|
|
233
233
|
private current;
|
|
234
234
|
private series;
|
|
235
|
+
private window;
|
|
235
236
|
private readonly listeners;
|
|
236
237
|
private readonly unsubscribe;
|
|
237
238
|
private liveTimer;
|
|
@@ -249,6 +250,14 @@ declare class WeatherController {
|
|
|
249
250
|
* dari laporan.
|
|
250
251
|
*/
|
|
251
252
|
timeline(): Timeline | null;
|
|
253
|
+
/**
|
|
254
|
+
* Batasi timeline (slider dan putar) ke satu jendela waktu, mis. 24 jam hari ini atau 72 jam ke
|
|
255
|
+
* depan. `null` melepas batas. Waktu yang sedang tampil di luar jendela dikembalikan ke awalnya.
|
|
256
|
+
*/
|
|
257
|
+
setWindow(window: {
|
|
258
|
+
from: Date;
|
|
259
|
+
to: Date;
|
|
260
|
+
} | null): void;
|
|
252
261
|
/**
|
|
253
262
|
* Seri model berstandar `aeroswarm.weather-grid`: dipakai menggantikan medan dari laporan, dengan
|
|
254
263
|
* timeline sepanjang seri. `null` kembali ke laporan. Grid dari `setExternalFields` tetap menang.
|
package/dist/index.js
CHANGED
|
@@ -244,6 +244,7 @@ var WeatherController = class {
|
|
|
244
244
|
options;
|
|
245
245
|
current = { time: null, playing: false, wind: false, color: null, stations: [], blend: false, timeline: null };
|
|
246
246
|
series = null;
|
|
247
|
+
window = null;
|
|
247
248
|
listeners = /* @__PURE__ */ new Set();
|
|
248
249
|
unsubscribe;
|
|
249
250
|
liveTimer = null;
|
|
@@ -264,11 +265,37 @@ var WeatherController = class {
|
|
|
264
265
|
* dari laporan.
|
|
265
266
|
*/
|
|
266
267
|
timeline() {
|
|
268
|
+
let timeline;
|
|
267
269
|
if (this.series) {
|
|
268
|
-
const { from, to } = seriesRange(this.series);
|
|
269
|
-
|
|
270
|
+
const { from: from2, to: to2 } = seriesRange(this.series);
|
|
271
|
+
timeline = { from: from2, to: to2, segments: [], observations: [] };
|
|
272
|
+
} else {
|
|
273
|
+
timeline = this.options.store.timeline();
|
|
270
274
|
}
|
|
271
|
-
|
|
275
|
+
if (!timeline || !this.window) return timeline;
|
|
276
|
+
const from = new Date(Math.max(timeline.from.getTime(), this.window.from.getTime()));
|
|
277
|
+
const to = new Date(Math.min(timeline.to.getTime(), this.window.to.getTime()));
|
|
278
|
+
if (from.getTime() >= to.getTime()) return null;
|
|
279
|
+
return {
|
|
280
|
+
from,
|
|
281
|
+
to,
|
|
282
|
+
segments: timeline.segments.filter((s) => s.to.getTime() > from.getTime() && s.from.getTime() < to.getTime()),
|
|
283
|
+
observations: timeline.observations.filter((d) => d.getTime() >= from.getTime() && d.getTime() <= to.getTime())
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Batasi timeline (slider dan putar) ke satu jendela waktu, mis. 24 jam hari ini atau 72 jam ke
|
|
288
|
+
* depan. `null` melepas batas. Waktu yang sedang tampil di luar jendela dikembalikan ke awalnya.
|
|
289
|
+
*/
|
|
290
|
+
setWindow(window2) {
|
|
291
|
+
this.window = window2;
|
|
292
|
+
const timeline = this.timeline();
|
|
293
|
+
const shown = this.current.time;
|
|
294
|
+
if (timeline && shown && (shown.getTime() < timeline.from.getTime() || shown.getTime() > timeline.to.getTime())) {
|
|
295
|
+
this.update({ time: timeline.from });
|
|
296
|
+
}
|
|
297
|
+
this.refreshTimeline();
|
|
298
|
+
this.render(true);
|
|
272
299
|
}
|
|
273
300
|
/**
|
|
274
301
|
* Seri model berstandar `aeroswarm.weather-grid`: dipakai menggantikan medan dari laporan, dengan
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/clouds3d.ts","../src/cloudLayer.ts","../src/colorRamp.ts","../src/controller.ts","../src/screenField.ts","../src/math.ts","../src/flowRenderer.ts","../src/renderer.ts","../src/overlay.ts","../src/scalarRenderer.ts","../src/fieldOverlay.ts","../src/riaUnproject.ts"],"sourcesContent":["import type { AnimationState } from \"@aeroswarm/weather\";\n\n/** Satu gumpalan awan di sekitar bandara, dalam meter dari titik pusat. */\nexport interface CloudPuff3D {\n id: string;\n eastM: number;\n northM: number;\n /** Ketinggian di atas permukaan laut (m): elevasi bandara + tinggi dasar awan (AGL). */\n altM: number;\n sizeM: number;\n opacity: number;\n convective: boolean;\n}\n\nexport interface CloudFieldOptions {\n /** Radius sebaran awan dari bandara (m). Bawaan 12.000. */\n radiusM?: number;\n /** Elevasi bandara (m di atas laut). METAR memberi tinggi awan AGL, jadi ini ditambahkan. */\n elevationM?: number;\n /** Biji acak supaya letak awan konsisten antar render. */\n seed?: number;\n}\n\nconst FT_TO_M = 0.3048;\nconst KT_TO_MS = 0.514_444;\nconst DEFAULT_BASE_M = 600;\nconst MAX_PER_LAYER = 14;\n\n/** Pembangkit acak kecil (mulberry32) agar medan awan deterministik. */\nexport function seededRandom(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Sebar gumpalan awan di sekitar bandara dari lapisan awan METAR/TAF. Banyaknya menurut tutupan,\n * ketinggiannya = elevasi bandara + tinggi dasar (AGL). Awan konvektif lebih besar dan lebih padat.\n */\nexport function buildCloudField(\n state: AnimationState,\n options: CloudFieldOptions = {},\n): CloudPuff3D[] {\n const radius = options.radiusM ?? 12_000;\n const elevation = options.elevationM ?? 0;\n const random = seededRandom(options.seed ?? 1);\n const field: CloudPuff3D[] = [];\n\n state.clouds.layers.forEach((layer, layerIndex) => {\n const count = Math.max(1, Math.round(layer.coverage * MAX_PER_LAYER));\n const baseM = layer.baseFt === null ? DEFAULT_BASE_M : layer.baseFt * FT_TO_M;\n for (let i = 0; i < count; i++) {\n const angle = random() * Math.PI * 2;\n const distance = Math.sqrt(random()) * radius;\n const size = (layer.convective ? 1400 : 800) * (0.6 + random() * 0.9);\n field.push({\n id: `cloud-${layerIndex}-${i}`,\n eastM: Math.sin(angle) * distance,\n northM: Math.cos(angle) * distance,\n altM: elevation + baseM + random() * (layer.convective ? 400 : 120),\n sizeM: size,\n opacity: Math.min(0.9, 0.3 + layer.coverage * 0.5) * (layer.convective ? 1.1 : 1),\n convective: layer.convective,\n });\n }\n });\n return field;\n}\n\n/**\n * Geser awan searah angin (`toDeg`, derajat dari utara). `timeScale` mempercepat gerak supaya\n * terlihat (1 = kecepatan nyata, yang nyaris tidak terlihat dari ketinggian peta). Awan yang\n * keluar dari radius muncul lagi di sisi seberang.\n */\nexport function advanceCloudField(\n field: readonly CloudPuff3D[],\n wind: { toDeg: number | null; speedKt: number },\n dtSeconds: number,\n options: { radiusM?: number; timeScale?: number } = {},\n): CloudPuff3D[] {\n if (wind.toDeg === null || wind.speedKt <= 0) return field.map((c) => ({ ...c }));\n const radius = options.radiusM ?? 12_000;\n const meters = wind.speedKt * KT_TO_MS * dtSeconds * (options.timeScale ?? 12);\n const east = Math.sin((wind.toDeg * Math.PI) / 180) * meters;\n const north = Math.cos((wind.toDeg * Math.PI) / 180) * meters;\n\n return field.map((cloud) => {\n let eastM = cloud.eastM + east;\n let northM = cloud.northM + north;\n if (Math.hypot(eastM, northM) > radius) {\n // Muncul lagi di sisi seberang lingkaran.\n eastM = -eastM * 0.98;\n northM = -northM * 0.98;\n }\n return { ...cloud, eastM, northM };\n });\n}\n\n/** Titik lat/lon dari selisih meter timur/utara terhadap pusat (pendekatan datar, cukup untuk ~20 km). */\nexport function offsetToLonLat(\n centerLat: number,\n centerLon: number,\n eastM: number,\n northM: number,\n): { lat: number; lon: number } {\n const metersPerDegLat = 111_320;\n const metersPerDegLon = metersPerDegLat * Math.cos((centerLat * Math.PI) / 180);\n return {\n lat: centerLat + northM / metersPerDegLat,\n lon: centerLon + eastM / metersPerDegLon,\n };\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport {\n advanceCloudField,\n buildCloudField,\n type CloudPuff3D,\n offsetToLonLat,\n} from \"./clouds3d.js\";\n\n/**\n * Kelas dan fungsi LuciadRIA yang dipakai layer awan. Diberikan oleh pemakai (injeksi) supaya package\n * ini tidak punya dependensi ke RIA dan memakai salinan RIA milik aplikasi yang sama dengan petanya.\n */\nexport interface RiaCloudDeps {\n /** Referensi EPSG:4326, mis. `getReference(\"EPSG:4326\")`. */\n reference: unknown;\n Feature: new (\n shape: unknown,\n properties: Record<string, unknown>,\n id?: string,\n ) => unknown;\n FeatureModel: new (store: unknown, options: { reference: unknown }) => CloudModel;\n MemoryStore: new () => unknown;\n FeatureLayer: new (model: CloudModel, options: Record<string, unknown>) => CloudFeatureLayer;\n FeaturePainter: new () => CloudPainter;\n createPoint: (reference: unknown, coordinates: number[]) => unknown;\n}\n\nexport interface CloudModel {\n put(feature: unknown): unknown;\n remove(id: string): unknown;\n}\n\nexport interface CloudFeatureLayer {\n visible: boolean;\n}\n\ninterface CloudPainter {\n getDetailLevelScales?: () => number[];\n paintBody: (\n geoCanvas: { drawIcon(shape: unknown, style: Record<string, unknown>): void },\n feature: { properties: Record<string, unknown> },\n shape: unknown,\n layer: unknown,\n map: { reference: { identifier: string } },\n ) => void;\n}\n\nexport interface CloudLayerOptions {\n /** Pusat sebaran awan (bandara). */\n center: { lat: number; lon: number };\n /** Elevasi bandara (m di atas laut), ditambahkan ke tinggi dasar awan AGL. Bawaan 0. */\n elevationM?: number;\n radiusM?: number;\n /** Awan hanya digambar di peta 3D (EPSG:4978). Bawaan true. */\n only3D?: boolean;\n /** Percepatan gerak awan. Bawaan 12. */\n timeScale?: number;\n /** Selang pembaruan posisi (ms). Bawaan 500. */\n updateIntervalMs?: number;\n id?: string;\n}\n\nexport interface CloudLayer3D {\n readonly layer: CloudFeatureLayer;\n /** Ganti kondisi cuaca: awan dibangun ulang dari lapisan awan METAR/TAF. */\n setState(state: AnimationState): void;\n setVisible(visible: boolean): void;\n destroy(): void;\n}\n\n/** Sprite gumpalan awan sebagai data URL (gradien radial lembut). */\nexport function makeCloudSpriteUrl(): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = canvas.height = 128;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return \"\";\n const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);\n gradient.addColorStop(0, \"rgba(255,255,255,0.95)\");\n gradient.addColorStop(0.55, \"rgba(255,255,255,0.45)\");\n gradient.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 128, 128);\n return canvas.toDataURL(\"image/png\");\n}\n\n/**\n * Layer RIA berisi gumpalan awan di ketinggian sebenarnya (elevasi bandara + tinggi dasar AGL),\n * berukuran dalam meter sehingga ikut zoom dan perspektif 3D. Tambahkan ke peta dengan\n * `map.layerTree.addChild(cloud.layer)`.\n */\nexport function createCloudLayer3D(\n ria: RiaCloudDeps,\n options: CloudLayerOptions,\n): CloudLayer3D {\n const model = new ria.FeatureModel(new ria.MemoryStore(), { reference: ria.reference });\n const painter = new ria.FeaturePainter();\n const spriteUrl = makeCloudSpriteUrl();\n const only3D = options.only3D ?? true;\n\n painter.getDetailLevelScales = () => [1 / 100000, 1 / 20000, 1 / 5000];\n painter.paintBody = (geoCanvas, feature, shape, _layer, map) => {\n if (only3D && map.reference.identifier !== \"EPSG:4978\") return;\n const size = Number(feature.properties.sizeM) || 800;\n geoCanvas.drawIcon(shape, {\n url: spriteUrl,\n width: `${size}m`,\n height: `${size}m`,\n opacity: Number(feature.properties.opacity) || 0.5,\n draped: false,\n modulationColor: feature.properties.convective === true ? \"#8f98a3\" : \"#ffffff\",\n });\n };\n\n const layer = new ria.FeatureLayer(model, {\n id: options.id ?? \"weather-clouds\",\n label: \"Awan\",\n painter,\n selectable: false,\n visible: true,\n });\n\n let wind: { toDeg: number | null; speedKt: number } = { toDeg: null, speedKt: 0 };\n let field: CloudPuff3D[] = [];\n let knownIds = new Set<string>();\n\n const render = (): void => {\n const next = new Set<string>();\n for (const cloud of field) {\n const { lat, lon } = offsetToLonLat(\n options.center.lat,\n options.center.lon,\n cloud.eastM,\n cloud.northM,\n );\n model.put(\n new ria.Feature(\n ria.createPoint(ria.reference, [lon, lat, cloud.altM]),\n { sizeM: cloud.sizeM, opacity: cloud.opacity, convective: cloud.convective },\n cloud.id,\n ),\n );\n next.add(cloud.id);\n }\n for (const id of knownIds) if (!next.has(id)) model.remove(id);\n knownIds = next;\n };\n\n const interval = options.updateIntervalMs ?? 500;\n const timer = setInterval(() => {\n if (!layer.visible || field.length === 0) return;\n field = advanceCloudField(field, wind, interval / 1000, {\n radiusM: options.radiusM,\n timeScale: options.timeScale,\n });\n render();\n }, interval);\n\n return {\n layer,\n setState(state) {\n wind = { toDeg: state.wind.toDeg, speedKt: state.wind.speedKt };\n field = buildCloudField(state, {\n radiusM: options.radiusM,\n elevationM: options.elevationM,\n });\n render();\n },\n setVisible(visible) {\n layer.visible = visible;\n },\n destroy() {\n clearInterval(timer);\n for (const id of knownIds) model.remove(id);\n knownIds = new Set();\n field = [];\n },\n };\n}\n","/** Warna: [nilai, merah, hijau, biru, alfa 0-1]. Nilai harus naik. */\nexport type RampStop = readonly [number, number, number, number, number?];\nexport type Rgba = readonly [number, number, number, number];\n\n/** Warna di `value` dengan interpolasi linear antar titik; di luar rentang dipakai titik ujung. */\nexport function rampColor(stops: readonly RampStop[], value: number): Rgba {\n const first = stops[0];\n const last = stops[stops.length - 1];\n if (!first || !last) return [0, 0, 0, 0];\n if (Number.isNaN(value) || value <= first[0]) return [first[1], first[2], first[3], first[4] ?? 1];\n if (value >= last[0]) return [last[1], last[2], last[3], last[4] ?? 1];\n for (let i = 1; i < stops.length; i++) {\n const hi = stops[i] as RampStop;\n const lo = stops[i - 1] as RampStop;\n if (value <= hi[0]) {\n const t = (value - lo[0]) / (hi[0] - lo[0] || 1);\n const mix = (a: number, b: number): number => a + (b - a) * t;\n return [mix(lo[1], hi[1]), mix(lo[2], hi[2]), mix(lo[3], hi[3]), mix(lo[4] ?? 1, hi[4] ?? 1)];\n }\n }\n return [last[1], last[2], last[3], last[4] ?? 1];\n}\n\nexport const rgbaCss = ([r, g, b, a]: Rgba, alpha = 1): string =>\n `rgba(${Math.round(r)},${Math.round(g)},${Math.round(b)},${(a * alpha).toFixed(3)})`;\n\n/** Kecepatan angin (m/s): dari biru dingin (tenang) ke merah (kencang), seperti peta angin umum. */\nexport const WIND_RAMP: readonly RampStop[] = [\n [0, 98, 113, 184],\n [2, 61, 110, 163],\n [4, 74, 148, 170],\n [7, 74, 146, 115],\n [10, 77, 142, 56],\n [13, 160, 168, 50],\n [17, 210, 168, 48],\n [22, 220, 110, 40],\n [28, 190, 50, 50],\n [36, 150, 40, 120],\n];\n\n/** Tutupan awan (%): bening ke putih keabu-abuan. */\nexport const CLOUD_RAMP: readonly RampStop[] = [\n [0, 255, 255, 255, 0],\n [20, 235, 238, 242, 0.15],\n [60, 205, 210, 218, 0.45],\n [100, 150, 158, 170, 0.75],\n];\n\n/** Curah hujan (mm/jam). */\nexport const PRECIPITATION_RAMP: readonly RampStop[] = [\n [0, 120, 190, 230, 0],\n [0.1, 120, 190, 230, 0.45],\n [1, 70, 190, 200, 0.7],\n [3, 80, 190, 110, 0.78],\n [6, 210, 220, 60, 0.82],\n [12, 240, 170, 40, 0.85],\n [25, 225, 80, 50, 0.88],\n [50, 170, 40, 150, 0.9],\n];\n\n/** Suhu (°C). */\nexport const TEMPERATURE_RAMP: readonly RampStop[] = [\n [10, 80, 90, 200, 0.7],\n [20, 70, 170, 200, 0.7],\n [26, 120, 200, 110, 0.7],\n [30, 235, 200, 70, 0.72],\n [34, 235, 130, 50, 0.75],\n [40, 200, 50, 50, 0.8],\n];\n","import type {\n FieldGridOptions,\n FieldSet,\n ScalarGrid,\n Timeline,\n WeatherGridSeries,\n WeatherStore,\n WindGrid,\n} from \"@aeroswarm/weather\";\nimport { seriesFieldsAt, seriesRange } from \"@aeroswarm/weather\";\nimport { CLOUD_RAMP, PRECIPITATION_RAMP, type RampStop, rgbaCss, rampColor, TEMPERATURE_RAMP } from \"./colorRamp.js\";\nimport type { FieldOverlay } from \"./fieldOverlay.js\";\n\nexport type ColorLayer = \"cloud\" | \"rain\" | \"temperature\";\n\nexport const LAYER_RAMPS: Record<ColorLayer, readonly RampStop[]> = {\n cloud: CLOUD_RAMP,\n rain: PRECIPITATION_RAMP,\n temperature: TEMPERATURE_RAMP,\n};\n\nexport interface LegendEntry {\n value: number;\n color: string;\n}\n\n/** Titik legenda (nilai dan warna CSS) untuk lapisan warna, untuk digambar sebagai palet di UI. */\nexport function legendFor(layer: ColorLayer): { unit: string; entries: LegendEntry[] } {\n const stops = LAYER_RAMPS[layer];\n const unit = layer === \"cloud\" ? \"%\" : layer === \"rain\" ? \"mm/jam\" : \"°C\";\n return {\n unit,\n entries: stops.map((s) => ({ value: s[0], color: rgbaCss(rampColor(stops, s[0]), 1) })),\n };\n}\n\nexport interface ControllerState {\n /** Waktu yang ditampilkan; null = langsung (waktu sekarang). */\n time: Date | null;\n playing: boolean;\n wind: boolean;\n color: ColorLayer | null;\n /** Stasiun (atau sumber model) yang dipakai pada waktu yang ditampilkan. */\n stations: string[];\n /** Medan model disesuaikan dengan laporan METAR/SPECI/TAF di sekitar tiap bandara. */\n blend: boolean;\n /** Rentang dan pembagian waktu yang bisa digeser/diputar; null bila belum ada data. */\n timeline: Timeline | null;\n}\n\nexport interface WeatherControllerOptions {\n store: WeatherStore;\n overlay: FieldOverlay;\n grid: FieldGridOptions;\n /** Jam di timeline per detik saat diputar. Bawaan 2. */\n hoursPerSecond?: number;\n /** Jarak pengaruh laporan stasiun terhadap medan model saat digabung (km). Bawaan 80. */\n blendRadiusKm?: number;\n /** Ikutkan PROB30/PROB40 dari TAF. Bawaan false. */\n includeProbable?: boolean;\n /** Sumber waktu sekarang (untuk tes). */\n now?: () => Date;\n}\n\n/**\n * Mengikat `WeatherStore` ke overlay peta: tiap data berubah atau waktu bergeser, medan dihitung\n * ulang dan digambar. Waktu bisa langsung (sekarang), digeser, atau diputar sepanjang timeline.\n */\nexport class WeatherController {\n private current: ControllerState = { time: null, playing: false, wind: false, color: null, stations: [], blend: false, timeline: null };\n private series: WeatherGridSeries | null = null;\n private readonly listeners = new Set<(state: ControllerState) => void>();\n private readonly unsubscribe: () => void;\n private liveTimer: ReturnType<typeof setInterval> | null = null;\n private playTimer: ReturnType<typeof setInterval> | null = null;\n private hoursPerSecond: number;\n private external: { wind: WindGrid | null; scalar: ScalarGrid | null } = { wind: null, scalar: null };\n private lastDrawn = Number.NaN;\n private lastVersion = -1;\n\n constructor(private readonly options: WeatherControllerOptions) {\n this.hoursPerSecond = options.hoursPerSecond ?? 2;\n this.unsubscribe = options.store.onChange(() => {\n this.refreshTimeline();\n this.render();\n });\n this.refreshTimeline();\n // Mode langsung: perbarui tiap menit agar sumber berganti saat pengamatan menua.\n this.liveTimer = setInterval(() => {\n if (this.current.time === null) this.render(true);\n }, 60_000);\n }\n\n get state(): ControllerState {\n return this.current;\n }\n\n /** Waktu yang sedang ditampilkan (sekarang bila langsung). */\n get effectiveTime(): Date {\n return this.current.time ?? this.now();\n }\n\n /**\n * Rentang waktu yang bisa dipilih: periode seri model bila ada, selain itu periode TAF dan pengamatan\n * dari laporan.\n */\n timeline(): Timeline | null {\n if (this.series) {\n const { from, to } = seriesRange(this.series);\n return { from, to, segments: [], observations: [] };\n }\n return this.options.store.timeline();\n }\n\n /**\n * Seri model berstandar `aeroswarm.weather-grid`: dipakai menggantikan medan dari laporan, dengan\n * timeline sepanjang seri. `null` kembali ke laporan. Grid dari `setExternalFields` tetap menang.\n */\n /**\n * Gabungkan seri model dengan laporan: grid model ditarik ke pengamatan METAR/SPECI (atau prakiraan\n * TAF untuk waktu ke depan) di bandara yang punya koordinat. Hanya berlaku bila ada seri model.\n */\n setBlend(blend: boolean): void {\n this.update({ blend });\n this.render(true);\n }\n\n setSeries(series: WeatherGridSeries | null): void {\n this.series = series;\n this.refreshTimeline();\n this.render(true);\n }\n\n onState(listener: (state: ControllerState) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n setWind(wind: boolean): void {\n this.update({ wind });\n this.render(true);\n }\n\n setColorLayer(color: ColorLayer | null): void {\n this.update({ color });\n this.render(true);\n }\n\n /** Tampilkan waktu tertentu; null kembali ke langsung. */\n setTime(time: Date | null): void {\n this.update({ time });\n if (time === null) this.pause();\n this.render(true);\n }\n\n /**\n * Grid dari sumber lain (berkas, layanan luar) yang dipakai sebagai pengganti medan hasil laporan\n * METAR/TAF. `null` mengembalikan ke medan dari laporan.\n */\n setExternalFields(fields: { wind?: WindGrid | null; scalar?: ScalarGrid | null }): void {\n this.external = {\n wind: fields.wind === undefined ? this.external.wind : fields.wind,\n scalar: fields.scalar === undefined ? this.external.scalar : fields.scalar,\n };\n this.render(true);\n }\n\n setSpeed(hoursPerSecond: number): void {\n this.hoursPerSecond = Math.max(0.1, hoursPerSecond);\n }\n\n play(): void {\n if (this.playTimer) return;\n const timeline = this.timeline();\n if (!timeline) return;\n // Mulai dari waktu yang sedang tampil, atau awal timeline bila langsung/di luar rentang.\n const start = this.effectiveTime;\n if (start.getTime() < timeline.from.getTime() || start.getTime() >= timeline.to.getTime()) {\n this.update({ time: timeline.from });\n } else if (this.current.time === null) {\n this.update({ time: start });\n }\n this.update({ playing: true });\n const interval = 200;\n this.playTimer = setInterval(() => {\n const tl = this.timeline();\n if (!tl || this.current.time === null) return;\n this.update({\n time: advanceTime(this.current.time, (interval / 1000) * this.hoursPerSecond * 3_600_000, tl),\n });\n this.render();\n }, interval);\n this.render(true);\n }\n\n pause(): void {\n if (this.playTimer) clearInterval(this.playTimer);\n this.playTimer = null;\n if (this.current.playing) this.update({ playing: false });\n }\n\n destroy(): void {\n this.pause();\n if (this.liveTimer) clearInterval(this.liveTimer);\n this.liveTimer = null;\n this.unsubscribe();\n this.listeners.clear();\n this.options.overlay.setWind(null);\n this.options.overlay.setScalar(null);\n }\n\n /** Hitung ulang medan dan gambar. `force` mengabaikan pembatasan \"belum berubah\". */\n render(force = false): void {\n const time = this.effectiveTime;\n const { store } = this.options;\n const minute = Math.floor(time.getTime() / 60_000);\n if (!force && minute === this.lastDrawn && store.version === this.lastVersion) return;\n this.lastDrawn = minute;\n this.lastVersion = store.version;\n\n const { wind, color } = this.current;\n const needFields = (wind && !this.external.wind) || (color !== null && !this.external.scalar);\n let fields: FieldSet | null = null;\n if (needFields && this.series) {\n const model = seriesFieldsAt(this.series, time);\n if (model) {\n const name = this.series.source.name;\n fields = this.current.blend\n ? store.blendInto(model, name, time, {\n radiusKm: this.options.blendRadiusKm,\n includeProbable: this.options.includeProbable,\n })\n : { ...model, stations: [name] };\n }\n } else if (needFields) {\n fields = store.fieldsAt(time, this.options.grid, { includeProbable: this.options.includeProbable });\n }\n\n this.options.overlay.setWind(wind ? (this.external.wind ?? fields?.wind ?? null) : null);\n const fromReports = fields && color ? (color === \"temperature\" ? fields.temperature : fields[color]) : null;\n const scalar = color ? (this.external.scalar ?? fromReports) : null;\n this.options.overlay.setScalar(scalar, color ? LAYER_RAMPS[color] : undefined);\n this.update({ stations: fields?.stations ?? [] });\n }\n\n private refreshTimeline(): void {\n this.update({ timeline: this.timeline() });\n }\n\n private now(): Date {\n return this.options.now?.() ?? new Date();\n }\n\n private update(patch: Partial<ControllerState>): void {\n this.current = { ...this.current, ...patch };\n for (const listener of this.listeners) listener(this.current);\n }\n}\n\n/** Majukan waktu sebesar `deltaMs`; melewati ujung timeline kembali ke awal (berulang). */\nexport function advanceTime(time: Date, deltaMs: number, timeline: { from: Date; to: Date }): Date {\n const next = time.getTime() + deltaMs;\n if (next >= timeline.to.getTime() || next < timeline.from.getTime()) return new Date(timeline.from);\n return new Date(next);\n}\n","import {\n directionFromUV,\n sampleScalar,\n sampleWind,\n type ScalarGrid,\n speedOf,\n type WindGrid,\n} from \"@aeroswarm/weather\";\nimport { screenVector } from \"./math.js\";\n\n/** Titik layar (piksel) ke koordinat geografis; null bila di luar peta (mis. di luar bola bumi). */\nexport type Unproject = (x: number, y: number) => { lon: number; lat: number } | null;\n\n/** Angin yang sudah dipetakan ke kisi layar: satu vektor arah layar dan kecepatan per sel. */\nexport interface ScreenWindField {\n cols: number;\n rows: number;\n cell: number;\n /** Vektor satuan arah gerak di layar (x kanan, y bawah). */\n dx: Float32Array;\n dy: Float32Array;\n /** Kecepatan (m/s); NaN = tanpa data. */\n speed: Float32Array;\n}\n\nexport interface ScreenScalarField {\n cols: number;\n rows: number;\n cell: number;\n values: Float32Array;\n}\n\n/**\n * Ubah grid angin geografis menjadi kisi layar. Dilakukan sekali tiap peta bergerak, supaya partikel\n * tinggal membaca kisi (tanpa memproyeksikan ulang tiap partikel tiap frame).\n */\nexport function buildScreenWindField(\n grid: WindGrid,\n width: number,\n height: number,\n unproject: Unproject,\n screenOffsetDeg: number,\n cell = 24,\n): ScreenWindField {\n const cols = Math.max(1, Math.ceil(width / cell));\n const rows = Math.max(1, Math.ceil(height / cell));\n const dx = new Float32Array(cols * rows);\n const dy = new Float32Array(cols * rows);\n const speed = new Float32Array(cols * rows).fill(Number.NaN);\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const ll = unproject((col + 0.5) * cell, (row + 0.5) * cell);\n const wind = ll ? sampleWind(grid, ll.lon, ll.lat) : null;\n if (!wind) continue;\n const index = row * cols + col;\n const vector = screenVector((directionFromUV(wind.u, wind.v) + 180) % 360, screenOffsetDeg);\n dx[index] = vector.x;\n dy[index] = vector.y;\n speed[index] = speedOf(wind.u, wind.v);\n }\n }\n return { cols, rows, cell, dx, dy, speed };\n}\n\nexport function buildScreenScalarField(\n grid: ScalarGrid,\n width: number,\n height: number,\n unproject: Unproject,\n cell = 8,\n): ScreenScalarField {\n const cols = Math.max(1, Math.ceil(width / cell));\n const rows = Math.max(1, Math.ceil(height / cell));\n const values = new Float32Array(cols * rows).fill(Number.NaN);\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const ll = unproject((col + 0.5) * cell, (row + 0.5) * cell);\n const value = ll ? sampleScalar(grid, ll.lon, ll.lat) : null;\n if (value !== null) values[row * cols + col] = value;\n }\n }\n return { cols, rows, cell, values };\n}\n\n/** Angin di titik layar (x, y) dari kisi; null di luar kisi atau tanpa data. */\nexport function windAt(\n field: ScreenWindField,\n x: number,\n y: number,\n): { dx: number; dy: number; speed: number } | null {\n const col = Math.floor(x / field.cell);\n const row = Math.floor(y / field.cell);\n if (col < 0 || row < 0 || col >= field.cols || row >= field.rows) return null;\n const index = row * field.cols + col;\n const speed = field.speed[index] as number;\n if (Number.isNaN(speed)) return null;\n return { dx: field.dx[index] as number, dy: field.dy[index] as number, speed };\n}\n","/** Hitungan murni untuk renderer: tanpa DOM supaya mudah dites. */\n\nconst RAD = Math.PI / 180;\n\n/**\n * Vektor gerak di layar (x ke kanan, y ke bawah) untuk arah `toDeg` (derajat dari utara, searah jarum\n * jam). `screenOffsetDeg` = putaran peta: arah di layar = arah geografis + offset.\n */\nexport function screenVector(\n toDeg: number,\n screenOffsetDeg = 0,\n): { x: number; y: number } {\n const angle = (toDeg + screenOffsetDeg) * RAD;\n return { x: Math.sin(angle), y: -Math.cos(angle) };\n}\n\nexport const clamp = (value: number, min = 0, max = 1): number =>\n Math.min(max, Math.max(min, value));\n\n/** Jumlah partikel angin menurut luas layar, dibatasi `max`. */\nexport function windParticleCount(width: number, height: number, max = 520): number {\n return Math.min(max, Math.round((width * height) / 2600));\n}\n\n/** Jumlah garis hujan yang digambar dari `pool` tetes, menurut intensitas 0-1. */\nexport function rainCount(pool: number, intensity: number): number {\n return Math.round(pool * clamp(intensity));\n}\n\n/** Kecepatan visual partikel (px/detik) dari kecepatan angin (kt) dan hembusan sesaat. */\nexport function particleSpeed(speedKt: number, gustKt = 0): number {\n return (speedKt + gustKt) * 7;\n}\n\n/** Hembusan sesaat (kt tambahan) yang berdenyut halus sesuai `gustiness` 0-1. */\nexport function gustPulse(gustiness: number, timeMs: number): number {\n if (gustiness <= 0) return 0;\n const wave = 0.5 + 0.5 * Math.sin(timeMs / 700) * Math.sin(timeMs / 1900 + 1);\n return wave * gustiness * 20;\n}\n\n/** Selang antar kilat (ms) dari laju kilat per menit, dengan sedikit acak (`random` 0-1). */\nexport function lightningInterval(perMinute: number, random: number): number {\n if (perMinute <= 0) return Number.POSITIVE_INFINITY;\n return (60_000 / perMinute) * (0.4 + random * 1.2);\n}\n","import type { WindGrid } from \"@aeroswarm/weather\";\nimport { rampColor, rgbaCss, WIND_RAMP } from \"./colorRamp.js\";\nimport { buildScreenWindField, type ScreenWindField, type Unproject, windAt } from \"./screenField.js\";\n\nexport interface FlowRendererOptions {\n container: HTMLElement;\n unproject: Unproject;\n zIndex?: number;\n maxFps?: number;\n /** Piksel per detik untuk tiap m/s angin. Bawaan 9. */\n pixelsPerMs?: number;\n /** Jumlah partikel maksimum. Bawaan 3000. */\n maxParticles?: number;\n reducedMotion?: boolean;\n}\n\ninterface Particle {\n x: number;\n y: number;\n age: number;\n life: number;\n}\n\n/**\n * Aliran angin ala Windy: partikel bergerak mengikuti medan angin dari grid, berwarna menurut\n * kecepatan, dengan ekor yang memudar. Medan dipetakan ke kisi layar sekali tiap peta bergerak.\n */\nexport class FlowRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private grid: WindGrid | null = null;\n private field: ScreenWindField | null = null;\n private particles: Particle[] = [];\n private width = 1;\n private height = 1;\n private dpr = 1;\n private offset = 0;\n private raf = 0;\n private running = false;\n private last = 0;\n private accumulated = 0;\n private readonly frameInterval: number;\n private readonly reduceMotion: boolean;\n\n constructor(private readonly options: FlowRendererOptions) {\n this.frameInterval = 1000 / (options.maxFps ?? 30);\n this.reduceMotion =\n options.reducedMotion ??\n (typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches);\n this.canvas = document.createElement(\"canvas\");\n this.canvas.dataset.weatherLayer = \"flow\";\n Object.assign(this.canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(options.zIndex ?? 4),\n });\n options.container.appendChild(this.canvas);\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.ctx = ctx;\n this.resize();\n }\n\n setWind(grid: WindGrid | null): void {\n this.grid = grid;\n this.refreshField();\n }\n\n setScreenOffset(degrees: number): void {\n if (degrees === this.offset) return;\n this.offset = degrees;\n this.refreshField();\n }\n\n /** Dipanggil saat peta bergerak/zoom: petakan ulang medan ke layar. */\n refreshField(): void {\n this.field = this.grid\n ? buildScreenWindField(this.grid, this.width, this.height, this.options.unproject, this.offset)\n : null;\n if (!this.field) this.ctx.clearRect(0, 0, this.width, this.height);\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n this.canvas.width = this.width * this.dpr;\n this.canvas.height = this.height * this.dpr;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n const count = Math.min(this.options.maxParticles ?? 3000, Math.round((this.width * this.height) / 520));\n this.particles = Array.from({ length: count }, () => this.spawn());\n this.refreshField();\n }\n\n setVisible(visible: boolean): void {\n this.canvas.style.display = visible ? \"block\" : \"none\";\n if (visible) this.start();\n else this.stop();\n }\n\n setOpacity(opacity: number): void {\n this.canvas.style.opacity = String(Math.min(1, Math.max(0, opacity)));\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.last = performance.now();\n const tick = (now: number): void => {\n if (!this.running) return;\n this.accumulated += now - this.last;\n this.last = now;\n if (!document.hidden && this.accumulated >= this.frameInterval) {\n this.step(Math.min(0.1, this.accumulated / 1000));\n this.accumulated = 0;\n }\n this.raf = requestAnimationFrame(tick);\n };\n this.raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n this.running = false;\n cancelAnimationFrame(this.raf);\n }\n\n destroy(): void {\n this.stop();\n this.canvas.remove();\n }\n\n private spawn(): Particle {\n return {\n x: Math.random() * this.width,\n y: Math.random() * this.height,\n age: Math.floor(Math.random() * 60),\n life: 50 + Math.random() * 80,\n };\n }\n\n private step(dt: number): void {\n const ctx = this.ctx;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.05})`;\n ctx.fillRect(0, 0, this.width, this.height);\n ctx.globalCompositeOperation = \"source-over\";\n const field = this.field;\n if (!field) return;\n\n const pixelsPerMs = this.options.pixelsPerMs ?? 9;\n ctx.lineWidth = 1.5;\n ctx.lineCap = \"round\";\n const motion = this.reduceMotion ? 0 : 1;\n\n // Kelompokkan garis per warna (dibulatkan) agar tidak ribuan stroke berbeda per frame.\n const buckets = new Map<number, { color: string; path: Path2D }>();\n for (const p of this.particles) {\n const wind = windAt(field, p.x, p.y);\n if (!wind || p.age > p.life) {\n Object.assign(p, this.spawn(), { age: 0 });\n continue;\n }\n const step = wind.speed * pixelsPerMs * dt * motion;\n const nx = p.x + wind.dx * step;\n const ny = p.y + wind.dy * step;\n const key = Math.round(wind.speed / 1.5);\n let bucket = buckets.get(key);\n if (!bucket) {\n bucket = { color: rgbaCss(rampColor(WIND_RAMP, key * 1.5), 0.85), path: new Path2D() };\n buckets.set(key, bucket);\n }\n bucket.path.moveTo(p.x, p.y);\n bucket.path.lineTo(nx, ny);\n p.x = nx;\n p.y = ny;\n p.age++;\n if (nx < 0 || ny < 0 || nx > this.width || ny > this.height) p.age = p.life + 1;\n }\n for (const { color, path } of buckets.values()) {\n ctx.strokeStyle = color;\n ctx.stroke(path);\n }\n }\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport {\n clamp,\n gustPulse,\n lightningInterval,\n particleSpeed,\n rainCount,\n screenVector,\n windParticleCount,\n} from \"./math.js\";\n\nexport type WeatherTheme = \"light\" | \"dark\";\n\nexport interface WeatherRendererOptions {\n /** Elemen tempat canvas dipasang (mis. `map.domNode`). Harus berposisi non-static. */\n container: HTMLElement;\n theme?: WeatherTheme;\n /** Batas frame per detik. Bawaan 30. */\n maxFps?: number;\n /** Gambar diam (tanpa gerak) bila pengguna meminta mengurangi animasi. Bawaan: ikuti OS. */\n reducedMotion?: boolean;\n zIndex?: number;\n}\n\ninterface Particle {\n x: number;\n y: number;\n age: number;\n life: number;\n}\ninterface Puff {\n dx: number;\n dy: number;\n r: number;\n}\ninterface Cloud {\n x: number;\n y: number;\n puffs: Puff[];\n layer: number;\n threshold: number;\n}\n\nconst CALM: AnimationState = {\n wind: { fromDeg: null, toDeg: null, speedKt: 0, gustiness: 0, turbulent: false },\n clouds: { layers: [], total: 0 },\n visibility: { meters: null, haze: 0 },\n obscuration: null,\n precipitation: null,\n thunderstorm: null,\n};\n\nconst DROP_POOL = 700;\n\nfunction makePuffSprite(): HTMLCanvasElement {\n const sprite = document.createElement(\"canvas\");\n sprite.width = sprite.height = 128;\n const ctx = sprite.getContext(\"2d\");\n if (ctx) {\n const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);\n gradient.addColorStop(0, \"rgba(255,255,255,0.95)\");\n gradient.addColorStop(0.55, \"rgba(255,255,255,0.45)\");\n gradient.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 128, 128);\n }\n return sprite;\n}\n\n/**\n * Menggambar angin, awan, hujan, kabut, dan petir ke dua canvas transparan di atas `container`.\n * Tidak mengenal peta: arah di layar diatur lewat `setScreenOffset` (putaran peta).\n */\nexport class WeatherRenderer {\n private readonly windCanvas: HTMLCanvasElement;\n private readonly fxCanvas: HTMLCanvasElement;\n private readonly wind: CanvasRenderingContext2D;\n private readonly fx: CanvasRenderingContext2D;\n private readonly puff = makePuffSprite();\n private readonly drops = Array.from({ length: DROP_POOL }, () => ({\n x: Math.random(),\n y: Math.random(),\n len: 0.6 + Math.random() * 0.8,\n speed: 0.7 + Math.random() * 0.6,\n }));\n\n private state: AnimationState = CALM;\n private theme: WeatherTheme;\n private screenOffset = 0;\n private width = 1;\n private height = 1;\n private dpr = 1;\n private particles: Particle[] = [];\n private clouds: Cloud[] = [];\n private raf = 0;\n private lastFrame = 0;\n private accumulated = 0;\n private flash = 0;\n private bolt: [number, number][] | null = null;\n private nextBolt = 0;\n private readonly frameInterval: number;\n private readonly reduceMotion: boolean;\n private running = false;\n\n constructor(private readonly options: WeatherRendererOptions) {\n this.theme = options.theme ?? \"light\";\n this.frameInterval = 1000 / (options.maxFps ?? 30);\n this.reduceMotion =\n options.reducedMotion ??\n (typeof matchMedia === \"function\" &&\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches);\n\n this.windCanvas = this.createCanvas(\"wind\");\n this.fxCanvas = this.createCanvas(\"fx\");\n const wind = this.windCanvas.getContext(\"2d\");\n const fx = this.fxCanvas.getContext(\"2d\");\n if (!wind || !fx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.wind = wind;\n this.fx = fx;\n this.resize();\n }\n\n setState(state: AnimationState): void {\n this.state = state;\n if (this.reduceMotion) this.renderFrame(performance.now(), 0);\n }\n\n /** Putaran peta (derajat) yang ditambahkan ke arah geografis angin. */\n setScreenOffset(degrees: number): void {\n this.screenOffset = degrees;\n }\n\n setTheme(theme: WeatherTheme): void {\n this.theme = theme;\n }\n\n setVisible(visible: boolean): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.display = visible ? \"block\" : \"none\";\n }\n if (visible) this.start();\n else this.stop();\n }\n\n setOpacity(opacity: number): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.opacity = String(clamp(opacity));\n }\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.width = this.width * this.dpr;\n canvas.height = this.height * this.dpr;\n }\n this.wind.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.fx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.seedParticles();\n this.seedClouds();\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.lastFrame = performance.now();\n const tick = (now: number): void => {\n if (!this.running) return;\n const dt = (now - this.lastFrame) / 1000;\n this.lastFrame = now;\n this.accumulated += dt * 1000;\n if (!document.hidden && this.accumulated >= this.frameInterval) {\n this.renderFrame(now, Math.min(0.1, this.accumulated / 1000));\n this.accumulated = 0;\n }\n this.raf = requestAnimationFrame(tick);\n };\n this.raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n this.running = false;\n cancelAnimationFrame(this.raf);\n }\n\n destroy(): void {\n this.stop();\n this.windCanvas.remove();\n this.fxCanvas.remove();\n }\n\n // ---- internal --------------------------------------------------------\n\n private createCanvas(name: string): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.dataset.weatherLayer = name;\n Object.assign(canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(this.options.zIndex ?? 5),\n });\n this.options.container.appendChild(canvas);\n return canvas;\n }\n\n private seedParticles(): void {\n const count = windParticleCount(this.width, this.height);\n this.particles = Array.from({ length: count }, () => this.newParticle());\n this.wind.clearRect(0, 0, this.width, this.height);\n }\n\n private newParticle(): Particle {\n return {\n x: Math.random() * this.width,\n y: Math.random() * this.height,\n age: Math.floor(Math.random() * 80),\n life: 60 + Math.random() * 90,\n };\n }\n\n private seedClouds(): void {\n this.clouds = Array.from({ length: 16 }, (_, i) => {\n const size = 90 + Math.random() * 120;\n const count = 5 + Math.floor(Math.random() * 4);\n return {\n x: Math.random() * (this.width + 300) - 150,\n y: Math.random() * (this.height + 100) - 50,\n layer: 0.6 + Math.random() * 0.8,\n threshold: i / 16,\n puffs: Array.from({ length: count }, () => ({\n dx: (Math.random() - 0.5) * size * 1.4,\n dy: (Math.random() - 0.5) * size * 0.5,\n r: size * (0.5 + Math.random() * 0.5),\n })),\n };\n });\n }\n\n private renderFrame(now: number, dt: number): void {\n const { wind, thunderstorm } = this.state;\n const motion = this.reduceMotion ? 0 : 1;\n const toDeg = wind.toDeg;\n const vector = toDeg === null ? null : screenVector(toDeg, this.screenOffset);\n const gust = gustPulse(wind.gustiness, now);\n const speed = particleSpeed(wind.speedKt, gust);\n\n this.drawWind(now, dt, vector, wind.turbulent, speed, motion);\n\n const fx = this.fx;\n fx.clearRect(0, 0, this.width, this.height);\n this.drawClouds(dt, vector, wind.speedKt, motion);\n this.drawPrecipitation(dt, vector, wind.speedKt, motion);\n this.drawHaze(now, motion);\n this.drawLightning(now, dt, thunderstorm?.lightningPerMinute ?? 0);\n }\n\n private drawWind(\n now: number,\n dt: number,\n vector: { x: number; y: number } | null,\n turbulent: boolean,\n speed: number,\n motion: number,\n ): void {\n const ctx = this.wind;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.1})`;\n ctx.fillRect(0, 0, this.width, this.height);\n ctx.globalCompositeOperation = \"source-over\";\n if (this.state.wind.speedKt <= 0) return;\n\n const alpha = Math.min(0.75, 0.22 + this.state.wind.speedKt / 50);\n ctx.lineWidth = 1.2;\n ctx.lineCap = \"round\";\n ctx.strokeStyle =\n this.theme === \"dark\"\n ? `rgba(120,200,220,${alpha})`\n : `rgba(31,122,140,${alpha})`;\n ctx.beginPath();\n for (const p of this.particles) {\n const fromX = p.x;\n const fromY = p.y;\n let vx: number;\n let vy: number;\n if (turbulent || vector === null) {\n vx = Math.cos(p.age / 12 + p.x) * 0.7;\n vy = Math.sin(p.age / 14 + p.y) * 0.7;\n } else {\n const wobble =\n Math.sin(p.y / 55 + now / 1400) * 0.35 + Math.cos(p.x / 70 - now / 1700) * 0.25;\n vx = vector.x - wobble * 0.25 * vector.y;\n vy = vector.y + wobble * 0.25 * vector.x;\n }\n p.x += vx * speed * dt * motion;\n p.y += vy * speed * dt * motion;\n p.age++;\n ctx.moveTo(fromX, fromY);\n ctx.lineTo(p.x, p.y);\n if (p.x < 0 || p.x > this.width || p.y < 0 || p.y > this.height || p.age > p.life) {\n Object.assign(p, this.newParticle(), { age: 0 });\n }\n }\n ctx.stroke();\n }\n\n private drawClouds(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const ctx = this.fx;\n const cover = this.state.clouds.total;\n const precipitation = this.state.precipitation;\n const dark = clamp(\n (precipitation?.intensity ?? 0) * 0.6 + (this.state.thunderstorm?.overhead ? 0.4 : 0),\n );\n if (cover <= 0) return;\n\n for (const cloud of this.clouds) {\n if (vector) {\n cloud.x += vector.x * speedKt * 1.4 * cloud.layer * dt * motion;\n cloud.y += vector.y * speedKt * 1.4 * cloud.layer * dt * motion;\n }\n if (cloud.x < -250) cloud.x = this.width + 250;\n if (cloud.x > this.width + 250) cloud.x = -250;\n if (cloud.y < -150) cloud.y = this.height + 150;\n if (cloud.y > this.height + 150) cloud.y = -150;\n const visible = clamp((cover * 1.15 - cloud.threshold) * 4);\n if (visible <= 0) continue;\n ctx.globalAlpha = visible * (0.38 + 0.3 * cover) * (1 - dark * 0.15);\n for (const puff of cloud.puffs) {\n ctx.drawImage(\n this.puff,\n cloud.x + puff.dx - puff.r,\n cloud.y + puff.dy - puff.r,\n puff.r * 2,\n puff.r * 2,\n );\n }\n }\n ctx.globalAlpha = 1;\n if (dark > 0 || cover > 0.6) {\n ctx.fillStyle = `rgba(38,48,62,${(cover * 0.2 + dark * 0.32).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n\n private drawPrecipitation(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const precipitation = this.state.precipitation;\n if (!precipitation) return;\n const ctx = this.fx;\n const count = rainCount(this.drops.length, precipitation.intensity);\n const slant = (vector?.x ?? 0) * Math.min(1, speedKt / 25) * 0.5;\n const snow = precipitation.kind === \"snow\";\n const fall = snow ? 0.25 : 1.6;\n\n if (snow) ctx.fillStyle = \"rgba(255,255,255,0.8)\";\n else {\n ctx.strokeStyle = `rgba(150,185,225,${(0.3 + 0.3 * precipitation.intensity).toFixed(2)})`;\n ctx.lineWidth = precipitation.kind === \"drizzle\" ? 0.7 : 1.1;\n ctx.beginPath();\n }\n for (let i = 0; i < count; i++) {\n const drop = this.drops[i];\n if (!drop) continue;\n drop.y += drop.speed * dt * fall * motion;\n drop.x += slant * dt * fall * motion;\n if (drop.y > 1) {\n drop.y = -0.05;\n drop.x = Math.random();\n }\n if (drop.x > 1) drop.x = 0;\n if (drop.x < 0) drop.x = 1;\n const x = drop.x * this.width;\n const y = drop.y * this.height;\n if (snow) ctx.fillRect(x, y, 2, 2);\n else {\n const length = 12 * drop.len;\n ctx.moveTo(x, y);\n ctx.lineTo(x - slant * length, y - length);\n }\n }\n if (!snow) ctx.stroke();\n }\n\n private drawHaze(now: number, motion: number): void {\n const { visibility, obscuration } = this.state;\n const haze = Math.max(visibility.haze, (obscuration?.density ?? 0) * 0.7);\n if (haze <= 0.01) return;\n const ctx = this.fx;\n const dark = this.theme === \"dark\";\n ctx.fillStyle = `${dark ? \"rgba(150,160,168,\" : \"rgba(214,219,223,\"}${(haze * 0.78).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (obscuration?.kind === \"fog\" || (visibility.meters ?? 10_000) < 1500) {\n for (let i = 0; i < 3; i++) {\n const cx = (((now / 40) * (i + 1) * 0.8 * motion) % (this.width * 1.3)) + i * this.width * 0.2;\n const cy = this.height * (0.3 + i * 0.25);\n const gradient = ctx.createRadialGradient(cx, cy, 10, cx, cy, this.width * 0.5);\n gradient.addColorStop(0, dark ? \"rgba(170,180,188,0.35)\" : \"rgba(235,238,240,0.5)\");\n gradient.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n }\n\n private drawLightning(now: number, dt: number, perMinute: number): void {\n if (this.reduceMotion) return;\n const ctx = this.fx;\n if (perMinute > 0 && now > this.nextBolt) {\n this.flash = 1;\n this.bolt = this.makeBolt();\n this.nextBolt = now + lightningInterval(perMinute, Math.random());\n }\n if (this.flash <= 0) return;\n ctx.fillStyle = `rgba(255,255,255,${(this.flash * 0.35).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (this.bolt && this.flash > 0.35) {\n ctx.strokeStyle = `rgba(255,255,255,${this.flash.toFixed(2)})`;\n ctx.lineWidth = 2.2;\n ctx.beginPath();\n this.bolt.forEach(([x, y], i) => {\n if (i === 0) ctx.moveTo(x, y);\n else ctx.lineTo(x, y);\n });\n ctx.stroke();\n }\n this.flash -= dt * 3.2;\n }\n\n private makeBolt(): [number, number][] {\n let x = this.width * (0.2 + Math.random() * 0.6);\n let y = 0;\n const points: [number, number][] = [[x, y]];\n const reach = this.height * (0.5 + Math.random() * 0.3);\n while (y < reach) {\n x += (Math.random() - 0.5) * 36;\n y += 14 + Math.random() * 22;\n points.push([x, y]);\n }\n return points;\n }\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport { WeatherRenderer, type WeatherTheme } from \"./renderer.js\";\n\n/** Bagian peta LuciadRIA yang dipakai overlay. Sengaja struktural supaya tidak bergantung pada RIA. */\nexport interface MapLike {\n readonly domNode: HTMLElement;\n readonly reference?: { readonly identifier: string };\n readonly camera?: {\n asLook2D?: () => { rotation: number };\n asLookAt?: (distance: number) => { yaw: number };\n };\n on(event: \"MapChange\", callback: () => void): { remove(): void };\n}\n\nexport interface WeatherOverlayOptions {\n /** `auto` mengikuti `data-theme` pada <html>, lalu `prefers-color-scheme`. */\n theme?: WeatherTheme | \"auto\";\n zIndex?: number;\n maxFps?: number;\n /** Ganti cara menghitung putaran peta (derajat) bila peta Anda berbeda. */\n getScreenOffset?: (map: MapLike) => number;\n}\n\nexport interface WeatherOverlay {\n readonly renderer: WeatherRenderer;\n setState(state: AnimationState): void;\n setVisible(visible: boolean): void;\n setOpacity(opacity: number): void;\n destroy(): void;\n}\n\n/**\n * Putaran peta terhadap utara, supaya arah angin di layar tetap benar: 2D memakai rotasi kamera,\n * 3D memakai yaw kamera. Keduanya dibalik karena peta berputar berlawanan dengan kamera.\n * Tanda ini perlu dicek di peta sungguhan (putar peta, lihat arah partikel).\n */\nexport function mapScreenOffset(map: MapLike): number {\n const camera = map.camera;\n if (!camera) return 0;\n try {\n if (map.reference?.identifier === \"EPSG:4978\") {\n return -(camera.asLookAt?.(1000).yaw ?? 0);\n }\n return -(camera.asLook2D?.().rotation ?? 0);\n } catch {\n return 0;\n }\n}\n\nfunction detectTheme(): WeatherTheme {\n const explicit = document.documentElement.dataset.theme;\n if (explicit === \"dark\" || explicit === \"light\") return explicit;\n return matchMedia?.(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/** Pasang overlay cuaca (canvas transparan) di atas peta; hapus dengan `destroy()`. */\nexport function createWeatherOverlay(\n map: MapLike,\n options: WeatherOverlayOptions = {},\n): WeatherOverlay {\n const container = map.domNode;\n if (getComputedStyle(container).position === \"static\") {\n container.style.position = \"relative\";\n }\n\n const theme = options.theme ?? \"auto\";\n const renderer = new WeatherRenderer({\n container,\n theme: theme === \"auto\" ? detectTheme() : theme,\n zIndex: options.zIndex,\n maxFps: options.maxFps,\n });\n const offsetOf = options.getScreenOffset ?? mapScreenOffset;\n\n let width = container.clientWidth;\n let height = container.clientHeight;\n const handle = map.on(\"MapChange\", () => {\n renderer.setScreenOffset(offsetOf(map));\n if (container.clientWidth !== width || container.clientHeight !== height) {\n width = container.clientWidth;\n height = container.clientHeight;\n renderer.resize();\n }\n });\n\n const themeObserver =\n theme === \"auto\"\n ? new MutationObserver(() => renderer.setTheme(detectTheme()))\n : null;\n themeObserver?.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n\n renderer.setScreenOffset(offsetOf(map));\n renderer.start();\n\n return {\n renderer,\n setState: (state) => renderer.setState(state),\n setVisible: (visible) => renderer.setVisible(visible),\n setOpacity: (opacity) => renderer.setOpacity(opacity),\n destroy() {\n handle.remove();\n themeObserver?.disconnect();\n renderer.destroy();\n },\n };\n}\n","import type { ScalarGrid } from \"@aeroswarm/weather\";\nimport { CLOUD_RAMP, type RampStop, rampColor } from \"./colorRamp.js\";\nimport { buildScreenScalarField, type Unproject } from \"./screenField.js\";\n\nexport interface ScalarRendererOptions {\n container: HTMLElement;\n unproject: Unproject;\n ramp?: readonly RampStop[];\n zIndex?: number;\n}\n\n/**\n * Peta warna dari grid skalar (tutupan awan, hujan, suhu): tiap sel layar diwarnai dari nilai grid,\n * digambar halus. Digambar ulang hanya saat grid atau peta berubah, bukan tiap frame.\n */\nexport class ScalarRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly small = document.createElement(\"canvas\");\n private grid: ScalarGrid | null = null;\n private ramp: readonly RampStop[];\n private width = 1;\n private height = 1;\n private dpr = 1;\n\n constructor(private readonly options: ScalarRendererOptions) {\n this.ramp = options.ramp ?? CLOUD_RAMP;\n this.canvas = document.createElement(\"canvas\");\n this.canvas.dataset.weatherLayer = \"scalar\";\n Object.assign(this.canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(options.zIndex ?? 3),\n });\n options.container.appendChild(this.canvas);\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.ctx = ctx;\n this.resize();\n }\n\n setGrid(grid: ScalarGrid | null, ramp?: readonly RampStop[]): void {\n this.grid = grid;\n if (ramp) this.ramp = ramp;\n this.draw();\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n this.canvas.width = this.width * this.dpr;\n this.canvas.height = this.height * this.dpr;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.draw();\n }\n\n /** Gambar ulang setelah peta bergerak atau di-zoom. */\n draw(): void {\n const ctx = this.ctx;\n ctx.clearRect(0, 0, this.width, this.height);\n if (!this.grid) return;\n const field = buildScreenScalarField(this.grid, this.width, this.height, this.options.unproject);\n this.small.width = field.cols;\n this.small.height = field.rows;\n const small = this.small.getContext(\"2d\");\n if (!small) return;\n const image = small.createImageData(field.cols, field.rows);\n for (let i = 0; i < field.values.length; i++) {\n const value = field.values[i] as number;\n if (Number.isNaN(value)) continue;\n const [r, g, b, a] = rampColor(this.ramp, value);\n image.data[i * 4] = r;\n image.data[i * 4 + 1] = g;\n image.data[i * 4 + 2] = b;\n image.data[i * 4 + 3] = Math.round(a * 255);\n }\n small.putImageData(image, 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(this.small, 0, 0, field.cols * field.cell, field.rows * field.cell);\n }\n\n setVisible(visible: boolean): void {\n this.canvas.style.display = visible ? \"block\" : \"none\";\n }\n\n setOpacity(opacity: number): void {\n this.canvas.style.opacity = String(Math.min(1, Math.max(0, opacity)));\n }\n\n destroy(): void {\n this.canvas.remove();\n }\n}\n","import type { ScalarGrid, WindGrid } from \"@aeroswarm/weather\";\nimport type { RampStop } from \"./colorRamp.js\";\nimport { FlowRenderer } from \"./flowRenderer.js\";\nimport { type MapLike, mapScreenOffset } from \"./overlay.js\";\nimport { ScalarRenderer } from \"./scalarRenderer.js\";\nimport type { Unproject } from \"./screenField.js\";\n\nexport interface FieldOverlayOptions {\n /** Titik layar ke lon/lat; lihat `createRiaUnproject`. */\n unproject: Unproject;\n zIndex?: number;\n maxFps?: number;\n pixelsPerMs?: number;\n getScreenOffset?: (map: MapLike) => number;\n}\n\nexport interface FieldOverlay {\n /** Aliran partikel angin dari grid; null untuk menghapus. */\n setWind(grid: WindGrid | null): void;\n /** Peta warna dari grid skalar (awan, hujan, suhu); null untuk menghapus. */\n setScalar(grid: ScalarGrid | null, ramp?: readonly RampStop[]): void;\n setVisible(visible: boolean): void;\n setOpacity(opacity: number): void;\n destroy(): void;\n}\n\n/**\n * Overlay medan cuaca ala Windy di atas peta: peta warna (di bawah) dan aliran partikel angin (di\n * atas). Medan dipetakan ulang ke layar tiap peta bergerak (dibatasi ~10 kali per detik).\n */\nexport function createFieldOverlay(map: MapLike, options: FieldOverlayOptions): FieldOverlay {\n const container = map.domNode;\n if (getComputedStyle(container).position === \"static\") container.style.position = \"relative\";\n const offsetOf = options.getScreenOffset ?? mapScreenOffset;\n\n const scalar = new ScalarRenderer({ container, unproject: options.unproject, zIndex: options.zIndex });\n const flow = new FlowRenderer({\n container,\n unproject: options.unproject,\n zIndex: (options.zIndex ?? 3) + 1,\n maxFps: options.maxFps,\n pixelsPerMs: options.pixelsPerMs,\n });\n\n let width = container.clientWidth;\n let height = container.clientHeight;\n let pending: ReturnType<typeof setTimeout> | null = null;\n const refresh = (): void => {\n pending = null;\n if (container.clientWidth !== width || container.clientHeight !== height) {\n width = container.clientWidth;\n height = container.clientHeight;\n scalar.resize();\n flow.resize();\n }\n flow.setScreenOffset(offsetOf(map));\n flow.refreshField();\n scalar.draw();\n };\n const handle = map.on(\"MapChange\", () => {\n if (pending === null) pending = setTimeout(refresh, 100);\n });\n\n flow.setScreenOffset(offsetOf(map));\n flow.start();\n\n return {\n setWind: (grid) => flow.setWind(grid),\n setScalar: (grid, ramp) => scalar.setGrid(grid, ramp),\n setVisible(visible) {\n flow.setVisible(visible);\n scalar.setVisible(visible);\n },\n setOpacity(opacity) {\n flow.setOpacity(opacity);\n scalar.setOpacity(opacity);\n },\n destroy() {\n if (pending !== null) clearTimeout(pending);\n handle.remove();\n flow.destroy();\n scalar.destroy();\n },\n };\n}\n","import type { Unproject } from \"./screenField.js\";\n\n/** Bagian RIA yang dipakai `createRiaUnproject`; diberikan oleh aplikasi (injeksi). */\nexport interface RiaUnprojectDeps {\n /** `getReference(\"EPSG:4326\")`. */\n wgs84: unknown;\n createPoint: (reference: unknown, coordinates: number[]) => unknown;\n createTransformation: (source: unknown, destination: unknown) => {\n transform(point: unknown): { x: number; y: number };\n };\n}\n\ninterface UnprojectableMap {\n readonly reference: unknown;\n readonly viewToMapTransformation: { transform(point: unknown): unknown };\n}\n\n/**\n * Fungsi titik layar -> lon/lat untuk peta RIA 2D maupun 3D. Mengembalikan null untuk titik yang tidak\n * jatuh pada peta (di 3D: di luar bola bumi), jadi aliran partikel berhenti di sana.\n */\nexport function createRiaUnproject(map: UnprojectableMap, deps: RiaUnprojectDeps): Unproject {\n const toWgs84 = deps.createTransformation(map.reference, deps.wgs84);\n return (x, y) => {\n try {\n const mapPoint = map.viewToMapTransformation.transform(deps.createPoint(null, [x, y, 0]));\n const point = toWgs84.transform(mapPoint);\n return Number.isFinite(point.x) && Number.isFinite(point.y) ? { lon: point.x, lat: point.y } : null;\n } catch {\n return null;\n }\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/clouds3d.ts","../src/cloudLayer.ts","../src/colorRamp.ts","../src/controller.ts","../src/screenField.ts","../src/math.ts","../src/flowRenderer.ts","../src/renderer.ts","../src/overlay.ts","../src/scalarRenderer.ts","../src/fieldOverlay.ts","../src/riaUnproject.ts"],"sourcesContent":["import type { AnimationState } from \"@aeroswarm/weather\";\n\n/** Satu gumpalan awan di sekitar bandara, dalam meter dari titik pusat. */\nexport interface CloudPuff3D {\n id: string;\n eastM: number;\n northM: number;\n /** Ketinggian di atas permukaan laut (m): elevasi bandara + tinggi dasar awan (AGL). */\n altM: number;\n sizeM: number;\n opacity: number;\n convective: boolean;\n}\n\nexport interface CloudFieldOptions {\n /** Radius sebaran awan dari bandara (m). Bawaan 12.000. */\n radiusM?: number;\n /** Elevasi bandara (m di atas laut). METAR memberi tinggi awan AGL, jadi ini ditambahkan. */\n elevationM?: number;\n /** Biji acak supaya letak awan konsisten antar render. */\n seed?: number;\n}\n\nconst FT_TO_M = 0.3048;\nconst KT_TO_MS = 0.514_444;\nconst DEFAULT_BASE_M = 600;\nconst MAX_PER_LAYER = 14;\n\n/** Pembangkit acak kecil (mulberry32) agar medan awan deterministik. */\nexport function seededRandom(seed: number): () => number {\n let a = seed >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Sebar gumpalan awan di sekitar bandara dari lapisan awan METAR/TAF. Banyaknya menurut tutupan,\n * ketinggiannya = elevasi bandara + tinggi dasar (AGL). Awan konvektif lebih besar dan lebih padat.\n */\nexport function buildCloudField(\n state: AnimationState,\n options: CloudFieldOptions = {},\n): CloudPuff3D[] {\n const radius = options.radiusM ?? 12_000;\n const elevation = options.elevationM ?? 0;\n const random = seededRandom(options.seed ?? 1);\n const field: CloudPuff3D[] = [];\n\n state.clouds.layers.forEach((layer, layerIndex) => {\n const count = Math.max(1, Math.round(layer.coverage * MAX_PER_LAYER));\n const baseM = layer.baseFt === null ? DEFAULT_BASE_M : layer.baseFt * FT_TO_M;\n for (let i = 0; i < count; i++) {\n const angle = random() * Math.PI * 2;\n const distance = Math.sqrt(random()) * radius;\n const size = (layer.convective ? 1400 : 800) * (0.6 + random() * 0.9);\n field.push({\n id: `cloud-${layerIndex}-${i}`,\n eastM: Math.sin(angle) * distance,\n northM: Math.cos(angle) * distance,\n altM: elevation + baseM + random() * (layer.convective ? 400 : 120),\n sizeM: size,\n opacity: Math.min(0.9, 0.3 + layer.coverage * 0.5) * (layer.convective ? 1.1 : 1),\n convective: layer.convective,\n });\n }\n });\n return field;\n}\n\n/**\n * Geser awan searah angin (`toDeg`, derajat dari utara). `timeScale` mempercepat gerak supaya\n * terlihat (1 = kecepatan nyata, yang nyaris tidak terlihat dari ketinggian peta). Awan yang\n * keluar dari radius muncul lagi di sisi seberang.\n */\nexport function advanceCloudField(\n field: readonly CloudPuff3D[],\n wind: { toDeg: number | null; speedKt: number },\n dtSeconds: number,\n options: { radiusM?: number; timeScale?: number } = {},\n): CloudPuff3D[] {\n if (wind.toDeg === null || wind.speedKt <= 0) return field.map((c) => ({ ...c }));\n const radius = options.radiusM ?? 12_000;\n const meters = wind.speedKt * KT_TO_MS * dtSeconds * (options.timeScale ?? 12);\n const east = Math.sin((wind.toDeg * Math.PI) / 180) * meters;\n const north = Math.cos((wind.toDeg * Math.PI) / 180) * meters;\n\n return field.map((cloud) => {\n let eastM = cloud.eastM + east;\n let northM = cloud.northM + north;\n if (Math.hypot(eastM, northM) > radius) {\n // Muncul lagi di sisi seberang lingkaran.\n eastM = -eastM * 0.98;\n northM = -northM * 0.98;\n }\n return { ...cloud, eastM, northM };\n });\n}\n\n/** Titik lat/lon dari selisih meter timur/utara terhadap pusat (pendekatan datar, cukup untuk ~20 km). */\nexport function offsetToLonLat(\n centerLat: number,\n centerLon: number,\n eastM: number,\n northM: number,\n): { lat: number; lon: number } {\n const metersPerDegLat = 111_320;\n const metersPerDegLon = metersPerDegLat * Math.cos((centerLat * Math.PI) / 180);\n return {\n lat: centerLat + northM / metersPerDegLat,\n lon: centerLon + eastM / metersPerDegLon,\n };\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport {\n advanceCloudField,\n buildCloudField,\n type CloudPuff3D,\n offsetToLonLat,\n} from \"./clouds3d.js\";\n\n/**\n * Kelas dan fungsi LuciadRIA yang dipakai layer awan. Diberikan oleh pemakai (injeksi) supaya package\n * ini tidak punya dependensi ke RIA dan memakai salinan RIA milik aplikasi yang sama dengan petanya.\n */\nexport interface RiaCloudDeps {\n /** Referensi EPSG:4326, mis. `getReference(\"EPSG:4326\")`. */\n reference: unknown;\n Feature: new (\n shape: unknown,\n properties: Record<string, unknown>,\n id?: string,\n ) => unknown;\n FeatureModel: new (store: unknown, options: { reference: unknown }) => CloudModel;\n MemoryStore: new () => unknown;\n FeatureLayer: new (model: CloudModel, options: Record<string, unknown>) => CloudFeatureLayer;\n FeaturePainter: new () => CloudPainter;\n createPoint: (reference: unknown, coordinates: number[]) => unknown;\n}\n\nexport interface CloudModel {\n put(feature: unknown): unknown;\n remove(id: string): unknown;\n}\n\nexport interface CloudFeatureLayer {\n visible: boolean;\n}\n\ninterface CloudPainter {\n getDetailLevelScales?: () => number[];\n paintBody: (\n geoCanvas: { drawIcon(shape: unknown, style: Record<string, unknown>): void },\n feature: { properties: Record<string, unknown> },\n shape: unknown,\n layer: unknown,\n map: { reference: { identifier: string } },\n ) => void;\n}\n\nexport interface CloudLayerOptions {\n /** Pusat sebaran awan (bandara). */\n center: { lat: number; lon: number };\n /** Elevasi bandara (m di atas laut), ditambahkan ke tinggi dasar awan AGL. Bawaan 0. */\n elevationM?: number;\n radiusM?: number;\n /** Awan hanya digambar di peta 3D (EPSG:4978). Bawaan true. */\n only3D?: boolean;\n /** Percepatan gerak awan. Bawaan 12. */\n timeScale?: number;\n /** Selang pembaruan posisi (ms). Bawaan 500. */\n updateIntervalMs?: number;\n id?: string;\n}\n\nexport interface CloudLayer3D {\n readonly layer: CloudFeatureLayer;\n /** Ganti kondisi cuaca: awan dibangun ulang dari lapisan awan METAR/TAF. */\n setState(state: AnimationState): void;\n setVisible(visible: boolean): void;\n destroy(): void;\n}\n\n/** Sprite gumpalan awan sebagai data URL (gradien radial lembut). */\nexport function makeCloudSpriteUrl(): string {\n const canvas = document.createElement(\"canvas\");\n canvas.width = canvas.height = 128;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return \"\";\n const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);\n gradient.addColorStop(0, \"rgba(255,255,255,0.95)\");\n gradient.addColorStop(0.55, \"rgba(255,255,255,0.45)\");\n gradient.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 128, 128);\n return canvas.toDataURL(\"image/png\");\n}\n\n/**\n * Layer RIA berisi gumpalan awan di ketinggian sebenarnya (elevasi bandara + tinggi dasar AGL),\n * berukuran dalam meter sehingga ikut zoom dan perspektif 3D. Tambahkan ke peta dengan\n * `map.layerTree.addChild(cloud.layer)`.\n */\nexport function createCloudLayer3D(\n ria: RiaCloudDeps,\n options: CloudLayerOptions,\n): CloudLayer3D {\n const model = new ria.FeatureModel(new ria.MemoryStore(), { reference: ria.reference });\n const painter = new ria.FeaturePainter();\n const spriteUrl = makeCloudSpriteUrl();\n const only3D = options.only3D ?? true;\n\n painter.getDetailLevelScales = () => [1 / 100000, 1 / 20000, 1 / 5000];\n painter.paintBody = (geoCanvas, feature, shape, _layer, map) => {\n if (only3D && map.reference.identifier !== \"EPSG:4978\") return;\n const size = Number(feature.properties.sizeM) || 800;\n geoCanvas.drawIcon(shape, {\n url: spriteUrl,\n width: `${size}m`,\n height: `${size}m`,\n opacity: Number(feature.properties.opacity) || 0.5,\n draped: false,\n modulationColor: feature.properties.convective === true ? \"#8f98a3\" : \"#ffffff\",\n });\n };\n\n const layer = new ria.FeatureLayer(model, {\n id: options.id ?? \"weather-clouds\",\n label: \"Awan\",\n painter,\n selectable: false,\n visible: true,\n });\n\n let wind: { toDeg: number | null; speedKt: number } = { toDeg: null, speedKt: 0 };\n let field: CloudPuff3D[] = [];\n let knownIds = new Set<string>();\n\n const render = (): void => {\n const next = new Set<string>();\n for (const cloud of field) {\n const { lat, lon } = offsetToLonLat(\n options.center.lat,\n options.center.lon,\n cloud.eastM,\n cloud.northM,\n );\n model.put(\n new ria.Feature(\n ria.createPoint(ria.reference, [lon, lat, cloud.altM]),\n { sizeM: cloud.sizeM, opacity: cloud.opacity, convective: cloud.convective },\n cloud.id,\n ),\n );\n next.add(cloud.id);\n }\n for (const id of knownIds) if (!next.has(id)) model.remove(id);\n knownIds = next;\n };\n\n const interval = options.updateIntervalMs ?? 500;\n const timer = setInterval(() => {\n if (!layer.visible || field.length === 0) return;\n field = advanceCloudField(field, wind, interval / 1000, {\n radiusM: options.radiusM,\n timeScale: options.timeScale,\n });\n render();\n }, interval);\n\n return {\n layer,\n setState(state) {\n wind = { toDeg: state.wind.toDeg, speedKt: state.wind.speedKt };\n field = buildCloudField(state, {\n radiusM: options.radiusM,\n elevationM: options.elevationM,\n });\n render();\n },\n setVisible(visible) {\n layer.visible = visible;\n },\n destroy() {\n clearInterval(timer);\n for (const id of knownIds) model.remove(id);\n knownIds = new Set();\n field = [];\n },\n };\n}\n","/** Warna: [nilai, merah, hijau, biru, alfa 0-1]. Nilai harus naik. */\nexport type RampStop = readonly [number, number, number, number, number?];\nexport type Rgba = readonly [number, number, number, number];\n\n/** Warna di `value` dengan interpolasi linear antar titik; di luar rentang dipakai titik ujung. */\nexport function rampColor(stops: readonly RampStop[], value: number): Rgba {\n const first = stops[0];\n const last = stops[stops.length - 1];\n if (!first || !last) return [0, 0, 0, 0];\n if (Number.isNaN(value) || value <= first[0]) return [first[1], first[2], first[3], first[4] ?? 1];\n if (value >= last[0]) return [last[1], last[2], last[3], last[4] ?? 1];\n for (let i = 1; i < stops.length; i++) {\n const hi = stops[i] as RampStop;\n const lo = stops[i - 1] as RampStop;\n if (value <= hi[0]) {\n const t = (value - lo[0]) / (hi[0] - lo[0] || 1);\n const mix = (a: number, b: number): number => a + (b - a) * t;\n return [mix(lo[1], hi[1]), mix(lo[2], hi[2]), mix(lo[3], hi[3]), mix(lo[4] ?? 1, hi[4] ?? 1)];\n }\n }\n return [last[1], last[2], last[3], last[4] ?? 1];\n}\n\nexport const rgbaCss = ([r, g, b, a]: Rgba, alpha = 1): string =>\n `rgba(${Math.round(r)},${Math.round(g)},${Math.round(b)},${(a * alpha).toFixed(3)})`;\n\n/** Kecepatan angin (m/s): dari biru dingin (tenang) ke merah (kencang), seperti peta angin umum. */\nexport const WIND_RAMP: readonly RampStop[] = [\n [0, 98, 113, 184],\n [2, 61, 110, 163],\n [4, 74, 148, 170],\n [7, 74, 146, 115],\n [10, 77, 142, 56],\n [13, 160, 168, 50],\n [17, 210, 168, 48],\n [22, 220, 110, 40],\n [28, 190, 50, 50],\n [36, 150, 40, 120],\n];\n\n/** Tutupan awan (%): bening ke putih keabu-abuan. */\nexport const CLOUD_RAMP: readonly RampStop[] = [\n [0, 255, 255, 255, 0],\n [20, 235, 238, 242, 0.15],\n [60, 205, 210, 218, 0.45],\n [100, 150, 158, 170, 0.75],\n];\n\n/** Curah hujan (mm/jam). */\nexport const PRECIPITATION_RAMP: readonly RampStop[] = [\n [0, 120, 190, 230, 0],\n [0.1, 120, 190, 230, 0.45],\n [1, 70, 190, 200, 0.7],\n [3, 80, 190, 110, 0.78],\n [6, 210, 220, 60, 0.82],\n [12, 240, 170, 40, 0.85],\n [25, 225, 80, 50, 0.88],\n [50, 170, 40, 150, 0.9],\n];\n\n/** Suhu (°C). */\nexport const TEMPERATURE_RAMP: readonly RampStop[] = [\n [10, 80, 90, 200, 0.7],\n [20, 70, 170, 200, 0.7],\n [26, 120, 200, 110, 0.7],\n [30, 235, 200, 70, 0.72],\n [34, 235, 130, 50, 0.75],\n [40, 200, 50, 50, 0.8],\n];\n","import type {\n FieldGridOptions,\n FieldSet,\n ScalarGrid,\n Timeline,\n WeatherGridSeries,\n WeatherStore,\n WindGrid,\n} from \"@aeroswarm/weather\";\nimport { seriesFieldsAt, seriesRange } from \"@aeroswarm/weather\";\nimport { CLOUD_RAMP, PRECIPITATION_RAMP, type RampStop, rgbaCss, rampColor, TEMPERATURE_RAMP } from \"./colorRamp.js\";\nimport type { FieldOverlay } from \"./fieldOverlay.js\";\n\nexport type ColorLayer = \"cloud\" | \"rain\" | \"temperature\";\n\nexport const LAYER_RAMPS: Record<ColorLayer, readonly RampStop[]> = {\n cloud: CLOUD_RAMP,\n rain: PRECIPITATION_RAMP,\n temperature: TEMPERATURE_RAMP,\n};\n\nexport interface LegendEntry {\n value: number;\n color: string;\n}\n\n/** Titik legenda (nilai dan warna CSS) untuk lapisan warna, untuk digambar sebagai palet di UI. */\nexport function legendFor(layer: ColorLayer): { unit: string; entries: LegendEntry[] } {\n const stops = LAYER_RAMPS[layer];\n const unit = layer === \"cloud\" ? \"%\" : layer === \"rain\" ? \"mm/jam\" : \"°C\";\n return {\n unit,\n entries: stops.map((s) => ({ value: s[0], color: rgbaCss(rampColor(stops, s[0]), 1) })),\n };\n}\n\nexport interface ControllerState {\n /** Waktu yang ditampilkan; null = langsung (waktu sekarang). */\n time: Date | null;\n playing: boolean;\n wind: boolean;\n color: ColorLayer | null;\n /** Stasiun (atau sumber model) yang dipakai pada waktu yang ditampilkan. */\n stations: string[];\n /** Medan model disesuaikan dengan laporan METAR/SPECI/TAF di sekitar tiap bandara. */\n blend: boolean;\n /** Rentang dan pembagian waktu yang bisa digeser/diputar; null bila belum ada data. */\n timeline: Timeline | null;\n}\n\nexport interface WeatherControllerOptions {\n store: WeatherStore;\n overlay: FieldOverlay;\n grid: FieldGridOptions;\n /** Jam di timeline per detik saat diputar. Bawaan 2. */\n hoursPerSecond?: number;\n /** Jarak pengaruh laporan stasiun terhadap medan model saat digabung (km). Bawaan 80. */\n blendRadiusKm?: number;\n /** Ikutkan PROB30/PROB40 dari TAF. Bawaan false. */\n includeProbable?: boolean;\n /** Sumber waktu sekarang (untuk tes). */\n now?: () => Date;\n}\n\n/**\n * Mengikat `WeatherStore` ke overlay peta: tiap data berubah atau waktu bergeser, medan dihitung\n * ulang dan digambar. Waktu bisa langsung (sekarang), digeser, atau diputar sepanjang timeline.\n */\nexport class WeatherController {\n private current: ControllerState = { time: null, playing: false, wind: false, color: null, stations: [], blend: false, timeline: null };\n private series: WeatherGridSeries | null = null;\n private window: { from: Date; to: Date } | null = null;\n private readonly listeners = new Set<(state: ControllerState) => void>();\n private readonly unsubscribe: () => void;\n private liveTimer: ReturnType<typeof setInterval> | null = null;\n private playTimer: ReturnType<typeof setInterval> | null = null;\n private hoursPerSecond: number;\n private external: { wind: WindGrid | null; scalar: ScalarGrid | null } = { wind: null, scalar: null };\n private lastDrawn = Number.NaN;\n private lastVersion = -1;\n\n constructor(private readonly options: WeatherControllerOptions) {\n this.hoursPerSecond = options.hoursPerSecond ?? 2;\n this.unsubscribe = options.store.onChange(() => {\n this.refreshTimeline();\n this.render();\n });\n this.refreshTimeline();\n // Mode langsung: perbarui tiap menit agar sumber berganti saat pengamatan menua.\n this.liveTimer = setInterval(() => {\n if (this.current.time === null) this.render(true);\n }, 60_000);\n }\n\n get state(): ControllerState {\n return this.current;\n }\n\n /** Waktu yang sedang ditampilkan (sekarang bila langsung). */\n get effectiveTime(): Date {\n return this.current.time ?? this.now();\n }\n\n /**\n * Rentang waktu yang bisa dipilih: periode seri model bila ada, selain itu periode TAF dan pengamatan\n * dari laporan.\n */\n timeline(): Timeline | null {\n let timeline: Timeline | null;\n if (this.series) {\n const { from, to } = seriesRange(this.series);\n timeline = { from, to, segments: [], observations: [] };\n } else {\n timeline = this.options.store.timeline();\n }\n if (!timeline || !this.window) return timeline;\n\n // Batasi ke jendela yang dipilih (mis. 24 jam hari ini); kosong bila tidak beririsan dengan data.\n const from = new Date(Math.max(timeline.from.getTime(), this.window.from.getTime()));\n const to = new Date(Math.min(timeline.to.getTime(), this.window.to.getTime()));\n if (from.getTime() >= to.getTime()) return null;\n return {\n from,\n to,\n segments: timeline.segments.filter((s) => s.to.getTime() > from.getTime() && s.from.getTime() < to.getTime()),\n observations: timeline.observations.filter((d) => d.getTime() >= from.getTime() && d.getTime() <= to.getTime()),\n };\n }\n\n /**\n * Batasi timeline (slider dan putar) ke satu jendela waktu, mis. 24 jam hari ini atau 72 jam ke\n * depan. `null` melepas batas. Waktu yang sedang tampil di luar jendela dikembalikan ke awalnya.\n */\n setWindow(window: { from: Date; to: Date } | null): void {\n this.window = window;\n const timeline = this.timeline();\n const shown = this.current.time;\n if (timeline && shown && (shown.getTime() < timeline.from.getTime() || shown.getTime() > timeline.to.getTime())) {\n this.update({ time: timeline.from });\n }\n this.refreshTimeline();\n this.render(true);\n }\n\n /**\n * Seri model berstandar `aeroswarm.weather-grid`: dipakai menggantikan medan dari laporan, dengan\n * timeline sepanjang seri. `null` kembali ke laporan. Grid dari `setExternalFields` tetap menang.\n */\n /**\n * Gabungkan seri model dengan laporan: grid model ditarik ke pengamatan METAR/SPECI (atau prakiraan\n * TAF untuk waktu ke depan) di bandara yang punya koordinat. Hanya berlaku bila ada seri model.\n */\n setBlend(blend: boolean): void {\n this.update({ blend });\n this.render(true);\n }\n\n setSeries(series: WeatherGridSeries | null): void {\n this.series = series;\n this.refreshTimeline();\n this.render(true);\n }\n\n onState(listener: (state: ControllerState) => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n setWind(wind: boolean): void {\n this.update({ wind });\n this.render(true);\n }\n\n setColorLayer(color: ColorLayer | null): void {\n this.update({ color });\n this.render(true);\n }\n\n /** Tampilkan waktu tertentu; null kembali ke langsung. */\n setTime(time: Date | null): void {\n this.update({ time });\n if (time === null) this.pause();\n this.render(true);\n }\n\n /**\n * Grid dari sumber lain (berkas, layanan luar) yang dipakai sebagai pengganti medan hasil laporan\n * METAR/TAF. `null` mengembalikan ke medan dari laporan.\n */\n setExternalFields(fields: { wind?: WindGrid | null; scalar?: ScalarGrid | null }): void {\n this.external = {\n wind: fields.wind === undefined ? this.external.wind : fields.wind,\n scalar: fields.scalar === undefined ? this.external.scalar : fields.scalar,\n };\n this.render(true);\n }\n\n setSpeed(hoursPerSecond: number): void {\n this.hoursPerSecond = Math.max(0.1, hoursPerSecond);\n }\n\n play(): void {\n if (this.playTimer) return;\n const timeline = this.timeline();\n if (!timeline) return;\n // Mulai dari waktu yang sedang tampil, atau awal timeline bila langsung/di luar rentang.\n const start = this.effectiveTime;\n if (start.getTime() < timeline.from.getTime() || start.getTime() >= timeline.to.getTime()) {\n this.update({ time: timeline.from });\n } else if (this.current.time === null) {\n this.update({ time: start });\n }\n this.update({ playing: true });\n const interval = 200;\n this.playTimer = setInterval(() => {\n const tl = this.timeline();\n if (!tl || this.current.time === null) return;\n this.update({\n time: advanceTime(this.current.time, (interval / 1000) * this.hoursPerSecond * 3_600_000, tl),\n });\n this.render();\n }, interval);\n this.render(true);\n }\n\n pause(): void {\n if (this.playTimer) clearInterval(this.playTimer);\n this.playTimer = null;\n if (this.current.playing) this.update({ playing: false });\n }\n\n destroy(): void {\n this.pause();\n if (this.liveTimer) clearInterval(this.liveTimer);\n this.liveTimer = null;\n this.unsubscribe();\n this.listeners.clear();\n this.options.overlay.setWind(null);\n this.options.overlay.setScalar(null);\n }\n\n /** Hitung ulang medan dan gambar. `force` mengabaikan pembatasan \"belum berubah\". */\n render(force = false): void {\n const time = this.effectiveTime;\n const { store } = this.options;\n const minute = Math.floor(time.getTime() / 60_000);\n if (!force && minute === this.lastDrawn && store.version === this.lastVersion) return;\n this.lastDrawn = minute;\n this.lastVersion = store.version;\n\n const { wind, color } = this.current;\n const needFields = (wind && !this.external.wind) || (color !== null && !this.external.scalar);\n let fields: FieldSet | null = null;\n if (needFields && this.series) {\n const model = seriesFieldsAt(this.series, time);\n if (model) {\n const name = this.series.source.name;\n fields = this.current.blend\n ? store.blendInto(model, name, time, {\n radiusKm: this.options.blendRadiusKm,\n includeProbable: this.options.includeProbable,\n })\n : { ...model, stations: [name] };\n }\n } else if (needFields) {\n fields = store.fieldsAt(time, this.options.grid, { includeProbable: this.options.includeProbable });\n }\n\n this.options.overlay.setWind(wind ? (this.external.wind ?? fields?.wind ?? null) : null);\n const fromReports = fields && color ? (color === \"temperature\" ? fields.temperature : fields[color]) : null;\n const scalar = color ? (this.external.scalar ?? fromReports) : null;\n this.options.overlay.setScalar(scalar, color ? LAYER_RAMPS[color] : undefined);\n this.update({ stations: fields?.stations ?? [] });\n }\n\n private refreshTimeline(): void {\n this.update({ timeline: this.timeline() });\n }\n\n private now(): Date {\n return this.options.now?.() ?? new Date();\n }\n\n private update(patch: Partial<ControllerState>): void {\n this.current = { ...this.current, ...patch };\n for (const listener of this.listeners) listener(this.current);\n }\n}\n\n/** Majukan waktu sebesar `deltaMs`; melewati ujung timeline kembali ke awal (berulang). */\nexport function advanceTime(time: Date, deltaMs: number, timeline: { from: Date; to: Date }): Date {\n const next = time.getTime() + deltaMs;\n if (next >= timeline.to.getTime() || next < timeline.from.getTime()) return new Date(timeline.from);\n return new Date(next);\n}\n","import {\n directionFromUV,\n sampleScalar,\n sampleWind,\n type ScalarGrid,\n speedOf,\n type WindGrid,\n} from \"@aeroswarm/weather\";\nimport { screenVector } from \"./math.js\";\n\n/** Titik layar (piksel) ke koordinat geografis; null bila di luar peta (mis. di luar bola bumi). */\nexport type Unproject = (x: number, y: number) => { lon: number; lat: number } | null;\n\n/** Angin yang sudah dipetakan ke kisi layar: satu vektor arah layar dan kecepatan per sel. */\nexport interface ScreenWindField {\n cols: number;\n rows: number;\n cell: number;\n /** Vektor satuan arah gerak di layar (x kanan, y bawah). */\n dx: Float32Array;\n dy: Float32Array;\n /** Kecepatan (m/s); NaN = tanpa data. */\n speed: Float32Array;\n}\n\nexport interface ScreenScalarField {\n cols: number;\n rows: number;\n cell: number;\n values: Float32Array;\n}\n\n/**\n * Ubah grid angin geografis menjadi kisi layar. Dilakukan sekali tiap peta bergerak, supaya partikel\n * tinggal membaca kisi (tanpa memproyeksikan ulang tiap partikel tiap frame).\n */\nexport function buildScreenWindField(\n grid: WindGrid,\n width: number,\n height: number,\n unproject: Unproject,\n screenOffsetDeg: number,\n cell = 24,\n): ScreenWindField {\n const cols = Math.max(1, Math.ceil(width / cell));\n const rows = Math.max(1, Math.ceil(height / cell));\n const dx = new Float32Array(cols * rows);\n const dy = new Float32Array(cols * rows);\n const speed = new Float32Array(cols * rows).fill(Number.NaN);\n\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const ll = unproject((col + 0.5) * cell, (row + 0.5) * cell);\n const wind = ll ? sampleWind(grid, ll.lon, ll.lat) : null;\n if (!wind) continue;\n const index = row * cols + col;\n const vector = screenVector((directionFromUV(wind.u, wind.v) + 180) % 360, screenOffsetDeg);\n dx[index] = vector.x;\n dy[index] = vector.y;\n speed[index] = speedOf(wind.u, wind.v);\n }\n }\n return { cols, rows, cell, dx, dy, speed };\n}\n\nexport function buildScreenScalarField(\n grid: ScalarGrid,\n width: number,\n height: number,\n unproject: Unproject,\n cell = 8,\n): ScreenScalarField {\n const cols = Math.max(1, Math.ceil(width / cell));\n const rows = Math.max(1, Math.ceil(height / cell));\n const values = new Float32Array(cols * rows).fill(Number.NaN);\n for (let row = 0; row < rows; row++) {\n for (let col = 0; col < cols; col++) {\n const ll = unproject((col + 0.5) * cell, (row + 0.5) * cell);\n const value = ll ? sampleScalar(grid, ll.lon, ll.lat) : null;\n if (value !== null) values[row * cols + col] = value;\n }\n }\n return { cols, rows, cell, values };\n}\n\n/** Angin di titik layar (x, y) dari kisi; null di luar kisi atau tanpa data. */\nexport function windAt(\n field: ScreenWindField,\n x: number,\n y: number,\n): { dx: number; dy: number; speed: number } | null {\n const col = Math.floor(x / field.cell);\n const row = Math.floor(y / field.cell);\n if (col < 0 || row < 0 || col >= field.cols || row >= field.rows) return null;\n const index = row * field.cols + col;\n const speed = field.speed[index] as number;\n if (Number.isNaN(speed)) return null;\n return { dx: field.dx[index] as number, dy: field.dy[index] as number, speed };\n}\n","/** Hitungan murni untuk renderer: tanpa DOM supaya mudah dites. */\n\nconst RAD = Math.PI / 180;\n\n/**\n * Vektor gerak di layar (x ke kanan, y ke bawah) untuk arah `toDeg` (derajat dari utara, searah jarum\n * jam). `screenOffsetDeg` = putaran peta: arah di layar = arah geografis + offset.\n */\nexport function screenVector(\n toDeg: number,\n screenOffsetDeg = 0,\n): { x: number; y: number } {\n const angle = (toDeg + screenOffsetDeg) * RAD;\n return { x: Math.sin(angle), y: -Math.cos(angle) };\n}\n\nexport const clamp = (value: number, min = 0, max = 1): number =>\n Math.min(max, Math.max(min, value));\n\n/** Jumlah partikel angin menurut luas layar, dibatasi `max`. */\nexport function windParticleCount(width: number, height: number, max = 520): number {\n return Math.min(max, Math.round((width * height) / 2600));\n}\n\n/** Jumlah garis hujan yang digambar dari `pool` tetes, menurut intensitas 0-1. */\nexport function rainCount(pool: number, intensity: number): number {\n return Math.round(pool * clamp(intensity));\n}\n\n/** Kecepatan visual partikel (px/detik) dari kecepatan angin (kt) dan hembusan sesaat. */\nexport function particleSpeed(speedKt: number, gustKt = 0): number {\n return (speedKt + gustKt) * 7;\n}\n\n/** Hembusan sesaat (kt tambahan) yang berdenyut halus sesuai `gustiness` 0-1. */\nexport function gustPulse(gustiness: number, timeMs: number): number {\n if (gustiness <= 0) return 0;\n const wave = 0.5 + 0.5 * Math.sin(timeMs / 700) * Math.sin(timeMs / 1900 + 1);\n return wave * gustiness * 20;\n}\n\n/** Selang antar kilat (ms) dari laju kilat per menit, dengan sedikit acak (`random` 0-1). */\nexport function lightningInterval(perMinute: number, random: number): number {\n if (perMinute <= 0) return Number.POSITIVE_INFINITY;\n return (60_000 / perMinute) * (0.4 + random * 1.2);\n}\n","import type { WindGrid } from \"@aeroswarm/weather\";\nimport { rampColor, rgbaCss, WIND_RAMP } from \"./colorRamp.js\";\nimport { buildScreenWindField, type ScreenWindField, type Unproject, windAt } from \"./screenField.js\";\n\nexport interface FlowRendererOptions {\n container: HTMLElement;\n unproject: Unproject;\n zIndex?: number;\n maxFps?: number;\n /** Piksel per detik untuk tiap m/s angin. Bawaan 9. */\n pixelsPerMs?: number;\n /** Jumlah partikel maksimum. Bawaan 3000. */\n maxParticles?: number;\n reducedMotion?: boolean;\n}\n\ninterface Particle {\n x: number;\n y: number;\n age: number;\n life: number;\n}\n\n/**\n * Aliran angin ala Windy: partikel bergerak mengikuti medan angin dari grid, berwarna menurut\n * kecepatan, dengan ekor yang memudar. Medan dipetakan ke kisi layar sekali tiap peta bergerak.\n */\nexport class FlowRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private grid: WindGrid | null = null;\n private field: ScreenWindField | null = null;\n private particles: Particle[] = [];\n private width = 1;\n private height = 1;\n private dpr = 1;\n private offset = 0;\n private raf = 0;\n private running = false;\n private last = 0;\n private accumulated = 0;\n private readonly frameInterval: number;\n private readonly reduceMotion: boolean;\n\n constructor(private readonly options: FlowRendererOptions) {\n this.frameInterval = 1000 / (options.maxFps ?? 30);\n this.reduceMotion =\n options.reducedMotion ??\n (typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches);\n this.canvas = document.createElement(\"canvas\");\n this.canvas.dataset.weatherLayer = \"flow\";\n Object.assign(this.canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(options.zIndex ?? 4),\n });\n options.container.appendChild(this.canvas);\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.ctx = ctx;\n this.resize();\n }\n\n setWind(grid: WindGrid | null): void {\n this.grid = grid;\n this.refreshField();\n }\n\n setScreenOffset(degrees: number): void {\n if (degrees === this.offset) return;\n this.offset = degrees;\n this.refreshField();\n }\n\n /** Dipanggil saat peta bergerak/zoom: petakan ulang medan ke layar. */\n refreshField(): void {\n this.field = this.grid\n ? buildScreenWindField(this.grid, this.width, this.height, this.options.unproject, this.offset)\n : null;\n if (!this.field) this.ctx.clearRect(0, 0, this.width, this.height);\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n this.canvas.width = this.width * this.dpr;\n this.canvas.height = this.height * this.dpr;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n const count = Math.min(this.options.maxParticles ?? 3000, Math.round((this.width * this.height) / 520));\n this.particles = Array.from({ length: count }, () => this.spawn());\n this.refreshField();\n }\n\n setVisible(visible: boolean): void {\n this.canvas.style.display = visible ? \"block\" : \"none\";\n if (visible) this.start();\n else this.stop();\n }\n\n setOpacity(opacity: number): void {\n this.canvas.style.opacity = String(Math.min(1, Math.max(0, opacity)));\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.last = performance.now();\n const tick = (now: number): void => {\n if (!this.running) return;\n this.accumulated += now - this.last;\n this.last = now;\n if (!document.hidden && this.accumulated >= this.frameInterval) {\n this.step(Math.min(0.1, this.accumulated / 1000));\n this.accumulated = 0;\n }\n this.raf = requestAnimationFrame(tick);\n };\n this.raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n this.running = false;\n cancelAnimationFrame(this.raf);\n }\n\n destroy(): void {\n this.stop();\n this.canvas.remove();\n }\n\n private spawn(): Particle {\n return {\n x: Math.random() * this.width,\n y: Math.random() * this.height,\n age: Math.floor(Math.random() * 60),\n life: 50 + Math.random() * 80,\n };\n }\n\n private step(dt: number): void {\n const ctx = this.ctx;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.05})`;\n ctx.fillRect(0, 0, this.width, this.height);\n ctx.globalCompositeOperation = \"source-over\";\n const field = this.field;\n if (!field) return;\n\n const pixelsPerMs = this.options.pixelsPerMs ?? 9;\n ctx.lineWidth = 1.5;\n ctx.lineCap = \"round\";\n const motion = this.reduceMotion ? 0 : 1;\n\n // Kelompokkan garis per warna (dibulatkan) agar tidak ribuan stroke berbeda per frame.\n const buckets = new Map<number, { color: string; path: Path2D }>();\n for (const p of this.particles) {\n const wind = windAt(field, p.x, p.y);\n if (!wind || p.age > p.life) {\n Object.assign(p, this.spawn(), { age: 0 });\n continue;\n }\n const step = wind.speed * pixelsPerMs * dt * motion;\n const nx = p.x + wind.dx * step;\n const ny = p.y + wind.dy * step;\n const key = Math.round(wind.speed / 1.5);\n let bucket = buckets.get(key);\n if (!bucket) {\n bucket = { color: rgbaCss(rampColor(WIND_RAMP, key * 1.5), 0.85), path: new Path2D() };\n buckets.set(key, bucket);\n }\n bucket.path.moveTo(p.x, p.y);\n bucket.path.lineTo(nx, ny);\n p.x = nx;\n p.y = ny;\n p.age++;\n if (nx < 0 || ny < 0 || nx > this.width || ny > this.height) p.age = p.life + 1;\n }\n for (const { color, path } of buckets.values()) {\n ctx.strokeStyle = color;\n ctx.stroke(path);\n }\n }\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport {\n clamp,\n gustPulse,\n lightningInterval,\n particleSpeed,\n rainCount,\n screenVector,\n windParticleCount,\n} from \"./math.js\";\n\nexport type WeatherTheme = \"light\" | \"dark\";\n\nexport interface WeatherRendererOptions {\n /** Elemen tempat canvas dipasang (mis. `map.domNode`). Harus berposisi non-static. */\n container: HTMLElement;\n theme?: WeatherTheme;\n /** Batas frame per detik. Bawaan 30. */\n maxFps?: number;\n /** Gambar diam (tanpa gerak) bila pengguna meminta mengurangi animasi. Bawaan: ikuti OS. */\n reducedMotion?: boolean;\n zIndex?: number;\n}\n\ninterface Particle {\n x: number;\n y: number;\n age: number;\n life: number;\n}\ninterface Puff {\n dx: number;\n dy: number;\n r: number;\n}\ninterface Cloud {\n x: number;\n y: number;\n puffs: Puff[];\n layer: number;\n threshold: number;\n}\n\nconst CALM: AnimationState = {\n wind: { fromDeg: null, toDeg: null, speedKt: 0, gustiness: 0, turbulent: false },\n clouds: { layers: [], total: 0 },\n visibility: { meters: null, haze: 0 },\n obscuration: null,\n precipitation: null,\n thunderstorm: null,\n};\n\nconst DROP_POOL = 700;\n\nfunction makePuffSprite(): HTMLCanvasElement {\n const sprite = document.createElement(\"canvas\");\n sprite.width = sprite.height = 128;\n const ctx = sprite.getContext(\"2d\");\n if (ctx) {\n const gradient = ctx.createRadialGradient(64, 64, 4, 64, 64, 62);\n gradient.addColorStop(0, \"rgba(255,255,255,0.95)\");\n gradient.addColorStop(0.55, \"rgba(255,255,255,0.45)\");\n gradient.addColorStop(1, \"rgba(255,255,255,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, 128, 128);\n }\n return sprite;\n}\n\n/**\n * Menggambar angin, awan, hujan, kabut, dan petir ke dua canvas transparan di atas `container`.\n * Tidak mengenal peta: arah di layar diatur lewat `setScreenOffset` (putaran peta).\n */\nexport class WeatherRenderer {\n private readonly windCanvas: HTMLCanvasElement;\n private readonly fxCanvas: HTMLCanvasElement;\n private readonly wind: CanvasRenderingContext2D;\n private readonly fx: CanvasRenderingContext2D;\n private readonly puff = makePuffSprite();\n private readonly drops = Array.from({ length: DROP_POOL }, () => ({\n x: Math.random(),\n y: Math.random(),\n len: 0.6 + Math.random() * 0.8,\n speed: 0.7 + Math.random() * 0.6,\n }));\n\n private state: AnimationState = CALM;\n private theme: WeatherTheme;\n private screenOffset = 0;\n private width = 1;\n private height = 1;\n private dpr = 1;\n private particles: Particle[] = [];\n private clouds: Cloud[] = [];\n private raf = 0;\n private lastFrame = 0;\n private accumulated = 0;\n private flash = 0;\n private bolt: [number, number][] | null = null;\n private nextBolt = 0;\n private readonly frameInterval: number;\n private readonly reduceMotion: boolean;\n private running = false;\n\n constructor(private readonly options: WeatherRendererOptions) {\n this.theme = options.theme ?? \"light\";\n this.frameInterval = 1000 / (options.maxFps ?? 30);\n this.reduceMotion =\n options.reducedMotion ??\n (typeof matchMedia === \"function\" &&\n matchMedia(\"(prefers-reduced-motion: reduce)\").matches);\n\n this.windCanvas = this.createCanvas(\"wind\");\n this.fxCanvas = this.createCanvas(\"fx\");\n const wind = this.windCanvas.getContext(\"2d\");\n const fx = this.fxCanvas.getContext(\"2d\");\n if (!wind || !fx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.wind = wind;\n this.fx = fx;\n this.resize();\n }\n\n setState(state: AnimationState): void {\n this.state = state;\n if (this.reduceMotion) this.renderFrame(performance.now(), 0);\n }\n\n /** Putaran peta (derajat) yang ditambahkan ke arah geografis angin. */\n setScreenOffset(degrees: number): void {\n this.screenOffset = degrees;\n }\n\n setTheme(theme: WeatherTheme): void {\n this.theme = theme;\n }\n\n setVisible(visible: boolean): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.display = visible ? \"block\" : \"none\";\n }\n if (visible) this.start();\n else this.stop();\n }\n\n setOpacity(opacity: number): void {\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.style.opacity = String(clamp(opacity));\n }\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n for (const canvas of [this.windCanvas, this.fxCanvas]) {\n canvas.width = this.width * this.dpr;\n canvas.height = this.height * this.dpr;\n }\n this.wind.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.fx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.seedParticles();\n this.seedClouds();\n }\n\n start(): void {\n if (this.running) return;\n this.running = true;\n this.lastFrame = performance.now();\n const tick = (now: number): void => {\n if (!this.running) return;\n const dt = (now - this.lastFrame) / 1000;\n this.lastFrame = now;\n this.accumulated += dt * 1000;\n if (!document.hidden && this.accumulated >= this.frameInterval) {\n this.renderFrame(now, Math.min(0.1, this.accumulated / 1000));\n this.accumulated = 0;\n }\n this.raf = requestAnimationFrame(tick);\n };\n this.raf = requestAnimationFrame(tick);\n }\n\n stop(): void {\n this.running = false;\n cancelAnimationFrame(this.raf);\n }\n\n destroy(): void {\n this.stop();\n this.windCanvas.remove();\n this.fxCanvas.remove();\n }\n\n // ---- internal --------------------------------------------------------\n\n private createCanvas(name: string): HTMLCanvasElement {\n const canvas = document.createElement(\"canvas\");\n canvas.dataset.weatherLayer = name;\n Object.assign(canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(this.options.zIndex ?? 5),\n });\n this.options.container.appendChild(canvas);\n return canvas;\n }\n\n private seedParticles(): void {\n const count = windParticleCount(this.width, this.height);\n this.particles = Array.from({ length: count }, () => this.newParticle());\n this.wind.clearRect(0, 0, this.width, this.height);\n }\n\n private newParticle(): Particle {\n return {\n x: Math.random() * this.width,\n y: Math.random() * this.height,\n age: Math.floor(Math.random() * 80),\n life: 60 + Math.random() * 90,\n };\n }\n\n private seedClouds(): void {\n this.clouds = Array.from({ length: 16 }, (_, i) => {\n const size = 90 + Math.random() * 120;\n const count = 5 + Math.floor(Math.random() * 4);\n return {\n x: Math.random() * (this.width + 300) - 150,\n y: Math.random() * (this.height + 100) - 50,\n layer: 0.6 + Math.random() * 0.8,\n threshold: i / 16,\n puffs: Array.from({ length: count }, () => ({\n dx: (Math.random() - 0.5) * size * 1.4,\n dy: (Math.random() - 0.5) * size * 0.5,\n r: size * (0.5 + Math.random() * 0.5),\n })),\n };\n });\n }\n\n private renderFrame(now: number, dt: number): void {\n const { wind, thunderstorm } = this.state;\n const motion = this.reduceMotion ? 0 : 1;\n const toDeg = wind.toDeg;\n const vector = toDeg === null ? null : screenVector(toDeg, this.screenOffset);\n const gust = gustPulse(wind.gustiness, now);\n const speed = particleSpeed(wind.speedKt, gust);\n\n this.drawWind(now, dt, vector, wind.turbulent, speed, motion);\n\n const fx = this.fx;\n fx.clearRect(0, 0, this.width, this.height);\n this.drawClouds(dt, vector, wind.speedKt, motion);\n this.drawPrecipitation(dt, vector, wind.speedKt, motion);\n this.drawHaze(now, motion);\n this.drawLightning(now, dt, thunderstorm?.lightningPerMinute ?? 0);\n }\n\n private drawWind(\n now: number,\n dt: number,\n vector: { x: number; y: number } | null,\n turbulent: boolean,\n speed: number,\n motion: number,\n ): void {\n const ctx = this.wind;\n ctx.globalCompositeOperation = \"destination-out\";\n ctx.fillStyle = `rgba(0,0,0,${this.reduceMotion ? 1 : 0.1})`;\n ctx.fillRect(0, 0, this.width, this.height);\n ctx.globalCompositeOperation = \"source-over\";\n if (this.state.wind.speedKt <= 0) return;\n\n const alpha = Math.min(0.75, 0.22 + this.state.wind.speedKt / 50);\n ctx.lineWidth = 1.2;\n ctx.lineCap = \"round\";\n ctx.strokeStyle =\n this.theme === \"dark\"\n ? `rgba(120,200,220,${alpha})`\n : `rgba(31,122,140,${alpha})`;\n ctx.beginPath();\n for (const p of this.particles) {\n const fromX = p.x;\n const fromY = p.y;\n let vx: number;\n let vy: number;\n if (turbulent || vector === null) {\n vx = Math.cos(p.age / 12 + p.x) * 0.7;\n vy = Math.sin(p.age / 14 + p.y) * 0.7;\n } else {\n const wobble =\n Math.sin(p.y / 55 + now / 1400) * 0.35 + Math.cos(p.x / 70 - now / 1700) * 0.25;\n vx = vector.x - wobble * 0.25 * vector.y;\n vy = vector.y + wobble * 0.25 * vector.x;\n }\n p.x += vx * speed * dt * motion;\n p.y += vy * speed * dt * motion;\n p.age++;\n ctx.moveTo(fromX, fromY);\n ctx.lineTo(p.x, p.y);\n if (p.x < 0 || p.x > this.width || p.y < 0 || p.y > this.height || p.age > p.life) {\n Object.assign(p, this.newParticle(), { age: 0 });\n }\n }\n ctx.stroke();\n }\n\n private drawClouds(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const ctx = this.fx;\n const cover = this.state.clouds.total;\n const precipitation = this.state.precipitation;\n const dark = clamp(\n (precipitation?.intensity ?? 0) * 0.6 + (this.state.thunderstorm?.overhead ? 0.4 : 0),\n );\n if (cover <= 0) return;\n\n for (const cloud of this.clouds) {\n if (vector) {\n cloud.x += vector.x * speedKt * 1.4 * cloud.layer * dt * motion;\n cloud.y += vector.y * speedKt * 1.4 * cloud.layer * dt * motion;\n }\n if (cloud.x < -250) cloud.x = this.width + 250;\n if (cloud.x > this.width + 250) cloud.x = -250;\n if (cloud.y < -150) cloud.y = this.height + 150;\n if (cloud.y > this.height + 150) cloud.y = -150;\n const visible = clamp((cover * 1.15 - cloud.threshold) * 4);\n if (visible <= 0) continue;\n ctx.globalAlpha = visible * (0.38 + 0.3 * cover) * (1 - dark * 0.15);\n for (const puff of cloud.puffs) {\n ctx.drawImage(\n this.puff,\n cloud.x + puff.dx - puff.r,\n cloud.y + puff.dy - puff.r,\n puff.r * 2,\n puff.r * 2,\n );\n }\n }\n ctx.globalAlpha = 1;\n if (dark > 0 || cover > 0.6) {\n ctx.fillStyle = `rgba(38,48,62,${(cover * 0.2 + dark * 0.32).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n\n private drawPrecipitation(\n dt: number,\n vector: { x: number; y: number } | null,\n speedKt: number,\n motion: number,\n ): void {\n const precipitation = this.state.precipitation;\n if (!precipitation) return;\n const ctx = this.fx;\n const count = rainCount(this.drops.length, precipitation.intensity);\n const slant = (vector?.x ?? 0) * Math.min(1, speedKt / 25) * 0.5;\n const snow = precipitation.kind === \"snow\";\n const fall = snow ? 0.25 : 1.6;\n\n if (snow) ctx.fillStyle = \"rgba(255,255,255,0.8)\";\n else {\n ctx.strokeStyle = `rgba(150,185,225,${(0.3 + 0.3 * precipitation.intensity).toFixed(2)})`;\n ctx.lineWidth = precipitation.kind === \"drizzle\" ? 0.7 : 1.1;\n ctx.beginPath();\n }\n for (let i = 0; i < count; i++) {\n const drop = this.drops[i];\n if (!drop) continue;\n drop.y += drop.speed * dt * fall * motion;\n drop.x += slant * dt * fall * motion;\n if (drop.y > 1) {\n drop.y = -0.05;\n drop.x = Math.random();\n }\n if (drop.x > 1) drop.x = 0;\n if (drop.x < 0) drop.x = 1;\n const x = drop.x * this.width;\n const y = drop.y * this.height;\n if (snow) ctx.fillRect(x, y, 2, 2);\n else {\n const length = 12 * drop.len;\n ctx.moveTo(x, y);\n ctx.lineTo(x - slant * length, y - length);\n }\n }\n if (!snow) ctx.stroke();\n }\n\n private drawHaze(now: number, motion: number): void {\n const { visibility, obscuration } = this.state;\n const haze = Math.max(visibility.haze, (obscuration?.density ?? 0) * 0.7);\n if (haze <= 0.01) return;\n const ctx = this.fx;\n const dark = this.theme === \"dark\";\n ctx.fillStyle = `${dark ? \"rgba(150,160,168,\" : \"rgba(214,219,223,\"}${(haze * 0.78).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (obscuration?.kind === \"fog\" || (visibility.meters ?? 10_000) < 1500) {\n for (let i = 0; i < 3; i++) {\n const cx = (((now / 40) * (i + 1) * 0.8 * motion) % (this.width * 1.3)) + i * this.width * 0.2;\n const cy = this.height * (0.3 + i * 0.25);\n const gradient = ctx.createRadialGradient(cx, cy, 10, cx, cy, this.width * 0.5);\n gradient.addColorStop(0, dark ? \"rgba(170,180,188,0.35)\" : \"rgba(235,238,240,0.5)\");\n gradient.addColorStop(1, \"rgba(0,0,0,0)\");\n ctx.fillStyle = gradient;\n ctx.fillRect(0, 0, this.width, this.height);\n }\n }\n }\n\n private drawLightning(now: number, dt: number, perMinute: number): void {\n if (this.reduceMotion) return;\n const ctx = this.fx;\n if (perMinute > 0 && now > this.nextBolt) {\n this.flash = 1;\n this.bolt = this.makeBolt();\n this.nextBolt = now + lightningInterval(perMinute, Math.random());\n }\n if (this.flash <= 0) return;\n ctx.fillStyle = `rgba(255,255,255,${(this.flash * 0.35).toFixed(3)})`;\n ctx.fillRect(0, 0, this.width, this.height);\n if (this.bolt && this.flash > 0.35) {\n ctx.strokeStyle = `rgba(255,255,255,${this.flash.toFixed(2)})`;\n ctx.lineWidth = 2.2;\n ctx.beginPath();\n this.bolt.forEach(([x, y], i) => {\n if (i === 0) ctx.moveTo(x, y);\n else ctx.lineTo(x, y);\n });\n ctx.stroke();\n }\n this.flash -= dt * 3.2;\n }\n\n private makeBolt(): [number, number][] {\n let x = this.width * (0.2 + Math.random() * 0.6);\n let y = 0;\n const points: [number, number][] = [[x, y]];\n const reach = this.height * (0.5 + Math.random() * 0.3);\n while (y < reach) {\n x += (Math.random() - 0.5) * 36;\n y += 14 + Math.random() * 22;\n points.push([x, y]);\n }\n return points;\n }\n}\n","import type { AnimationState } from \"@aeroswarm/weather\";\nimport { WeatherRenderer, type WeatherTheme } from \"./renderer.js\";\n\n/** Bagian peta LuciadRIA yang dipakai overlay. Sengaja struktural supaya tidak bergantung pada RIA. */\nexport interface MapLike {\n readonly domNode: HTMLElement;\n readonly reference?: { readonly identifier: string };\n readonly camera?: {\n asLook2D?: () => { rotation: number };\n asLookAt?: (distance: number) => { yaw: number };\n };\n on(event: \"MapChange\", callback: () => void): { remove(): void };\n}\n\nexport interface WeatherOverlayOptions {\n /** `auto` mengikuti `data-theme` pada <html>, lalu `prefers-color-scheme`. */\n theme?: WeatherTheme | \"auto\";\n zIndex?: number;\n maxFps?: number;\n /** Ganti cara menghitung putaran peta (derajat) bila peta Anda berbeda. */\n getScreenOffset?: (map: MapLike) => number;\n}\n\nexport interface WeatherOverlay {\n readonly renderer: WeatherRenderer;\n setState(state: AnimationState): void;\n setVisible(visible: boolean): void;\n setOpacity(opacity: number): void;\n destroy(): void;\n}\n\n/**\n * Putaran peta terhadap utara, supaya arah angin di layar tetap benar: 2D memakai rotasi kamera,\n * 3D memakai yaw kamera. Keduanya dibalik karena peta berputar berlawanan dengan kamera.\n * Tanda ini perlu dicek di peta sungguhan (putar peta, lihat arah partikel).\n */\nexport function mapScreenOffset(map: MapLike): number {\n const camera = map.camera;\n if (!camera) return 0;\n try {\n if (map.reference?.identifier === \"EPSG:4978\") {\n return -(camera.asLookAt?.(1000).yaw ?? 0);\n }\n return -(camera.asLook2D?.().rotation ?? 0);\n } catch {\n return 0;\n }\n}\n\nfunction detectTheme(): WeatherTheme {\n const explicit = document.documentElement.dataset.theme;\n if (explicit === \"dark\" || explicit === \"light\") return explicit;\n return matchMedia?.(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\n/** Pasang overlay cuaca (canvas transparan) di atas peta; hapus dengan `destroy()`. */\nexport function createWeatherOverlay(\n map: MapLike,\n options: WeatherOverlayOptions = {},\n): WeatherOverlay {\n const container = map.domNode;\n if (getComputedStyle(container).position === \"static\") {\n container.style.position = \"relative\";\n }\n\n const theme = options.theme ?? \"auto\";\n const renderer = new WeatherRenderer({\n container,\n theme: theme === \"auto\" ? detectTheme() : theme,\n zIndex: options.zIndex,\n maxFps: options.maxFps,\n });\n const offsetOf = options.getScreenOffset ?? mapScreenOffset;\n\n let width = container.clientWidth;\n let height = container.clientHeight;\n const handle = map.on(\"MapChange\", () => {\n renderer.setScreenOffset(offsetOf(map));\n if (container.clientWidth !== width || container.clientHeight !== height) {\n width = container.clientWidth;\n height = container.clientHeight;\n renderer.resize();\n }\n });\n\n const themeObserver =\n theme === \"auto\"\n ? new MutationObserver(() => renderer.setTheme(detectTheme()))\n : null;\n themeObserver?.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\"],\n });\n\n renderer.setScreenOffset(offsetOf(map));\n renderer.start();\n\n return {\n renderer,\n setState: (state) => renderer.setState(state),\n setVisible: (visible) => renderer.setVisible(visible),\n setOpacity: (opacity) => renderer.setOpacity(opacity),\n destroy() {\n handle.remove();\n themeObserver?.disconnect();\n renderer.destroy();\n },\n };\n}\n","import type { ScalarGrid } from \"@aeroswarm/weather\";\nimport { CLOUD_RAMP, type RampStop, rampColor } from \"./colorRamp.js\";\nimport { buildScreenScalarField, type Unproject } from \"./screenField.js\";\n\nexport interface ScalarRendererOptions {\n container: HTMLElement;\n unproject: Unproject;\n ramp?: readonly RampStop[];\n zIndex?: number;\n}\n\n/**\n * Peta warna dari grid skalar (tutupan awan, hujan, suhu): tiap sel layar diwarnai dari nilai grid,\n * digambar halus. Digambar ulang hanya saat grid atau peta berubah, bukan tiap frame.\n */\nexport class ScalarRenderer {\n private readonly canvas: HTMLCanvasElement;\n private readonly ctx: CanvasRenderingContext2D;\n private readonly small = document.createElement(\"canvas\");\n private grid: ScalarGrid | null = null;\n private ramp: readonly RampStop[];\n private width = 1;\n private height = 1;\n private dpr = 1;\n\n constructor(private readonly options: ScalarRendererOptions) {\n this.ramp = options.ramp ?? CLOUD_RAMP;\n this.canvas = document.createElement(\"canvas\");\n this.canvas.dataset.weatherLayer = \"scalar\";\n Object.assign(this.canvas.style, {\n position: \"absolute\",\n inset: \"0\",\n width: \"100%\",\n height: \"100%\",\n pointerEvents: \"none\",\n zIndex: String(options.zIndex ?? 3),\n });\n options.container.appendChild(this.canvas);\n const ctx = this.canvas.getContext(\"2d\");\n if (!ctx) throw new Error(\"Canvas 2D tidak tersedia\");\n this.ctx = ctx;\n this.resize();\n }\n\n setGrid(grid: ScalarGrid | null, ramp?: readonly RampStop[]): void {\n this.grid = grid;\n if (ramp) this.ramp = ramp;\n this.draw();\n }\n\n resize(): void {\n const rect = this.options.container.getBoundingClientRect();\n this.width = Math.max(1, Math.round(rect.width));\n this.height = Math.max(1, Math.round(rect.height));\n this.dpr = Math.min(2, window.devicePixelRatio || 1);\n this.canvas.width = this.width * this.dpr;\n this.canvas.height = this.height * this.dpr;\n this.ctx.setTransform(this.dpr, 0, 0, this.dpr, 0, 0);\n this.draw();\n }\n\n /** Gambar ulang setelah peta bergerak atau di-zoom. */\n draw(): void {\n const ctx = this.ctx;\n ctx.clearRect(0, 0, this.width, this.height);\n if (!this.grid) return;\n const field = buildScreenScalarField(this.grid, this.width, this.height, this.options.unproject);\n this.small.width = field.cols;\n this.small.height = field.rows;\n const small = this.small.getContext(\"2d\");\n if (!small) return;\n const image = small.createImageData(field.cols, field.rows);\n for (let i = 0; i < field.values.length; i++) {\n const value = field.values[i] as number;\n if (Number.isNaN(value)) continue;\n const [r, g, b, a] = rampColor(this.ramp, value);\n image.data[i * 4] = r;\n image.data[i * 4 + 1] = g;\n image.data[i * 4 + 2] = b;\n image.data[i * 4 + 3] = Math.round(a * 255);\n }\n small.putImageData(image, 0, 0);\n ctx.imageSmoothingEnabled = true;\n ctx.imageSmoothingQuality = \"high\";\n ctx.drawImage(this.small, 0, 0, field.cols * field.cell, field.rows * field.cell);\n }\n\n setVisible(visible: boolean): void {\n this.canvas.style.display = visible ? \"block\" : \"none\";\n }\n\n setOpacity(opacity: number): void {\n this.canvas.style.opacity = String(Math.min(1, Math.max(0, opacity)));\n }\n\n destroy(): void {\n this.canvas.remove();\n }\n}\n","import type { ScalarGrid, WindGrid } from \"@aeroswarm/weather\";\nimport type { RampStop } from \"./colorRamp.js\";\nimport { FlowRenderer } from \"./flowRenderer.js\";\nimport { type MapLike, mapScreenOffset } from \"./overlay.js\";\nimport { ScalarRenderer } from \"./scalarRenderer.js\";\nimport type { Unproject } from \"./screenField.js\";\n\nexport interface FieldOverlayOptions {\n /** Titik layar ke lon/lat; lihat `createRiaUnproject`. */\n unproject: Unproject;\n zIndex?: number;\n maxFps?: number;\n pixelsPerMs?: number;\n getScreenOffset?: (map: MapLike) => number;\n}\n\nexport interface FieldOverlay {\n /** Aliran partikel angin dari grid; null untuk menghapus. */\n setWind(grid: WindGrid | null): void;\n /** Peta warna dari grid skalar (awan, hujan, suhu); null untuk menghapus. */\n setScalar(grid: ScalarGrid | null, ramp?: readonly RampStop[]): void;\n setVisible(visible: boolean): void;\n setOpacity(opacity: number): void;\n destroy(): void;\n}\n\n/**\n * Overlay medan cuaca ala Windy di atas peta: peta warna (di bawah) dan aliran partikel angin (di\n * atas). Medan dipetakan ulang ke layar tiap peta bergerak (dibatasi ~10 kali per detik).\n */\nexport function createFieldOverlay(map: MapLike, options: FieldOverlayOptions): FieldOverlay {\n const container = map.domNode;\n if (getComputedStyle(container).position === \"static\") container.style.position = \"relative\";\n const offsetOf = options.getScreenOffset ?? mapScreenOffset;\n\n const scalar = new ScalarRenderer({ container, unproject: options.unproject, zIndex: options.zIndex });\n const flow = new FlowRenderer({\n container,\n unproject: options.unproject,\n zIndex: (options.zIndex ?? 3) + 1,\n maxFps: options.maxFps,\n pixelsPerMs: options.pixelsPerMs,\n });\n\n let width = container.clientWidth;\n let height = container.clientHeight;\n let pending: ReturnType<typeof setTimeout> | null = null;\n const refresh = (): void => {\n pending = null;\n if (container.clientWidth !== width || container.clientHeight !== height) {\n width = container.clientWidth;\n height = container.clientHeight;\n scalar.resize();\n flow.resize();\n }\n flow.setScreenOffset(offsetOf(map));\n flow.refreshField();\n scalar.draw();\n };\n const handle = map.on(\"MapChange\", () => {\n if (pending === null) pending = setTimeout(refresh, 100);\n });\n\n flow.setScreenOffset(offsetOf(map));\n flow.start();\n\n return {\n setWind: (grid) => flow.setWind(grid),\n setScalar: (grid, ramp) => scalar.setGrid(grid, ramp),\n setVisible(visible) {\n flow.setVisible(visible);\n scalar.setVisible(visible);\n },\n setOpacity(opacity) {\n flow.setOpacity(opacity);\n scalar.setOpacity(opacity);\n },\n destroy() {\n if (pending !== null) clearTimeout(pending);\n handle.remove();\n flow.destroy();\n scalar.destroy();\n },\n };\n}\n","import type { Unproject } from \"./screenField.js\";\n\n/** Bagian RIA yang dipakai `createRiaUnproject`; diberikan oleh aplikasi (injeksi). */\nexport interface RiaUnprojectDeps {\n /** `getReference(\"EPSG:4326\")`. */\n wgs84: unknown;\n createPoint: (reference: unknown, coordinates: number[]) => unknown;\n createTransformation: (source: unknown, destination: unknown) => {\n transform(point: unknown): { x: number; y: number };\n };\n}\n\ninterface UnprojectableMap {\n readonly reference: unknown;\n readonly viewToMapTransformation: { transform(point: unknown): unknown };\n}\n\n/**\n * Fungsi titik layar -> lon/lat untuk peta RIA 2D maupun 3D. Mengembalikan null untuk titik yang tidak\n * jatuh pada peta (di 3D: di luar bola bumi), jadi aliran partikel berhenti di sana.\n */\nexport function createRiaUnproject(map: UnprojectableMap, deps: RiaUnprojectDeps): Unproject {\n const toWgs84 = deps.createTransformation(map.reference, deps.wgs84);\n return (x, y) => {\n try {\n const mapPoint = map.viewToMapTransformation.transform(deps.createPoint(null, [x, y, 0]));\n const point = toWgs84.transform(mapPoint);\n return Number.isFinite(point.x) && Number.isFinite(point.y) ? { lon: point.x, lat: point.y } : null;\n } catch {\n return null;\n }\n };\n}\n"],"mappings":"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","names":["from","to","window"]}
|
package/package.json
CHANGED