@@ -692,6 +692,29 @@ def test_visvalingam_whyatt_two_points(self):
692692 result = _visvalingam_whyatt (coords , 1.0 )
693693 assert_allclose (result , coords )
694694
695+ def test_simplify_polygons_drops_degenerate (self ):
696+ """Polygons that collapse below 4 vertices should be dropped."""
697+ from ..polygonize import _simplify_polygons
698+
699+ # A single-pixel polygon (4 unique vertices forming a unit square).
700+ # With a very large tolerance, it should collapse and be dropped.
701+ raster = np .array ([
702+ [1 , 2 , 2 ],
703+ [2 , 2 , 2 ],
704+ [2 , 2 , 2 ],
705+ ], dtype = np .int64 )
706+ data = xr .DataArray (raster )
707+ column , pp = polygonize (data , return_type = "numpy" )
708+
709+ # Find the small polygon (value 1, single pixel).
710+ small_idx = column .index (1 )
711+ small_poly = [pp [small_idx ]] # wrap in list for _simplify_polygons
712+
713+ # Large tolerance should collapse the single-pixel polygon.
714+ result = _simplify_polygons (small_poly , tolerance = 10.0 )
715+ # The polygon should be dropped since its exterior collapses.
716+ assert len (result ) == 0 or all (len (r [0 ]) >= 4 for r in result )
717+
695718
696719class TestPolygonizeSimplify :
697720 """Tests for simplify_tolerance and simplify_method parameters."""
@@ -830,6 +853,32 @@ def test_simplify_vw_with_geopandas(self):
830853 assert len (gdf ) == 2
831854 assert gdf .geometry .is_valid .all ()
832855
856+ def test_simplify_with_spatialpandas (self ):
857+ """Simplification should work with spatialpandas return type."""
858+ pytest .importorskip ("spatialpandas" )
859+ raster = np .array ([
860+ [1 , 1 , 2 , 2 ],
861+ [1 , 1 , 2 , 2 ],
862+ [1 , 1 , 2 , 2 ],
863+ ], dtype = np .int64 )
864+ data = xr .DataArray (raster )
865+ result = polygonize (data , simplify_tolerance = 0.5 ,
866+ return_type = "spatialpandas" )
867+ assert len (result ) == 2
868+
869+ def test_simplify_with_awkward (self ):
870+ """Simplification should work with awkward return type."""
871+ pytest .importorskip ("awkward" )
872+ raster = np .array ([
873+ [1 , 1 , 2 , 2 ],
874+ [1 , 1 , 2 , 2 ],
875+ [1 , 1 , 2 , 2 ],
876+ ], dtype = np .int64 )
877+ data = xr .DataArray (raster )
878+ result = polygonize (data , simplify_tolerance = 0.5 ,
879+ return_type = "awkward" )
880+ assert result is not None
881+
833882
834883@pytest .mark .skipif (da is None , reason = "dask not installed" )
835884class TestPolygonizeSimplifyDask :
@@ -895,3 +944,38 @@ def test_simplify_vw_dask_matches_numpy(self):
895944
896945 for val in areas_np :
897946 assert_allclose (areas_dask [val ], areas_np [val ], atol = 1e-10 )
947+
948+
949+ @cuda_and_cupy_available
950+ class TestPolygonizeSimplifyCuPy :
951+ """Simplification with CuPy backend."""
952+
953+ def test_simplify_cupy_matches_numpy (self ):
954+ """CuPy simplification should produce same areas as numpy."""
955+ import cupy as cp
956+ raster = np .array ([
957+ [1 , 1 , 1 , 2 , 2 , 2 ],
958+ [1 , 1 , 2 , 2 , 2 , 2 ],
959+ [1 , 2 , 2 , 2 , 2 , 2 ],
960+ [1 , 1 , 2 , 2 , 2 , 2 ],
961+ [1 , 1 , 1 , 2 , 2 , 2 ],
962+ ], dtype = np .int64 )
963+
964+ data_np = xr .DataArray (raster )
965+ data_cp = xr .DataArray (cp .asarray (raster ))
966+
967+ col_np , pp_np = polygonize (data_np , simplify_tolerance = 1.5 )
968+ col_cp , pp_cp = polygonize (data_cp , simplify_tolerance = 1.5 )
969+
970+ areas_np = {}
971+ for val , rings in zip (col_np , pp_np ):
972+ a = sum (calc_boundary_area (r ) for r in rings )
973+ areas_np [val ] = areas_np .get (val , 0.0 ) + a
974+
975+ areas_cp = {}
976+ for val , rings in zip (col_cp , pp_cp ):
977+ a = sum (calc_boundary_area (r ) for r in rings )
978+ areas_cp [val ] = areas_cp .get (val , 0.0 ) + a
979+
980+ for val in areas_np :
981+ assert_allclose (areas_cp [val ], areas_np [val ], atol = 1e-10 )
0 commit comments