[Git][debian-gis-team/asf-search][upstream] New upstream version 13.1.0
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sat Sep 12 16:42:11 BST 2026
Antonio Valentino pushed to branch upstream at Debian GIS Project / asf-search
Commits:
8667e7a5 by Antonio Valentino at 2026-09-12T15:35:01+00:00
New upstream version 13.1.0
- - - - -
6 changed files:
- CHANGELOG.md
- asf_search/ASFProduct.py
- asf_search/CMR/datasets.py
- asf_search/Products/ALOSProduct.py
- asf_search/Products/SMAPProduct.py
- asf_search/constants/PRODUCT_TYPE.py
Changes:
=====================================
CHANGELOG.md
=====================================
@@ -27,6 +27,12 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-->
---
+## [v13.1.0](https://github.com/asfadmin/Discovery-asf_search/compare/v13.0.1...v13.1.0)
+
+### Added
+
+- Added support for upcoming ALOS-1 and SMAP metadata changes, added `L1A_RADAR_RO`, `L1C_S0_HIRES`, `L1B_S0_LORES`, `L1A_RADAR` product type constants.
+
## [v13.0.1](https://github.com/asfadmin/Discovery-asf_search/compare/v13.0.0...v13.0.1)
### Fixed
=====================================
asf_search/ASFProduct.py
=====================================
@@ -1,5 +1,6 @@
+from collections import namedtuple
import os
-from typing import Any, Dict, Tuple, Type, List, final
+from typing import Any, Dict, Tuple, Type, List, final, Literal
import warnings
from shapely.geometry import shape, Point, Polygon, mapping
import json
@@ -14,6 +15,8 @@ from asf_search.download.file_download_type import FileDownloadType
from asf_search.CMR.translate import try_parse_date
from asf_search.CMR.translate import try_parse_float, try_parse_int, try_round_float
+FileSizeInfo = namedtuple("FileSizeInfo", ["file_sizes", "md5_sums"])
+
class ASFProduct:
"""
@@ -47,71 +50,71 @@ class ASFProduct:
_base_properties = {
# min viable product
- 'centerLat': {
- 'path': ['AdditionalAttributes', ('Name', 'CENTER_LAT'), 'Values', 0],
- 'cast': try_parse_float,
+ "centerLat": {
+ "path": ["AdditionalAttributes", ("Name", "CENTER_LAT"), "Values", 0],
+ "cast": try_parse_float,
},
- 'centerLon': {
- 'path': ['AdditionalAttributes', ('Name', 'CENTER_LON'), 'Values', 0],
- 'cast': try_parse_float,
+ "centerLon": {
+ "path": ["AdditionalAttributes", ("Name", "CENTER_LON"), "Values", 0],
+ "cast": try_parse_float,
},
- 'stopTime': {
- 'path': ['TemporalExtent', 'RangeDateTime', 'EndingDateTime'],
- 'cast': try_parse_date,
+ "stopTime": {
+ "path": ["TemporalExtent", "RangeDateTime", "EndingDateTime"],
+ "cast": try_parse_date,
}, # primary search results sort key
- 'fileID': {'path': ['GranuleUR']}, # secondary search results sort key
- 'flightDirection': {
- 'path': [
- 'AdditionalAttributes',
- ('Name', 'ASCENDING_DESCENDING'),
- 'Values',
+ "fileID": {"path": ["GranuleUR"]}, # secondary search results sort key
+ "flightDirection": {
+ "path": [
+ "AdditionalAttributes",
+ ("Name", "ASCENDING_DESCENDING"),
+ "Values",
0,
]
},
- 'pathNumber': {
- 'path': ['AdditionalAttributes', ('Name', 'PATH_NUMBER'), 'Values', 0],
- 'cast': try_parse_int,
+ "pathNumber": {
+ "path": ["AdditionalAttributes", ("Name", "PATH_NUMBER"), "Values", 0],
+ "cast": try_parse_int,
},
- 'processingLevel': {
- 'path': ['AdditionalAttributes', ('Name', 'PROCESSING_TYPE'), 'Values', 0]
+ "processingLevel": {
+ "path": ["AdditionalAttributes", ("Name", "PROCESSING_TYPE"), "Values", 0]
},
# commonly used
- 'url': {'path': ['RelatedUrls', ('Type', 'GET DATA'), 'URL']},
- 'startTime': {
- 'path': ['TemporalExtent', 'RangeDateTime', 'BeginningDateTime'],
- 'cast': try_parse_date,
+ "url": {"path": ["RelatedUrls", ("Type", "GET DATA"), "URL"]},
+ "startTime": {
+ "path": ["TemporalExtent", "RangeDateTime", "BeginningDateTime"],
+ "cast": try_parse_date,
},
- 'sceneName': {
- 'path': [
- 'DataGranule',
- 'Identifiers',
- ('IdentifierType', 'ProducerGranuleId'),
- 'Identifier',
+ "sceneName": {
+ "path": [
+ "DataGranule",
+ "Identifiers",
+ ("IdentifierType", "ProducerGranuleId"),
+ "Identifier",
]
},
- 'browse': {'path': ['RelatedUrls', ('Type', [('GET RELATED VISUALIZATION', 'URL')])]},
- 'platform': {'path': ['AdditionalAttributes', ('Name', 'ASF_PLATFORM'), 'Values', 0]},
- 'bytes': {
- 'path': ['AdditionalAttributes', ('Name', 'BYTES'), 'Values', 0],
- 'cast': try_round_float,
+ "browse": {"path": ["RelatedUrls", ("Type", [("GET RELATED VISUALIZATION", "URL")])]},
+ "platform": {"path": ["AdditionalAttributes", ("Name", "ASF_PLATFORM"), "Values", 0]},
+ "bytes": {
+ "path": ["AdditionalAttributes", ("Name", "BYTES"), "Values", 0],
+ "cast": try_round_float,
},
- 'md5sum': {'path': ['AdditionalAttributes', ('Name', 'MD5SUM'), 'Values', 0]},
- 'frameNumber': {
- 'path': ['AdditionalAttributes', ('Name', 'CENTER_ESA_FRAME'), 'Values', 0],
- 'cast': try_parse_int,
+ "md5sum": {"path": ["AdditionalAttributes", ("Name", "MD5SUM"), "Values", 0]},
+ "frameNumber": {
+ "path": ["AdditionalAttributes", ("Name", "CENTER_ESA_FRAME"), "Values", 0],
+ "cast": try_parse_int,
}, # overloaded by S1, ALOS, and ERS
- 'granuleType': {'path': ['AdditionalAttributes', ('Name', 'GRANULE_TYPE'), 'Values', 0]},
- 'orbit': {
- 'path': ['OrbitCalculatedSpatialDomains', 0, 'OrbitNumber'],
- 'cast': try_parse_int,
+ "granuleType": {"path": ["AdditionalAttributes", ("Name", "GRANULE_TYPE"), "Values", 0]},
+ "orbit": {
+ "path": ["OrbitCalculatedSpatialDomains", 0, "OrbitNumber"],
+ "cast": try_parse_int,
},
- 'polarization': {'path': ['AdditionalAttributes', ('Name', 'POLARIZATION'), 'Values', 0]},
- 'processingDate': {
- 'path': ['DataGranule', 'ProductionDateTime'],
- 'cast': try_parse_date,
+ "polarization": {"path": ["AdditionalAttributes", ("Name", "POLARIZATION"), "Values", 0]},
+ "processingDate": {
+ "path": ["DataGranule", "ProductionDateTime"],
+ "cast": try_parse_date,
},
- 'sensor': {
- 'path': ['Platforms', 0, 'Instruments', 0, 'ShortName'],
+ "sensor": {
+ "path": ["Platforms", 0, "Instruments", 0, "ShortName"],
},
}
"""
@@ -128,16 +131,25 @@ class ASFProduct:
combine `ASFProduct._base_properties` with their own separately defined `_base_properties`
"""
- _url_types = ['GET DATA', 'EXTENDED METADATA', 'GET DATA VIA DIRECT ACCESS', 'GET RELATED VISUALIZATION', 'VIEW RELATED INFORMATION', 'USE SERVICE API']
-
+ _url_types = [
+ "GET DATA",
+ "EXTENDED METADATA",
+ "GET DATA VIA DIRECT ACCESS",
+ "GET RELATED VISUALIZATION",
+ "VIEW RELATED INFORMATION",
+ "USE SERVICE API",
+ ]
+
+ _default_browse_extensions = (".png", ".jpg", ".jpeg")
+
def __init__(self, args: Dict = {}, session: ASFSession = ASFSession()):
- self.meta = args.get('meta')
- self.umm = args.get('umm')
+ self.meta = args.get("meta")
+ self.umm = args.get("umm")
translated = self.translate_product(args)
- self.properties = translated['properties']
- self.geometry = translated['geometry']
+ self.properties = translated["properties"]
+ self.geometry = translated["geometry"]
self.baseline = None
self.session = session
@@ -150,9 +162,9 @@ class ASFProduct:
with `type`, `geometry`, and `properties` keys
"""
return {
- 'type': 'Feature',
- 'geometry': self.geometry,
- 'properties': self.properties,
+ "type": "Feature",
+ "geometry": self.geometry,
+ "properties": self.properties,
}
def download(
@@ -172,16 +184,16 @@ class ASFProduct:
:return: None
"""
- default_filename = self.properties['fileName']
+ default_filename = self.properties["fileName"]
if filename is not None:
multiple_files = (
fileType == FileDownloadType.ADDITIONAL_FILES
- and len(self.properties['additionalUrls']) > 1
+ and len(self.properties["additionalUrls"]) > 1
) or fileType == FileDownloadType.ALL_FILES
if multiple_files:
warnings.warn(
- 'Attempting to download multiple files for product, '
+ "Attempting to download multiple files for product, "
f'ignoring user provided filename argument "{filename}", using default.'
)
else:
@@ -193,12 +205,12 @@ class ASFProduct:
urls = self.get_urls(fileType=fileType)
for url in urls:
- base_filename = '.'.join(default_filename.split('.')[:-1])
- extension = url.split('.')[-1]
+ base_filename = ".".join(default_filename.split(".")[:-1])
+ extension = url.split(".")[-1]
download_url(
url=url,
path=path,
- filename=f'{base_filename}.{extension}',
+ filename=f"{base_filename}.{extension}",
session=session,
)
@@ -206,12 +218,12 @@ class ASFProduct:
urls = []
if fileType == FileDownloadType.DEFAULT_FILE:
- urls.append(self.properties['url'])
+ urls.append(self.properties["url"])
elif fileType == FileDownloadType.ADDITIONAL_FILES:
- urls.extend(self.properties.get('additionalUrls', []))
+ urls.extend(self.properties.get("additionalUrls", []))
elif fileType == FileDownloadType.ALL_FILES:
- urls.append(self.properties['url'])
- urls.extend(self.properties.get('additionalUrls', []))
+ urls.append(self.properties["url"])
+ urls.extend(self.properties.get("additionalUrls", []))
else:
raise ValueError(
"Invalid FileDownloadType provided, the valid types are 'DEFAULT_FILE', 'ADDITIONAL_FILES', and 'ALL_FILES'"
@@ -224,7 +236,7 @@ class ASFProduct:
) -> List[Tuple[str, str]]:
return [
(self._parse_filename_from_url(url), url)
- for url in self.properties.get('additionalUrls', [])
+ for url in self.properties.get("additionalUrls", [])
]
def _parse_filename_from_url(self, url: str) -> str:
@@ -233,7 +245,7 @@ class ASFProduct:
return filename
def stack(
- self, opts: ASFSearchOptions = None, useSubclass: Type['ASFProduct'] = None
+ self, opts: ASFSearchOptions = None, useSubclass: Type["ASFProduct"] = None
) -> ASFSearchResults:
"""
Builds a baseline stack from this product.
@@ -267,13 +279,12 @@ class ASFProduct:
return None
def _get_access_urls(
- self,
- url_types: List[str] = ['GET DATA', 'EXTENDED METADATA']
+ self, url_types: List[str] = ["GET DATA", "EXTENDED METADATA"]
) -> List[str]:
accessUrls = []
for url_type in url_types:
- if urls := self.umm_get(self.umm, 'RelatedUrls', ('Type', [(url_type, 'URL')]), 0):
+ if urls := self.umm_get(self.umm, "RelatedUrls", ("Type", [(url_type, "URL")]), 0):
accessUrls.extend(urls)
return sorted(list(set(accessUrls)))
@@ -281,27 +292,28 @@ class ASFProduct:
def _get_urls(self) -> List[str]:
"""Finds and returns all umm urls"""
urls = self._get_access_urls(self._url_types)
- return [
- url for url in urls if not url.startswith('s3://')
- ]
+ return [url for url in urls if not url.startswith("s3://")]
def _get_s3_uris(self) -> List[str]:
"""Finds and returns all umm S3 direct access uris"""
s3_urls = self._get_access_urls(self._url_types)
- return [url for url in s3_urls if url.startswith('s3://')]
+ return [url for url in s3_urls if url.startswith("s3://")]
def _get_additional_urls(self) -> List[str]:
"""Finds and returns all non-md5/image urls and filters out the existing `url` property"""
access_urls = self._get_urls()
return [
- url for url in access_urls
- if not url.endswith('.md5')
- and not url.endswith('.png')
- and url != self.properties['url']
- and 's3credentials' not in url
+ url
+ for url in access_urls
+ if not url.endswith(".md5")
+ and not url.endswith(".png")
+ and url != self.properties["url"]
+ and "s3credentials" not in url
]
- def find_urls(self, extension: str = None, pattern: str = r'.*', directAccess: bool = False) -> List[str]:
+ def find_urls(
+ self, extension: str = None, pattern: str = r".*", directAccess: bool = False
+ ) -> List[str]:
"""
Searches for all urls matching a given extension and/or pattern
param extension: the file extension to search for. (Defaults to `None`)
@@ -311,13 +323,13 @@ class ASFProduct:
param directAccess: should search in s3 bucket urls (Defaults to `False`)
"""
search_list = self._get_s3_uris() if directAccess else self._get_urls()
-
+
def _get_extension(file_url: str):
path = parse.urlparse(file_url).path
return os.path.splitext(path)[-1]
-
+
if extension is not None:
- search_list = [url for url in search_list if _get_extension(url) == extension]
+ search_list = [url for url in search_list if _get_extension(url) == extension]
regexp = re.compile(pattern=pattern)
@@ -327,7 +339,7 @@ class ASFProduct:
"""
Finds the centroid of a product
"""
- coords = mapping(shape(self.geometry))['coordinates'][0]
+ coords = mapping(shape(self.geometry))["coordinates"][0]
lons = [p[0] for p in coords]
if max(lons) - min(lons) > 180:
unwrapped_coords = [a if a[0] > 0 else [a[0] + 360, a[1]] for a in coords]
@@ -336,7 +348,7 @@ class ASFProduct:
return Polygon(unwrapped_coords).centroid
- def remotezip(self, session: ASFSession) -> 'RemoteZip': # type: ignore # noqa: F821
+ def remotezip(self, session: ASFSession) -> "RemoteZip": # type: ignore # noqa: F821
"""Returns a RemoteZip object which can be used to download
a part of an ASFProduct's zip archive. (See example in examples/5-Download.ipynb)
@@ -348,36 +360,36 @@ class ASFProduct:
"""
from .download.download import remotezip
- return remotezip(self.properties['url'], session=session)
+ return remotezip(self.properties["url"], session=session)
def _read_umm_property(self, umm: Dict, mapping: Dict) -> Any:
- value = self.umm_get(umm, *mapping['path'])
- if mapping.get('cast') is None:
+ value = self.umm_get(umm, *mapping["path"])
+ if mapping.get("cast") is None:
return value
- return self.umm_cast(mapping['cast'], value)
+ return self.umm_cast(mapping["cast"], value)
def _get_geometry(self, item: Dict):
- """Helper method that creates a geometry context dictionary.
+ """Helper method that creates a geometry context dictionary.
Meant primarily for NISARProduct to override for dateline multipolygon parsing.
"""
try:
- coordinates = item['umm']['SpatialExtent']['HorizontalSpatialDomain']['Geometry'][
- 'GPolygons'
- ][0]['Boundary']['Points']
- coordinates = [[c['Longitude'], c['Latitude']] for c in coordinates]
- geometry = {'coordinates': [coordinates], 'type': 'Polygon'}
+ coordinates = item["umm"]["SpatialExtent"]["HorizontalSpatialDomain"]["Geometry"][
+ "GPolygons"
+ ][0]["Boundary"]["Points"]
+ coordinates = [[c["Longitude"], c["Latitude"]] for c in coordinates]
+ geometry = {"coordinates": [coordinates], "type": "Polygon"}
except KeyError:
- geometry = {'coordinates': None, 'type': 'Polygon'}
+ geometry = {"coordinates": None, "type": "Polygon"}
return geometry
-
+
def translate_product(self, item: Dict) -> Dict:
"""
Generates `properties` and `geometry` from the CMR UMM response
"""
geometry = self._get_geometry(item)
- umm = item.get('umm')
+ umm = item.get("umm")
# additionalAttributes = {attr['Name']: attr['Values'] for attr in umm['AdditionalAttributes']}
@@ -386,21 +398,21 @@ class ASFProduct:
for prop, umm_mapping in self._base_properties.items()
}
- if properties.get('url') is not None:
- properties['fileName'] = properties['url'].split('/')[-1]
+ if properties.get("url") is not None:
+ properties["fileName"] = properties["url"].split("/")[-1]
else:
- properties['fileName'] = None
+ properties["fileName"] = None
# Fallbacks
- if properties.get('beamModeType') is None:
- properties['beamModeType'] = self.umm_get(
- umm, 'AdditionalAttributes', ('Name', 'BEAM_MODE'), 'Values', 0
+ if properties.get("beamModeType") is None:
+ properties["beamModeType"] = self.umm_get(
+ umm, "AdditionalAttributes", ("Name", "BEAM_MODE"), "Values", 0
)
- if properties.get('platform') is None:
- properties['platform'] = self.umm_get(umm, 'Platforms', 0, 'ShortName')
+ if properties.get("platform") is None:
+ properties["platform"] = self.umm_get(umm, "Platforms", 0, "ShortName")
- return {'geometry': geometry, 'properties': properties, 'type': 'Feature'}
+ return {"geometry": geometry, "properties": properties, "type": "Feature"}
def get_sort_keys(self) -> Tuple[str, str]:
"""
@@ -409,9 +421,9 @@ class ASFProduct:
"""
# `sort()` will raise an error when comparing `NoneType`,
# using self._read_property() to wrap standard `dict.get()` for possible `None` values
- primary_key = self._read_property(key='stopTime', default='')
+ primary_key = self._read_property(key="stopTime", default="")
secondary_key = self._read_property(
- key='fileID', default=self._read_property('sceneName', '')
+ key="fileID", default=self._read_property("sceneName", "")
)
return (primary_key, secondary_key)
@@ -428,6 +440,52 @@ class ASFProduct:
return output
+ def _get_file_sizes_and_sums(self) -> FileSizeInfo | None:
+ """Helper method for returning file sizes and md5sums from `ArchiveAndDistributionInformation` if available.
+ Returns None if `ArchiveAndDistributionInformation` isn't defined"""
+ bytes_temp = self.umm_get(self.umm, "DataGranule", "ArchiveAndDistributionInformation")
+ if bytes_temp is None or len(bytes_temp) == 0:
+ return None
+
+ if bytes_temp[0].get("SizeInBytes"):
+ size_key = "SizeInBytes"
+ size_format = "Format"
+ else:
+ size_key = "Size"
+ size_format = "SizeUnit"
+
+ bytes_mapping = {
+ entry["Name"]: {
+ "bytes": entry.get(size_key),
+ "format": entry.get(size_format),
+ }
+ for entry in bytes_temp
+ }
+
+ md5sum_mapping = {
+ entry["Name"]: entry.get("Checksum", {"Value": None})["Value"] for entry in bytes_temp
+ }
+
+ return FileSizeInfo(bytes_mapping, md5sum_mapping)
+
+ def _set_additional_metadata(self):
+ """Helper method for data migrated off-prem"""
+ file_info = self._get_file_sizes_and_sums()
+
+ if file_info is not None:
+ self.properties["bytes"], self.properties["md5sum"] = file_info
+ self.properties["additionalUrls"] = self._get_additional_urls()
+ self.properties["browse"] = [
+ url for url in self._get_urls() if url.endswith(self._default_browse_extensions)
+ ]
+ self.properties["s3Urls"] = self._get_s3_uris()
+
+ self.properties["conceptID"] = self.umm_get(self.meta, "collection-concept-id")
+
+ center = self.centroid()
+ self.properties["centerLat"] = center.y
+ self.properties["centerLon"] = center.x
+
@final
@staticmethod
def umm_get(item: Dict, *args):
@@ -535,7 +593,7 @@ class ASFProduct:
return None
if item is None:
return None
- if item in [None, 'NA', 'N/A', '']:
+ if item in [None, "NA", "N/A", ""]:
item = None
return item
=====================================
asf_search/CMR/datasets.py
=====================================
@@ -205,6 +205,10 @@ dataset_collections = {
"ALOS_PSR_L2.2",
"ALOS_PSR_RTC_HIGH",
"ALOS_PSR_RTC_LOW",
+ "ALOS_L10_PSR",
+ "ALOS_L11_PSR",
+ "ALOS_L15_PSR",
+ "ALOS_RTC_PSR",
},
"ALOS AVNIR-2": {"ALOS_AVNIR_OBS_ORI", "ALOS_AVNIR_OBS_ORI_BROWSE"},
"SIR-C": {
@@ -266,6 +270,10 @@ dataset_collections = {
"SPL1C_S0_HiRes_QA_001",
"SPL1C_S0_HiRes_QA_002",
"SPL1C_S0_HiRes_QA_003",
+ "SMAP_L1A_RO_V3",
+ "SMAP_L1A_V2",
+ "SMAP_L1B_S0_V3",
+ "SMAP_L1C_S0_V3",
},
"UAVSAR": {
"UAVSAR_INSAR_AMP",
@@ -394,6 +402,10 @@ collections_per_platform = {
"ALOS_PSR_L2.2",
"ALOS_PSR_RTC_HIGH",
"ALOS_PSR_RTC_LOW",
+ "ALOS_L10_PSR",
+ "ALOS_L11_PSR",
+ "ALOS_L15_PSR",
+ "ALOS_RTC_PSR",
},
"ALOS-2": {"ALOS2_L1_PSR2"},
"ERS-1": {"ERS-1_L0", "ERS-1_L1"},
@@ -429,6 +441,10 @@ collections_per_platform = {
"SPL1A_RO_QA_001",
"SPL1A_RO_QA_002",
"SPL1A_RO_QA_003",
+ "SMAP_L1A_RO_V3",
+ "SMAP_L1A_V2",
+ "SMAP_L1B_S0_V3",
+ "SMAP_L1C_S0_V3",
},
"G-III": {
"UAVSAR_POL_META",
@@ -652,6 +668,10 @@ collections_by_processing_level = {
"GSLC": {"NISAR_L2_GSLC_BETA_V1", "NISAR_L2_GSLC_PROVISIONAL_V1", "NISAR_L2_GSLC_V1"},
"GUNW": {"NISAR_L2_GUNW_BETA_V1", "NISAR_L2_GUNW_PROVISIONAL_V1", "NISAR_L2_GUNW_V1"},
"SME2": {"NISAR_L3_SME2_BETA_V1", "NISAR_L3_SME2_PROVISIONAL_V1", "NISAR_L3_SME2_V1"},
+ "L1A_RADAR_RO": {"L1A_RADAR_RO"},
+ "L1C_S0_HIRES": {"L1C_S0_HIRES"},
+ "L1B_S0_LORES": {"L1B_S0_LORES"},
+ "L1A_RADAR": {"L1A_RADAR"},
}
=====================================
asf_search/Products/ALOSProduct.py
=====================================
@@ -13,31 +13,40 @@ class ALOSProduct(ASFStackableProduct):
_base_properties = {
**ASFStackableProduct._base_properties,
- 'frameNumber': {
- 'path': ['AdditionalAttributes', ('Name', 'FRAME_NUMBER'), 'Values', 0],
- 'cast': try_parse_int,
+ "frameNumber": {
+ "path": ["AdditionalAttributes", ("Name", "FRAME_NUMBER"), "Values", 0],
+ "cast": try_parse_int,
},
- 'faradayRotation': {
- 'path': ['AdditionalAttributes', ('Name', 'FARADAY_ROTATION'), 'Values', 0],
- 'cast': try_parse_float,
+ "faradayRotation": {
+ "path": ["AdditionalAttributes", ("Name", "FARADAY_ROTATION"), "Values", 0],
+ "cast": try_parse_float,
},
- 'offNadirAngle': {
- 'path': ['AdditionalAttributes', ('Name', 'OFF_NADIR_ANGLE'), 'Values', 0],
- 'cast': try_parse_float,
+ "offNadirAngle": {
+ "path": ["AdditionalAttributes", ("Name", "OFF_NADIR_ANGLE"), "Values", 0],
+ "cast": try_parse_float,
},
- 'bytes': {
- 'path': ['AdditionalAttributes', ('Name', 'BYTES'), 'Values', 0],
- 'cast': try_round_float,
+ "bytes": {
+ "path": ["AdditionalAttributes", ("Name", "BYTES"), "Values", 0],
+ "cast": try_round_float,
},
- 'insarStackId': {'path': ['AdditionalAttributes', ('Name', 'INSAR_STACK_ID'), 'Values', 0]},
- 'beamModeType': {'path': ['AdditionalAttributes', ('Name', 'BEAM_MODE_TYPE'), 'Values', 0]},
+ "insarStackId": {"path": ["AdditionalAttributes", ("Name", "INSAR_STACK_ID"), "Values", 0]},
+ "beamModeType": {"path": ["AdditionalAttributes", ("Name", "BEAM_MODE"), "Values", 0]},
+ "polarization": {"path": ["AdditionalAttributes", ("Name", "POLARIZATION"), "Values"]},
}
def __init__(self, args: Dict = {}, session: ASFSession = ASFSession()):
super().__init__(args, session)
- if self.properties.get('groupID') is None:
- self.properties['groupID'] = self.properties['sceneName']
+ if self.properties["bytes"] is None:
+ self._set_additional_metadata()
+
+ if (
+ self.properties["polarization"] is not None
+ and len(self.properties["polarization"]) == 1
+ ):
+ self.properties["polarization"] = self.properties["polarization"].pop()
+ if self.properties.get("groupID") is None:
+ self.properties["groupID"] = self.properties["sceneName"]
@staticmethod
def get_default_baseline_product_type() -> Union[str, None]:
=====================================
asf_search/Products/SMAPProduct.py
=====================================
@@ -10,10 +10,18 @@ class SMAPProduct(ASFProduct):
_base_properties = {
**ASFProduct._base_properties,
- 'groupID': {'path': ['AdditionalAttributes', ('Name', 'GROUP_ID'), 'Values', 0]},
- 'insarStackId': {'path': ['AdditionalAttributes', ('Name', 'INSAR_STACK_ID'), 'Values', 0]},
- 'md5sum': {'path': ['AdditionalAttributes', ('Name', 'MD5SUM'), 'Values', 0]},
+ "groupID": {"path": ["AdditionalAttributes", ("Name", "GROUP_ID"), "Values", 0]},
+ "insarStackId": {"path": ["AdditionalAttributes", ("Name", "INSAR_STACK_ID"), "Values", 0]},
+ "md5sum": {"path": ["AdditionalAttributes", ("Name", "MD5SUM"), "Values", 0]},
+ "processingLevel": {
+ "path": ["AdditionalAttributes", ("Name", "PRODUCT_TYPE"), "Values", 0]
+ },
}
def __init__(self, args: Dict = {}, session: ASFSession = ASFSession()):
super().__init__(args, session)
+ if self.properties["md5sum"] is None:
+ self._set_additional_metadata()
+
+ if self.properties.get("groupID") is None:
+ self.properties["groupID"] = self.properties["sceneName"]
=====================================
asf_search/constants/PRODUCT_TYPE.py
=====================================
@@ -1,29 +1,33 @@
# Sentinel-1
-GRD_HD = 'GRD_HD'
-GRD_MD = 'GRD_MD'
-GRD_MS = 'GRD_MS'
-GRD_HS = 'GRD_HS'
-GRD_FD = 'GRD_FD'
-SLC = 'SLC'
-OCN = 'OCN'
-RAW = 'RAW'
-METADATA_GRD_HD = 'METADATA_GRD_HD'
-METADATA_GRD_MD = 'METADATA_GRD_MD'
-METADATA_GRD_MS = 'METADATA_GRD_MS'
-METADATA_GRD_HS = 'METADATA_GRD_HS'
-METADATA_SLC = 'METADATA_SLC'
-METADATA_OCN = 'METADATA_OCN'
-METADATA_RAW = 'METADATA_RAW'
-BURST = 'BURST'
+GRD_HD = "GRD_HD"
+GRD_MD = "GRD_MD"
+GRD_MS = "GRD_MS"
+GRD_HS = "GRD_HS"
+GRD_FD = "GRD_FD"
+SLC = "SLC"
+OCN = "OCN"
+RAW = "RAW"
+METADATA_GRD_HD = "METADATA_GRD_HD"
+METADATA_GRD_MD = "METADATA_GRD_MD"
+METADATA_GRD_MS = "METADATA_GRD_MS"
+METADATA_GRD_HS = "METADATA_GRD_HS"
+METADATA_SLC = "METADATA_SLC"
+METADATA_OCN = "METADATA_OCN"
+METADATA_RAW = "METADATA_RAW"
+BURST = "BURST"
# ALOS PALSAR
-L1_0 = 'L1.0'
-L1_1 = 'L1.1'
-L1_5 = 'L1.5'
-L2_2 = 'L2.2'
-RTC_LOW_RES = 'RTC_LOW_RES'
-RTC_HIGH_RES = 'RTC_HI_RES'
-KMZ = 'KMZ'
+L1_0 = "L1.0"
+L1_1 = "L1.1"
+L1_5 = "L1.5"
+L2_2 = "L2.2"
+RTC_LOW_RES = "RTC_LOW_RES"
+RTC_HIGH_RES = "RTC_HI_RES"
+KMZ = "KMZ"
+L1A_RADAR_RO = "L1A_RADAR_RO"
+L1C_S0_HIRES = "L1C_S0_HIRES"
+L1B_S0_LORES = "L1B_S0_LORES"
+L1A_RADAR = "L1A_RADAR"
# ALOS AVNIR
# No PROCESSING_TYPE attribute in CMR
@@ -32,45 +36,45 @@ KMZ = 'KMZ'
# SLC and SLC metadata are both 'SLC', provided by Sentinel-1 constants
# Sentinel-1 InSAR
-GUNW_STD = 'GUNW_STD'
-GUNW_AMP = 'GUNW_AMP'
-GUNW_CON = 'GUNW_CON'
-GUN_COH = 'GUNW_COH'
-GUNW_UNW = 'GUNW_UNW'
+GUNW_STD = "GUNW_STD"
+GUNW_AMP = "GUNW_AMP"
+GUNW_CON = "GUNW_CON"
+GUN_COH = "GUNW_COH"
+GUNW_UNW = "GUNW_UNW"
# SMAP
-L1A_RADAR_RO_HDF5 = 'L1A_Radar_RO_HDF5'
-L1A_RADAR_HDF5 = 'L1A_Radar_HDF5'
-L1B_S0_LOW_RES_HDF5 = 'L1B_S0_LoRes_HDF5'
-L1C_S0_HIGH_RES_HDF5 = 'L1C_S0_HiRes_HDF5'
-L1A_RADAR_RO_QA = 'L1A_Radar_RO_QA'
-L1A_RADAR_QA = 'L1A_Radar_QA'
-L1B_S0_LOW_RES_QA = 'L1B_S0_LoRes_QA'
-L1C_S0_HIGH_RES_QA = 'L1C_S0_HiRes_QA'
-L1A_RADAR_RO_ISO_XML = 'L1A_Radar_RO_ISO_XML'
-L1B_S0_LOW_RES_ISO_XML = 'L1B_S0_LoRes_ISO_XML'
-L1C_S0_HIGH_RES_ISO_XML = 'L1C_S0_HiRes_ISO_XML'
+L1A_RADAR_RO_HDF5 = "L1A_Radar_RO_HDF5"
+L1A_RADAR_HDF5 = "L1A_Radar_HDF5"
+L1B_S0_LOW_RES_HDF5 = "L1B_S0_LoRes_HDF5"
+L1C_S0_HIGH_RES_HDF5 = "L1C_S0_HiRes_HDF5"
+L1A_RADAR_RO_QA = "L1A_Radar_RO_QA"
+L1A_RADAR_QA = "L1A_Radar_QA"
+L1B_S0_LOW_RES_QA = "L1B_S0_LoRes_QA"
+L1C_S0_HIGH_RES_QA = "L1C_S0_HiRes_QA"
+L1A_RADAR_RO_ISO_XML = "L1A_Radar_RO_ISO_XML"
+L1B_S0_LOW_RES_ISO_XML = "L1B_S0_LoRes_ISO_XML"
+L1C_S0_HIGH_RES_ISO_XML = "L1C_S0_HiRes_ISO_XML"
# UAVSAR
-AMPLITUDE = 'AMPLITUDE'
-STOKES = 'STOKES'
-AMPLITUDE_GRD = 'AMPLITUDE_GRD'
-PROJECTED = 'PROJECTED'
-PROJECTED_ML5X5 = 'PROJECTED_ML5X5'
-PROJECTED_ML3X3 = 'PROJECTED_ML3X3'
-INTERFEROMETRY_GRD = 'INTERFEROMETRY_GRD'
-INTERFEROMETRY = 'INTERFEROMETRY'
-COMPLEX = 'COMPLEX'
+AMPLITUDE = "AMPLITUDE"
+STOKES = "STOKES"
+AMPLITUDE_GRD = "AMPLITUDE_GRD"
+PROJECTED = "PROJECTED"
+PROJECTED_ML5X5 = "PROJECTED_ML5X5"
+PROJECTED_ML3X3 = "PROJECTED_ML3X3"
+INTERFEROMETRY_GRD = "INTERFEROMETRY_GRD"
+INTERFEROMETRY = "INTERFEROMETRY"
+COMPLEX = "COMPLEX"
# KMZ provided by ALOS PALSAR
-INC = 'INC'
-SLOPE = 'SLOPE'
-DEM_TIFF = 'DEM_TIFF'
-PAULI = 'PAULI'
-METADATA = 'METADATA'
+INC = "INC"
+SLOPE = "SLOPE"
+DEM_TIFF = "DEM_TIFF"
+PAULI = "PAULI"
+METADATA = "METADATA"
# RADARSAT
-L0 = 'L0'
-L1 = 'L1'
+L0 = "L0"
+L1 = "L1"
# ERS
# L0 provided by RADARSAT
@@ -81,67 +85,67 @@ L1 = 'L1'
# L1 provided by RADARSAT
# AIRSAR
-CTIF = 'CTIF'
-PTIF = 'PTIF'
-LTIF = 'LTIF'
-JPG = 'JPG'
-LSTOKES = 'LSTOKES'
-PSTOKES = 'PSTOKES'
-CSTOKES = 'CSTOKES'
-DEM = 'DEM'
-THREEFP = '3FP'
+CTIF = "CTIF"
+PTIF = "PTIF"
+LTIF = "LTIF"
+JPG = "JPG"
+LSTOKES = "LSTOKES"
+PSTOKES = "PSTOKES"
+CSTOKES = "CSTOKES"
+DEM = "DEM"
+THREEFP = "3FP"
# OPERA-S1
-RTC = 'RTC'
-CSLC = 'CSLC'
-RTC_STATIC = 'RTC-STATIC'
-CSLC_STATIC = 'CSLC-STATIC'
-DISP_S1 = 'DISP-S1'
-DISP_S1_STATIC = 'DISP-S1-STATIC'
-DIST_ALERT_S1 = 'DIST-ALERT-S1'
+RTC = "RTC"
+CSLC = "CSLC"
+RTC_STATIC = "RTC-STATIC"
+CSLC_STATIC = "CSLC-STATIC"
+DISP_S1 = "DISP-S1"
+DISP_S1_STATIC = "DISP-S1-STATIC"
+DIST_ALERT_S1 = "DIST-ALERT-S1"
# TROPO
-TROPO_ZENITH = 'TROPO-ZENITH'
-ECMWF_TROPO = 'ECMWF_TROPO'
+TROPO_ZENITH = "TROPO-ZENITH"
+ECMWF_TROPO = "ECMWF_TROPO"
### NISAR
### NISAR Science Products ###
-L0B = 'L0B'
+L0B = "L0B"
"""alias for RRSD Level 0B product types"""
-RRSD = 'RRSD'
+RRSD = "RRSD"
"""Level 0B Radar Raw Signal Data"""
-RSLC = 'RSLC'
+RSLC = "RSLC"
"""Level 1 Range-Doppler Single Look Complex"""
-RIFG = 'RIFG'
+RIFG = "RIFG"
"""Level 1 Range-Doppler Wrapped Interferrogram"""
-RUNW = 'RUNW'
+RUNW = "RUNW"
"""Level 1 Range-Doppler Unwrapped Interferrogram"""
-ROFF = 'ROFF'
+ROFF = "ROFF"
"""Level 1 Range-Doppler Pixel Offsets"""
-GSLC = 'GSLC'
+GSLC = "GSLC"
"""Level 2 Geocoded Single Look Complex"""
-GCOV = 'GCOV'
+GCOV = "GCOV"
"""Level 2 Geocoded Polarimetric Covariance"""
-GUNW = 'GUNW'
+GUNW = "GUNW"
""""Level 2 Geocoded Unwrapped Inteferrogram"""
-GOFF = 'GOFF'
+GOFF = "GOFF"
"""Level 2 Geocoded Pixel Offsets"""
-SME2 = 'SME2'
+SME2 = "SME2"
"""Level 3 Soil Moisture EASE-Grid 2.0"""
### NISAR ANCILLARY PRODUCTS ###
-CRSD = 'CRSD'
+CRSD = "CRSD"
"""NISAR Radar Raw Signal Calibration Data"""
# NISAR Coordinated Observation Plan (NISAR_COP)
-DCOP = 'DCOP'
+DCOP = "DCOP"
"""NISAR Coordinates Observation Plan"""
# NISAR_OROST
-OROST = 'OROST'
+OROST = "OROST"
"""NISAR Radar Observation Sequence Table"""
# NISAR_STUF
STUF = "STUF"
@@ -157,7 +161,7 @@ LRCLK_UTC = "LRCLK_UTC"
# NISAR Orbit Ephemeris (NISAR_OE)
-# "The NASA-ISRO Synthetic Aperture Radar (NISAR) Orbit Ephemeris collection contains
+# "The NASA-ISRO Synthetic Aperture Radar (NISAR) Orbit Ephemeris collection contains
# the state vector files for the NISAR mission"
FOE = "FOE"
"""Forecast Orbit Ephemeris"""
@@ -169,7 +173,7 @@ POE = "POE"
""" Precise Orbit Ephemeris"""
# NISAR Ancillary and Auxiliary Data (NISAR_ANC_AUX)
-# "The NASA-ISRO Synthetic Aperture Radar (NISAR) Ancilliary and Auxiallry collection contains products
+# "The NASA-ISRO Synthetic Aperture Radar (NISAR) Ancilliary and Auxiallry collection contains products
# that are supplementary information for the NISAR mission and are created a limited number of times.""
TSR_STATIC = "TSR_STATIC"
"""Time Series Ratio (TSR_STATIC)"""
@@ -218,7 +222,7 @@ ECMWF_SMST = "ECMWF_SMST"
# DEM = "DEM"
# """ Digital Elevation Model"""
-# LOCAL_INC_ANG = "LOCAL_INC_ANG"
+# LOCAL_INC_ANG = "LOCAL_INC_ANG"
# """ Local Incidence Angles"""
# VWC = "VWC"
# """ Vegetation water content"""
View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/commit/8667e7a562519aa476929ee8d053c2ed9ae605ac
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/commit/8667e7a562519aa476929ee8d053c2ed9ae605ac
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260912/e0a905b3/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list