Skip to content

Commit

Permalink
Merge pull request #223 from stac-utils/feature/rde/extent-from-items
Browse files Browse the repository at this point in the history
Add 'from_items' to Extent
  • Loading branch information
lossyrob authored Oct 28, 2020
2 parents 0040d84 + 35a528b commit 5265b5b
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 35 deletions.
66 changes: 40 additions & 26 deletions pystac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,32 +180,7 @@ def update_extent_from_items(self):
"""
Update datetime and bbox based on all items to a single bbox and time window.
"""
def extract_extent_props(item):
return item.bbox + [
item.datetime, item.common_metadata.start_datetime,
item.common_metadata.end_datetime
]

xmins, ymins, xmaxs, ymaxs, datetimes, starts, ends = zip(
*map(extract_extent_props, self.get_all_items()))

if not any(datetimes + starts):
start_timestamp = None
else:
start_timestamp = min([
dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC)
for dt in filter(None, datetimes + starts)
])
if not any(datetimes + ends):
end_timestamp = None
else:
end_timestamp = max([
dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC)
for dt in filter(None, datetimes + ends)
])

self.extent.spatial.bboxes = [[min(xmins), min(ymins), max(xmaxs), max(ymaxs)]]
self.extent.temporal.intervals = [[start_timestamp, end_timestamp]]
self.extent = Extent.from_items(self.get_all_items())


class Extent:
Expand Down Expand Up @@ -262,6 +237,45 @@ def from_dict(d):
return Extent(SpatialExtent.from_dict(spatial_extent_dict),
TemporalExtent.from_dict(temporal_extent_dict))

@staticmethod
def from_items(items):
"""Create an Extent based on the datetimes and bboxes of a list of items.
Args:
items (List[Item]): A list of items to derive the extent from.
Returns:
Extent: An Extent that spatially and temporally covers all of the
given items.
"""
def extract_extent_props(item):
return item.bbox + [
item.datetime, item.common_metadata.start_datetime,
item.common_metadata.end_datetime
]

xmins, ymins, xmaxs, ymaxs, datetimes, starts, ends = zip(*map(extract_extent_props, items))

if not any(datetimes + starts):
start_timestamp = None
else:
start_timestamp = min([
dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC)
for dt in filter(None, datetimes + starts)
])
if not any(datetimes + ends):
end_timestamp = None
else:
end_timestamp = max([
dt if dt.tzinfo else dt.replace(tzinfo=tz.UTC)
for dt in filter(None, datetimes + ends)
])

spatial = SpatialExtent([[min(xmins), min(ymins), max(xmaxs), max(ymaxs)]])
temporal = TemporalExtent([[start_timestamp, end_timestamp]])

return Extent(spatial, temporal)


class SpatialExtent:
"""Describes the spatial extent of a Collection.
Expand Down
44 changes: 35 additions & 9 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
from tempfile import TemporaryDirectory
from datetime import datetime
from dateutil import tz

import pystac
from pystac.validation import validate_dict
Expand Down Expand Up @@ -223,17 +224,42 @@ def test_spatial_allows_single_bbox(self):

collection.validate()

def test_temporal_allows_single_interval(self):
spatial_extent = SpatialExtent(bboxes=[RANDOM_BBOX])
def test_from_items(self):
item1 = Item(id='test-item-1',
geometry=RANDOM_GEOM,
bbox=[-10, -20, 0, -10],
datetime=datetime(2000, 2, 1, 12, 0, 0, 0, tzinfo=tz.UTC),
properties={})

# Pass in a single interval
temporal_extent = TemporalExtent(intervals=[TEST_DATETIME, None])
item2 = Item(id='test-item-2',
geometry=RANDOM_GEOM,
bbox=[0, -9, 10, 1],
datetime=None,
properties={
'start_datetime':
datetime_to_str(datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC)),
'end_datetime':
datetime_to_str(datetime(2000, 7, 1, 12, 0, 0, 0, tzinfo=tz.UTC))
})

collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent)
item3 = Item(id='test-item-2',
geometry=RANDOM_GEOM,
bbox=[-5, -20, 5, 0],
datetime=None,
properties={
'start_datetime':
datetime_to_str(datetime(2000, 12, 1, 12, 0, 0, 0, tzinfo=tz.UTC)),
'end_datetime':
datetime_to_str(datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC), )
})

collection = Collection(id='test', description='test desc', extent=collection_extent)
extent = Extent.from_items([item1, item2, item3])

# HREF required by validation
collection.set_self_href('https://example.com/collection.json')
self.assertEqual(len(extent.spatial.bboxes), 1)
self.assertEqual(extent.spatial.bboxes[0], [-10, -20, 10, 1])

collection.validate()
self.assertEqual(len(extent.temporal.intervals), 1)
interval = extent.temporal.intervals[0]

self.assertEqual(interval[0], datetime(2000, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC))
self.assertEqual(interval[1], datetime(2001, 1, 1, 12, 0, 0, 0, tzinfo=tz.UTC))

0 comments on commit 5265b5b

Please sign in to comment.