diff --git a/CHANGELOG.md b/CHANGELOG.md
index fe085f6..0bb89c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,17 @@
Notable changes to NwsAlertBot, most recent first. For setup and usage, see
[README.md](README.md); for architecture and internals, see [docs/TECHNICAL.md](docs/TECHNICAL.md).
+- **Fix: `Map.Enabled: false` (the shipped default) silently disabled free IEM map images too,
+ not just Mapbox.** `MapService.GetMapUrlAsync` gated its entire body — including both
+ no-account-needed IEM autoplot paths (#208 for VTEC alerts, #217 for SPS) — behind
+ `Map.Enabled`, a setting documented and named for Mapbox specifically. A fresh install that
+ never set up Mapbox (the shipped default) got zero map images for ordinary NWS
+ warnings/watches/advisories, even though IEM needs no signup. Confirmed live: a Discord post
+ for an Extreme Heat Warning had no image attached. IEM is now always attempted first regardless
+ of `Map.Enabled`; that setting (and `AccessToken`) only gate the Mapbox fallback, routed through
+ the existing `GetMapboxFallbackUrlAsync` so both call sites share one "is Mapbox usable" check.
+ SPC Outlook/MCD/ERO were already unaffected — they build their own image URLs independently of
+ `MapService`. See docs/TECHNICAL.md "Map Images — Internals".
- **Fix: "Details" links pointed at the raw `api.weather.gov` JSON API instead of a
human-readable webpage, and were SMS-only.** NWS alerts and HWO posts built their `DetailsUrl`
from `api.weather.gov/alerts/{id}` / `api.weather.gov/products/{uuid}` — link-preview generators
diff --git a/Config/AppSettings.cs b/Config/AppSettings.cs
index f8c9232..c073a7c 100644
--- a/Config/AppSettings.cs
+++ b/Config/AppSettings.cs
@@ -570,7 +570,7 @@ public class VoipMsSettings : IPlatformFilterSettings
public class MapSettings
{
- /// Whether to generate Mapbox static map images for alert areas.
+ /// Whether to generate Mapbox static map images for alert areas. Only gates the Mapbox fallback — IEM autoplot maps (the primary source for most NWS alerts) need no account and are always attempted regardless of this setting.
public bool Enabled { get; set; }
///
diff --git a/README.md b/README.md
index 34560e1..c209dbb 100644
--- a/README.md
+++ b/README.md
@@ -990,14 +990,15 @@ If nothing is enabled, it logs a warning and exits without posting anything.
## Map Images (Mapbox)
-When enabled, the bot generates a map image for each alert (NWS warnings/watches/advisories, plus
-SPC Outlook and WPC ERO posts) and attaches it to every platform that supports images. Most maps
-come from a free IEM service requiring no setup on your part; Mapbox is used as a fallback for
-alerts with no VTEC code. See
+The bot generates a map image for each alert (NWS warnings/watches/advisories, plus SPC Outlook,
+SPC MCD, and WPC ERO posts) and attaches it to every platform that supports images. Most maps come
+from a free IEM service that needs no account or setup on your part — this always runs, even
+without Mapbox configured. Mapbox is only used as a fallback, for the minority of alerts IEM can't
+provide a map for (no VTEC code, or not yet indexed by IEM), and requires the setup below. See
[docs/TECHNICAL.md — Map Images](docs/TECHNICAL.md#map-images--internals) for how the bot decides
which source to use.
-### Setup
+### Setup (Mapbox fallback only — optional)
1. Create a free account at [account.mapbox.com](https://account.mapbox.com/)
2. Copy your **default public token** (starts with `pk.`) from the Tokens page
@@ -1016,7 +1017,7 @@ which source to use.
| Field | Description | Default |
|---|---|---|
-| `Enabled` | Whether to generate map images | `false` |
+| `Enabled` | Whether to use Mapbox as a fallback when IEM has no map for an alert. Does not affect IEM, which is always attempted first regardless of this setting. | `false` |
| `AccessToken` | Mapbox public token (starts with `pk.`) | `""` |
| `Style` | Mapbox style ID, format `{username}/{style_id}` | `"mapbox/outdoors-v12"` |
| `Width` | Image width in pixels (max 1280) | `600` |
diff --git a/Services/MapService.cs b/Services/MapService.cs
index d7dd1b5..7f2dd3c 100644
--- a/Services/MapService.cs
+++ b/Services/MapService.cs
@@ -17,7 +17,9 @@ namespace NwsAlertBot.Services;
/// warning polygon, county boundaries, and NEXRAD radar overlay. IEM's VTEC JSON API is
/// checked first to confirm the event exists in their database; if not found, falls through
/// to Mapbox. IEM silently serves a default demo image (HTTP 200) for unknown events, so
-/// the pre-flight JSON check is required to avoid posting wrong maps.
+/// the pre-flight JSON check is required to avoid posting wrong maps. IEM needs no
+/// account/token, so both IEM paths (#208 here and #217 for SPS) are always attempted
+/// regardless of Map.Enabled/AccessToken — only the Mapbox fallback below is gated by those.
///
/// Mapbox fallback: Overlay geometry priority:
/// 1. Alert's own GeoJSON polygon (when NWS provides one).
@@ -57,13 +59,13 @@ public MapService(
}
///
- /// Returns a map image URL for the alert, or null if map generation is disabled,
- /// unconfigured, or no bounding box could be determined.
+ /// Returns a map image URL for the alert, or null if no image source is available.
+ /// IEM autoplot (both #217 and #208 below) needs no account/token and is always attempted
+ /// regardless of — that setting only gates the Mapbox
+ /// fallback, since Mapbox is the one source that requires a configured account.
///
public async Task GetMapUrlAsync(NwsAlert alert)
{
- if (!_settings.Enabled) return null;
-
// IEM autoplot #217: SPS-specific map image (non-VTEC). Takes the IEM product ID
// constructed from AWIPSidentifier (AFOS PIL) and WMOidentifier parsed from the alert.
// Pre-flight GeoJSON check required: IEM returns HTTP 200 with a demo image for unknown
@@ -110,8 +112,10 @@ alert.VtecAction is not ("CAN" or "EXP"))
}
// Fallback: Mapbox static image (used for non-VTEC events, CAN/EXP, or when IEM
- // doesn't have the event in its VTEC database)
- return await GetMapboxUrlAsync(alert);
+ // doesn't have the event in its VTEC database). Unlike IEM above, this does require
+ // Map.Enabled + a configured AccessToken — routed through GetMapboxFallbackUrlAsync so
+ // both entry points share the same "is Mapbox actually usable" check.
+ return await GetMapboxFallbackUrlAsync(alert);
}
// IEM sometimes uses a different phenomena code than NWS does in the VTEC string.
@@ -172,9 +176,11 @@ alert.VtecAction is not ("CAN" or "EXP"))
}
///
- /// Returns a Mapbox static map URL for the alert, bypassing the IEM check.
- /// Called when the IEM image download fails after all retries.
- /// Returns null if Mapbox is not configured or no bounding box is available.
+ /// Returns a Mapbox static map URL for the alert, bypassing the IEM check. Called both by
+ /// GetMapUrlAsync (when no IEM image is available at all) and by
+ /// SocialMediaOrchestrator.DownloadMapImageAsync (when an IEM image URL was returned but its
+ /// download then failed after all retries). Returns null if Map is disabled, no AccessToken
+ /// is configured, or no bounding box is available.
///
public Task GetMapboxFallbackUrlAsync(NwsAlert alert)
{
diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md
index 3630816..7c2ee45 100644
--- a/docs/TECHNICAL.md
+++ b/docs/TECHNICAL.md
@@ -626,13 +626,18 @@ filter fields were added beyond `IncludeEro` (see [Configuration Reference](#con
## Map Images — Internals
-When enabled, the bot generates a **Mapbox Static Images** URL for each NWS alert (warnings,
-watches, advisories) and attaches it via the alert's `MapImageUrl` field to every platform that
-supports images. SPC Convective Outlook and WPC ERO posts get their own image independently of
-this setting through the same `MapImageUrl` field, so the platform behavior table in the README's
+For each NWS alert (warnings, watches, advisories), `MapService.GetMapUrlAsync` generates an
+image URL and attaches it via the alert's `MapImageUrl` field to every platform that supports
+images. SPC Convective Outlook, SPC MCD, and WPC ERO posts get their own image independently of
+`MapService` entirely (built directly from spc.noaa.gov/wpc.ncep.noaa.gov by their own services)
+through the same `MapImageUrl` field, so the platform behavior table in the README's
[Map Images](../README.md#map-images-mapbox) section applies to all of them.
-Map images are generated from two sources depending on the alert type:
+Map images are generated from two sources depending on the alert type. **`Map.Enabled` and
+`Map.AccessToken` only gate the Mapbox source** — the two IEM paths below need no account/token
+and are always attempted first, regardless of whether Mapbox is configured. (Before this was
+decoupled, `Map.Enabled: false` — the shipped default — silently skipped IEM too, so a fresh
+install with no Mapbox account got no map images at all for ordinary NWS alerts.)
**IEM (primary — most NWS alerts):** The bot parses the VTEC code from each NWS alert
(`parameters.VTEC`) and requests a pre-rendered PNG from Iowa State University's IEM Autoplot