Ejemplo n.º 1
0
    def test_cache_get_angles(self, input_func, input2_func, exp_equal_sun,
                              exp_num_zarr, tmpdir):
        """Test get_angles when caching is enabled."""
        from satpy.modifiers.angles import (
            STATIC_EARTH_INERTIAL_DATETIME,
            _get_sensor_angles_from_sat_pos,
            _get_valid_lonlats,
            get_angles,
        )

        # Patch methods
        data = input_func()
        additional_cache = exp_num_zarr > 4

        # Compute angles
        from pyorbital.orbital import get_observer_look
        with mock.patch("satpy.modifiers.angles.get_observer_look", wraps=get_observer_look) as gol, \
                satpy.config.set(cache_lonlats=True, cache_sensor_angles=True, cache_dir=str(tmpdir)):
            res = get_angles(data)
            assert all(isinstance(x, xr.DataArray) for x in res)

            # call again, should be cached
            new_data = input2_func(data)
            res2 = get_angles(new_data)
            assert all(isinstance(x, xr.DataArray) for x in res2)
            res, res2 = da.compute(res, res2)
            for r1, r2 in zip(res[:2], res2[:2]):
                if additional_cache:
                    pytest.raises(AssertionError, np.testing.assert_allclose,
                                  r1, r2)
                else:
                    np.testing.assert_allclose(r1, r2)

            for r1, r2 in zip(res[2:], res2[2:]):
                if exp_equal_sun:
                    np.testing.assert_allclose(r1, r2)
                else:
                    pytest.raises(AssertionError, np.testing.assert_allclose,
                                  r1, r2)

            zarr_dirs = glob(str(tmpdir / "*.zarr"))
            assert len(
                zarr_dirs
            ) == exp_num_zarr  # two for lon/lat, one for sata, one for satz

            _get_sensor_angles_from_sat_pos.cache_clear()
            _get_valid_lonlats.cache_clear()
            zarr_dirs = glob(str(tmpdir / "*.zarr"))
            assert len(zarr_dirs) == 0

        assert gol.call_count == data.data.blocks.size * (
            int(additional_cache) + 1)
        args = gol.call_args_list[0][0]
        assert args[:4] == (10.0, 0.0, 12345.678,
                            STATIC_EARTH_INERTIAL_DATETIME)
        exp_sat_lon = 10.1 if additional_cache else 10.0
        args = gol.call_args_list[-1][0]
        assert args[:4] == (exp_sat_lon, 0.0, 12345.678,
                            STATIC_EARTH_INERTIAL_DATETIME)
Ejemplo n.º 2
0
 def test_no_cache_dir_fails(self, tmp_path):
     """Test that 'cache_dir' not being set fails."""
     from satpy.modifiers.angles import _get_sensor_angles_from_sat_pos, get_angles
     data = _get_angle_test_data()
     with pytest.raises(RuntimeError), \
             satpy.config.set(cache_lonlats=True, cache_sensor_angles=True, cache_dir=None):
         get_angles(data)
     with pytest.raises(RuntimeError), \
             satpy.config.set(cache_lonlats=True, cache_sensor_angles=True, cache_dir=None):
         _get_sensor_angles_from_sat_pos.cache_clear()
