Skip to content

Commit 1fe8811

Browse files
committed
Initial commit.
0 parents  commit 1fe8811

38 files changed

+2797
-0
lines changed

.gitignore

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
*.pyc
2+
*.egg-info
3+
build/
4+
dist/
5+
*.eggs
6+
MANIFEST
7+
.DS_Store
8+
.coverage
9+
.cache
10+
data
11+
config.json
12+
stdout*
13+
/integration*
14+
.idea/
15+
docs/_build/

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## [v0.1.0] - 2019-01-13
4+
5+
Initial Release

LICENSE

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
This software is licensed under the Apache 2 license, quoted below.
2+
3+
Copyright 2017 Azavea [http://www.azavea.com]
4+
5+
Licensed under the Apache License, Version 2.0 (the "License"); you may not
6+
use this file except in compliance with the License. You may obtain a copy of
7+
the License at
8+
9+
[http://www.apache.org/licenses/LICENSE-2.0]
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
License for the specific language governing permissions and limitations under
15+
the License.

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## PySTAC
2+
3+
TKTK

pystac/__init__.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from pystac.version import (__version__, STAC_VERSION)
2+
3+
class STACError(Exception):
4+
pass
5+
6+
from pystac.io import STAC_IO
7+
from pystac.link import Link
8+
from pystac.catalog import Catalog
9+
from pystac.collection import (Collection, Extent, SpatialExtent, TemporalExtent, Provider)
10+
from pystac.item import (Item, Asset)
11+
from pystac.eo import *
12+
from pystac.label import *
13+
14+
def stac_object_from_dict(d):
15+
"""Determines how to deserialize a dictionary into a STAC object."""
16+
if 'type' in d:
17+
if 'label:description' in d['properties']:
18+
return LabelItem.from_dict(d)
19+
else:
20+
return Item.from_dict(d)
21+
elif 'extent' in d:
22+
return Collection.from_dict(d)
23+
else:
24+
return Catalog.from_dict(d)
25+
26+
STAC_IO.stac_object_from_dict = stac_object_from_dict

pystac/catalog.py

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import os
2+
import json
3+
4+
from pystac import STAC_VERSION
5+
from pystac.stac_object import STACObject
6+
from pystac.io import STAC_IO
7+
from pystac.link import Link
8+
from pystac.resolved_object_cache import ResolvedObjectCache
9+
10+
class Catalog(STACObject):
11+
DEFAULT_FILE_NAME = "catalog.json"
12+
13+
def __init__(self, id, description, title=None, href=None):
14+
self.id = id
15+
self.description = description
16+
self.title = title
17+
self.links = [Link.root(self)]
18+
19+
if href is not None:
20+
self.set_self_href(href)
21+
22+
self._resolved_objects = ResolvedObjectCache()
23+
24+
def __repr__(self):
25+
return '<Catalog id={}>'.format(self.id)
26+
27+
def set_root(self, root, recursive=False):
28+
STACObject.set_root(self, root)
29+
root._resolved_objects = ResolvedObjectCache.merge(root._resolved_objects,
30+
self._resolved_objects)
31+
32+
if recursive:
33+
for child in self.get_children():
34+
child.set_root(root, recurive=True)
35+
for item in self.get_items():
36+
item.set_root(root)
37+
38+
def add_child(self, child, title=None):
39+
child.set_root(self.get_root())
40+
child.set_parent(self)
41+
self.add_link(Link.child(child, title=title))
42+
43+
def add_item(self, item, title=None):
44+
item.set_root(self.get_root())
45+
item.set_parent(self)
46+
self.add_link(Link.item(item, title=title))
47+
48+
def add_items(self, items):
49+
for item in items:
50+
self.add_item(item)
51+
52+
def get_child(self, id):
53+
return next((c for c in self.get_children() if c.id == id), None)
54+
55+
def get_children(self):
56+
return self.get_stac_objects('child', parent=self)
57+
58+
def get_child_links(self):
59+
return self.get_links('child')
60+
61+
def clear_children(self):
62+
self.links = [l for l in self.links if l.rel != 'child']
63+
return self
64+
65+
def get_item(self, id):
66+
return next((i for i in self.get_items() if i.id == id), None)
67+
68+
def get_items(self):
69+
return self.get_stac_objects('item', parent=self)
70+
71+
def clear_items(self):
72+
self.links = [l for l in self.links if l.rel != 'item']
73+
return self
74+
75+
def get_all_items(self):
76+
"""Get all items from this catalog and all subcatalogs."""
77+
items = self.get_items()
78+
for child in self.get_children():
79+
items += child.get_all_items()
80+
81+
return items
82+
83+
def get_item_links(self):
84+
return self.get_links('item')
85+
86+
def to_dict(self):
87+
d = {
88+
'id': self.id,
89+
'stac_version': STAC_VERSION,
90+
'description': self.description,
91+
'links': [l.to_dict() for l in self.links]
92+
}
93+
94+
if self.title is not None:
95+
d['title'] = self.title
96+
97+
return d
98+
99+
def clone(self):
100+
clone = Catalog(id=self.id,
101+
description=self.description,
102+
title=self.title)
103+
clone._resolved_objects.set(clone)
104+
105+
clone.add_links([l.clone() for l in self.links])
106+
107+
return clone
108+
109+
def set_uris_from_root(self, root_uri):
110+
self.set_self_href(os.path.join(root_uri, self.DEFAULT_FILE_NAME))
111+
for child in self.get_children():
112+
child_root = os.path.join(root_uri, '{}/'.format(child.id))
113+
child.set_uris_from_root(child_root)
114+
for item in self.get_items():
115+
item.set_self_href(os.path.join(root_uri, '{}.json'.format(item.id)))
116+
117+
def set_relative_paths(self, include_assets=True):
118+
"""Converts all HREFs in links (and optionally assets) into relative paths.
119+
120+
Any path that does not share a root with the self HREF (i.e. cannot be made relative)
121+
will be skipped.
122+
"""
123+
self_href = self.get_self_href()
124+
if self_href is None:
125+
raise STACError('Self HREFs must be set in order to make relative paths.')
126+
127+
os.path.basename(self_href)
128+
129+
def save(self):
130+
for child_link in self.get_child_links():
131+
if child_link.is_resolved():
132+
child_link.target.save()
133+
134+
for item_link in self.get_item_links():
135+
if item_link.is_resolved():
136+
item_link.target.save()
137+
138+
STAC_IO.save_json(self.get_self_href(), self.to_dict())
139+
140+
def map_items(self, item_mapper):
141+
"""Creates a copy of a catalog, with each item passed through the item_mapper function.
142+
143+
Args:
144+
item_mapper: A function that takes in an item, and returns either an item or list of items.
145+
The item that is passed into the item_mapper is a copy, so the method can mutate it safetly.
146+
"""
147+
148+
new_cat = self.full_copy()
149+
150+
def process_catalog(catalog):
151+
for child in catalog.get_children():
152+
process_catalog(child)
153+
154+
item_links = []
155+
for item_link in catalog.get_item_links():
156+
mapped = item_mapper(item_link.target)
157+
if type(mapped) is not list:
158+
item_link.target = mapped
159+
item_links.append(item_link)
160+
else:
161+
for i in mapped:
162+
l = item_link.clone()
163+
l.target = i
164+
item_links.append(l)
165+
catalog.clear_items()
166+
catalog.add_links(item_links)
167+
168+
process_catalog(new_cat)
169+
return new_cat
170+
171+
def map_assets(self, asset_mapper):
172+
"""Creates a copy of a catalog, with each Asset for each Item passed
173+
through the asset_mapper function.
174+
175+
Args:
176+
asset_mapper: A function that takes in an key and an Asset, and returns
177+
either an Asset, a (key, Asset), or a dictionary of Assets with unique keys.
178+
The Asset that is passed into the item_mapper is a copy, so the method can mutate it safetly.
179+
"""
180+
def apply_asset_mapper(tup):
181+
k, v = tup
182+
result = asset_mapper(k, v)
183+
if issubclass(type(result), Asset):
184+
return [(k, result)]
185+
elif isinstance(result, tuple):
186+
return [result]
187+
else:
188+
assets = list(result.items())
189+
if len(assets) < 1:
190+
raise Exception('asset_mapper must return a non-empy list')
191+
return assets
192+
193+
def item_mapper(item):
194+
new_assets = [x for result in map(apply_asset_mapper, item.assets.items())
195+
for x in result]
196+
item.assets = dict(new_assets)
197+
return item
198+
199+
return self.map_items(item_mapper)
200+
201+
def describe(self, indent=0, include_hrefs=False):
202+
s = '{}* {}'.format(' ' * indent, self)
203+
if include_hrefs:
204+
s += ' {}'.format(self.get_self_href())
205+
print(s)
206+
for child in self.get_children():
207+
child.describe(indent=indent+4)
208+
for item in self.get_items():
209+
s = '{}* {}'.format(' ' * (indent+2), item)
210+
if include_hrefs:
211+
s += ' {}'.format(item.get_self_href())
212+
print(s)
213+
214+
@staticmethod
215+
def from_dict(d):
216+
id = d['id']
217+
description = d['description']
218+
title = d.get('title')
219+
220+
cat = Catalog(id=id,
221+
description=description,
222+
title=title)
223+
224+
for l in d['links']:
225+
if not l['rel'] == 'root':
226+
cat.add_link(Link.from_dict(l))
227+
228+
return cat
229+
230+
@staticmethod
231+
def from_file(uri):
232+
d = json.loads(STAC_IO.read_text(uri))
233+
c = Catalog.from_dict(d)
234+
c.set_self_href(uri)
235+
return c

0 commit comments

Comments
 (0)