[Git][debian-gis-team/asf-search][upstream] New upstream version 12.3.0

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sat Aug 8 17:11:05 BST 2026



Antonio Valentino pushed to branch upstream at Debian GIS Project / asf-search


Commits:
5ab9bd0a by Antonio Valentino at 2026-08-08T16:03:02+00:00
New upstream version 12.3.0
- - - - -


27 changed files:

- .github/workflows/lint.yml
- .github/workflows/run-pytest-authenticated.yml
- CHANGELOG.md
- asf_search/ASFSearchOptions/validator_map.py
- asf_search/CMR/datasets.py
- asf_search/CMR/subquery.py
- asf_search/Pair.py
- + asf_search/S1MultiBurstProduct.py
- asf_search/SBASNetwork.py
- asf_search/Stack.py
- asf_search/__init__.py
- + asf_search/constants/MATURITIES.py
- asf_search/constants/__init__.py
- asf_search/search/geo_search.py
- asf_search/search/search.py
- asf_search/search/search_count.py
- asf_search/search/search_generator.py
- conftest.py
- examples/SBASNetwork.ipynb
- + examples/SBASNetwork_S1-Multiburst.ipynb
- examples/Stack.ipynb
- pyproject.toml
- setup.py
- + tests/S1MultiBurstProduct/test_S1MultiBurstProduct.py
- tests/SBASNetwork/test_SBASNetwork.py
- tests/yml_tests/test_ASFSearchResults.yml
- tests/yml_tests/test_known_bugs.yml


Changes:

=====================================
.github/workflows/lint.yml
=====================================
@@ -9,4 +9,4 @@ jobs:
       - uses: astral-sh/ruff-action at v3
         with:
           src: './asf_search'
-          version-file: 'pyproject.toml'
\ No newline at end of file
+          version: '0.15.22'
\ No newline at end of file


=====================================
.github/workflows/run-pytest-authenticated.yml
=====================================
@@ -8,7 +8,6 @@ on:
     branches:
       - master
 
-
 jobs:
   run-tests:
     runs-on: ubuntu-latest


=====================================
CHANGELOG.md
=====================================
@@ -25,6 +25,22 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 -
 
 -->
+=======
+## [v12.3.0](https://github.com/asfadmin/Discovery-asf_search/compare/v12.2.3...v12.3.0)
+### Added
+- Adds Sentinel-1 multi-burst support to `SBASNetwork` class
+  - Adds multi-burst supporting classes:
+    - S1MultiBurstSceneIDPair
+    - S1MultiBurst
+    - S1MultiBurstGroup
+    - S1MultiBurstProduct
+  - For usage examples, see `examples/SBASNetwork_S1-Multiburst.ipynb`
+- Updates `SBASNetwork.add_pairs()` and `SABANetork.remove_pairs()` to take either Pair objects or tuples of date pair strings.
+- Parameter for filtering NISAR products based off of data maturity.
+```python
+asf.search_count(dataMaturity=asf.constants.MATURITIES.BETA, dataset=asf.constants.DATASET.NISAR)
+```
+
 ------
 ## [v12.2.3](https://github.com/asfadmin/Discovery-asf_search/compare/v12.2.2...v12.2.3)
 ### Added
@@ -38,7 +54,7 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 ------
 ## [v12.2.1](https://github.com/asfadmin/Discovery-asf_search/compare/v12.2.0...v12.2.1)
 ### Fixed
-- Fixes optinal dependency `pandas` causing import error in `SBASNetwork` class
+- Fixes optional dependency `pandas` causing import error in `SBASNetwork` class
 
 ------
 ## [v12.2.0](https://github.com/asfadmin/Discovery-asf_search/compare/v12.1.1...v12.2.0)
@@ -64,6 +80,11 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 - `OPERA_L3_DIST-ALERT-S1_V1` shortname/collections added
 - Add `NISAR_EA` shortname/collections to NISAR dataset (available to authorized users)
 
+------
+## [v12.0.8](https://github.com/asfadmin/Discovery-asf_search/compare/v12.0.7...v12.0.8)
+### Added
+- `Sentinel-1D` constants and search support
+
 ------
 ## [v12.0.7](https://github.com/asfadmin/Discovery-asf_search/compare/v12.0.6...v12.0.7)
 ### Added


