Stories
September 6, 2026 · View on GitHub
A Story is a self-contained paragraph about one weather parameter.
"Temperature for today", "wind overview for 1–3 days", "forest fire warning
for a county" are all stories. Every story has a name string, and
StoryFactory maps the name to the class that produces it.
Hierarchy
Story (abstract: hasStory, makeStory)
├── TemperatureStory → temperature_mean, temperature_max36hours, ...
├── PrecipitationStory → pop_max, precipitation_classification, ...
├── CloudinessStory → cloudiness_overview
├── WeatherStory → weather_overview, weather_forecast, ...
├── WindStory → wind_overview, wind_sea_overview, wind_anomaly, ...
├── FrostStory → frost_onenight, frost_twonights, ...
├── RelativeHumidityStory → relativehumidity_day, ...
├── RoadStory → roadwarning_overview, roadcondition_overview, ...
├── ForestStory → evaporation_day, forestfirewarning_county, ...
├── DewPointStory → dewpoint_range
├── PressureStory → pressure_mean
├── WaveStory → wave_range
└── SpecialStory → none, date, text, table
Dispatch
StoryFactory::create(name) walks these classes in order and returns the
Paragraph produced by the first one whose hasStory(name) returns true:
Paragraph StoryFactory::create(
const TextGenPosixTime& forecastTime,
const AnalysisSources& sources,
const WeatherArea& area,
const WeatherPeriod& period,
const std::string& name, // e.g. "wind_overview"
const std::string& variable); // e.g. "textgen::day1::story::wind_overview"
The variable argument is the prefix under which the story reads its own
configuration (::mininterval, ::fake::*, ::today::phrases, …).
Per-parameter Story classes
Each concrete class exposes:
class XxxStory : public Story
{
public:
static bool hasStory(const std::string& name);
Paragraph makeStory(const std::string& name) const;
// plus one method per generator, typically defined in its own .cpp:
// Paragraph overview() const;
// Paragraph anomaly() const;
// Paragraph mean() const;
};
makeStory is a big switch that dispatches on the name to the matching
method. For small stories the method body is inline in the class's .cpp;
for large ones the body is extracted into a lowercase file
(wind_overview.cpp, temperature_max36hours.cpp) — see the
file naming convention.
Generator entry points
Naming convention:
WindStory.cppholds the class itself and the trivial generators.wind_overview.cppholds the body ofWindStory::overview().wind_anomaly.cppholds the body ofWindStory::anomaly().wind_sea_overview.cppholds the body ofWindStory::sea_overview().
The top of each generator file has a \brief Implementation of method ...
comment that identifies the owning class.
Helper tool namespaces
Several families share helper code:
| Namespace | Used by |
|---|---|
TemperatureStoryTools | most temperature_* generators |
PrecipitationStoryTools | precipitation_*, pop_* |
WindStoryTools | wind_* |
CloudinessStoryTools | cloudiness_overview, weather_overview |
FrostStoryTools | frost_* |
RoadStoryTools | roadcondition_*, roadwarning_* |
Larger generators (weather_overview, weather_forecast, wind_overview)
additionally pull in narrative helper classes — WeatherForecast,
CloudinessForecast, PrecipitationForecast, FogForecast,
ThunderForecast, WindForecast. These are not Stories themselves; they
encapsulate multi-period classification and sentence composition that would
otherwise bloat the generator file.
Adding a new story
- Pick the parameter class it belongs to (
WindStory,TemperatureStory, …). - Add the name to that class's
hasStoryandmakeStory. - Add a method on the class.
- If the body is > ~100 lines, extract it into a lowercase file
(
my_story.cpp) and list it inMakefile's sources. - Add a user-facing doc under
docs/users/stories/<parameter>/my_story.mdwith the standard header and a status tag.
See also
- architecture.md — how a story call fits into the overall request flow
- user-facing catalogue — all 55 stories tagged by status and complexity