Ejemplo n.º 3
0
    def test_get_angles_satpos_preference(self, forced_preference):
        """Test that 'actual' satellite position is used for generating sensor angles."""
        from satpy.modifiers.angles import get_angles

        input_data1 = _get_angle_test_data()
        # add additional satellite position metadata
        input_data1.attrs["orbital_parameters"]["nadir_longitude"] = 9.0
        input_data1.attrs["orbital_parameters"]["nadir_latitude"] = 0.01
        input_data1.attrs["orbital_parameters"][
            "satellite_actual_longitude"] = 9.5
        input_data1.attrs["orbital_parameters"][
            "satellite_actual_latitude"] = 0.005
        input_data1.attrs["orbital_parameters"][
            "satellite_actual_altitude"] = 12345679
        input_data2 = input_data1.copy(deep=True)
        input_data2.attrs = deepcopy(input_data1.attrs)
        input_data2.attrs["orbital_parameters"]["nadir_longitude"] = 9.1
        input_data2.attrs["orbital_parameters"]["nadir_latitude"] = 0.02
        input_data2.attrs["orbital_parameters"][
            "satellite_actual_longitude"] = 9.5
        input_data2.attrs["orbital_parameters"][
            "satellite_actual_latitude"] = 0.005
        input_data2.attrs["orbital_parameters"][
            "satellite_actual_altitude"] = 12345679

        from pyorbital.orbital import get_observer_look
        with mock.patch("satpy.modifiers.angles.get_observer_look", wraps=get_observer_look) as gol, \
                satpy.config.set(sensor_angles_position_preference=forced_preference):
            angles1 = get_angles(input_data1)
            da.compute(angles1)
            angles2 = get_angles(input_data2)
            da.compute(angles2)

        # get_observer_look should have been called once per array chunk
        assert gol.call_count == input_data1.data.blocks.size * 2
        if forced_preference == "actual":
            exp_call = mock.call(9.5, 0.005, 12345.679,
                                 input_data1.attrs["start_time"], mock.ANY,
                                 mock.ANY, 0)
            all_same_calls = [exp_call] * gol.call_count
            gol.assert_has_calls(all_same_calls)
            # the dask arrays should have the same name to prove they are the same computation
            for angle_arr1, angle_arr2 in zip(angles1, angles2):
                assert angle_arr1.data.name == angle_arr2.data.name
        else:
            # nadir 1
            gol.assert_any_call(9.0, 0.01, 12345.679,
                                input_data1.attrs["start_time"], mock.ANY,
                                mock.ANY, 0)
            # nadir 2
            gol.assert_any_call(9.1, 0.02, 12345.679,
                                input_data1.attrs["start_time"], mock.ANY,
                                mock.ANY, 0)
Ejemplo n.º 4
0
    def test_cache_get_angles(self, input_func, num_normalized_chunks,
                              exp_zarr_chunks, input2_func, exp_equal_sun,
                              exp_num_zarr, force_bad_glob, tmp_path):
        """Test get_angles when caching is enabled."""
        from satpy.modifiers.angles import STATIC_EARTH_INERTIAL_DATETIME, get_angles

        # Patch methods
        data = input_func()
        additional_cache = exp_num_zarr > 4

        # Compute angles
        from pyorbital.orbital import get_observer_look
        with mock.patch("satpy.modifiers.angles.get_observer_look", wraps=get_observer_look) as gol, \
                satpy.config.set(cache_lonlats=True, cache_sensor_angles=True, cache_dir=str(tmp_path)), \
                warnings.catch_warnings(record=True) as caught_warnings:
            res = get_angles(data)
            self._check_cached_result(res, exp_zarr_chunks)

            # call again, should be cached
            new_data = input2_func(data)
            with _mock_glob_if(force_bad_glob):
                res2 = get_angles(new_data)
            self._check_cached_result(res2, exp_zarr_chunks)

            res_numpy, res2_numpy = da.compute(res, res2)
            for r1, r2 in zip(res_numpy[:2], res2_numpy[:2]):
                _assert_allclose_if(not additional_cache, r1, r2)
            for r1, r2 in zip(res_numpy[2:], res2_numpy[2:]):
                _assert_allclose_if(exp_equal_sun, r1, r2)

            self._check_cache_and_clear(tmp_path, exp_num_zarr)

        if "odd_chunks" in input_func.__name__:
            assert any(w.category is PerformanceWarning
                       for w in caught_warnings)
        else:
            assert not any(w.category is PerformanceWarning
                           for w in caught_warnings)
        assert gol.call_count == num_normalized_chunks * (
            int(additional_cache) + 1)
        args = gol.call_args_list[0][0]
        assert args[:4] == (10.0, 0.0, 12345.678,
                            STATIC_EARTH_INERTIAL_DATETIME)
        exp_sat_lon = 10.1 if additional_cache else 10.0
        args = gol.call_args_list[-1][0]
        assert args[:4] == (exp_sat_lon, 0.0, 12345.678,
                            STATIC_EARTH_INERTIAL_DATETIME)
