Land Surface Temperature Explained

On this page

Problem statement

Satellite heat maps of cities are maps of land surface temperature (LST): the radiative temperature of whatever the satellite sees from above — roofs, tree crowns, roads, water, dry grass. It is a real, physical quantity, measured consistently across whole cities, and it is not the temperature people feel. It is hotter than the air in the day, varies by tens of degrees between neighbouring surfaces, depends on what each surface is made of, and is measured at one moment of one clear day.

Reading heat maps correctly starts with knowing what LST is, what it responds to, and how it relates to the air temperature that heat warnings and health studies use.

Quick answer

  • What it is: the skin temperature of the surface, derived from thermal infrared radiance and the surface's emissivity.
  • What drives it: sunlight absorbed (albedo), heat used for evaporation (moisture, vegetation), heat stored and released (materials), and ventilation.
  • What it is not: air temperature. Daytime LST in summer can be 10–30 °C above the air at 2 m.
import numpy as np, rasterio, pystac_client, planetary_computer
from rasterio.warp import reproject, Resampling, transform_bounds
from rasterio.transform import from_origin

AOI = (-3.75, 40.38, -3.63, 40.47)                            # central Madrid
CRS = "EPSG:32630"
x0, y0, x1, y1 = transform_bounds("EPSG:4326", CRS, *AOI)
x0, y1 = np.floor(x0 / 30) * 30, np.ceil(y1 / 30) * 30
W, H = int(np.ceil((x1 - x0) / 30)), int(np.ceil((y1 - y0) / 30))
TF = from_origin(x0, y1, 30, 30)
cat = pystac_client.Client.open("https://planetarycomputer.microsoft.com/api/stac/v1")

def on_grid(item, asset, dtype="uint16", resampling=Resampling.nearest):
    out = np.zeros((H, W), dtype=dtype)
    with rasterio.open(planetary_computer.sign(item.assets[asset].href)) as src:
        reproject(rasterio.band(src, 1), out, dst_transform=TF, dst_crs=CRS, resampling=resampling)
    return out

scene = min((i for i in cat.search(collections=["landsat-c2-l2"], bbox=list(AOI),
                                   datetime="2023-07-01/2023-07-31").items()
             if i.properties["platform"] in ("landsat-8", "landsat-9")),
            key=lambda i: i.properties["eo:cloud_cover"])
lst = on_grid(scene, "lwir11").astype("float32")
lst = np.where(lst == 0, np.nan, lst * 0.00341802 + 149.0 - 273.15)
wc_item = next(cat.search(collections=["esa-worldcover"], bbox=list(AOI), datetime="2021").items())
wc = on_grid(wc_item, "map", "uint8", Resampling.mode)
print(f"{scene.id}, {scene.datetime:%Y-%m-%d %H:%M} UTC")
for code, name in ((10, "tree cover"), (30, "grassland"), (50, "built-up"), (60, "bare"), (80, "water")):
    m = (wc == code) & np.isfinite(lst)
    if m.sum() > 50:
        print(f"{name:<11} {m.sum():>6,} px: median {np.nanmedian(lst[m]):5.1f} °C, "
              f"10th–90th {np.nanpercentile(lst[m], 10):.1f}–{np.nanpercentile(lst[m], 90):.1f}")
LC08_L2SP_201032_20230729_02_T1, 2023-07-29 10:55 UTC
tree cover  23,520 px: median  42.4 °C, 10th–90th 38.4–46.3
grassland    5,948 px: median  46.3 °C, 10th–90th 40.3–51.5
built-up    84,938 px: median  44.3 °C, 10th–90th 41.9–46.3
bare           332 px: median  43.1 °C, 10th–90th 40.4–45.7
water          219 px: median  36.6 °C, 10th–90th 32.0–41.7

