Skip to content

Commit

Permalink
Merge pull request #9 from stac-utils/rde/fixes
Browse files Browse the repository at this point in the history
Small fixes
  • Loading branch information
lossyrob authored Nov 14, 2020
2 parents 8d8cd33 + 62c3535 commit b4fa34c
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 42 deletions.
4 changes: 3 additions & 1 deletion stactools_cli/stactools/cli/commands/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ def create_copy_command(cli):
'be alongside the new item location.'))
def copy_command(src, dst, catalog_type, copy_assets):
"""Copy a STAC Catalog or Collection at SRC to the directory
at DST."""
at DST.
Note: Copying a catalog will upgrade it to the latest version of STAC."""
source_catalog = pystac.read_file(src)
copy_catalog(source_catalog, dst, catalog_type, copy_assets)

Expand Down
3 changes: 2 additions & 1 deletion stactools_cli/stactools/cli/commands/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ def create_describe_command(cli):
short_help='Prints out a list of all catalogs, collections and items '
'in this STAC.')
@click.argument('catalog_path')
@click.option('--include-hrefs',
@click.option('-h',
'--include-hrefs',
is_flag=True,
help='Include HREFs in description.')
def describe_command(catalog_path, include_hrefs):
Expand Down
10 changes: 6 additions & 4 deletions stactools_core/stactools/core/copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,14 @@ def move_all_assets(catalog,
this function will throw an error unless ignore_conflicts is True.
Returns:
[Catalog or Collection]: Returns an updated catalog or collection.
Note that this method does not mutate the original catalog.
[Catalog or Collection]: Returns the updated catalog.
This operation mutates the catalog.
"""
for item in catalog.get_all_items():
move_assets(item, asset_subdirectory, href_type, copy,
ignore_conflicts)

return catalog.map_items(lambda item: move_assets(
item, asset_subdirectory, href_type, copy, ignore_conflicts))
return catalog


