File: test_spherical_utils.py

package info (click to toggle)
pyresample 1.35.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 34,880 kB
  • sloc: python: 20,340; cpp: 463; makefile: 105
file content (406 lines) | stat: -rw-r--r-- 12,750 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2021 Pyresample developers
#
# This program 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.
#
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
"""Test for spherical calculations."""

from unittest.mock import patch

import numpy as np
import pytest

from pyresample.spherical import SphPolygon
from pyresample.spherical_utils import (
    GetNonOverlapUnionsBaseClass,
    check_if_two_polygons_overlap,
    check_keys_int_or_tuple,
    merge_tuples,
)

SET_A = {1, 3, 5, 7, 9}
SET_B = {2, 4, 6, 8, 10}
SET_C = {12, 14, 16, 18}
SET_D = set(range(10, 20))
SET_E = set(range(20, 30, 3))
SET_F = set(range(21, 30, 3))
SET_G = set(range(22, 30, 2))


def fake_merge_tuples(intuple):
    """Fake the merge tuples method."""
    if intuple == (2, (1, 3)):
        return (2, 1, 3)
    if intuple == (5, (4, 6)):
        return (5, 4, 6)

    return None


def test_check_overlap_one_polygon_entirely_inide_another():
    """Test the function to check if two polygons overlap each other.

    In this case one polygon is entirely inside the other.
    """
    vertices = np.array([[1, 1, 20, 20],
                         [1, 20, 20, 1]]).T
    poly1 = SphPolygon(np.deg2rad(vertices))
    vertices = np.array([[0, 0, 30, 30],
                         [0, 30, 30, 0]]).T
    poly2 = SphPolygon(np.deg2rad(vertices))

    result = check_if_two_polygons_overlap(poly1, poly2)
    assert result is True


def test_check_overlap_one_polygon_not_entirely_inside_another():
    """Test the function to check if two polygons overlap each other.

    In this case one polygon is not entirely inside the other but they overlap
    each other.
    """
    vertices = np.array([[180, 90, 0, -90],
                         [89, 89, 89, 89]]).T
    poly1 = SphPolygon(np.deg2rad(vertices))

    vertices = np.array([[-45, -135, 135, 45],
                         [89, 89, 89, 89]]).T
    poly2 = SphPolygon(np.deg2rad(vertices))

    res = check_if_two_polygons_overlap(poly1, poly2)
    assert res is True


def test_check_overlap_two_polygons_having_no_overlap():
    """Test the function to check if two polygons overlap each other.

    In this case the two polygons do not have any overlap.
    """
    vertices = np.array([[10, 10, 20, 20],
                         [10, 20, 20, 10]]).T
    poly1 = SphPolygon(np.deg2rad(vertices))
    vertices = np.array([[25, 25, 40, 40],
                         [25, 40, 40, 25]]).T
    poly2 = SphPolygon(np.deg2rad(vertices))

    res = check_if_two_polygons_overlap(poly1, poly2)

    assert res is False


@patch('pyresample.spherical_utils.check_keys_int_or_tuple')
def test_merge_when_input_objects_do_not_overlap(keys_int_or_tuple):
    """Test main method (merge) of the GetNonOverlapUnionsBaseClass class."""
    mysets = [{1, 3, 5, 7, 9}, {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}]
    myobjects = GetNonOverlapUnionsBaseClass(mysets)

    keys_int_or_tuple.return_code = None

    with patch('pyresample.spherical_utils.merge_tuples', return_value=0):
        myobjects.merge()

    polygons = myobjects.get_polygons()

    assert polygons == mysets


