Ejemplo n.º 1
0
    def test_without_shapely(self):
        geom = {
            'coordinates': (((-93.52300099792355,
                              41.241436141055345), (-93.7138666, 40.703737),
                             (-94.37053769704536,
                              40.83098709945576), (-94.2036617, 41.3717716),
                             (-93.52300099792355, 41.241436141055345)), ),
            'type':
            'Polygon'
        }
        bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355,
                        41.3717716)
        ctx = geocontext.AOI(geom,
                             bounds=bounds_wgs84,
                             resolution=40,
                             crs="EPSG:3857")
        self.assertIs(ctx.geometry, geom)
        self.assertEqual(ctx.__geo_interface__, geom)
        self.assertEqual(ctx.bounds, bounds_wgs84)

        raster_params = ctx.raster_params
        self.assertEqual(raster_params["cutline"], geom)

        with self.assertRaises(NotImplementedError):
            geocontext.AOI(geom)
 def test_validate_bounds_values_for_bounds_crs__latlon(self):
     # invalid latlon bounds
     with pytest.raises(ValueError, match="Bounds must be in lat-lon coordinates"):
         geocontext.AOI(
             bounds_crs="EPSG:4326", bounds=[500000, 2000000, 501000, 2001000]
         )
     # valid latlon bounds, no error should raise
     geocontext.AOI(bounds_crs="EPSG:4326", bounds=[12, -41, 14, -40])
Ejemplo n.º 3
0
    def test_default_ctx(self):
        # test doesn't fail with nothing
        ctx = MockScene({}, {}).default_ctx()
        self.assertEqual(ctx, geocontext.AOI(bounds_crs=None, align_pixels=False))

        # no geotrans
        ctx = MockScene({}, {
            'crs': 'EPSG:4326'
        }).default_ctx()
        self.assertEqual(ctx, geocontext.AOI(crs="EPSG:4326", bounds_crs=None, align_pixels=False))

        # north-up geotrans - resolution
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")  # otherwise, the duplicate warning is suppressed the second time
            ctx = MockScene({}, {
                'crs': 'EPSG:4326',
                # origin: (0, 0), pixel size: 2, rotation: 0 degrees
                'geotrans': [0, 2, 0, 0, 0, -2],
            }).default_ctx()
            self.assertEqual(len(w), 0)
        self.assertEqual(ctx.resolution, 2)

        # non-north-up geotrans - resolution
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            ctx = MockScene({}, {
                'crs': 'EPSG:4326',
                # origin: (0, 0), pixel size: 2, rotation: 30 degrees
                'geotrans': (0.0, 1.7320508075688774, -1, 0.0, 1, 1.7320508075688774)
            }).default_ctx()
            warning = w[0]
            self.assertIn("The GeoContext will *not* return this Scene's original data", str(warning.message))
        self.assertEqual(ctx.resolution, 2)

        # north-up geotrans - bounds
        ctx = MockScene({}, {
            'crs': 'EPSG:4326',
            # origin: (10, 20), pixel size: 2, rotation: 0 degrees
            'geotrans': [10, 2, 0, 20, 0, -2],
            'raster_size': [1, 2]
        }).default_ctx()
        self.assertEqual(ctx.bounds, (10, 16, 12, 20))

        # non-north-up geotrans - bounds
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter("always")
            ctx = MockScene({}, {
                'crs': 'EPSG:4326',
                # origin: (0, 0), pixel size: 2, rotation: 45 degrees
                'geotrans': (0.0, np.sqrt(2), np.sqrt(2), 0.0, np.sqrt(2), -np.sqrt(2)),
                'raster_size': [1, 1]
            }).default_ctx()
            warning = w[0]
            self.assertIn("The GeoContext will *not* return this Scene's original data", str(warning.message))
        diagonal = np.sqrt(2**2 + 2**2)
        self.assertEqual(ctx.bounds, (0, -diagonal / 2, diagonal, diagonal / 2))
    def test_assign_update_bounds_crs(self):
        ctx = geocontext.AOI(bounds_crs="EPSG:32615")
        assert ctx.bounds_crs == "EPSG:32615"
        geom = shapely.geometry.Point(-20, 30).buffer(1).envelope

        ctx_no_update_bounds = ctx.assign(geometry=geom)
        assert ctx_no_update_bounds.bounds_crs == "EPSG:32615"

        ctx_update_bounds = ctx.assign(geometry=geom, bounds="update")
        assert ctx_update_bounds.bounds_crs == "EPSG:4326"

        with pytest.raises(ValueError, match="Can't compute bounds from a geometry while also explicitly setting"):
            ctx = geocontext.AOI(geometry=geom, resolution=40, bounds_crs="EPSG:32615")
 def test_validate_bounds_values_for_bounds_crs__non_latlon(self):
     # valid latlon bounds, should warn
     with warnings.catch_warnings(record=True) as w:
         warnings.simplefilter("always")
         ctx = geocontext.AOI(bounds_crs="EPSG:32615", bounds=(12, -41, 14, -40))
         assert ctx.bounds_crs == "EPSG:32615"
         assert ctx.bounds == (12, -41, 14, -40)
         warning = w[0]
         assert "You might have the wrong `bounds_crs` set." in str(warning.message)
     # not latlon bounds, no error should raise
     geocontext.AOI(
         bounds_crs="EPSG:32615", bounds=[500000, 2000000, 501000, 2001000]
     )
