Weather Banners as Lossy Summaries of Station Observations

PHP GD weather banners are lossy summaries of temperature, wind, and pressure. What belongs on a status image, how to cache it, and why hotlinking failed.

Back to PHP weather scripts and software guides

A station banner is a small PNG or JPEG with a handful of numbers painted on it: temperature, wind, pressure, maybe a station name. PHP’s GD extension made those images easy to generate from the same files a Weather Display or Cumulus site already uploaded. The historical TNET Weather URL /projects/phpggbanner sat in that workflow. This article is about the image as a scientific object: what it can honestly carry, what it throws away, and why letting the internet hotlink it became a load and security problem.

This page is not the Cumulus-host banner gallery, and it is not a download of the old generator. Sample banner listings show how operators once shared the pictures. Here the subject is the observation contract those pictures compress.

A banner is not a station file

Weather Display can FTP clientraw.txt, climate HTML, and graphs. Cumulus can emit web tags and a realtime file. Those artifacts still contain fields, units, and timestamps. A GD banner does not. GD rasterizes text and maybe an icon onto a bitmap (PHP image processing and GD). Once the pixels are written, a consumer cannot recover:

  • the native precision of the logger (a banner might print 72° from 72.4°F);
  • the observation time, unless you also paint a clock;
  • the unit system, unless you paint it;
  • quality flags, sensor identity, or missing-value codes;
  • the difference between an observed gust and a forecast phrase someone dropped into the same template.

The image is therefore a lossy summary of observations, not a syndication format. Treat it the way you would treat a screenshot of a table: useful for a human glance, unsafe as an archival source.

Which variables belong on a small status image

Physical space on a typical 468×60 or 500×80 banner is the first filter. Cognitive space is the second: a visitor should be able to read the strip in one look. Three core scalars earn their pixels because they are independently useful and routinely present in PWS files:

  • Air temperature. The most requested status number. Always pair it with a unit. A bare 23 is unusable across a mixed °F/°C community.
  • Wind. Speed without direction is incomplete; direction without a gust can hide a short peak. If only one wind number fits, say which one it is (average versus gust) instead of labeling it “wind.”
  • Pressure. Tendency matters as much as the absolute value for a glance forecast, but a tiny banner rarely has room for a three-hour change. If you omit tendency, do not imply the pressure is a forecast.

Everything else is optional and expensive:

  • Humidity and dewpoint fight for the same moisture story; pick one.
  • Rain “today” needs a reset rule (midnight local versus a rolling 24 hours) that the bitmap will not explain.
  • Forecast text from a model or a canned WD phrase is not an observation. If you paint it, mark it as forecast.
  • Webcams and radar thumbnails turn the banner into a different product with different caching and privacy rules.

Latitude and longitude do not belong in the pixels. They belong in station metadata, in signed decimal degrees with a stated datum. Painting 33.4N on a banner does not make that metadata quality-controlled.

Painting observations with GD

The usual pattern was: read a local station file, format a few strings, allocate a truecolor image, draw a background, call imagestring or a TrueType function, emit imagepng or imagejpeg. That pattern is still valid. The scientific mistakes were almost never in the drawing calls. They were in the data path:

  • Reading a remote URL on every hit instead of a local file the station software already uploaded.
  • Rendering even when the source file is older than the station’s expected upload interval, so the banner looks live while the observations are stale.
  • Defaulting missing fields to zero. A calm wind can be 0; a missing wind cannot.
  • Mixing unit systems when the template was copied from another station.

A generated banner should carry a generation timestamp in the HTTP headers even if you refuse to paint a clock. Last-Modified derived from the station file’s mtime is more honest than a decorative “LIVE” in the corner.

Caching is part of the observation

Station files often update every few minutes. Banners do not need to. Regenerating a PNG on every request wastes CPU, locks the PHP process, and multiplies disk reads of the same clientraw bytes. More important, uncached banners train consumers to treat the image URL as a realtime API.

Practical rules that do not depend on any particular generator:

  • Generate from a local file. Do not let GD imagecreatefrompng pull a remote template on each hit.
  • Cache the output bytes keyed by the source file’s modification time (or a hash of the fields you actually painted).
  • Send Cache-Control with a max-age on the order of the station upload interval, not “must-revalidate” on every request unless you are debugging.
  • If you must support an immediate refresh for the operator, gate it behind a private query that is not the public hotlink URL.

HTTP caching semantics are specified in RFC 9111. A weather banner is a perfect case for a short, explicit freshness lifetime. The observed temperature in the pixels should not change faster than the file you read.

Why hotlinked banners became a problem

PWS culture encouraged <img src="https://someone.example/banner.php"> on forums, mesomaps, and signature blocks. Each remote page view then executed PHP and GD on the station host. That produced three distinct failures:

  1. Load. A popular mesomap can request the banner far more often than the station uploads data. Without a cache, you pay GD cost per viewer, not per observation.
  2. Availability. Shared hosts killed scripts that ran too long or too often. The banner went red or blank, which readers interpreted as a station outage.
  3. Security. A PHP URL that accepts query parameters for colors, text, or a source file is a remote renderer. If those parameters reach GD or the filesystem, the banner endpoint is no longer a status image; it is an open function. Even without parameters, a public generator can leak PHP errors, path names, or last-known observations you did not mean to syndicate.

The durable fix is to publish a static image that a cron job or the station software refreshes, and to block hotlink floods at the web server. Dynamic banner.php belongs behind the same access rules as any other script that reads local data. See the downloads provenance notes for why TNET also does not rehost the historical generator package.

How to read a banner you did not generate

If you consume someone else’s status image:

  • Treat values as historical the moment they leave the origin, unless the publisher documents freshness.
  • Do not digitize pixels as if they were a logger dump. Ask for the feed, CSV, or ClientRaw instead.
  • Units painted on the image win over assumptions from the station’s country.
  • A photogenic banner is not quality control. Siting bias, a stuck anemometer, and a stale cache all look like clean type.

TNET’s connection research still has to decide which atmospheric fields are worth summarizing and which must remain in the underlying records. That grouping problem is the same one a banner designer faces with sixty pixels of height. The public explanation of those groups is the evidence families brief, which is the right next step if you care about what a short status display should and should not imply.

The projects index and the legacy scripts hub remain the historical map of related utilities. None of them turn a PNG back into a climate archive.