# How the Hotspot Escalation Scoring Algorithm Works in WorldMonitor

> Discover how the WorldMonitor hotspot escalation scoring algorithm uses news activity, country instability, geo-alerts, and military data to generate risk scores. Learn how geographic convergence is detected.

- Repository: [Elie Habib/worldmonitor](https://github.com/koala73/worldmonitor)
- Tags: internals
- Published: 2026-03-09

---

**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`](https://github.com/koala73/worldmonitor/blob/main/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`:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/koala73/worldmonitor/blob/main/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:

```typescript
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:

```typescript
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`](https://github.com/koala73/worldmonitor/blob/main/src/services/hotspot-escalation.ts).

### Detecting Geographic Convergence

This example shows how to ingest events from multiple sources and detect convergence clusters:

```typescript
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`](https://github.com/koala73/worldmonitor/blob/main/src/services/geo-convergence.ts).

## Key Implementation Files

| File | Purpose |
|------|---------|
| **[`src/services/hotspot-escalation.ts`](https://github.com/koala73/worldmonitor/blob/main/src/services/hotspot-escalation.ts)** | Implements the hotspot escalation algorithm, normalisation, scoring, history tracking, trend detection, and signal emission. |
| **[`src/services/geo-convergence.ts`](https://github.com/koala73/worldmonitor/blob/main/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`](https://github.com/koala73/worldmonitor/blob/main/src/config/geo.ts)** | Defines static hotspot, conflict zone, and strategic waterway data used by both services. |
| **[`src/types/index.ts`](https://github.com/koala73/worldmonitor/blob/main/src/types/index.ts)** | Declares TypeScript types for hotspots, escalation trends, and signal structures. |
| **[`src/utils/analysis-constants.ts`](https://github.com/koala73/worldmonitor/blob/main/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`](https://github.com/koala73/worldmonitor/blob/main/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.