@patch('pyresample.spherical_utils.check_keys_int_or_tuple')
def test_merge_overlapping_and_nonoverlapping_objects(keys_int_or_tuple):
    """Test main method (merge) of the GetNonOverlapUnionsBaseClass class."""
    mysets = [SET_A, SET_B, SET_C, SET_D, SET_E, SET_F, SET_G]
    myobjects = GetNonOverlapUnionsBaseClass(mysets)

    keys_int_or_tuple.return_code = None

    with patch('pyresample.spherical_utils.merge_tuples') as mypatch:
        mypatch.side_effect = fake_merge_tuples
        myobjects.merge()

    polygons = myobjects.get_polygons()
    ids = myobjects.get_ids()

    polygons_expected = [{1, 3, 5, 7, 9},
                         {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
                         {20, 21, 22, 23, 24, 26, 27, 28, 29}]

    assert polygons == polygons_expected

    ids_expected = [0, (2, 1, 3), (5, 4, 6)]

    assert ids == ids_expected


def test_flatten_tuple_input_requires_tuple():
    """Test flatten a nested tuple of integers.

    Input a scalar (not a tuple).

    """
    with pytest.raises(TypeError) as exec_info:
        _ = merge_tuples(7)

    exception_raised = exec_info.value
    assert str(exception_raised) == "Function argument must be a tuple!"


def test_flatten_tuple_input_1tuple():
    """Test flatten a nested tuple of integers.

    Input a tuple of one scalar.

    """
    intuple = (0,)
    res = merge_tuples(intuple)
    assert res == (0,)


def test_flatten_tuple_input_2tuple_of_2tuples_of_2tuples():
    """Test flatten a nested tuple of integers.

    Input a 2-tuple of 2-tuples of 2-tuples

    """
    intuple = (((0, 1), (2, 3)), ((4, 5), (6, 7)))
    res = merge_tuples(intuple)
    assert res == (0, 1, 2, 3, 4, 5, 6, 7)


def test_flatten_tuple_input_2tuple_of_scalar_and_2tuple():
    """Test flatten a nested tuple of integers.

    Input a 2-tuple of a scalar and a 2-tuple

    """
    intuple = (3, (1, 2))
    res = merge_tuples(intuple)
    assert res == (3, 1, 2)


def test_flatten_tuple_input_3tuple_of_scalar_and_2tuple_and_2tuple_of_scalar_and_tuple():
    """Test flatten a nested tuple of integers.

    Input a 3-tuple of a scalar, a 2-tuple and a 2-tuple of a scalar and a tuple

    """
    intuple = (0, (1, 2), (3, (4, 5)))
    res = merge_tuples(intuple)
    assert res == (0, 1, 2, 3, 4, 5)


def test_flatten_tuple_input_tuple_of_scalar_and_2tuple_and_2tuple_of_scalar_and_tuple_and_1tuple():
    """Test flatten a nested tuple of integers.

    Input a tuple of a scalar, a 2-tuple, a 2-tuple of a scalar and a tuple, and a 1-tuple.

    """
    intuple = (0, (1, 2), (3, (4, 5)), (6,))
    res = merge_tuples(intuple)
    assert res == (0, 1, 2, 3, 4, 5, 6)


def test_find_union_pairs_input_one_set():
    """Test finding a union pair.

    In this case input only one listed set (~polygon).

    """
    listed_sets = dict(enumerate((SET_A, )))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv is None


def test_find_union_pairs_input_two_non_overlapping_sets():
    """Test finding a union pair.

    In this case input two non-overlapping sets giving no union.

    """
    listed_sets = dict(enumerate((SET_A, SET_B)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv is None


def test_find_union_pairs_input_two_overlapping_sets():
    """Test finding a union pair.

    In this case input two overlapping sets.

    """
    listed_sets = dict(enumerate((SET_C, SET_D)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv == ((0, 1), set(range(10, 20)))


def test_find_union_pairs_input_three_sets_one_entirely_without_overlap():
    """Test finding a union pair.

    In this case input three sets where one does not overlap the two others
    which in turn do overlap each other.

    """
    listed_sets = dict(enumerate((SET_A, SET_B, SET_D)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv == ((1, 2), {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19})


def test_find_union_pairs_input_four_sets_where_only_two_have_overlap():
    """Test finding a union pair.

    In this case input four sets where two overlap and the other two does not
    overlap with any other.

    """
    listed_sets = dict(enumerate((SET_A, SET_B, SET_C, SET_D)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv == ((1, 3), {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19})


def test_find_union_pairs_input_three_sets_one_entirely_without_overlap_one_already_a_union():
    """Test finding a union pair.

    In this case input three sets, one with no overlap of the others, and one
    of the overlapping ones is already a paired union.

    """
    listed_sets = {0: {1, 3, 5, 7, 9},
                   4: {0, 10},
                   (2, (1, 3)): {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}

    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._find_union_pair(listed_sets)

    assert retv == ((4, (2, (1, 3))), {0, 2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19})


def test_find_union_pairs_input_two_sets_without_overlap_one_already_a_union():
    """Test finding a union pair.

    In this case input two sets with no overlap, but one is already a paired
    union.

    """
    listed_sets = {0: {1, 3, 5, 7, 9},
                   (2, (1, 3)): {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}

    this = GetNonOverlapUnionsBaseClass([])
    retv = this._find_union_pair(listed_sets)

    assert retv is None


def test_merge_unions_input_three_sets_without_overlap():
    """Test merging union pairs iteratively.

    In this case input three sets without any overlap.

    """
    listed_sets = dict(enumerate((SET_A, SET_B, SET_C)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._merge_unions(listed_sets)

    assert retv == {0: SET_A, 1: SET_B, 2: SET_C}


def test_merge_unions_input_two_overlapping_sets():
    """Test merging union pairs iteratively.

    In this case input two overlapping sets.
    """
    listed_sets = dict(enumerate((SET_C, SET_D)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._merge_unions(listed_sets)

    assert retv == {(0, 1): set(range(10, 20))}


def test_merge_unions_input_four_sets_one_overlapping_two_others():
    """Test merging union pairs iteratively.

    In this case input 4 sets, where one is overlapping two others.

    """
    listed_sets = dict(enumerate((SET_A, SET_B, SET_C, SET_D)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._merge_unions(listed_sets)

    assert retv == {0: {1, 3, 5, 7, 9},
                    (2, (1, 3)): {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}


def test_merge_unions_input_two_non_overlapping_sets():
    """Test merging union pairs iteratively.

    In this case input 2 sets that do not overlap, but one is already a paired union.

    """
    listed_sets = {0: {1, 3, 5, 7, 9},
                   (2, (1, 3)): {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._merge_unions(listed_sets)

    assert retv == listed_sets


def test_merge_unions_input_seven_sets_with_overlaps():
    """Test merging union pairs iteratively.

    In this case input 7 sets, several of which overlap each other but one is
    completely unique and has no overlap with any of the other 6.

    """
    listed_sets = dict(enumerate((SET_A, SET_B, SET_C, SET_D, SET_E, SET_F, SET_G)))
    this = GetNonOverlapUnionsBaseClass(listed_sets)
    retv = this._merge_unions(listed_sets)

    assert retv == {0: {1, 3, 5, 7, 9},
                    (2, (1, 3)): {2, 4, 6, 8, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
                    (5, (4, 6)): {20, 21, 22, 23, 24, 26, 27, 28, 29}}


def test_check_keys_int_or_tuple_input_okay():
    """Test the check for dictionary keys and input only a dict with the accepted keys of integers and tuples."""
    adict = {1: [1, 2, 3], (2, 3): [1, 2, 3, 4], (6, (4, 5)): [1, 2, 3, 4, 5]}
    check_keys_int_or_tuple(adict)


def test_check_keys_int_or_tuple_input_string():
    """Test the check for dictionary keys and input a dict with a key which is a string."""
    adict = {1: [1, 2, 3], 'set B': [1, 2, 3, 4]}
    with pytest.raises(KeyError) as exec_info:
        check_keys_int_or_tuple(adict)

    exception_raised = exec_info.value

    assert str(exception_raised) == "'Key must be integer or a tuple (of integers)'"


def test_check_keys_int_or_tuple_input_float():
    """Test the check for dictionary keys and input a dict with a key which is a float."""
    adict = {1.1: [1, 2, 3]}
    with pytest.raises(KeyError) as exec_info:
        check_keys_int_or_tuple(adict)

    exception_raised = exec_info.value
    assert str(exception_raised) == "'Key must be integer or a tuple (of integers)'"