On one July late morning the classes span ten degrees, and not in the order most people expect. Water is coolest at 36.6 °C. The hottest class is not built-up land but grassland, at 46.3 °C: in a Madrid summer, unirrigated grass is dry and has nothing to evaporate. Built-up land sits at 44.3 °C and tree canopy only two degrees below it. The spread within each class is as large as the gaps between them — 8 °C between the cooler and warmer tenths of tree cover.

What is measured, when, and what a pixel contains

1. Know what the sensor measures

A thermal infrared sensor measures radiance; converting it to temperature needs the atmosphere removed and the surface's emissivity known. Level-2 products (Landsat C2 ST, MODIS LST) have done both.

2. Know when it was measured

Landsat passes in late morning, MODIS Terra and Aqua around 10:30 and 13:30 (and their night twins), ECOSTRESS at varying times. The same surface has very different temperatures at different hours.

3. Know what the pixel contains

At 100 m native resolution, a pixel mixes roofs, streets, trees and courtyards. Its LST is a radiance-weighted mix of their temperatures.

4. Separate LST from air temperature

Use LST to locate hot surfaces and compare places; use air temperature for exposure and health. Relating them needs data at both, at the same time.

5. Compare like with like

Different days, seasons and sensors give different absolute temperatures. Compare within one scene or composite, or use relative values.

The surface energy balance: sunlight absorbed, reflected, used for evaporation, stored and emitted as thermal radiation. what a surface does with sunlightsunlightminus what isreflectedevaporationif there is waterstorageinto masonry, soilheatingwhatever is leftthermalemissionwhat the sensorseesDry, dark, impervious surfaces have nowhere else to put the energy spatialworkflow.io
What a surface does with sunlight decides how hot it gets.

Surface against air at the same moment

Example 1 — surface against air at the same moment

import pandas as pd
df = pd.read_csv("https://noaa-global-hourly-pds.s3.amazonaws.com/2023/08221099999.csv",
                 usecols=["DATE", "TMP"], low_memory=False)
val = pd.to_numeric(df.TMP.str.split(",").str[0], errors="coerce") / 10
air = pd.Series(np.where(val.abs() < 90, val, np.nan),
                index=pd.to_datetime(df.DATE).dt.tz_localize("UTC")).dropna()
near = air[(air.index > scene.datetime - pd.Timedelta("45min")) &
           (air.index < scene.datetime + pd.Timedelta("45min"))].mean()
print(f"air temperature at Barajas around the overpass: {near:.1f} °C")
print(f"median LST of built-up surfaces in the centre: {np.nanmedian(lst[wc == 50]):.1f} °C "
      f"({np.nanmedian(lst[wc == 50]) - near:+.1f})")
print(f"median LST of tree canopy: {np.nanmedian(lst[wc == 10]):.1f} °C "
      f"({np.nanmedian(lst[wc == 10]) - near:+.1f})")
air temperature at Barajas around the overpass: 32.0 °C
median LST of built-up surfaces in the centre: 44.3 °C (+12.3)
median LST of tree canopy: 42.4 °C (+10.4)

Every land surface runs far above the air: built-up surfaces 12 °C above it, even tree canopy 10 °C. The air temperature is one number for the whole area, measured in shade at 2 m; LST varies surface by surface.

Example 2 — the same place on different days

days = [i for i in cat.search(collections=["landsat-c2-l2"], bbox=list(AOI),
                              datetime="2023-06-01/2023-08-31").items()
        if i.properties["platform"] in ("landsat-8", "landsat-9") and i.properties["eo:cloud_cover"] < 10]
for it in sorted(days, key=lambda i: i.datetime):
    t = on_grid(it, "lwir11").astype("float32")
    t = np.where(t == 0, np.nan, t * 0.00341802 + 149.0 - 273.15)
    print(f"{it.datetime:%Y-%m-%d}: built-up median {np.nanmedian(t[wc == 50]):5.1f} °C, "
          f"tree cover {np.nanmedian(t[wc == 10]):5.1f} °C")