Ejemplo n.º 6
0
    def test_validate_reasonable_resolution(self):
        # different CRSs --- no error
        ctx = geocontext.AOI(
            crs="EPSG:32615",
            bounds_crs="EPSG:4326",
            bounds=[0, 0, 1.5, 1.5],
            resolution=15,
        )
        assert ctx.crs == "EPSG:32615"
        assert ctx.bounds_crs == "EPSG:4326"
        assert ctx.bounds == (0, 0, 1.5, 1.5)
        assert ctx.resolution == 15

        # same CRSs, bounds < resolution --- no error
        geocontext.AOI(
            crs="EPSG:32615",
            bounds_crs="EPSG:32615",
            bounds=[200000, 5000000, 200100, 5000300],
            resolution=15,
        )

        # same CRSs, width < resolution --- error
        with pytest.raises(ValueError, match="less than one pixel wide"):
            geocontext.AOI(
                crs="EPSG:32615",
                bounds_crs="EPSG:32615",
                bounds=[200000, 5000000, 200001, 5000300],
                resolution=15,
            )

        # same CRSs, height < resolution --- error
        with pytest.raises(ValueError, match="less than one pixel tall"):
            geocontext.AOI(
                crs="EPSG:32615",
                bounds_crs="EPSG:32615",
                bounds=[200000, 5000000, 200100, 5000001],
                resolution=15,
            )

        # same CRSs, width < resolution, CRS is lat-lon --- error including "decimal degrees"
        with pytest.raises(
            ValueError, match="resolution must be given in decimal degrees"
        ):
            geocontext.AOI(
                crs="EPSG:4326",
                bounds_crs="EPSG:4326",
                bounds=[10, 10, 11, 11],
                resolution=15,
            )
    def test_validate_reasonable_resolution(self):
        # different CRSs --- no error
        ctx = geocontext.AOI(
            crs="EPSG:32615",
            bounds_crs="EPSG:4326",
            bounds=[0, 0, 1.5, 1.5],
            resolution=15,
        )
        self.assertEqual(ctx.crs, "EPSG:32615")
        self.assertEqual(ctx.bounds_crs, "EPSG:4326")
        self.assertEqual(ctx.bounds, (0, 0, 1.5, 1.5))
        self.assertEqual(ctx.resolution, 15)

        # same CRSs, bounds < resolution --- no error
        geocontext.AOI(
            crs="EPSG:32615",
            bounds_crs="EPSG:32615",
            bounds=[200000, 5000000, 200100, 5000300],
            resolution=15,
        )

        # same CRSs, width < resolution --- error
        with self.assertRaisesRegexp(ValueError, "less than one pixel wide"):
            geocontext.AOI(
                crs="EPSG:32615",
                bounds_crs="EPSG:32615",
                bounds=[200000, 5000000, 200001, 5000300],
                resolution=15,
            )

        # same CRSs, height < resolution --- error
        with self.assertRaisesRegexp(ValueError, "less than one pixel tall"):
            geocontext.AOI(
                crs="EPSG:32615",
                bounds_crs="EPSG:32615",
                bounds=[200000, 5000000, 200100, 5000001],
                resolution=15,
            )

        # same CRSs, width < resolution, CRS is lat-lon --- error including "decimal degrees"
        with self.assertRaisesRegexp(
                ValueError, "resolution must be given in decimal degrees"):
            geocontext.AOI(
                crs="EPSG:4326",
                bounds_crs="EPSG:4326",
                bounds=[10, 10, 11, 11],
                resolution=15,
            )
    def test_assign_update_bounds(self):
        geom = shapely.geometry.Point(-90, 30).buffer(1).envelope
        ctx = geocontext.AOI(geometry=geom, resolution=40)

        geom_overlaps = shapely.affinity.translate(geom, xoff=1)
        self.assertTrue(geom.intersects(geom_overlaps))
        ctx_overlap = ctx.assign(geometry=geom_overlaps)
        self.assertEqual(ctx_overlap.bounds, ctx.bounds)

        ctx_updated = ctx.assign(geometry=geom_overlaps, bounds="update")
        self.assertEqual(ctx_updated.bounds, geom_overlaps.bounds)

        geom_doesnt_overlap = shapely.affinity.translate(geom, xoff=3)
        with self.assertRaisesRegexp(ValueError,
                                     "Geometry and bounds do not intersect"):
            ctx.assign(geometry=geom_doesnt_overlap)
        ctx_doesnt_overlap_updated = ctx.assign(geometry=geom_doesnt_overlap,
                                                bounds="update")
        self.assertEqual(ctx_doesnt_overlap_updated.bounds,
                         geom_doesnt_overlap.bounds)

        with self.assertRaisesRegexp(
                ValueError,
                "A geometry must be given with which to update the bounds"):
            ctx.assign(bounds="update")
