Skip to content

Commit

Permalink
Merge 9c9e345 into ec3cdb1
Browse files Browse the repository at this point in the history
  • Loading branch information
pnuu committed Feb 14, 2017
2 parents ec3cdb1 + 9c9e345 commit 4733255
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 37 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: python
python:
- '2.6'
- '2.7'
install:
- pip install .
Expand Down
52 changes: 44 additions & 8 deletions mpop/projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

import numpy as np
from pyresample import image, utils, geometry, kd_tree
from pyresample.bilinear import get_sample_from_bil_info, get_bil_info

from mpop import CONFIG_PATH

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -93,21 +95,25 @@ class Projector(object):
generated projectors can be saved to disk for later reuse. Use the
:meth:`save` method for this.
To define a projector object, on has to specify *in_area* and *out_area*,
and can also input the *in_lonlats* or the *mode* ('quick' which works only
if both in- and out-areas are AreaDefinitions, or 'nearest'). *radius*
defines the radius of influence for nearest neighbour search in 'nearest'
mode.
To define a projector object, on has to specify *in_area* and
*out_area*, and can also input the *in_lonlats* or the *mode*.
Available modes area:
- 'quick' (works only if both in- and out-areas are AreaDefinitions)
- 'bilinear' (out-area needs to be AreaDefinition with proj4_string)
- 'ewa'
- 'nearest'.
*radius* defines the radius of influence for nearest neighbour
search in 'nearest' mode.
"""

def __init__(self, in_area, out_area,
in_latlons=None, mode=None,
radius=10000, nprocs=1):

if (mode is not None and
mode not in ["quick", "nearest", "ewa"]):
raise ValueError(
"Projector mode must be 'nearest', 'quick' or 'ewa'")
mode not in ["quick", "nearest", "ewa", "bilinear"]):
raise ValueError("Projector mode must be one of 'nearest', "
"'quick', 'ewa', 'bilinear'")

self.area_file = get_area_file()

Expand All @@ -134,6 +140,7 @@ def __init__(self, in_area, out_area,
self.in_area = in_area
except AttributeError:
try:
# TODO: Note that latlons are in order (lons, lats)
self.in_area = geometry.SwathDefinition(lons=in_latlons[0],
lats=in_latlons[1])
in_id = in_area
Expand Down Expand Up @@ -227,6 +234,19 @@ def __init__(self, in_area, out_area,
self._cache['ewa_cols'] = cols
self._cache['ewa_rows'] = rows

elif self.mode == "bilinear":

bilinear_t, bilinear_s, input_idxs, idx_arr = \
get_bil_info(self.in_area, self.out_area,
self.radius, neighbours=32,
nprocs=nprocs, masked=False)

self._cache = {}
self._cache['bilinear_s'] = bilinear_s
self._cache['bilinear_t'] = bilinear_t
self._cache['input_idxs'] = input_idxs
self._cache['idx_arr'] = idx_arr

def save(self, resave=False):
"""Save the precomputation to disk, and overwrite existing file in case
*resave* is true.
Expand Down Expand Up @@ -282,4 +302,20 @@ def project_array(self, data):
self.out_area, data,
rows_per_scan=rows_per_scan)

elif self.mode == "bilinear":

if 'bilinear_t' not in self._cache:
self._cache['bilinear_t'] = self._file_cache['bilinear_t']
self._cache['bilinear_s'] = self._file_cache['bilinear_s']
self._cache['input_idxs'] = self._file_cache['input_idxs']
self._cache['idx_arr'] = self._file_cache['idx_arr']

res = get_sample_from_bil_info(data.ravel(),
self._cache['bilinear_t'],
self._cache['bilinear_s'],
self._cache['input_idxs'],
self._cache['idx_arr'],
output_shape=self.out_area.shape)
res = np.ma.masked_invalid(res)

return res
70 changes: 42 additions & 28 deletions mpop/tests/test_projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

# SMHI,
# Folkborgsvägen 1,
# Norrköping,
# Norrköping,
# Sweden

# Author(s):

# Martin Raspaud <martin.raspaud@smhi.se>
# Adam Dybbroe <adam.dybbroe@smhi.se>

Expand Down Expand Up @@ -37,23 +37,26 @@
from mock import MagicMock, patch
import sys
sys.modules['pyresample'] = MagicMock()
sys.modules['pyresample.bilinear'] = MagicMock()

from pyresample import geometry, utils

from mpop.projector import Projector
import mpop.projector



class TestProjector(unittest.TestCase):

"""Class for testing the Projector class.
"""

proj = None

@patch('mpop.projector.get_bil_info')
@patch.object(utils, 'generate_quick_linesample_arrays')
@patch.object(mpop.projector.kd_tree, 'get_neighbour_info')
@patch.object(mpop.projector, '_get_area_hash')
def test_init(self, gah, gni, gqla):
def test_init(self, gah, gni, gqla, bil_info):
"""Creation of coverage.
"""

Expand All @@ -62,7 +65,6 @@ def test_init(self, gah, gni, gqla):
self.assertRaises(TypeError, Projector)
self.assertRaises(TypeError, Projector, random_string(20))


