Skip to content

Commit

Permalink
Merge branch 'master' into cs.searchapi-v3-edits
Browse files Browse the repository at this point in the history
  • Loading branch information
kim committed May 1, 2024
2 parents 0b7b394 + 1bf62d1 commit 4923c3d
Show file tree
Hide file tree
Showing 15 changed files with 145 additions and 56 deletions.
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,23 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Exposed `ASFProduct.get_urls` which returns the URL's for it's products directly. Can control which products with the `fileType` enum.

------
## [v7.0.10](https://github.com/asfadmin/Discovery-asf_search/compare/v7.0.9...v7.0.10)
## [v7.1.1](https://github.com/asfadmin/Discovery-asf_search/compare/v7.1.0...v7.1.1)
### Changed
- Uses `ciso8601.parse_datetime()` in baseline calculations, speeds up calculations on larger stacks
### Added
- Adds `ASF_LOGGER` logging in `search_generator()` and related methods
### Fixed
- `ASFProduct.get_sort_keys()` will no longer returns `None` if missing sort key, defaults to empty string

------
## [v7.1.0](https://github.com/asfadmin/Discovery-asf_search/compare/v7.0.9...v7.1.0)
### Added
- Improved logging in `ASFSession` authentication methods

### Changed
- Uses `ciso8601` module for parsing dates from CMR response, significant performance improvement post-query
- `ASFSession` now allows for authorized user access to hidden/restricted CMR datasets via `auth_with_creds()` or `auth_with_cookiejar()` authentication methods (previously only supported via `auth_with_token()` method)
- `ASFSession.auth_with_token()` now authenticates directly against EDL endpoint
- UMM Platform ShortName used as final fallback criteria for product subclass assignment

------
## [v7.0.9](https://github.com/asfadmin/Discovery-asf_search/compare/v7.0.8...v7.0.9)
Expand Down
39 changes: 36 additions & 3 deletions asf_search/ASFProduct.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,45 @@ def translate_product(self, item: Dict) -> Dict:

return {'geometry': geometry, 'properties': properties, 'type': 'Feature'}

def get_sort_keys(self) -> Tuple:
# ASFProduct subclasses define extra/override param key + UMM pathing here
@staticmethod
def get_property_paths() -> Dict:
"""
Returns tuple of primary and secondary date values used for sorting final search results
Returns _base_properties of class, subclasses such as `S1Product` (or user provided subclasses) can override this to
define which properties they want in their subclass's properties dict.
(See `S1Product.get_property_paths()` for example of combining _base_properties of multiple classes)
:returns dictionary, {`PROPERTY_NAME`: {'path': [umm, path, to, value], 'cast (optional)': Callable_to_cast_value}, ...}
"""
return (self.properties.get('stopTime'), self.properties.get('fileID', 'sceneName'))
return ASFProduct._base_properties

def get_sort_keys(self) -> Tuple[str, str]:
"""
Returns tuple of primary and secondary date values used for sorting final search results
Any subclasses must return string for final `sort()` to work
"""
# `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='')
secondary_key = self._read_property(
key='fileID',
default=self._read_property('sceneName', '')
)

return (primary_key, secondary_key)

def _read_property(self, key: str, default: Any = None) -> Any:
"""
Helper method wraps `properties.get()`.
Since a property can be `None`, if the key exists `dict.get('key', 'default')` will never return the default
"""
output = default
if (value:=self.properties.get(key)) is not None:
output = value

return output

@final
@staticmethod
def umm_get(item: Dict, *args):
Expand Down
2 changes: 1 addition & 1 deletion asf_search/ASFSearchOptions/ASFSearchOptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def __str__(self):
"""
What to display if `print(opts)` is called.
"""
return json.dumps(dict(self), default=str, indent=4)
return json.dumps(dict(self), indent=4, default=str)

