@@ -840,6 +840,62 @@ def test_convolution_2d_boundary_no_nan(boundary):
840840 np_result .data , da_result .data .compute (), equal_nan = True , rtol = 1e-5 )
841841
842842
843+ # --- convolve_2d float64 preservation (issue-1096) ---
844+
845+
846+ @pytest .mark .parametrize ("backend" , ['numpy' , 'dask+numpy' , 'cupy' , 'dask+cupy' ])
847+ def test_convolve_2d_preserves_float64_1096 (backend ):
848+ """Float64 input should produce float64 output, not float32.
849+
850+ Regression test for #1096: convolve_2d hardcoded .astype(float32)
851+ across all backends.
852+ """
853+ from xrspatial .tests .general_checks import has_cuda_and_cupy
854+ if 'cupy' in backend and not has_cuda_and_cupy ():
855+ pytest .skip ("Requires CUDA and CuPy" )
856+ if 'dask' in backend and da is None :
857+ pytest .skip ("Requires Dask" )
858+
859+ # Values near 1e7 where float32 loses the 0.0x differences
860+ data = np .array ([[1e7 + 0.01 , 1e7 + 0.02 , 1e7 + 0.03 ],
861+ [1e7 + 0.04 , 1e7 + 0.05 , 1e7 + 0.06 ],
862+ [1e7 + 0.07 , 1e7 + 0.08 , 1e7 + 0.09 ],
863+ [1e7 + 0.10 , 1e7 + 0.11 , 1e7 + 0.12 ]], dtype = np .float64 )
864+ kernel = np .array ([[0 , 1 , 0 ], [1 , 1 , 1 ], [0 , 1 , 0 ]], dtype = np .float64 )
865+
866+ agg = create_test_raster (data , backend = backend , chunks = (4 , 3 ))
867+ result = convolve_2d (agg .data , kernel )
868+
869+ if hasattr (result , 'compute' ):
870+ result = result .compute ()
871+ if hasattr (result , 'get' ):
872+ result = result .get ()
873+ result = np .asarray (result )
874+
875+ # Output must be float64
876+ assert result .dtype == np .float64 , f"got { result .dtype } "
877+
878+ # Interior pixel (1,1): kernel cross hits [0.02, 0.04, 0.05, 0.06, 0.08]
879+ # Expected: sum = 5e7 + 0.25
880+ expected_center = 5e7 + 0.25
881+ assert abs (float (result [1 , 1 ]) - expected_center ) < 1e-8 , (
882+ f"center={ result [1 , 1 ]} , expected={ expected_center } "
883+ )
884+
885+
886+ def test_convolve_2d_int_promotes_to_float32_1096 ():
887+ """Integer input should be promoted to float32 (not stay int)."""
888+ data = np .array ([[1 , 2 , 3 ],
889+ [4 , 5 , 6 ],
890+ [7 , 8 , 9 ]], dtype = np .int32 )
891+ kernel = np .array ([[0 , 1 , 0 ], [1 , 1 , 1 ], [0 , 1 , 0 ]], dtype = np .float64 )
892+
893+ result = convolve_2d (data , kernel )
894+ assert np .issubdtype (result .dtype , np .floating ), f"got { result .dtype } "
895+ # Interior pixel (1,1): cross sum = 2+4+5+6+8 = 25
896+ assert float (result [1 , 1 ]) == 25.0
897+
898+
843899# --- 3D (multi-band) focal tests ---
844900
845901
0 commit comments