2020
2121_DEFAULT_RTOL = 1e-6
2222_DEFAULT_ATOL = 1e-6
23+ _DEFAULT_PXTOL = 0.0
2324
2425# https://paulbourke.net/dataformats/asciiart
2526DEFAULT_GRAYSCALE_70_CHARACTERS = r"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. " [::- 1 ]
2627DEFAULT_GRAYSCALE_10_CHARACTERS = " .:-=+*#%@"
2728
29+
2830def _load_xarray_netcdf (path : Union [str , Path ], ** kwargs ) -> xarray .Dataset :
2931 """
3032 Load a netCDF file as Xarray Dataset
@@ -194,10 +196,11 @@ def _compare_xarray_dataarray(
194196 * ,
195197 rtol : float = _DEFAULT_RTOL ,
196198 atol : float = _DEFAULT_ATOL ,
199+ pxtol : Optional [float ] = _DEFAULT_PXTOL ,
197200 name : str = None ,
198201) -> List [str ]:
199202 """
200- Compare two xarray DataArrays with tolerance and report mismatch issues (as strings)
203+ Compare two xarray DataArrays either with tolerance or with allowable pixels and report mismatch issues (as strings)
201204
202205 Checks that are done (with tolerance):
203206 - (optional) Check fraction of mismatching pixels (difference exceeding some tolerance).
@@ -230,7 +233,12 @@ def _compare_xarray_dataarray(
230233 issues .append (f"Shape mismatch: { actual .shape } != { expected .shape } " )
231234 compatible = len (issues ) == 0
232235 try :
233- xarray .testing .assert_allclose (a = actual , b = expected , rtol = rtol , atol = atol )
236+ if pxtol > _DEFAULT_PXTOL :
237+ assert (
238+ actual != expected
239+ ).mean ().item () <= pxtol , "Percentage number of pixels that are different is above the threshold"
240+ else :
241+ xarray .testing .assert_allclose (a = actual , b = expected , rtol = rtol , atol = atol )
234242 except AssertionError as e :
235243 # TODO: message of `assert_allclose` is typically multiline, split it again or make it one line?
236244 issues .append (str (e ).strip ())
@@ -266,12 +274,14 @@ def assert_xarray_dataarray_allclose(
266274 if issues :
267275 raise AssertionError ("\n " .join (issues ))
268276
277+
269278def _compare_xarray_datasets (
270279 actual : Union [xarray .Dataset , str , Path ],
271280 expected : Union [xarray .Dataset , str , Path ],
272281 * ,
273282 rtol : float = _DEFAULT_RTOL ,
274283 atol : float = _DEFAULT_ATOL ,
284+ pxtol : Optional [float ] = _DEFAULT_PXTOL ,
275285) -> List [str ]:
276286 """
277287 Compare two xarray ``DataSet``s with tolerance and report mismatch issues (as strings)
@@ -290,7 +300,7 @@ def _compare_xarray_datasets(
290300 all_issues .append (f"Xarray DataSet variables mismatch: { actual_vars } != { expected_vars } " )
291301 for var in expected_vars .intersection (actual_vars ):
292302 _log .debug (f"_compare_xarray_datasets: comparing variable { var !r} " )
293- issues = _compare_xarray_dataarray (actual [var ], expected [var ], rtol = rtol , atol = atol , name = var )
303+ issues = _compare_xarray_dataarray (actual [var ], expected [var ], rtol = rtol , atol = atol , pxtol = pxtol , name = var )
294304 if issues :
295305 all_issues .append (f"Issues for variable { var !r} :" )
296306 all_issues .extend (issues )
@@ -387,6 +397,7 @@ def _compare_job_results(
387397 * ,
388398 rtol : float = _DEFAULT_RTOL ,
389399 atol : float = _DEFAULT_ATOL ,
400+ pxtol : Optional [float ] = _DEFAULT_PXTOL ,
390401 tmp_path : Optional [Path ] = None ,
391402) -> List [str ]:
392403 """
@@ -415,12 +426,16 @@ def _compare_job_results(
415426 all_issues .append (f"Issues for metadata file { filename !r} :" )
416427 all_issues .extend (issues )
417428 elif expected_path .suffix .lower () in {".nc" , ".netcdf" }:
418- issues = _compare_xarray_datasets (actual = actual_path , expected = expected_path , rtol = rtol , atol = atol )
429+ issues = _compare_xarray_datasets (
430+ actual = actual_path , expected = expected_path , rtol = rtol , atol = atol , pxtol = pxtol
431+ )
419432 if issues :
420433 all_issues .append (f"Issues for file { filename !r} :" )
421434 all_issues .extend (issues )
422435 elif expected_path .suffix .lower () in {".tif" , ".tiff" , ".gtiff" , ".geotiff" }:
423- issues = _compare_xarray_dataarray (actual = actual_path , expected = expected_path , rtol = rtol , atol = atol )
436+ issues = _compare_xarray_dataarray (
437+ actual = actual_path , expected = expected_path , rtol = rtol , atol = atol , pxtol = pxtol
438+ )
424439 if issues :
425440 all_issues .append (f"Issues for file { filename !r} :" )
426441 all_issues .extend (issues )
@@ -463,6 +478,7 @@ def assert_job_results_allclose(
463478 * ,
464479 rtol : float = _DEFAULT_RTOL ,
465480 atol : float = _DEFAULT_ATOL ,
481+ pxtol : Optional [float ] = _DEFAULT_PXTOL ,
466482 tmp_path : Optional [Path ] = None ,
467483):
468484 """
@@ -474,6 +490,7 @@ def assert_job_results_allclose(
474490 :py:meth:`~openeo.rest.job.JobResults` object or path to directory with downloaded assets.
475491 :param rtol: relative tolerance
476492 :param atol: absolute tolerance
493+ :param pxtol: allowable tolerance for the number of pixels in percentage
477494 :param tmp_path: root temp path to download results if needed.
478495 It's recommended to pass pytest's `tmp_path` fixture here
479496 :raises AssertionError: if not equal within the given tolerance
@@ -483,6 +500,6 @@ def assert_job_results_allclose(
483500 .. warning::
484501 This function is experimental and subject to change.
485502 """
486- issues = _compare_job_results (actual , expected , rtol = rtol , atol = atol , tmp_path = tmp_path )
503+ issues = _compare_job_results (actual , expected , rtol = rtol , atol = atol , pxtol = pxtol , tmp_path = tmp_path )
487504 if issues :
488505 raise AssertionError ("\n " .join (issues ))
0 commit comments