Ejemplo n.º 9
0
 def test_geojson_featurecollection(self):
     feature = {
         'type': 'Feature',
         'geometry': {
             'coordinates':
             (((-93.52300099792355, 41.241436141055345),
               (-93.7138666, 40.703737), (-94.37053769704536,
                                          40.83098709945576),
               (-94.2036617, 41.3717716), (-93.52300099792355,
                                           41.241436141055345)), ),
             'type':
             'Polygon'
         }
     }
     collection = {
         'type': 'FeatureCollection',
         'features': [feature, feature, feature],
     }
     bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355,
                     41.3717716)
     ctx = geocontext.AOI(collection,
                          bounds=bounds_wgs84,
                          resolution=40,
                          crs="EPSG:3857")
     self.assertEqual(ctx.__geo_interface__['type'], 'GeometryCollection')
     self.assertEqual(ctx.__geo_interface__['geometries'][0],
                      feature['geometry'])
Ejemplo n.º 10
0
    def test_assign_update_bounds(self):
        geom = shapely.geometry.Point(-90, 30).buffer(1).envelope
        ctx = geocontext.AOI(geometry=geom, resolution=40)

        geom_overlaps = shapely.affinity.translate(geom, xoff=1)
        assert geom.intersects(geom_overlaps)
        ctx_overlap = ctx.assign(geometry=geom_overlaps)
        assert ctx_overlap.bounds == ctx.bounds

        ctx_updated = ctx.assign(geometry=geom_overlaps, bounds="update")
        assert ctx_updated.bounds == geom_overlaps.bounds

        geom_doesnt_overlap = shapely.affinity.translate(geom, xoff=3)
        with pytest.raises(ValueError,
                           match="Geometry and bounds do not intersect"):
            ctx.assign(geometry=geom_doesnt_overlap)
        ctx_doesnt_overlap_updated = ctx.assign(geometry=geom_doesnt_overlap,
                                                bounds="update")
        assert ctx_doesnt_overlap_updated.bounds == geom_doesnt_overlap.bounds

        with pytest.raises(
                ValueError,
                match="A geometry must be given with which to update the bounds"
        ):
            ctx.assign(bounds="update")
    def test_raster_params(self):
        geom = {
            "coordinates": (
                (
                    (-93.52300099792355, 41.241436141055345),
                    (-93.7138666, 40.703737),
                    (-94.37053769704536, 40.83098709945576),
                    (-94.2036617, 41.3717716),
                    (-93.52300099792355, 41.241436141055345),
                ),
            ),
            "type": "Polygon",
        }
        bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355, 41.3717716)
        resolution = 40
        crs = "EPSG:32615"
        align_pixels = False

        ctx = geocontext.AOI(geom, resolution, crs, align_pixels)
        raster_params = ctx.raster_params
        expected = {
            "cutline": geom,
            "resolution": resolution,
            "srs": crs,
            "bounds_srs": "EPSG:4326",
            "align_pixels": align_pixels,
            "bounds": bounds_wgs84,
            "dimensions": None,
        }
        assert raster_params == expected
 def test_init(self):
     feature = {
         "type": "Feature",
         "geometry": {
             "coordinates": (
                 (
                     (-93.52300099792355, 41.241436141055345),
                     (-93.7138666, 40.703737),
                     (-94.37053769704536, 40.83098709945576),
                     (-94.2036617, 41.3717716),
                     (-93.52300099792355, 41.241436141055345),
                 ),
             ),
             "type": "Polygon",
         },
     }
     collection = {
         "type": "FeatureCollection",
         "features": [feature, feature, feature],
     }
     bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355, 41.3717716)
     resolution = 40
     ctx = geocontext.AOI(collection, resolution=resolution)
     assert ctx.resolution == resolution
     assert tuple(round(e, 5) for e in ctx.bounds) == tuple(
         round(e, 5) for e in bounds_wgs84
     )
     assert ctx.bounds_crs == "EPSG:4326"
     assert isinstance(ctx.geometry, shapely.geometry.GeometryCollection)
     assert ctx.__geo_interface__["type"] == "GeometryCollection"
     assert ctx.__geo_interface__["geometries"][0] == feature["geometry"]
    def test_assign(self):
        geom = {
            "coordinates": [
                [
                    [-93.52300099792355, 41.241436141055345],
                    [-93.7138666, 40.703737],
                    [-94.37053769704536, 40.83098709945576],
                    [-94.2036617, 41.3717716],
                    [-93.52300099792355, 41.241436141055345],
                ]
            ],
            "type": "Polygon",
        }
        ctx = geocontext.AOI(resolution=40)
        ctx2 = ctx.assign(geometry=geom)
        assert (
            ctx2.geometry.__geo_interface__
            == shapely.geometry.shape(geom).__geo_interface__
        )
        assert ctx2.resolution == 40
        assert ctx2.align_pixels
        assert ctx2.shape is None

        ctx3 = ctx2.assign(geometry=None)
        assert ctx3.geometry is None
