def set_self_href(self, href): """Sets the absolute HREF that is represented by the ``rel == 'self'`` :class:`~pystac.Link`. Changing the self HREF of the item will ensure that all asset HREFs remain valid. If asset HREFs are relative, the HREFs will change to point to the same location based on the new item self HREF, either by making them relative to the new location or making them absolute links if the new location does not share the same protocol as the old location. Args: href (str): The absolute HREF of this object. If the given HREF is not absolute, it will be transformed to an absolute HREF based on the current working directory. If this is None the call will clear the self HREF link. """ prev_href = self.get_self_href() super().set_self_href(href) new_href = self.get_self_href() # May have been made absolute. if prev_href is not None: # Make sure relative asset links remain valid. for asset in self.assets.values(): asset_href = asset.href if not is_absolute_href(asset_href): abs_href = make_absolute_href(asset_href, prev_href) new_relative_href = make_relative_href(abs_href, new_href) asset.href = new_relative_href
def test_make_relative_href_windows(self): utils._pathlib = ntpath try: # Test cases of (source_href, start_href, expected) test_cases = [ ('C:\\a\\b\\c\\d\\catalog.json', 'C:\\a\\b\\c\\catalog.json', '.\\d\\catalog.json'), ('C:\\a\\b\\catalog.json', 'C:\\a\\b\\c\\catalog.json', '..\\catalog.json'), ('C:\\a\\catalog.json', 'C:\\a\\b\\c\\catalog.json', '..\\..\\catalog.json'), ('a\\b\\c\\catalog.json', 'a\\b\\catalog.json', '.\\c\\catalog.json'), ('a\\b\\catalog.json', 'a\\b\\c\\catalog.json', '..\\catalog.json'), ('http://stacspec.org/a/b/c/d/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', './d/catalog.json'), ('http://stacspec.org/a/b/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', '../catalog.json'), ('http://stacspec.org/a/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', '../../catalog.json'), ('http://stacspec.org/a/catalog.json', 'http://cogeo.org/a/b/c/catalog.json', 'http://stacspec.org/a/catalog.json'), ('http://stacspec.org/a/catalog.json', 'https://stacspec.org/a/b/c/catalog.json', 'http://stacspec.org/a/catalog.json') ] for source_href, start_href, expected in test_cases: actual = make_relative_href(source_href, start_href) self.assertEqual(actual, expected) finally: utils._pathlib = os.path
def merge_items(source_item: pystac.Item, target_item: pystac.Item, move_assets: bool = False, ignore_conflicts: bool = False) -> None: """Merges the assets from source_item into target_item. The geometry and bounding box of the items will also be merged. Args: source_item (pystac.Item): The Item that will be merged into target_item. This item is not mutated in this operation. target_item (pystac.Item): The target item that will be merged into. This item will be mutated in this operation. move_assets (bool): If true, move the asset files alongside the target item. ignore_conflicts (bool): If True, assets with the same keys will not be merged, and asset files that would be moved to overwrite an existing file will not be moved. If False, either of these situations will throw an error. """ target_item_href = target_item.get_self_href() if target_item_href is None: raise ValueError( f"Target Item {target_item.id} must have an HREF for merge") for key, asset in source_item.assets.items(): if key in target_item.assets: if ignore_conflicts: continue else: raise Exception( 'Target item {} already has asset with key {}, ' 'cannot merge asset in from {}'.format( target_item, key, source_item)) else: asset_href = asset.get_absolute_href() if asset_href is None: raise ValueError( f"Asset {asset.title} must have an HREF for merge") if move_assets: new_asset_href = move_asset_file_to_item( target_item, asset_href, ignore_conflicts=ignore_conflicts) else: if not is_absolute_href(asset.href): asset_href = make_relative_href(asset_href, target_item_href) new_asset_href = asset_href new_asset = asset.clone() new_asset.href = new_asset_href target_item.add_asset(key, new_asset) source_geom = shape(source_item.geometry) target_geom = shape(target_item.geometry) union_geom = source_geom.union(target_geom).buffer(0) target_item.geometry = mapping(union_geom) target_item.bbox = list(union_geom.bounds)
def make_asset_hrefs_relative(self): """Modify each asset's HREF to be relative to this item's self HREF. Returns: Item: self """ self_href = self.get_self_href() if self_href is None: raise STACError('Cannot make asset HREFs relative if no self_href is set.') for asset in self.assets.values(): asset.href = make_relative_href(asset.href, self_href) return self
def test_make_relative_href(self) -> None: # Test cases of (source_href, start_href, expected) test_cases = [ ("/a/b/c/d/catalog.json", "/a/b/c/catalog.json", "./d/catalog.json"), ("/a/b/catalog.json", "/a/b/c/catalog.json", "../catalog.json"), ("/a/catalog.json", "/a/b/c/catalog.json", "../../catalog.json"), ("/a/b/c/d/", "/a/b/c/catalog.json", "./d/"), ("/a/b/c/d/.dotfile", "/a/b/c/d/catalog.json", "./.dotfile"), ] for source_href, start_href, expected in test_cases: actual = make_relative_href(source_href, start_href) self.assertEqual(actual, expected)
def move_assets(item: Item, asset_subdirectory: Optional[str] = None, make_hrefs_relative: bool = True, copy: bool = False, ignore_conflicts: bool = False) -> Item: """Moves assets for an item to be alongside that item. Args: item (Item): The PySTAC Item to perform the asset transformation on. asset_subdirectory (str or None): A subdirectory that will be used to store the assets. If not supplied, the assets will be moved or copied to the same directory as their item. make_assets_relative (bool): If True, will make the asset HREFs relative to the assets. If false, the asset will be an absolute href. copy (bool): If False this function will move the asset file; if True, the asset file will be copied. ignore_conflicts (bool): If the asset destination file already exists, this function will throw an error unless ignore_conflicts is True. Returns: Item: Returns an updated catalog or collection. This operation mutates the Item. """ item_href = item.get_self_href() if item_href is None: raise ValueError( f"Self HREF is not available for item {item.id}. This operation " "requires that the Item HREFs are available.") for asset in item.assets.values(): abs_asset_href = asset.get_absolute_href() if abs_asset_href is None: raise ValueError( f"Asset {asset.title} HREF is not available for item {item.id}. This operation " "requires that the Asset HREFs are available.") new_asset_href = move_asset_file_to_item( item, abs_asset_href, asset_subdirectory=asset_subdirectory, copy=copy, ignore_conflicts=ignore_conflicts) if make_hrefs_relative: asset.href = make_relative_href(new_asset_href, item_href) else: asset.href = new_asset_href return item
def normalize_hrefs(self, root_href): if not is_absolute_href(root_href): root_href = make_absolute_href(root_href, os.getcwd(), start_is_dir=True) old_self_href = self.get_self_href() new_self_href = os.path.join(root_href, '{}.json'.format(self.id)) self.set_self_href(new_self_href) # Make sure relative asset links remain valid. # This will only work if there is a self href set. for asset in self.assets.values(): asset_href = asset.href if not is_absolute_href(asset_href): if old_self_href is not None: abs_href = make_absolute_href(asset_href, old_self_href) new_relative_href = make_relative_href(abs_href, new_self_href) asset.href = new_relative_href
def make_asset_hrefs_relative(self) -> "Item": """Modify each asset's HREF to be relative to this item's self HREF. Returns: Item: self """ self_href = None for asset in self.assets.values(): href = asset.href if is_absolute_href(href): if self_href is None: self_href = self.get_self_href() if self_href is None: raise STACError("Cannot make asset HREFs relative " "if no self_href is set.") asset.href = make_relative_href(asset.href, self_href) return self
def test_make_relative_href_url(self) -> None: test_cases = [ ( "http://stacspec.org/a/b/c/d/catalog.json", "http://stacspec.org/a/b/c/catalog.json", "./d/catalog.json", ), ( "http://stacspec.org/a/b/catalog.json", "http://stacspec.org/a/b/c/catalog.json", "../catalog.json", ), ( "http://stacspec.org/a/catalog.json", "http://stacspec.org/a/b/c/catalog.json", "../../catalog.json", ), ( "http://stacspec.org/a/catalog.json", "http://cogeo.org/a/b/c/catalog.json", "http://stacspec.org/a/catalog.json", ), ( "http://stacspec.org/a/catalog.json", "https://stacspec.org/a/b/c/catalog.json", "http://stacspec.org/a/catalog.json", ), ( "http://stacspec.org/a/", "https://stacspec.org/a/b/c/catalog.json", "http://stacspec.org/a/", ), ( "http://stacspec.org/a/b/.dotfile", "http://stacspec.org/a/b/catalog.json", "./.dotfile", ), ] for source_href, start_href, expected in test_cases: actual = make_relative_href(source_href, start_href) self.assertEqual(actual, expected)
def get_href(self): """Gets the HREF for this link. Returns: str: Returns this link's HREF. If the link type is LinkType.RELATIVE, and there is an owner of the link, then the HREF returned will be relative. In all other cases, this method will return an absolute HREF. """ if self.link_type == LinkType.RELATIVE: if self.is_resolved(): href = self.target.get_self_href() else: href = self.target if href and is_absolute_href(href) and self.owner is not None: href = make_relative_href(href, self.owner.get_self_href()) else: href = self.get_absolute_href() return href
def test_make_relative_href(self): # Test cases of (source_href, start_href, expected) test_cases = [ ('/a/b/c/d/catalog.json', '/a/b/c/catalog.json', './d/catalog.json'), ('/a/b/catalog.json', '/a/b/c/catalog.json', '../catalog.json'), ('/a/catalog.json', '/a/b/c/catalog.json', '../../catalog.json'), ('http://stacspec.org/a/b/c/d/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', './d/catalog.json'), ('http://stacspec.org/a/b/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', '../catalog.json'), ('http://stacspec.org/a/catalog.json', 'http://stacspec.org/a/b/c/catalog.json', '../../catalog.json'), ('http://stacspec.org/a/catalog.json', 'http://cogeo.org/a/b/c/catalog.json', 'http://stacspec.org/a/catalog.json'), ('http://stacspec.org/a/catalog.json', 'https://stacspec.org/a/b/c/catalog.json', 'http://stacspec.org/a/catalog.json') ] for source_href, start_href, expected in test_cases: actual = make_relative_href(source_href, start_href) self.assertEqual(actual, expected)
def get_href(self, transform_href: bool = True) -> Optional[str]: """Gets the HREF for this link. Args: transform_href: If True, transform the HREF based on the type of catalog the owner belongs to (if any). I.e. if the link owner belongs to a root catalog that is RELATIVE_PUBLISHED or SELF_CONTAINED, the HREF will be transformed to be relative to the catalog root if this is a hierarchical link relation. Returns: str: Returns this link's HREF. If there is an owner of the link and the root catalog (if there is one) is of type RELATIVE_PUBLISHED, then the HREF returned will be relative. In all other cases, this method will return an absolute HREF. """ # get the self href if self._target_object: href = self._target_object.get_self_href() else: href = self._target_href if (transform_href and href and is_absolute_href(href) and self.owner and self.owner.get_root()): root = self.owner.get_root() rel_links = [ *HIERARCHICAL_LINKS, *pystac.EXTENSION_HOOKS.get_extended_object_links(self.owner), ] # if a hierarchical link with an owner and root, and relative catalog if root and root.is_relative() and self.rel in rel_links: owner_href = self.owner.get_self_href() if owner_href is not None: href = make_relative_href(href, owner_href) return href
def save( self, catalog_type: Optional[CatalogType] = None, dest_href: Optional[str] = None, stac_io: Optional[pystac.StacIO] = None, ) -> None: """Save this catalog and all it's children/item to files determined by the object's self link HREF or a specified path. Args: catalog_type : The catalog type that dictates the structure of the catalog to save. Use a member of :class:`~pystac.CatalogType`. If not supplied, the catalog_type of this catalog will be used. If that attribute is not set, an exception will be raised. dest_href : The location where the catalog is to be saved. If not supplied, the catalog's self link HREF is used to determine the location of the catalog file and children's files. stac_io : Optional instance of :class:`~pystac.StacIO` to use. If not provided, will use the instance set while reading in the catalog, or the default instance if this is not available. Note: If the catalog type is ``CatalogType.ABSOLUTE_PUBLISHED``, all self links will be included, and hierarchical links be absolute URLs. If the catalog type is ``CatalogType.RELATIVE_PUBLISHED``, this catalog's self link will be included, but no child catalog will have self links, and hierarchical links will be relative URLs If the catalog type is ``CatalogType.SELF_CONTAINED``, no self links will be included and hierarchical links will be relative URLs. """ root = self.get_root() if root is None: raise Exception("There is no root catalog") if catalog_type is not None: root.catalog_type = catalog_type items_include_self_link = root.catalog_type in [CatalogType.ABSOLUTE_PUBLISHED] for child_link in self.get_child_links(): if child_link.is_resolved(): child = cast(Catalog, child_link.target) if dest_href is not None: rel_href = make_relative_href(child.self_href, self.self_href) child_dest_href = make_absolute_href( rel_href, dest_href, start_is_dir=True ) child.save( dest_href=os.path.dirname(child_dest_href), stac_io=stac_io, ) else: child.save(stac_io=stac_io) for item_link in self.get_item_links(): if item_link.is_resolved(): item = cast(pystac.Item, item_link.target) if dest_href is not None: rel_href = make_relative_href(item.self_href, self.self_href) item_dest_href = make_absolute_href( rel_href, dest_href, start_is_dir=True ) item.save_object( include_self_link=items_include_self_link, dest_href=item_dest_href, stac_io=stac_io, ) else: item.save_object( include_self_link=items_include_self_link, stac_io=stac_io ) include_self_link = False # include a self link if this is the root catalog # or if ABSOLUTE_PUBLISHED catalog if root.catalog_type == CatalogType.ABSOLUTE_PUBLISHED: include_self_link = True elif root.catalog_type != CatalogType.SELF_CONTAINED: root_link = self.get_root_link() if root_link and root_link.get_absolute_href() == self.get_self_href(): include_self_link = True catalog_dest_href = None if dest_href is not None: rel_href = make_relative_href(self.self_href, self.self_href) catalog_dest_href = make_absolute_href( rel_href, dest_href, start_is_dir=True ) self.save_object( include_self_link=include_self_link, dest_href=catalog_dest_href, stac_io=stac_io, ) if catalog_type is not None: self.catalog_type = catalog_type