From 0174534a1c338b443287fef6621341ac49dbbaf0 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Tue, 2 Mar 2021 12:17:55 -0500 Subject: [PATCH 1/2] Fix issue with proj extension setters handling None values. Before this change, setting a projection extension value to None would break if the value wasn't required; the proper way to handle this case is to pop the property key or not set it. This fixes an issue brought up in #268 around using apply with the only required property (epsg). --- pystac/extensions/projection.py | 70 +++++++++++++++++------------ tests/extensions/test_projection.py | 8 ++++ 2 files changed, 50 insertions(+), 28 deletions(-) diff --git a/pystac/extensions/projection.py b/pystac/extensions/projection.py index 0c8b8d604..6e8b2cdc1 100644 --- a/pystac/extensions/projection.py +++ b/pystac/extensions/projection.py @@ -141,16 +141,18 @@ def get_wkt2(self, asset=None): else: return asset.properties.get('proj:wkt2') - def set_wkt2(self, wkt2, asset=None): + def set_wkt2(self, value, asset=None): """Set an Item or an Asset wkt2. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:wkt2'] = wkt2 + key = 'proj:wkt2' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:wkt2'] = wkt2 + target[key] = value @property def projjson(self): @@ -188,16 +190,18 @@ def get_projjson(self, asset=None): else: return asset.properties.get('proj:projjson') - def set_projjson(self, projjson, asset=None): + def set_projjson(self, value, asset=None): """Set an Item or an Asset projjson. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:projjson'] = projjson + key = 'proj:projjson' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:projjson'] = projjson + target[key] = value @property def geometry(self): @@ -233,16 +237,18 @@ def get_geometry(self, asset=None): else: return asset.properties.get('proj:geometry') - def set_geometry(self, geometry, asset=None): + def set_geometry(self, value, asset=None): """Set an Item or an Asset projection geometry. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:geometry'] = geometry + key = 'proj:geometry' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:geometry'] = geometry + target[key] = value @property def bbox(self): @@ -279,16 +285,18 @@ def get_bbox(self, asset=None): else: return asset.properties.get('proj:bbox') - def set_bbox(self, bbox, asset=None): + def set_bbox(self, value, asset=None): """Set an Item or an Asset projection bbox. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:bbox'] = bbox + key = 'proj:bbox' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:bbox'] = bbox + target[key] = value @property def centroid(self): @@ -324,16 +332,18 @@ def get_centroid(self, asset=None): else: return asset.properties.get('proj:centroid') - def set_centroid(self, centroid, asset=None): + def set_centroid(self, value, asset=None): """Set an Item or an Asset centroid. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:centroid'] = centroid + key = 'proj:centroid' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:centroid'] = centroid + target[key] = value @property def shape(self): @@ -367,16 +377,18 @@ def get_shape(self, asset=None): else: return asset.properties.get('proj:shape') - def set_shape(self, shape, asset=None): + def set_shape(self, value, asset=None): """Set an Item or an Asset shape. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:shape'] = shape + key = 'proj:shape' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:shape'] = shape + target[key] = value @property def transform(self): @@ -413,16 +425,18 @@ def get_transform(self, asset=None): else: return asset.properties.get('proj:transform') - def set_transform(self, transform, asset=None): + def set_transform(self, value, asset=None): """Set an Item or an Asset transform. If an Asset is supplied, sets the property on the Asset. Otherwise sets the Item's value. """ - if asset is None: - self.item.properties['proj:transform'] = transform + key = 'proj:transform' + target = self.item.properties if asset is None else asset.properties + if value is None: + target.pop(key, None) else: - asset.properties['proj:transform'] = transform + target[key] = value @classmethod def _object_links(cls): diff --git a/tests/extensions/test_projection.py b/tests/extensions/test_projection.py index 9681299dd..7e420b3bb 100644 --- a/tests/extensions/test_projection.py +++ b/tests/extensions/test_projection.py @@ -97,6 +97,14 @@ def test_apply(self): shape=[100, 100], transform=[30.0, 0.0, 224985.0, 0.0, -30.0, 6790215.0, 0.0, 0.0, 1.0]) + def test_partial_apply(self): + proj_item = pystac.read_file(self.example_uri) + + proj_item.ext.projection.apply(epsg=1111) + + self.assertEqual(proj_item.ext.projection.epsg, 1111) + proj_item.validate() + def test_validate_proj(self): item = pystac.read_file(self.example_uri) item.validate() From 4c1430069e9938387462e956cdcd3daf2fbca439 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Tue, 2 Mar 2021 12:22:40 -0500 Subject: [PATCH 2/2] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 186d1ee2c..da664ddd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Fixed - Fix handling of optional properties when using apply on view extension ([#259](https://github.com/stac-utils/pystac/pull/259)) +- Fixed issue with setting None into projection extension fields that are not required breaking validation ([#269](https://github.com/stac-utils/pystac/pull/269)) ### Changed