def copy_catalog(source_catalog,
Expand Down
16 changes: 10 additions & 6 deletions stactools_core/stactools/core/merge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
from pystac.utils import (is_absolute_href, make_relative_href)
from shapely.geometry import shape, mapping

from stactools.core.copy import (move_asset_file_to_item)
from stactools.core.copy import (move_asset_file_to_item, move_assets as
do_move_assets)


def merge_items(source_item,
Expand Down Expand Up @@ -108,13 +109,16 @@ def merge_all_items(source_catalog,
target_catalog.add_item(item_copy)

if isinstance(target_catalog, pystac.Collection):
item.set_collection(target_catalog)
item_copy.set_collection(target_catalog)
else:
item.set_collection(None)
item_copy.set_collection(None)

if move_assets:
move_assets(item_copy,
href_type=pystac.LinkType.RELATIVE,
copy=False)
do_move_assets(item_copy,
href_type=pystac.LinkType.RELATIVE,
copy=False)

if target_catalog.STAC_OBJECT_TYPE == pystac.STACObjectType.COLLECTION:
target_catalog.update_extent_from_items()

return target_catalog
2 changes: 2 additions & 0 deletions stactools_planet/stactools/planet/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pystac

PLANET_EXTENSION_PREFIX = 'pl'

PLANET_PROVIDER = pystac.Provider(
name='Planet',
description=(
Expand Down
5 changes: 5 additions & 0 deletions stactools_planet/stactools/planet/planet_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from shapely.geometry import shape

from stactools.planet import PLANET_PROVIDER
from stactools.planet.constants import PLANET_EXTENSION_PREFIX

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -64,6 +65,10 @@ def to_stac(self):
item.ext.enable('projection')
item.ext.projection.epsg = props.pop('epsg_code')

# Add all additional properties with Planet extension designation.
for k, v in props.items():
item.properties['{}:{}'.format(PLANET_EXTENSION_PREFIX, k)] = v

for planet_asset in self.item_assets:
href = make_absolute_href(planet_asset['path'],
start_href=self.base_dir,
Expand Down
19 changes: 5 additions & 14 deletions tests/cli/commands/test_copy.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,21 @@
import os
import unittest
from tempfile import TemporaryDirectory

import click
from click.testing import CliRunner
import pystac

from stactools.cli.commands.copy import create_copy_command
from tests.utils import TestCases
from tests.utils import (TestCases, CliTestCase)


class CopyTest(unittest.TestCase):
def setUp(self):
@click.group()
def cli():
pass

create_copy_command(cli)
self.cli = cli
class CopyTest(CliTestCase):
def create_subcommand_functions(self):
return [create_copy_command]

def test_copy(self):
cat = TestCases.planet_disaster()
item_ids = set([i.id for i in cat.get_all_items()])
with TemporaryDirectory() as tmp_dir:
runner = CliRunner()
runner.invoke(self.cli, ['copy', cat.get_self_href(), tmp_dir])
self.run_command(['copy', cat.get_self_href(), tmp_dir])

copy_cat = pystac.read_file(
os.path.join(tmp_dir, 'collection.json'))
Expand Down
99 changes: 99 additions & 0 deletions tests/cli/commands/test_merge.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import os
from tempfile import TemporaryDirectory

import pystac

from stactools.cli.commands.merge import create_merge_command
from stactools.core import move_all_assets
from tests.utils import (TestCases, CliTestCase)


def copy_two_planet_disaster_subsets(tmp_dir):
"""Test setup util that makes two copies of subset of the planet
disaster data, each containing a single item. Updates the collection
extents to match the single items.
Returns a list of collection paths in the temporary directory.
"""
item_ids = ['20170831_172754_101c', '20170831_162740_ssc1d1']
new_cols = []
for item_id in item_ids:
col = TestCases.planet_disaster()
for item in list(col.get_all_items()):
if item.id != item_id:
item.get_parent().remove_item(item.id)
col.update_extent_from_items()
col.normalize_hrefs(os.path.join(tmp_dir, item_id))
col.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)
move_all_assets(col, copy=True)
col.save()
new_cols.append(col.get_self_href())

return new_cols


class MergeTest(CliTestCase):
def create_subcommand_functions(self):
return [create_merge_command]

def test_merge_moves_assets(self):
with TemporaryDirectory() as tmp_dir:
col_paths = copy_two_planet_disaster_subsets(tmp_dir)

cmd = ['merge', '-a', col_paths[0], col_paths[1]]

self.run_command(cmd)

target_col = pystac.read_file(col_paths[1])

items = list(target_col.get_all_items())
self.assertEqual(len(items), 2)

for item in items:
for asset in item.assets.values():
self.assertEqual(
os.path.dirname(asset.get_absolute_href()),
os.path.dirname(item.get_self_href()))

def test_merge_updates_collection_extent(self):
with TemporaryDirectory() as tmp_dir:
col_paths = copy_two_planet_disaster_subsets(tmp_dir)

extent1 = pystac.read_file(col_paths[0]).extent
extent2 = pystac.read_file(col_paths[1]).extent

xmin = min(
[extent1.spatial.bboxes[0][0], extent2.spatial.bboxes[0][0]])
time_max = max([
extent1.temporal.intervals[0][1],
extent2.temporal.intervals[0][1],
])

cmd = ['merge', col_paths[0], col_paths[1]]

self.run_command(cmd)

result_extent = pystac.read_file(col_paths[1]).extent

def set_of_values(x):
result = set([])
if type(x) is dict:
result |= set_of_values(list(x.values()))
elif type(x) is list:
for e in x:
if type(e) is list:
result |= set_of_values(e)
else:
result.add(e)
return result

# Make sure it didn't just carry forward the old extent
self.assertNotEqual(set_of_values(result_extent.spatial.bboxes),
set_of_values(extent2.spatial.bboxes))

self.assertNotEqual(
set_of_values(result_extent.temporal.intervals),
set_of_values(extent2.temporal.intervals))

self.assertEqual(result_extent.spatial.bboxes[0][0], xmin)
self.assertEqual(result_extent.temporal.intervals[0][1], time_max)
25 changes: 9 additions & 16 deletions tests/planet/test_commands.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
import os
import unittest
from tempfile import TemporaryDirectory

import click
from click.testing import CliRunner
import pystac

from stactools.planet.commands import create_planet_command
from tests.utils import TestCases
from tests.utils import (TestCases, CliTestCase)


class ConvertOrderTest(unittest.TestCase):
def setUp(self):
@click.group()
def cli():
pass

create_planet_command(cli)
self.cli = cli
class ConvertOrderTest(CliTestCase):
def create_subcommand_functions(self):
return [create_planet_command]

def test_converts(self):
test_order_manifest = TestCases.get_path(
'data-files/planet-order/manifest.json')

with TemporaryDirectory() as tmp_dir:
runner = CliRunner()
result = runner.invoke(self.cli, [
result = self.run_command([
'planet', 'convert-order', test_order_manifest, tmp_dir,
'test_id', 'A test catalog', '--title', 'test-catalog', '-a',
'copy'
],
catch_exceptions=False)
])

self.assertEqual(result.exit_code,
0,
Expand All @@ -46,3 +36,6 @@ def test_converts(self):
self.assertTrue(os.path.exists(asset.get_absolute_href()))
self.assertEqual(os.path.dirname(asset.get_absolute_href()),
os.path.dirname(item.get_self_href()))

self.assertEqual(item.properties.get('pl:quality_category'),
'standard')
1 change: 1 addition & 0 deletions tests/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# flake8: noqa

from tests.utils.test_cases import TestCases
from tests.utils.cli_test import CliTestCase
25 changes: 25 additions & 0 deletions tests/utils/cli_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from abc import (ABC, abstractmethod)
import unittest

import click
from click.testing import CliRunner


class CliTestCase(unittest.TestCase, ABC):
def setUp(self):
@click.group()
def cli():
pass

for create_subcommand in self.create_subcommand_functions():
create_subcommand(cli)
self.cli = cli

def run_command(self, cmd):
runner = CliRunner()
return runner.invoke(self.cli, cmd, catch_exceptions=False)

@abstractmethod
def create_subcommand_functions(self):
"""Return list of 'create_command' functions from implementations"""
pass

0 comments on commit b4fa34c

Please sign in to comment.