Skip to content

Commit dbac6b7

Browse files
authored
Merge pull request stac-utils#20 from azavea/sk/add-item-collection-property
add collection property
2 parents 39f6f11 + 4584a4b commit dbac6b7

File tree

8 files changed

+170
-19
lines changed

8 files changed

+170
-19
lines changed

pystac/catalog.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import json
3+
from copy import (deepcopy, copy)
34

45
from pystac import STACError
56
from pystac import STAC_VERSION
@@ -135,7 +136,7 @@ def to_dict(self, include_self_link=True):
135136
if self.title is not None:
136137
d['title'] = self.title
137138

138-
return d
139+
return deepcopy(d)
139140

140141
def clone(self):
141142
clone = Catalog(id=self.id,

pystac/collection.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from datetime import datetime
33
from datetime import timezone
44
import dateutil.parser
5-
from copy import copy
5+
from copy import (copy, deepcopy)
66
import json
77

88
from pystac import STACError, STAC_IO
@@ -62,7 +62,7 @@ def to_dict(self, include_self_link=True):
6262
if self.summaries is not None:
6363
d['summaries'] = self.summaries
6464

65-
return d
65+
return deepcopy(d)
6666

6767
def clone(self):
6868
clone = Collection(id=self.id,
@@ -139,11 +139,13 @@ def __init__(self, spatial, temporal):
139139
self.temporal = temporal
140140

141141
def to_dict(self):
142-
return {
142+
d = {
143143
'spatial': self.spatial.to_dict(),
144144
'temporal': self.temporal.to_dict()
145145
}
146146

147+
return deepcopy(d)
148+
147149
def clone(self):
148150
return Extent(spatial=copy(self.spatial),
149151
temporal=copy(self.temporal))
@@ -159,7 +161,8 @@ def __init__(self, bboxes):
159161
self.bboxes = bboxes
160162

161163
def to_dict(self):
162-
return { 'bbox' : self.bboxes }
164+
d = { 'bbox' : self.bboxes }
165+
return deepcopy(d)
163166

164167
def clone(self):
165168
return SpatialExtent(self.bboxes)
@@ -214,7 +217,8 @@ def to_dict(self):
214217

215218
encoded_intervals.append([start, end])
216219

217-
return { 'interval': encoded_intervals }
220+
d = {'interval': encoded_intervals}
221+
return deepcopy(d)
218222

219223
def clone(self):
220224
return TemporalExtent(intervals=copy(self.intervals))
@@ -256,7 +260,7 @@ def to_dict(self):
256260
if self.url is not None:
257261
d['url'] = self.url
258262

259-
return d
263+
return deepcopy(d)
260264

261265
@staticmethod
262266
def from_dict(d):

pystac/item.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ def __init__(self,
1818
datetime,
1919
properties,
2020
stac_extensions=None,
21-
href=None):
21+
href=None,
22+
collection=None):
2223
self.id = id
2324
self.geometry = geometry
2425
self.bbox = bbox
2526
self.datetime = datetime
2627
self.properties = properties
2728
self.stac_extensions = stac_extensions
29+
self.collection = collection
2830

2931
self.links = []
3032
self.assets = {}
@@ -62,6 +64,7 @@ def set_collection(self, collection, link_type=None):
6264
link_type = LinkType.ABSOLUTE
6365
self.remove_links('collection')
6466
self.add_link(Link.collection(collection, link_type=link_type))
67+
self.collection = collection.id
6568
return self
6669

6770
def to_dict(self, include_self_link=True):
@@ -86,8 +89,11 @@ def to_dict(self, include_self_link=True):
8689

8790
if self.stac_extensions is not None:
8891
d['stac_extensions'] = self.stac_extensions
92+
93+
if self.collection:
94+
d['collection'] = self.collection
8995

90-
return d
96+
return deepcopy(d)
9197

9298
def clone(self):
9399
clone = Item(id=self.id,
@@ -130,6 +136,9 @@ def from_dict(d):
130136
bbox = d['bbox']
131137
properties = d['properties']
132138
stac_extensions = d.get('stac_extensions')
139+
collection = None
140+
if 'collection' in d.keys():
141+
collection = d['collection']
133142

134143
datetime = properties.get('datetime')
135144
if datetime is None:
@@ -141,7 +150,8 @@ def from_dict(d):
141150
bbox=bbox,
142151
datetime=datetime,
143152
properties=properties,
144-
stac_extensions=stac_extensions)
153+
stac_extensions=stac_extensions,
154+
collection=collection)
145155

146156
for l in d['links']:
147157
item.add_link(Link.from_dict(l))
@@ -178,7 +188,7 @@ def __init__(self, href, title=None, media_type=None, properties=None):
178188
self.href = href
179189
self.title = title
180190
self.media_type = media_type
181-
self.properties = None
191+
self.properties = properties
182192

183193
# The Item which owns this Asset.
184194
self.item = None
@@ -210,10 +220,10 @@ def to_dict(self):
210220
d['title'] = self.title
211221

212222
if self.properties is not None:
213-
for k in properties:
214-
d[k] = properties[k]
223+
for k, v in self.properties.items():
224+
d[k] = v
215225

216-
return d
226+
return deepcopy(d)
217227

218228
def clone(self):
219229
return Asset(href=self.href,
@@ -229,7 +239,6 @@ def from_dict(d):
229239
href = d.pop('href')
230240
media_type = d.pop('type', None)
231241
title = d.pop('title', None)
232-
233242
properties = None
234243
if any(d):
235244
properties = d

pystac/label.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def to_dict(self, include_self_link=True):
9292
if self.label_overviews is not None:
9393
d['properties']['label:overviews'] = [ov.to_dict() for ov in self.label_overviews]
9494

95-
return d
95+
return deepcopy(d)
9696

9797
def add_source(self, source_item, title=None, assets=None):
9898
properties = None

pystac/link.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import os
2-
from copy import copy
2+
from copy import (copy, deepcopy)
33
from urllib.parse import urlparse
44

55
from pystac import STACError
@@ -127,7 +127,7 @@ def to_dict(self):
127127
for k, v in self.properties.items():
128128
d[k] = v
129129

130-
return d
130+
return deepcopy(d)
131131

132132
def clone(self):
133133
return Link(rel=self.rel,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"type": "FeatureCollection",
3+
"features": [
4+
{
5+
"stac_version": "0.8.0",
6+
"stac_extensions": [],
7+
"type": "Feature",
8+
"id": "CS3-20160503_132131_05",
9+
"bbox": [
10+
-122.59750209,
11+
37.48803556,
12+
-122.2880486,
13+
37.613537207
14+
],
15+
"geometry": {
16+
"type": "Polygon",
17+
"coordinates": [
18+
[
19+
[
20+
-122.308150179,
21+
37.488035566
22+
],
23+
[
24+
-122.597502109,
25+
37.538869539
26+
],
27+
[
28+
-122.576687533,
29+
37.613537207
30+
],
31+
[
32+
-122.288048600,
33+
37.562818007
34+
],
35+
[
36+
-122.308150179,
37+
37.488035566
38+
]
39+
]
40+
]
41+
},
42+
"properties": {
43+
"datetime": "2016-05-03T13:22:30.040Z",
44+
"title": "A CS3 item",
45+
"license": "PDDL-1.0",
46+
"providers": [
47+
{
48+
"name": "CoolSat",
49+
"roles": [
50+
"producer",
51+
"licensor"
52+
],
53+
"url": "https://cool-sat.com/"
54+
}
55+
]
56+
},
57+
"collection": "CS3",
58+
"links": [
59+
{
60+
"rel": "self",
61+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/CS3-20160503_132130_04.json"
62+
},
63+
{
64+
"rel": "collection",
65+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/catalog.json"
66+
}
67+
],
68+
"assets": {
69+
"analytic": {
70+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/analytic.tif",
71+
"title": "4-Band Analytic",
72+
"product": "http://cool-sat.com/catalog/products/analytic.json"
73+
},
74+
"thumbnail": {
75+
"href": "http://cool-sat.com/catalog/CS3-20160503_132130_04/thumbnail.png",
76+
"title": "Thumbnail"
77+
}
78+
}
79+
}
80+
],
81+
"links": [
82+
{
83+
"rel": "self",
84+
"href": "./item-collection.json"
85+
}
86+
]
87+
}

tests/test_item.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import os
2+
import unittest
3+
import json
4+
from tempfile import TemporaryDirectory
5+
from datetime import datetime
6+
import dateutil
7+
8+
from pystac import *
9+
from pystac.utils import is_absolute_href
10+
from tests.utils import (TestCases, RANDOM_GEOM, RANDOM_BBOX, test_to_from_dict)
11+
12+
13+
class ItemTest(unittest.TestCase):
14+
def test_to_from_dict(self):
15+
m = TestCases.get_path('data-files/itemcollections/sample-item-collection.json')
16+
with open(m) as f:
17+
item_dict = json.load(f)['features'][0]
18+
19+
test_to_from_dict(self, Item, item_dict)
20+
item = Item.from_dict(item_dict)
21+
self.assertEqual(item.get_self_href(), 'http://cool-sat.com/catalog/CS3-20160503_132130_04/CS3-20160503_132130_04.json')
22+
23+
# test asset creation additional field(s)
24+
self.assertEqual(item.assets['analytic'].properties['product'], 'http://cool-sat.com/catalog/products/analytic.json')
25+
self.assertIsNone(item.assets['thumbnail'].properties)
26+
27+
28+

tests/utils.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import os
22
import json
33
from datetime import datetime
4-
4+
from copy import copy, deepcopy
5+
from dateutil.parser import parse
56
import jsonschema
67
from jsonschema.validators import RefResolver
78

@@ -162,3 +163,24 @@ def validate_dict(self, d, obj_type):
162163
except jsonschema.exceptions.ValidationError as e:
163164
print('Validation error in {}'.format(obj_type))
164165
raise e
166+
167+
def test_to_from_dict(test_class, stac_object_class, d):
168+
def _parse_times(a_dict):
169+
for k, v in a_dict.items():
170+
if isinstance(v, dict):
171+
_parse_times(v)
172+
elif isinstance(v, (tuple, list, set)):
173+
for vv in v:
174+
if isinstance(vv, dict):
175+
_parse_times(vv)
176+
else:
177+
if k == 'datetime':
178+
if not isinstance(v, datetime):
179+
a_dict[k] = parse(v)
180+
a_dict[k] = a_dict[k].replace(microsecond=0)
181+
182+
d1 = deepcopy(d)
183+
d2 = stac_object_class.from_dict(d).to_dict()
184+
_parse_times(d1)
185+
_parse_times(d2)
186+
test_class.assertDictEqual(d1, d2)

0 commit comments

Comments
 (0)