Skip to content

Commit

Permalink
Removing Landsat collection 1 support
Browse files Browse the repository at this point in the history
  • Loading branch information
cgmorton committed Feb 13, 2024
1 parent b890336 commit 30d5578
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 321 deletions.
145 changes: 0 additions & 145 deletions openet/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,151 +3,6 @@
from . import landsat


def landsat_c1_toa_cloud_mask(
input_img,
snow_flag=False,
cirrus_flag=False,
cloud_confidence=2,
shadow_confidence=3,
snow_confidence=3,
cirrus_confidence=3,
):
"""Extract cloud mask from the Landsat Collection 1 TOA BQA band
Parameters
----------
input_img : ee.Image
Image from a Landsat Collection 1 TOA collection with a BQA band
(e.g. LANDSAT/LE07/C01/T1_TOA).
snow_flag : bool
If true, mask snow pixels (the default is False).
cirrus_flag : bool
If true, mask cirrus pixels (the default is False).
Note, cirrus bits are only set for Landsat 8 (OLI) images.
cloud_confidence : int
Minimum cloud confidence value (the default is 2).
shadow_confidence : int
Minimum cloud confidence value (the default is 3).
snow_confidence : int
Minimum snow confidence value (the default is 3). Only used if
snow_flag is True.
cirrus_confidence : int
Minimum cirrus confidence value (the default is 3). Only used if
cirrus_flag is True.
Returns
-------
ee.Image
Notes
-----
Output image is structured to be applied directly with updateMask()
i.e. 0 is cloud/masked, 1 is clear/unmasked
Assuming Cloud must be set to check Cloud Confidence
Bits
0: Designated Fill
1: Terrain Occlusion (OLI) / Dropped Pixel (TM, ETM+)
2-3: Radiometric Saturation
4: Cloud
5-6: Cloud Confidence
7-8: Cloud Shadow Confidence
9-10: Snow/Ice Confidence
11-12: Cirrus Confidence (Landsat 8 only)
Confidence values
00: "Not Determined", algorithm did not determine the status of this
condition
01: "No", algorithm has low to no confidence that this condition exists
(0-33 percent confidence)
10: "Maybe", algorithm has medium confidence that this condition exists
(34-66 percent confidence)
11: "Yes", algorithm has high confidence that this condition exists
(67-100 percent confidence)
References
----------
"""
qa_img = input_img.select(['BQA'])
cloud_mask = (
qa_img.rightShift(4).bitwiseAnd(1).neq(0)
.And(qa_img.rightShift(5).bitwiseAnd(3).gte(cloud_confidence))
.Or(qa_img.rightShift(7).bitwiseAnd(3).gte(shadow_confidence))
)
if snow_flag:
cloud_mask = cloud_mask.Or(qa_img.rightShift(9).bitwiseAnd(3).gte(snow_confidence))
if cirrus_flag:
cloud_mask = cloud_mask.Or(qa_img.rightShift(11).bitwiseAnd(3).gte(cirrus_confidence))

# Set cloudy pixels to 0 and clear to 1
return cloud_mask.Not()


def landsat_c1_sr_cloud_mask(
input_img,
cloud_confidence=3,
shadow_flag=True,
snow_flag=False,
):
"""Extract cloud mask from the Landsat Collection 1 SR pixel_qa band
Parameters
----------
input_img : ee.Image
Image from a Landsat Collection 1 SR image collection with a pixel_qa
band (e.g. LANDSAT/LE07/C01/T1_SR).
cloud_confidence : int
Minimum cloud confidence value (the default is 3).
shadow_flag : bool
If true, mask shadow pixels (the default is True).
snow_flag : bool
If true, mask snow pixels (the default is False).
Returns
-------
ee.Image
Notes
-----
Output image is structured to be applied directly with updateMask()
i.e. 0 is cloud/masked, 1 is clear/unmasked
Assuming Cloud must be set to check Cloud Confidence
Bits
0: Fill
1: Clear
2: Water
3: Cloud Shadow
4: Snow
5: Cloud
6-7: Cloud Confidence
Confidence values
00: "None"
01: "Low"
10: "Medium"
11: "High"
References
----------
"""
qa_img = input_img.select(['pixel_qa'])
cloud_mask = qa_img.rightShift(5).bitwiseAnd(1).neq(0)\
.And(qa_img.rightShift(6).bitwiseAnd(3).gte(cloud_confidence))
if shadow_flag:
cloud_mask = cloud_mask.Or(qa_img.rightShift(3).bitwiseAnd(1).neq(0))
if snow_flag:
cloud_mask = cloud_mask.Or(qa_img.rightShift(4).bitwiseAnd(1).neq(0))

