Visual Regression for Temperature Test Images

A temperature test image maps known input to known pixels and legend text. Visual regression for GD thermometers is not a live weather banner.

Back to Cumulus weather software guides

/test/temp_image.php was a script that drew a temperature picture so an operator could see whether PHP GD still worked. The scientific version of that script is a visual regression test: a known input (value, unit, scale range, legend strings) produces a known output (pixel positions, colors, readable text). It is not a live banner, not current weather, and not the essay on why a decorative analog scale can distort the observation—that lives on analog thermometer graphics as a mapped temperature scale.

A public /test/ image endpoint is a security smell. Historical weather PHP often accepted a temperature in the query string and returned a PNG. That is an unauthenticated renderer: easy to hammer, easy to probe for GD/font paths in error text, easy to hotlink with someone else’s number. Generate test images in CI or on a private box. TNET does not rehost the historical PHP or a ZIP of thermometer skins.

Historical context

Cumulus sites liked a tall thermometer PNG or a small temperature badge in a sidebar. GD drew the tube, the fill, the ice-point mark, and a numeral. When the picture looked wrong after a PHP upgrade, operators hit /test/temp_image.php and trusted their eyes. Eyes do not catch a one-pixel ice-point drift. Incoming links treated the URL as a cute graphic. The restoration keeps it as a regression-methods page.

Neighboring URLs are easy to conflate. A 468×60 banner packs several fields into a strip. An operational temperature chart is a time series. The thermometer project page is about mapping science: range, linearity, ice point, clip behavior. This test URL is about locking that map down so a later GD, font, or anti-aliasing change cannot move the mark without a failing comparison.

Known input, known pixels

A visual test is only as honest as the input record. Use a constructed example, labeled as such. Do not pull tonight’s <#temp> and bless the PNG as a golden; tomorrow the air will move and the test will either fail noisily or be rewritten until it is useless.

A sufficient input record:

  • quantity name (outside dry-bulb, not “feels like” unless that is the test);
  • numeric value;
  • unit token (C or F);
  • visible scale minimum and maximum;
  • canvas size;
  • whether the ice-point mark is expected;
  • legend strings that must appear in the image or in an adjacent HTML caption.

Example of a labeled fixture, not a weather report: “constructed input 0 °C on a linear −20 to 40 °C tube, 200 px stem, ice-point mark required.” That sentence is a test vector. It is not a claim about a station.

Outputs to assert:

  1. Legend / numeral. OCR is optional. Better: the renderer also returns the text it painted, or you keep the numeral in HTML and only paint the stem. If text is inside the PNG, a pixel hash alone will fail on font hinting; assert a region hash for the stem and a separate string assertion for the legend.
  2. Ice-point pixel. On a linear −20 to 40 °C stem of height H, 0 °C is one-third of the way up from the stem origin—not from a decorative bulb. Compute expected y from the same function the painter uses; allow a one-pixel tolerance for rounding. The thermometer article explains why the bulb must not count as data. The test is where that rule is enforced.
  3. Fill height for the value. Same mapping function. 0 °C fill and ice-point mark must agree.
  4. Clip flag. An off-scale constructed input (for example a value above the stated max) must not produce a “full but unlabeled” tube. Assert a cap marker or a > in the legend.
  5. Missing input. A missing value must not paint as 0 °C. Assert an empty stem and a -- numeral, matching the mapping article.

Do not treat a byte-identical PNG as the only golden across operating systems. GD, libpng, and fonts differ. Prefer structural assertions (coordinates, legend strings) plus a small stem-region comparison on one pinned CI image. If you hash the whole PNG, pin the OS and GD version in the test README.

Visual regression is not a live weather product

The PNG the harness writes should be tagged fixture output. Publishing it on the public site as “current temperature” is the same class of error as publishing an unlabeled sample graph. If you need to show operators what the test produced, show it in CI artifacts, with the input record printed above the image.

Query-string renderers (/test/temp_image.php?t=21.4) look convenient and are how hotlink farms and cache-busters were born. They also skip the input record: no unit, no range, no ice-point expectation. A regression suite always passes the full record into a function, not into HTTP.

Fonts and encoding belong in the test. A degree sign that becomes ° is a failed legend assertion. A fallback font that changes digit width can shift a label into the tube; that is a layout bug the thermometer science page does not cover and this harness should.

Distinct from scale-distortion science

The /projects.phpgdthermometer article tells you why a nonlinear bulb, a tick that does not match the fill, or a color-only “heatmap tube” is a false observation. This article tells you how to catch those bugs automatically.

If you only repeat the ice-point geometry essay here, you clone a neighbor. The extra duty is operational:

  • store vectors (inputs) and goldens (expected y, expected strings);
  • run them on every renderer change;
  • fail on stem drift, not on a new drop-shadow in a region you marked as chrome;
  • keep chrome (bulb highlight, background) out of the compared region or accept that every aesthetic tweak will fail the suite.

A banner test is a different canvas. Reuse the input-record idea; do not reuse a 200 px stem golden on a 60 px strip. Chart PNGs are a third family: they need time on the x-axis, which a thermometer test must not invent.

How to run it without /test/

  • Call render_thermometer($vector) in PHPUnit or a plain PHP script; assert coordinates; write a PNG only as an artifact.
  • Deny GD endpoints on the public vhost.
  • Do not accept remote font URLs or file paths from the request.
  • If you inherit temp_image.php, delete it. The Cumulus hub is the catalog of explainers that replaced those scripts.

The XML harness on /test/parsexml.php is the same architectural split for a different artifact: goldens in CI, nothing executable at a public test URL. Do not parse XML to get the temperature inside the image test unless that parse is already gated; the image test should take a scalar vector so failures are about pixels, not about XML.

Modern relevance

A mapped graphic is a derived display of an observed (or constructed) scalar. Whether later research can use a backyard temperature at all depends on units, identity, and honesty of the display. TNET’s public note on source identity and quality control is data sources, quality controls, and methodology. Observed versus derived labeling is how the service works. This URL does not stream a thermometer. It states how to lock the picture to the number.

Sources