=====================================
asf_search/ASFSearchOptions/validator_map.py
=====================================
@@ -44,6 +44,7 @@ validator_map = {
     'bbox': parse_bbox,
     'beamMode': parse_string_list,
     'beamSwath': parse_string_list,
+    'dataMaturity': parse_string_list,
     'campaign': parse_string,
     'circle': parse_circle,
     'linestring': parse_linestring,


=====================================
asf_search/CMR/datasets.py
=====================================
@@ -1994,6 +1994,24 @@ collections_by_processing_level = {
 
 # Helper Methods
 
+def get_nisar_collection_by_maturity(maturities: List[str]):
+    collections = {
+        'BETA': [],
+        'PROVISIONAL': [],
+        'VALIDATED': []
+    }
+
+    for short_name, maturity_collection in dataset_collections['NISAR'].items():
+        if 'BETA' in short_name:
+            collections['BETA'].extend(maturity_collection)
+        elif 'PROVISIONAL' in short_name:
+            collections['PROVISIONAL'].extend(maturity_collection)
+        elif short_name.endswith('V1'):
+            collections['VALIDATED'].extend(maturity_collection)
+    try:
+        return sum([collections[maturity.upper()] for maturity in maturities],[])
+    except KeyError:
+        raise ValueError(f'Invalid maturity option provided valid options are: {", ".join(collections.keys())}' )
 
 def get_concept_id_alias(param_list: List[str], collections_dict: dict) -> List[str]:
     """


=====================================
asf_search/CMR/subquery.py
=====================================
@@ -10,6 +10,7 @@ from asf_search.CMR.datasets import (
     collections_per_platform,
     get_concept_id_alias,
     get_dataset_concept_ids,
+    get_nisar_collection_by_maturity
 )
 from numpy import intersect1d, union1d
 
@@ -128,12 +129,13 @@ def get_keyword_concept_ids(params: dict, use_collection_alias: bool = True, inc
         if alias_processing_levels and alias_platforms and not len(collections):
             # processingLevel isn't part of the given platform. Drop aliased keywords so we don't search on zero collections
             aliased_keywords = []
-            
+
     if 'dataset' in params.keys():
         aliased_keywords.append('dataset')
         dataset_concept_ids = get_dataset_concept_ids(params.get('dataset'))
         collections = _get_intersection(dataset_concept_ids, collections)
-
+    if 'dataMaturity' in params.keys():
+        collections = _get_intersection(get_nisar_collection_by_maturity(params.get('dataMaturity')), collections)
     return collections, aliased_keywords
 
 


=====================================
asf_search/Pair.py
=====================================
@@ -1,7 +1,9 @@
 import importlib.util
 import math
+from typing import Union
 
 from .ASFProduct import ASFProduct
+from .S1MultiBurstProduct import S1MultiBurstProduct
 from .baseline import calculate_perpendicular_baselines
 from .exceptions import CoherenceEstimationError
 import pytz
@@ -27,8 +29,7 @@ except ImportError:
 
 class Pair:
     """
-    A Pair is comprised of a reference scene and a secondary scene. These scenes typically intersect geographically,
-    but that is not a requirement. When a pair is created, its perpendicular and temporal baselines are calculated
+    A Pair is comprised of a reference scene and a secondary scene. When a pair is created, its perpendicular and temporal baselines are calculated
     and stored in the self.perpendicular_baseline and self.temporal_baseline member variables.
 
     Two pairs are equivalent if they have matching reference and secondary dates
@@ -36,19 +37,30 @@ class Pair:
     ref: ASFProduct for reference scene
     sec: ASFProduct for secondary scene
     """
-    def __init__(self, ref: ASFProduct, sec: ASFProduct):
-        self.ref = ref
-        self.sec = sec
-        self.id = (ref.properties['sceneName'], sec.properties['sceneName'])
-
-        self.perpendicular_baseline = calculate_perpendicular_baselines(
-            ref.properties['sceneName'], 
-            [sec, ref])[0].properties['perpendicularBaseline']
+    def __init__(self, 
+            ref: Union[ASFProduct, S1MultiBurstProduct], 
+            sec: Union[ASFProduct, S1MultiBurstProduct]):
+        
 
-        self.ref_time = parse_datetime(ref.properties["startTime"])
+        self.ref = ref
+        self.sec = sec          
+        
+        self.id = (self.ref.properties['sceneName'], self.sec.properties['sceneName'])
+        if isinstance(ref, ASFProduct) and isinstance(sec, ASFProduct):
+            self.perpendicular_baseline = calculate_perpendicular_baselines(
+                self.ref.properties['sceneName'], 
+                [self.sec, self.ref])[0].properties['perpendicularBaseline']
+            
+        elif isinstance(ref, S1MultiBurstProduct) and isinstance(sec, S1MultiBurstProduct):
+            perpendicular_baselines = [calculate_perpendicular_baselines(ref.properties['sceneName'], [sec, ref])[0].properties['perpendicularBaseline'] 
+                                       for ref, sec in zip(self.ref.geo_reference_bursts, self.sec.geo_reference_bursts)]
+            # Use the largest burst perpendicular baseline to represent the S1MultiBurstProduct
+            self.perpendicular_baseline = max((x for x in perpendicular_baselines if x is not None), default=None)
+
+        self.ref_time = parse_datetime(self.ref.properties["startTime"])
         if self.ref_time.tzinfo is None:
             self.ref_time = pytz.utc.localize(self.ref_time)
-        self.sec_time = parse_datetime(sec.properties["startTime"])
+        self.sec_time = parse_datetime(self.sec.properties["startTime"])
         if self.sec_time.tzinfo is None:
             self.sec_time = pytz.utc.localize(self.sec_time)
 


=====================================
asf_search/S1MultiBurstProduct.py
=====================================
@@ -0,0 +1,294 @@
+from collections import defaultdict
+from dataclasses import dataclass
+from datetime import timedelta
+from typing import List, Literal, Optional
+
+from asf_search import ASF_LOGGER
+from .ASFProduct import ASFProduct
+from asf_search.ASFSearchOptions import ASFSearchOptions
+from .search import geo_search, search
+from .ASFSearchResults import ASFSearchResults
+from .baseline import get_baseline_from_stack
+
+try:
+    from ciso8601 import parse_datetime
+except ImportError:
+    from dateutil.parser import parse as parse_datetime
+
+Subswath = Literal["IW1", "IW2", "IW3"]
+
+
+ at dataclass(frozen=True)
+class S1MultiBurst:
+    """
+    Represents a Sentinel-1 burst and which subswaths it should include.
+    """
+    relative_burst_id: str
+    subswaths: tuple[Subswath, ...]
+
+
+ at dataclass(frozen=True)
+class S1MultiBurstGroup:
+    """
+    Represents a group of S1MultiBursts to be processed together.
+    """
+    bursts: list[S1MultiBurst]
+
+    def __post_init__(self):
+        self.check_group_validity()
+
+    def check_group_validity(self) -> None:
+        """Check that the burst group is valid.
+
+        Temporarily borrowed (edited) from burst2safe, until validation logic moves to its own package.
+        https://github.com/ASFHyP3/burst2safe/blob/develop/src/burst2safe/safe.py#L83
+
+        A valid burst group must:
+        - Have the same acquisition mode
+        - Be from the same absolute orbit
+        - Be contiguous in time and space
+
+        Args:
+            burst_infos: A list of BurstInfo objects
+        """
+        group_size = sum(len(burst.subswaths) for burst in self.bursts)
+        if group_size > 15:
+            raise ValueError("An S1MultiBurstGroup may include no more than 15 burst/subswath combinations")
+
+        swaths = sorted(list(set([int(subswath[2]) for burst in self.bursts for subswath in burst.subswaths])))
+
+        burst_range: dict[str, tuple[int, int]] = {}
+        for swath in swaths:
+            burst_subset = [burst for burst in self.bursts if f"IW{swath}" in burst.subswaths]
+
+
+            if not burst_subset:
+                burst_range[swath] = (0, 0)
+                continue
+
+            self.check_burst_group_validity(burst_subset, swath)
+
+            burst_ids = [int(info.relative_burst_id.split("_")[1]) for info in burst_subset]
+            burst_range[swath] = (min(burst_ids), max(burst_ids))
+
+        if len(swaths) <= 1:
+            return
+
+        for swath1, swath2 in zip(swaths, swaths[1:]):
+            min_diff = burst_range[swath1][0] - burst_range[swath2][0]
+            if abs(min_diff) > 1:
+                raise ValueError(f'Products from swaths {swath1} and {swath2} do not overlap')
+
+            max_diff = burst_range[swath1][1] - burst_range[swath2][1]
+            if abs(max_diff) > 1:
+                raise ValueError(f'Products from swaths {swath1} and {swath2} do not overlap')
+                     
+    def check_burst_group_validity(self, burst_subset, swath):
+        """Check that the burst group is valid.
+
+        Temporarily borrowed (edited) from burst2safe, until validation logic moves to its own package.
+        https://github.com/ASFHyP3/burst2safe/blob/develop/src/burst2safe/safe.py#L83
+
+        The burst group must:
+        - Not contain duplicate granules
+        - Have the same absolute orbit
+        - Have consecutive burst IDs
+
+        Args:
+            burst_infos: A list of BurstInfo objects
+        """
+        granules = [f"{burst.relative_burst_id}_IW{swath}" for burst in burst_subset]
+
+        duplicates = list(set([x for x in granules if granules.count(x) > 1]))
+        if duplicates:
+            raise ValueError(f'Found duplicate granules: {duplicates}.')
+
+        orbits = set([granule.split("_")[0] for granule in granules])
+        if len(orbits) != 1:
+            raise ValueError(f'All bursts must have the same absolute orbit. Found: {orbits}.')
+
+        burst_ids = [int(burst.split('_')[1]) for burst in granules]
+        burst_ids.sort()
+        if burst_ids != list(range(min(burst_ids), max(burst_ids) + 1)):
+            raise ValueError(f'All bursts must have consecutive burst IDs. Found: {burst_ids}.')
+
+
+class S1MultiBurstProduct():
+    """
+    Represents a group of Sentinel-1 bursts. 
+     
+    member variables:
+    - multiburst_group (S1MultiBurstGroup): Represents the geographical area with burst and subswath IDs
+    - geo_reference_bursts (list of ASFProducts): A list of geographic reference burst ASFProducts
+
+    An S1MultiBurstProduct can be initialized from either a multiburst_group and start_date or a geo_reference_bursts list.
+    When creating a S1MultiBurstProduct from a S1MultiBurstGroup, a geographic search is performed to identify geographic
+    reference products with acquisition dates as near as possible to the start_date. 
+
+    ### multiburst_group example ###
+
+    multiburst_group = asf.S1MultiBurstGroup(
+        bursts=[
+        S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+        S1MultiBurst("173_370306", ("IW1", "IW2", "IW3")),
+        S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+        ])
+
+    start_date = '2023-01-01'
+    multiburst = asf.S1MultiBurstProduct(multiburst_group, start_date)
+
+    ### geo_reference_bursts example ###
+
+    multiburst = asf.S1MultiBurstProduct([burst_product_1, burst_product_2, burst_product_3, ...])
+    
+    """
+    def __init__(
+        self,
+        multiburst_group: S1MultiBurstGroup = None,
+        start_date: str = None,
+        geo_reference_bursts: List[ASFProduct] = None   
+    ):
+        self.start_date=start_date
+        if not multiburst_group and not geo_reference_bursts:
+            raise ValueError("Provide multiburst_group or geo_reference_bursts")
+        
+        if not geo_reference_bursts:
+            self.geo_reference_bursts = self.identify_geo_reference_bursts(multiburst_group)
+        else:
+            self.geo_reference_bursts = geo_reference_bursts
+
+        if not multiburst_group:
+            self.multiburst_group = self.identify_multiburst_group(geo_reference_bursts)
+        else:
+            self.multiburst_group = multiburst_group
+
+        start_time = max([p.properties["startTime"] for p in self.geo_reference_bursts])
+        stop_time = max([p.properties["stopTime"] for p in self.geo_reference_bursts])
+        self.id = self._build_scene_name(start_time)
+        self.properties = {
+            "sceneName": self.id,
+            "startTime": start_time,
+            "stopTime": stop_time,
+        }
+
+    def __repr__(self) -> str:
+        return f"S1MultiBurstProduct({self.id})"
+
+    def __eq__(self, other):
+        if not isinstance(other, S1MultiBurstProduct):
+            return NotImplemented
+        return self.id == other.id
+    
+    def __hash__(self):
+        return hash(self.id)
+
+    def __len__(self):
+        return len(self.geo_reference_bursts)
+    
+    def identify_geo_reference_bursts(self, multiburst_group: S1MultiBurstGroup) -> List[ASFProduct]:
+        """
+        Returns one geographic reference product for each burst/subswath in multiburst_group,
+        from the earliest acquisition date with complete coverage.
+
+        There is no guarantee that the returned products will fall on self.start_date; they will
+        fall on the first available date on or after self.start_date.
+        """
+        relative_burst_ids = [
+            f"{burst.relative_burst_id}_{subswath}"
+            for burst in multiburst_group.bursts
+            for subswath in burst.subswaths
+        ]
+
+        results = geo_search(
+            fullBurstID=relative_burst_ids,
+            polarization="VV",
+            start=self.start_date,
+            end=str(
+                (parse_datetime(self.start_date) + timedelta(days=48)).date()
+            ),
+        )
+
+        products_by_date = {}
+
+        for result in results:
+            acquisition_date = parse_datetime(
+                result.properties["startTime"]
+            ).date()
+
+            full_burst_id = result.properties["burst"]["fullBurstID"]
+
+            products_by_date.setdefault(acquisition_date, {})[full_burst_id] = result
+
+        for acquisition_date in sorted(products_by_date):
+            products = products_by_date[acquisition_date]
+
+            if set(relative_burst_ids) <= products.keys():
+                return [products[burst_id] for burst_id in relative_burst_ids]
+
+        raise ValueError(
+            "No acquisition date contains all requested burst/subswath "
+            f"combinations: {relative_burst_ids}"
+        )
+
+    def identify_multiburst_group(self, geo_reference_bursts):
+        """
+        Returns an S1MultiBurstGroup corresponding to the list of geo_reference_bursts
+        """
+        relative_burst_ids = sorted(set([f'{p.properties["pathNumber"]}_{p.properties["burst"]["relativeBurstID"]}' for p in geo_reference_bursts]))
+        burst_group_dict = {burst_id: [] for burst_id in relative_burst_ids}
+        for p in geo_reference_bursts:
+            burst_id = f'{p.properties["pathNumber"]}_{p.properties["burst"]["relativeBurstID"]}'
+            burst_group_dict[burst_id].append(p.properties["burst"]["subswath"])
+
+        multibursts = [S1MultiBurst(k, v) for k, v in burst_group_dict.items()]
+        return S1MultiBurstGroup(bursts=multibursts)
+    
+    def _build_scene_name(self, start_time):
+        start_time = "".join(start_time.split("T")[0].split("-"))
+        id = ""
+        for multiburst in self.multiburst_group.bursts:
+            id = f"{id}_{multiburst.relative_burst_id}_IW"
+            for subswath in multiburst.subswaths:
+                id = f"{id}{subswath[2]}"
+        return f"{id[1:]}_{start_time}"
+
+    def stack(self, opts: Optional[ASFSearchOptions] = None) -> List[ASFSearchResults]:
+        """
+        Builds a baseline stack for each burst product in self.geo_reference_bursts.
+
+        opts: An ASFSearchOptions object describing the search parameters to be used.
+
+        Returns: A list of asf_search.ASFSearchResults, each containing the stack for 
+                 a burst ASFProduct in self.geo_reference_bursts, with the addition of baseline 
+                 values (temporal, perpendicular) attached to each burst ASFProduct.
+        """
+        full_burst_id_list = [burst.properties["burst"]["fullBurstID"] for burst in self.geo_reference_bursts]
+
+        stack_opts = self.geo_reference_bursts[0].get_stack_opts()
+        stack_opts.fullBurstID = full_burst_id_list
+
+        opts.merge_args(**dict(stack_opts))
+
+        combined_results = search(opts=opts)
+
+        grouped = defaultdict(list)
+
+        for product in combined_results:
+            full_burst_id = product.properties["burst"]["fullBurstID"]
+            grouped[full_burst_id].append(product)
+
+        split_results = {
+            burst_id: ASFSearchResults(products)
+            for burst_id, products in grouped.items()
+        }
+
+        stack_search_results = []
+
+        for p in self.geo_reference_bursts:
+            full_burst_id = p.properties["burst"]["fullBurstID"]
+            stack, _ = get_baseline_from_stack(reference=p, stack=split_results[full_burst_id])
+            stack.sort(key=lambda product: product.properties['temporalBaseline'])
+            stack_search_results.append(stack)
+
+        return stack_search_results
+    
\ No newline at end of file


=====================================
asf_search/SBASNetwork.py
=====================================
@@ -1,9 +1,12 @@
 from datetime import datetime, timedelta
+from itertools import combinations
 import importlib.util
 import numpy as np
-from typing import Optional, List, Dict, Tuple, Union
+from typing import Optional, List, Dict, Tuple, Union, NamedTuple
 
+from asf_search import ASF_LOGGER
 from .ASFProduct import ASFProduct
+from .S1MultiBurstProduct import S1MultiBurstProduct
 from .Pair import Pair
 from .Stack import Stack
 from .ASFSearchOptions import ASFSearchOptions
@@ -30,23 +33,37 @@ except ImportError:
     sample_colorscale = None
     pd = None
 
+class S1MultiBurstSceneIDPair(NamedTuple):
+    """
+    Defines a named tuple of lists of reference and secondary ASFProducts ID strings
+    for Pairs of S1MultiBurstProducts
+
+    This is useful when ordering S1 multi-burst interferograms from HyP3
+    """
+    reference: List[str]
+    secondary: List[str]
 class SBASNetwork(Stack):
     """
     SBASNetwork is a child class of Stack, used to create seasonal SBAS networks. It can be used
     to create multiannual networks connected with multiannual bridge pairs.
+
+    Construct using:
+    - SBASNetwork.from_geo_reference()
+    - SBASNetwork.from_search_results()
     """
     def __init__(
         self,
-        geo_reference: ASFProduct = None,
-        start_date: str = None,
-        end_date: str = None,
-        season: Tuple[int] = (1, 365),
-        perpendicular_baseline: Optional[int] = 400,
-        inseason_temporal_baseline: Optional[int] = 36,
-        bridge_year_threshold: Optional[int] = 1,
+        geo_reference: Union[ASFProduct, S1MultiBurstProduct],
+        *,
+        start_date: Optional[str] = None,
+        end_date: Optional[str] = None,
+        season: Tuple[int, int] = (1, 365),
+        perpendicular_baseline: int = 400,
+        inseason_temporal_baseline: int = 36,
+        bridge_year_threshold: int = 0,
         bridge_target_date: Optional[str] = None,
-        opts: Optional[ASFSearchOptions] = ASFSearchOptions(),
-        allow_missing_state_vectors: Optional[bool] = False
+        opts: Optional[ASFSearchOptions] = None,
+        allow_missing_state_vectors: bool = False
     ):
         """
         geo_reference: an ASFProduct used as a georeference scene for the network
@@ -55,65 +72,92 @@ class SBASNetwork(Stack):
         bridge_year_threshold: the number of year for which to allow multiannual bridging
         bridge_target_date: %m-%d string of the inseason bridge date to target. 
                             Reference scenes for bridge pairs are valid within an inseason_temporal_baseline of this date.
-        opts: ASFSearchOptions to limit the size of your SBASNetwork and define seasons
-              Recommended to include: "start", "end", "season"
+        opts:  Additional ASFSearchOptions used for the stack search. Explicit start_date and end_date arguments 
+               override corresponding values in opts. The opts season value is always overriden by the season argument.
         """
-        self.season = season
-        if bridge_target_date:
-            self.bridge_target_date = bridge_target_date
-        else:
-            self.bridge_target_date = self.season[0]
         self.start_date = start_date
         self.end_date = end_date
-        self.perpendicular_baseline = perpendicular_baseline
-        self.inseason_temporal_baseline = inseason_temporal_baseline
-        self.bridge_year_threshold = bridge_year_threshold
-        opts.merge_args(
-            start=self.start_date,
-            end=self.end_date,
-            season=self.season,
-        )
-        self.opts = opts
-        self.allow_missing_state_vectors = allow_missing_state_vectors
+        self._set_network_parameters(
+            season=season, 
+            perpendicular_baseline=perpendicular_baseline, 
+            inseason_temporal_baseline=inseason_temporal_baseline, 
+            bridge_year_threshold=bridge_year_threshold,
+            bridge_target_date=bridge_target_date,
+            allow_missing_state_vectors=allow_missing_state_vectors
+            )
 
-        self.temporal_baseline = (bridge_year_threshold * 365) + inseason_temporal_baseline
+        if opts is None:
+            opts = ASFSearchOptions()
+
+        merge_kwargs = {"season": self.season}
+
+        if self.start_date is not None:
+            merge_kwargs["start"] = self.start_date
+
+        if self.end_date is not None:
+            merge_kwargs["end"] = self.end_date
+
+        opts.merge_args(**merge_kwargs)
+        self.opts = opts
 
         super().__init__(
             geo_reference=geo_reference, 
             opts=self.opts,
             allow_missing_state_vectors = self.allow_missing_state_vectors
             )
-
         self._build_sbas_network()
+        self.geo_reference = self.subset_stack[0].ref # must reset here because ref object is replaced with a duplicate when building full stack
+
+    @classmethod
+    def from_geo_reference(
+        cls,
+        geo_reference: Union[ASFProduct, S1MultiBurstProduct],
+        **kwargs,
+    ):
+        """
+        Class constructor using a geo_reference ASFProduct or S1MultiBurstProduct
+        """
+        if not isinstance(geo_reference, (ASFProduct, S1MultiBurstProduct)):
+            raise TypeError(
+                f"Unsupported geo_reference type: {type(geo_reference).__name__}"
+            )
+
+        return cls(geo_reference=geo_reference, **kwargs)
 
     @classmethod
     def from_search_results(
         cls,
         stack_search_results: ASFSearchResults,
-        season: Tuple[int] = (1, 365),
-        perpendicular_baseline: Optional[int] = 400,
-        inseason_temporal_baseline: Optional[int] = 36,
-        bridge_year_threshold: Optional[int] = 1,
+        season: Tuple[int, int] = (1, 365),
+        perpendicular_baseline: int = 400,
+        inseason_temporal_baseline: int = 36,
+        bridge_year_threshold: int = 1,
         bridge_target_date: Optional[str] = None,
-        allow_missing_state_vectors: Optional[bool] = False
+        allow_missing_state_vectors: bool = False
     ):
         """
-        Alternate class method constructor using ASFSearchResults instead of a single geo_reference.
+        Class constructor using ASFSearchResults instead of a single geo_reference.
         """
-        obj = cls.__new__(cls)
+        if not stack_search_results:
+            raise ValueError("stack_search_results cannot be empty")
 
-        obj.season = season
-        if bridge_target_date:
-            obj.bridge_target_date = bridge_target_date
-        else:
-            obj.bridge_target_date = obj.season[0]
+        # Intentionally bypass __init__ and Stack.__init__:
+        # search results are already available, so no stack search
+        # or ASFSearchOptions are needed
+        obj = cls.__new__(cls)
         dates = [parse_datetime(product.properties["startTime"]).date() for product in stack_search_results]
         obj.start_date = str(min(dates))
         obj.end_date = str(max(dates))
-        obj.perpendicular_baseline = perpendicular_baseline
-        obj.inseason_temporal_baseline = inseason_temporal_baseline
-        obj.bridge_year_threshold = bridge_year_threshold
-        obj.allow_missing_state_vectors = allow_missing_state_vectors
+
+        obj._set_network_parameters(
+            season=season, 
+            perpendicular_baseline=perpendicular_baseline, 
+            inseason_temporal_baseline=inseason_temporal_baseline, 
+            bridge_year_threshold=bridge_year_threshold,
+            bridge_target_date=bridge_target_date,
+            allow_missing_state_vectors=allow_missing_state_vectors
+            )
+        
         obj.full_stack = obj._build_full_stack(stack_search_results)
         obj._remove_list = []
         obj.subset_stack = obj._get_subset_stack()
@@ -123,7 +167,52 @@ class SBASNetwork(Stack):
         obj._build_sbas_network()
 
         return obj
+    
+    def _set_network_parameters(
+        self,
+        season: Tuple[int, int],
+        perpendicular_baseline: int,
+        inseason_temporal_baseline: int,
+        bridge_year_threshold: int,
+        bridge_target_date: Optional[str],
+        allow_missing_state_vectors: bool,
+    ) -> None:
+        self.season = season
+        if bridge_target_date is not None:
+            self.bridge_target_date = bridge_target_date
+        elif self.start_date is not None:
+            year = parse_datetime(self.start_date).year
+            self.bridge_target_date = datetime.strptime(
+                f"{year}-{season[0]}",
+                "%Y-%j",
+            ).strftime("%m-%d")
+        else:
+            raise ValueError(
+                "start_date is required when bridge_target_date is not provided"
+    )
 
+        self.perpendicular_baseline = perpendicular_baseline
+        self.inseason_temporal_baseline = inseason_temporal_baseline
+        self.bridge_year_threshold = bridge_year_threshold
+        self.allow_missing_state_vectors = allow_missing_state_vectors
+        self.temporal_baseline = (
+            bridge_year_threshold * 365
+            + inseason_temporal_baseline
+        )
+    
+    def _full_stack_pair_is_valid(self, pair: Pair) -> bool:
+        perpendicular_baseline = pair.perpendicular_baseline
+
+        if perpendicular_baseline is None and not self.allow_missing_state_vectors:
+            return False
+
+        if perpendicular_baseline is not None and abs(perpendicular_baseline) > self.perpendicular_baseline:
+            return False
+
+        maximum_temporal_baseline = self.inseason_temporal_baseline + self.bridge_year_threshold * 365
+
+        return pair.temporal_baseline.days <= maximum_temporal_baseline
+       
     def _build_full_stack(self, stack_search_results: Optional[ASFSearchResults]=None) -> List[Pair]:
         """
         Create self._full_stack, which involves performing a stack search
@@ -131,35 +220,45 @@ class SBASNetwork(Stack):
 
         Overrides Stack._build_full_stack() to naively exclude pairs based on self.perpendicular_baseline,
         self.inseason_temporal_baseline, and self.bridge_year_threshold. This is done for efficiency, to
-        avoid build massive full_stacks. More targeted occures in self._build_sbas_network()
+        avoid build massive full_stacks. More targeted filtering occures in self._build_sbas_network()
 
         stack_search_results: (Optional) ASFSearchResults from an ASFProduct.stack search
 
         Returns: A list of pairs filtered by baselines, and including every possible bridge pair within 
-                 self.bridge_year_threshold
+                    self.bridge_year_threshold
         """
-        if stack_search_results is None: 
+        if stack_search_results is None:
             stack_search_results = self.geo_reference.stack(opts=self.opts)
 
-        if self.allow_missing_state_vectors:
-             full_stack = [
-                Pair(p1, p2)
-                for i, p1 in enumerate(stack_search_results)
-                for p2 in stack_search_results[i+1:]
-                if (Pair(p1, p2).perpendicular_baseline is None
-                or Pair(p1, p2).perpendicular_baseline <= self.perpendicular_baseline)
-                and Pair(p1, p2).temporal_baseline.days <= self.inseason_temporal_baseline + (self.bridge_year_threshold * 365)
-            ]
-        else:
-            full_stack = [
-                Pair(p1, p2)
-                for i, p1 in enumerate(stack_search_results)
-                for p2 in stack_search_results[i+1:]
-                if Pair(p1, p2).perpendicular_baseline is not None
-                and Pair(p1, p2).perpendicular_baseline <= self.perpendicular_baseline
-                and Pair(p1, p2).temporal_baseline.days <= self.inseason_temporal_baseline + (self.bridge_year_threshold * 365)
-            ]
-        return full_stack
+            if isinstance(self.geo_reference, S1MultiBurstProduct):
+                multiburst_dict = {}
+                for results in stack_search_results:
+                    for p1, p2 in combinations(results, 2):
+                        key = (parse_datetime(p1.properties["startTime"]).date(), parse_datetime(p2.properties["startTime"]).date())
+                        if self._full_stack_pair_is_valid(Pair(p1, p2)):
+                                multiburst_dict.setdefault(key, [[], []])
+                                multiburst_dict[key][0].append(p1)
+                                multiburst_dict[key][1].append(p2) 
+                
+                full_stack = []
+                for product_lists in multiburst_dict.values():
+                    # skip pairs that don't contain a product for every burst/subswath
+                    if len(product_lists[0]) == len(self.geo_reference):
+                        pair = Pair(
+                            S1MultiBurstProduct(geo_reference_bursts=product_lists[0]), 
+                            S1MultiBurstProduct(geo_reference_bursts=product_lists[1])
+                            )
+                        full_stack.append(pair)
+                return self._sort_pair_list(full_stack)
+    
+        full_stack = []
+        for p1, p2 in combinations(stack_search_results, 2):
+            pair = Pair(p1, p2)
+            if self._full_stack_pair_is_valid(pair):
+                full_stack.append(pair)
+
+        return self._sort_pair_list(full_stack)
+
 
     def _is_valid_bridge_pair(self, pair: Pair) -> bool:
         """
@@ -180,7 +279,6 @@ class SBASNetwork(Stack):
                 datetime.strptime(f"{self.bridge_target_date}-{pair.ref_time.year}", "%m-%d-%Y") 
                 - pair.ref_time.replace(tzinfo=None)).days
             )
-        
         half_inseason_temporal_baseline = self.inseason_temporal_baseline / 2
 
         # Create lists of valid secondary scene date ranges.
@@ -207,6 +305,35 @@ class SBASNetwork(Stack):
                 remove_list.append(pair)
         self.remove_list = remove_list
 
+    def get_scene_ids(
+            self,
+            pair_list: Optional[List[Pair]] = None
+            ) -> Union[List[Tuple[List[str], List[str]]], List[Tuple[str, str]]]:
+        """
+        Overrides Stack.get_scene_ids
+
+        Provides scene names for all asf_search.ASFProducts in a list of Pairs.
+        Useful when ordering pair-based products from ASF HyP3 On-Demand Processing.
+
+        If no pair_list is passed, defaults to the largest connected substack
+
+        pair_list: (Optional) A list of `Pair`s for which to retrieve scene IDs.
+        
+        Returns: If the SBASNetwork is comprised of S1MultiBurstProducts, returns a list of tuples of lists. 
+                 Each tuple contains a list of reference scene IDs and a list of secondary scene IDs for a 
+                 Pair of S1MultiBurstProducts. If not an S1MultiBurstProducts SBASNetwork, defaults 
+                 to using Stack.get_scene_ids
+        """
+        if pair_list is None:
+            pair_list = max(self.connected_substacks, key=len)
+
+        if pair_list and isinstance(pair_list[0].ref, S1MultiBurstProduct):
+            return [S1MultiBurstSceneIDPair([p.properties["sceneName"] for p in pair.ref.geo_reference_bursts],
+                     [p.properties["sceneName"] for p in pair.sec.geo_reference_bursts])
+                     for pair in pair_list]
+
+        return super().get_scene_ids(pair_list)
+
     def add_digraph_edge_traces(self, digraph, largest_network, pair_lists, node_positions):
         """
         Used for SBASNetwork plotting. Adds networkx traces to graph edges
@@ -397,7 +524,7 @@ class SBASNetwork(Stack):
             )
         return plot_header_text
 
-    def plot(self, pair_list: Optional[Union[List[Pair], None]] = None):
+    def plot(self, pair_list: Optional[List[Pair]] = None):
         """
         Plot the SBAS network(s). Accepts a pair_list or a list of pair_lists.
         The largest network is plotted in blue; others are plotted in distinct colors.
