geowatch.utils.util_bands module¶
Collected information about satellite bands from https://github.com/stac-extensions/eo/
This should be mostly independent of the data source used. (eg Google Cloud vs element84 AWS, Collection-1 vs Collection-2) No guarantees that every value will be indentical, but close enough for heuristics.
Each constant is a list of ‘eo:bands’ Band Object dicts. They are sorted in the same order in which they appear if the bands come stacked in a single image, or in lexicographic order if the bands come in separate images.
Coverage of the catalogs is inconsistent. Where necessary, info has been filled in by hand.
Notes
Sentinal 2 Band Table¶
Band Resolution Central Wavelength Description B1 60 m 443 nm Ultra blue (Coastal and Aerosol) B2 10 m 490 nm Blue B3 10 m 560 nm Green B4 10 m 665 nm Red B5 20 m 705 nm Visible and Near Infrared (VNIR) B6 20 m 740 nm Visible and Near Infrared (VNIR) B7 20 m 783 nm Visible and Near Infrared (VNIR) B8 10 m 842 nm Visible and Near Infrared (VNIR) B8a 20 m 865 nm Visible and Near Infrared (VNIR) B9 60 m 940 nm Short Wave Infrared (SWIR) B10 60 m 1375 nm Short Wave Infrared (SWIR) B11 20 m 1610 nm Short Wave Infrared (SWIR) B12 20 m 2190 nm Short Wave Infrared (SWIR)
Landsat 8 Band Table¶
Band Resolution Central Wavelength Description 1 30 m 430 nm Coastal aerosol 2 30 m 450 nm Blue 3 30 m 530 nm Green 4 30 m 640 nm Red 5 30 m 850 nm Near Infrared (NIR) 6 30 m 1570 nm SWIR 1 7 30 m 2110 nm SWIR 2 8 15 m 500 nm Panchromatic 9 30 m 1360 nm Cirrus 10 100 m 10600 nm Thermal Infrared (TIRS) 1 11 100 m 11500 nm Thermal Infrared (TIRS) 2
Worldview 3 MUL Band Table¶
Band Resolution Central Wavelength Description 1 1.38 m 400 nm Coastal aerosol 2 1.38 m 450 nm Blue 3 1.38 m 510 nm Green 4 1.38 m 585 nm Yellow 5 1.38 m 630 nm Red 6 1.38 m 705 nm Red edge 7 1.38 m 770 nm Near-IR1 8 1.38 m 860 nm Near-IR2
Worldview 3 PAN Band Table¶
1 0.34 m 450-800 nm Panchromatic
References
https://gis.stackexchange.com/questions/290796/how-to-edit-the-metadata-for-individual-bands-of-a-multiband-raster-preferably https://gisgeography.com/sentinel-2-bands-combinations/ https://earth.esa.int/eogateway/missions/worldview-3 https://www.usgs.gov/faqs/what-are-band-designations-landsat-satellites?qt-news_science_products=0#qt-news_science_products .. [MODIS-SPEC] https://modis.gsfc.nasa.gov/about/specifications.php .. [SentinelHub] https://apps.sentinel-hub.com/eo-browser/?zoom=15&lat=42.87425&lng=-73.83164&themeId=DEFAULT-THEME&visualizationUrl=https%3A%2F%2Fservices.sentinel-hub.com%2Fogc%2Fwms%2Fbd86bcc0-f318-402b-a145-015f85b9427e&datasetId=S2L2A&fromTime=2022-02-16T00%3A00%3A00.000Z&toTime=2022-02-16T23%3A59%3A59.999Z&layerId=4-FALSE-COLOR-URBAN
- geowatch.utils.util_bands.SENTINEL2 = [{'center_wavelength': 0.4439, 'common_name': 'coastal', 'full_width_half_max': 0.027, 'gsd': 60, 'name': 'B01'}, {'center_wavelength': 0.4966, 'common_name': 'blue', 'full_width_half_max': 0.098, 'gsd': 10, 'name': 'B02'}, {'center_wavelength': 0.56, 'common_name': 'green', 'full_width_half_max': 0.045, 'gsd': 10, 'name': 'B03'}, {'center_wavelength': 0.6645, 'common_name': 'red', 'full_width_half_max': 0.038, 'gsd': 10, 'name': 'B04'}, {'center_wavelength': 0.7039, 'full_width_half_max': 0.019, 'gsd': 20, 'name': 'B05'}, {'center_wavelength': 0.7402, 'full_width_half_max': 0.018, 'gsd': 20, 'name': 'B06'}, {'center_wavelength': 0.7825, 'full_width_half_max': 0.028, 'gsd': 20, 'name': 'B07'}, {'center_wavelength': 0.8351, 'common_name': 'nir', 'full_width_half_max': 0.145, 'gsd': 10, 'name': 'B08'}, {'center_wavelength': 0.8648, 'full_width_half_max': 0.033, 'gsd': 20, 'name': 'B8A'}, {'center_wavelength': 0.945, 'full_width_half_max': 0.026, 'gsd': 60, 'name': 'B09'}, {'center_wavelength': 1.3735, 'common_name': 'cirrus', 'full_width_half_max': 0.075, 'gsd': 60, 'name': 'B10'}, {'center_wavelength': 1.6137, 'common_name': 'swir16', 'full_width_half_max': 0.143, 'gsd': 20, 'name': 'B11'}, {'center_wavelength': 2.22024, 'common_name': 'swir22', 'full_width_half_max': 0.242, 'gsd': 20, 'name': 'B12'}]¶
This band info is taken from the sentinelhub AWS catalog. It will need to be updated to match RGD’s when that is STAC-compliant.
References
https://www.element84.com/earth-search/ https://docs.sentinel-hub.com/api/latest/data/landsat-8/ https://landsat.gsfc.nasa.gov/satellites/landsat-8/ https://planetarycomputer.microsoft.com/dataset/landsat-c2-l2
Example
>>> from pystac_client import Client >>> # for Collection 1 >>> cat = Client.open('https://earth-search.aws.element84.com/v0') >>> search = cat.search(bbox=[-110, 39.5, -105, 40.5], max_items=1, collections=['landsat-8-l1-c1']) >>> # for Collection 2 >>> # cat = Client.open('https://earth-search.aws.element84.com/v1') >>> # search = cat.search(bbox=[-110, 39.5, -105, 40.5], max_items=1, collections=['landsat-ot-l1']) >>> i = list(search.items())[0] >>> # one image for all bands >>> bands = [v.to_dict()['eo:bands'][0] for k,v in i.assets.items() if k.startswith('B') and (k != 'BQA')] >>> >>> from geowatch.utils.util_bands import * >>> assert dicts_contain(LANDSAT8, bands)
- geowatch.utils.util_bands.LANDSAT8 = [{'alias': ['aerosol'], 'center_wavelength': 0.48, 'common_name': 'coastal', 'full_width_half_max': 0.02, 'gsd': 30, 'name': 'B1'}, {'center_wavelength': 0.44, 'common_name': 'blue', 'full_width_half_max': 0.06, 'gsd': 30, 'name': 'B2'}, {'center_wavelength': 0.56, 'common_name': 'green', 'full_width_half_max': 0.06, 'gsd': 30, 'name': 'B3'}, {'center_wavelength': 0.65, 'common_name': 'red', 'full_width_half_max': 0.04, 'gsd': 30, 'name': 'B4'}, {'alias': ['nir08'], 'center_wavelength': 0.86, 'common_name': 'nir', 'full_width_half_max': 0.03, 'gsd': 30, 'name': 'B5'}, {'center_wavelength': 1.6, 'common_name': 'swir16', 'full_width_half_max': 0.08, 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 2.2, 'common_name': 'swir22', 'full_width_half_max': 0.2, 'gsd': 30, 'name': 'B7'}, {'center_wavelength': 0.59, 'common_name': 'pan', 'full_width_half_max': 0.18, 'gsd': 15, 'name': 'B8'}, {'center_wavelength': 1.37, 'common_name': 'cirrus', 'full_width_half_max': 0.02, 'gsd': 30, 'name': 'B9'}, {'alias': ['tir1'], 'center_wavelength': 10.9, 'common_name': 'lwir11', 'full_width_half_max': 0.8, 'gsd': 100, 'name': 'B10', 'notes': 'thermal-ir'}, {'alias': ['tir2'], 'center_wavelength': 12, 'common_name': 'lwir12', 'full_width_half_max': 1, 'gsd': 100, 'name': 'B11', 'notes': 'thermal-ir'}]¶
This band info is taken from the USGS Landsat catalog.
This is for Collection-2 Level-1; may be slightly different from Collection-1 Level-1 (RGD’s current source)
Example
>>> # not compatible with pystac_client for some reason >>> import requests >>> item = requests.get(('https://landsatlook.usgs.gov/sat-api/collections' >>> '/landsat-c2l1/items/LE07_L1TP_026043_20210518_20210518_02_RT')).json() >>> assets = item['assets'] >>> keys = sorted(k for k in assets.keys() if 'B' in k) >>> bands = [assets[k]['eo:bands'][0] for k in keys] >>> >>> from geowatch.utils.util_bands import * >>> assert dicts_contain(LANDSAT7, bands)
- geowatch.utils.util_bands.LANDSAT7 = [{'center_wavelength': 0.49, 'common_name': 'blue', 'gsd': 30, 'name': 'B1'}, {'center_wavelength': 0.56, 'common_name': 'green', 'gsd': 30, 'name': 'B2'}, {'center_wavelength': 0.66, 'common_name': 'red', 'gsd': 30, 'name': 'B3'}, {'center_wavelength': 0.84, 'common_name': 'nir08', 'gsd': 30, 'name': 'B4'}, {'center_wavelength': 1.65, 'common_name': 'swir16', 'gsd': 30, 'name': 'B5'}, {'center_wavelength': 11.45, 'common_name': 'tir', 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 11.45, 'common_name': 'tir', 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 2.22, 'common_name': 'swir22', 'gsd': 30, 'name': 'B7'}, {'center_wavelength': 0.71, 'common_name': 'pan', 'gsd': 30, 'name': 'B8'}]¶
This band info is taken from the IARPA T&E STAC catalog.
Example
>>> # xdoctest: +SKIP >>> # requires the api_key for this catalog >>> from pystac_client import Client >>> catalog = Client.open('https://api.smart-stac.com/', headers={"x-api-key": api_key}) >>> search = catalog.search(collections=['worldview-nitf'], bbox=[128.662489, 37.659517, 128.676673, 37.664560]) >>> items = list(search.items()) >>> props = [i.to_dict()['properties'] for i in items] >>> >>> wv01 = [p for p in props if p['mission'] == 'WV01'] >>> assert np.unique([p['instruments'] for p in wv01]) == ['panchromatic'] >>> wv01_pan = [p for p in wv01 if p['instruments'] == ['panchromatic']][0]['eo:bands'] >>> >>> wv02 = [p for p in props if p['mission'] == 'WV02'] >>> assert np.all(np.unique([p['instruments'] for p in wv02]) == ['panchromatic', 'vis-multi']) >>> assert np.all(np.unique([len(p['eo:bands']) for p in wv02]) == [1, 4, 8]) >>> wv02_pan = [p for p in wv02 if p['instruments'] == ['panchromatic']][0]['eo:bands'] >>> wv02_ms = [p['eo:bands'] for p in wv02 if p['instruments'] == ['vis-multi']] >>> wv02_ms4 = [p for p in wv02_ms if len(p) == 4][0] >>> wv02_ms8 = [p for p in wv02_ms if len(p) == 8][0] >>> >>> wv03 = [p for p in props if p['mission'] == 'WV03'] >>> assert np.all(np.unique([p['instruments'] for p in wv03]) == ['panchromatic', 'vis-multi']) >>> assert np.all(np.unique([len(p['eo:bands']) for p in wv03]) == [1, 8]) >>> wv03_pan = [p for p in wv03 if p['instruments'] == ['panchromatic']][0]['eo:bands'] >>> wv03_ms = [p['eo:bands'] for p in wv03 if p['instruments'] == ['vis-multi']] >>> wv03_ms8 = [p for p in wv03_ms if len(p) == 8][0] >>> >>> # not sure if this must be true, but it is >>> assert wv02_pan == wv03_pan >>> assert wv02_ms8 == wv03_ms8 >>> >>> from geowatch.utils.util_bands import * >>> assert dicts_contain(WORLDVIEW1_PAN, wv01_pan) >>> assert dicts_contain(WORLDVIEW2_PAN, wv02_pan) >>> assert dicts_contain(WORLDVIEW2_MS4, wv02_ms4) >>> assert dicts_contain(WORLDVIEW2_MS8, wv02_ms8) >>> assert dicts_contain(WORLDVIEW3_PAN, wv03_pan) >>> assert dicts_contain(WORLDVIEW3_MS8, wv03_ms8)
- geowatch.utils.util_bands.PLANETSCOPE_8BAND = [{'common_name': 'coastal', 'name': 'B1'}, {'common_name': 'blue', 'name': 'B2'}, {'common_name': 'green1', 'name': 'B3'}, {'common_name': 'green', 'name': 'B4'}, {'common_name': 'yellow', 'name': 'B5'}, {'common_name': 'red', 'name': 'B6'}, {'common_name': 'red-edge', 'name': 'B7'}, {'common_name': 'nir', 'name': 'B8'}]¶
TODO
fix wv doctest
- geowatch.utils.util_bands.ALL_BANDS = [{'center_wavelength': 0.4439, 'common_name': 'coastal', 'full_width_half_max': 0.027, 'gsd': 60, 'name': 'B01'}, {'center_wavelength': 0.4966, 'common_name': 'blue', 'full_width_half_max': 0.098, 'gsd': 10, 'name': 'B02'}, {'center_wavelength': 0.56, 'common_name': 'green', 'full_width_half_max': 0.045, 'gsd': 10, 'name': 'B03'}, {'center_wavelength': 0.6645, 'common_name': 'red', 'full_width_half_max': 0.038, 'gsd': 10, 'name': 'B04'}, {'center_wavelength': 0.7039, 'full_width_half_max': 0.019, 'gsd': 20, 'name': 'B05'}, {'center_wavelength': 0.7402, 'full_width_half_max': 0.018, 'gsd': 20, 'name': 'B06'}, {'center_wavelength': 0.7825, 'full_width_half_max': 0.028, 'gsd': 20, 'name': 'B07'}, {'center_wavelength': 0.8351, 'common_name': 'nir', 'full_width_half_max': 0.145, 'gsd': 10, 'name': 'B08'}, {'center_wavelength': 0.8648, 'full_width_half_max': 0.033, 'gsd': 20, 'name': 'B8A'}, {'center_wavelength': 0.945, 'full_width_half_max': 0.026, 'gsd': 60, 'name': 'B09'}, {'center_wavelength': 1.3735, 'common_name': 'cirrus', 'full_width_half_max': 0.075, 'gsd': 60, 'name': 'B10'}, {'center_wavelength': 1.6137, 'common_name': 'swir16', 'full_width_half_max': 0.143, 'gsd': 20, 'name': 'B11'}, {'center_wavelength': 2.22024, 'common_name': 'swir22', 'full_width_half_max': 0.242, 'gsd': 20, 'name': 'B12'}, {'alias': ['aerosol'], 'center_wavelength': 0.48, 'common_name': 'coastal', 'full_width_half_max': 0.02, 'gsd': 30, 'name': 'B1'}, {'center_wavelength': 0.44, 'common_name': 'blue', 'full_width_half_max': 0.06, 'gsd': 30, 'name': 'B2'}, {'center_wavelength': 0.56, 'common_name': 'green', 'full_width_half_max': 0.06, 'gsd': 30, 'name': 'B3'}, {'center_wavelength': 0.65, 'common_name': 'red', 'full_width_half_max': 0.04, 'gsd': 30, 'name': 'B4'}, {'alias': ['nir08'], 'center_wavelength': 0.86, 'common_name': 'nir', 'full_width_half_max': 0.03, 'gsd': 30, 'name': 'B5'}, {'center_wavelength': 1.6, 'common_name': 'swir16', 'full_width_half_max': 0.08, 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 2.2, 'common_name': 'swir22', 'full_width_half_max': 0.2, 'gsd': 30, 'name': 'B7'}, {'center_wavelength': 0.59, 'common_name': 'pan', 'full_width_half_max': 0.18, 'gsd': 15, 'name': 'B8'}, {'center_wavelength': 1.37, 'common_name': 'cirrus', 'full_width_half_max': 0.02, 'gsd': 30, 'name': 'B9'}, {'alias': ['tir1'], 'center_wavelength': 10.9, 'common_name': 'lwir11', 'full_width_half_max': 0.8, 'gsd': 100, 'name': 'B10', 'notes': 'thermal-ir'}, {'alias': ['tir2'], 'center_wavelength': 12, 'common_name': 'lwir12', 'full_width_half_max': 1, 'gsd': 100, 'name': 'B11', 'notes': 'thermal-ir'}, {'center_wavelength': 0.49, 'common_name': 'blue', 'gsd': 30, 'name': 'B1'}, {'center_wavelength': 0.56, 'common_name': 'green', 'gsd': 30, 'name': 'B2'}, {'center_wavelength': 0.66, 'common_name': 'red', 'gsd': 30, 'name': 'B3'}, {'center_wavelength': 0.84, 'common_name': 'nir08', 'gsd': 30, 'name': 'B4'}, {'center_wavelength': 1.65, 'common_name': 'swir16', 'gsd': 30, 'name': 'B5'}, {'center_wavelength': 11.45, 'common_name': 'tir', 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 11.45, 'common_name': 'tir', 'gsd': 30, 'name': 'B6'}, {'center_wavelength': 2.22, 'common_name': 'swir22', 'gsd': 30, 'name': 'B7'}, {'center_wavelength': 0.71, 'common_name': 'pan', 'gsd': 30, 'name': 'B8'}, {'center_wavelength': 0.65, 'common_name': 'panchromatic', 'name': 'PAN'}, {'center_wavelength': 0.625, 'common_name': 'panchromatic', 'name': 'PAN'}, {'center_wavelength': 0.48, 'common_name': 'blue', 'name': 'B2'}, {'center_wavelength': 0.545, 'common_name': 'green', 'name': 'B3'}, {'center_wavelength': 0.66, 'common_name': 'red', 'name': 'B5'}, {'center_wavelength': 0.833, 'common_name': 'near-ir1', 'name': 'B7'}, {'center_wavelength': 0.425, 'common_name': 'coastal', 'name': 'B1'}, {'center_wavelength': 0.48, 'common_name': 'blue', 'name': 'B2'}, {'center_wavelength': 0.545, 'common_name': 'green', 'name': 'B3'}, {'center_wavelength': 0.605, 'common_name': 'yellow', 'name': 'B4'}, {'center_wavelength': 0.66, 'common_name': 'red', 'name': 'B5'}, {'center_wavelength': 0.725, 'common_name': 'red-edge', 'name': 'B6'}, {'center_wavelength': 0.833, 'common_name': 'near-ir1', 'name': 'B7'}, {'center_wavelength': 0.95, 'common_name': 'near-ir2', 'name': 'B8'}, {'center_wavelength': 0.625, 'common_name': 'panchromatic', 'name': 'PAN'}, {'center_wavelength': 0.425, 'common_name': 'coastal', 'name': 'B1'}, {'center_wavelength': 0.48, 'common_name': 'blue', 'name': 'B2'}, {'center_wavelength': 0.545, 'common_name': 'green', 'name': 'B3'}, {'center_wavelength': 0.605, 'common_name': 'yellow', 'name': 'B4'}, {'center_wavelength': 0.66, 'common_name': 'red', 'name': 'B5'}, {'center_wavelength': 0.725, 'common_name': 'red-edge', 'name': 'B6'}, {'center_wavelength': 0.833, 'common_name': 'near-ir1', 'name': 'B7'}, {'center_wavelength': 0.95, 'common_name': 'near-ir2', 'name': 'B8'}]¶
WIP Collect synonyms for allowed common_names values (not enforced by STAC) TODO do we even need to conform to this? Should we only collect “true” synonyms like {‘pan’: ‘panchromatic’} ?
Example
>>> from geowatch.utils.util_bands import * >>> import itertools >>> names = set(b.get('common_name', '') for b in ALL_BANDS) >>> accounted_names = set(EO_COMMONNAMES.keys()).union( >>> set(itertools.chain.from_iterable(EO_COMMONNAMES.values()))) >>> todo = names.difference(accounted_names) >>> # not sure what to do with these >>> print(todo) {'', 'tir'}
References
https://github.com/stac-extensions/eo/blob/main/json-schema/schema.json#L151
- geowatch.utils.util_bands.EO_COMMONNAMES = {'blue': [], 'cirrus': [], 'coastal': [], 'green': [], 'lwir': [], 'lwir11': [], 'lwir12': [], 'nir': ['near-ir1', 'near-ir2'], 'nir08': [], 'nir09': [], 'pan': ['panchromatic'], 'red': [], 'rededge': ['red-edge'], 'swir16': [], 'swir22': [], 'yellow': []}¶
WIP Bands that are used to observe targets on the ground This is just a rough first pass
Example
>>> from geowatch.utils.util_bands import * >>> assert GROUND.issubset(set(EO_COMMONNAMES.keys()))
- geowatch.utils.util_bands.GROUND = {'blue', 'coastal', 'green', 'nir', 'nir08', 'nir09', 'pan', 'red', 'rededge', 'yellow'}¶
These band fields can be accessed as python objects as well using pystac
Example
>>> from pystac.extensions.eo import Band >>> from geowatch.utils.util_bands import * >>> for band in ALL_BANDS: >>> band.pop('gsd', None) # pystac doesn't support this yet >>> b = Band.create(**band)
- geowatch.utils.util_bands.specialized_index_bands(bands=None, coco_img=None, symbolic=False)[source]¶
Ported from code from by (Yongquan Zhao on 26 April 2017)
References
https://mail.google.com/mail/u/1/#chat/space/AAAAE5jpxTc
# Example: # >>> # xdoctest: +REQUIRES(module:sympy) # >>> from geowatch.utils.util_bands import * # NOQA # >>> symbolic = True # >>> indexes = specialized_index_bands(coco_img=None, symbolic=symbolic) # >>> import sympy as sym # >>> for key, index in indexes.items(): # >>> print(‘===============’) # >>> print(‘key = {!r}’.format(key)) # >>> print(’nOrig {}’.format(key)) # >>> print(index) # >>> print(’nSimplified {}’.format(key)) # >>> print(sym.simplify(index))