[pyresample] 01/07: New upstream version 1.7.1
Antonio Valentino
a_valentino-guest at moszumanska.debian.org
Fri Dec 22 13:40:18 UTC 2017
This is an automated email from the git hooks/post-receive script.
a_valentino-guest pushed a commit to branch master
in repository pyresample.
commit 52e36972e7a752a5aaae937fde1f82add1014644
Author: Antonio Valentino <antonio.valentino at tiscali.it>
Date: Fri Dec 22 12:34:15 2017 +0100
New upstream version 1.7.1
---
.bumpversion.cfg | 2 +-
.github/ISSUE_TEMPLATE.md | 18 ++++
.github/PULL_REQUEST_TEMPLATE.md | 7 ++
.travis.yml | 3 +
changelog.rst | 97 ++++++++++++++++++++-
pyresample/geometry.py | 183 +++++++++++++++++++++++++++++----------
pyresample/grid.py | 6 +-
pyresample/image.py | 2 +
pyresample/kd_tree.py | 27 +++---
pyresample/test/test_bilinear.py | 99 +++++++++++----------
pyresample/test/test_geometry.py | 128 +++++++++++++++++++++++++++
pyresample/test/test_grid.py | 16 ++++
pyresample/test/test_kd_tree.py | 63 +++++++++-----
pyresample/test/test_rotation.py | 97 +++++++++++++++++++++
pyresample/utils.py | 21 +++--
pyresample/version.py | 2 +-
16 files changed, 636 insertions(+), 135 deletions(-)
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 064551a..e1d2934 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 1.7.0
+current_version = 1.7.1
commit = True
tag = True
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..18c258a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,18 @@
+#### Code Sample, a minimal, complete, and verifiable piece of code
+
+```python
+# Your code here
+
+```
+#### Problem description
+
+[this should also explain **why** the current behaviour is a problem and why the
+expected output is a better solution.]
+
+#### Expected Output
+
+#### Actual Result, Traceback if applicable
+
+#### Versions of Python, package at hand and relevant dependencies
+
+Thank you for reporting an issue !
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..670a685
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+Please make the PR against the `develop` branch.
+
+ - [ ] Closes #xxxx (remove if there is no corresponding issue, which should only be the case for minor changes)
+ - [ ] Tests added (for all bug fixes or enhancements)
+ - [ ] Tests passed (for all non-documentation changes)
+ - [ ] Passes ``git diff origin/develop **/*py | flake8 --diff`` (remove if you did not edit any Python files)
+ - [ ] Fully documented (remove if this change should not be visible to users, e.g., if it is an internal clean-up, or if this is part of a larger project that will be documented later)
diff --git a/.travis.yml b/.travis.yml
index f7c6604..f3e5fae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,6 +21,9 @@ install:
- if [[ $TRAVIS_PYTHON_VERSION == "3.6" ]]; then pip install "matplotlib>=1.5.0,!=2.1.0"; fi
- if [[ $TRAVIS_PYTHON_VERSION == "3.6" ]]; then pip install "sphinx>=1.5.0"; fi
- pip install -r requirements.txt
+- pip install toolz
+- pip install dask
+- pip install xarray
- pip install -e ".[pykdtree,quicklook]"
- pip install coveralls
script:
diff --git a/changelog.rst b/changelog.rst
index 090290a..2560491 100644
--- a/changelog.rst
+++ b/changelog.rst
@@ -2,6 +2,92 @@ Changelog
=========
+v1.7.1 (2017-12-21)
+-------------------
+- update changelog. [davidh-ssec]
+- Bump version: 1.7.0 → 1.7.1. [davidh-ssec]
+- Merge pull request #88 from pytroll/bugfix-masked-target. [David
+ Hoese]
+
+ Fix kdtree when target lons/lats are masked arrays
+- Add test for masked valid_output_index fix. [davidh-ssec]
+- Move bilinear test setup to a special method. [davidh-ssec]
+- Fix kdtree when target lons/lats are masked arrays. [davidh-ssec]
+- Merge pull request #89 from Funkensieper/fix-masks-in-get-resampled-
+ image. [David Hoese]
+
+ Fix masks in grid.get_resampled_image
+- Add test for mask preservation. [Stephan Finkensieper]
+- Distinguish between ndarrays and masked arrays. [Stephan Finkensieper]
+- Fix masks in grid.get_resampled_image. [Stephan Finkensieper]
+
+ Use numpy.ma version of row_stack to prevent loosing the mask of
+ large images (rows > cut_off)
+
+- Add github templates. [Martin Raspaud]
+- Merge pull request #84 from pytroll/feature-add-hash. [Martin Raspaud]
+
+ Add hash method to AreaDefinition and SwathDefinition
+- Fix dask array not being hashable in py3.x. [Martin Raspaud]
+- Use identity checking instead of equality. [Martin Raspaud]
+- Do not has the mask if it's empty. [Martin Raspaud]
+- Bugfix geometry test. [Martin Raspaud]
+- Replace hash value checks with type checks. [Martin Raspaud]
+
+ The value can be different depending on the python version apparently.
+- Add dask and xarray for testing on travis. [Martin Raspaud]
+- Fix case of missing xarray dependency in the tests. [Martin Raspaud]
+- Add __hash__ for SwathDefinitions, along with some unittests. [Martin
+ Raspaud]
+- Add hash method to AreaDefinition. [davidh-ssec]
+
+ Removes annoying log message when xarray/dask is missing
+
+- Merge branch 'feature-xarray-improvements' into develop. [Martin
+ Raspaud]
+
+ Conflicts:
+ pyresample/geometry.py
+
+- Type coords to np.float. [Martin Raspaud]
+- Add support for fill_value in nn search. [Martin Raspaud]
+- Change the get_lonlats_dask interface to return a tuple. [Martin
+ Raspaud]
+- Fix masking bad latitude values. [davidh-ssec]
+- Fix consistency with numpy arrays. [davidh-ssec]
+- Allow xarrays internally in geometry objects. [davidh-ssec]
+- Merge remote-tracking branch 'origin/develop' into develop. [davidh-
+ ssec]
+
+ # Conflicts:
+ # .travis.yml
+
+- Fix proj4 dict to string against recent changes to str to dict funcs.
+ [davidh-ssec]
+- Change appveyor python 3.5 environments to python 3.6. [davidh-ssec]
+
+ Also removes slack notification webhook which is no longer the
+ recommended way to post to slack from appveyor.
+
+- Exclude buggy version of matplotlib in travis tests. [davidh-ssec]
+- Fix proj4 dict conversion test. [davidh-ssec]
+- Use more descriptive variable names. [davidh-ssec]
+- Add proj4_dict_to_str utility function. [davidh-ssec]
+
+ Includes fixes for dynamic area definitions proj_id and
+ small performance improvement for projection coordinate generation
+
+- Merge pull request #83 from loreclem/master. [Martin Raspaud]
+
+ Added ROTATION in an area definition
+- Bugfix in get_area_def. [lorenzo clementi]
+- Unit test for rotation. [lorenzo clementi]
+- Removed unused parameter. [lorenzo clementi]
+- Now working also with yaml. [lorenzo clementi]
+- Code improvements. [lorenzo clementi]
+- Added ROTATION in an area definition. [lorenzo clementi]
+
+
v1.7.0 (2017-10-13)
-------------------
- update changelog. [Martin Raspaud]
@@ -526,6 +612,7 @@ v1.2.2 (2016-06-21)
Without this, the compilation of the ewa extension crashes.
+
v1.2.1 (2016-06-21)
-------------------
- update changelog. [Martin Raspaud]
@@ -681,9 +768,11 @@ v1.2.0 (2016-06-17)
- Make kd_tree test work on older numpy version. [Martin Raspaud]
VisibleDeprecationWarning is not available in numpy <1.9.
+
- Adapt to newest pykdtree version. [Martin Raspaud]
The kdtree object's attribute `data_pts` has been renamed to `data`.
+
- Run tests on python 3.5 in travis also. [Martin Raspaud]
@@ -695,6 +784,7 @@ v1.1.6 (2016-02-25)
A previous commit was looking for a 'data_pts' attribute in the kdtree
object, which is available in pykdtree, but not scipy.
+
- Merge pull request #32 from mitkin/master. [Martin Raspaud]
[tests] Skip deprecation warnings in test_gauss_multi_uncert
@@ -706,6 +796,7 @@ v1.1.6 (2016-02-25)
The latest matplotlib (1.5) doesn't support python 2.6 and 3.3. This patch
chooses the right matplotlib version to install depending on the python
version at hand.
+
- Skip deprecation warnings. [Mikhail Itkin]
Catch the rest of the warnings. Check if there is only one, and
@@ -747,6 +838,7 @@ Other
- Bugfix to address a numpy DeprecationWarning. [Martin Raspaud]
Numpy won't take non-integer indices soon, so make index an int.
+
- Merge branch 'release-1.1.3' [Martin Raspaud]
- Merge branch 'licence-lgpl' into pre-master. [Martin Raspaud]
- Switch to lgplv3, and bump up version number. [Martin Raspaud]
@@ -968,7 +1060,7 @@ Other
- Set svn:mime-type. [StorPipfugl]
- Corrected doc errors. [StorPipfugl]
- Removed dist dir. [StorPipfugl]
-- No commit message. [StorPipfugl]
+- [StorPipfugl]
- Updated documentation. New release. [StorPipfugl]
- Started updating docstrings. [StorPipfugl]
- Restructured API. [StorPipfugl]
@@ -981,9 +1073,8 @@ Other
- Removed unneeded function. [StorPipfugl]
- Mime types set. [StorPipfugl]
- Mime types set. [StorPipfugl]
-- No commit message. [StorPipfugl]
+- [StorPipfugl]
- Moved to Google Code under GPLv3 license. [StorPipfugl]
- moved to Google Code. [StorPipfugl]
-
diff --git a/pyresample/geometry.py b/pyresample/geometry.py
index 988c310..9044f0f 100644
--- a/pyresample/geometry.py
+++ b/pyresample/geometry.py
@@ -25,6 +25,7 @@
import warnings
from collections import OrderedDict
from logging import getLogger
+import hashlib
import numpy as np
import yaml
@@ -32,6 +33,11 @@ from pyproj import Geod, Proj
from pyresample import _spatial_mp, utils
+try:
+ from xarray import DataArray
+except ImportError:
+ DataArray = np.ndarray
+
logger = getLogger(__name__)
@@ -63,31 +69,37 @@ class BaseDefinition(object):
if type(lons) != type(lats):
raise TypeError('lons and lats must be of same type')
elif lons is not None:
- lons = np.asanyarray(lons)
- lats = np.asanyarray(lats)
+ if not isinstance(lons, (np.ndarray, DataArray)):
+ lons = np.asanyarray(lons)
+ lats = np.asanyarray(lats)
if lons.shape != lats.shape:
raise ValueError('lons and lats must have same shape')
self.nprocs = nprocs
# check the latitutes
- if lats is not None and ((lats.min() < -90. or lats.max() > +90.)):
- # throw exception
- raise ValueError(
- 'Some latitudes are outside the [-90.;+90] validity range')
- else:
- self.lats = lats
+ if lats is not None:
+ if isinstance(lats, np.ndarray) and (lats.min() < -90. or lats.max() > 90.):
+ raise ValueError(
+ 'Some latitudes are outside the [-90.;+90] validity range')
+ elif not isinstance(lats, np.ndarray):
+ # assume we have to mask an xarray
+ lats = lats.where((lats >= -90.) & (lats <= 90.))
+ self.lats = lats
# check the longitudes
- if lons is not None and ((lons.min() < -180. or lons.max() >= +180.)):
- # issue warning
- warnings.warn('All geometry objects expect longitudes in the [-180:+180[ range. ' +
- 'We will now automatically wrap your longitudes into [-180:+180[, and continue. ' +
- 'To avoid this warning next time, use routine utils.wrap_longitudes().')
- # wrap longitudes to [-180;+180[
- self.lons = utils.wrap_longitudes(lons)
- else:
- self.lons = lons
+ if lons is not None:
+ if isinstance(lons, np.ndarray) and (lons.min() < -180. or lons.max() >= +180.):
+ # issue warning
+ warnings.warn('All geometry objects expect longitudes in the [-180:+180[ range. ' +
+ 'We will now automatically wrap your longitudes into [-180:+180[, and continue. ' +
+ 'To avoid this warning next time, use routine utils.wrap_longitudes().')
+ # assume we have to mask an xarray
+ # wrap longitudes to [-180;+180[
+ lons = utils.wrap_longitudes(lons)
+ elif not isinstance(lons, np.ndarray):
+ lons = utils.wrap_longitudes(lons)
+ self.lons = lons
self.ndim = None
self.cartesian_coords = None
@@ -357,8 +369,9 @@ class CoordinateDefinition(BaseDefinition):
"""Base class for geometry definitions defined by lons and lats only"""
def __init__(self, lons, lats, nprocs=1):
- lons = np.asanyarray(lons)
- lats = np.asanyarray(lats)
+ if not isinstance(lons, (np.ndarray, DataArray)):
+ lons = np.asanyarray(lons)
+ lats = np.asanyarray(lats)
super(CoordinateDefinition, self).__init__(lons, lats, nprocs)
if lons.shape == lats.shape and lons.dtype == lats.dtype:
self.shape = lons.shape
@@ -429,9 +442,23 @@ class GridDefinition(CoordinateDefinition):
raise ValueError('2 dimensional lon lat grid expected')
-class SwathDefinition(CoordinateDefinition):
+def get_array_hashable(arr):
+ """Compute a hashable form of the array `arr`.
+
+ Works with numpy arrays, dask.array.Array, and xarray.DataArray.
+ """
+ # look for precomputed value
+ if isinstance(arr, DataArray) and np.ndarray is not DataArray:
+ return arr.attrs.get('hash', get_array_hashable(arr.data))
+ else:
+ try:
+ return arr.name.encode('utf-8') # dask array
+ except AttributeError:
+ return arr.view(np.uint8) # np array
- """Swath defined by lons and lats
+
+class SwathDefinition(CoordinateDefinition):
+ """Swath defined by lons and lats.
Parameters
----------
@@ -454,24 +481,49 @@ class SwathDefinition(CoordinateDefinition):
Swath lats
cartesian_coords : object
Swath cartesian coordinates
+
"""
def __init__(self, lons, lats, nprocs=1):
- lons = np.asanyarray(lons)
- lats = np.asanyarray(lats)
+ if not isinstance(lons, (np.ndarray, DataArray)):
+ lons = np.asanyarray(lons)
+ lats = np.asanyarray(lats)
super(SwathDefinition, self).__init__(lons, lats, nprocs)
if lons.shape != lats.shape:
raise ValueError('lon and lat arrays must have same shape')
elif lons.ndim > 2:
raise ValueError('Only 1 and 2 dimensional swaths are allowed')
+ self.hash = None
+
+ def __hash__(self):
+ """Compute the hash of this object."""
+ if self.hash is None:
+ hasher = hashlib.sha1()
+ hasher.update(get_array_hashable(self.lons))
+ hasher.update(get_array_hashable(self.lats))
+ try:
+ if self.lons.mask is not np.bool_(False):
+ hasher.update(get_array_hashable(self.lons.mask))
+ except AttributeError:
+ pass
+ self.hash = int(hasher.hexdigest(), 16)
+
+ return self.hash
+
def get_lonlats_dask(self, blocksize=1000, dtype=None):
"""Get the lon lats as a single dask array."""
import dask.array as da
lons, lats = self.get_lonlats()
- lons = da.from_array(lons, chunks=blocksize * lons.ndim)
- lats = da.from_array(lats, chunks=blocksize * lons.ndim)
- return da.stack((lons, lats), axis=-1)
+
+ if isinstance(lons.data, da.Array):
+ return lons.data, lats.data
+ else:
+ lons = da.from_array(np.asanyarray(lons),
+ chunks=blocksize * lons.ndim)
+ lats = da.from_array(np.asanyarray(lats),
+ chunks=blocksize * lats.ndim)
+ return lons, lats
def _compute_omerc_parameters(self, ellipsoid):
"""Compute the oblique mercator projection bouding box parameters."""
@@ -538,13 +590,14 @@ class SwathDefinition(CoordinateDefinition):
class DynamicAreaDefinition(object):
"""An AreaDefintion containing just a subset of the needed parameters.
- The purpose of this class is to be able to adapt the area extent and size of
- the area to a given set of longitudes and latitudes, such that e.g. polar
- satellite granules can be resampled optimaly to a give projection."""
+ The purpose of this class is to be able to adapt the area extent and size
+ of the area to a given set of longitudes and latitudes, such that e.g.
+ polar satellite granules can be resampled optimaly to a give projection.
+ """
def __init__(self, area_id=None, description=None, proj_dict=None,
x_size=None, y_size=None, area_extent=None,
- optimize_projection=False):
+ optimize_projection=False, rotation=None):
"""Initialize the DynamicAreaDefinition.
area_id:
@@ -559,6 +612,8 @@ class DynamicAreaDefinition(object):
The area extent of the area.
optimize_projection:
Whether the projection parameters have to be optimized.
+ rotation:
+ Rotation in degrees (negative is cw)
"""
self.area_id = area_id
self.description = description
@@ -567,6 +622,7 @@ class DynamicAreaDefinition(object):
self.y_size = y_size
self.area_extent = area_extent
self.optimize_projection = optimize_projection
+ self.rotation = rotation
def compute_domain(self, corners, resolution=None, size=None):
"""Compute size and area_extent from corners and [size or resolution]
@@ -597,7 +653,7 @@ class DynamicAreaDefinition(object):
def freeze(self, lonslats=None,
resolution=None, size=None,
- proj_info=None):
+ proj_info=None, rotation=None):
"""Create an AreaDefintion from this area with help of some extra info.
lonlats:
@@ -608,6 +664,8 @@ class DynamicAreaDefinition(object):
the size of the resulting area.
proj_info:
complementing parameters to the projection info.
+ rotation:
+ rotation in degrees (negative is cw)
Resolution and Size parameters are ignored if the instance is created
with the `optimize_projection` flag set to True.
@@ -632,7 +690,7 @@ class DynamicAreaDefinition(object):
return AreaDefinition(self.area_id, self.description, '',
self.proj_dict, self.x_size, self.y_size,
- self.area_extent)
+ self.area_extent, self.rotation)
class AreaDefinition(BaseDefinition):
@@ -653,6 +711,8 @@ class AreaDefinition(BaseDefinition):
x dimension in number of pixels
y_size : int
y dimension in number of pixels
+ rotation: float
+ rotation in degrees (negative is cw)
area_extent : list
Area extent as a list (LL_x, LL_y, UR_x, UR_y)
nprocs : int, optional
@@ -676,6 +736,8 @@ class AreaDefinition(BaseDefinition):
x dimension in number of pixels
y_size : int
y dimension in number of pixels
+ rotation: float
+ rotation in degrees (negative is cw)
shape : tuple
Corresponding array shape as (rows, cols)
size : int
@@ -711,7 +773,8 @@ class AreaDefinition(BaseDefinition):
"""
def __init__(self, area_id, name, proj_id, proj_dict, x_size, y_size,
- area_extent, nprocs=1, lons=None, lats=None, dtype=np.float64):
+ area_extent, rotation=None, nprocs=1, lons=None, lats=None,
+ dtype=np.float64):
if not isinstance(proj_dict, dict):
raise TypeError('Wrong type for proj_dict: %s. Expected dict.'
% type(proj_dict))
@@ -723,6 +786,10 @@ class AreaDefinition(BaseDefinition):
self.x_size = int(x_size)
self.y_size = int(y_size)
self.shape = (y_size, x_size)
+ try:
+ self.rotation = float(rotation)
+ except TypeError:
+ self.rotation = 0
if lons is not None:
if lons.shape != self.shape:
raise ValueError('Shape of lon lat grid must match '
@@ -749,8 +816,8 @@ class AreaDefinition(BaseDefinition):
float(area_extent[3]) -
float(self.pixel_size_y) / 2)
- # Pixel_offset defines the distance to projection center from origen (UL)
- # of image in units of pixels.
+ # Pixel_offset defines the distance to projection center from origen
+ # (UL) of image in units of pixels.
self.pixel_offset_x = -self.area_extent[0] / self.pixel_size_x
self.pixel_offset_y = self.area_extent[3] / self.pixel_size_y
@@ -810,6 +877,7 @@ class AreaDefinition(BaseDefinition):
fmt += "\tPCS_DEF:\t{proj_str}\n"
fmt += "\tXSIZE:\t{x_size}\n"
fmt += "\tYSIZE:\t{y_size}\n"
+ fmt += "\tROTATION:\t{rotation}\n"
fmt += "\tAREA_EXTENT: {area_extent}\n}};\n"
area_def_str = fmt.format(name=self.name, area_id=self.area_id,
proj_str=proj_str, x_size=self.x_size,
@@ -823,7 +891,7 @@ class AreaDefinition(BaseDefinition):
"""Test for equality"""
try:
- return ((self.proj_dict == other.proj_dict) and
+ return ((self.proj_str == other.proj_str) and
(self.shape == other.shape) and
(np.allclose(self.area_extent, other.area_extent)))
except AttributeError:
@@ -834,6 +902,13 @@ class AreaDefinition(BaseDefinition):
return not self.__eq__(other)
+ def __hash__(self):
+ return hash((
+ self.proj_str,
+ self.shape,
+ self.area_extent
+ ))
+
def colrow2lonlat(self, cols, rows):
"""
Return longitudes and latitudes for the given image columns
@@ -1007,6 +1082,13 @@ class AreaDefinition(BaseDefinition):
Grids of area x- and y-coordinates in projection units
"""
+ def do_rotation(xspan, yspan, rot_deg=0):
+ rot_rad = np.radians(rot_deg)
+ rot_mat = np.array([[np.cos(rot_rad), np.sin(rot_rad)],
+ [-np.sin(rot_rad), np.cos(rot_rad)]])
+ x, y = np.meshgrid(xspan, yspan)
+ return np.einsum('ji, mni -> jmn', rot_mat, np.dstack([x, y]))
+
def get_val(val, sub_val, max):
# Get value with substitution and wrapping
if val is None:
@@ -1081,9 +1163,15 @@ class AreaDefinition(BaseDefinition):
cols = 1
# Calculate coordinates
- target_x = np.arange(col_start, col_start + cols, dtype=dtype) * self.pixel_size_x + self.pixel_upper_left[0]
- target_y = np.arange(row_start, row_start + rows, dtype=dtype) * -self.pixel_size_y + self.pixel_upper_left[1]
- target_x, target_y = np.meshgrid(target_x, target_y)
+ target_x = np.arange(col_start, col_start + cols, dtype=dtype) * \
+ self.pixel_size_x + self.pixel_upper_left[0]
+ target_y = np.arange(row_start, row_start + rows, dtype=dtype) * - \
+ self.pixel_size_y + self.pixel_upper_left[1]
+ if self.rotation != 0:
+ res = do_rotation(target_x, target_y, self.rotation)
+ target_x, target_y = res[0, :, :], res[1, :, :]
+ else:
+ target_x, target_y = np.meshgrid(target_x, target_y)
if is_single_value:
# Return single values
@@ -1169,7 +1257,7 @@ class AreaDefinition(BaseDefinition):
res = Array(dsk, name, shape=list(self.shape) + [2],
chunks=(blocksize, blocksize, 2),
dtype=dtype)
- return res
+ return res[:, :, 0], res[:, :, 1]
def get_lonlats(self, nprocs=None, data_slice=None, cache=False, dtype=None):
"""Returns lon and lat arrays of area.
@@ -1363,16 +1451,19 @@ class StackedAreaDefinition(BaseDefinition):
def get_lonlats_dask(self, blocksize=1000, dtype=None):
""""Return lon and lat dask arrays of the area."""
import dask.array as da
- llonslats = []
+ llons = []
+ llats = []
for definition in self.defs:
- lonslats = definition.get_lonlats_dask(blocksize=blocksize,
- dtype=dtype)
+ lons, lats = definition.get_lonlats_dask(blocksize=blocksize,
+ dtype=dtype)
- llonslats.append(lonslats)
+ llons.append(lons)
+ llats.append(lats)
- self.lonlats = da.concatenate(llonslats, axis=0)
+ self.lons = da.concatenate(llons, axis=0)
+ self.lats = da.concatenate(llats, axis=0)
- return self.lonlats
+ return self.lons, self.lats
def squeeze(self):
"""Generate a single AreaDefinition if possible."""
diff --git a/pyresample/grid.py b/pyresample/grid.py
index a484e59..05afb1e 100644
--- a/pyresample/grid.py
+++ b/pyresample/grid.py
@@ -236,7 +236,11 @@ def get_resampled_image(target_area_def, source_area_def, source_image_data,
# First iteration
result = next_result
else:
- result = np.row_stack((result, next_result))
+ if isinstance(next_result, np.ma.core.MaskedArray):
+ stack = np.ma.row_stack
+ else:
+ stack = np.row_stack
+ result = stack((result, next_result))
return result
else:
diff --git a/pyresample/image.py b/pyresample/image.py
index 5254ac1..629b2d7 100644
--- a/pyresample/image.py
+++ b/pyresample/image.py
@@ -55,6 +55,8 @@ class ImageContainer(object):
"""
def __init__(self, image_data, geo_def, fill_value=0, nprocs=1):
+ if type(geo_def).__name__ == "DynamicAreaDefinition":
+ geo_def = geo_def.freeze()
if not isinstance(image_data, (np.ndarray, np.ma.core.MaskedArray)):
raise TypeError('image_data must be either an ndarray'
' or a masked array')
diff --git a/pyresample/kd_tree.py b/pyresample/kd_tree.py
index a838461..0f9f54c 100644
--- a/pyresample/kd_tree.py
+++ b/pyresample/kd_tree.py
@@ -36,7 +36,8 @@ try:
from xarray import DataArray
import dask.array as da
except ImportError:
- logger.info("XArray or dask unavailable, some functionality missing.")
+ DataArray = None
+ da = None
if sys.version < '3':
range = xrange
@@ -466,6 +467,8 @@ def _get_valid_output_index(source_geo_def, target_geo_def, target_lons,
# Combine reduced and legal values
valid_output_index = (valid_output_index & valid_out)
+ if isinstance(valid_output_index, np.ma.MaskedArray):
+ valid_output_index = valid_output_index.filled(False)
return valid_output_index
@@ -914,6 +917,8 @@ class XArrayResamplerNN(object):
Number of segments to use when resampling.
If set to None an estimate will be calculated
"""
+ if DataArray is None:
+ raise ImportError("Missing 'xarray' and 'dask' dependencies")
self.valid_input_index = None
self.valid_output_index = None
@@ -960,7 +965,7 @@ class XArrayResamplerNN(object):
raise EmptyResult('No valid data points in input data')
# Build kd-tree on input
-
+ input_coords = input_coords.astype(np.float)
if kd_tree_name == 'pykdtree':
resample_kdtree = KDTree(input_coords.compute())
else:
@@ -1036,9 +1041,7 @@ class XArrayResamplerNN(object):
warnings.warn('Searching for %s neighbours in %s data points' %
(self.neighbours, self.source_geo_def.size))
- source_lonlats = self.source_geo_def.get_lonlats_dask()
- source_lons = source_lonlats[:, :, 0]
- source_lats = source_lonlats[:, :, 1]
+ source_lons, source_lats = self.source_geo_def.get_lonlats_dask()
valid_input_index = ((source_lons >= -180) & (source_lons <= 180) &
(source_lats <= 90) & (source_lats >= -90))
@@ -1053,12 +1056,14 @@ class XArrayResamplerNN(object):
valid_output_index, index_array, distance_array = \
_create_empty_info(self.source_geo_def,
self.target_geo_def, self.neighbours)
+ self.valid_input_index = valid_input_index
+ self.valid_output_index = valid_output_index
+ self.index_array = index_array
+ self.distance_array = distance_array
return (valid_input_index, valid_output_index, index_array,
distance_array)
- target_lonlats = self.target_geo_def.get_lonlats_dask()
- target_lons = target_lonlats[:, :, 0]
- target_lats = target_lonlats[:, :, 1]
+ target_lons, target_lats = self.target_geo_def.get_lonlats_dask()
valid_output_index = ((target_lons >= -180) & (target_lons <= 180) &
(target_lats <= 90) & (target_lats >= -90))
@@ -1074,7 +1079,7 @@ class XArrayResamplerNN(object):
return valid_input_index, valid_output_index, index_array, distance_array
- def get_sample_from_neighbour_info(self, data):
+ def get_sample_from_neighbour_info(self, data, fill_value=np.nan):
# flatten x and y in the source array
@@ -1124,9 +1129,9 @@ class XArrayResamplerNN(object):
new_data = source_data[:, line][self.valid_input_index.ravel()]
# could this be a bug in dask ? we have to compute to avoid errors
result = new_data.compute()[new_index_array]
- result[index_mask.ravel()] = np.nan
+ result[index_mask.ravel()] = fill_value
#target_data_line = da.full(target_shape[0], np.nan, chunks=1000000)
- target_data_line = np.full(target_shape[0], np.nan)
+ target_data_line = np.full(target_shape[0], fill_value)
target_data_line[valid_targets] = result
target_lines.append(target_data_line[:, np.newaxis])
diff --git a/pyresample/test/test_bilinear.py b/pyresample/test/test_bilinear.py
index 7a0eb1c..f5a9051 100644
--- a/pyresample/test/test_bilinear.py
+++ b/pyresample/test/test_bilinear.py
@@ -9,52 +9,59 @@ from pyresample import geometry, utils, kd_tree
class Test(unittest.TestCase):
- pts_irregular = (np.array([[-1., 1.], ]),
- np.array([[1., 2.], ]),
- np.array([[-2., -1.], ]),
- np.array([[2., -4.], ]))
- pts_vert_parallel = (np.array([[-1., 1.], ]),
- np.array([[1., 2.], ]),
- np.array([[-1., -1.], ]),
- np.array([[1., -2.], ]))
- pts_both_parallel = (np.array([[-1., 1.], ]),
- np.array([[1., 1.], ]),
- np.array([[-1., -1.], ]),
- np.array([[1., -1.], ]))
-
- # Area definition with four pixels
- target_def = geometry.AreaDefinition('areaD',
- 'Europe (3km, HRV, VTC)',
- 'areaD',
- {'a': '6378144.0',
- 'b': '6356759.0',
- 'lat_0': '50.00',
- 'lat_ts': '50.00',
- 'lon_0': '8.00',
- 'proj': 'stere'},
- 4, 4,
- [-1370912.72,
- -909968.64000000001,
- 1029087.28,
- 1490031.3600000001])
-
- # Input data around the target pixel at 0.63388324, 55.08234642,
- in_shape = (100, 100)
- data1 = np.ones((in_shape[0], in_shape[1]))
- data2 = 2. * data1
- lons, lats = np.meshgrid(np.linspace(-5., 5., num=in_shape[0]),
- np.linspace(50., 60., num=in_shape[1]))
- swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
-
- radius = 50e3
- neighbours = 32
- input_idxs, output_idxs, idx_ref, dists = \
- kd_tree.get_neighbour_info(swath_def, target_def,
- radius, neighbours=neighbours,
- nprocs=1)
- input_size = input_idxs.sum()
- index_mask = (idx_ref == input_size)
- idx_ref = np.where(index_mask, 0, idx_ref)
+ @classmethod
+ def setUpClass(cls):
+ cls.pts_irregular = (np.array([[-1., 1.], ]),
+ np.array([[1., 2.], ]),
+ np.array([[-2., -1.], ]),
+ np.array([[2., -4.], ]))
+ cls.pts_vert_parallel = (np.array([[-1., 1.], ]),
+ np.array([[1., 2.], ]),
+ np.array([[-1., -1.], ]),
+ np.array([[1., -2.], ]))
+ cls.pts_both_parallel = (np.array([[-1., 1.], ]),
+ np.array([[1., 1.], ]),
+ np.array([[-1., -1.], ]),
+ np.array([[1., -1.], ]))
+
+ # Area definition with four pixels
+ target_def = geometry.AreaDefinition('areaD',
+ 'Europe (3km, HRV, VTC)',
+ 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_0': '50.00',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'proj': 'stere'},
+ 4, 4,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ # Input data around the target pixel at 0.63388324, 55.08234642,
+ in_shape = (100, 100)
+ cls.data1 = np.ones((in_shape[0], in_shape[1]))
+ cls.data2 = 2. * cls.data1
+ lons, lats = np.meshgrid(np.linspace(-5., 5., num=in_shape[0]),
+ np.linspace(50., 60., num=in_shape[1]))
+ cls.swath_def = geometry.SwathDefinition(lons=lons, lats=lats)
+
+ radius = 50e3
+ cls.neighbours = 32
+ input_idxs, output_idxs, idx_ref, dists = \
+ kd_tree.get_neighbour_info(cls.swath_def, target_def,
+ radius, neighbours=cls.neighbours,
+ nprocs=1)
+ input_size = input_idxs.sum()
+ index_mask = (idx_ref == input_size)
+ idx_ref = np.where(index_mask, 0, idx_ref)
+
+ cls.input_idxs = input_idxs
+ cls.target_def = target_def
+ cls.idx_ref = idx_ref
+
def test_calc_abc(self):
# No np.nan inputs
diff --git a/pyresample/test/test_geometry.py b/pyresample/test/test_geometry.py
index 73a3050..4aec478 100644
--- a/pyresample/test/test_geometry.py
+++ b/pyresample/test/test_geometry.py
@@ -165,6 +165,134 @@ class Test(unittest.TestCase):
"BaseDefinition did not maintain dtype of longitudes (in:%s out:%s)" %
(lons2_ints.dtype, lons.dtype,))
+ def test_area_hash(self):
+ area_def = geometry.AreaDefinition('areaD', 'Europe (3km, HRV, VTC)', 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_0': '50.00',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'proj': 'stere'},
+ 800,
+ 800,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ self.assertIsInstance(hash(area_def), int)
+
+ area_def = geometry.AreaDefinition('areaD', 'Europe (3km, HRV, VTC)', 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'lat_0': '50.00',
+ 'proj': 'stere'},
+ 800,
+ 800,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ self.assertIsInstance(hash(area_def), int)
+
+ area_def = geometry.AreaDefinition('New area', 'Europe', 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'lat_0': '50.00',
+ 'proj': 'stere'},
+ 800,
+ 800,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ self.assertIsInstance(hash(area_def), int)
+
+ def test_get_array_hashable(self):
+ arr = np.array([1.2, 1.3, 1.4, 1.5])
+
+ np.testing.assert_allclose(np.array([ 51, 51, 51, 51, 51, 51, 243,
+ 63, 205, 204, 204, 204, 204,
+ 204, 244, 63, 102, 102, 102, 102,
+ 102, 102, 246, 63, 0, 0,
+ 0, 0, 0, 0, 248, 63],
+ dtype=np.uint8),
+ geometry.get_array_hashable(arr))
+
+ try:
+ import xarray as xr
+ except ImportError:
+ pass
+ else:
+ xrarr = xr.DataArray(arr)
+ np.testing.assert_allclose(np.array([ 51, 51, 51, 51, 51, 51, 243,
+ 63, 205, 204, 204, 204, 204,
+ 204, 244, 63, 102, 102, 102, 102,
+ 102, 102, 246, 63, 0, 0,
+ 0, 0, 0, 0, 248, 63],
+ dtype=np.uint8),
+ geometry.get_array_hashable(arr))
+
+ xrarr.attrs['hash'] = 42
+ self.assertEqual(geometry.get_array_hashable(xrarr),
+ xrarr.attrs['hash'])
+
+
+ def test_swath_hash(self):
+ lons = np.array([1.2, 1.3, 1.4, 1.5])
+ lats = np.array([65.9, 65.86, 65.82, 65.78])
+ swath_def = geometry.SwathDefinition(lons, lats)
+
+ self.assertIsInstance(hash(swath_def), int)
+
+ try:
+ import dask.array as da
+ except ImportError:
+ print("Not testing with dask arrays")
+ else:
+ dalons = da.from_array(lons, chunks=1000)
+ dalats = da.from_array(lats, chunks=1000)
+ swath_def = geometry.SwathDefinition(dalons, dalats)
+
+ self.assertIsInstance(hash(swath_def), int)
+
+ try:
+ import xarray as xr
+ except ImportError:
+ print("Not testing with xarray")
+ else:
+ xrlons = xr.DataArray(lons)
+ xrlats = xr.DataArray(lats)
+ swath_def = geometry.SwathDefinition(xrlons, xrlats)
+
+ self.assertIsInstance(hash(swath_def), int)
+
+ try:
+ import xarray as xr
+ import dask.array as da
+ except ImportError:
+ print("Not testing with xarrays and dask arrays")
+ else:
+ xrlons = xr.DataArray(da.from_array(lons, chunks=1000))
+ xrlats = xr.DataArray(da.from_array(lats, chunks=1000))
+ swath_def = geometry.SwathDefinition(xrlons, xrlats)
+
+ self.assertIsInstance(hash(swath_def), int)
+
+
+ lons = np.ma.array([1.2, 1.3, 1.4, 1.5])
+ lats = np.ma.array([65.9, 65.86, 65.82, 65.78])
+ swath_def = geometry.SwathDefinition(lons, lats)
+
+ self.assertIsInstance(hash(swath_def), int)
+
+
def test_area_equal(self):
area_def = geometry.AreaDefinition('areaD', 'Europe (3km, HRV, VTC)', 'areaD',
{'a': '6378144.0',
diff --git a/pyresample/test/test_grid.py b/pyresample/test/test_grid.py
index 9a7f53d..9abd5f4 100644
--- a/pyresample/test/test_grid.py
+++ b/pyresample/test/test_grid.py
@@ -152,6 +152,22 @@ class Test(unittest.TestCase):
self.assertAlmostEqual(
cross_sum, expected, msg='Resampling of image failed')
+ def test_resampled_image_masked(self):
+ # Generate test image with masked elements
+ data = np.ma.ones(self.msg_area.shape)
+ data.mask = np.zeros(data.shape)
+ data.mask[253:400, 1970:2211] = 1
+
+ # Resample image using multiple segments
+ target_def = self.area_def
+ source_def = self.msg_area
+ res = grid.get_resampled_image(
+ target_def, source_def, data, segments=4, fill_value=None)
+
+ # Make sure the mask has been preserved
+ self.assertGreater(res.mask.sum(), 0,
+ msg='Resampling did not preserve the mask')
+
@tmp
def test_generate_linesample(self):
data = np.fromfunction(lambda y, x: y * x * 10 ** -6, (3712, 3712))
diff --git a/pyresample/test/test_kd_tree.py b/pyresample/test/test_kd_tree.py
index 1ec401a..d1cadd2 100644
--- a/pyresample/test/test_kd_tree.py
+++ b/pyresample/test/test_kd_tree.py
@@ -16,26 +16,30 @@ else:
class Test(unittest.TestCase):
- area_def = geometry.AreaDefinition('areaD', 'Europe (3km, HRV, VTC)', 'areaD',
- {'a': '6378144.0',
- 'b': '6356759.0',
- 'lat_0': '50.00',
- 'lat_ts': '50.00',
- 'lon_0': '8.00',
- 'proj': 'stere'},
- 800,
- 800,
- [-1370912.72,
- -909968.64000000001,
- 1029087.28,
- 1490031.3600000001])
-
- tdata = numpy.array([1, 2, 3])
- tlons = numpy.array([11.280789, 12.649354, 12.080402])
- tlats = numpy.array([56.011037, 55.629675, 55.641535])
- tswath = geometry.SwathDefinition(lons=tlons, lats=tlats)
- tgrid = geometry.CoordinateDefinition(lons=numpy.array([12.562036]),
- lats=numpy.array([55.715613]))
+ @classmethod
+ def setUpClass(cls):
+ cls.area_def = geometry.AreaDefinition('areaD',
+ 'Europe (3km, HRV, VTC)',
+ 'areaD',
+ {'a': '6378144.0',
+ 'b': '6356759.0',
+ 'lat_0': '50.00',
+ 'lat_ts': '50.00',
+ 'lon_0': '8.00',
+ 'proj': 'stere'},
+ 800,
+ 800,
+ [-1370912.72,
+ -909968.64000000001,
+ 1029087.28,
+ 1490031.3600000001])
+
+ cls.tdata = numpy.array([1, 2, 3])
+ cls.tlons = numpy.array([11.280789, 12.649354, 12.080402])
+ cls.tlats = numpy.array([56.011037, 55.629675, 55.641535])
+ cls.tswath = geometry.SwathDefinition(lons=cls.tlons, lats=cls.tlats)
+ cls.tgrid = geometry.CoordinateDefinition(
+ lons=numpy.array([12.562036]), lats=numpy.array([55.715613]))
def test_nearest_base(self):
res = kd_tree.resample_nearest(self.tswath,
@@ -123,6 +127,25 @@ class Test(unittest.TestCase):
self.assertEqual(cross_sum, expected,
msg='Swath resampling nearest failed')
+ def test_nearest_masked_swath_target(self):
+ """Test that a masked array works as a target."""
+ data = numpy.fromfunction(lambda y, x: y * x, (50, 10))
+ lons = numpy.fromfunction(lambda y, x: 3 + x, (50, 10))
+ lats = numpy.fromfunction(lambda y, x: 75 - y, (50, 10))
+ mask = numpy.ones_like(lons, dtype=numpy.bool)
+ mask[::2, ::2] = False
+ swath_def = geometry.SwathDefinition(
+ lons=numpy.ma.masked_array(lons, mask=mask), # numpy.ones_like(lons, dtype=numpy.bool)),
+ lats=numpy.ma.masked_array(lats, mask=False)
+ )
+ res = kd_tree.resample_nearest(swath_def, data.ravel(),
+ swath_def, 50000, segments=3)
+ cross_sum = res.sum()
+ # expected = 12716 # if masks aren't respected
+ expected = 12000
+ self.assertEqual(cross_sum, expected,
+ msg='Swath resampling masked nearest failed')
+
def test_nearest_1d(self):
data = numpy.fromfunction(lambda x, y: x * y, (800, 800))
lons = numpy.fromfunction(lambda x: 3 + x / 100., (500,))
diff --git a/pyresample/test/test_rotation.py b/pyresample/test/test_rotation.py
new file mode 100644
index 0000000..0a8c4db
--- /dev/null
+++ b/pyresample/test/test_rotation.py
@@ -0,0 +1,97 @@
+import unittest, os
+from pyresample.utils import load_area
+
+class TestProjRotation(unittest.TestCase):
+
+ def test_rotation_legacy (self):
+ legacyDef = """REGION: regionB {
+ NAME: regionB
+ PCS_ID: regionB
+ PCS_DEF: proj=merc, lon_0=-34, k=1, x_0=0, y_0=0, a=6378137, b=6378137
+ XSIZE: 800
+ YSIZE: 548
+ ROTATION: -45
+ AREA_EXTENT: (-7761424.714818418, -4861746.639279127, 11136477.43264252, 8236799.845095873)
+ };"""
+ flegacy = "/tmp/TestProjRotation_test_rotation_legacy.txt"
+ f = open(flegacy,"w")
+ f.write(legacyDef)
+ f.close()
+ test_area = load_area(flegacy, 'regionB')
+ self.assertEqual(test_area.rotation, -45)
+ os.remove(flegacy)
+
+ def test_rotation_yaml (self):
+ yamlDef = """regionB:
+ description: regionB
+ projection:
+ a: 6378137.0
+ b: 6378137.0
+ lon_0: -34
+ proj: merc
+ x_0: 0
+ y_0: 0
+ k_0: 1
+ shape:
+ height: 548
+ width: 800
+ rotation: -45
+ area_extent:
+ lower_left_xy: [-7761424.714818418, -4861746.639279127]
+ upper_right_xy: [11136477.43264252, 8236799.845095873]
+ units: m"""
+ fyaml = "/tmp/TestProjRotation_test_rotation_yaml.txt"
+ f = open(fyaml,"w")
+ f.write(yamlDef)
+ f.close()
+ test_area = load_area(fyaml, 'regionB')
+ self.assertEqual(test_area.rotation, -45)
+ os.remove(fyaml)
+
+ def test_norotation_legacy (self):
+ legacyDef = """REGION: regionB {
+ NAME: regionB
+ PCS_ID: regionB
+ PCS_DEF: proj=merc, lon_0=-34, k=1, x_0=0, y_0=0, a=6378137, b=6378137
+ XSIZE: 800
+ YSIZE: 548
+ AREA_EXTENT: (-7761424.714818418, -4861746.639279127, 11136477.43264252, 8236799.845095873)
+ };"""
+ flegacy = "/tmp/TestProjRotation_test_rotation_legacy.txt"
+ f = open(flegacy,"w")
+ f.write(legacyDef)
+ f.close()
+ test_area = load_area(flegacy, 'regionB')
+ self.assertEqual(test_area.rotation, 0)
+ os.remove(flegacy)
+
+ def test_norotation_yaml (self):
+ yamlDef = """regionB:
+ description: regionB
+ projection:
+ a: 6378137.0
+ b: 6378137.0
+ lon_0: -34
+ proj: merc
+ x_0: 0
+ y_0: 0
+ k_0: 1
+ shape:
+ height: 548
+ width: 800
+ area_extent:
+ lower_left_xy: [-7761424.714818418, -4861746.639279127]
+ upper_right_xy: [11136477.43264252, 8236799.845095873]
+ units: m"""
+ fyaml = "/tmp/TestProjRotation_test_rotation_yaml.txt"
+ f = open(fyaml,"w")
+ f.write(yamlDef)
+ f.close()
+ test_area = load_area(fyaml, 'regionB')
+ self.assertEqual(test_area.rotation, 0)
+ os.remove(fyaml)
+
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pyresample/utils.py b/pyresample/utils.py
index 7425481..c97ae5a 100644
--- a/pyresample/utils.py
+++ b/pyresample/utils.py
@@ -150,10 +150,15 @@ def _parse_yaml_area_file(area_file_name, *regions):
params['area_extent']['upper_right_xy'])
except KeyError:
area_extent = None
+ try:
+ rotation = params['rotation']
+ except KeyError:
+ rotation = 0
area = pr.geometry.DynamicAreaDefinition(area_name, description,
projection, xsize, ysize,
area_extent,
- optimize_projection)
+ optimize_projection,
+ rotation)
try:
area = area.freeze()
except (TypeError, AttributeError):
@@ -241,6 +246,10 @@ def _create_area(area_id, area_content):
config['XSIZE'] = int(config['XSIZE'])
config['YSIZE'] = int(config['YSIZE'])
+ if 'ROTATION' in config.keys():
+ config['ROTATION'] = float(config['ROTATION'])
+ else:
+ config['ROTATION'] = 0
config['AREA_EXTENT'][0] = config['AREA_EXTENT'][0].replace('(', '')
config['AREA_EXTENT'][3] = config['AREA_EXTENT'][3].replace(')', '')
@@ -248,15 +257,14 @@ def _create_area(area_id, area_content):
config['AREA_EXTENT'][i] = float(val)
config['PCS_DEF'] = _get_proj4_args(config['PCS_DEF'])
-
return pr.geometry.AreaDefinition(config['REGION'], config['NAME'],
config['PCS_ID'], config['PCS_DEF'],
config['XSIZE'], config['YSIZE'],
- config['AREA_EXTENT'])
+ config['AREA_EXTENT'], config['ROTATION'])
def get_area_def(area_id, area_name, proj_id, proj4_args, x_size, y_size,
- area_extent):
+ area_extent, rotation=0):
"""Construct AreaDefinition object from arguments
Parameters
@@ -273,6 +281,8 @@ def get_area_def(area_id, area_name, proj_id, proj4_args, x_size, y_size,
Number of pixel in x dimension
y_size : int
Number of pixel in y dimension
+ rotation: float
+ Rotation in degrees (negative is cw)
area_extent : list
Area extent as a list of ints (LL_x, LL_y, UR_x, UR_y)
@@ -500,8 +510,7 @@ def wrap_longitudes(lons):
Longitudes wrapped into [-180:+180[ validity range
"""
- lons_wrap = (lons + 180) % (360) - 180
- return lons_wrap.astype(lons.dtype)
+ return (lons + 180) % 360 - 180
def recursive_dict_update(d, u):
diff --git a/pyresample/version.py b/pyresample/version.py
index 3e0309b..38c6ea6 100644
--- a/pyresample/version.py
+++ b/pyresample/version.py
@@ -15,4 +15,4 @@
# You should have received a copy of the GNU Lesser General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
-__version__ = '1.7.0'
+__version__ = '1.7.1'
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/pyresample.git
More information about the Pkg-grass-devel
mailing list