From e4b58f57c4825d2f834cbd759b098ddb169cdbce Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Thu, 11 Mar 2021 14:19:51 -0700 Subject: [PATCH 1/2] Don't error when pointcloud statistics are missing The `statistics` attribute of the pointcloud extension is optional, but `PointcloudItemExt.get_statistics` was erroring if the pointcloud was missing stats. This fixes the issue to return `None` if there are no statistics defined. --- pystac/extensions/pointcloud.py | 5 +- .../pointcloud/example-laz-no-statistics.json | 140 ++++++++++++++++++ tests/extensions/test_pointcloud.py | 6 + 3 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 tests/data-files/pointcloud/example-laz-no-statistics.json diff --git a/pystac/extensions/pointcloud.py b/pystac/extensions/pointcloud.py index f2ece2bdd..4573841ec 100644 --- a/pystac/extensions/pointcloud.py +++ b/pystac/extensions/pointcloud.py @@ -262,7 +262,10 @@ def get_statistics(self, asset=None): """ if asset is None or 'pc:statistics' not in asset.properties: stats = self.item.properties.get('pc:statistics') - return [PointcloudStatistic(s) for s in stats] + if stats: + return [PointcloudStatistic(s) for s in stats] + else: + return None else: return [PointcloudStatistic.create(s) for s in asset.properties.get('pc:statistics')] diff --git a/tests/data-files/pointcloud/example-laz-no-statistics.json b/tests/data-files/pointcloud/example-laz-no-statistics.json new file mode 100644 index 000000000..102c94c07 --- /dev/null +++ b/tests/data-files/pointcloud/example-laz-no-statistics.json @@ -0,0 +1,140 @@ +{ + "stac_version": "1.0.0-beta.2", + "stac_extensions": [ + "pointcloud" + ], + "assets": {}, + "bbox": [ + -123.0755422, + 44.04971882, + 123.791472, + -123.0619599, + 44.06278031, + 187.531248 + ], + "geometry": { + "coordinates": [ + [ + [ + -123.07498674, + 44.04971882 + ], + [ + -123.07554223, + 44.06248623 + ], + [ + -123.0625126, + 44.06278031 + ], + [ + -123.06195992, + 44.05001283 + ], + [ + -123.07498674, + 44.04971882 + ] + ] + ], + "type": "Polygon" + }, + "id": "autzen-full.laz", + "links": [ + { + "href": "/Users/hobu/dev/git/pdal/test/data/autzen/autzen-full.laz", + "rel": "self" + } + ], + "properties": { + "datetime": "2013-07-17T00:00:00Z", + "pc:count": 10653336, + "pc:density": 0, + "pc:encoding": "LASzip", + "pc:schemas": [ + { + "name": "X", + "size": 8, + "type": "floating" + }, + { + "name": "Y", + "size": 8, + "type": "floating" + }, + { + "name": "Z", + "size": 8, + "type": "floating" + }, + { + "name": "Intensity", + "size": 2, + "type": "unsigned" + }, + { + "name": "ReturnNumber", + "size": 1, + "type": "unsigned" + }, + { + "name": "NumberOfReturns", + "size": 1, + "type": "unsigned" + }, + { + "name": "ScanDirectionFlag", + "size": 1, + "type": "unsigned" + }, + { + "name": "EdgeOfFlightLine", + "size": 1, + "type": "unsigned" + }, + { + "name": "Classification", + "size": 1, + "type": "unsigned" + }, + { + "name": "ScanAngleRank", + "size": 4, + "type": "floating" + }, + { + "name": "UserData", + "size": 1, + "type": "unsigned" + }, + { + "name": "PointSourceId", + "size": 2, + "type": "unsigned" + }, + { + "name": "GpsTime", + "size": 8, + "type": "floating" + }, + { + "name": "Red", + "size": 2, + "type": "unsigned" + }, + { + "name": "Green", + "size": 2, + "type": "unsigned" + }, + { + "name": "Blue", + "size": 2, + "type": "unsigned" + } + ], + "pc:type": "lidar", + "title": "USGS 3DEP LiDAR" + }, + "type": "Feature" +} \ No newline at end of file diff --git a/tests/extensions/test_pointcloud.py b/tests/extensions/test_pointcloud.py index ac90a3ba5..22dd3db40 100644 --- a/tests/extensions/test_pointcloud.py +++ b/tests/extensions/test_pointcloud.py @@ -13,6 +13,8 @@ class PointcloudTest(unittest.TestCase): def setUp(self): self.maxDiff = None self.example_uri = TestCases.get_path('data-files/pointcloud/example-laz.json') + self.example_uri_no_statistics = TestCases.get_path( + 'data-files/pointcloud/example-laz-no-statistics.json') def test_to_from_dict(self): with open(self.example_uri) as f: @@ -184,3 +186,7 @@ def test_pointcloud_statistics(self): val = props[k] + 1 setattr(stat, k, val) self.assertEqual(getattr(stat, k), val) + + def test_statistics_accessor_when_no_stats(self): + pc_item = pystac.read_file(self.example_uri_no_statistics) + self.assertEqual(pc_item.ext.pointcloud.statistics, None) From d88f1de86c423edec55b3ca9ca52ea1cb7e54572 Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Thu, 11 Mar 2021 14:32:18 -0700 Subject: [PATCH 2/2] Add changelog entry for #282 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a34fa547..5065cae42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ ### Fixed +- Fixed error when accessing the statistics attribute of the pointcloud extension when no statistics were defined ([#282](https://github.com/stac-utils/pystac/pull/282)) + ### Changed ### Removed @@ -20,8 +22,6 @@ - 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)) -- Remove unnecessary `deepcopy` calls in `to_dict` methods to avoid costly overhead ([#273](https://github.com/stac-utils/pystac/pull/273)) - ### Changed