Atmospheric correction engine for satellite imagery
I built this app out of nostalgia — both to revive the 6S satellite image atmospheric correction tool that was once hosted at 6s.ltdri.org (now a redirect to salsa.umd.edu, with the web-based version long gone), and as a tribute to Vermote et al. (1997) for creating the 6S model and making it freely available online from 2006 to 2013. Feel free to use it, or just take a stroll down memory lane.
Dikembangkan dan dikelola oleh Lalu Muhamad Jaelani, Laboratorium Geospasial, Departemen Teknik Geomatika ITS. (Developed & managed by the Geospatial Laboratory, Geomatics Engineering ITS)
6S Natural is a web-based atmospheric correction tool built on top of Py6S — a Python interface to the 6S (Second Simulation of a Satellite Signal in the Solar Spectrum) radiative transfer code developed by Vermote et al. (1997).
| Parameter | Range | Steps |
|---|---|---|
| Solar Zenith | 0°–75° | 6 steps |
| Water Vapour (H₂O) | 0–8.5 g/cm² | 6 steps |
| Ozone (O₃) | 0–0.8 cm·atm | 5 steps |
| Aerosol Optical Thickness (AOT) | 0–3 | 6 steps |
| Target Altitude | 0–7.75 km | 6 steps |
a, b, and k for each band, then the at-sensor signal is converted to surface reflectance via ρs = (ρTOA − a·k) / (b·k), where ρTOA = L × k and k = π·ds² / (F₀·cos θs).Surface reflectance (ρs) is derived from the at-sensor signal via:
ρs = (ρTOA − a·k) / (b·k) | ρTOA = L × k | k = π·ds² / (F₀·cos θs)
You only need three coefficients per band — a, b, and k — taken directly from the results table:
| ρs | = surface reflectance | (unitless, 0–1) |
| ρTOA | = top-of-atmosphere reflectance = L × k | (unitless) |
| L | = at-sensor radiance | (W·m⁻²·sr⁻¹·µm⁻¹) |
| a | = path radiance (Lp) | (W·m⁻²·sr⁻¹·µm⁻¹) |
| b | = (τ₂ · E₀) / π — total upward transmittance × solar irradiance ÷ π | (W·m⁻²·sr⁻¹·µm⁻¹) |
| k | = π·ds² / (F₀·cos θs) — radiance → reflectance conversion | (conversion factor) |
| ds | = Earth–Sun distance | (AU, from day-of-year) |
| F₀ | = TOA solar irradiance in the band | (W·m⁻²·µm⁻¹) |
| θs | = solar zenith angle | (degrees) |
The coefficients a, b, and k are obtained per band from the pre-computed iLUT for the given atmospheric parameters (solar zenith, H₂O, O₃, AOT, altitude, day-of-year).
Knowing the actual atmospheric conditions is the biggest barrier to physics-based correction. 6S Natural can now fetch H₂O, O₃, and AOT automatically from the nearest station: set the location (lat/lon) and image date, then click “🌤️ Auto-fill (Nearest Station)” in the Location section. All retrieved values remain fine-tunable — the auto-fill only sets the sliders.
| Parameter | Source | Method |
|---|---|---|
| AOT (550 nm) | METAR horizontal visibility — nearest of 19 Indonesian airports (NOAA aviationweather.gov; the same NOAA feed used by Ogimet) | Koschmieder: AOT₅₅₀ = (3.912 / V) × H, with V = visibility (km) and H = aerosol scale height (default 1.5 km, 0.5–3 km adjustable). Daytime reports (00–06Z ≈ 07:00–13:00 WIB) are preferred; when visibility is unreported (CAVOK), V = 10 km is assumed. |
| Water vapour (H₂O) | AERONET V3 Level 1.0 — nearest of 11 sites in Indonesia & nearby (Jakarta, Bandung, Palangkaraya, Pontianak, Makassar, Jambi, Singapore, …) | Precipitable water column in cm (= g/cm²), median over ±1 day of the image date. Falls back to 2.5 g/cm² if unavailable. |
| Ozone (O₃) | Tropical climatology default | 0.30 cm·atm (≈ 300 DU). Actual column ozone (e.g. CAMS/MERRA-2) is planned for Phase 2. |
📖 Related reading: Jaelani, L.M. (2024) — “Sumber Data Visibilitas Horizontal untuk Koreksi Atmosfer Citra Satelit” (lmj.my.id) — the rationale for using horizontal visibility as an aerosol proxy when direct aerosol data are unavailable.
| Sensor | Agency | Bands | Spectral Range | Rural* (=Continental) | Maritime | Urban | Desert |
|---|---|---|---|---|---|---|---|
| Sentinel 3A OLCI | ESA · Copernicus | 21 | 0.4–1.02 µm | ✓ | ✓ | ✓ | ✓ |
| Sentinel 3A SLSTR | ESA · Copernicus | 6 | 0.55–1.38 µm | ✓ | ✓ | ✓ | ✓ |
| Sentinel 2 MSI | ESA · Copernicus | 13 | 0.44–2.19 µm | ✓ | ✓ | ✓ | ✓ |
| Landsat 8 OLI | NASA · USGS | 8† | 0.44–2.30 µm | ✓ | ✓ | ✓ | ✓ |
| Landsat 7 ETM+ | NASA · USGS | 6 | 0.45–2.35 µm | ✓ | ✓ | ✓ | ✓ |
| Landsat 4/5 TM | NASA · USGS | 6 | 0.45–2.35 µm | ✓ | ✓ | ✓ | ✓ |
| WorldView-3 | Maxar · DigitalGlobe | 16 | 0.4–2.37 µm | ✓ | ✓ | ✓ | ✓ |
| WorldView-2 | Maxar · DigitalGlobe | 8 | 0.4–1.04 µm | ✓ | ✓ | ✓ | ✓ |
| GeoEye-1 | Maxar · DigitalGlobe | 4 | 0.45–0.92 µm | ✓ | ✓ | ✓ | ✓ |
| QuickBird | Maxar · DigitalGlobe | 4 | 0.45–0.90 µm | ✓ | ✓ | ✓ | ✓ |
| Total | — | 92 | — | 92 ✓ | 92 ✓ | 92 ✓ | 92 ✓ |
† Landsat 8 OLI Band 8 (panchromatic, 500–680 nm) is used for pansharpening only and is not atmospherically corrected. Total bands: 92 across 10 sensors. All 92 bands support Rural* (=Continental), Maritime, Urban, and Desert aerosol types.
| Type | Status | Notes |
|---|---|---|
| Rural* (=Continental) | ✓ 92 bands | Default — rural & continental areas |
| Maritime | ✓ 92 bands | Suitable for coastal/ocean areas |
| Urban | ✓ 92 bands | Polluted urban environments |
| Desert | ✓ 92 bands | Arid and semi-arid regions |
| Model | Status | Notes |
|---|---|---|
| Tropical | Default | Default for Indonesia & tropical regions |
| Mid-Latitude | Available | Selectable in dropdown (same LUT) |
| Subarctic | Available | Selectable in dropdown (same LUT) |
| US Standard | Available | Selectable in dropdown (same LUT) |
ℹ️ Why the same LUT works for all atmospheric models — The LUTs pre-compute aerosol scattering & absorption coefficients (path radiance a and transmittance b) as functions of aerosol type, solar zenith, H₂O, O₃, AOT, and altitude. These aerosol optical properties are independent of the atmospheric profile (temperature, pressure, humidity vertical distribution). The atmospheric model is used by the 6S engine on-the-fly to compute Rayleigh (molecular) scattering and gaseous absorption (H₂O, O₃, CO₂, etc.), which are separate from the aerosol LUT. Therefore, one LUT per aerosol type suffices for all atmospheric models. Tropical is the default as it is most appropriate for Indonesia.
✅ Rural* (=Continental), Maritime, Urban, and Desert available for all 92 bands (all sensors).
| Component | Technology |
|---|---|
| Backend | Python · Flask · Gunicorn |
| RT Engine | Py6S (6S v1.1 — Vermote et al. 1997) |
| LUT Framework | Py6S NumPy/SciPy |
| Atmospheric Data | NOAA aviationweather.gov (METAR visibility) · AERONET V3 (NASA) |
| Frontend | HTML · CSS · Vanilla JS |
| Deployment | Docker · Compose · Nginx |
6S Natural builds upon several open-source software packages and data sources. We acknowledge and thank their contributors:
6S Emulator (github.com/samsammurphy/6S_emulator) by Sam Murphy — Licensed under Apache 2.0. This is the core engine for building and interpolating Look-Up Tables for atmospheric correction.
Why the existing Continental LUTs do not need to be rebuilt for Tropical.
The LUT generator (LUT_build.py) builds all simulations using
AtmosProfile.UserWaterAndOzone(h2o, o3) — it does not
fix an atmospheric profile (US Standard, Tropical, etc.). Instead, H₂O and O₃ values are
supplied explicitly as grid parameters across their full physical ranges
(H₂O: 0–8.5 g/cm², O₃: 0–0.8 cm·atm). This means the LUT captures aerosol effects
independently of any particular atmospheric model. The same method was used for
all the original Continental LUTs and all new Maritime, Urban, and Desert LUTs,
so they are fully consistent with each other. No rebuild is required
for Tropical or any other atmospheric model. The atmospheric model is applied
on the fly by the 6S engine for Rayleigh scattering and gaseous absorption
calculations, which are separate from the aerosol LUT.
Py6S (github.com/robintw/Py6S) by Robin Wilson — Licensed under LGPL-3.0. Provides the Python wrapper that drives 6S simulations from within the LUT generation pipeline.
The 6S (Second Simulation of a Satellite Signal in the Solar Spectrum) code was developed by Vermote, E.F., Tanré, D., Deuzé, J.L., Herman, M., & Morcrette, J.J. at NASA / University of Maryland. See the original 6S code at modis-sr.ltdri.org.
ACOLITE (github.com/acolite/acolite) by Quinten Vanhellemont, Royal Belgian Institute of Natural Sciences (RBINS) — Licensed under GNU General Public License v3 (GPL-3.0).
6S Natural uses the following ACOLITE modules and data to build the per-band
solar irradiance table (f0_table.json) and to support cross-validation:
f0_get — TOA solar irradiance spectrum Thuillier2003 (from ACOLITE data/Solar/).rsr_convolute_dict — convolution of the irradiance spectrum with each sensor's relative spectral response (RSR).distance_se — Earth–Sun distance from day-of-year.data/RSR/ (Landsat, Sentinel-2/3, WorldView, GeoEye-1, QuickBird).
These modules produce the F₀ values used in the conversion factor
k = π·ds² / (F₀·cos θs). ACOLITE is also run as a separate
validation service (/api/acolite) to cross-check 6S Natural results.
ACOLITE itself is distributed under GPL-3.0 — see the
ACOLITE repository.
The Continental aerosol LUTs (for Landsat 5/7/8 and Sentinel-2) were originally pre-computed by Sam Murphy and distributed via the 6S Emulator project on GitHub. The Maritime, Urban, and Desert aerosol LUTs for all 60 atmospheric-correction bands (panchromatic B8 excluded), as well as expanded coverage for Sentinel-3 sensors, were built by Lalu Muhamad Jaelani at the Department of Geomatics Engineering, Institut Teknologi Sepuluh Nopember (ITS), using the same 6S Emulator framework. The WorldView-2/3, GeoEye-1, and QuickBird LUTs (32 bands across 4 sensors) were also built at ITS, with full support for Maritime, Continental, and Urban aerosol types.
6S Natural itself is distributed for research and educational purposes. The underlying components (6S Emulator, Py6S, 6S RT code) retain their respective open-source licenses (Apache 2.0, LGPL-3.0, and permissive academic license).
6S Natural — Developed for operational atmospheric correction. Powered by Py6S and the 6S radiative transfer code.