LANDSAT
Data Availability
Landsat imagery can be obtained from USGS' LANDSAT archive:
- USGS GloVis
- EarthExplorer
- Bulk Download Orders using the Bulk Download Application
- LandsatLook Viewer
See also the Global datasets wiki page
Pre-Processing Overview
Typically, pre-processing Landsat imagery comprises the following steps:
- import in the database → r.in.gdal
- geometrically & orthometrically correct imagery
- already done for L1T products, read more at USGS' Landsat Information Products webpage
- optionally, automatically cut-off border fringes → i.landsat.trim
- of course one can use the official WRS2 Path/Row vector tiles to manually trim border fringes
- optionally, denoise for obvious/intensive salt & pepper effects, stripes, etc.
- for example by applying Principal Components Analysis as a denoising technique → i.pca
- convert the Digital Numbers (DNs) to Top-of-Atmosphere Radiances/Reflectances (ToARs) → i.landsat.toar
- optionally, correct for atmospheric effects → i.atcorr
- that is, accounting for distorting atmospheric effects and estimating actual reflectances as they would have been measured on the ground
- also described as conversion to Top-of-Canopy Reflectances (ToCRs)
- assessing cloud cover → i.landsat.acca
- optionally, detect and remove clouds shadows as well
- topographically normalise imagery → i.topo.corr
- also known as topographic correction, that is, accounting for illumination differences due to the acquisition's geometry
- radiometrically normalise → one approach via i.histo.match (in grass 7)
- also known as relative radiometric normalisation -- one approach is the histogram matching technique of two or more raster maps
- ToDo/More techniques?
Modules overview
Generic modules applicable to Landsat
- d.rgb → display 3-band data
- r.composite → flatten 3-bands of data into a single image (lossy, maybe used in combination with i.landsat.rgb)
- i.atcorr → correct top of atmosphere to surface reflectance - see also the Atmospheric correction wiki page
- i.topo.corr → used to topographically correct reflectance from imagery files, e.g. obtained with i.landsat.toar, using a sun illumination terrain model
- i.oif → calculate the 3 bands showing the greatest difference (for use as R,G,B bands)
- i.histo.match → histogram matching of two or more raster maps (in grass 7) Note, the module works with integer values and does not accept the "." character as part of the raster map's name!
Landsat specific modules
- i.landsat.rgb → auto-enhance colors
- i.landsat.toar → convert DN to top of atmosphere radiance
- i.landsat.acca → cloud cover assessment
Landsat specific GRASS AddOns
- i.landsat.trim → trims border fringes for each band separately or with the MASK where coverage exists for all bands
- i.landsat.dehaze (addon) → haze removal
Pre-Processing
Importing data
- Open GRASS GIS, select Location Wizard in order to create a new location from georeferenced file
- Use r.in.gdal or the Import file tool to import the GeoTIFF files into GRASS GIS. See also Automated data import below.
- Most Landsat scenes are delivered in north-is-up orientation, hence import is straightforward
- If you get
ERROR: Input map is rotated - cannot import.
, the image must be first externally rotated, applying the rotation info stored in the metadata field of the raster image file. For example, thegdalwarp
software can be used to transform the map to North-up (note, there are severalgdalwarp
parameters to select the resampling algorithm):gdalwarp rotated.tif northup.tif
Automated data import
The following Python script imports Landsat imagery into GRASS' data base. Specifically, the script
- creates an independent Mapset for each Landsat scene
- imports and renames bands of a scene as B<id>, e.g. B10, B20, ..., B80.
- additionaly sets up the timestamp based on MTL metadata file
Note, the (newest) official naming pattern for Landsat scenes -- explained in USGS' Landsat Data Dictionary as the Landsat Scene Identifier -- and all individual bands that compose a scene -- bands have a suffix which is like _B10, _B20, _B30, etc. -- differ from what some Landsat specific modules expect. For example, the modules i.landsat.toar and i.landsat.acca expect the bands to follow a naming pattern such as "scenename.1, .2, .3", etc.
To use the script save it as import_landsat.py
file and make sure it is granted the execution permission.
#!/usr/bin/python
import os
import sys
import glob
import grass.script as grass
def get_timestamp(mapset):
try:
metafile = glob.glob(mapset + '/*MTL.txt')[0]
except IndexError:
return
result = dict()
try:
fd = open(metafile)
for line in fd.readlines():
line = line.rstrip('\n')
if len(line) == 0:
continue
if ('DATE_ACQUIRED' or 'ACQUISITION_DATE') in line:
result['date'] = line.strip().split('=')[1].strip()
finally:
fd.close()
return result
def import_tifs(mapset):
meta = get_timestamp(mapset)
for file in os.listdir(mapset):
if os.path.splitext(file)[-1] != '.TIF':
continue
ffile = os.path.join(mapset, file)
name = os.path.splitext(file)[0].split('_')[-1]
band = int(name[-1])
grass.message('Importing %s -> %s@%s...' % (file, name, mapset))
grass.run_command('g.mapset',
flags = 'c',
mapset = mapset,
quiet = True,
stderr = open(os.devnull, 'w'))
grass.run_command('r.in.gdal',
input = ffile,
output = name,
quiet = True,
overwrite = True,
title = 'band %d' % band)
if meta:
year, month, day = meta['date'].split('-')
if month == '01':
month = 'jan'
elif month == '02':
month = 'feb'
elif month == '03':
month = 'mar'
elif month == '04':
month = 'apr'
elif month == '05':
month = 'may'
elif month == '06':
month = 'jun'
elif month == '07':
month = 'jul'
elif month == '08':
month = 'aug'
elif month == '09':
month = 'sep'
elif month == '10':
month = 'oct'
elif month == '11':
month = 'nov'
elif month == '12':
month = 'dec'
grass.run_command('r.timestamp',
map = name,
date = ' '.join((day, month, year)))
def main():
if len(sys.argv) == 1:
for directory in filter(os.path.isdir, os.listdir(os.getcwd())):
import_tifs(directory)
else:
import_tifs(sys.argv[1])
if __name__ == "__main__":
main()
Example of usage
After having collected the Landsat scenes of interest in one directory, the script can be used as follows:
./import_landsat.py
→ the script will walk through a pool directory that contains unique Landsat scene directories (e.g. three directories named after the official naming pattern: LT51800342011158MOR00 LT51810352009079MTI00 LT51820352009326MTI00) and import all bands of each individual scene in their own Mapset./import_landsat.py LM41890261983200FFF03
→ the scrip will import only bands of the specified Landsat scene directory
Hint: Minimal disk space copies
Here's a little trick using r.reclass to rename maps (for example, from L71074092_09220040924_B10 to L71074092_09220040924_B1) without touching the data or wasting disk space:
BASE=L71074092_09220040924
for BAND in 10 20 30 40 50 61 70 80; do
BAND1st=`echo $BAND | sed -e 's/0$//'`
r.reclass in="${BASE}_B$BAND" out=$BASE.$BAND1st << EOF
* = *
EOF
done
Natural color composites
Solution A) use i.landsat.rgb
Solution B) equalize colors on each R,G,B band with
r.colors -e map=band1 color=grey
Composite: then run r.composite
Reset color tables
BASE=L71074092_09220040924
for map in `g.mlist pat="$BASE.[0-8]*"` ; do
r.colors $map color=grey255
done
Create a MASK to only show data where coverage exists for all bands
BASE=L71074092_09220040924
g.region rast=$BASE.1
r.series in=`g.mlist pat="$BASE.[0-8]*" sep=,` -n out=$BASE.thresh method=threshold thresh=1
r.mapcalc "$BASE.mask = if(isnull($BASE.thresh))"
g.remove $BASE.thresh
Calculate Top-of-Atmosphere Reflectance and band-6 Temperature
Calculate Top-of-Atmosphere reflectance and band-6 temperature using the i.landsat.toar module. For details, refer to USGS' Landsat Filename and Metadata Changes dedicated page.
i.landsat.toar input_prefix=$BASE output_prefix=${BASE}_toar metfile=${BASE}_MTL.txt
Note, the resulting temperature map is in Kelvin:
# convert to degree Celsius
r.mapcalc "$BASE.temp_celsius = ${BASE}_toar.6 - 273.15"
r.info -r $BASE.temp_celsius
Haze removal
Simple haze removal can be done with i.landsat.dehaze. i.landsat.dehaze applies a bandwise haze correction using tasscap4 (haze) and linear regression.
Atmospheric correction
Cloud identification
Identify clouds in the image with i.landsat.acca:
i.landsat.acca -f input_prefix=226_62_toar. output=226_62.acca_cloudmask
Mask out the clouds:
r.mapcalc "MASK = if(isnull($BASE.acca_cloudmask))"
Download sample data
Preprocessed Landsat-7 data for North Carolina
The North Carolina 2008 sample dataset comes with 3 different Landsat scenes:
- (Wake County -- path: 16 row: 35 for various dates)
The above import efforts are not needed since the data are already in a GRASS location.
Landsat-5: Oct 14, 1987
- Glovis download
- LT50160351987287XXX08
- http://edcsns17.cr.usgs.gov/cgi-bin/EarthExplorer/run-phtml/results/download.phtml?node=GV&ordered=LT50160351987287XXX08&dataset_name=LANDSAT_TM
Provided metadata:
IMAGE_ID=P016R35_5T871014 PATH=16 ROW=35 DATE=10/14/87 PLATFORM=LANDSAT5
Landsat-7: Mar 31, 2000
- Glovis download
- LE70160352000091EDC00
- http://edcsns17.cr.usgs.gov/cgi-bin/EarthExplorer/run-phtml/results/download.phtml?node=GV&ordered=LE70160352000091EDC00&dataset_name=LANDSAT_ETM
Values in the metadata below indicate that the version provided with the NC 2008 dataset's production date was after July 1, 2000, and that the channel gains were HHHLHLHHL.
Convert DNs to radiance/temperatures:
GRASS> i.landsat.toar -v band=lsat7_2000 sensor=7 date=2000-03-31 \
product_date=2000-07-02 solar_elevation=51.5246529 gain=HHHLHLHHL
Provided metadata:
SPACECRAFT_ID=Landsat7 SENSOR_ID=ETM+ ACQUISITION_DATE=2000-03-31 WRS_PATH=16 CPF_FILE_NAME=L7CPF20000101_20000331_12 SUN_AZIMUTH=139.6033279 SUN_ELEVATION=51.5246529 LMAX_BAND1=191.600 LMIN_BAND1=-6.200 LMAX_BAND2=196.500 LMIN_BAND2=-6.400 LMAX_BAND3=152.900 LMIN_BAND3=-5.000 LMAX_BAND4=241.100 LMIN_BAND4=-5.100 LMAX_BAND5=31.060 LMIN_BAND5=-1.000 LMAX_BAND61=17.040 LMIN_BAND61=0.000 LMAX_BAND62=12.650 LMIN_BAND62=3.200 LMAX_BAND7=10.800 LMIN_BAND7=-0.350 LMAX_BAND8=243.100 LMIN_BAND8=-4.700 QCALMAX_BAND1=255.0 QCALMIN_BAND1=1.0 QCALMAX_BAND2=255.0 QCALMIN_BAND2=1.0 QCALMAX_BAND3=255.0 QCALMIN_BAND3=1.0 QCALMAX_BAND4=255.0 QCALMIN_BAND4=1.0 QCALMAX_BAND5=255.0 QCALMIN_BAND5=1.0 QCALMAX_BAND61=255.0 QCALMIN_BAND61=1.0 QCALMAX_BAND62=255.0 QCALMIN_BAND62=1.0 QCALMAX_BAND7=255.0 QCALMIN_BAND7=1.0 QCALMAX_BAND8=255.0 QCALMIN_BAND8=1.0
Landsat-7: May 24, 2002
- Glovis download
- LE70160352002144EDC00
- http://edcsns17.cr.usgs.gov/cgi-bin/EarthExplorer/run-phtml/results/download.phtml?node=GV&ordered=LE70160352002144EDC00&dataset_name=LANDSAT_ETM
Provided metadata: (`p016r035_7x20020524.met`)
SPACECRAFT_ID=Landsat7 SENSOR_ID=ETM+ ACQUISITION_DATE=2002-05-24 WRS_PATH=016 WRS_ROW=035 SUN_AZIMUTH=120.8810347 SUN_ELEVATION=64.7730999 QA_PERCENT_MISSING_DATA=0 CLOUD_COVER=0 CPF_FILE_NAME=L7CPF20020401_20020630_03 LMAX_BAND1=191.600 LMIN_BAND1=-6.200 LMAX_BAND2=196.500 LMIN_BAND2=-6.400 LMAX_BAND3=152.900 LMIN_BAND3=-5.000 LMAX_BAND4=241.100 LMIN_BAND4=-5.100 LMAX_BAND5=31.060 LMIN_BAND5=-1.000 LMAX_BAND61=17.040 LMIN_BAND61=0.000 LMAX_BAND62=12.650 LMIN_BAND62=3.200 LMAX_BAND7=10.800 LMIN_BAND7=-0.350 LMAX_BAND8=243.100 LMIN_BAND8=-4.700 QCALMAX_BAND1=255.0 QCALMIN_BAND1=1.0 QCALMAX_BAND2=255.0 QCALMIN_BAND2=1.0 QCALMAX_BAND3=255.0 QCALMIN_BAND3=1.0 QCALMAX_BAND4=255.0 QCALMIN_BAND4=1.0 QCALMAX_BAND5=255.0 QCALMIN_BAND5=1.0 QCALMAX_BAND61=255.0 QCALMIN_BAND61=1.0 QCALMAX_BAND62=255.0 QCALMIN_BAND62=1.0 QCALMAX_BAND7=255.0 QCALMIN_BAND7=1.0 QCALMAX_BAND8=255.0 QCALMIN_BAND8=1.0