mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
feat(fires): flag possible explosions in satellite thermal detections (#2850)
* feat(fires): flag possible explosions in satellite thermal detections Adds possibleExplosion field (FRP >80 MW + brightness >380 K) to fire detections, surfacing non-fire thermal signatures that may indicate strikes or explosions. Seeder computes the flag, panel shows inline badge per region and summary alert when explosions are detected. * refactor(fires): extract brightness/frp locals to avoid double-parse
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -202,6 +202,11 @@ components:
|
||||
dayNight:
|
||||
type: string
|
||||
description: Day or night detection ("D" or "N").
|
||||
possibleExplosion:
|
||||
type: boolean
|
||||
description: |-
|
||||
Whether the thermal signature suggests a possible explosion rather than a fire
|
||||
(FRP > 80 MW and brightness > 380 K).
|
||||
required:
|
||||
- id
|
||||
description: FireDetection represents a satellite-detected active fire from NASA FIRMS.
|
||||
|
||||
@@ -30,6 +30,9 @@ message FireDetection {
|
||||
string region = 8;
|
||||
// Day or night detection ("D" or "N").
|
||||
string day_night = 9;
|
||||
// Whether the thermal signature suggests a possible explosion rather than a fire
|
||||
// (FRP > 80 MW and brightness > 380 K).
|
||||
bool possible_explosion = 10;
|
||||
}
|
||||
|
||||
// FireConfidence represents the confidence level of a fire detection.
|
||||
|
||||
@@ -78,19 +78,22 @@ async function fetchAllRegions(apiKey) {
|
||||
if (seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
const detectedAt = parseDetectedAt(row.acq_date || '', row.acq_time || '');
|
||||
const brightness = parseFloat(row.bright_ti4 ?? '0') || 0;
|
||||
const frp = parseFloat(row.frp ?? '0') || 0;
|
||||
fireDetections.push({
|
||||
id,
|
||||
location: {
|
||||
latitude: parseFloat(row.latitude ?? '0') || 0,
|
||||
longitude: parseFloat(row.longitude ?? '0') || 0,
|
||||
},
|
||||
brightness: parseFloat(row.bright_ti4 ?? '0') || 0,
|
||||
frp: parseFloat(row.frp ?? '0') || 0,
|
||||
brightness,
|
||||
frp,
|
||||
confidence: mapConfidence(row.confidence || ''),
|
||||
satellite: row.satellite || '',
|
||||
detectedAt,
|
||||
region: regionName,
|
||||
dayNight: row.daynight || '',
|
||||
possibleExplosion: frp > 80 && brightness > 380,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -43,8 +43,11 @@ export class SatelliteFiresPanel extends Panel {
|
||||
? `${(s.totalFrp / 1000).toFixed(1)}k`
|
||||
: Math.round(s.totalFrp).toLocaleString();
|
||||
const highClass = s.highIntensityCount > 0 ? ' fires-high' : '';
|
||||
const explosionBadge = s.possibleExplosionCount > 0
|
||||
? `<span class="fires-explosion-badge" title="${t('components.satelliteFires.explosionTooltip')}">${s.possibleExplosionCount}</span>`
|
||||
: '';
|
||||
return `<tr class="fire-row${highClass}">
|
||||
<td class="fire-region">${escapeHtml(s.region)}</td>
|
||||
<td class="fire-region">${escapeHtml(s.region)}${explosionBadge}</td>
|
||||
<td class="fire-count">${s.fireCount}</td>
|
||||
<td class="fire-hi">${s.highIntensityCount}</td>
|
||||
<td class="fire-frp">${frpStr}</td>
|
||||
@@ -53,6 +56,7 @@ export class SatelliteFiresPanel extends Panel {
|
||||
|
||||
const totalFrp = this.stats.reduce((sum, s) => sum + s.totalFrp, 0);
|
||||
const totalHigh = this.stats.reduce((sum, s) => sum + s.highIntensityCount, 0);
|
||||
const totalExplosions = this.stats.reduce((sum, s) => sum + s.possibleExplosionCount, 0);
|
||||
const ago = this.lastUpdated ? timeSince(this.lastUpdated) : t('components.satelliteFires.never');
|
||||
|
||||
this.setContent(`
|
||||
@@ -76,6 +80,7 @@ export class SatelliteFiresPanel extends Panel {
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
${totalExplosions > 0 ? `<div class="fires-explosion-alert">${t('components.satelliteFires.possibleExplosions', { count: String(totalExplosions) })}</div>` : ''}
|
||||
<div class="fires-footer">
|
||||
<span class="fires-source">NASA FIRMS (VIIRS SNPP)</span>
|
||||
<span class="fires-updated">${ago}</span>
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface FireDetection {
|
||||
detectedAt: number;
|
||||
region: string;
|
||||
dayNight: string;
|
||||
possibleExplosion: boolean;
|
||||
}
|
||||
|
||||
export interface GeoCoordinates {
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface FireDetection {
|
||||
detectedAt: number;
|
||||
region: string;
|
||||
dayNight: string;
|
||||
possibleExplosion: boolean;
|
||||
}
|
||||
|
||||
export interface GeoCoordinates {
|
||||
|
||||
@@ -1647,7 +1647,9 @@
|
||||
"minutesAgo": "{{count}}m ago",
|
||||
"hoursAgo": "{{count}}h ago"
|
||||
},
|
||||
"infoTooltip": "NASA FIRMS VIIRS satellite thermal detections across monitored conflict regions. High-intensity = brightness >360K & confidence >80%."
|
||||
"infoTooltip": "NASA FIRMS VIIRS satellite thermal detections across monitored conflict regions. High-intensity = brightness >360K & confidence >80%.",
|
||||
"possibleExplosions": "{{count}} possible explosion(s) detected",
|
||||
"explosionTooltip": "Thermal signature consistent with explosion (FRP >80 MW, brightness >380 K)"
|
||||
},
|
||||
"ucdpEvents": {
|
||||
"stateBased": "State-Based",
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface FireRegionStats {
|
||||
fireCount: number;
|
||||
totalFrp: number;
|
||||
highIntensityCount: number;
|
||||
possibleExplosionCount: number;
|
||||
}
|
||||
|
||||
export interface FetchResult {
|
||||
@@ -74,12 +75,14 @@ export function computeRegionStats(regions: Record<string, FireDetection[]>): Fi
|
||||
const highIntensity = fires.filter(
|
||||
f => f.brightness > 360 && f.confidence === 'FIRE_CONFIDENCE_HIGH',
|
||||
);
|
||||
const possibleExplosions = fires.filter(f => f.possibleExplosion);
|
||||
stats.push({
|
||||
region,
|
||||
fires,
|
||||
fireCount: fires.length,
|
||||
totalFrp: fires.reduce((sum, f) => sum + (f.frp || 0), 0),
|
||||
highIntensityCount: highIntensity.length,
|
||||
possibleExplosionCount: possibleExplosions.length,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -140,6 +140,29 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fires-explosion-badge {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 8px;
|
||||
background: var(--threat-critical);
|
||||
color: var(--bg);
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.fires-explosion-alert {
|
||||
padding: 6px 8px;
|
||||
margin-top: 4px;
|
||||
border-radius: 4px;
|
||||
background: color-mix(in srgb, var(--threat-critical) 12%, transparent);
|
||||
color: var(--threat-critical);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.fires-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user