Ejemplo n.º 14
0
 def test_init(self):
     feature = {
         'type': 'Feature',
         'geometry': {
             'coordinates': ((
                 (-93.52300099792355, 41.241436141055345),
                 (-93.7138666, 40.703737),
                 (-94.37053769704536, 40.83098709945576),
                 (-94.2036617, 41.3717716),
                 (-93.52300099792355, 41.241436141055345)),
             ),
             'type': 'Polygon'
         }
     }
     collection = {
         'type': 'FeatureCollection',
         'features': [feature, feature, feature],
     }
     bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355, 41.3717716)
     resolution = 40
     ctx = geocontext.AOI(collection, resolution=resolution)
     self.assertEqual(ctx.resolution, resolution)
     self.assertEqual(ctx.bounds, bounds_wgs84)
     self.assertIsInstance(ctx.geometry, shapely.geometry.GeometryCollection)
     self.assertEqual(ctx.__geo_interface__['type'], 'GeometryCollection')
     self.assertEqual(ctx.__geo_interface__['geometries'][0], feature['geometry'])
Ejemplo n.º 15
0
 def test_help_with_too_big_resolution(self):
     aoi = geocontext.AOI(resolution=10,
                          bounds=(-100, 35, -99.9, 35.1),
                          crs="EPSG:4326")
     with self.assertRaises(ValueError):
         aoi.raster_params
     aoi = aoi.assign(crs="EPSG:3857")
     aoi.raster_params
Ejemplo n.º 16
0
    def test_search_AOI(self):
        aoi = geocontext.AOI(self.geom, resolution=5)
        sc, ctx = search(aoi, products="landsat:LC08:PRE:TOAR", limit=4)
        self.assertGreater(len(sc), 0)
        self.assertLessEqual(len(sc), 4)  # test client only has 2 scenes available

        self.assertEqual(ctx.resolution, 5)
        self.assertEqual(ctx.crs, "EPSG:32615")