2023-06-27: built-up median  42.6 °C, tree cover  40.5 °C
2023-07-13: built-up median  45.8 °C, tree cover  43.5 °C
2023-07-29: built-up median  44.3 °C, tree cover  42.4 °C
2023-08-06: built-up median  42.9 °C, tree cover  41.1 °C
2023-08-14: built-up median  42.5 °C, tree cover  40.8 °C
2023-08-22: built-up median  45.9 °C, tree cover  44.0 °C

The absolute temperatures move by more than three degrees between clear days; the gap between built-up land and trees stays at about two degrees in every scene. That stability of the pattern is what makes LST useful for planning despite its day-to-day swing.

Example 3 — within-class spread

b = lst[(wc == 50) & np.isfinite(lst)]
print(f"built-up pixels: {b.size:,}; interquartile range {np.percentile(b, 75) - np.percentile(b, 25):.1f} °C; "
      f"hottest 5% above {np.percentile(b, 95):.1f} °C")
built-up pixels: 84,938; interquartile range 2.3 °C; hottest 5% above 46.9 °C

"Built-up" is not one surface. Half its pixels fall within a 2.3 °C band, but the hottest twentieth run above 46.9 °C — roof material, colour, street width and missing trees make the difference within the same land cover class.

Median land surface temperature by land cover class in central Madrid on 29 July 2023 at 10:55 UTC. median LST by land cover, one July late morninggrassland (dry)46.3 °Cbuilt-up44.3 °Cbare43.1 °Ctree cover42.4 °Cwater36.6 °C025°50°Air at Barajas around the overpass: 32.0 °C spatialworkflow.io
One late morning, five surfaces, a spread of many degrees.

Why surfaces differ, and LST diverges from air

Why surfaces differ so much

Dark, dry, impervious surfaces absorb most sunlight and have no water to evaporate; they heat until they can shed the energy by radiation and convection. Vegetation reflects more near-infrared and spends much of its absorbed energy evaporating water. Water bodies mix heat into depth. The same sunshine produces temperatures tens of degrees apart.

Why LST and air temperature diverge

Air near the ground is heated by all surfaces around it, mixed by wind and buffered by its heat capacity. It responds slowly and smoothly; surfaces respond immediately and locally. The divergence is largest on calm, sunny afternoons and smallest at night and on cloudy days.

Why the timing matters

Surfaces heat through the morning and peak after midday; at night they cool, with materials such as concrete releasing stored heat slowly. A late-morning map shows where surfaces heat fastest; a night map shows where heat is stored. They highlight overlapping but different places.

Air temperature at the overpass against the land surface temperature of built-up land and tree canopy. surface against air, same momenttemperatureabove the airair, Barajas, 2 m32.0 °C—built-up surfaces44.3 °C+12.3 °Ctree canopy42.4 °C+10.4 °CAcross six clear scenes, built-up stays about 2 °C above trees spatialworkflow.io
Every surface is well above the air in a sunny late morning; trees only a little less.

Emissivity, viewing angle, water and clouds

  • Emissivity errors of 0.01 translate into LST errors of roughly 0.5–1 K.
  • Viewing geometry: a satellite looking straight down sees roofs more than walls; the "complete" urban surface temperature includes vertical surfaces it misses.
  • Water LST is valid but responds to depth and flow, not only to the day's weather.
  • Clouds are cold; any unmasked cloud is a false cool spot.
  • Units: products are in Kelvin; convert with care.

FAQ

What is land surface temperature?

The radiative skin temperature of the surface as seen from above, derived from thermal infrared measurements and surface emissivity.

Is LST the same as air temperature?

No. In daytime it is often 10–30 °C higher for dry, dark surfaces.

Why are some surfaces so much hotter than others?

Different albedo, moisture, materials and shade change how much sunlight becomes heat.

When is LST measured?

At the satellite's overpass time — late morning for Landsat, several fixed times for MODIS.

Can I compare LST between days?

Absolute values change with weather; compare patterns or relative values, or use composites.

Why use LST at all for heat studies?

It maps surface heat everywhere at high resolution, which shows where materials and greening make a difference.