@@ -42,7 +42,8 @@ def test_numpy_equals_qgis(elevation_raster, qgis_slope):
4242 # slope by xrspatial
4343 numpy_agg = input_data (elevation_raster , backend = 'numpy' )
4444 xrspatial_slope_numpy = slope (numpy_agg , name = 'slope_numpy' )
45- general_output_checks (numpy_agg , xrspatial_slope_numpy )
45+ # slope overrides attrs['units'] to 'degrees', so skip exact-attrs check
46+ general_output_checks (numpy_agg , xrspatial_slope_numpy , verify_attrs = False )
4647 assert xrspatial_slope_numpy .name == 'slope_numpy'
4748 print ('numpy_agg' , numpy_agg )
4849 print ('xrspatial_slope_numpy' , xrspatial_slope_numpy )
@@ -61,15 +62,15 @@ def test_numpy_equals_dask_qgis_data(elevation_raster):
6162 # compare using the data run through QGIS
6263 numpy_agg = input_data (elevation_raster , 'numpy' )
6364 dask_agg = input_data (elevation_raster , 'dask+numpy' )
64- assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope )
65+ assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope , verify_attrs = False )
6566
6667
6768@cuda_and_cupy_available
6869def test_numpy_equals_cupy_qgis_data (elevation_raster ):
6970 # compare using the data run through QGIS
7071 numpy_agg = input_data (elevation_raster , 'numpy' )
7172 cupy_agg = input_data (elevation_raster , 'cupy' )
72- assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope )
73+ assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope , verify_attrs = False )
7374
7475
7576@dask_array_available
@@ -80,7 +81,8 @@ def test_numpy_equals_cupy_qgis_data(elevation_raster):
8081def test_numpy_equals_dask_cupy_random_data (random_data ):
8182 numpy_agg = create_test_raster (random_data , backend = 'numpy' )
8283 dask_cupy_agg = create_test_raster (random_data , backend = 'dask+cupy' )
83- assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , atol = 1e-6 , rtol = 1e-6 )
84+ assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , atol = 1e-6 , rtol = 1e-6 ,
85+ verify_attrs = False )
8486
8587
8688@dask_array_available
@@ -330,7 +332,7 @@ def test_degenerate_shape_numpy(shape):
330332 agg = create_test_raster (_degenerate_data (shape ), backend = 'numpy' ,
331333 attrs = {'res' : (1 , 1 )})
332334 result = slope (agg )
333- general_output_checks (agg , result )
335+ general_output_checks (agg , result , verify_attrs = False )
334336 assert result .shape == shape
335337 assert np .all (np .isnan (result .data ))
336338
@@ -342,7 +344,8 @@ def test_degenerate_shape_dask_numpy(shape):
342344 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
343345 dask_agg = create_test_raster (data , backend = 'dask+numpy' ,
344346 attrs = {'res' : (1 , 1 )}, chunks = shape )
345- assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope , nan_edges = False )
347+ assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope , nan_edges = False ,
348+ verify_attrs = False )
346349
347350
348351@cuda_and_cupy_available
@@ -351,7 +354,8 @@ def test_degenerate_shape_cupy(shape):
351354 data = _degenerate_data (shape )
352355 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
353356 cupy_agg = create_test_raster (data , backend = 'cupy' , attrs = {'res' : (1 , 1 )})
354- assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope , nan_edges = False )
357+ assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope , nan_edges = False ,
358+ verify_attrs = False )
355359
356360
357361@dask_array_available
@@ -362,7 +366,8 @@ def test_degenerate_shape_dask_cupy(shape):
362366 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
363367 dask_cupy_agg = create_test_raster (data , backend = 'dask+cupy' ,
364368 attrs = {'res' : (1 , 1 )}, chunks = shape )
365- assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , nan_edges = False )
369+ assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , nan_edges = False ,
370+ verify_attrs = False )
366371
367372
368373@pytest .mark .parametrize ("shape" , DEGENERATE_SHAPES )
@@ -376,7 +381,7 @@ def test_degenerate_shape_geodesic(shape):
376381 coords = {'lat' : lat , 'lon' : lon },
377382 )
378383 result = slope (raster , method = 'geodesic' )
379- general_output_checks (raster , result )
384+ general_output_checks (raster , result , verify_attrs = False )
380385 assert result .shape == shape
381386 assert np .all (np .isnan (result .data ))
382387
@@ -432,7 +437,7 @@ def _center_nan_data():
432437def test_center_nan_propagates_numpy ():
433438 agg = create_test_raster (_center_nan_data (), backend = 'numpy' , attrs = {'res' : (1 , 1 )})
434439 result = slope (agg )
435- general_output_checks (agg , result )
440+ general_output_checks (agg , result , verify_attrs = False )
436441 assert np .isnan (result .data [2 , 2 ])
437442
438443
@@ -442,7 +447,8 @@ def test_center_nan_propagates_dask_numpy():
442447 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
443448 dask_agg = create_test_raster (data , backend = 'dask+numpy' ,
444449 attrs = {'res' : (1 , 1 )}, chunks = (3 , 3 ))
445- assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope , nan_edges = False )
450+ assert_numpy_equals_dask_numpy (numpy_agg , dask_agg , slope , nan_edges = False ,
451+ verify_attrs = False )
446452 assert np .isnan (slope (dask_agg ).data .compute ()[2 , 2 ])
447453
448454
@@ -451,7 +457,8 @@ def test_center_nan_propagates_cupy():
451457 data = _center_nan_data ()
452458 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
453459 cupy_agg = create_test_raster (data , backend = 'cupy' , attrs = {'res' : (1 , 1 )})
454- assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope , nan_edges = False )
460+ assert_numpy_equals_cupy (numpy_agg , cupy_agg , slope , nan_edges = False ,
461+ verify_attrs = False )
455462 assert np .isnan (slope (cupy_agg ).data .get ()[2 , 2 ])
456463
457464
@@ -462,4 +469,67 @@ def test_center_nan_propagates_dask_cupy():
462469 numpy_agg = create_test_raster (data , backend = 'numpy' , attrs = {'res' : (1 , 1 )})
463470 dask_cupy_agg = create_test_raster (data , backend = 'dask+cupy' ,
464471 attrs = {'res' : (1 , 1 )}, chunks = (3 , 3 ))
465- assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , nan_edges = False )
472+ assert_numpy_equals_dask_cupy (numpy_agg , dask_cupy_agg , slope , nan_edges = False ,
473+ verify_attrs = False )
474+
475+
476+ # slope is reported in degrees, so the output must advertise units='degrees'
477+ # rather than leaking the input elevation units (e.g. 'm'). Regression for #2894.
478+ def test_units_overridden_to_degrees_numpy ():
479+ agg = create_test_raster (_degenerate_data ((5 , 5 )), backend = 'numpy' ,
480+ attrs = {'res' : (1 , 1 ), 'units' : 'm' , 'crs' : 'EPSG:5070' })
481+ result = slope (agg )
482+ assert result .attrs ['units' ] == 'degrees'
483+ # other attrs are preserved untouched
484+ assert result .attrs ['res' ] == (1 , 1 )
485+ assert result .attrs ['crs' ] == 'EPSG:5070'
486+ # input is not mutated
487+ assert agg .attrs ['units' ] == 'm'
488+
489+
490+ @dask_array_available
491+ def test_units_overridden_to_degrees_dask_numpy ():
492+ agg = create_test_raster (_degenerate_data ((5 , 5 )), backend = 'dask+numpy' ,
493+ attrs = {'res' : (1 , 1 ), 'units' : 'm' }, chunks = (3 , 3 ))
494+ result = slope (agg )
495+ assert result .attrs ['units' ] == 'degrees'
496+ assert agg .attrs ['units' ] == 'm'
497+
498+
499+ @cuda_and_cupy_available
500+ def test_units_overridden_to_degrees_cupy ():
501+ agg = create_test_raster (_degenerate_data ((5 , 5 )), backend = 'cupy' ,
502+ attrs = {'res' : (1 , 1 ), 'units' : 'm' })
503+ result = slope (agg )
504+ assert result .attrs ['units' ] == 'degrees'
505+ assert agg .attrs ['units' ] == 'm'
506+
507+
508+ @dask_array_available
509+ @cuda_and_cupy_available
510+ def test_units_overridden_to_degrees_dask_cupy ():
511+ agg = create_test_raster (_degenerate_data ((5 , 5 )), backend = 'dask+cupy' ,
512+ attrs = {'res' : (1 , 1 ), 'units' : 'm' }, chunks = (3 , 3 ))
513+ result = slope (agg )
514+ assert result .attrs ['units' ] == 'degrees'
515+ assert agg .attrs ['units' ] == 'm'
516+
517+
518+ def test_units_set_to_degrees_when_input_has_no_units ():
519+ agg = create_test_raster (_degenerate_data ((5 , 5 )), backend = 'numpy' ,
520+ attrs = {'res' : (1 , 1 )})
521+ result = slope (agg )
522+ assert result .attrs ['units' ] == 'degrees'
523+
524+
525+ def test_units_overridden_to_degrees_geodesic ():
526+ H , W = 5 , 5
527+ raster = xr .DataArray (
528+ _degenerate_data ((H , W )),
529+ dims = ['lat' , 'lon' ],
530+ coords = {'lat' : np .linspace (40.0 , 41.0 , H ), 'lon' : np .linspace (10.0 , 11.0 , W )},
531+ attrs = {'units' : 'm' },
532+ )
533+ result = slope (raster , method = 'geodesic' )
534+ assert result .attrs ['units' ] == 'degrees'
535+ assert raster .attrs ['units' ] == 'm'
0 commit comments