How the Hotspot Escalation Scoring Algorithm Works in WorldMonitor
The hotspot escalation scoring algorithm in koala73/worldmonitor combines four weighted signals—news activity, country instability, geo-alert convergence, and military activity—into a normalized 1-5 risk score, while geographic convergence is detected separately by clustering three or more distinct event types within the same 1°×1° grid cell.
The hotspot escalation scoring algorithm is the core risk-calculation engine in the WorldMonitor platform. Implemented in TypeScript within the koala73/worldmonitor repository, it aggregates multi-source intelligence into dynamic threat scores for predefined geographic hotspots. This article breaks down the input signals, the normalization and weighting logic, and the separate geographic convergence detection pipeline that identifies multi-event clusters.
Input Signals for the Hotspot Escalation Algorithm
The algorithm ingests four independent data streams defined in src/services/hotspot-escalation.ts. Each signal is normalized to a 0-100 scale before being weighted and summed.
News Activity Signal
The news activity component quantifies media attention using three inputs: the raw count of matching articles (newsMatches), a boolean flag for breaking-news tags (hasBreaking), and a velocity metric (newsVelocity) measuring recent publication speed.
Normalization follows the formula implemented in normalizeNewsActivity:
matches * 15 + (hasBreaking ? 30 : 0) + velocity * 5
The result is capped at 100. This normalized value receives a weight of 0.35 in the final composite score.
Country-Level Instability Index (CII)
The Country-Level Instability Index (CII) provides a macro-political risk baseline. The algorithm looks up the highest CII value for any country overlapping the hotspot via getCIIForHotspot. If no data exists, normalizeCII defaults to 30.
The CII value is used directly after normalization (0-100 scale) and carries a weight of 0.25.
Geo-Alert Convergence
Geo-alert convergence captures localized anomaly detection from a dedicated service. It ingests a proximity score (geoAlertScore) and the count of distinct event types (geoAlertTypes) detected near the hotspot.
The normalizeGeo function calculates:
alertScore + alertTypes * 10
Capped at 100, this signal receives a weight of 0.25.
Military Activity
The military activity signal tracks kinetic indicators by counting military flights and naval vessels within a configurable radius of the hotspot (flightsNearby, vesselsNearby).
Normalization in normalizeMilitary uses:
flights * 10 + vessels * 15
Capped at 100, this component has the lowest weight of 0.15, reflecting its supplementary role in the risk model.
How Scores Are Calculated and Blended
After normalization, the algorithm composites the signals in calculateDynamicRaw by multiplying each 0-100 normalized value by its respective weight from the COMPONENT_WEIGHTS constant and summing the results. This produces a raw score between 0 and 100.
The rawToScore function maps this raw value to a dynamic score on a 1-5 integer scale. Finally, blendScores merges this dynamic score with the hotspot’s static baseline (escalationScore from the configuration) using a 70% dynamic / 30% static weighting. This blended result becomes the official escalation score for the hotspot.
The service also maintains a 24-hour rolling history (maximum 48 data points) via the history tracking logic, enabling trend detection in detectTrend. When thresholds are crossed, rapid increases occur, or a critical score (≥4.5) is reached, shouldEmitSignal triggers downstream alerts.
Detecting Geographic Convergence
Geographic convergence detection operates as a separate pipeline in src/services/geo-convergence.ts, distinct from the weighted scoring algorithm. It identifies multi-event clusters where diverse incident types co-locate within the same geographic cell.
Cell-Based Event Ingestion
The system divides the world into 1°×1° grid cells. Functions like ingestProtests, ingestFlights, ingestVessels, and ingestEarthquakes all delegate to ingestGeoEvent, which uses getCellId(lat, lon) to map coordinates to integer cell identifiers. Each cell tracks event counts and last-seen timestamps.
Pruning and Detection
The pruneOldEvents function removes entries older than 24 hours, ensuring only recent activity contributes to convergence detection. The detectGeoConvergence function then iterates over all populated cells. A cell triggers a convergence alert when it contains at least three distinct event types (CONVERGENCE_THRESHOLD = 3) and has not been previously reported (seenAlerts set).
Scoring and Signal Conversion
Alert scoring follows the formula:
typeScore = distinctTypes * 25;
countBoost = min(25, totalEvents * 2);
finalScore = min(100, typeScore + countBoost);
The geoConvergenceToSignal function converts these alerts into CorrelationSignalCore objects, adding human-readable location names via getLocationName and deriving confidence from the calculated score for downstream processing.
Code Examples
Updating a Hotspot Escalation Score
The following example demonstrates how to configure data getters and compute a new escalation score for a specific hotspot:
import { updateHotspotEscalation } from '@/services/hotspot-escalation';
import { setCIIGetter, setGeoAlertGetter, setMilitaryData } from '@/services/hotspot-escalation';
// Provide data sources (example stubs)
setCIIGetter(countryCode => {
// Return a mock CII value or null
return countryCode === 'US' ? 70 : null;
});
setGeoAlertGetter((lat, lon, radiusKm) => ({
score: 40,
types: 2,
}));
setMilitaryData(
[{ lat: 34.5, lon: 31.2 }], // flights
[{ lat: 34.6, lon: 31.3 }] // vessels
);
// Compute the new score for hotspot “dubai”
const result = updateHotspotEscalation(
'dubai', // hotspotId
12, // newsMatches
true, // hasBreaking
3 // newsVelocity
);
console.log('New combined score:', result.combinedScore);
The function pulls the CII, geo-alert, and military data, normalises each component, blends the dynamic score with the static baseline, updates the history, and returns the full DynamicEscalationScore object.
Source: src/services/hotspot-escalation.ts.
Detecting Geographic Convergence
This example shows how to ingest events from multiple sources and detect convergence clusters:
import {
ingestProtests,
ingestFlights,
ingestVessels,
detectConvergence,
} from '@/services/geo-convergence';
// Feed recent events (simplified)
ingestProtests([
{ lat: 31.2, lon: 35.0, time: new Date() }, // protest in Gaza
]);
ingestFlights([
{ lat: 31.3, lon: 35.1, lastSeen: new Date() }, // military flight nearby
]);
ingestVessels([
{ lat: 31.1, lon: 34.9, lastAisUpdate: new Date() }, // naval vessel nearby
]);
// Find any convergent cells
const alerts = detectConvergence();
alerts.forEach(alert => {
console.log(`Convergence at ${alert.lat},${alert.lon} – score ${alert.score}`);
});
The three distinct event types within the same 1°×1° cell trigger a convergence alert, scoring it according to the number of types and total events.
Source: src/services/geo-convergence.ts.
Key Implementation Files
| File | Purpose |
|---|---|
src/services/hotspot-escalation.ts |
Implements the hotspot escalation algorithm, normalisation, scoring, history tracking, trend detection, and signal emission. |
src/services/geo-convergence.ts |
Handles ingestion of geo-events, cell management, detection of multi-type convergence, scoring, and conversion to correlation signals. |
src/config/geo.ts |
Defines static hotspot, conflict zone, and strategic waterway data used by both services. |
src/types/index.ts |
Declares TypeScript types for hotspots, escalation trends, and signal structures. |
src/utils/analysis-constants.ts |
Contains constants and helper functions (e.g., generateSignalId) used when creating signal objects. |
These files together form the backbone of the risk-scoring and geographic convergence subsystems in the WorldMonitor platform.
Summary
- The hotspot escalation scoring algorithm aggregates four weighted signals—news activity (35%), country instability (25%), geo-alerts (25%), and military activity (15%)—into a normalized 0-100 raw score.
- Raw scores map to a 1-5 dynamic scale, then blend 70/30 with static baseline scores to produce final escalation values.
- Geographic convergence detection operates independently via a cell-based grid system, triggering alerts when three or more distinct event types cluster within a 1°×1° cell within 24 hours.
- Both systems maintain 24-hour rolling histories and emit structured signals for downstream processing.
Frequently Asked Questions
What are the four input signals used in the hotspot escalation algorithm?
The algorithm ingests news activity (article counts, breaking news flags, and velocity), the Country-Level Instability Index (CII) for overlapping countries, geo-alert convergence data (proximity scores and distinct event types), and military activity (counts of nearby flights and naval vessels). Each signal is normalized to 0-100 before weighting.
How is the final escalation score calculated from the raw signal data?
First, the calculateDynamicRaw function multiplies each normalized signal by its respective weight—0.35 for news, 0.25 for CII, 0.25 for geo-alerts, and 0.15 for military activity—and sums them into a 0-100 raw score. The rawToScore function maps this to a 1-5 integer scale. Finally, blendScores combines this dynamic score with the hotspot’s static baseline using a 70% dynamic / 30% static weighting to produce the final escalation value.
What constitutes a geographic convergence alert in the system?
A geographic convergence alert triggers when the detectGeoConvergence function in src/services/geo-convergence.ts identifies a 1°×1° grid cell containing at least three distinct event types—such as protests, military flights, naval vessels, and earthquakes—within a rolling 24-hour window. The cell must not have been previously reported, and the alert score is calculated as distinctTypes * 25 + min(25, totalEvents * 2), capped at 100.
How does the system prevent stale data from affecting scores?
Both subsystems implement 24-hour rolling windows. The hotspot escalation service prunes history to the last 24 hours with a maximum of 48 data points to prevent memory bloat and ensure trend detection reflects recent dynamics. Similarly, the geographic convergence service runs pruneOldEvents to remove entries older than 24 hours from the cell-based grid before executing detectGeoConvergence, ensuring that only current event clusters trigger alerts.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →