#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2017-2023 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy. If not, see <http://www.gnu.org/licenses/>.
"""CF processing of time information (coordinates and dimensions)."""
import logging
import numpy as np
import pytest
import xarray as xr
from pyresample import AreaDefinition
# NOTE:
# The following fixtures are not defined in this file, but are used and injected by Pytest:
# - caplog
[docs]
class TestCFtime:
"""Test cases for CF time dimension and coordinates."""
[docs]
def test_add_time_bounds_dimension(self):
"""Test addition of CF-compliant time attributes."""
from satpy.cf.coords import add_time_bounds_dimension
test_array = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
times = np.array(["2018-05-30T10:05:00", "2018-05-30T10:05:01",
"2018-05-30T10:05:02", "2018-05-30T10:05:03"], dtype=np.datetime64)
dataarray = xr.DataArray(test_array,
dims=["y", "x"],
coords={"time": ("y", times)},
attrs=dict(start_time=times[0], end_time=times[-1]))
ds = dataarray.to_dataset(name="test-array")
ds = add_time_bounds_dimension(ds)
assert "bnds_1d" in ds.dims
assert ds.dims["bnds_1d"] == 2
assert "time_bnds" in list(ds.data_vars)
assert "bounds" in ds["time"].attrs
assert "standard_name" in ds["time"].attrs
# set_cf_time_info
[docs]
class TestCFcoords:
"""Test cases for CF spatial dimension and coordinates."""
[docs]
def test_check_unique_projection_coords(self):
"""Test that the x and y coordinates are unique."""
from satpy.cf.coords import check_unique_projection_coords
dummy = [[1, 2], [3, 4]]
datas = {"a": xr.DataArray(data=dummy, dims=("y", "x"), coords={"y": [1, 2], "x": [3, 4]}),
"b": xr.DataArray(data=dummy, dims=("y", "x"), coords={"y": [1, 2], "x": [3, 4]}),
"n": xr.DataArray(data=dummy, dims=("v", "w"), coords={"v": [1, 2], "w": [3, 4]})}
check_unique_projection_coords(datas)
datas["c"] = xr.DataArray(data=dummy, dims=("y", "x"), coords={"y": [1, 3], "x": [3, 4]})
with pytest.raises(ValueError, match="must have identical projection coordinates"):
check_unique_projection_coords(datas)
[docs]
def test_add_coordinates_attrs_coords(self):
"""Check that coordinates link has been established correctly."""
from satpy.cf.coords import add_coordinates_attrs_coords
data = [[1, 2], [3, 4]]
lon = np.zeros((2, 2))
lon2 = np.zeros((1, 2, 2))
lat = np.ones((2, 2))
datasets = {
"var1": xr.DataArray(data=data, dims=("y", "x"), attrs={"coordinates": "lon lat"}),
"var2": xr.DataArray(data=data, dims=("y", "x")),
"var3": xr.DataArray(data=data, dims=("y", "x"), attrs={"coordinates": "lon2 lat"}),
"var4": xr.DataArray(data=data, dims=("y", "x"), attrs={"coordinates": "not_exist lon lat"}),
"lon": xr.DataArray(data=lon, dims=("y", "x")),
"lon2": xr.DataArray(data=lon2, dims=("time", "y", "x")),
"lat": xr.DataArray(data=lat, dims=("y", "x"))
}
datasets = add_coordinates_attrs_coords(datasets)
# Check that link has been established correctly and 'coordinate' atrribute has been dropped
assert "lon" in datasets["var1"].coords
assert "lat" in datasets["var1"].coords
np.testing.assert_array_equal(datasets["var1"]["lon"].data, lon)
np.testing.assert_array_equal(datasets["var1"]["lat"].data, lat)
assert "coordinates" not in datasets["var1"].attrs
# There should be no link if there was no 'coordinate' attribute
assert "lon" not in datasets["var2"].coords
assert "lat" not in datasets["var2"].coords
# The non-existent dimension or coordinate should be dropped
assert "time" not in datasets["var3"].coords
assert "not_exist" not in datasets["var4"].coords
[docs]
def test_ensure_unique_nondimensional_coords(self):
"""Test that created coordinate variables are unique."""
from satpy.cf.coords import ensure_unique_nondimensional_coords
data = [[1, 2], [3, 4]]
y = [1, 2]
x = [1, 2]
time1 = [1, 2]
time2 = [3, 4]
datasets = {"var1": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x, "acq_time": ("y", time1)}),
"var2": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x, "acq_time": ("y", time2)})}
# Test that dataset names are prepended to alternative coordinates
res = ensure_unique_nondimensional_coords(datasets)
np.testing.assert_array_equal(res["var1"]["var1_acq_time"], time1)
np.testing.assert_array_equal(res["var2"]["var2_acq_time"], time2)
assert "acq_time" not in res["var1"].coords
assert "acq_time" not in res["var2"].coords
# Make sure nothing else is modified
np.testing.assert_array_equal(res["var1"]["x"], x)
np.testing.assert_array_equal(res["var1"]["y"], y)
np.testing.assert_array_equal(res["var2"]["x"], x)
np.testing.assert_array_equal(res["var2"]["y"], y)
# Coords not unique -> Dataset names must be prepended, even if pretty=True
with pytest.warns(UserWarning, match='Cannot pretty-format "acq_time"'):
res = ensure_unique_nondimensional_coords(datasets, pretty=True)
np.testing.assert_array_equal(res["var1"]["var1_acq_time"], time1)
np.testing.assert_array_equal(res["var2"]["var2_acq_time"], time2)
assert "acq_time" not in res["var1"].coords
assert "acq_time" not in res["var2"].coords
# Coords unique and pretty=True -> Don't modify coordinate names
datasets["var2"]["acq_time"] = ("y", time1)
res = ensure_unique_nondimensional_coords(datasets, pretty=True)
np.testing.assert_array_equal(res["var1"]["acq_time"], time1)
np.testing.assert_array_equal(res["var2"]["acq_time"], time1)
assert "var1_acq_time" not in res["var1"].coords
assert "var2_acq_time" not in res["var2"].coords
[docs]
def test_is_projected(self, caplog):
"""Tests for private _is_projected function."""
from satpy.cf.coords import _is_projected
# test case with units but no area
da = xr.DataArray(
np.arange(25).reshape(5, 5),
dims=("y", "x"),
coords={"x": xr.DataArray(np.arange(5), dims=("x",), attrs={"units": "m"}),
"y": xr.DataArray(np.arange(5), dims=("y",), attrs={"units": "m"})})
assert _is_projected(da)
da = xr.DataArray(
np.arange(25).reshape(5, 5),
dims=("y", "x"),
coords={"x": xr.DataArray(np.arange(5), dims=("x",), attrs={"units": "degrees_east"}),
"y": xr.DataArray(np.arange(5), dims=("y",), attrs={"units": "degrees_north"})})
assert not _is_projected(da)
da = xr.DataArray(
np.arange(25).reshape(5, 5),
dims=("y", "x"))
with caplog.at_level(logging.WARNING):
assert _is_projected(da)
assert "Failed to tell if data are projected." in caplog.text
[docs]
@pytest.fixture
def datasets(self):
"""Create test dataset."""
data = [[75, 2], [3, 4]]
y = [1, 2]
x = [1, 2]
geos = AreaDefinition(
area_id="geos",
description="geos",
proj_id="geos",
projection={"proj": "geos", "h": 35785831., "a": 6378169., "b": 6356583.8},
width=2, height=2,
area_extent=[-1, -1, 1, 1])
datasets = {
"var1": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x}),
"var2": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x}),
"lat": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x}),
"lon": xr.DataArray(data=data,
dims=("y", "x"),
coords={"y": y, "x": x})}
datasets["lat"].attrs["standard_name"] = "latitude"
datasets["var1"].attrs["standard_name"] = "dummy"
datasets["var2"].attrs["standard_name"] = "dummy"
datasets["var2"].attrs["area"] = geos
datasets["var1"].attrs["area"] = geos
datasets["lat"].attrs["name"] = "lat"
datasets["var1"].attrs["name"] = "var1"
datasets["var2"].attrs["name"] = "var2"
datasets["lon"].attrs["name"] = "lon"
return datasets
[docs]
def test__is_lon_or_lat_dataarray(self, datasets):
"""Test the _is_lon_or_lat_dataarray function."""
from satpy.cf.coords import _is_lon_or_lat_dataarray
assert _is_lon_or_lat_dataarray(datasets["lat"])
assert not _is_lon_or_lat_dataarray(datasets["var1"])
[docs]
def test_has_projection_coords(self, datasets):
"""Test the has_projection_coords function."""
from satpy.cf.coords import has_projection_coords
assert has_projection_coords(datasets)
datasets["lat"].attrs["standard_name"] = "dummy"
assert not has_projection_coords(datasets)