Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
lossyrob committed Sep 12, 2019
0 parents commit 1fe8811
Show file tree
Hide file tree
Showing 38 changed files with 2,797 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*.pyc
*.egg-info
build/
dist/
*.eggs
MANIFEST
.DS_Store
.coverage
.cache
data
config.json
stdout*
/integration*
.idea/
docs/_build/
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## [v0.1.0] - 2019-01-13

Initial Release
15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
This software is licensed under the Apache 2 license, quoted below.

Copyright 2017 Azavea [http://www.azavea.com]

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

[http://www.apache.org/licenses/LICENSE-2.0]

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## PySTAC

TKTK
26 changes: 26 additions & 0 deletions pystac/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from pystac.version import (__version__, STAC_VERSION)

class STACError(Exception):
pass

from pystac.io import STAC_IO
from pystac.link import Link
from pystac.catalog import Catalog
from pystac.collection import (Collection, Extent, SpatialExtent, TemporalExtent, Provider)
from pystac.item import (Item, Asset)
from pystac.eo import *
from pystac.label import *

def stac_object_from_dict(d):
"""Determines how to deserialize a dictionary into a STAC object."""
if 'type' in d:
if 'label:description' in d['properties']:
return LabelItem.from_dict(d)
else:
return Item.from_dict(d)
elif 'extent' in d:
return Collection.from_dict(d)
else:
return Catalog.from_dict(d)

STAC_IO.stac_object_from_dict = stac_object_from_dict
235 changes: 235 additions & 0 deletions pystac/catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import os
import json

from pystac import STAC_VERSION
from pystac.stac_object import STACObject
from pystac.io import STAC_IO
from pystac.link import Link
from pystac.resolved_object_cache import ResolvedObjectCache

class Catalog(STACObject):
DEFAULT_FILE_NAME = "catalog.json"

def __init__(self, id, description, title=None, href=None):
self.id = id
self.description = description
self.title = title
self.links = [Link.root(self)]

if href is not None:
self.set_self_href(href)

self._resolved_objects = ResolvedObjectCache()

def __repr__(self):
return '<Catalog id={}>'.format(self.id)

def set_root(self, root, recursive=False):
STACObject.set_root(self, root)
root._resolved_objects = ResolvedObjectCache.merge(root._resolved_objects,
self._resolved_objects)

if recursive:
for child in self.get_children():
child.set_root(root, recurive=True)
for item in self.get_items():
item.set_root(root)

def add_child(self, child, title=None):
child.set_root(self.get_root())
child.set_parent(self)
self.add_link(Link.child(child, title=title))

def add_item(self, item, title=None):
item.set_root(self.get_root())
item.set_parent(self)
self.add_link(Link.item(item, title=title))

def add_items(self, items):
for item in items:
self.add_item(item)

def get_child(self, id):
return next((c for c in self.get_children() if c.id == id), None)

def get_children(self):
return self.get_stac_objects('child', parent=self)

def get_child_links(self):
return self.get_links('child')

def clear_children(self):
self.links = [l for l in self.links if l.rel != 'child']
return self

def get_item(self, id):
return next((i for i in self.get_items() if i.id == id), None)

def get_items(self):
return self.get_stac_objects('item', parent=self)

def clear_items(self):
self.links = [l for l in self.links if l.rel != 'item']
return self

def get_all_items(self):
"""Get all items from this catalog and all subcatalogs."""
items = self.get_items()
for child in self.get_children():
items += child.get_all_items()

return items

def get_item_links(self):
return self.get_links('item')

def to_dict(self):
d = {
'id': self.id,
'stac_version': STAC_VERSION,
'description': self.description,
'links': [l.to_dict() for l in self.links]
}

if self.title is not None:
d['title'] = self.title

return d

def clone(self):
clone = Catalog(id=self.id,
description=self.description,
title=self.title)
clone._resolved_objects.set(clone)

clone.add_links([l.clone() for l in self.links])

return clone

def set_uris_from_root(self, root_uri):
self.set_self_href(os.path.join(root_uri, self.DEFAULT_FILE_NAME))
for child in self.get_children():
child_root = os.path.join(root_uri, '{}/'.format(child.id))
child.set_uris_from_root(child_root)
for item in self.get_items():
item.set_self_href(os.path.join(root_uri, '{}.json'.format(item.id)))

def set_relative_paths(self, include_assets=True):
"""Converts all HREFs in links (and optionally assets) into relative paths.
Any path that does not share a root with the self HREF (i.e. cannot be made relative)
will be skipped.
"""
self_href = self.get_self_href()
if self_href is None:
raise STACError('Self HREFs must be set in order to make relative paths.')

os.path.basename(self_href)

def save(self):
for child_link in self.get_child_links():
if child_link.is_resolved():
child_link.target.save()

for item_link in self.get_item_links():
if item_link.is_resolved():
item_link.target.save()

STAC_IO.save_json(self.get_self_href(), self.to_dict())

def map_items(self, item_mapper):
"""Creates a copy of a catalog, with each item passed through the item_mapper function.
Args:
item_mapper: A function that takes in an item, and returns either an item or list of items.
The item that is passed into the item_mapper is a copy, so the method can mutate it safetly.
"""

new_cat = self.full_copy()

def process_catalog(catalog):
for child in catalog.get_children():
process_catalog(child)

item_links = []
for item_link in catalog.get_item_links():
mapped = item_mapper(item_link.target)
if type(mapped) is not list:
item_link.target = mapped
item_links.append(item_link)
else:
for i in mapped:
l = item_link.clone()
l.target = i
item_links.append(l)
catalog.clear_items()
catalog.add_links(item_links)

process_catalog(new_cat)
return new_cat

def map_assets(self, asset_mapper):
"""Creates a copy of a catalog, with each Asset for each Item passed
through the asset_mapper function.
Args:
asset_mapper: A function that takes in an key and an Asset, and returns
either an Asset, a (key, Asset), or a dictionary of Assets with unique keys.
The Asset that is passed into the item_mapper is a copy, so the method can mutate it safetly.
"""
def apply_asset_mapper(tup):
k, v = tup
result = asset_mapper(k, v)
if issubclass(type(result), Asset):
return [(k, result)]
elif isinstance(result, tuple):
return [result]
else:
assets = list(result.items())
if len(assets) < 1:
raise Exception('asset_mapper must return a non-empy list')
return assets

def item_mapper(item):
new_assets = [x for result in map(apply_asset_mapper, item.assets.items())
for x in result]
item.assets = dict(new_assets)
return item

return self.map_items(item_mapper)

def describe(self, indent=0, include_hrefs=False):
s = '{}* {}'.format(' ' * indent, self)
if include_hrefs:
s += ' {}'.format(self.get_self_href())
print(s)
for child in self.get_children():
child.describe(indent=indent+4)
for item in self.get_items():
s = '{}* {}'.format(' ' * (indent+2), item)
if include_hrefs:
s += ' {}'.format(item.get_self_href())
print(s)

@staticmethod
def from_dict(d):
id = d['id']
description = d['description']
title = d.get('title')

cat = Catalog(id=id,
description=description,
title=title)

for l in d['links']:
if not l['rel'] == 'root':
cat.add_link(Link.from_dict(l))

return cat

@staticmethod
def from_file(uri):
d = json.loads(STAC_IO.read_text(uri))
c = Catalog.from_dict(d)
c.set_self_href(uri)
return c
Loading

0 comments on commit 1fe8811

Please sign in to comment.