Skip to content

Commit 875124b

Browse files
authored
Merge pull request #22 from sjoro/vii
Add VII interpolator.
2 parents 24552f0 + 0607bd9 commit 875124b

File tree

3 files changed

+464
-0
lines changed

3 files changed

+464
-0
lines changed

AUTHORS.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Project Contributors
2+
3+
The following people have made contributions to this project:
4+
5+
<!--- Use your GitHub account or any other personal reference URL --->
6+
<!--- If you wish to not use your real name, please use your github username --->
7+
<!--- The list should be alphabetical by last name if possible, with github usernames at the bottom --->
8+
<!--- See https://gist.github.com/djhoese/52220272ec73b12eb8f4a29709be110d for auto-generating parts of this list --->
9+
10+
- [Amit Aronovitch (AmitAronovitch)](https://github.com/AmitAronovitch)
11+
- [Adam Dybbroe (adybbroe)](https://github.com/adybbroe)
12+
- [Rolf Helge Pfeiffer (HelgeDMI)](https://github.com/HelgeDMI)
13+
- [David Hoese (djhoese)](https://github.com/djhoese)
14+
- [Mikhail Itkin (mitkin)](https://github.com/mitkin)
15+
- [Sauli Joro (sjoro)](https://github.com/sjoro)
16+
- [Panu Lahtinen (pnuu)](https://github.com/pnuu)
17+
- [Martin Raspaud (mraspaud)](https://github.com/mraspaud)
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# Copyright (c) 2017-2020 Python-geotiepoints developers
4+
#
5+
# This file is part of python-geotiepoints.
6+
#
7+
# python-geotiepoints is free software: you can redistribute it and/or modify it under the
8+
# terms of the GNU General Public License as published by the Free Software
9+
# Foundation, either version 3 of the License, or (at your option) any later
10+
# version.
11+
#
12+
# python-geotiepoints is distributed in the hope that it will be useful, but WITHOUT ANY
13+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License along with
17+
# python-geotiepoints. If not, see <http://www.gnu.org/licenses/>.
18+
19+
"""Test of the interpolation of geographical tiepoints for the VII products.
20+
It follows the description provided in document "EPS-SG VII Level 1B Product Format Specification".
21+
"""
22+
23+
import unittest
24+
import numpy as np
25+
import xarray as xr
26+
from geotiepoints.viiinterpolator import tie_points_interpolation, tie_points_geo_interpolation
27+
28+
29+
TEST_N_SCANS = 2
30+
TEST_TIE_POINTS_FACTOR = 2
31+
TEST_SCAN_ALT_TIE_POINTS = 3
32+
TEST_VALID_ALT_TIE_POINTS = TEST_SCAN_ALT_TIE_POINTS * TEST_N_SCANS
33+
TEST_INVALID_ALT_TIE_POINTS = TEST_SCAN_ALT_TIE_POINTS * TEST_N_SCANS + 1
34+
TEST_ACT_TIE_POINTS = 4
35+
36+
# Results of latitude/longitude interpolation with simple interpolation on coordinates
37+
TEST_LON_1 = np.array(
38+
[[-12., -11.5, -11., -10.5, -9., -8.5, -8., -7.5],
39+
[-9., -8.5, -8., -7.5, -6., -5.5, -5., -4.5],
40+
[-6., -5.5, -5., -4.5, -3., -2.5, -2., -1.5],
41+
[-3., -2.5, -2., -1.5, 0., 0.5, 1., 1.5],
42+
[0., 0.5, 1., 1.5, 3., 3.5, 4., 4.5],
43+
[3., 3.5, 4., 4.5, 6., 6.5, 7., 7.5]]
44+
)
45+
TEST_LAT_1 = np.array(
46+
[[0., 0.5, 1., 1.5, 3., 3.5, 4., 4.5],
47+
[3., 3.5, 4., 4.5, 6., 6.5, 7., 7.5],
48+
[6., 6.5, 7., 7.5, 9., 9.5, 10., 10.5],
49+
[9., 9.5, 10., 10.5, 12., 12.5, 13., 13.5],
50+
[12., 12.5, 13., 13.5, 15., 15.5, 16., 16.5],
51+
[15., 15.5, 16., 16.5, 18., 18.5, 19., 19.5]]
52+
)
53+
54+
# Results of latitude/longitude interpolation on cartesian coordinates (latitude above 60 degrees)
55+
TEST_LON_2 = np.array(
56+
[[-12., -11.50003808, -11., -10.50011426, -9., -8.50026689, -8., -7.50034342],
57+
[-9.00824726, -8.50989187, -8.01100418, -7.51272848, -6.01653996, -5.51842688, -5.01932226, -4.52129225],
58+
[-6., -5.50049716, -5., -4.50057447, -3., -2.50073021, -2., -1.50080874],
59+
[-3.02492451, -2.52706443, -2.02774808, -1.52997501, -0.03344942, 0.46414517, 0.96366893, 1.4611719],
60+
[0., 0.49903263, 1., 1.49895241, 3., 3.49878988, 4., 4.49870746],
61+
[2.9578336, 3.45514812, 3.9548757, 4.4520932, 5.94886832, 6.44588569, 6.94581415, 7.44272818]]
62+
)
63+
64+
TEST_LAT_2 = np.array(
65+
[[0., 0.49998096, 1., 1.49994287, 3., 3.49986656, 4., 4.4998283],
66+
[2.99588485, 3.49506416, 3.99450923, 4.49364876, 5.99174708, 6.49080542, 6.99035883, 7.4893757],
67+
[6., 6.49975143, 7., 7.49971278, 9., 9.49963492, 10., 10.49959566],
68+
[8.98756357, 9.48649563, 9.98615477, 10.4850434, 11.98331018, 12.48210974, 12.98187246, 13.4806263],
69+
[12., 12.49951634, 13., 13.49947623, 15., 15.49939498, 16., 16.49935377],
70+
[14.97896116, 15.47762097, 15.97748548, 16.47609689, 17.97448854, 18.47300011, 18.97296495, 19.47142496]]
71+
)
72+
73+
# Results of latitude/longitude interpolation on cartesian coordinates (longitude with a 360 degrees step)
74+
TEST_LON_3 = np.array(
75+
[[-12., -11.50444038, -11., -10.50459822, -9., -8.50493209, -8., -7.50510905],
76+
[-9.17477341, -8.68280267, -8.18102962, -7.68936161, -6.19433153, -5.70332248, -5.20141997, -4.71077058],
77+
[-6., -5.50548573, -5., -4.50568668, -3., -2.50611746, -2., -1.506349],
78+
[-3.2165963, -2.72673687, -2.22474246, -1.73531828, -0.24232275, 0.24613534, 0.7481615, 1.23608137],
79+
[0., 0.49315061, 1., 1.49287934, 3., 3.49228746, 4., 4.49196335],
80+
[2.72743411, 3.21414435, 3.71610443, 4.20213182, 5.69115252, 6.17562189, 6.67735289, 7.16092853]]
81+
)
82+
83+
TEST_LAT_3 = np.array(
84+
[[45., 45.49777998, 46., 46.49770107, 48., 48.49753416, 49., 49.49744569],
85+
[47.91286617, 48.40886652, 48.90975264, 49.40560282, 50.90313463, 51.39865815, 51.8996091, 52.39495445],
86+
[51., 51.49725738, 52., 52.49715691, 54., 54.50311196, 55., 55.50299846],
87+
[54.11364234, 54.61463583, 55.10950687, 55.61043728, 57.10152529, 57.60232834, 58.09766771, 58.59840655],
88+
[57., 57.50277937, 58., 58.50267347, 60., 60.50246826, 61., 61.5023687],
89+
[60.09019289, 60.59080228, 61.0865661, 61.58711028, 63.07951189, 63.57992468, 64.07607638, 64.57642295]]
90+
)
91+
92+
93+
class TestViiInterpolator(unittest.TestCase):
94+
"""Test the vii_utils module."""
95+
96+
def setUp(self):
97+
"""Set up the test."""
98+
# Create the arrays for the interpolation test
99+
# The first has a valid number of n_tie_alt points (multiple of SCAN_ALT_TIE_POINTS)
100+
self.valid_data_for_interpolation = xr.DataArray(
101+
np.arange(
102+
TEST_VALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
103+
dtype=np.float64,
104+
).reshape(TEST_ACT_TIE_POINTS, TEST_VALID_ALT_TIE_POINTS),
105+
dims=('num_tie_points_act', 'num_tie_points_alt'),
106+
)
107+
# The second has an invalid number of n_tie_alt points (not multiple of SCAN_ALT_TIE_POINTS)
108+
self.invalid_data_for_interpolation = xr.DataArray(
109+
np.arange(
110+
TEST_INVALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
111+
dtype=np.float64,
112+
).reshape(TEST_ACT_TIE_POINTS, TEST_INVALID_ALT_TIE_POINTS),
113+
dims=('num_tie_points_act', 'num_tie_points_alt'),
114+
)
115+
# Then two arrays containing valid longitude and latitude data
116+
self.longitude = xr.DataArray(
117+
np.linspace(
118+
-12,
119+
11,
120+
num=TEST_VALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
121+
dtype=np.float64,
122+
).reshape(TEST_ACT_TIE_POINTS, TEST_VALID_ALT_TIE_POINTS),
123+
dims=('num_tie_points_act', 'num_tie_points_alt'),
124+
)
125+
self.latitude = xr.DataArray(
126+
np.linspace(
127+
0,
128+
23,
129+
num=TEST_VALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
130+
dtype=np.float64,
131+
).reshape(TEST_ACT_TIE_POINTS, TEST_VALID_ALT_TIE_POINTS),
132+
dims=('num_tie_points_act', 'num_tie_points_alt'),
133+
)
134+
# Then one containing latitude data above 60 degrees
135+
self.latitude_over60 = xr.DataArray(
136+
np.linspace(
137+
45,
138+
68,
139+
num=TEST_VALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
140+
dtype=np.float64,
141+
).reshape(TEST_ACT_TIE_POINTS, TEST_VALID_ALT_TIE_POINTS),
142+
dims=('num_tie_points_act', 'num_tie_points_alt'),
143+
)
144+
# Then one containing longitude data with a 360 degrees step
145+
self.longitude_over360 = xr.DataArray(
146+
np.linspace(
147+
-12,
148+
11,
149+
num=TEST_VALID_ALT_TIE_POINTS * TEST_ACT_TIE_POINTS,
150+
dtype=np.float64,
151+
).reshape(TEST_ACT_TIE_POINTS, TEST_VALID_ALT_TIE_POINTS) % 360.,
152+
dims=('num_tie_points_act', 'num_tie_points_alt'),
153+
)
154+
155+
def tearDown(self):
156+
"""Tear down the test."""
157+
# Nothing to do
158+
pass
159+
160+
def test_tie_points_interpolation(self):
161+
"""# Test the interpolation routine with valid and invalid input."""
162+
# Test the interpolation routine with valid input
163+
result_valid = tie_points_interpolation(
164+
[self.valid_data_for_interpolation],
165+
TEST_SCAN_ALT_TIE_POINTS,
166+
TEST_TIE_POINTS_FACTOR
167+
)[0]
168+
169+
act_points_interp = (TEST_ACT_TIE_POINTS - 1) * TEST_TIE_POINTS_FACTOR
170+
num_scans = TEST_VALID_ALT_TIE_POINTS // TEST_SCAN_ALT_TIE_POINTS
171+
scan_alt_points_interp = (TEST_SCAN_ALT_TIE_POINTS - 1) * TEST_TIE_POINTS_FACTOR
172+
173+
# It is easier to check the delta between interpolated points, which must be 1/8 of the original delta
174+
# Across the track, it is possible to check the delta on the entire array
175+
delta_axis_0 = 1.0 * TEST_VALID_ALT_TIE_POINTS / TEST_TIE_POINTS_FACTOR
176+
self.assertTrue(np.allclose(
177+
np.diff(result_valid, axis=0),
178+
np.ones((act_points_interp - 1, num_scans * scan_alt_points_interp)) * delta_axis_0
179+
))
180+
181+
delta_axis_1 = 1.0 / TEST_TIE_POINTS_FACTOR
182+
# Along the track, it is necessary to check the delta on each scan separately
183+
for i in range(num_scans):
184+
first_index = i*(TEST_SCAN_ALT_TIE_POINTS-1)*TEST_TIE_POINTS_FACTOR
185+
last_index = (i+1)*(TEST_SCAN_ALT_TIE_POINTS-1)*TEST_TIE_POINTS_FACTOR
186+
result_per_scan = result_valid[:, first_index:last_index]
187+
self.assertTrue(np.allclose(
188+
np.diff(result_per_scan, axis=1),
189+
np.ones((act_points_interp, (TEST_SCAN_ALT_TIE_POINTS-1)*TEST_TIE_POINTS_FACTOR - 1)) * delta_axis_1
190+
))
191+
192+
self.assertEqual(len(result_valid.coords), 0)
193+
194+
# Test the interpolation routine with invalid input
195+
with self.assertRaises(ValueError):
196+
tie_points_interpolation(
197+
[self.invalid_data_for_interpolation],
198+
TEST_SCAN_ALT_TIE_POINTS,
199+
TEST_TIE_POINTS_FACTOR
200+
)[0]
201+
202+
def test_tie_points_geo_interpolation(self):
203+
"""# Test the coordinates interpolation routine with valid and invalid input."""
204+
# Test the interpolation routine with valid input
205+
lon, lat = tie_points_geo_interpolation(
206+
self.longitude,
207+
self.latitude,
208+
TEST_SCAN_ALT_TIE_POINTS,
209+
TEST_TIE_POINTS_FACTOR
210+
)
211+
self.assertTrue(np.allclose(lon, TEST_LON_1))
212+
self.assertTrue(np.allclose(lat, TEST_LAT_1))
213+
214+
lon, lat = tie_points_geo_interpolation(
215+
self.longitude_over360,
216+
self.latitude,
217+
TEST_SCAN_ALT_TIE_POINTS,
218+
TEST_TIE_POINTS_FACTOR
219+
)
220+
self.assertTrue(np.allclose(lon, TEST_LON_2))
221+
self.assertTrue(np.allclose(lat, TEST_LAT_2))
222+
223+
lon, lat = tie_points_geo_interpolation(
224+
self.longitude,
225+
self.latitude_over60,
226+
TEST_SCAN_ALT_TIE_POINTS,
227+
TEST_TIE_POINTS_FACTOR
228+
)
229+
self.assertTrue(np.allclose(lon, TEST_LON_3))
230+
self.assertTrue(np.allclose(lat, TEST_LAT_3))
231+
232+
# Test the interpolation routine with invalid input (different dimensions of the two arrays)
233+
with self.assertRaises(ValueError):
234+
tie_points_geo_interpolation(
235+
self.longitude,
236+
self.invalid_data_for_interpolation,
237+
TEST_SCAN_ALT_TIE_POINTS,
238+
TEST_TIE_POINTS_FACTOR
239+
)
240+
241+
242+
def suite():
243+
"""The suite for VII interpolator"""
244+
loader = unittest.TestLoader()
245+
mysuite = unittest.TestSuite()
246+
mysuite.addTest(loader.loadTestsFromTestCase(TestViiInterpolator))
247+
248+
return mysuite
249+
250+
251+
if __name__ == "__main__":
252+
unittest.main()

0 commit comments

Comments
 (0)