Skip to content

Commit

Permalink
Merge pull request #25 from sat-utils/develop
Browse files Browse the repository at this point in the history
publish 0.1.0rc6
  • Loading branch information
matthewhanson authored Jan 12, 2019
2 parents b1cca98 + b149c9e commit 16931d2
Show file tree
Hide file tree
Showing 12 changed files with 1,134 additions and 306 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ This is a Python library for working with [Spatio-Temporal Asset Catalogs (STAC)

## Installation

sat-stac cam be installed from this repository, or from PyPi using pip. It has minimal dependencies (the Python requests library only)
sat-stac has minimal dependencies (`requests` and `python-dateutil`). To install sat-stac from PyPi:
sat-stac cam be installed from this repository, or .

```bash
$ pip install sat-stac
```

From source repository:

```bash
$ git clone https://github.com/sat-utils/sat-stac.git
$ cd sat-stac
$ pip install .
```

From pip
```bash
$ pip install sat-stac
```

#### Versions
The initial sat-stac version is 0.1.0, which uses the STAC spec v0.6.0. To install other versions of sat-stac, install the matching version of sat-stac.
Expand All @@ -41,7 +44,7 @@ The table below shows the corresponding versions between sat-stac and STAC:

## Tutorials

See the [Jupyter notebook tutorial](tutorial-1.ipynb) for detailed examples on how to use sat-stac to open and update existing catalogs, and create new ones.
There are two tutorials. [Tutorial-1](tutorial-1.ipynb) includes an overview of how to create and manipulate STAC static catalogs. [Tutorial-2](tutorial-2.ipynb) is on the Python classes that reflect STAC entities: Catalogs, Collections, and Items.

## About
[sat-stac](https://github.com/sat-utils/sat-stac) was created by [Development Seed](<http://developmentseed.org>) and is part of a collection of tools called [sat-utils](https://github.com/sat-utils).
38 changes: 18 additions & 20 deletions satstac/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .collection import Collection
from .item import Item
from .utils import get_text_calendar
from .utils import terminal_calendar


class Items(object):
Expand All @@ -25,7 +25,7 @@ def load(cls, filename):
geoj = json.loads(f.read())
collections = [Collection(col) for col in geoj['collections']]
items = [Item(feature) for feature in geoj['features']]
return cls(items, collections, search=geoj.get('search'))
return cls(items, collections=collections, search=geoj.get('search'))

def __len__(self):
""" Number of scenes """
Expand Down Expand Up @@ -54,7 +54,7 @@ def bbox(self):
lons = [c[0] for c in coords[0]]
return [min(lons), min(lats), max(lons), max(lats)]
else:
return []
return None

def center(self):
if 'intersects' in self._search:
Expand All @@ -63,39 +63,36 @@ def center(self):
lons = [c[0] for c in coords[0]]
return [(min(lats) + max(lats))/2.0, (min(lons) + max(lons))/2.0]
else:
return 0, 0
return None

def platforms(self, date=None):
""" List of all available sensors across scenes """
def properties(self, key, date=None):
""" Set of values for 'key' property in Items, for specific date if provided """
if date is None:
return list(set([i['eo:platform'] for i in self._items]))
return list(set([i[key] for i in self._items]))
else:
return list(set([i['eo:platform'] for i in self._items if i.date == date]))
return list(set([i[key] for i in self._items if i.date == date]))

def print_summary(self, params=[]):
def summary(self, params=[]):
""" Print summary of all scenes """
if len(params) == 0:
params = ['date', 'id']
txt = 'Items (%s):\n' % len(self._items)
txt += ''.join(['{:<20}'.format(p) for p in params]) + '\n'
txt += ''.join(['{:<25} '.format(p) for p in params]) + '\n'
for s in self._items:
# NOTE - the string conversion is because .date returns a datetime obj
txt += ''.join(['{:<20}'.format(str(s[p])) for p in params]) + '\n'
print(txt)
txt += ''.join(['{:<25} '.format(s.substitute('${%s}' % p)) for p in params]) + '\n'
return txt

def text_calendar(self):
def calendar(self):
""" Get calendar for dates """
date_labels = {}
dates = self.dates()
if len(dates) == 0:
return ''
for d in self.dates():
sensors = self.platforms(d)
sensors = self.properties('eo:platform', d)
if len(sensors) > 1:
date_labels[d] = 'Multiple'
else:
date_labels[d] = sensors[0]
return get_text_calendar(date_labels)
return terminal_calendar(date_labels)

def save(self, filename):
""" Save scene metadata """
Expand All @@ -121,10 +118,11 @@ def filter(self, key, values):
items += list(filter(lambda x: x[key] == val, self._items))
self._items = items

def download(self, **kwargs):
def download(self, *args, **kwargs):
""" Download all Items """
dls = []
for i in self._items:
fname = i.download(**kwargs)
fname = i.download(*args, **kwargs)
if fname is not None:
dls.append(fname)
return dls
4 changes: 0 additions & 4 deletions satstac/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,6 @@ def path(self):
""" Return path to this catalog file (None if no filename set) """
return os.path.dirname(self.filename) if self.filename else None

def keys(self):
""" Get keys from catalog """
return self.data.keys()

def links(self, rel=None):
""" Get links for specific rel type """
links = self.data.get('links', [])
Expand Down
53 changes: 22 additions & 31 deletions satstac/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def dict_merge(dct, merge_dct, add_keys=True):


def download_file(url, filename=None):
""" Download a file """
""" Download a file as filename """
filename = os.path.basename(url) if filename is None else filename
logger.info('Downloading %s as %s' % (url, filename))
headers = {}
Expand Down Expand Up @@ -104,6 +104,7 @@ def get_s3_signed_url(url, rtype='GET', public=False, requestor_pays=False, cont
region = os.environ.get('AWS_BUCKET_REGION', os.environ.get('AWS_REGION', 'eu-central-1'))
if access_key is None or secret_key is None:
# if credentials not provided, just try to download without signed URL
logger.debug('Not using signed URL for %s' % url)
return url, None

parts = url.replace('https://', '').split('/')
Expand Down Expand Up @@ -142,7 +143,7 @@ def getSignatureKey(key, dateStamp, regionName, serviceName):
'x-amz-date': amzdate
}
if requestor_pays:
headers['x-amz-request-payer'] = 'requestor'
headers['x-amz-request-payer'] = 'requester'
if public:
headers['x-amz-acl'] = 'public-read'
if os.environ.get('AWS_SESSION_TOKEN') and 'AWS_BUCKET_ACCESS_KEY_ID' not in os.environ:
Expand All @@ -168,44 +169,34 @@ def getSignatureKey(key, dateStamp, regionName, serviceName):
return request_url, headers


def get_text_calendar_dates(date1, date2, cols=3):
""" Get array of datetimes between two dates suitable for formatting """
"""
The return value is a list of years.
Each year contains a list of month rows.
Each month row contains cols months (default 3).
Each month contains list of 6 weeks (the max possible).
Each week contains 1 to 7 days.
Days are datetime.date objects.
"""
year1 = date1.year
year2 = date2.year
def terminal_calendar(events, cols=3):
""" Get calendar covering all dates, with provided dates colored and labeled """
if len(events.keys()) == 0:
return ''
# events is {'date': 'label'}
_dates = sorted(events.keys())
_labels = set(events.values())
labels = dict(zip(_labels, [str(41 + i) for i in range(0, len(_labels))]))

start_year = _dates[0].year
end_year = _dates[-1].year

# start and end rows
row1 = int((date1.month - 1) / cols)
row2 = int((date2.month - 1) / cols) + 1
row1 = int((_dates[0].month - 1) / cols)
row2 = int((_dates[-1].month - 1) / cols) + 1

# generate base calendar array
Calendar = calendar.Calendar()
cal = []
for yr in range(year1, year2+1):
for yr in range(start_year, end_year+1):
ycal = Calendar.yeardatescalendar(yr, width=cols)
if yr == year1 and yr == year2:
if yr == start_year and yr == end_year:
ycal = ycal[row1:row2]
elif yr == year1:
elif yr == start_year:
ycal = ycal[row1:]
elif yr == year2:
elif yr == end_year:
ycal = ycal[:row2]
cal.append(ycal)
return cal


def get_text_calendar(dates, cols=3):
""" Get calendar covering all dates, with provided dates colored and labeled """
_dates = sorted(dates.keys())
_labels = set(dates.values())
labels = dict(zip(_labels, [str(41 + i) for i in range(0, len(_labels))]))
cal = get_text_calendar_dates(_dates[0], _dates[-1])

# month and day headers
months = calendar.month_name
Expand Down Expand Up @@ -237,14 +228,14 @@ def get_text_calendar(dates, cols=3):
else:
string = str(d.day).rjust(2, ' ')
if d in _dates:
string = '%s%sm%s%s' % (col0, labels[dates[d]], string, col_end)
string = '%s%sm%s%s' % (col0, labels[events[d]], string, col_end)
wk.append(string)
out += rformat.format(*wk)
out += '\n'
out += '\n'
# print labels
for lbl, col in labels.items():
vals = list(dates.values())
vals = list(_labels)
out += '%s%sm%s (%s)%s\n' % (col0, col, lbl, vals.count(lbl), col_end)
out += '%s total dates' % len(_dates)
return out
2 changes: 1 addition & 1 deletion satstac/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.0rc5'
__version__ = '0.1.0rc6'
Loading

0 comments on commit 16931d2

Please sign in to comment.