# Default is set to '...', since 'None' is a very valid value here
def pop(self, key, default=...):
Expand Down
1 change: 0 additions & 1 deletion asf_search/CMR/translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
from shapely.geometry.base import BaseGeometry
from .field_map import field_map
from .datasets import collections_per_platform
import dateparser
import ciso8601
import logging

Expand Down
12 changes: 11 additions & 1 deletion asf_search/Products/ARIAS1GUNWProduct.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Dict
from asf_search import ASFSession
from asf_search.ASFProduct import ASFProduct
from asf_search.ASFSearchOptions import ASFSearchOptions
from asf_search.Products import S1Product
from asf_search.CMR.translate import try_parse_float
Expand Down Expand Up @@ -48,4 +49,13 @@ def get_default_baseline_product_type() -> None:
"""
Returns the product type to search for when building a baseline stack.
"""
return None
return None

@staticmethod
def is_ARIAS1GUNWProduct(item: Dict) -> bool:
platform = ASFProduct.umm_get(item['umm'], 'Platforms', 0, 'ShortName')
if platform in ['SENTINEL-1A', 'SENTINEL-1B']:
asf_platform = ASFProduct.umm_get(item['umm'], 'AdditionalAttributes', ('Name', 'ASF_PLATFORM'), 'Values', 0)
return 'Sentinel-1 Interferogram' in asf_platform

return False
10 changes: 5 additions & 5 deletions asf_search/Products/NISARProduct.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Union
from typing import Dict, Tuple, Union
from asf_search import ASFSearchOptions, ASFSession, ASFStackableProduct
from asf_search.CMR.translate import try_parse_float, try_parse_int, try_round_float
from asf_search.constants import PRODUCT_TYPE
Expand Down Expand Up @@ -42,10 +42,10 @@ def get_stack_opts(self, opts: ASFSearchOptions = None) -> ASFSearchOptions:
"""
return None

def get_sort_keys(self):
def get_sort_keys(self) -> Tuple[str, str]:
keys = super().get_sort_keys()

if keys[0] is None:
return (self.properties.get('processingDate', ''), keys[1])
if keys[0] == '':
return (self._read_property('processingDate', ''), keys[1])

return keys
8 changes: 4 additions & 4 deletions asf_search/Products/OPERAS1Product.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict
from typing import Dict, Tuple
from asf_search import ASFSearchOptions, ASFSession
from asf_search.CMR.translate import try_parse_date
from asf_search.Products import S1Product
Expand Down Expand Up @@ -65,10 +65,10 @@ def get_stack_opts(self, opts: ASFSearchOptions = None) -> ASFSearchOptions:
"""
return None

def get_sort_keys(self):
def get_sort_keys(self) -> Tuple[str, str]:
keys = super().get_sort_keys()

if keys[0] is None:
keys = self.properties.get('validityStartDate'), keys[1]
if keys[0] == '':
return (self._read_property('validityStartDate', ''), keys[1])

return keys
2 changes: 1 addition & 1 deletion asf_search/WKT/RepairEntry.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ def __init__(self, report_type: str, report: str) -> None:
self.report = report

def __str__(self) -> str:
return f'{self.report_type}\n\t{self.report}'
return f"{self.report_type}: {self.report}"
6 changes: 3 additions & 3 deletions asf_search/WKT/validate_wkt.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from asf_search.exceptions import ASFWKTError