Ejemplo n.º 5
0
    def __call__(self, projectables, optional_datasets=None, **info):
        """Get the corrected reflectance when removing Rayleigh scattering.

        Uses pyspectral.
        """
        from pyspectral.rayleigh import Rayleigh
        if not optional_datasets or len(optional_datasets) != 4:
            vis, red = self.match_data_arrays(projectables)
            sata, satz, suna, sunz = get_angles(vis)
        else:
            vis, red, sata, satz, suna, sunz = self.match_data_arrays(
                projectables + optional_datasets)

        # get the dask array underneath
        sata = sata.data
        satz = satz.data
        suna = suna.data
        sunz = sunz.data

        # First make sure the two azimuth angles are in the range 0-360:
        sata = sata % 360.
        suna = suna % 360.
        ssadiff = da.absolute(suna - sata)
        ssadiff = da.minimum(ssadiff, 360 - ssadiff)
        del sata, suna

        atmosphere = self.attrs.get('atmosphere', 'us-standard')
        aerosol_type = self.attrs.get('aerosol_type', 'marine_clean_aerosol')
        rayleigh_key = (vis.attrs['platform_name'],
                        vis.attrs['sensor'], atmosphere, aerosol_type)
        logger.info("Removing Rayleigh scattering with atmosphere '%s' and "
                    "aerosol type '%s' for '%s'",
                    atmosphere, aerosol_type, vis.attrs['name'])
        if rayleigh_key not in self._rayleigh_cache:
            corrector = Rayleigh(vis.attrs['platform_name'], vis.attrs['sensor'],
                                 atmosphere=atmosphere,
                                 aerosol_type=aerosol_type)
            self._rayleigh_cache[rayleigh_key] = corrector
        else:
            corrector = self._rayleigh_cache[rayleigh_key]

        try:
            refl_cor_band = corrector.get_reflectance(sunz, satz, ssadiff,
                                                      vis.attrs['name'],
                                                      red.data)
        except (KeyError, IOError):
            logger.warning("Could not get the reflectance correction using band name: %s", vis.attrs['name'])
            logger.warning("Will try use the wavelength, however, this may be ambiguous!")
            refl_cor_band = corrector.get_reflectance(sunz, satz, ssadiff,
                                                      vis.attrs['wavelength'][1],
                                                      red.data)
        proj = vis - refl_cor_band
        proj.attrs = vis.attrs
        self.apply_modifier_info(vis, proj)
        return proj
Ejemplo n.º 6
0
 def _extract_angle_data_arrays(self, datasets, optional_datasets):
     all_datasets = datasets + optional_datasets
     if len(all_datasets) == 1:
         vis = self.match_data_arrays(datasets)[0]
         return vis, get_angles(vis)
     if len(all_datasets) == 5:
         vis, *angles = self.match_data_arrays(datasets + optional_datasets)
         return vis, angles
     raise ValueError("Not sure how to handle provided dependencies. "
                      "Either all 4 angles must be provided or none of "
                      "of them.")
Ejemplo n.º 7
0
    def test_get_angles(self, input_func):
        """Test sun and satellite angle calculation."""
        from satpy.modifiers.angles import get_angles
        data = input_func()

        from pyorbital.orbital import get_observer_look
        with mock.patch("satpy.modifiers.angles.get_observer_look", wraps=get_observer_look) as gol:
            angles = get_angles(data)
            assert all(isinstance(x, xr.DataArray) for x in angles)
            da.compute(angles)

        # get_observer_look should have been called once per array chunk
        assert gol.call_count == data.data.blocks.size
        # Check arguments of get_orbserver_look() call, especially the altitude
        # unit conversion from meters to kilometers
        args = gol.call_args[0]
        assert args[:4] == (10.0, 0.0, 12345.678, data.attrs["start_time"])