@@ -431,7 +558,7 @@ class SBASNetwork(Stack):
         edge_traces = self.add_digraph_edge_traces(G, largest_network, pair_lists, (node_x_positions, node_y_positions))
         node_traces, unused_slcs_traces = self.add_digraph_node_traces(G, plot_geo_ref, pair_lists, (node_x_positions, node_y_positions))
 
-        start_date, end_date = get_pair_lists_date_range(pair_lists)
+        start_date, end_date = get_pair_lists_date_range([self.full_stack])
         date_range = (
             pd.date_range(
                 start=start_date,
@@ -476,26 +603,6 @@ class SBASNetwork(Stack):
         )
         fig.show()
 
-    def get_pair_from_dates(self, pair_list: List[Pair],
-                                ref_date: datetime.date, sec_date: datetime.date) -> Pair:
-        """
-        This convenience method allows for the retrieval of a Pair object from a list
-        of Pairs by reference and secondary date. This is usefull when identifying a 
-        Pair object for removal or addition to an SBASNetwork.subset_stack
-
-        This method is included in SBASNetwork and not Stack because we only care about
-        dates in SBASNetwork and Stack is aware of datetimes.
-
-        pair_list: a list of Pairs from which to find a Pair object
-        ref_date: a datetime.date of a target Pair's reference scene
-        sec_date: a datetime.date of a target Pair's secondary scene
-
-        Returns: A Pair with a corresponding ref_date and sec_date
-        """
-        for pair in pair_list:
-            if pair.ref_time.date() == ref_date and pair.sec_time.date() == sec_date:
-                return pair
-
 def get_n_colors(n: int, colorscale: str="Rainbow", alpha: float=0.4) -> List[str]:
     """
     Used for SBASNetwork plotting. Creates an n-length list of rgba colors from a plotly colorscale
@@ -613,4 +720,4 @@ def julian_to_month_day(ref_product: ASFProduct, julian_tuple: Tuple[int, int])
     for day in julian_tuple:
         date = datetime(year, 1, 1) + timedelta(days=day - 1)
         month_day.append(date.strftime("%m-%d"))
-    return tuple(month_day)
\ No newline at end of file
+    return tuple(month_day)        


=====================================
asf_search/Stack.py
=====================================
@@ -1,14 +1,35 @@
 from collections import defaultdict, deque
 from copy import copy
-from typing import Optional, List, Tuple, Union
+from datetime import date, timedelta
+from typing import Optional, List, Tuple, Union, NamedTuple
 import warnings
 
+from .search import geo_search
 from .ASFProduct import ASFProduct
+from .S1MultiBurstProduct import S1MultiBurstProduct
 from .Pair import Pair
 from .ASFSearchOptions import ASFSearchOptions
 from .ASFSearchResults import ASFSearchResults
 from .warnings import PairNotInFullStackWarning
 
+try:
+    from ciso8601 import parse_datetime
+except ImportError:
+    from dateutil.parser import parse as parse_datetime
+
+DatePair = Tuple[str, str]
+PairInput = Union[Pair, DatePair]
+
+class SceneIDPair(NamedTuple):
+    """
+    Defines a named tuple of reference and secondary ASFProducts ID strings
+    for Pairs of ASFProducts
+
+    This is useful when ordering interferograms from HyP3
+    """
+    reference: str
+    secondary: str
+
 class Stack:
     """
     A Stack object contains 4+ lists of Pair objects. Each Pair contains a pair of asf_search.ASFProduct objects.
@@ -29,6 +50,24 @@ class Stack:
     - Stack.get_scene_ids(): A convenience method that returns a given list of Pairs as a list of tuples of asf_search.ASFProduct
       product IDs, which is useful when ordering on-demand processing via ASF's HyP3 or HyP3+ services.
     """
+
+
+    @property
+    def scene_ids(self) -> List[Tuple[str, str]]:
+        """
+        Provides scene names for all asf_search.ASFProducts in the largest connected substack
+        Useful when ordering pair-based products from ASF HyP3 On-Demand Processing.
+
+        Use the get_scene_ids method to get the IDs for other pair lists (full_stack, subset_stack, 
+        any network in connected_substacks, or remove_list)
+        
+        Returns:
+            A list tuples containing the reference and secondary scene names for each `Pair` in a `Pair` list
+        """
+        pair_list = max(self.connected_substacks, key=len)
+        return self.get_scene_ids(pair_list)
+    
+
     def __init__(
         self,
         geo_reference: ASFProduct,
@@ -92,18 +131,36 @@ class Stack:
         pairs: A list of Pairs to remove from self.subset_stack
         """
         # remove duplicates
-        self._remove_list = list(set(pairs))
+        self._remove_list = self._sort_pair_list(list(set(pairs)))
         self._update_stack()
 
-    def remove_pairs(self, pairs: Union[List[Pair], Pair]):
+    def remove_pairs(self, pairs: Union[List[Pair], Pair, Tuple[str], List[Tuple[str]]]):
         """
         Remove pairs from self.subset_stack, 
         i.e., add them to self._remove_list
 
+        Example calls:
+            my_stack.remove_pairs(my_pair)
+            my_stack.remove_pairs([my_pair_1, my_pair_2, ...])
+            my_stack.remove_pairs(("2024-10-20", "2024-11-02"))
+            my_stack.remove_pairs([("2024-10-20", "2024-11-02"), ("2024-10-26", "2024-11-02")])
+
         pairs: A Pair or list of Pairs to remove from self.subset_stack
+               or a Tuple of parsable date pair strings
         """
-        if isinstance(pairs, Pair):
+        if not isinstance(pairs, List):
             pairs = [pairs]
+        if isinstance(pairs[0], Pair):
+            if type(pairs[0].ref) is not type(self.full_stack[0].ref):
+                raise ValueError(
+                    (f"Can't remove Pairs of type {type(pairs[0].ref)} from a Stack"
+                     f"of Pairs of type {type(self.full_stack[0].ref)}"))
+        else:
+            pairs = [get_existing_pair_from_dates(
+                self.subset_stack,
+                parse_datetime(date_pair[0]).date(),
+                parse_datetime(date_pair[1]).date()) for
+                date_pair in pairs]
 
         for pair in pairs:
             if pair not in self._remove_list:
@@ -114,26 +171,136 @@ class Stack:
                     warnings.warn(PairNotInFullStackWarning(msg))
         self._update_stack()
 
-    def add_pairs(self, pairs: Union[List[Pair], Pair]):
+    def safe_pair_append(self, pair: Pair) -> None:
+        products_by_time = {
+            product_time: product
+            for existing_pair in self.full_stack
+            for product_time, product in (
+                (existing_pair.ref_time, existing_pair.ref),
+                (existing_pair.sec_time, existing_pair.sec),
+            )
+        }
+
+        ref = products_by_time.get(pair.ref_time, pair.ref)
+        sec = products_by_time.get(pair.sec_time, pair.sec)
+
+        self.full_stack.append(Pair(ref, sec))
+
+    def _validate_pair_type(self, pair: Pair) -> None:
+        expected_type = type(self.full_stack[0].ref)
+        actual_type = type(pair.ref)
+
+        if actual_type is not expected_type:
+            raise ValueError(
+                f"Can't add Pairs of type {actual_type} to a Stack "
+                f"of Pairs of type {expected_type}"
+            )
+        
+    def _pair_from_dates(self, date_pair: DatePair) -> Pair:
+        ref_date_string, sec_date_string = date_pair
+
+        ref_date = parse_datetime(ref_date_string).date()
+        sec_date = parse_datetime(sec_date_string).date()
+
+        try:
+            return get_existing_pair_from_dates(
+                self.remove_list,
+                ref_date,
+                sec_date,
+            )
+        except ValueError:
+            pass
+
+        stack_product = self.full_stack[0].ref
+
+        if isinstance(stack_product, S1MultiBurstProduct):
+            group = stack_product.multiburst_group
+
+            return Pair(
+                S1MultiBurstProduct(group, ref_date_string),
+                S1MultiBurstProduct(group, sec_date_string),
+            )
+
+        return Pair(
+            self._search_product_by_date(ref_date_string),
+            self._search_product_by_date(sec_date_string),
+        )
+    
+    def _search_product_by_date(self, date_string: str):
+        stack_product = self.full_stack[0].ref
+
+        end_date = str(
+            (parse_datetime(date_string) + timedelta(days=12)).date()
+        )
+
+        if stack_product.properties["processingLevel"] == "BURST":
+            results = geo_search(
+                fullBurstID=stack_product.properties["fullBurstID"],
+                start=date_string,
+                end=end_date,
+                processingLevel="BURST",
+            )
+        else:
+            results = geo_search(
+                asfFrame=stack_product.properties["frameNumber"],
+                relativeOrbit=stack_product.properties["pathNumber"],
+                start=date_string,
+                end=end_date,
+                processingLevel="SLC",
+            )
+
+        try:
+            return min(
+                results,
+                key=lambda product: product.properties["startTime"],
+            )
+        except ValueError:
+            raise ValueError(
+                f"No matching product found for {date_string}"
+            ) from None
+
+    def add_pairs(
+        self,
+        pairs: Union[PairInput, List[PairInput]],
+    ) -> None:
         """
-        Add pairs to self.subset_stack and, if necessary, to self.full_stack 
-        i.e., remove them from self._remove_list if present or else add them to self.full_stack 
+        Add pairs to subset_stack and, when necessary, to full_stack.
 
-        This allows for the addition of custom pairs that were not originally present
-        in self.full_stack
-
-        pairs: A Pair or list of Pairs to add to self.subset_stack
+        Date pairs are resolved from remove_list when possible. Otherwise,
+        the corresponding products are constructed or searched for.
         """
-        if isinstance(pairs, Pair):
+        if not isinstance(pairs, list):
             pairs = [pairs]
 
-        for pair in pairs:
+        if not pairs:
+            return
+        
+        contains_pairs = any(isinstance(item, Pair) for item in pairs)
+        if contains_pairs and not all(isinstance(item, Pair) for item in pairs):
+            raise TypeError("pairs must not mix Pair objects and date tuples")
+
+        resolved_pairs = []
+
+        for item in pairs:
+            if isinstance(item, Pair):
+                self._validate_pair_type(item)
+                resolved_pairs.append(item)
+            else:
+                resolved_pairs.append(self._pair_from_dates(item))
+
+        for pair in resolved_pairs:
             if pair in self._remove_list:
                 self._remove_list.remove(pair)
             else:
-                self.full_stack.append(pair)
+                self.safe_pair_append(pair)
+
         self._update_stack()
 
+    def _sort_pair_list(self, pair_list: List[Pair]) -> List[Pair]:
+        return sorted(
+            pair_list,
+            key=lambda item: item.id)
+
     def _build_full_stack(self, stack_search_results: Optional[ASFSearchResults]=None) -> List[Pair]:
         """
         Create self._full_stack, which involves performing a stack search
@@ -163,7 +330,9 @@ class Stack:
         Recalculate self.subset_stack and find its connected substacks.
         These two things should always happen together.
         """
-        self.subset_stack = self._get_subset_stack()
+        self.full_stack = self._sort_pair_list(self.full_stack)
+        self._remove_list = self._sort_pair_list(self._remove_list)
+        self.subset_stack = self._sort_pair_list(self._get_subset_stack())
         self.connected_substacks = self._find_connected_substacks()
 
     def _find_connected_substacks(self) -> List[List[Pair]]:
@@ -207,9 +376,10 @@ class Stack:
                             queue.append(neighbor)
                 component_pairs = [v for v in component_pairs.values()]
                 components.append(component_pairs)
-
+        components = [self._sort_pair_list(component) for component in components]
         return components
 
+
     def get_scene_ids(self, pair_list: Optional[List[Pair]] = None) -> List[Tuple[str, str]]:
         """
         Provides scene names for all asf_search.ASFProducts in a list of Pairs.
@@ -222,11 +392,29 @@ class Stack:
         Returns:
             A list tuples containing the reference and secondary scene names for each `Pair` in a `Pair` list
         """
-        if not pair_list:
+        if pair_list is None:
             pair_list = max(self.connected_substacks, key=len)
 
         return [
-            (pair.ref.properties["sceneName"], pair.sec.properties["sceneName"])
+            SceneIDPair(pair.ref.properties["sceneName"], pair.sec.properties["sceneName"])
             for pair in pair_list
             ]
-    
\ No newline at end of file
+    
+def get_existing_pair_from_dates(pair_list: List[Pair],
+                            ref_date: date, sec_date: date) -> Pair:
+    """
+    This convenience method allows for the retrieval of a Pair object from a list
+    of Pairs by reference and secondary date. This is usefull when identifying a 
+    Pair object for removal or addition to an SBASNetwork.subset_stack
+
+    pair_list: a list of Pairs from which to find a Pair object
+    ref_date: a datetime.date of a target Pair's reference scene
+    sec_date: a datetime.date of a target Pair's secondary scene
+
+    Returns: A Pair with a corresponding ref_date and sec_date
+    """
+    for pair in pair_list:
+        if pair.ref_time.date() == ref_date and pair.sec_time.date() == sec_date:
+            return pair
+    raise ValueError(f"No Pairs found corresponding to reference date: {ref_date}, secondary date: {sec_date}")
+            
\ No newline at end of file


=====================================
asf_search/__init__.py
=====================================
@@ -54,9 +54,10 @@ from .CMR import *  # noqa: F403 F401 E402
 from .baseline import *  # noqa: F403 F401 E402
 from .WKT import validate_wkt  # noqa: F401 E402
 from .export import *  # noqa: F403 F401 E402
-from .Pair import Pair  # noqa:  F401, E402
-from .Stack import Stack  # noqa:  F401, E402
-from .SBASNetwork import SBASNetwork # noqa:  F401, E402
+from .Pair import Pair  # noqa:  F401 E402
+from .Stack import *  # noqa:  F403 F401 E402
+from .SBASNetwork import * # noqa: F403 F401 E402
+from .S1MultiBurstProduct import * # noqa: F403 F401 E402
 from . import utils # noqa: F401, E402
 
 REPORT_ERRORS = True


=====================================
asf_search/constants/MATURITIES.py
=====================================
@@ -0,0 +1,3 @@
+BETA='BETA'
+PROVISIONAL='PROVISIONAL'
+VALIDATED='VALIDATED'


=====================================
asf_search/constants/__init__.py
=====================================
@@ -11,3 +11,4 @@ from .INTERNAL import *  # noqa: F403 F401
 from .DATASET import *  # noqa: F403 F401
 from .RANGE_BANDWIDTH import *  # noqa: F403 F401
 from .PRODUCTION_CONFIGURATION import * # noqa: F403 F401
+from .MATURITIES import * # noqa: F403 F401


=====================================
asf_search/search/geo_search.py
=====================================
@@ -17,6 +17,7 @@ def geo_search(
     beamMode: Union[str, Sequence[str]] = None,
     beamSwath: Union[str, Sequence[str]] = None,
     campaign: Union[str, Sequence[str]] = None,
+    dataMaturity: Union[Literal['BETA', 'PROVISIONAL', 'VALIDATED'], Sequence[Literal['BETA', 'PROVISIONAL', 'VALIDATED']]] = None,
     maxDoppler: float = None,
     minDoppler: float = None,
     end: Union[datetime.datetime, str] = None,
@@ -86,6 +87,8 @@ def geo_search(
     campaign:
         For UAVSAR and AIRSAR data collections only. Search by general location,
         site description, or data grouping as supplied by flight agency or project.
+    dataMaturity:
+        For NISAR data collections only. Determined by when products were processed
     maxDoppler:
         Doppler provides an indication of how much the look direction deviates
         from the ideal perpendicular flight direction acquisition.


=====================================
asf_search/search/search.py
=====================================
@@ -19,6 +19,7 @@ def search(
     beamMode: Union[str, Sequence[str]] = None,
     beamSwath: Union[str, Sequence[str]] = None,
     campaign: Union[str, Sequence[str]] = None,
+    dataMaturity: Union[Literal['BETA', 'PROVISIONAL', 'VALIDATED'], Sequence[Literal['BETA', 'PROVISIONAL', 'VALIDATED']]] = None,
     maxDoppler: float = None,
     minDoppler: float = None,
     end: Union[datetime.datetime, str] = None,
@@ -93,6 +94,8 @@ def search(
     campaign:
         For UAVSAR and AIRSAR data collections only. Search by general location,
         site description, or data grouping as supplied by flight agency or project.
+    dataMaturity:
+        For NISAR data collections only. Determined by when products were processed
     maxDoppler:
         Doppler provides an indication of how much the look direction deviates
         from the ideal perpendicular flight direction acquisition.


=====================================
asf_search/search/search_count.py
=====================================
@@ -18,6 +18,7 @@ def search_count(
     beamMode: Union[str, Sequence[str]] = None,
     beamSwath: Union[str, Sequence[str]] = None,
     campaign: Union[str, Sequence[str]] = None,
+    dataMaturity: Union[Literal['BETA', 'PROVISIONAL', 'VALIDATED'], Sequence[Literal['BETA', 'PROVISIONAL', 'VALIDATED']]] = None,
     maxDoppler: float = None,
     minDoppler: float = None,
     end: Union[datetime.datetime, str] = None,


=====================================
asf_search/search/search_generator.py
=====================================
@@ -44,6 +44,7 @@ def search_generator(
     beamMode: Union[str, Sequence[str]] = None,
     beamSwath: Union[str, Sequence[str]] = None,
     campaign: Union[str, Sequence[str]] = None,
+    dataMaturity: Union[Literal['BETA', 'PROVISIONAL', 'VALIDATED'], Sequence[Literal['BETA', 'PROVISIONAL', 'VALIDATED']]] = None,
     maxDoppler: float = None,
     minDoppler: float = None,
     end: Union[datetime.datetime, str] = None,
@@ -117,6 +118,8 @@ def search_generator(
     campaign:
         For UAVSAR and AIRSAR data collections only. Search by general location,
         site description, or data grouping as supplied by flight agency or project.
+    dataMaturity:
+        For NISAR data collections only. Determined by when products were processed
     maxDoppler:
         Doppler provides an indication of how much the look direction deviates
         from the ideal perpendicular flight direction acquisition.


=====================================
conftest.py
=====================================
@@ -2,6 +2,7 @@ import argparse
 import pytest
 import os
 from asf_search.ASFSession import ASFSession
+from asf_search import S1MultiBurst, S1MultiBurstGroup, S1MultiBurstProduct
 from getpass import getpass
 
 def string_to_session(user_input: str) -> ASFSession:
@@ -61,3 +62,18 @@ def pytest_addoption(parser: pytest.Parser):
 
     parser.addoption("--auth_with_token", action="store", dest="authenticated_session", type=set_should_auth_session_with_token, default='FALSE',
                      help = "'auth_with_creds': Use EDL token to authenticate session for relevant tests")
+
+ at pytest.fixture(scope="module")
+def three_by_three_multiburst_group():
+    return S1MultiBurstGroup(
+    bursts=[
+    S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+    S1MultiBurst("173_370306", ("IW1", "IW2", "IW3")),
+    S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+    ]
+)
+
+ at pytest.fixture(scope="module")
+def three_by_three_multiburst_product(three_by_three_multiburst_group):
+    start_date = '2023-01-01'
+    return S1MultiBurstProduct(three_by_three_multiburst_group, start_date)


=====================================
examples/SBASNetwork.ipynb
=====================================
@@ -81,7 +81,7 @@
     "    start_date = '2023-01-01',\n",
     "    end_date = '2025-10-02',\n",
     "    season = get_julian_season(season),\n",
-    "    perpendicular_baseline=100, \n",
+    "    perpendicular_baseline=150, \n",
     "    inseason_temporal_baseline=24,\n",
     "    bridge_target_date='3-1',\n",
     "    bridge_year_threshold=2,\n",
@@ -131,6 +131,16 @@
     "print(len(sbas_from_search_results.full_stack))"
    ]
   },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a674e164",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "sbas_from_search_results.plot()"
+   ]
+  },
   {
    "cell_type": "markdown",
    "id": "1d03afce",
@@ -246,192 +256,151 @@
   },
   {
    "cell_type": "markdown",
-   "id": "c3aa3107",
+   "id": "d4bddacd",
    "metadata": {},
    "source": [
-    "### Add an automatically filtered Pair back to the `SBASNetwork` to manually connect one of the disconnected sub-networks\n",
+    "### Plot a different network\n",
     "\n",
-    "Use the `SBASNetwork.get_pair_from_dates()` method to identify a Pair to return to the network\n",
-    "\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "98a38281",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "removed_pair_to_return_to_network = sbas.get_pair_from_dates(sbas.remove_list, date(2023,1,17), date(2024,6,16))\n",
-    "print(removed_pair_to_return_to_network)\n",
-    "print(f\"Pair Dates: {removed_pair_to_return_to_network.ref_time.date()} - {removed_pair_to_return_to_network.sec_time.date()}\")\n",
+    "By default `SBASNetwork.plot()` plots all connected sub-networks (`sbas.connected_substacks`)\n",
     "\n",
-    "sbas.add_pairs(removed_pair_to_return_to_network)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "fbd56642",
-   "metadata": {},
-   "source": [
-    "### Re-plot the the `SBASNetwork`\n",
+    "However, `SBASNetwork` contains multiple networks that you can plot:\n",
+    "- `SBASNetwork.full_stack`: an SBAS network filtered by baselines, and including every possible bridge pair within SBASNetwork.bridge_year_threshold\n",
+    "- `SBASNetwork.subset_stack`: a possibly disconnected SBAS network filtered by temporal baseline, perpendicular baseline, and bridge pair filtering applied\n",
+    "- `SBASNetwork.connected_substacks`: SBASNetwork.subset_stack, broken up into a list of connected sub-networks\n",
     "\n",
-    "Note that the selected Pair has beeed back to the network, connecting a previously disconnected sub-network."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "da83378a",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "sbas.plot()"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "4a3754dc",
-   "metadata": {},
-   "source": [
-    "### Remove pairs from the network"
+    "The code cell below plots `sbas.full_stack`\n"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "06bc7afc",
+   "id": "94c08504",
    "metadata": {},
    "outputs": [],
    "source": [
-    "pair1_to_remove = sbas.get_pair_from_dates(sbas.subset_stack, date(2025,1,6), date(2025,1,18))\n",
-    "pair2_to_remove = sbas.get_pair_from_dates(sbas.subset_stack, date(2025,1,6), date(2025,1,30))\n",
-    "print(f\"{pair1_to_remove}, {pair2_to_remove}\")\n",
-    "print(f\"Pair 1 Dates: {pair1_to_remove.ref_time.date()} - {pair1_to_remove.sec_time.date()}\")\n",
-    "print(f\"Pair 2 Dates: {pair2_to_remove.ref_time.date()} - {pair2_to_remove.sec_time.date()}\")\n",
-    "\n",
-    "sbas.remove_pairs([pair1_to_remove, pair2_to_remove])"
+    "sbas.plot(sbas.full_stack)"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "4344ee36",
+   "id": "0367d3ea",
    "metadata": {},
    "source": [
-    "### Re-plot the network after removing 2 Pairs\n",
+    "### Plot the SBASNetwork that contains missing state vectors, resulting in perpendicular baselines with values of None\n",
     "\n",
-    "Note that after removing both Pairs, the date 2025-01-06 is no longer included in the network at all and its vertex on the plot is red."
+    "To view the perpendicular baselines of None on the plot, hover over the edges representing the following date pairs:\n",
+    "- 2023/01/05 - 2023/01/29\n",
+    "- 2023/01/17 - 2023/01/29\n",
+    "- 2023/01/29 - 2023/02/10\n",
+    "- 2023/01/29 - 2023/02/22"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "caed94de",
+   "id": "ebf20960",
    "metadata": {},
    "outputs": [],
    "source": [
-    "sbas.plot()"
+    "missing_state_vectors_allowed_sbas.plot()"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "7a8b4c11",
-   "metadata": {},
-   "source": [
-    "### You can also add Pairs that were not included in the initial search and are not included in `SBASNetwork.remove_list` at all"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "10ee6452",
+   "id": "c3aa3107",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "existing_pair = sbas.get_pair_from_dates(sbas.subset_stack, date(2025,3,7), date(2025,3,19))\n",
-    "existing_ref_scene = existing_pair.ref\n",
+    "<hr>\n",
     "\n",
-    "results = asf.product_search('S1A_IW_SLC__1SDV_20260401T105744_20260401T105802_063885_0808EC_7B1B-SLC')\n",
-    "new_sec_scene = results[0]\n",
+    "## Add and remove Pairs from an SBASNetwork \n",
     "\n",
-    "new_pair = asf.Pair(existing_ref_scene, new_sec_scene)\n",
-    "print(new_pair)\n",
-    "print(f\"Pair Dates: {new_pair.ref_time.date()} - {new_pair.sec_time.date()}\")\n",
+    "### Add Pair to the `SBASNetwork` to manually connect one of the disconnected sub-networks\n",
     "\n",
-    "sbas.add_pairs(new_pair)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "13591778",
-   "metadata": {},
-   "source": [
-    "### Re-plot the network after adding a Pair that was not in the scope of the original `SBASNetwork`'s time bounds\n",
+    "You can add or remove pairs by passing:\n",
+    "- A Tuple of date pair strings: (\"2023-01-05\", \"2023-01-29\")\n",
+    "- A List of tuples of date pair strings: [(\"2023-01-05\", \"2023-01-29\"), (\"2023-01-05\", \"2024-01-12\")]\n",
+    "- A Pair object\n",
+    "- A List of Pair objects\n",
     "\n",
-    "Note that there is now a pair with a secondary scene date in 2026. While this new pair has been manually added to the network, its perpendiccular baseline was not determined during the initial search and is therefore set to `None`. "
+    "Note that the following code cell adds a Pair to the network, connecting a previously disconnected sub-network in 2023."
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "205bd3c8",
+   "id": "0a45ffd8",
    "metadata": {},
    "outputs": [],
    "source": [
+    "sbas.add_pairs((\"2023-01-05\", \"2023-01-29\"))\n",
+    "\n",
     "sbas.plot()"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "d4bddacd",
+   "id": "eec0a391",
    "metadata": {},
    "source": [
-    "### Plot a different network\n",
+    "### Add Pairs by passing the Pair objects themselves (instead of date pair strings)\n",
     "\n",
-    "By default `SBASNetwork.plot()` plots all connected sub-networks (`sbas.connected_substacks`)\n",
+    "Note that we are able to add a Pair that extends beyond the original temporal range of the SBASNetwork. \n",
     "\n",
-    "However, `SBASNetwork` contains multiple networks that you can plot:\n",
-    "- `SBASNetwork.full_stack`: an SBAS network filtered by baselines, and including every possible bridge pair within SBASNetwork.bridge_year_threshold\n",
-    "- `SBASNetwork.subset_stack`: a possibly disconnected SBAS network filtered by baselines, with additional bridge pair filtering applied\n",
-    "- `SBASNetwork.connected_substacks`: SBASNetwork.subset_stack, broken up into a list of connected sub-networks\n",
-    "\n",
-    "The code cell below plots `sbas.full_stack`\n"
+    "Arbitrarily added pairs are not required to adhere to the baselines or temporal range of the SBASNetwork. "
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "94c08504",
+   "id": "e6226426",
    "metadata": {},
    "outputs": [],
    "source": [
-    "sbas.plot(sbas.full_stack)"
+    "results = asf.product_search('S1A_IW_SLC__1SDV_20230529T225128_20230529T225155_048755_05DD08_82C5-SLC')\n",
+    "ref = results[0]\n",
+    "stack = ref.stack()\n",
+    "sec_1 = stack[-1]\n",
+    "sec_2 = stack[-2]\n",
+    "\n",
+    "pair_1 = asf.Pair(ref, sec_1)\n",
+    "pair_2 = asf.Pair(ref, sec_2)\n",
+    "\n",
+    "\n",
+    "sbas.add_pairs([\n",
+    "    pair_1,\n",
+    "    pair_2\n",
+    "])\n",
+    "\n",
+    "sbas.plot()"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "0367d3ea",
+   "id": "4a3754dc",
    "metadata": {},
    "source": [
-    "### Plot the SBASNetwork that contains missing state vectors, resulting in perpendicular baselines with values of None\n",
+    "### Remove pairs from the network\n",
     "\n",
-    "Note that perpendicular baselines with None values are plotted with values of 0.0 m.\n",
+    "#### Remove Pairs with list of date pair strings\n",
     "\n",
-    "To view the perpendicular baselines of None on the plot, hover over the edges representing the following date pairs:\n",
-    "- 2023/01/05 - 2023/01/29\n",
-    "- 2023/01/17 - 2023/01/29\n",
-    "- 2023/01/29 - 2023/02/10\n",
-    "- 2023/01/29 - 2023/02/22"
+    "Like the `add_pairs` method, you can pass date pair strings or Pair objects to `remove_pairs`"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "ebf20960",
+   "id": "6a126430",
    "metadata": {},
    "outputs": [],
    "source": [
-    "missing_state_vectors_allowed_sbas.plot()"
+    "sbas.remove_pairs([\n",
+    "    (\"2024-05-11\", \"2024-05-23\"),\n",
+    "    (\"2024-05-23\", \"2024-06-04\"),\n",
+    "    (\"2024-05-23\", \"2024-06-16\"),\n",
+    "    (\"2024-06-04\", \"2024-06-16\"),\n",
+    "])\n",
+    "\n",
+    "sbas.plot()"
    ]
   },
   {
@@ -441,13 +410,11 @@
    "source": [
     "<hr>\n",
     "\n",
-    "## View the SLC IDs of an `SBASNetwork`'s `Pairs`\n",
-    "\n",
-    "### View `SBASNetwork.subset_stack`'s scene ID's \n",
+    "## Prepare a list of the SBASNetwork's SLC IDs to order on-demand processing from ASF [HyP3](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/) or [HyP3+](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/hyp3_plus/)\n",
     "\n",
     "When ordering data from ASF [HyP3](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/) or [HyP3+](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/hyp3_plus/) On-Demand Processing, it is helpful to provide an `SBASNetwork` as a list of scene IDs.\n",
     "\n",
-    "The `SBASNetwork.get_scene_ids()` method (inherited from the `Stack` class) offers this functionality."
+    "### Use the `scene_ids` property to view the scene ID's of the largest connected network in `connected_substacks`\n"
    ]
   },
   {
@@ -457,7 +424,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "largest_fully_connected_subset_stack_ids = sbas.get_scene_ids()\n",
+    "largest_fully_connected_subset_stack_ids = sbas.scene_ids\n",
     "largest_fully_connected_subset_stack_ids"
    ]
   },
@@ -466,7 +433,13 @@
    "id": "25f0d098",
    "metadata": {},
    "source": [
-    "### You can view the scene IDs of any stack_list contained in a `SBASNetwork` object, including the complete `SBASNetwork.full_stack`, `SBASNetwork.remove_list`, the possibly disconnected `SBASNetwork.subset_stack`, or any of the stack_lists in `SBASNetwork.connected_substacks`"
+    "### Use the `get_scene_ids` method to view the scene IDs of any stack_list contained in an `SBASNetwork` object\n",
+    "\n",
+    "Options include:\n",
+    "- `SBASNetwork.full_stack`\n",
+    "- `SBASNetwork.remove_list`\n",
+    "- The possibly disconnected `SBASNetwork.subset_stack`\n",
+    "- Any of the stack_lists in `SBASNetwork.connected_substacks`"
    ]
   },
   {
@@ -483,15 +456,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "3d0e4696",
-   "metadata": {},
-   "outputs": [],
-   "source": []
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "677e5bbf",
+   "id": "119e5a76",
    "metadata": {},
    "outputs": [],
    "source": []


=====================================
examples/SBASNetwork_S1-Multiburst.ipynb
=====================================
@@ -0,0 +1,295 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "eb53c259",
+   "metadata": {
+    "vscode": {
+     "languageId": "plaintext"
+    }
+   },
+   "source": [
+    "## Create SBASNetworks from groups of Sentinel-1 burst data\n",
+    "\n",
+    "This notebook demonstrates how to create SBASNetworks of interferometric pairs from groups of adjacent Sentinel-1 burst data. Refer to the [SBASNetwork Tutorial](SBASNetwork.ipynb) for examples of creating SBASNetworks of networks of full Sentinel-1 scenes or individual Sentinel-1 bursts."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "aeb8be28",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# This is useful if you are working with a dev install of asf_search and experimenting with changes to the codebase\n",
+    "%load_ext autoreload\n",
+    "%autoreload 2"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3ccc6d0a",
+   "metadata": {},
+   "source": [
+    "### Create an asf_search.S1MultiBurstGroup object defining a group of Sentinel-1 bursts and subswaths\n",
+    "\n",
+    "Refer to [HyP3's mutli-burst guidelines](https://hyp3-docs.asf.alaska.edu/guides/burst_insar_product_guide/#considerations-for-selecting-input-bursts) when assembling a collection of bursts.\n",
+    "\n",
+    "S1MultiBurstGroup validation is performed during initialitzation to prevent the creation of invalid multiburst SBASNetworks that result in failed HyP3 on-demand processing jobs."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a4333bc6",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import asf_search as asf\n",
+    "\n",
+    "multiburst_group = asf.S1MultiBurstGroup(\n",
+    "    bursts=[\n",
+    "    asf.S1MultiBurst(\"173_370305\", (\"IW1\", \"IW2\", \"IW3\")),\n",
+    "    asf.S1MultiBurst(\"173_370306\", (\"IW1\", \"IW2\", \"IW3\")),\n",
+    "    asf.S1MultiBurst(\"173_370307\", (\"IW1\", \"IW2\", \"IW3\"))\n",
+    "    ]\n",
+    ")\n",
+    "multiburst_group"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aac455e5",
+   "metadata": {},
+   "source": [
+    "### Create a geoographic reference S1MultiBurstProduct object\n",
+    "\n",
+    "An `S1MultiBurstProduct` contains a collection of Sentinel-1 burst products."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f35864ff",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "start_date = '2023-01-01'\n",
+    "\n",
+    "reference_multiburst = asf.S1MultiBurstProduct(multiburst_group, start_date)\n",
+    "reference_multiburst"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "833d605b",
+   "metadata": {},
+   "source": [
+    "### Create an SBASNetwork from the S1MultiBurstProduct object"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "af8ee46c",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from datetime import datetime, date\n",
+    "import pandas as pd\n",
+    "\n",
+    "def get_julian_season(season) -> tuple[int,int]:\n",
+    "    season_start_ts = pd.Timestamp(\n",
+    "        datetime.strptime(f\"{season[0]}-0001\", \"%m-%d-%Y\"), tz=\"UTC\"\n",
+    "        )\n",
+    "    season_start_day = season_start_ts.timetuple().tm_yday\n",
+    "    season_end_ts = pd.Timestamp(\n",
+    "        datetime.strptime(f\"{season[1]}-0001\", \"%m-%d-%Y\"), tz=\"UTC\"\n",
+    "    )\n",
+    "    season_end_day = season_end_ts.timetuple().tm_yday\n",
+    "    return (season_start_day, season_end_day)\n",
+    "\n",
+    "season = (\"1-1\", \"6-25\")\n",
+    "\n",
+    "multiburst_sbas = asf.SBASNetwork.from_geo_reference(\n",
+    "    geo_reference = reference_multiburst,\n",
+    "    start_date = '2023-01-01',\n",
+    "    end_date = '2025-10-02',\n",
+    "    season = get_julian_season(season),\n",
+    "    perpendicular_baseline=150, \n",
+    "    inseason_temporal_baseline=36,\n",
+    "    bridge_target_date='3-1',\n",
+    "    bridge_year_threshold=1,\n",
+    "    allow_missing_state_vectors=True)\n",
+    "\n",
+    "multiburst_sbas.plot()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8c8d61e1",
+   "metadata": {},
+   "source": [
+    "## Add and remove Pairs from an SBASNetwork \n",
+    "\n",
+    "### Add Pairs to the `SBASNetwork` to manually connect an unused S1MultiBurst\n",
+    "\n",
+    "You can add or remove pairs by passing:\n",
+    "- A Tuple of date pair strings: (\"2024-02-03\", \"2024-04-15\")\n",
+    "- A List of tuples of date pair strings: [(\"2024-02-03\", \"2024-04-15\"), (\"2024-02-03\", \"2024-04-15\")]\n",
+    "- A Pair object\n",
+    "- A List of Pair objects"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "488db17a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "multiburst_sbas.add_pairs([\n",
+    "    (\"2024-03-22\", \"2024-04-15\"),\n",
+    "    (\"2024-04-03\", \"2024-04-15\")\n",
+    "    ])\n",
+    "\n",
+    "multiburst_sbas.plot()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b5b29930",
+   "metadata": {},
+   "source": [
+    "### Add a Pair by passing a Pair object"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3749d06d",
+   "metadata": {},
+   "source": [
+    "### "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "3666f00a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "pair = multiburst_sbas.remove_list[60]\n",
+    "print(f\"{pair.ref_time.date()}, {pair.sec_time.date()}\")\n",
+    "\n",
+    "multiburst_sbas.add_pairs(pair)\n",
+    "\n",
+    "multiburst_sbas.plot()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "58de13ef",
+   "metadata": {},
+   "source": [
+    "### Remove Pairs"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "361ecbfd",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "multiburst_sbas.remove_pairs([\n",
+    "    (\"2024-04-27\", \"2024-05-21\"),\n",
+    "    (\"2024-05-21\", \"2024-06-14\"),\n",
+    "    (\"2024-06-02\", \"2024-06-14\"),\n",
+    "    (\"2024-05-09\", \"2024-06-14\"),\n",
+    "    (\"2024-05-09\", \"2024-06-02\"),\n",
+    "    (\"2024-04-27\", \"2024-06-02\"),\n",
+    "    (\"2024-04-27\", \"2024-05-09\")\n",
+    "])\n",
+    "\n",
+    "multiburst_sbas.plot()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d4d21b40",
+   "metadata": {},
+   "source": [
+    "<hr>\n",
+    "\n",
+    "## Use the `scene_ids` property to access multi-burst product IDs for easy InSAR product ordering from HyP3 \n",
+    "\n",
+    "The `scene_ids` property provides scene IDs for the largest network in the `connected_substacks` list for each SBASNetwork in `sbas_networks`\n",
+    "\n",
+    "An SBAS Network generated from a `S1MultiBurstProduct` geo-reference can be used to order Sentinel-1 multi-burst InSAR processing from ASF [HyP3](https://hyp3-docs.asf.alaska.edu/hyp3-docs/guides/burst_insar_product_guide/) and [HyP3+](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/hyp3_plus/)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8cc9451a",
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "sbas_burst_ids = multiburst_sbas.scene_ids\n",
+    "sbas_burst_ids"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9f32cacb",
+   "metadata": {},
+   "source": [
+    "<hr>\n",
+    "\n",
+    "## Use the `get_scene_ids` method to access multi-burst product IDs from any of the SBASNetwork pair lists in `sbas_networks`\n",
+    "\n",
+    "Options include:\n",
+    "- `SBASNetwork.full_stack`\n",
+    "- `SBASNetwork.remove_list`\n",
+    "- The possibly disconnected `SBASNetwork.subset_stack`\n",
+    "- Any of the stack_lists in `SBASNetwork.connected_substacks`\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "c26c928a",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "full_stack_burst_ids = multiburst_sbas.get_scene_ids(multiburst_sbas.full_stack)\n",
+    "full_stack_burst_ids"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.12.0"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}


=====================================
examples/Stack.ipynb
=====================================
@@ -358,71 +358,56 @@
     "\n",
     "When ordering data from ASF [HyP3](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/) or [HyP3+](https://hyp3-docs.asf.alaska.edu/hyp3-docs/about/hyp3_plus/) On-Demand Processing, it is helpful to provide a `Stack` as a list of scene IDs.\n",
     "\n",
-    "The `Stack.get_scene_ids()` method offers this functionality."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "e04d7bf8",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "possibly_disconnected_subset_stack_ids = stack.get_scene_ids(stack.subset_stack)\n",
-    "possibly_disconnected_subset_stack_ids"
+    "The `Stack.scene_ids` property and `Stack.get_scene_ids()` method offer this functionality."
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "87798431",
-   "metadata": {},
-   "source": [
-    "### View the largest connected substack in `stack.connected_substacks`\n",
-    "\n",
-    "`stack.get_scene_ids()` takes the optional argument `stack_list`. \n",
-    "\n",
-    "If you do not provide a stack_list, the method defaults to using the largest connected substack in `stack.connected_substacks`."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "14711e1f",
+   "id": "183233d2",
    "metadata": {},
-   "outputs": [],
    "source": [
-    "largest_fully_connected_subset_stack_ids = stack.get_scene_ids()\n",
-    "largest_fully_connected_subset_stack_ids\n"
+    "### Use the `scene_ids` property to view the scene IDs of the largest connected network in `stack.connected_substacks`"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "c2d4c0c7",
+   "id": "7fbc5b28",
    "metadata": {},
    "outputs": [],
    "source": [
-    "len(largest_fully_connected_subset_stack_ids)"
+    "largest_connected_substack_scene_ids = stack.scene_ids\n",
+    "largest_connected_substack_scene_ids"
    ]
   },
   {
    "cell_type": "markdown",
-   "id": "183233d2",
+   "id": "ce5a0754",
    "metadata": {},
    "source": [
-    "### You can view the scene IDs of any stack_list contained in a `Stack` object, including the complete `stack.full_stack`, `stack.remove_list`, the possibly disconnected `stack.subset_stack`, or any of the stack_lists in `stack.connected_substacks`"
+    "### Use the `get_scene_ids` method view the scene IDs of any stack_list contained in a `Stack` object\n",
+    "\n",
+    "Options include the complete `stack.full_stack`, `stack.remove_list`, the possibly disconnected `stack.subset_stack`, or any of the stack_lists in `stack.connected_substacks`"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "id": "7fbc5b28",
+   "id": "10e04773",
    "metadata": {},
    "outputs": [],
    "source": [
     "remove_list_scene_ids = stack.get_scene_ids(stack.remove_list)\n",
     "remove_list_scene_ids"
    ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b500b090",
+   "metadata": {},
+   "outputs": [],
+   "source": []
   }
  ],
  "metadata": {


=====================================
pyproject.toml
=====================================
@@ -12,6 +12,7 @@ build-backend = "setuptools.build_meta"
 [tool.setuptools_scm]
 
 [tool.ruff]
+required-version = "==0.15.22"
 line-length = 100
 fix = true
 


=====================================
setup.py
=====================================
@@ -72,7 +72,7 @@ setup(
                     'extras': extra_requirements, 
                     'asf-enumeration': asf_enumeration, 
                     'coherence': coherence,
-                    'sbasnetwork_plot': sbasnetwork_plot
+                    'sbasnetwork_plot': sbasnetwork_plot,
                     },
     license='BSD',
     license_files=('LICENSE',),


=====================================
tests/S1MultiBurstProduct/test_S1MultiBurstProduct.py
=====================================
@@ -0,0 +1,99 @@
+import pytest
+import re
+
+from asf_search import S1MultiBurst, S1MultiBurstGroup, S1MultiBurstProduct, SBASNetwork
+
+def test_three_by_three_group(three_by_three_multiburst_group):       
+        assert len(three_by_three_multiburst_group.bursts) == 3
+
+def test_horizontal_line_group():
+        multiburst_group = S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+                ])
+        
+        assert len(multiburst_group.bursts) == 1
+
+
+def test_vertical_line_group():
+        multiburst_group = S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1",)),
+                S1MultiBurst("173_370306", ("IW1",)),
+                S1MultiBurst("173_370307", ("IW1",))
+                ])
+        
+        assert len(multiburst_group.bursts) == 3
+
+def test_L_shaped_group():
+    with pytest.raises(
+        ValueError,
+        match="Products from swaths 2 and 3 do not overlap"
+        ):
+        S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1", "IW2")),
+                S1MultiBurst("173_370306", ("IW1", "IW2")),
+                S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+                ])
+
+def test_u_shaped_group():
+    with pytest.raises(
+        ValueError,
+        match="Products from swaths 1 and 2 do not overlap"
+        ):
+        S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1",        "IW3")),
+                S1MultiBurst("173_370306", ("IW1",        "IW3")),
+                S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+                ])
+
+def test_c_shaped_group():
+    with pytest.raises(
+        ValueError,
+        match=re.escape("All bursts must have consecutive burst IDs. Found: [370305, 370307].")
+        ):
+        S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370306", ("IW1",             )),
+                S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+                ])
+
+def test_donut_shaped_group():
+    with pytest.raises(
+        ValueError,
+        match=re.escape("All bursts must have consecutive burst IDs. Found: [370305, 370307].")
+        ):   
+        S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370306", ("IW1",        "IW3")),
+                S1MultiBurst("173_370307", ("IW1", "IW2", "IW3"))
+                ])
+
+def test_too_large_group():
+    with pytest.raises(
+        ValueError,
+        match="An S1MultiBurstGroup may include no more than 15 burst/subswath combinations"
+        ):
+        S1MultiBurstGroup(
+            bursts=[
+                S1MultiBurst("173_370305", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370306", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370307", ("IW1", "IW2", "IW3")), 
+                S1MultiBurst("173_370308", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370309", ("IW1", "IW2", "IW3")),
+                S1MultiBurst("173_370310", ("IW1", "IW2", "IW3")), 
+                ])
+
+def test_multiburst_product_from_multiburst_group(three_by_three_multiburst_product):
+     assert three_by_three_multiburst_product.id == '173_370305_IW123_173_370306_IW123_173_370307_IW123_20230103'
+     assert len(three_by_three_multiburst_product.geo_reference_bursts) == 9
+
+def test_multiburst_product_from_geo_reference_bursts(three_by_three_multiburst_product):
+    geo_ref_bursts = three_by_three_multiburst_product.geo_reference_bursts
+    multiburst_product = S1MultiBurstProduct(geo_reference_bursts=geo_ref_bursts)
+    assert three_by_three_multiburst_product == multiburst_product
+


=====================================
tests/SBASNetwork/test_SBASNetwork.py
=====================================
@@ -1,9 +1,9 @@
+import copy
 from datetime import date
 import pickle
 from pathlib import Path
 
-from asf_search.ASFSearchOptions import ASFSearchOptions
-from asf_search import SBASNetwork
+from asf_search import SBASNetwork, get_existing_pair_from_dates
 
 import pytest
 
@@ -15,14 +15,6 @@ def reference():
         # trusted local test fixture
         return pickle.load(f) # nosec B301
     
- at pytest.fixture
-def pair():
-    path = Path(__file__).parent / "data/pair_0.pkl"
-
-    with open(path, "rb") as f:
-        # trusted local test fixture
-        return pickle.load(f) # nosec B301
-    
 @pytest.fixture
 def stack_results():
     path = Path(__file__).parent / "data/stack_search_0.pkl"
@@ -40,19 +32,24 @@ def sbas_network(stack_results):
         inseason_temporal_baseline=24,
         bridge_target_date='3-1',
         bridge_year_threshold=2)
-    
-def test_sbas_network_from_ref_scene(mocker, reference, stack_results):
+
+ at pytest.fixture
+def multiburst_sbas_network(three_by_three_multiburst_product):
+    return SBASNetwork.from_geo_reference(
+    geo_reference = three_by_three_multiburst_product,
+    start_date = '2023-01-01',
+    end_date = '2025-10-02',
+    season = (1, 176),
+    perpendicular_baseline=150, 
+    inseason_temporal_baseline=36,
+    bridge_target_date='3-1',
+    bridge_year_threshold=1)
+
+def test_sbas_network_from_ref_scene(reference):
     """
     Create an SBASNetwork from a geographic reference scene
     """
-    mock_stack = mocker.patch.object(
-        reference,
-        "stack",
-        return_value=stack_results,
-    )
-
-    # Create an SBASNetwork and confrm expected size of its full_stack and subset_stack
-    sbas = SBASNetwork(
+    sbas = SBASNetwork.from_geo_reference(
         reference,
         start_date="2020-01-01",
         end_date="2025-10-02",
@@ -62,19 +59,25 @@ def test_sbas_network_from_ref_scene(mocker, reference, stack_results):
         bridge_target_date='3-1',
         bridge_year_threshold=2)
 
-    assert len(sbas.full_stack) == 1730
-    assert len(sbas.subset_stack) == 176
-    assert len(sbas.connected_substacks) == 3
-    assert len(max(sbas.connected_substacks, key=lambda s: len(s))) == 161
+    assert len(sbas.full_stack) == 1508
+    assert len(sbas.subset_stack) == 159
+    assert len(sbas.connected_substacks) == 4
+    assert len(max(sbas.connected_substacks, key=lambda s: len(s))) == 145
+
+def test_multiburst_sbas_network(multiburst_sbas_network):
+    assert len(multiburst_sbas_network.full_stack) == 416
+    assert len(multiburst_sbas_network.subset_stack) == 99
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 96
 
 def test_sbasnetwork_from_search_results(sbas_network):
     """
     Create an SBASNetwork from ASFProduct.stack search results with the 
     SBASNetwork.from_search_results alternate class method constructor
     """
-    assert len(sbas_network.full_stack) == 1730
-    assert len(sbas_network.subset_stack) == 176
-    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 161
+    assert len(sbas_network.full_stack) == 1508
+    assert len(sbas_network.subset_stack) == 159
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 145
 
 def test_disallow_missing_state_vectors(stack_results):
     """
@@ -104,34 +107,193 @@ def test_disallow_missing_state_vectors(stack_results):
         bridge_target_date='3-1',
         bridge_year_threshold=2,
         allow_missing_state_vectors=True)
-    assert len(missing_state_vectors_allowed_sbas.full_stack) == 1730
+    assert len(missing_state_vectors_allowed_sbas.full_stack) == 1508
     assert len([p for p in missing_state_vectors_allowed_sbas.full_stack if p.perpendicular_baseline is None]) == 32
 
-def test_add_pair(sbas_network):
-    removed_pair_0 = sbas_network.get_pair_from_dates(sbas_network.remove_list, date(2023,1,17), date(2024,6,16))
-    sbas_network.add_pairs(removed_pair_0)
-    assert len(sbas_network.subset_stack) == 177
-    assert len(sbas_network.connected_substacks) == 2
-    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 165
+def test_add_pair_date_strings(sbas_network):
+    """
+    Tests adding a single pair, which is on the remove_list, using date pair strings
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.add_pairs(("2023-01-17", "2024-06-16"))
+    assert len(sbas_network.subset_stack) == 160
+    assert len(sbas_network.remove_list) == removed_list_size - 1
+    assert len(sbas_network.connected_substacks) == 3
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 145
 
-def test_add_pairs(sbas_network):
-    removed_pair_0 = sbas_network.get_pair_from_dates(sbas_network.remove_list, date(2023,1,17), date(2024,6,16))
-    removed_pair_1 = sbas_network.get_pair_from_dates(sbas_network.remove_list, date(2023,1,5), date(2024,2,5))
-    sbas_network.add_pairs([removed_pair_0, removed_pair_1])
-    assert len(sbas_network.subset_stack) == 178
+def test_add_multiburst_pair_date_strings(multiburst_sbas_network):
+    """
+    Tests adding a single S1MultiBurstProduct pair, which is on the remove_list, using date pair strings
+    """
+    multiburst_sbas_network.add_pairs(("2024-03-22", "2024-04-15"))
+    assert len(multiburst_sbas_network.subset_stack) == 100
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 97
+
+def test_add_new_pair_date_strings(sbas_network):
+    """
+    tests adding a pair previously unknown to the SBASNetwork
+    and not on the remove_list, using date pair strings
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.add_pairs(("2023-01-17", "2025-05-14"))
+    assert len(sbas_network.subset_stack) == 160
+    assert len(sbas_network.remove_list) == removed_list_size
+    assert len(sbas_network.connected_substacks) == 3
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 145
+
+def test_add_pairs_date_strings(sbas_network):
+    """
+    Tests adding a multiple pairs using date pair strings
+    """
+    sbas_network.add_pairs([("2023-01-17", "2024-06-16"), ("2023-01-05", "2024-02-05")])
+    assert len(sbas_network.subset_stack) == 161
     assert len(sbas_network.connected_substacks) == 2
-    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 166
-
-def test_remove_pairs(sbas_network):
-    pair1_to_remove = sbas_network.get_pair_from_dates(sbas_network.subset_stack, date(2025,1,6), date(2025,1,18))
-    pair2_to_remove = sbas_network.get_pair_from_dates(sbas_network.subset_stack, date(2025,1,6), date(2025,1,30))
-    sbas_network.remove_pairs([pair1_to_remove, pair2_to_remove])
-    assert len(sbas_network.subset_stack) == 174
-    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 159
-    assert len(sbas_network.remove_list) == 1556
-
-def add_new_pair(sbas_network, pair):
-    sbas_network.add_pairs(pair)
-    assert len(sbas_network.full_stack) == 1731
-    assert len(sbas_network.subset_stack) == 176
-    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 164
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 151
+
+def test_add_multiburst_pairs_date_strings(multiburst_sbas_network):
+    """
+    Tests adding multiple S1MultiBurstProduct pairs using date pair strings
+    """
+    multiburst_sbas_network.add_pairs([("2024-03-22", "2024-04-15"), ("2024-04-03", "2024-02-15")])
+    assert len(multiburst_sbas_network.subset_stack) == 101
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 98
+
+def test_add_pair_object(sbas_network):
+    """
+    Tests adding a single pair using a Pair object
+    """
+    pair_to_add = sbas_network.remove_list[10]
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.add_pairs(pair_to_add)
+    assert len(sbas_network.subset_stack) == 160
+    assert len(sbas_network.remove_list) == removed_list_size - 1
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 146
+
+def test_add_multiburst_pair_object(multiburst_sbas_network):
+    """
+    Tests adding a single S1MultiBurstProduct pair using a Pair object
+    """
+    pair_to_add = multiburst_sbas_network.remove_list[10]
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.add_pairs(pair_to_add)
+    assert len(multiburst_sbas_network.subset_stack) == 100
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size - 1
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 97
+
+def test_add_pair_objects(sbas_network):
+    """
+    Tests adding a multiple pairs using Pair objects
+    """
+
+    pair_to_add_1 = sbas_network.remove_list[10]
+    pair_to_add_2 = sbas_network.remove_list[-1]
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.add_pairs([pair_to_add_1, pair_to_add_2])
+    assert len(sbas_network.subset_stack) == 161
+    assert len(sbas_network.remove_list) == removed_list_size - 2
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 146
+
+def test_add_multiburst_pair_objects(multiburst_sbas_network):
+    """
+    Tests adding multiple S1MultiBurstProduct pairs using a Pair objects
+    """
+    pairs_to_add = multiburst_sbas_network.remove_list[10:12]
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.add_pairs(pairs_to_add)
+    assert len(multiburst_sbas_network.subset_stack) == 101
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size - 2
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 98
+
+def test_remove_pair_date_strings(sbas_network):
+    """
+    Tests removing a single pair using date pair strings
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.remove_pairs(("2025-01-06", "2025-01-18"))
+    assert len(sbas_network.subset_stack) == 158
+    assert len(sbas_network.remove_list) == removed_list_size + 1
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 144
+
+def test_remove_multiburst_pair_date_strings(multiburst_sbas_network):
+    """
+    Tests removing a single S1MultiBurstProduct pair using date pair strings
+    """
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.remove_pairs(("2023-02-20", "2024-02-15"))
+    assert len(multiburst_sbas_network.subset_stack) == 98
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size + 1
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 95
+
+def test_remove_pairs_date_strings(sbas_network):
+    """
+    Tests a multiple pairs using date pair strings
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.remove_pairs([("2025-01-06", "2025-01-18"), ("2025-01-06", "2025-01-30")])
+    assert len(sbas_network.subset_stack) == 157
+    assert len(sbas_network.remove_list) == removed_list_size + 2
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 143
+
+def test_remove_multiburst_pairs_date_strings(multiburst_sbas_network):
+    """
+    Tests removing multiple S1MultiBurstProduct pairs using date pair strings
+    """
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.remove_pairs([("2023-02-20", "2024-02-15"), ("2023-03-04", "2024-02-15")])
+    assert len(multiburst_sbas_network.subset_stack) == 97
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size + 2
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 94
+
+def test_remove_pair_object(sbas_network):
+    """
+    Tests removing a single pair using a Pair object
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.remove_pairs(sbas_network.subset_stack[0])
+    assert len(sbas_network.subset_stack) == 158
+    assert len(sbas_network.remove_list) == removed_list_size + 1
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 144
+
+def test_remove_multiburst_pair_object(multiburst_sbas_network):
+    """
+    Tests removing a single S1MultiBurstProduct pair using a Pair object
+    """
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.remove_pairs(multiburst_sbas_network.subset_stack[0])
+    assert len(multiburst_sbas_network.subset_stack) == 98
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size + 1
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 95
+
+def test_remove_pair_objects(sbas_network):
+    """
+    Tests removing multiple pairs using pair objects
+    """
+    removed_list_size = len(sbas_network.remove_list)
+    sbas_network.remove_pairs(sbas_network.subset_stack[:3])
+    assert len(sbas_network.subset_stack) == 156
+    assert len(sbas_network.remove_list) == removed_list_size + 3
+    assert len(sbas_network.connected_substacks) == 4
+    assert len(max(sbas_network.connected_substacks, key=lambda s: len(s))) == 142
+
+def test_remove_multiburst_pair_objects(multiburst_sbas_network):
+    """
+    Tests removing multiple S1MultiBurstProduct pairs using pair objects
+    """
+    removed_list_size = len(multiburst_sbas_network.remove_list)
+    multiburst_sbas_network.remove_pairs(multiburst_sbas_network.subset_stack[:3])
+    assert len(multiburst_sbas_network.subset_stack) == 96
+    assert len(multiburst_sbas_network.remove_list) == removed_list_size + 3
+    assert len(multiburst_sbas_network.connected_substacks) == 3
+    assert len(max(multiburst_sbas_network.connected_substacks, key=lambda s: len(s))) == 93


=====================================
tests/yml_tests/test_ASFSearchResults.yml
=====================================
@@ -2,9 +2,6 @@ tests:
 - Test ASFSearchResults_intersection simple-polygon:
     wkt: POLYGON ((0 0, 50 0, 50 50, 0 50, 0 0))
 
-- Test ASFSearchResults_intersection Shovel_Creek Polygon:
-    wkt: POLYGON ((-148.395929999329 65.02006999976498, -148.398010619511 65.02049651421811, -148.3980473296276 65.02074794119113, -148.3982428597838 65.02104161295625, -148.3992698433036 65.02120374777655, -148.4007460405182 65.02123858637134, -148.401412730549 65.02108323434425, -148.4027226255657 65.0207780217462, -148.4035199986349 65.0207700013126, -148.4086000003617 65.02056000058639, -148.4120399989118 65.02099999942624, -148.4150700005966 65.02205999978662, -148.4207299999013 65.02348000026927, -148.4247500012678 65.02348000026927, -148.4283700011887 65.02275000067596, -148.4360600005286 65.02072999998933, -148.4453999984648 65.01838999919386, -148.4505993951639 65.01648358639204, -148.4509054577393 65.01644310572868, -148.4524066683864 65.01582945088057, -148.4524399995483 65.0158299996333, -148.4554199992516 65.01481999994485, -148.4574399986286 65.01414000102335, -148.4595599993493 65.01347999948933, -148.4620200014952 65.01214000165317, -148.4626900010681 65.01101999996274, -148.4630200005255 65.01047999977919, -148.4648500004927 65.00898999992751, -148.466480000392 65.00734000002149, -148.4677900355436 65.00472812040925, -148.4678953842795 65.0045180620574, -148.4689400012282 65.00142999997701, -148.4691890681716 65.00087774780837, -148.4696833359056 64.9997817550319, -148.470480000442 64.9987799984213, -148.4725000011287 64.99773999937747, -148.4759300016398 64.99630000019761, -148.4771114544676 64.99570064042348, -148.4788537666308 64.99430364564563, -148.4826219690773 64.99391567746977, -148.4874281274788 64.99320716567104, -148.4913011914632 64.99292186009308, -148.4943174586139 64.99262299154805, -148.4981470362368 64.99216857452501, -148.5015947304217 64.99144294923741, -148.5039482954939 64.990973367518, -148.5048332384966 64.99045220828117, -148.5053359562367 64.98921814329481, -148.5042842447928 64.9883712934228, -148.5010520624355 64.98800007464683, -148.4973591294795 64.98818001315345, -148.4903337063593 64.98897527503073, -148.485345891161 64.98929340361769, -148.4795349390187 64.98966013414639, -148.4775199989061 64.98850000147519, -148.4745600005193 64.98700000096528, -148.471699999547 64.9833199997143, -148.4681499989951 64.97882000080398, -148.4666722602962 64.9756643308163, -148.4665600004192 64.97416000052954, -148.4661999989871 64.97277999875075, -148.4650700005675 64.97143000025636, -148.4640093414419 64.97080265016427, -148.4628142313506 64.97009573223681, -148.4620099995273 64.96962000160562, -148.4618567455953 64.96961620748476, -148.4614782594474 64.96942664943458, -148.4598182575734 64.96893576851335, -148.4536700006213 64.96761000157716, -148.4487799996138 64.96622999979837, -148.4423399987343 64.96448999920693, -148.4402099999748 64.96339000145235, -148.4397699998252 64.96224000040655, -148.4397849536643 64.96133779461235, -148.4396582494107 64.96122947161047, -148.4379060963693 64.95777518584453, -148.4351494364053 64.95236794152066, -148.4341252896358 64.95051261379939, -148.4336461918369 64.95000396848212, -148.4326677303688 64.94967417071433, -148.4312261504144 64.9496943226431, -148.4274077338195 64.95021227580196, -148.4249869485031 64.9502836608043, -148.4239955946201 64.95031288352391, -148.4233534465399 64.95033180959939, -148.4193875700035 64.95055001018432, -148.410199567404 64.95105270173099, -148.4071979777517 64.95096232255025, -148.3997900949375 64.95123385692324, -148.3944654857017 64.9530024044434, -148.3860846014435 64.95415602411396, -148.380667186204 64.95455384495511, -148.3795556764293 64.95500717756943, -148.3788123705647 64.95541219113124, -148.3774486833746 64.95607947575331, -148.3766577250807 64.95675944717169, -148.3766052713925 64.95727565060821, -148.3770170284574 64.95794499796432, -148.377050976475 64.95800018100971, -148.3771632101586 64.95806187181716, -148.3772045434187 64.95807053268055, -148.3774508535018 64.95828005668602, -148.377498737743 64.95832078880647, -148.377759414932 64.95856235525622, -148.3781711340164 64.95878103911025, -148.3784735543929 64.95901422234715, -148.3788887925669 64.95936592831441, -148.3792663134863 64.95965258940328, -148.3797080397841 64.95992131452897, -148.3797919439446 64.96002399153332, -148.3799253235983 64.96023662863001, -148.3799373699648 64.96025583366571, -148.3799253864626 64.96027401060854, -148.3798222785761 64.96043041037359, -148.3798023047627 64.96046070571492, -148.3797408130255 64.96055398058127, -148.3797688924012 64.96085547632907, -148.3800383705885 64.96131891044644, -148.3801248718294 64.96147992942593, -148.3804082652857 64.96191838547173, -148.3804958561739 64.96223228774596, -148.3804993752636 64.96251140643164, -148.3804642550893 64.96266856580667, -148.3804734764924 64.96285187671958, -148.3804406181223 64.96297794185318, -148.3803955247928 64.96345177084009, -148.3803961940354 64.96349963150726, -148.380437265361 64.96386935856634, -148.3804609036377 64.96397647797937, -148.3803516036201 64.96426590640596, -148.3803191643452 64.96442038357196, -148.3802623363513 64.96484149038787, -148.3803492553776 64.96490510903305, -148.3809197277082 64.96508850507468, -148.3811571032064 64.96517140209716, -148.3813156063053 64.96542520612559, -148.3816272441559 64.96598654218172, -148.3816793795937 64.96658065147938, -148.3820580739796 64.96716864198771, -148.3823038326906 64.96757156007368, -148.3823237803105 64.96761866636962, -148.3823864782559 64.96775411792504, -148.3824091368976 64.96780183714759, -148.3819600004736 64.96810000108889, -148.3805500006492 64.96962000029595, -148.3777400003488 64.97000999977365, -148.3746299999464 64.96986999841641, -148.3716999996053 64.96940999956973, -148.3693700688807 64.96870474361651, -148.3704556510715 64.96824846031035, -148.3716986624298 64.96719965110043, -148.3728913037886 64.96588657488957, -148.3722037716317 64.96468081454253, -148.3700530198037 64.96407850407655, -148.3674836154858 64.96350482435821, -148.3659655231618 64.96283775452241, -148.3646722033587 64.96195656111172, -148.3631116881271 64.96117245240436, -148.3612595533871 64.9608537213681, -148.359609841609 64.96051427524395, -148.3579713393167 64.96011922435986, -148.3560216693461 64.95962893933955, -148.3542673671323 64.95948162084278, -148.3520203714107 64.95948445628346, -148.3501949120776 64.95936251661789, -148.3483878064931 64.95882129058094, -148.3469311181582 64.958180732443, -148.3458400418918 64.95769335620503, -148.344821788648 64.95684466755296, -148.3441971115972 64.95601021111054, -148.343851605619 64.95510186688193, -148.3411043373156 64.95463851003529, -148.3382125322595 64.95358196459068, -148.3351337138467 64.95279853822257, -148.3308052859461 64.95166178058383, -148.3302523252447 64.95080186149119, -148.329953998904 64.94933527858154, -148.3236780331795 64.9477077055052, -148.3213589203488 64.94709154525384, -148.3184873025826 64.94659524097932, -148.313846778446 64.94655529204294, -148.3083189591352 64.94649584994295, -148.3075757462573 64.94648784260607, -148.3053740442451 64.94646410217501, -148.2992930494466 64.94732037644945, -148.2930874877798 64.94839646089616, -148.2897545745913 64.94912953560021, -148.2873323656611 64.94940456417908, -148.2865662178006 64.94965351456165, -148.2850427977293 64.94964716396032, -148.2842981455215 64.94964405479811, -148.2807296235829 64.94962910357827, -148.2749596864503 64.94986141719272, -148.2686122807172 64.95065066112551, -148.2618036790968 64.9518252565087, -148.2610336441288 64.95214683739204, -148.2596114984027 64.95249326406997, -148.2583534480766 64.95265094731394, -148.2578890343243 64.95276207825339, -148.2571120384476 64.95299071561749, -148.2565297921623 64.95326090757629, -148.251326658968 64.9547869836631, -148.248432115387 64.95545683786241, -148.2443829633328 64.95607704893041, -148.2411535352165 64.95683790309516, -148.2380841556124 64.95745650850483, -148.2366640726204 64.95779057318521, -148.2316172082239 64.95784155742098, -148.2249859886296 64.95784259337182, -148.2183133820788 64.95779126469222, -148.2150862406504 64.95759969760462, -148.2111793085385 64.95712510638839, -148.2070109946338 64.95657001877908, -148.203830876992 64.95661712507496, -148.2014863408011 64.95638876012276, -148.1993577983972 64.95619194255863, -148.1960149578922 64.95595001463931, -148.1936230456129 64.95541336328802, -148.1908710166504 64.95500470752734, -148.1889280613701 64.95480992126505, -148.1869174313892 64.95422134140415, -148.1853585545578 64.95369196135385, -148.183965305443 64.95316872759599, -148.1812561433669 64.95309146609378, -148.1783299096435 64.95352461403746, -148.1748649867186 64.95440904102924, -148.1721725557071 64.95532220354249, -148.1669249971162 64.95675283856491, -148.163271104184 64.95800712227327, -148.157881333509 64.95931432984224, -148.1525901994989 64.95961150367128, -148.1488088846126 64.95987073498196, -148.1463068825834 64.96013064077397, -148.1443847157326 64.96051093426973, -148.144343724297 64.96154064976611, -148.1450132864394 64.9620813519341, -148.1469237880384 64.96167409228411, -148.1506507528309 64.96089282163678, -148.1546971323087 64.96108099143424, -148.1582184182938 64.96128626555287, -148.1620292361697 64.96260715788844, -148.1672635330181 64.96335495461966, -148.1727892764982 64.96311903940619, -148.1809728206329 64.96180335563764, -148.1844446966085 64.96056988131352, -148.1848876919788 64.96045006986554, -148.1873364715422 64.96255566549979, -148.1886712398565 64.9649254705094, -148.1895612093818 64.96650534007927, -148.1900985876013 64.96810893436418, -148.1906662965232 64.96956634039947, -148.191603369725 64.97007991794294, -148.1959102102958 64.97027664121066, -148.1978054136118 64.96994185228147, -148.199281080409 64.96911271441854, -148.2007944264804 64.96810082356313, -148.2017371234153 64.96732556169263, -148.2040386421075 64.96586411138901, -148.2066731947678 64.96489373845753, -148.2099223255953 64.96431436035471, -148.2128187027177 64.96375869256059, -148.2163599284647 64.96344779205543, -148.2186145635052 64.96305242161128, -148.220782868872 64.96265383068283, -148.2243163452876 64.96237929977946, -148.2276396310745 64.96228106387412, -148.2291398463707 64.96259422618346, -148.2295934330614 64.96334749341617, -148.2297506487523 64.96427395565235, -148.2306506883484 64.96497004258924, -148.2327925802428 64.96512270454934, -148.2352217055529 64.96518624985288, -148.2366696387279 64.96785253587478, -148.2367990815065 64.96809087005312, -148.2402036193674 64.97435802561159, -148.2386825868289 64.97560729458451, -148.239508957354 64.97555546037137, -148.23961858348 64.97550091775571, -148.2417614825124 64.97548188690649, -148.2432022923794 64.97522692119873, -148.2443149664529 64.97495986852459, -148.2449653589207 64.97436780624486, -148.2454844881653 64.97377091389347, -148.2463116575907 64.97296135694222, -148.2467222961953 64.97224847476679, -148.2467704109389 64.97172686500272, -148.2484462375842 64.97117562259149, -148.2496482195264 64.97016615986865, -148.2505066364248 64.96921430832384, -148.2513935648908 64.96812301596151, -148.251922689555 64.96715905911492, -148.252401799141 64.96643911649579, -148.2523076258394 64.96613705235012, -148.2520008481829 64.965152998581, -148.2522470194407 64.96436125841274, -148.2518461313468 64.96388994530639, -148.2518315009967 64.96315185848619, -148.2517274514558 64.96244560456262, -148.2511406226268 64.96207281679921, -148.2511797294439 64.96147719352035, -148.2513296659758 64.96074515869617, -148.2506534637945 64.96040420251973, -148.2490794288151 64.95999510147044, -148.2482999733736 64.95973987513787, -148.2479919214059 64.95963900417172, -148.2475446211425 64.95899036142623, -148.2474729558703 64.95853114838292, -148.2496941746203 64.95861286146157, -148.2509676240742 64.95887042163037, -148.2516471292492 64.95892198736067, -148.2523550176438 64.95876706490606, -148.25480858275 64.9577460325375, -148.2569387334316 64.95662729468091, -148.2584837277358 64.9557762879088, -148.2604817259266 64.9547931980585, -148.2622952620025 64.95407049595968, -148.2642207789952 64.95354471096005, -148.2663159797598 64.95349983634588, -148.2674565616418 64.95302202464524, -148.2680631364011 64.9521945395889, -148.2691240810352 64.95177589364732, -148.271238896763 64.95153967328019, -148.2727945177488 64.95130479139829, -148.2744288814764 64.95148267764824, -148.2756123773931 64.95183399595248, -148.2763401584014 64.95247659194064, -148.2759734710919 64.95330308950406, -148.2744004982568 64.95388942720581, -148.2721344703763 64.95431036900305, -148.2708771888278 64.95432028977126, -148.2703607365535 64.95393739264676, -148.2693001966082 64.95395451137438, -148.2676469644307 64.9542858624136, -148.2660163974433 64.9545060406038, -148.2658114546718 64.95486249544308, -148.2662023813976 64.95521281053828, -148.2655213596219 64.95563579804372, -148.2644889095296 64.95592453365362, -148.2637095719587 64.95614247623314, -148.2639175309057 64.95674715458676, -148.2657138447899 64.95769106296871, -148.2672879897818 64.95809999637993, -148.269387397214 64.95796624215984, -148.2706965706012 64.95804934349121, -148.2711048072668 64.95848574597079, -148.2712022128398 64.95922683979876, -148.27151397249 64.95972994651163, -148.271790171916 64.9604073509721, -148.2722634194084 64.96133781687678, -148.2736346765047 64.96233629144842, -148.2756957838781 64.96279808192691, -148.2778280326548 64.96291124547793, -148.2793914173785 64.96296844934778, -148.2814739732564 64.96332558259735, -148.2830444891462 64.96334788893722, -148.284768282542 64.96302458321537, -148.2866849893687 64.96256783104644, -148.2888952933091 64.96229737715322, -148.2896954075225 64.96162420817313, -148.2899536041921 64.96152670699405, -148.2899635236506 64.96152864007047, -148.2924390362464 64.9615839016962, -148.2936689338553 64.96124247796678, -148.2965727224136 64.96082163439502, -148.2972407509296 64.96038945167982, -148.2977654554495 64.9598466815392, -148.2986829634557 64.95900216550331, -148.2989537146446 64.95848525353398, -148.30001885154 64.95813778173755, -148.3017138255953 64.95795373609837, -148.3029222249321 64.95771682422424, -148.3042951466221 64.95748589755289, -148.3049983385316 64.95687937910952, -148.3056475195525 64.95613041676989, -148.3060428048679 64.95540732569833, -148.3054252301704 64.95436639678712, -148.3041797775828 64.95396983847013, -148.3017659466267 64.95402234323552, -148.3009733185006 64.95380783330734, -148.301256199875 64.95372288926694, -148.3003198090124 64.95312881664012, -148.2982891227089 64.95271880143952, -148.298091060956 64.95206765329073, -148.2992883031939 64.95202733109772, -148.3012477380817 64.95246273429728, -148.3033214693872 64.95298626535077, -148.304439814342 64.953334977407, -148.3053917038673 64.95320167764322, -148.30772272162 64.95278264403868, -148.3086997673322 64.95251889959695, -148.3093157887583 64.9527521614142, -148.3123310592483 64.95300237301126, -148.3141195596271 64.95317279150942, -148.3165474093163 64.95305037381343, -148.3177177901738 64.95340898044435, -148.3189869583087 64.95327952064002, -148.3197081542843 64.95323709249385, -148.3201915936472 64.9535161509346, -148.3213444668044 64.95403801477516, -148.3212197047949 64.95494094350852, -148.3213536764206 64.95550769244062, -148.321974209668 64.95612723426603, -148.3238170404954 64.95643997748022, -148.3266635204101 64.95629747464869, -148.327605206278 64.95628193014733, -148.3290006163522 64.95699947606607, -148.3314802976352 64.95846119092352, -148.3326199103597 64.95903442142418, -148.3327216915481 64.96049389019345, -148.3342890995853 64.96189448918966, -148.3364409554671 64.96236438130143, -148.3402520392065 64.96345421861776, -148.3449330924643 64.96449142674982, -148.3483806779464 64.96509197963547, -148.3502456671207 64.96501938842493, -148.3513416559681 64.96449907261712, -148.3524610552092 64.96484744939715, -148.3530638031058 64.96579305904351, -148.3537972405891 64.96589468307121, -148.3533126264502 64.96671188981878, -148.3539871247477 64.96720437246933, -148.3533599999193 64.96727000015193, -148.3498800000459 64.96771000161118, -148.3464500008445 64.96823999851716, -148.3447900015899 64.96853000058024, -148.3400000006164 64.96950000156482, -148.3366000007703 64.97006000044553, -148.3348299995138 64.97023999919702, -148.3311600002308 64.97043000122613, -148.3275299996517 64.97057999931258, -148.3240800004434 64.97095999944179, -148.3209300000275 64.97153000029039, -148.3208900916909 64.97152527761182, -148.3197333183293 64.97187038283028, -148.3194400014854 64.97185000039917, -148.3165000004861 64.97253000063029, -148.3142100003624 64.97340000027123, -148.3131390131606 64.97428978251332, -148.3136603033646 64.97503578368367, -148.3138100002265 64.97525000024524, -148.3157199989112 64.97607000052398, -148.3173927425877 64.97628215435157, -148.3181154158738 64.97710949927301, -148.318506832417 64.97752678315527, -148.3187389443419 64.97826073664947, -148.3191775248066 64.97838722480719, -148.3187036827229 64.9785124478214, -148.3174808822287 64.97901466526673, -148.3183191301726 64.97940472760871, -148.3179133845039 64.98006608334396, -148.3178643739444 64.98039869559773, -148.317717597652 64.98046658639413, -148.3165037173362 64.98102803770149, -148.3158305797883 64.98133936777919, -148.3136403766995 64.98169043855529, -148.3124228974041 64.98159245803612, -148.3106442287358 64.9817736328734, -148.3085689899975 64.9821898415147, -148.3075063283829 64.98248161818384, -148.3074353009211 64.9823643173778, -148.3075113274024 64.98152061988714, -148.307689970643 64.98064034848585, -148.3053761881787 64.98031430947776, -148.3052377492606 64.98037990703784, -148.304154185275 64.98023807737798, -148.3033326487505 64.98101592847075, -148.3035788815629 64.9822540966606, -148.303609042008 64.9829224774785, -148.3029990450748 64.98388363555517, -148.30328332256 64.98452614903402, -148.303235581073 64.98557802025795, -148.3029986141926 64.98634992936718, -148.3001099994984 64.98611999935645, -148.2988772743069 64.9865277226304, -148.298759997075 64.98649000013705, -148.2985074044935 64.98665005257834, -148.2976941778403 64.98716532090884, -148.2961399988037 64.98814999939168, -148.29416000075 64.99019000139481, -148.2919799986993 64.99236999951654, -148.2910500003476 64.99376000064387, -148.2943157993754 64.99484862651326, -148.2946053050727 64.99503986356336, -148.2946899989657 64.99503000042074, -148.296969998431 64.99563999997326, -148.3006600003402 64.99654000158887, -148.3006688655125 64.99656069048336, -148.303190430118 64.99696088445086, -148.3032105912145 64.99718197417303, -148.3038899994737 64.99720999985215, -148.3063199996449 64.99755000127738, -148.3073614236123 64.99776908065252, -148.3077700007926 64.99793999944546, -148.3085600004063 64.99809999949986, -148.3094099961114 64.99820000346284, -148.3121299983457 64.99851999964261, -148.3139300002672 64.99865999969018, -148.3157000002141 64.99879000038925, -148.316639999224 64.99889999977177, -148.3180141624833 64.99914803731269, -148.3194200014786 64.99951999998257, -148.3211799994576 65.00009999887004, -148.3229000000423 65.00068999972541, -148.3241044886974 65.00118402124099, -148.3253100002067 65.00175000008574, -148.3259799997796 65.00217999957709, -148.3273699995973 65.00380999947635, -148.3293199996053 65.00625000030578, -148.3312600002648 65.0080600002662, -148.3343199999953 65.00923999935765, -148.3350104396249 65.00939097184039, -148.335234252156 65.01018466211087, -148.3351465892357 65.01100225914081, -148.3339866936152 65.01174824459508, -148.334658432433 65.01226500464242, -148.3368734105941 65.01244359287676, -148.3390489073719 65.01281770733829, -148.3417658200891 65.01281737992019, -148.3440400975958 65.01270253344023, -148.3452138914596 65.01304038438343, -148.3470812878118 65.01340325923667, -148.3486601947724 65.01365721256769, -148.3506178891056 65.01395763962284, -148.3519985090498 65.01404025375609, -148.3538791959572 65.01433786239636, -148.3545136981701 65.01465620053091, -148.3556942656594 65.01496138169676, -148.35727017478 65.01446004435132, -148.3590641024411 65.0140322045097, -148.361102294477 65.01354747904861, -148.3619877351552 65.01377632857964, -148.3634815618695 65.01445385091068, -148.364793324479 65.01410708764695, -148.3662673162052 65.01333936556028, -148.3668481362573 65.01302032806069, -148.3708081886807 65.01304589286542, -148.3711290400765 65.0134813091617, -148.3721097764455 65.01400898663792, -148.3738183724032 65.01400470138992, -148.3752070520711 65.0136606203352, -148.376363068442 65.01370212385268, -148.3766764533957 65.01407449514028, -148.3768917281725 65.01493578153088, -148.3783168455452 65.0151839028909, -148.3797186062208 65.01477459229397, -148.381435142722 65.01434373103825, -148.382481453077 65.01454539962884, -148.3830196105516 65.01495864186217, -148.3845231733703 65.01481557325218, -148.3849148151772 65.01482634661716, -148.3854207822145 65.01577727746229, -148.387758416432 65.01701172749227, -148.3907954156963 65.01809066440546, -148.3921436592297 65.01856960242435, -148.395929999329 65.02006999976498))
-
 - Test ASFSearchResults_intersection complex-polygon:
     wkt: POLYGON ((0 0, 5 0, 5 5, 5 5.1, 0 5, 0 5, 0 0))
 


=====================================
tests/yml_tests/test_known_bugs.yml
=====================================
@@ -1,4 +1,5 @@
 # To exclude from the pipeline, but still keep track of
 
 tests:
-    [] # placeholder until this file has tests
+- Test ASFSearchResults_intersection Shovel_Creek Polygon:
+    wkt: POLYGON ((-148.395929999329 65.02006999976498, -148.398010619511 65.02049651421811, -148.3980473296276 65.02074794119113, -148.3982428597838 65.02104161295625, -148.3992698433036 65.02120374777655, -148.4007460405182 65.02123858637134, -148.401412730549 65.02108323434425, -148.4027226255657 65.0207780217462, -148.4035199986349 65.0207700013126, -148.4086000003617 65.02056000058639, -148.4120399989118 65.02099999942624, -148.4150700005966 65.02205999978662, -148.4207299999013 65.02348000026927, -148.4247500012678 65.02348000026927, -148.4283700011887 65.02275000067596, -148.4360600005286 65.02072999998933, -148.4453999984648 65.01838999919386, -148.4505993951639 65.01648358639204, -148.4509054577393 65.01644310572868, -148.4524066683864 65.01582945088057, -148.4524399995483 65.0158299996333, -148.4554199992516 65.01481999994485, -148.4574399986286 65.01414000102335, -148.4595599993493 65.01347999948933, -148.4620200014952 65.01214000165317, -148.4626900010681 65.01101999996274, -148.4630200005255 65.01047999977919, -148.4648500004927 65.00898999992751, -148.466480000392 65.00734000002149, -148.4677900355436 65.00472812040925, -148.4678953842795 65.0045180620574, -148.4689400012282 65.00142999997701, -148.4691890681716 65.00087774780837, -148.4696833359056 64.9997817550319, -148.470480000442 64.9987799984213, -148.4725000011287 64.99773999937747, -148.4759300016398 64.99630000019761, -148.4771114544676 64.99570064042348, -148.4788537666308 64.99430364564563, -148.4826219690773 64.99391567746977, -148.4874281274788 64.99320716567104, -148.4913011914632 64.99292186009308, -148.4943174586139 64.99262299154805, -148.4981470362368 64.99216857452501, -148.5015947304217 64.99144294923741, -148.5039482954939 64.990973367518, -148.5048332384966 64.99045220828117, -148.5053359562367 64.98921814329481, -148.5042842447928 64.9883712934228, -148.5010520624355 64.98800007464683, -148.4973591294795 64.98818001315345, -148.4903337063593 64.98897527503073, -148.485345891161 64.98929340361769, -148.4795349390187 64.98966013414639, -148.4775199989061 64.98850000147519, -148.4745600005193 64.98700000096528, -148.471699999547 64.9833199997143, -148.4681499989951 64.97882000080398, -148.4666722602962 64.9756643308163, -148.4665600004192 64.97416000052954, -148.4661999989871 64.97277999875075, -148.4650700005675 64.97143000025636, -148.4640093414419 64.97080265016427, -148.4628142313506 64.97009573223681, -148.4620099995273 64.96962000160562, -148.4618567455953 64.96961620748476, -148.4614782594474 64.96942664943458, -148.4598182575734 64.96893576851335, -148.4536700006213 64.96761000157716, -148.4487799996138 64.96622999979837, -148.4423399987343 64.96448999920693, -148.4402099999748 64.96339000145235, -148.4397699998252 64.96224000040655, -148.4397849536643 64.96133779461235, -148.4396582494107 64.96122947161047, -148.4379060963693 64.95777518584453, -148.4351494364053 64.95236794152066, -148.4341252896358 64.95051261379939, -148.4336461918369 64.95000396848212, -148.4326677303688 64.94967417071433, -148.4312261504144 64.9496943226431, -148.4274077338195 64.95021227580196, -148.4249869485031 64.9502836608043, -148.4239955946201 64.95031288352391, -148.4233534465399 64.95033180959939, -148.4193875700035 64.95055001018432, -148.410199567404 64.95105270173099, -148.4071979777517 64.95096232255025, -148.3997900949375 64.95123385692324, -148.3944654857017 64.9530024044434, -148.3860846014435 64.95415602411396, -148.380667186204 64.95455384495511, -148.3795556764293 64.95500717756943, -148.3788123705647 64.95541219113124, -148.3774486833746 64.95607947575331, -148.3766577250807 64.95675944717169, -148.3766052713925 64.95727565060821, -148.3770170284574 64.95794499796432, -148.377050976475 64.95800018100971, -148.3771632101586 64.95806187181716, -148.3772045434187 64.95807053268055, -148.3774508535018 64.95828005668602, -148.377498737743 64.95832078880647, -148.377759414932 64.95856235525622, -148.3781711340164 64.95878103911025, -148.3784735543929 64.95901422234715, -148.3788887925669 64.95936592831441, -148.3792663134863 64.95965258940328, -148.3797080397841 64.95992131452897, -148.3797919439446 64.96002399153332, -148.3799253235983 64.96023662863001, -148.3799373699648 64.96025583366571, -148.3799253864626 64.96027401060854, -148.3798222785761 64.96043041037359, -148.3798023047627 64.96046070571492, -148.3797408130255 64.96055398058127, -148.3797688924012 64.96085547632907, -148.3800383705885 64.96131891044644, -148.3801248718294 64.96147992942593, -148.3804082652857 64.96191838547173, -148.3804958561739 64.96223228774596, -148.3804993752636 64.96251140643164, -148.3804642550893 64.96266856580667, -148.3804734764924 64.96285187671958, -148.3804406181223 64.96297794185318, -148.3803955247928 64.96345177084009, -148.3803961940354 64.96349963150726, -148.380437265361 64.96386935856634, -148.3804609036377 64.96397647797937, -148.3803516036201 64.96426590640596, -148.3803191643452 64.96442038357196, -148.3802623363513 64.96484149038787, -148.3803492553776 64.96490510903305, -148.3809197277082 64.96508850507468, -148.3811571032064 64.96517140209716, -148.3813156063053 64.96542520612559, -148.3816272441559 64.96598654218172, -148.3816793795937 64.96658065147938, -148.3820580739796 64.96716864198771, -148.3823038326906 64.96757156007368, -148.3823237803105 64.96761866636962, -148.3823864782559 64.96775411792504, -148.3824091368976 64.96780183714759, -148.3819600004736 64.96810000108889, -148.3805500006492 64.96962000029595, -148.3777400003488 64.97000999977365, -148.3746299999464 64.96986999841641, -148.3716999996053 64.96940999956973, -148.3693700688807 64.96870474361651, -148.3704556510715 64.96824846031035, -148.3716986624298 64.96719965110043, -148.3728913037886 64.96588657488957, -148.3722037716317 64.96468081454253, -148.3700530198037 64.96407850407655, -148.3674836154858 64.96350482435821, -148.3659655231618 64.96283775452241, -148.3646722033587 64.96195656111172, -148.3631116881271 64.96117245240436, -148.3612595533871 64.9608537213681, -148.359609841609 64.96051427524395, -148.3579713393167 64.96011922435986, -148.3560216693461 64.95962893933955, -148.3542673671323 64.95948162084278, -148.3520203714107 64.95948445628346, -148.3501949120776 64.95936251661789, -148.3483878064931 64.95882129058094, -148.3469311181582 64.958180732443, -148.3458400418918 64.95769335620503, -148.344821788648 64.95684466755296, -148.3441971115972 64.95601021111054, -148.343851605619 64.95510186688193, -148.3411043373156 64.95463851003529, -148.3382125322595 64.95358196459068, -148.3351337138467 64.95279853822257, -148.3308052859461 64.95166178058383, -148.3302523252447 64.95080186149119, -148.329953998904 64.94933527858154, -148.3236780331795 64.9477077055052, -148.3213589203488 64.94709154525384, -148.3184873025826 64.94659524097932, -148.313846778446 64.94655529204294, -148.3083189591352 64.94649584994295, -148.3075757462573 64.94648784260607, -148.3053740442451 64.94646410217501, -148.2992930494466 64.94732037644945, -148.2930874877798 64.94839646089616, -148.2897545745913 64.94912953560021, -148.2873323656611 64.94940456417908, -148.2865662178006 64.94965351456165, -148.2850427977293 64.94964716396032, -148.2842981455215 64.94964405479811, -148.2807296235829 64.94962910357827, -148.2749596864503 64.94986141719272, -148.2686122807172 64.95065066112551, -148.2618036790968 64.9518252565087, -148.2610336441288 64.95214683739204, -148.2596114984027 64.95249326406997, -148.2583534480766 64.95265094731394, -148.2578890343243 64.95276207825339, -148.2571120384476 64.95299071561749, -148.2565297921623 64.95326090757629, -148.251326658968 64.9547869836631, -148.248432115387 64.95545683786241, -148.2443829633328 64.95607704893041, -148.2411535352165 64.95683790309516, -148.2380841556124 64.95745650850483, -148.2366640726204 64.95779057318521, -148.2316172082239 64.95784155742098, -148.2249859886296 64.95784259337182, -148.2183133820788 64.95779126469222, -148.2150862406504 64.95759969760462, -148.2111793085385 64.95712510638839, -148.2070109946338 64.95657001877908, -148.203830876992 64.95661712507496, -148.2014863408011 64.95638876012276, -148.1993577983972 64.95619194255863, -148.1960149578922 64.95595001463931, -148.1936230456129 64.95541336328802, -148.1908710166504 64.95500470752734, -148.1889280613701 64.95480992126505, -148.1869174313892 64.95422134140415, -148.1853585545578 64.95369196135385, -148.183965305443 64.95316872759599, -148.1812561433669 64.95309146609378, -148.1783299096435 64.95352461403746, -148.1748649867186 64.95440904102924, -148.1721725557071 64.95532220354249, -148.1669249971162 64.95675283856491, -148.163271104184 64.95800712227327, -148.157881333509 64.95931432984224, -148.1525901994989 64.95961150367128, -148.1488088846126 64.95987073498196, -148.1463068825834 64.96013064077397, -148.1443847157326 64.96051093426973, -148.144343724297 64.96154064976611, -148.1450132864394 64.9620813519341, -148.1469237880384 64.96167409228411, -148.1506507528309 64.96089282163678, -148.1546971323087 64.96108099143424, -148.1582184182938 64.96128626555287, -148.1620292361697 64.96260715788844, -148.1672635330181 64.96335495461966, -148.1727892764982 64.96311903940619, -148.1809728206329 64.96180335563764, -148.1844446966085 64.96056988131352, -148.1848876919788 64.96045006986554, -148.1873364715422 64.96255566549979, -148.1886712398565 64.9649254705094, -148.1895612093818 64.96650534007927, -148.1900985876013 64.96810893436418, -148.1906662965232 64.96956634039947, -148.191603369725 64.97007991794294, -148.1959102102958 64.97027664121066, -148.1978054136118 64.96994185228147, -148.199281080409 64.96911271441854, -148.2007944264804 64.96810082356313, -148.2017371234153 64.96732556169263, -148.2040386421075 64.96586411138901, -148.2066731947678 64.96489373845753, -148.2099223255953 64.96431436035471, -148.2128187027177 64.96375869256059, -148.2163599284647 64.96344779205543, -148.2186145635052 64.96305242161128, -148.220782868872 64.96265383068283, -148.2243163452876 64.96237929977946, -148.2276396310745 64.96228106387412, -148.2291398463707 64.96259422618346, -148.2295934330614 64.96334749341617, -148.2297506487523 64.96427395565235, -148.2306506883484 64.96497004258924, -148.2327925802428 64.96512270454934, -148.2352217055529 64.96518624985288, -148.2366696387279 64.96785253587478, -148.2367990815065 64.96809087005312, -148.2402036193674 64.97435802561159, -148.2386825868289 64.97560729458451, -148.239508957354 64.97555546037137, -148.23961858348 64.97550091775571, -148.2417614825124 64.97548188690649, -148.2432022923794 64.97522692119873, -148.2443149664529 64.97495986852459, -148.2449653589207 64.97436780624486, -148.2454844881653 64.97377091389347, -148.2463116575907 64.97296135694222, -148.2467222961953 64.97224847476679, -148.2467704109389 64.97172686500272, -148.2484462375842 64.97117562259149, -148.2496482195264 64.97016615986865, -148.2505066364248 64.96921430832384, -148.2513935648908 64.96812301596151, -148.251922689555 64.96715905911492, -148.252401799141 64.96643911649579, -148.2523076258394 64.96613705235012, -148.2520008481829 64.965152998581, -148.2522470194407 64.96436125841274, -148.2518461313468 64.96388994530639, -148.2518315009967 64.96315185848619, -148.2517274514558 64.96244560456262, -148.2511406226268 64.96207281679921, -148.2511797294439 64.96147719352035, -148.2513296659758 64.96074515869617, -148.2506534637945 64.96040420251973, -148.2490794288151 64.95999510147044, -148.2482999733736 64.95973987513787, -148.2479919214059 64.95963900417172, -148.2475446211425 64.95899036142623, -148.2474729558703 64.95853114838292, -148.2496941746203 64.95861286146157, -148.2509676240742 64.95887042163037, -148.2516471292492 64.95892198736067, -148.2523550176438 64.95876706490606, -148.25480858275 64.9577460325375, -148.2569387334316 64.95662729468091, -148.2584837277358 64.9557762879088, -148.2604817259266 64.9547931980585, -148.2622952620025 64.95407049595968, -148.2642207789952 64.95354471096005, -148.2663159797598 64.95349983634588, -148.2674565616418 64.95302202464524, -148.2680631364011 64.9521945395889, -148.2691240810352 64.95177589364732, -148.271238896763 64.95153967328019, -148.2727945177488 64.95130479139829, -148.2744288814764 64.95148267764824, -148.2756123773931 64.95183399595248, -148.2763401584014 64.95247659194064, -148.2759734710919 64.95330308950406, -148.2744004982568 64.95388942720581, -148.2721344703763 64.95431036900305, -148.2708771888278 64.95432028977126, -148.2703607365535 64.95393739264676, -148.2693001966082 64.95395451137438, -148.2676469644307 64.9542858624136, -148.2660163974433 64.9545060406038, -148.2658114546718 64.95486249544308, -148.2662023813976 64.95521281053828, -148.2655213596219 64.95563579804372, -148.2644889095296 64.95592453365362, -148.2637095719587 64.95614247623314, -148.2639175309057 64.95674715458676, -148.2657138447899 64.95769106296871, -148.2672879897818 64.95809999637993, -148.269387397214 64.95796624215984, -148.2706965706012 64.95804934349121, -148.2711048072668 64.95848574597079, -148.2712022128398 64.95922683979876, -148.27151397249 64.95972994651163, -148.271790171916 64.9604073509721, -148.2722634194084 64.96133781687678, -148.2736346765047 64.96233629144842, -148.2756957838781 64.96279808192691, -148.2778280326548 64.96291124547793, -148.2793914173785 64.96296844934778, -148.2814739732564 64.96332558259735, -148.2830444891462 64.96334788893722, -148.284768282542 64.96302458321537, -148.2866849893687 64.96256783104644, -148.2888952933091 64.96229737715322, -148.2896954075225 64.96162420817313, -148.2899536041921 64.96152670699405, -148.2899635236506 64.96152864007047, -148.2924390362464 64.9615839016962, -148.2936689338553 64.96124247796678, -148.2965727224136 64.96082163439502, -148.2972407509296 64.96038945167982, -148.2977654554495 64.9598466815392, -148.2986829634557 64.95900216550331, -148.2989537146446 64.95848525353398, -148.30001885154 64.95813778173755, -148.3017138255953 64.95795373609837, -148.3029222249321 64.95771682422424, -148.3042951466221 64.95748589755289, -148.3049983385316 64.95687937910952, -148.3056475195525 64.95613041676989, -148.3060428048679 64.95540732569833, -148.3054252301704 64.95436639678712, -148.3041797775828 64.95396983847013, -148.3017659466267 64.95402234323552, -148.3009733185006 64.95380783330734, -148.301256199875 64.95372288926694, -148.3003198090124 64.95312881664012, -148.2982891227089 64.95271880143952, -148.298091060956 64.95206765329073, -148.2992883031939 64.95202733109772, -148.3012477380817 64.95246273429728, -148.3033214693872 64.95298626535077, -148.304439814342 64.953334977407, -148.3053917038673 64.95320167764322, -148.30772272162 64.95278264403868, -148.3086997673322 64.95251889959695, -148.3093157887583 64.9527521614142, -148.3123310592483 64.95300237301126, -148.3141195596271 64.95317279150942, -148.3165474093163 64.95305037381343, -148.3177177901738 64.95340898044435, -148.3189869583087 64.95327952064002, -148.3197081542843 64.95323709249385, -148.3201915936472 64.9535161509346, -148.3213444668044 64.95403801477516, -148.3212197047949 64.95494094350852, -148.3213536764206 64.95550769244062, -148.321974209668 64.95612723426603, -148.3238170404954 64.95643997748022, -148.3266635204101 64.95629747464869, -148.327605206278 64.95628193014733, -148.3290006163522 64.95699947606607, -148.3314802976352 64.95846119092352, -148.3326199103597 64.95903442142418, -148.3327216915481 64.96049389019345, -148.3342890995853 64.96189448918966, -148.3364409554671 64.96236438130143, -148.3402520392065 64.96345421861776, -148.3449330924643 64.96449142674982, -148.3483806779464 64.96509197963547, -148.3502456671207 64.96501938842493, -148.3513416559681 64.96449907261712, -148.3524610552092 64.96484744939715, -148.3530638031058 64.96579305904351, -148.3537972405891 64.96589468307121, -148.3533126264502 64.96671188981878, -148.3539871247477 64.96720437246933, -148.3533599999193 64.96727000015193, -148.3498800000459 64.96771000161118, -148.3464500008445 64.96823999851716, -148.3447900015899 64.96853000058024, -148.3400000006164 64.96950000156482, -148.3366000007703 64.97006000044553, -148.3348299995138 64.97023999919702, -148.3311600002308 64.97043000122613, -148.3275299996517 64.97057999931258, -148.3240800004434 64.97095999944179, -148.3209300000275 64.97153000029039, -148.3208900916909 64.97152527761182, -148.3197333183293 64.97187038283028, -148.3194400014854 64.97185000039917, -148.3165000004861 64.97253000063029, -148.3142100003624 64.97340000027123, -148.3131390131606 64.97428978251332, -148.3136603033646 64.97503578368367, -148.3138100002265 64.97525000024524, -148.3157199989112 64.97607000052398, -148.3173927425877 64.97628215435157, -148.3181154158738 64.97710949927301, -148.318506832417 64.97752678315527, -148.3187389443419 64.97826073664947, -148.3191775248066 64.97838722480719, -148.3187036827229 64.9785124478214, -148.3174808822287 64.97901466526673, -148.3183191301726 64.97940472760871, -148.3179133845039 64.98006608334396, -148.3178643739444 64.98039869559773, -148.317717597652 64.98046658639413, -148.3165037173362 64.98102803770149, -148.3158305797883 64.98133936777919, -148.3136403766995 64.98169043855529, -148.3124228974041 64.98159245803612, -148.3106442287358 64.9817736328734, -148.3085689899975 64.9821898415147, -148.3075063283829 64.98248161818384, -148.3074353009211 64.9823643173778, -148.3075113274024 64.98152061988714, -148.307689970643 64.98064034848585, -148.3053761881787 64.98031430947776, -148.3052377492606 64.98037990703784, -148.304154185275 64.98023807737798, -148.3033326487505 64.98101592847075, -148.3035788815629 64.9822540966606, -148.303609042008 64.9829224774785, -148.3029990450748 64.98388363555517, -148.30328332256 64.98452614903402, -148.303235581073 64.98557802025795, -148.3029986141926 64.98634992936718, -148.3001099994984 64.98611999935645, -148.2988772743069 64.9865277226304, -148.298759997075 64.98649000013705, -148.2985074044935 64.98665005257834, -148.2976941778403 64.98716532090884, -148.2961399988037 64.98814999939168, -148.29416000075 64.99019000139481, -148.2919799986993 64.99236999951654, -148.2910500003476 64.99376000064387, -148.2943157993754 64.99484862651326, -148.2946053050727 64.99503986356336, -148.2946899989657 64.99503000042074, -148.296969998431 64.99563999997326, -148.3006600003402 64.99654000158887, -148.3006688655125 64.99656069048336, -148.303190430118 64.99696088445086, -148.3032105912145 64.99718197417303, -148.3038899994737 64.99720999985215, -148.3063199996449 64.99755000127738, -148.3073614236123 64.99776908065252, -148.3077700007926 64.99793999944546, -148.3085600004063 64.99809999949986, -148.3094099961114 64.99820000346284, -148.3121299983457 64.99851999964261, -148.3139300002672 64.99865999969018, -148.3157000002141 64.99879000038925, -148.316639999224 64.99889999977177, -148.3180141624833 64.99914803731269, -148.3194200014786 64.99951999998257, -148.3211799994576 65.00009999887004, -148.3229000000423 65.00068999972541, -148.3241044886974 65.00118402124099, -148.3253100002067 65.00175000008574, -148.3259799997796 65.00217999957709, -148.3273699995973 65.00380999947635, -148.3293199996053 65.00625000030578, -148.3312600002648 65.0080600002662, -148.3343199999953 65.00923999935765, -148.3350104396249 65.00939097184039, -148.335234252156 65.01018466211087, -148.3351465892357 65.01100225914081, -148.3339866936152 65.01174824459508, -148.334658432433 65.01226500464242, -148.3368734105941 65.01244359287676, -148.3390489073719 65.01281770733829, -148.3417658200891 65.01281737992019, -148.3440400975958 65.01270253344023, -148.3452138914596 65.01304038438343, -148.3470812878118 65.01340325923667, -148.3486601947724 65.01365721256769, -148.3506178891056 65.01395763962284, -148.3519985090498 65.01404025375609, -148.3538791959572 65.01433786239636, -148.3545136981701 65.01465620053091, -148.3556942656594 65.01496138169676, -148.35727017478 65.01446004435132, -148.3590641024411 65.0140322045097, -148.361102294477 65.01354747904861, -148.3619877351552 65.01377632857964, -148.3634815618695 65.01445385091068, -148.364793324479 65.01410708764695, -148.3662673162052 65.01333936556028, -148.3668481362573 65.01302032806069, -148.3708081886807 65.01304589286542, -148.3711290400765 65.0134813091617, -148.3721097764455 65.01400898663792, -148.3738183724032 65.01400470138992, -148.3752070520711 65.0136606203352, -148.376363068442 65.01370212385268, -148.3766764533957 65.01407449514028, -148.3768917281725 65.01493578153088, -148.3783168455452 65.0151839028909, -148.3797186062208 65.01477459229397, -148.381435142722 65.01434373103825, -148.382481453077 65.01454539962884, -148.3830196105516 65.01495864186217, -148.3845231733703 65.01481557325218, -148.3849148151772 65.01482634661716, -148.3854207822145 65.01577727746229, -148.387758416432 65.01701172749227, -148.3907954156963 65.01809066440546, -148.3921436592297 65.01856960242435, -148.395929999329 65.02006999976498))



View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/commit/5ab9bd0abdf28d8633ab73a8886aaa735ef8cb5c

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/asf-search/-/commit/5ab9bd0abdf28d8633ab73a8886aaa735ef8cb5c
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/20260808/c60fd0b7/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list