def validate_wkt(aoi: Union[str, BaseGeometry]) -> Tuple[BaseGeometry, List[RepairEntry]]:
def validate_wkt(aoi: Union[str, BaseGeometry]) -> Tuple[BaseGeometry, BaseGeometry, List[RepairEntry]]:
"""
Param aoi: the WKT string or Shapely Geometry to validate and prepare for the CMR query
Validates the given area of interest, and returns a validated and simplified WKT string
Expand Down Expand Up @@ -52,7 +52,7 @@ def _search_wkt_prep(shape: BaseGeometry):
if isinstance(shape, Polygon):
return orient(Polygon(shape.exterior), sign=1.0)

def _simplify_geometry(geometry: BaseGeometry) -> Tuple[BaseGeometry, List[RepairEntry]]:
def _simplify_geometry(geometry: BaseGeometry) -> Tuple[BaseGeometry, BaseGeometry, List[RepairEntry]]:
"""
param geometry: AOI Shapely Geometry to be prepped for CMR
prepares geometry for CMR by:
Expand Down Expand Up @@ -165,7 +165,7 @@ def _counter_clockwise_reorientation(geometry: Union[Point, LineString, Polygon]
return reoriented, None


def _get_clamped_and_wrapped_geometry(shape: BaseGeometry) -> Tuple[BaseGeometry, List[RepairEntry]]:
def _get_clamped_and_wrapped_geometry(shape: BaseGeometry) -> Tuple[BaseGeometry, BaseGeometry, List[RepairEntry]]:
"""
param geometry: Shapely geometry to clamp
Clamps geometry to +/-90 latitude and wraps longitude +/-180
Expand Down
12 changes: 6 additions & 6 deletions asf_search/baseline/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List

import numpy as np
from dateutil.parser import parse
from ciso8601 import parse_datetime

from asf_search import ASFProduct
# WGS84 constants
Expand All @@ -23,17 +23,17 @@ def calculate_perpendicular_baselines(reference: str, stack: List[ASFProduct]):
baselineProperties['noStateVectors'] = True
continue

asc_node_time = parse(baselineProperties['ascendingNodeTime']).timestamp()
asc_node_time = parse_datetime(baselineProperties['ascendingNodeTime']).timestamp()

start = parse(product.properties['startTime']).timestamp()
end = parse(product.properties['stopTime']).timestamp()
start = parse_datetime(product.properties['startTime']).timestamp()
end = parse_datetime(product.properties['stopTime']).timestamp()
center = start + ((end - start) / 2)
baselineProperties['relative_start_time'] = start - asc_node_time
baselineProperties['relative_center_time'] = center - asc_node_time
baselineProperties['relative_end_time'] = end - asc_node_time

t_pre = parse(positionProperties['prePositionTime']).timestamp()
t_post = parse(positionProperties['postPositionTime']).timestamp()
t_pre = parse_datetime(positionProperties['prePositionTime']).timestamp()
t_post = parse_datetime(positionProperties['postPositionTime']).timestamp()
product.baseline['relative_sv_pre_time'] = t_pre - asc_node_time
product.baseline['relative_sv_post_time'] = t_post - asc_node_time

Expand Down
6 changes: 3 additions & 3 deletions asf_search/baseline/stack.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from typing import Tuple, List
from dateutil.parser import parse
from ciso8601 import parse_datetime
import pytz

from .calc import calculate_perpendicular_baselines
Expand Down Expand Up @@ -66,12 +66,12 @@ def calculate_temporal_baselines(reference: ASFProduct, stack: ASFSearchResults)
:param stack: The stack to operate on.
:return: None, as the operation occurs in-place on the stack provided.
"""
reference_time = parse(reference.properties['startTime'])
reference_time = parse_datetime(reference.properties['startTime'])
if reference_time.tzinfo is None:
reference_time = pytz.utc.localize(reference_time)

for secondary in stack:
secondary_time = parse(secondary.properties['startTime'])
secondary_time = parse_datetime(secondary.properties['startTime'])
if secondary_time.tzinfo is None:
secondary_time = pytz.utc.localize(secondary_time)
secondary.properties['temporalBaseline'] = (secondary_time.date() - reference_time.date()).days
Expand Down
6 changes: 5 additions & 1 deletion asf_search/search/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ def search(
perf = time.time()

results.raise_if_incomplete()
results.sort(key=lambda p: p.get_sort_keys(), reverse=True)

try:
results.sort(key=lambda p: p.get_sort_keys(), reverse=True)
except TypeError as exc:
ASF_LOGGER.warning(f"Failed to sort final results, leaving results unsorted. Reason: {exc}")

return results
Loading

0 comments on commit 4923c3d

Please sign in to comment.