Ejemplo n.º 17
0
    def test_search_AOI(self):
        aoi = geocontext.AOI(self.geom, resolution=5)
        sc, ctx = search(aoi, products="landsat:LC08:PRE:TOAR", limit=4)
        assert len(sc) > 0
        assert len(sc) <= 4  # test client only has 2 scenes available

        assert ctx.resolution == 5
        assert ctx.crs == "EPSG:32615"
    def test_assign_update_bounds_crs(self):
        ctx = geocontext.AOI(bounds_crs="EPSG:32615")
        self.assertEqual(ctx.bounds_crs, "EPSG:32615")
        geom = shapely.geometry.Point(-20, 30).buffer(1).envelope

        ctx_no_update_bounds = ctx.assign(geometry=geom)
        self.assertEqual(ctx_no_update_bounds.bounds_crs, "EPSG:32615")

        ctx_update_bounds = ctx.assign(geometry=geom, bounds="update")
        self.assertEqual(ctx_update_bounds.bounds_crs, "EPSG:4326")

        with self.assertRaisesRegexp(
                ValueError,
                "Can't compute bounds from a geometry while also explicitly setting"
        ):
            ctx = geocontext.AOI(geometry=geom,
                                 resolution=40,
                                 bounds_crs="EPSG:32615")
Ejemplo n.º 19
0
    def test_search_AOI_with_shape(self):
        aoi = geocontext.AOI(self.geom, shape=(100, 100))
        sc, ctx = search(aoi, products="landsat:LC08:PRE:TOAR", limit=4)
        assert len(sc) > 0
        assert len(sc) <= 4  # test client only has 2 scenes available

        assert ctx.resolution is None
        assert ctx.shape == aoi.shape
        assert ctx.crs == "EPSG:32615"
Ejemplo n.º 20
0
    def test_filter_coverage(self):
        polygon = shapely.geometry.Point(0.0, 0.0).buffer(1)
        ctx = geocontext.AOI(geometry=polygon)

        scenes = SceneCollection([
            Scene(dict(id='foo', geometry=polygon, properties={}), {}),
            Scene(dict(id='bar', geometry=polygon.buffer(-0.1), properties={}), {}),
        ])

        self.assertEqual(len(scenes.filter_coverage(ctx)), 1)
Ejemplo n.º 21
0
 def test_download(self, mock_geotiff):
     scene = MockScene({}, {
         "id": "foo:bar",
         "bands": {
             "nir": {"dtype": "UInt16"},
             "yellow": {"dtype": "UInt16"},
         }
     })
     ctx = geocontext.AOI(bounds=[30, 40, 50, 60], resolution=2, crs="EPSG:4326")
     scene.download("nir yellow", ctx)
     mock_geotiff.assert_called_once()
Ejemplo n.º 22
0
    def setUp(self):
        properties = [{
            "id": "foo:bar" + str(i),
            "bands": {
                "nir": {"dtype": "UInt16"},
                "yellow": {"dtype": "UInt16"},
            }
        } for i in range(3)]

        self.scenes = SceneCollection([MockScene({}, p) for p in properties])
        self.ctx = geocontext.AOI(bounds=[30, 40, 50, 60], resolution=2, crs="EPSG:4326")
Ejemplo n.º 23
0
    def test_coverage(self):
        scene_geometry = shapely.geometry.mapping(
            shapely.geometry.Point(0.0, 0.0).buffer(1))

        scene = Scene(dict(id='foo', geometry=scene_geometry, properties={}),
                      {})

        # same geometry
        ctx = geocontext.AOI(scene_geometry)
        self.assertEqual(scene.coverage(ctx), 1.0)

        # ctx is larger
        ctx = geocontext.AOI(
            shapely.geometry.mapping(
                shapely.geometry.Point(0.0, 0.0).buffer(2)))
        self.assertEqual(scene.coverage(ctx), 0.25)

        # ctx is smaller
        ctx = geocontext.AOI(
            shapely.geometry.mapping(
                shapely.geometry.Point(0.0, 0.0).buffer(0.5)))
        self.assertEqual(scene.coverage(ctx), 1.0)
    def test_validate_bound_geom_intersection(self):
        # bounds don't intersect
        with pytest.raises(ValueError, match="Geometry and bounds do not intersect"):
            geocontext.AOI(
                geometry=shapely.geometry.box(0, 0, 1, 1),
                bounds=[5, 5, 6, 6],
                bounds_crs="EPSG:4326",
            )

        # bounds do intersect; no error should raise
        geocontext.AOI(
            geometry=shapely.geometry.box(0, 0, 1, 1),
            bounds=[0.5, 0.5, 3, 4],
            bounds_crs="EPSG:4326",
        )

        # bounds_crs is not WGS84, so we can't check if bounds and geometry intersect or not---no error should raise
        geocontext.AOI(
            geometry=shapely.geometry.box(0, 0, 1, 1),
            bounds_crs="EPSG:32615",
            bounds=[500000, 2000000, 501000, 2001000],
        )