# in case of string arguments

in_area_id = random_string(20)
Expand All @@ -78,12 +80,9 @@ def test_init(self, gah, gni, gqla):
utils.parse_area_file.assert_any_call(area_file, in_area_id)
utils.parse_area_file.assert_any_call(area_file, out_area_id)



self.assertEquals(self.proj.in_area, area_type)
self.assertEquals(self.proj.out_area, area_type)


# in case of undefined areas

mock = MagicMock(side_effect=Exception("raise"))
Expand All @@ -109,18 +108,21 @@ def test_init(self, gah, gni, gqla):
self.assertEquals(self.proj.in_area, in_area)

in_area = geometry.SwathDefinition()
utils.parse_area_file.return_value.__getitem__.side_effect = [AttributeError, out_area_id]
utils.parse_area_file.return_value.__getitem__.side_effect = [
AttributeError, out_area_id]
self.proj = Projector(in_area, out_area_id)
self.assertEquals(self.proj.in_area, in_area)

out_area = geometry.AreaDefinition()
utils.parse_area_file.return_value.__getitem__.side_effect = [in_area_id, AttributeError]
utils.parse_area_file.return_value.__getitem__.side_effect = [
in_area_id, AttributeError]
self.proj = Projector(in_area_id, out_area)
self.assertEquals(self.proj.out_area, out_area)

# in case of lon/lat is input

utils.parse_area_file.return_value.__getitem__.side_effect = [AttributeError, out_area_id]
utils.parse_area_file.return_value.__getitem__.side_effect = [
AttributeError, out_area_id]
lonlats = ("great_lons", "even_greater_lats")

self.proj = Projector("raise", out_area_id, lonlats)
Expand All @@ -146,13 +148,25 @@ def test_init(self, gah, gni, gqla):
self.assertTrue(cache['col_idx'] is not None)

# nearest mode cache

self.proj = Projector(in_area_id, out_area_id, mode="nearest")
cache = getattr(self.proj, "_cache")
self.assertTrue(cache['valid_index'] is not None)
self.assertTrue(cache['valid_output_index'] is not None)
self.assertTrue(cache['index_array'] is not None)

# bilinear mode cache
bil_info.return_value = (1, 2, 3, 4)

def spam(val):
return 'adef'

with patch.object(mpop.projector, 'get_area_def', spam):
self.proj = Projector(in_area_id, out_area_id, mode="bilinear")
cache = getattr(self.proj, "_cache")
self.assertTrue(cache['bilinear_t'] is not None)
self.assertTrue(cache['bilinear_s'] is not None)
self.assertTrue(cache['input_idxs'] is not None)
self.assertTrue(cache['idx_arr'] is not None)

@patch.object(np.ma, "array")
@patch.object(mpop.projector.kd_tree, 'get_sample_from_neighbour_info')
Expand All @@ -164,35 +178,36 @@ def test_project_array(self, npload, gsfni, marray):
out_area_id = random_string(20)
data = np.random.standard_normal((3, 1))

utils.parse_area_file.return_value.__getitem__.side_effect = ["a", "b", "c", "d"]
utils.parse_area_file.return_value.__getitem__.side_effect = [
"a", "b", "c", "d"]
# test quick
self.proj = Projector(in_area_id, out_area_id, mode="quick")
self.proj.project_array(data)
mpop.projector.image.ImageContainer.assert_called_with(\
mpop.projector.image.ImageContainer.assert_called_with(
data, "a", fill_value=None)
mpop.projector.image.ImageContainer.return_value.\
get_array_from_linesample.assert_called_with(\
self.proj._cache["row_idx"], self.proj._cache["col_idx"])
marray.assert_called_once_with(\
mpop.projector.image.ImageContainer.return_value.\
get_array_from_linesample.assert_called_with(
self.proj._cache["row_idx"], self.proj._cache["col_idx"])
marray.assert_called_once_with(
mpop.projector.image.ImageContainer.return_value.
get_array_from_linesample.return_value,
dtype=np.dtype('float64'))

# test nearest
in_area = MagicMock()
out_area = MagicMock()
utils.parse_area_file.return_value.__getitem__.side_effect = \
[in_area, out_area]
utils.parse_area_file.return_value.__getitem__.side_effect = [
in_area, out_area]
self.proj = Projector(in_area_id, out_area_id, mode="nearest")
self.proj.project_array(data)
mpop.projector.kd_tree.get_sample_from_neighbour_info.\
assert_called_with('nn',
out_area.shape,
data,
npload.return_value.__getitem__.return_value,
npload.return_value.__getitem__.return_value,
npload.return_value.__getitem__.return_value,
fill_value=None)
assert_called_with('nn',
out_area.shape,
data,
npload.return_value.__getitem__.return_value,
npload.return_value.__getitem__.return_value,
npload.return_value.__getitem__.return_value,
fill_value=None)


def random_string(length,
Expand All @@ -214,4 +229,3 @@ def suite():
mysuite.addTest(loader.loadTestsFromTestCase(TestProjector))

return mysuite

0 comments on commit 4733255

Please sign in to comment.