From 69a728f84c5161b4ff827c6a3838827c0b9a7244 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 12:35:17 -0400 Subject: [PATCH 01/11] Fix bug when link to resolved object has no href --- pystac/link.py | 2 +- tests/test_link.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/test_link.py diff --git a/pystac/link.py b/pystac/link.py index 11825f51b..fe01f7cdd 100644 --- a/pystac/link.py +++ b/pystac/link.py @@ -105,7 +105,7 @@ def get_href(self): else: href = self.target - if is_absolute_href(href) and self.owner is not None: + if href and is_absolute_href(href) and self.owner is not None: href = make_relative_href(href, self.owner.get_self_href()) else: href = self.get_absolute_href() diff --git a/tests/test_link.py b/tests/test_link.py new file mode 100644 index 000000000..2450712a1 --- /dev/null +++ b/tests/test_link.py @@ -0,0 +1,24 @@ +from datetime import datetime +import unittest + +import pystac +from tests.utils import (RANDOM_BBOX, RANDOM_GEOM) + + +class LinkTest(unittest.TestCase): + def test_link_doest_fail_if_href_is_none(self): + """Tests to cover a bug that was uncovered where a non-None HREF + was supposed""" + catalog = pystac.Catalog(id='test', description='test') + item = pystac.Item(id='test-item', + geometry=RANDOM_GEOM, + bbox=RANDOM_BBOX, + datetime=datetime.utcnow(), + properties={'key': 'one'}) + catalog.add_item(item) + catalog.set_self_href('/some/href') + catalog.make_all_links_relative() + + for link in catalog.links: + if link.rel == 'item': + self.assertIsNone(link.get_href()) From 495d316bb8ac6d83f7a5c20976c0628404ea1331 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 12:35:39 -0400 Subject: [PATCH 02/11] Fix tutorial's passing in of spatial extent. Also adds validation to the catalogs at each point so that we can avoid this type of error moving forward. Modifies the construction of the label items to avoid validation errors. Fixes #197 --- docs/tutorials/pystac-spacenet-tutorial.ipynb | 92 ++++++++++++++++--- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/docs/tutorials/pystac-spacenet-tutorial.ipynb b/docs/tutorials/pystac-spacenet-tutorial.ipynb index 9d770b644..c24f4d276 100644 --- a/docs/tutorials/pystac-spacenet-tutorial.ipynb +++ b/docs/tutorials/pystac-spacenet-tutorial.ipynb @@ -68,7 +68,7 @@ "from botocore.errorfactory import ClientError\n", "import pystac\n", "from pystac.extensions import label\n", - "from shapely.geometry import GeometryCollection, Polygon, box, shape" + "from shapely.geometry import GeometryCollection, Polygon, box, shape, mapping" ] }, { @@ -217,7 +217,7 @@ " params['id'] = basename(uri).split('.')[0]\n", " with rasterio.open(uri) as src:\n", " params['bbox'] = list(src.bounds)\n", - " params['geometry'] = box(*params['bbox']).__geo_interface__\n", + " params['geometry'] = mapping(box(*params['bbox']))\n", " params['datetime'] = capture_date\n", " params['properties'] = {}\n", " i = pystac.Item(**params)\n", @@ -240,7 +240,7 @@ "metadata": {}, "outputs": [], "source": [ - "bounds = GeometryCollection([shape(s.geometry) for s in spacenet.get_all_items()]).bounds\n", + "bounds = [list(GeometryCollection([shape(s.geometry) for s in spacenet.get_all_items()]).bounds)]\n", "vegas.extent.spatial = pystac.SpatialExtent(bounds)" ] }, @@ -248,16 +248,45 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Currently, this STAC only exists in memory. We need to set all of the paths based on the root directory we want to save off that catalog too, and then save a \"self contained\" catalog, which will have all links be relative and contain no 'self' links. We can do this all in one shot with the `normalize_and_save` method:" + "Currently, this STAC only exists in memory. We need to set all of the paths based on the root directory we want to save off that catalog too, and then save a \"self contained\" catalog, which will have all links be relative and contain no 'self' links. We can do this by using the `normalize` method to set the HREFs of all of our STAC objects. We'll then validate the catalog, and then save:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spacenet.normalize_hrefs('spacenet-stac')" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, "outputs": [], "source": [ - "spacenet.normalize_and_save('spacenet-stac', catalog_type=pystac.CatalogType.SELF_CONTAINED)" + "spacenet.validate_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "spacenet.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)" ] }, { @@ -285,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -298,7 +327,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -314,7 +343,7 @@ " label_item = pystac.Item(\n", " id='{}-labels'.format(item.id),\n", " bbox=item.bbox,\n", - " geometry=box(*item.bbox).__geo_interface__,\n", + " geometry=mapping(box(*item.bbox)),\n", " datetime=item.datetime,\n", " properties={},\n", " stac_extensions=[pystac.Extensions.LABEL]\n", @@ -323,7 +352,12 @@ " label_item.ext.label.apply(\n", " label_description='Building labels for scene {}'.format(item.id),\n", " label_type=label.LabelType.VECTOR,\n", - " label_properties=['partialBuilding']\n", + " label_properties=['partialBuilding'],\n", + " \n", + " # Label classes is marked as required in 1.0.0-beta.2, so make it up.\n", + " # Once this PR is released, this can be removed:\n", + " # https://github.com/radiantearth/stac-spec/pull/905\n", + " label_classes=[label.LabelClasses.create(classes=['building'], name='partialBuilding')]\n", " )\n", " \n", " label_item.ext.label.add_geojson_labels(href=label_uri)\n", @@ -347,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -356,7 +390,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -383,7 +417,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -428,12 +462,40 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spacenet_cog.normalize_hrefs('spacenet-cog-stac')" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "spacenet_cog.validate_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "spacenet_cog.normalize_and_save('spacenet-cog-stac', \n", - " catalog_type=pystac.CatalogType.SELF_CONTAINED)" + "spacenet_cog.save(catalog_type=pystac.CatalogType.SELF_CONTAINED)" ] }, { From 7e1b1bf96b504b88448d175525442899025f8d13 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 12:37:35 -0400 Subject: [PATCH 03/11] Allow single interval or bbox to be passed into extents. Fixes #198 --- pystac/collection.py | 14 ++++++++++++++ tests/test_collection.py | 41 ++++++++++++++++++++++++++++++++++------ 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/pystac/collection.py b/pystac/collection.py index 1e4a93626..c9b36e3f7 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -1,3 +1,4 @@ +import collections.abc from datetime import datetime import dateutil.parser from dateutil import tz @@ -305,6 +306,12 @@ class SpatialExtent: 2D Collection with only one bbox would be [[xmin, ymin, xmax, ymax]] """ def __init__(self, bboxes): + # A common mistake is to pass in a single bbox instead of a list of bboxes. + # Account for this by transforming the input in that case. + if isinstance(bboxes, collections.abc.Sequence): + if not isinstance(bboxes[0], collections.abc.Sequence): + bboxes = [bboxes] + self.bboxes = bboxes def to_dict(self): @@ -388,6 +395,13 @@ class TemporalExtent: Datetimes are required to be in UTC. """ def __init__(self, intervals): + # A common mistake is to pass in a single interval instead of a + # list of intervals. Account for this by transforming the input + # in that case. + if isinstance(intervals, collections.abc.Sequence): + if not isinstance(intervals[0], collections.abc.Sequence): + intervals = [intervals] + for i in intervals: if i[0] is None and i[1] is None: raise STACError('TemporalExtent interval must have either ' diff --git a/tests/test_collection.py b/tests/test_collection.py index 0ff3f0158..3c694e12a 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -5,7 +5,7 @@ from datetime import datetime import pystac -from pystac.validation import (validate_dict, STACValidationError) +from pystac.validation import validate_dict from pystac.serialization.identify import STACObjectType from pystac import (Collection, Item, Extent, SpatialExtent, TemporalExtent, CatalogType) from pystac.extensions.eo import Band @@ -114,11 +114,6 @@ def test_multiple_extents(self): cloned_ext = ext.clone() self.assertDictEqual(cloned_ext.to_dict(), multi_ext_dict['extent']) - multi_ext_dict['extent']['spatial']['bbox'] = multi_ext_dict['extent']['spatial']['bbox'][0] - invalid_col = Collection.from_dict(multi_ext_dict) - with self.assertRaises(STACValidationError): - invalid_col.validate() - def test_extra_fields(self): catalog = TestCases.test_case_2() collection = catalog.get_child('1a8c1632-fa91-4a62-b33e-3a87c2ebdf16') @@ -179,3 +174,37 @@ def test_update_extents(self): self.assertEqual( [[item2.common_metadata.start_datetime, base_extent.temporal.intervals[0][1]]], collection.extent.temporal.intervals) + + +class ExtentTest(unittest.TestCase): + def test_spatial_allows_single_bbox(self): + temporal_extent = TemporalExtent(intervals=[[datetime.utcnow(), None]]) + + # Pass in a single BBOX + spatial_extent = SpatialExtent(bboxes=RANDOM_BBOX) + + collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent) + + collection = Collection(id='test', + description='test', + extent=collection_extent, + license='CC-BY-SA-4.0') + collection.set_self_href('/usr/collection.json') + + collection.validate() + + def test_temporal_allows_single_interval(self): + spatial_extent = SpatialExtent(bboxes=[RANDOM_BBOX]) + + # Pass in a single interval + temporal_extent = TemporalExtent(intervals=[datetime.utcnow(), None]) + + collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent) + + collection = Collection(id='test', + description='test', + extent=collection_extent, + license='CC-BY-SA-4.0') + collection.set_self_href('/usr/collection.json') + + collection.validate() From 9329569f1e2a190f2a88d6a5f89522052cfceb5b Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 12:38:00 -0400 Subject: [PATCH 04/11] Fix validation issue with tuples vs lists. Shapely can produce GeoJSON mappings that use tuples in the coordinate sequences of geometries instead of lists. A direct validation of the dict for GeoJSON will produce errors, since coordinates are expected to be list-based. This commit adds a translation to and from JSON serialization in order to work around this and other issue which may appear if a JSON serialization would pass validation but the STAC dict may not. --- pystac/validation/stac_validator.py | 9 +++++++-- tests/validation/test_validate.py | 25 ++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/pystac/validation/stac_validator.py b/pystac/validation/stac_validator.py index f7e09063e..ebc2442b7 100644 --- a/pystac/validation/stac_validator.py +++ b/pystac/validation/stac_validator.py @@ -67,12 +67,17 @@ def validate(self, stac_dict, stac_object_type, stac_version, extensions, href=N STACValidator implementation. """ results = [] - core_result = self.validate_core(stac_dict, stac_object_type, stac_version, href) + + # Pass the dict through JSON serialization and parsing, otherwise + # some valid properties can be marked as invalid (e.g. tuples in + # coordinate sequences for geometries). + json_dict = json.loads(json.dumps(stac_dict)) + core_result = self.validate_core(json_dict, stac_object_type, stac_version, href) if core_result is not None: results.append(core_result) for extension_id in extensions: - ext_result = self.validate_extension(stac_dict, stac_object_type, stac_version, + ext_result = self.validate_extension(json_dict, stac_object_type, stac_version, extension_id, href) if ext_result is not None: results.append(ext_result) diff --git a/tests/validation/test_validate.py b/tests/validation/test_validate.py index 688bb2179..2ef5c6174 100644 --- a/tests/validation/test_validate.py +++ b/tests/validation/test_validate.py @@ -1,6 +1,7 @@ +from datetime import datetime +import json import os import shutil -import json import unittest from tempfile import TemporaryDirectory @@ -108,3 +109,25 @@ def test_validate_all(self): with self.assertRaises(STACValidationError): pystac.validation.validate_all(stac_dict, new_cat_href) + + def test_validates_geojson_with_tuple_coordinates(self): + """This unit tests guards against a bug where if a geometry + dict has tuples instead of lists for the coordinate sequence, + which can be produced by shapely, then the geometry still passses + validation. + """ + geom = { + 'type': + 'Polygon', + 'coordinates': (((-115.3057626, 36.1265426997), (-115.3057626, 36.1282976997), + (-115.3075176, 36.1282976997), (-115.3075176, 36.1265426997), + (-115.3057626, 36.1265426997)), ) + } + + item = pystac.Item(id='test-item', + geometry=geom, + bbox=[-115.308, 36.126, -115.305, 36.129], + datetime=datetime.utcnow(), + properties={'key': 'one'}) + + self.assertIsNone(item.validate()) From 30e93f2570c30904a2e53777fc97ccbece00326d Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 12:39:53 -0400 Subject: [PATCH 05/11] Conslidate lint commands in README --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6bf98f2d1..45c88628d 100644 --- a/README.md +++ b/README.md @@ -81,15 +81,13 @@ PySTAC uses [flake8](http://flake8.pycqa.org/en/latest/) and [yapf](https://gith To run the flake8 style checks: ``` -> flake8 pystac -> flake8 tests +> flake8 pystac tests ``` To format code: ``` -> yapf -ipr pystac -> yapf -ipr tests +> yapf -ipr pystac tests ``` You can also run the `./scripts/test` script to check flake8 and yapf. From 954811d8901ebed0e1f7aeaed17807354c7faf08 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Fri, 16 Oct 2020 13:26:43 -0400 Subject: [PATCH 06/11] Update nbsphinx version and include ipython to fix readthedoc build --- requirements-dev.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index fe757bec1..e7722d0bb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,3 +1,4 @@ +ipython==7.16.1 jsonschema==3.2.0 pylint==1.9.1 Sphinx==1.8.0 @@ -6,5 +7,5 @@ sphinxcontrib-fulltoc==1.2.0 sphinxcontrib-napoleon==0.7 flake8==3.8.* yapf==0.28.* -nbsphinx==0.4.3 +nbsphinx==0.7.1 coverage==5.2.* From 95f39d051d3e8d4f0fef68a1165e42b15087375c Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Wed, 21 Oct 2020 18:59:57 -0400 Subject: [PATCH 07/11] Make a note about yapf3. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 45c88628d..257e4abb2 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,8 @@ To format code: > yapf -ipr pystac tests ``` +Note that you may have to use `yapf3` explicitly depending on your environment. + You can also run the `./scripts/test` script to check flake8 and yapf. ### Documentation From 1f216cf2a03028b456325484ed4066747aeded8e Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Wed, 21 Oct 2020 19:02:04 -0400 Subject: [PATCH 08/11] Use abc instead of collections.abc --- pystac/collection.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pystac/collection.py b/pystac/collection.py index c9b36e3f7..55ed9753a 100644 --- a/pystac/collection.py +++ b/pystac/collection.py @@ -1,4 +1,4 @@ -import collections.abc +from collections import abc from datetime import datetime import dateutil.parser from dateutil import tz @@ -308,8 +308,8 @@ class SpatialExtent: def __init__(self, bboxes): # A common mistake is to pass in a single bbox instead of a list of bboxes. # Account for this by transforming the input in that case. - if isinstance(bboxes, collections.abc.Sequence): - if not isinstance(bboxes[0], collections.abc.Sequence): + if isinstance(bboxes, abc.Sequence): + if not isinstance(bboxes[0], abc.Sequence): bboxes = [bboxes] self.bboxes = bboxes @@ -398,8 +398,8 @@ def __init__(self, intervals): # A common mistake is to pass in a single interval instead of a # list of intervals. Account for this by transforming the input # in that case. - if isinstance(intervals, collections.abc.Sequence): - if not isinstance(intervals[0], collections.abc.Sequence): + if isinstance(intervals, abc.Sequence): + if not isinstance(intervals[0], abc.Sequence): intervals = [intervals] for i in intervals: From 02836c37086aaecba06e8c2d444cbdba3648e88b Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Wed, 21 Oct 2020 19:18:38 -0400 Subject: [PATCH 09/11] Modify test based on code review feedback. - Use consistent datetime - Don't use license if not required - Make descriptions different than IDs - Specify why we're setting th href --- tests/test_collection.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/test_collection.py b/tests/test_collection.py index 3c694e12a..87beeec19 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -12,6 +12,8 @@ from pystac.utils import datetime_to_str from tests.utils import (TestCases, RANDOM_GEOM, RANDOM_BBOX) +TEST_DATETIME = datetime(2020, 3, 14, 16, 32) + class CollectionTest(unittest.TestCase): def test_spatial_extent_from_coordinates(self): @@ -27,14 +29,14 @@ def test_eo_items_are_heritable(self): item1 = Item(id='test-item-1', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, - datetime=datetime.utcnow(), + datetime=TEST_DATETIME, properties={'key': 'one'}, stac_extensions=['eo', 'commons']) item2 = Item(id='test-item-2', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, - datetime=datetime.utcnow(), + datetime=TEST_DATETIME, properties={'key': 'two'}, stac_extensions=['eo', 'commons']) @@ -142,7 +144,7 @@ def test_update_extents(self): item1 = Item(id='test-item-1', geometry=RANDOM_GEOM, bbox=[-180, -90, 180, 90], - datetime=datetime.utcnow(), + datetime=TEST_DATETIME, properties={'key': 'one'}, stac_extensions=['eo', 'commons']) @@ -178,18 +180,17 @@ def test_update_extents(self): class ExtentTest(unittest.TestCase): def test_spatial_allows_single_bbox(self): - temporal_extent = TemporalExtent(intervals=[[datetime.utcnow(), None]]) + temporal_extent = TemporalExtent(intervals=[[TEST_DATETIME, None]]) # Pass in a single BBOX spatial_extent = SpatialExtent(bboxes=RANDOM_BBOX) collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent) - collection = Collection(id='test', - description='test', - extent=collection_extent, - license='CC-BY-SA-4.0') - collection.set_self_href('/usr/collection.json') + collection = Collection(id='test', description='test desc', extent=collection_extent) + + # HREF required by validation + collection.set_self_href('https://example.com/collection.json') collection.validate() @@ -197,14 +198,13 @@ def test_temporal_allows_single_interval(self): spatial_extent = SpatialExtent(bboxes=[RANDOM_BBOX]) # Pass in a single interval - temporal_extent = TemporalExtent(intervals=[datetime.utcnow(), None]) + temporal_extent = TemporalExtent(intervals=[TEST_DATETIME, None]) collection_extent = Extent(spatial=spatial_extent, temporal=temporal_extent) - collection = Collection(id='test', - description='test', - extent=collection_extent, - license='CC-BY-SA-4.0') - collection.set_self_href('/usr/collection.json') + collection = Collection(id='test', description='test desc', extent=collection_extent) + + # HREF required by validation + collection.set_self_href('https://example.com/collection.json') collection.validate() From 3bc9a01097f42c2b321279a078eb1105e557458e Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Wed, 21 Oct 2020 19:28:18 -0400 Subject: [PATCH 10/11] Modify test based on code review comments. - use consistent datetime - better docstring - use different ID and description - Get item link explicitly --- tests/test_link.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/test_link.py b/tests/test_link.py index 2450712a1..f3794fd0d 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -4,21 +4,21 @@ import pystac from tests.utils import (RANDOM_BBOX, RANDOM_GEOM) +TEST_DATETIME = datetime(2020, 3, 14, 16, 32) + class LinkTest(unittest.TestCase): - def test_link_doest_fail_if_href_is_none(self): - """Tests to cover a bug that was uncovered where a non-None HREF - was supposed""" - catalog = pystac.Catalog(id='test', description='test') + def test_link_does_not_fail_if_href_is_none(self): + """Test to ensure get_href does not fail when the href is None""" + catalog = pystac.Catalog(id='test', description='test desc') item = pystac.Item(id='test-item', geometry=RANDOM_GEOM, bbox=RANDOM_BBOX, datetime=datetime.utcnow(), - properties={'key': 'one'}) + properties={}) catalog.add_item(item) catalog.set_self_href('/some/href') catalog.make_all_links_relative() - for link in catalog.links: - if link.rel == 'item': - self.assertIsNone(link.get_href()) + link = catalog.get_single_link('item') + self.assertIsNone(link.get_href()) From 1f472cf9cd5f935a1ea77634ab1937971a992730 Mon Sep 17 00:00:00 2001 From: Rob Emanuele Date: Wed, 21 Oct 2020 19:37:47 -0400 Subject: [PATCH 11/11] Modify tests based on code review feedback. - Use less decimal places - use simpler parameters. - comment why the trailing , is needed. --- tests/validation/test_validate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/validation/test_validate.py b/tests/validation/test_validate.py index 2ef5c6174..8b11ae193 100644 --- a/tests/validation/test_validate.py +++ b/tests/validation/test_validate.py @@ -119,15 +119,15 @@ def test_validates_geojson_with_tuple_coordinates(self): geom = { 'type': 'Polygon', - 'coordinates': (((-115.3057626, 36.1265426997), (-115.3057626, 36.1282976997), - (-115.3075176, 36.1282976997), (-115.3075176, 36.1265426997), - (-115.3057626, 36.1265426997)), ) + # Last , is required to ensure tuple creation. + 'coordinates': (((-115.305, 36.126), (-115.305, 36.128), (-115.307, 36.128), + (-115.307, 36.126), (-115.305, 36.126)), ) } item = pystac.Item(id='test-item', geometry=geom, bbox=[-115.308, 36.126, -115.305, 36.129], datetime=datetime.utcnow(), - properties={'key': 'one'}) + properties={}) self.assertIsNone(item.validate())