Ejemplo n.º 25
0
 def test_assign_update_bounds_no_shapely_failes(self):
     geom = {
         'coordinates': (((-93.52300099792355,
                           41.241436141055345), (-93.7138666, 40.703737),
                          (-94.37053769704536,
                           40.83098709945576), (-94.2036617, 41.3717716),
                          (-93.52300099792355, 41.241436141055345)), ),
         'type':
         'Polygon'
     }
     bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355,
                     41.3717716)
     ctx = geocontext.AOI(geom, bounds=bounds_wgs84)
     with self.assertRaises(NotImplementedError):
         ctx.assign(geometry=geom, bounds="update")
Ejemplo n.º 26
0
 def aoi_factory():
     return geocontext.AOI(
         {
             'coordinates': [[
                 [-93.52300099792355, 41.241436141055345],
                 [-93.7138666, 40.703737],
                 [-94.37053769704536, 40.83098709945576],
                 [-94.2036617, 41.3717716],
                 [-93.52300099792355, 41.241436141055345],
             ]],
             'type': 'Polygon'
         },
         crs="EPSG:3857",
         resolution=10
     )
Ejemplo n.º 27
0
    def test_geointerface_without_shapely(self):
        geom = {
            'coordinates': (((-93.52300099792355,
                              41.241436141055345), (-93.7138666, 40.703737),
                             (-94.37053769704536,
                              40.83098709945576), (-94.2036617, 41.3717716),
                             (-93.52300099792355, 41.241436141055345)), ),
            'type':
            'Polygon'
        }
        geointerfaceable = mock.Mock()
        geointerfaceable.__geo_interface__ = geom

        bounds_wgs84 = (-94.37053769704536, 40.703737, -93.52300099792355,
                        41.3717716)
        ctx = geocontext.AOI(geometry=geointerfaceable, bounds=bounds_wgs84)
        self.assertEqual(ctx.geometry, geom)
Ejemplo n.º 28
0
    def test_init_base_params(self):
        geom = {
            'coordinates': [
                [[-93.52300099792355, 41.241436141055345],
                 [-93.7138666, 40.703737],
                 [-94.37053769704536, 40.83098709945576],
                 [-94.2036617, 41.3717716],
                 [-93.52300099792355, 41.241436141055345]],
            ],
            'type':
            'Polygon'
        }
        resolution = 40

        ctx = geocontext.AOI(geom, resolution)
        self.assertEqual(ctx.resolution, resolution)
        self.assertEqual(
            ctx.bounds,
            (-94.37053769704536, 40.703737, -93.52300099792355, 41.3717716))
        self.assertIsInstance(ctx.geometry, shapely.geometry.Polygon)
Ejemplo n.º 29
0
    def test_assign(self):
        geom = {
            'coordinates': [[
                [-93.52300099792355, 41.241436141055345],
                [-93.7138666, 40.703737],
                [-94.37053769704536, 40.83098709945576],
                [-94.2036617, 41.3717716],
                [-93.52300099792355, 41.241436141055345]],
            ],
            'type': 'Polygon'
        }
        ctx = geocontext.AOI(resolution=40)
        ctx2 = ctx.assign(geometry=geom)
        self.assertEqual(ctx2.geometry, shapely.geometry.shape(geom))
        self.assertEqual(ctx2.resolution, 40)
        self.assertEqual(ctx2.align_pixels, True)
        self.assertEqual(ctx2.shape, None)

        ctx3 = ctx2.assign(geometry=None)
        self.assertEqual(ctx3.geometry, None)
 def test_validate_resolution_shape(self):
     with pytest.raises(ValueError):
         geocontext.AOI(resolution=40, shape=(120, 280))