# Set cloudy pixels to 0 and clear to 1
return cloud_mask.Not()


def landsat_c2_sr_cloud_mask(
input_img,
cirrus_flag=False,
Expand Down
130 changes: 65 additions & 65 deletions openet/core/landsat.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def c02_qa_pixel_mask(
shadow_flag=True,
snow_flag=False,
):
"""Extract cloud mask from the Landsat Collection 2 Level 2 QA_PIXEL band
"""Landsat Collection 2 QA_PIXEL band cloud mask
Parameters
----------
Expand Down Expand Up @@ -93,13 +93,13 @@ def c02_qa_pixel_mask(


def c02_cloud_score_mask(input_img, cloud_score_pct=100):
"""Extract cloud mask from the Landsat Collection 2 SR QA_PIXEL band
"""Landsat Collection 2 TOA simple cloud score based cloud mask
Parameters
----------
input_img : ee.Image
Image from a Landsat Collection 2 SR image collection
(e.g. LANDSAT/LC08/C02/T1_L2) with the LANDSAT_PRODUCT_ID property.
(e.g. LANDSAT/LC08/C02/T1_L2).
cloud_score_pct : float
Pixels with a simple cloud score values greater than or equal to this
parameter will be masked (the default is 100).
Expand All @@ -110,7 +110,7 @@ def c02_cloud_score_mask(input_img, cloud_score_pct=100):
"""

# Using the system:index requires an extra map calll but is probably more reliable
# Using the system:index requires an extra map call but might be more robust
# since the other properties may have been dropped
toa_coll = c02_matched_toa_coll(input_img, 'system:index', 'system:index')
# toa_coll = landsat_c2_l2_matched_toa_coll(input_img, 'LANDSAT_SCENE_ID', 'LANDSAT_SCENE_ID')
Expand All @@ -126,67 +126,8 @@ def c02_cloud_score_mask(input_img, cloud_score_pct=100):
).rename(['mask'])


def c02_matched_toa_coll(
input_img,
sr_match_property='LANDSAT_SCENE_ID',
toa_match_property='LANDSAT_SCENE_ID',
):
"""
Parameters
----------
input_img : ee.Image
Image with the "sr_match_property" metadata property.
sr_match_property : str
The metedata property name in input_img to use as a match criteria
(the default is "LANDSAT_SCENE_ID").
toa_match_property : str
The metadata property name in the Landsat Collection 2 TOA collections
to use as a match criteria (the default is "LANDSAT_SCENE_ID").
Returns
-------
ee.ImageCollection
Todo
----
Try using LinkCollection instead
"""

# Filter TOA collections to the target to image UTC day
# This filter range could be a lot tighter but keeping it to the day makes it easier to test
# and will hopefully not impact the performance too much
start_date = ee.Date(input_img.get('system:time_start')).update(hour=0, minute=0, second=0)
end_date = start_date.advance(1, 'day')
# # Buffer the image time_start +/- 30 minutes (this could probably be set tighter)
# start_date = ee.Date(input_img.get('system:time_start')).advance(-0.5, 'hour')
# end_date = start_date.advance(1, 'hour')

l5_coll = ee.ImageCollection('LANDSAT/LT05/C02/T1_TOA').filterDate(start_date, end_date)
l7_coll = ee.ImageCollection('LANDSAT/LE07/C02/T1_TOA').filterDate(start_date, end_date)
l8_coll = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA').filterDate(start_date, end_date)
l9_coll = ee.ImageCollection('LANDSAT/LC09/C02/T1_TOA').filterDate(start_date, end_date)

# The default system:index gets modified when the collections are merged below,
# so save the system:index to a new "scene_id" property and use that for matching
if toa_match_property == 'system:index':
# def set_scene_id(img):
# return img.set('scene_id', img.get('system:index'))
l5_coll = l5_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l7_coll = l7_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l8_coll = l8_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l9_coll = l9_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
toa_match_property = 'scene_id'

return (
l9_coll.merge(l8_coll).merge(l7_coll).merge(l5_coll)
.filter(ee.Filter.eq(toa_match_property, ee.String(input_img.get(sr_match_property))))
)


def c02_qa_radsat_mask(input_img):
"""Extract cloud mask from the Landsat Collection 2 QA_RADSAT band
"""Landsat Collection 2 QA_RADSAT band mask for saturated pixels
Parameters
----------
Expand Down Expand Up @@ -221,7 +162,7 @@ def c02_qa_radsat_mask(input_img):


def c02_l2_sr_cloud_qa_mask(input_img, adjacent_flag=True, shadow_flag=True, snow_flag=True):
"""Extract cloud mask from the Landsat Collection 2 Level 2 SR_CLOUD_QA band
"""Landsat Collection 2 Level 2 SR_CLOUD_QA band cloud mask (Landsat 4/5/7 only)
Parameters
----------
Expand Down Expand Up @@ -273,3 +214,62 @@ def sr_cloud_qa_l89(sr_cloud_qa_img):
sr_cloud_qa_l57(input_img.select('SR_CLOUD_QA'))
)
).rename(['mask'])


def c02_matched_toa_coll(
input_img,
sr_match_property='LANDSAT_SCENE_ID',
toa_match_property='LANDSAT_SCENE_ID',
):
"""Return the Landsat Collection 2 TOA collection matching an image property
Parameters
----------
input_img : ee.Image
Image with the "sr_match_property" metadata property.
sr_match_property : str
The metedata property name in input_img to use as a match criteria
(the default is "LANDSAT_SCENE_ID").
toa_match_property : str
The metadata property name in the Landsat Collection 2 TOA collections
to use as a match criteria (the default is "LANDSAT_SCENE_ID").
Returns
-------
ee.ImageCollection
Todo
----
Try using LinkCollection instead
"""

# Filter TOA collections to the target to image UTC day
# This filter range could be a lot tighter but keeping it to the day makes it easier to test
# and will hopefully not impact the performance too much
start_date = ee.Date(input_img.get('system:time_start')).update(hour=0, minute=0, second=0)
end_date = start_date.advance(1, 'day')
# # Buffer the image time_start +/- 30 minutes (this could probably be set tighter)
# start_date = ee.Date(input_img.get('system:time_start')).advance(-0.5, 'hour')
# end_date = start_date.advance(1, 'hour')

l5_coll = ee.ImageCollection('LANDSAT/LT05/C02/T1_TOA').filterDate(start_date, end_date)
l7_coll = ee.ImageCollection('LANDSAT/LE07/C02/T1_TOA').filterDate(start_date, end_date)
l8_coll = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA').filterDate(start_date, end_date)
l9_coll = ee.ImageCollection('LANDSAT/LC09/C02/T1_TOA').filterDate(start_date, end_date)

# The default system:index gets modified when the collections are merged below,
# so save the system:index to a new "scene_id" property and use that for matching
if toa_match_property == 'system:index':
# def set_scene_id(img):
# return img.set('scene_id', img.get('system:index'))
l5_coll = l5_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l7_coll = l7_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l8_coll = l8_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
l9_coll = l9_coll.map(lambda img: img.set('scene_id', img.get('system:index')))
toa_match_property = 'scene_id'

return (
l9_coll.merge(l8_coll).merge(l7_coll).merge(l5_coll)
.filter(ee.Filter.eq(toa_match_property, ee.String(input_img.get(sr_match_property))))
)
Loading

0 comments on commit 30d5578

Please sign in to comment.