Commit 56de57fb authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

geo.coord_grid:

- is_coord_grid_equal(): added keyword 'tolerance'; added type hint and docstring
- is_point_on_grid(): added keyword 'tolerance'; added type hint and updated docstring

io.raster.GeoArray:
- GeoArray:
    - arr.setter: removed shape assertion -> not useful in combination with image warping
- BadDataMask and NoDataMask:
    -  __init__(): added pixel value validation
    - arr.setter: moved pixel value validation to _validate_array_values()
    - added _validate_array_values()

- updated __version__
parent 650c5b1d
...@@ -15,7 +15,7 @@ __all__=[#'compatibility', ...@@ -15,7 +15,7 @@ __all__=[#'compatibility',
'similarity', 'similarity',
'GeoArray'] 'GeoArray']
__version__ = '20170119_01' __version__ = '20170119_02'
__author__='Daniel Scheffler' __author__='Daniel Scheffler'
# Validate GDAL version # Validate GDAL version
......
...@@ -28,23 +28,35 @@ def snap_bounds_to_pixGrid(bounds, gt, roundAlg='auto'): ...@@ -28,23 +28,35 @@ def snap_bounds_to_pixGrid(bounds, gt, roundAlg='auto'):
return xmin, ymin, xmax, ymax return xmin, ymin, xmax, ymax
def is_coord_grid_equal(gt,xgrid,ygrid): def is_coord_grid_equal(gt,xgrid,ygrid, tolerance=0.):
"""Checks if a given GeoTransform exactly matches the given X/Y grid.
:param gt: GDAL GeoTransform
:param xgrid: numpy array defining the coordinate grid in x-direction
:param ygrid: numpy array defining the coordinate grid in y-direction
:param tolerance: float value defining a tolerance, e.g. 1e-8
:return:
"""
ULx,xgsd,holder,ULy,holder,ygsd = gt ULx,xgsd,holder,ULy,holder,ygsd = gt
if is_point_on_grid((ULx,ULy),xgrid,ygrid) and xgsd==abs(xgrid[1]-xgrid[0]) and abs(ygsd)==abs(ygrid[1]-ygrid[0]): if all([is_point_on_grid((ULx,ULy), xgrid, ygrid, tolerance),
abs(xgsd - abs(xgrid[1]-xgrid[0])) <= tolerance,
abs(abs(ygsd) - abs(ygrid[1]-ygrid[0])) <= tolerance]):
return True return True
else: else:
return False return False
def is_point_on_grid(pointXY,xgrid,ygrid): def is_point_on_grid(pointXY, xgrid, ygrid, tolerance=0.):
# type: (tuple,np.array,np.array) -> bool # type: (tuple, np.array, np.array, float) -> bool
"""Checks if a given point is exactly on the given coordinate grid. """Checks if a given point is exactly on the given coordinate grid.
:param pointXY: (X,Y) coordinates of the point to check :param pointXY: (X,Y) coordinates of the point to check
:param xgrid: numpy array defining the coordinate grid in x-direction :param xgrid: numpy array defining the coordinate grid in x-direction
:param ygrid: numpy array defining the coordinate grid in y-direction :param ygrid: numpy array defining the coordinate grid in y-direction
:param tolerance: float value defining a tolerance, e.g. 1e-8
""" """
if find_nearest(xgrid,pointXY[0],extrapolate=True)==pointXY[0] and \ if abs(find_nearest(xgrid,pointXY[0],extrapolate=True) - pointXY[0]) <= tolerance and \
find_nearest(ygrid,pointXY[1],extrapolate=True)==pointXY[1]: abs(find_nearest(ygrid,pointXY[1],extrapolate=True) - pointXY[1]) <= tolerance:
return True return True
else: else:
return False return False
......
...@@ -18,7 +18,7 @@ from rasterio.warp import reproject as rio_reproject ...@@ -18,7 +18,7 @@ from rasterio.warp import reproject as rio_reproject
from rasterio.warp import calculate_default_transform as rio_calc_transform from rasterio.warp import calculate_default_transform as rio_calc_transform
from rasterio.warp import Resampling from rasterio.warp import Resampling
from ...dtypes.conversion import dTypeDic_NumPy2GDAL, dTypeDic_GDAL2Numpy from ...dtypes.conversion import dTypeDic_NumPy2GDAL
from ..projection import WKT2EPSG, isProjectedOrGeographic, prj_equal from ..projection import WKT2EPSG, isProjectedOrGeographic, prj_equal
from ..coord_trafo import pixelToLatLon from ..coord_trafo import pixelToLatLon
from ...io.raster.gdal import get_GDAL_ds_inmem from ...io.raster.gdal import get_GDAL_ds_inmem
...@@ -212,7 +212,7 @@ def warp_ndarray_OLD(ndarray, in_gt, in_prj, out_prj, out_gt=None, outRowsCols=N ...@@ -212,7 +212,7 @@ def warp_ndarray_OLD(ndarray, in_gt, in_prj, out_prj, out_gt=None, outRowsCols=N
def warp_GeoArray(geoArr, **kwargs): def warp_GeoArray(geoArr, **kwargs):
ndarray = geoArr[:] ndarray = geoArr[:]
from ... import GeoArray from ... import GeoArray
return GeoArray(*warp_ndarray(ndarray, geoArr.geotransform, geoArr.projection, **kwargs)) return GeoArray(*warp_ndarray(ndarray, geoArr.geotransform, geoArr.projection, **kwargs)) # FIXME this does not copy GeoArray attributes
def warp_ndarray(ndarray, in_gt, in_prj, out_prj=None, out_dtype=None, out_gsd=(None, None), def warp_ndarray(ndarray, in_gt, in_prj, out_prj=None, out_dtype=None, out_gsd=(None, None),
......
...@@ -134,9 +134,10 @@ class GeoArray(object): ...@@ -134,9 +134,10 @@ class GeoArray(object):
@arr.setter @arr.setter
def arr(self, ndarray): def arr(self, ndarray):
assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array! Got %s." %type(ndarray) assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array! Got %s." %type(ndarray)
assert ndarray.shape == self.shape, "'arr' can only be set to a numpy array with shape %s. Received %s. " \ #assert ndarray.shape == self.shape, "'arr' can only be set to a numpy array with shape %s. Received %s. " \
"If you need to change the dimensions, create a new instance of %s." \ # "If you need to change the dimensions, create a new instance of %s." \
%(self.shape, ndarray.shape, self.__class__.__name__) # %(self.shape, ndarray.shape, self.__class__.__name__)
# THIS would avoid warping like this: geoArr.arr, geoArr.gt, geoArr.prj = warp(...)
self._arr = ndarray self._arr = ndarray
...@@ -1250,6 +1251,8 @@ class BadDataMask(GeoArray): ...@@ -1250,6 +1251,8 @@ class BadDataMask(GeoArray):
bandnames=bandnames, nodata=nodata, progress=progress, q=q) bandnames=bandnames, nodata=nodata, progress=progress, q=q)
if self.is_inmem: if self.is_inmem:
# validate input data - before converting to bool
self._validate_array_values(self.arr)
self.arr = self.arr.astype(np.bool) self.arr = self.arr.astype(np.bool)
# del self._mask_baddata, self.mask_baddata # TODO delete property (requires deleter) # del self._mask_baddata, self.mask_baddata # TODO delete property (requires deleter)
...@@ -1262,14 +1265,17 @@ class BadDataMask(GeoArray): ...@@ -1262,14 +1265,17 @@ class BadDataMask(GeoArray):
@arr.setter @arr.setter
def arr(self, ndarray): def arr(self, ndarray):
assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array!" assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array!"
pixelVals_in_mask = sorted(list(np.unique(ndarray))) self._validate_array_values(ndarray)
self._arr = ndarray.astype(np.bool)
def _validate_array_values(self, maskarray):
pixelVals_in_mask = sorted(list(np.unique(maskarray)))
assert len(pixelVals_in_mask) <= 2, 'Bad data mask must have only two pixel values (boolean) - 0 and 1 or ' \ assert len(pixelVals_in_mask) <= 2, 'Bad data mask must have only two pixel values (boolean) - 0 and 1 or ' \
'False and True! The given mask for %s contains the values %s.' % ( 'False and True! The given mask for %s contains the values %s.' \
self.basename, pixelVals_in_mask) % (self.basename, pixelVals_in_mask)
assert pixelVals_in_mask in [[0, 1], [0],[1], [False, True], [False], [True]],\ assert pixelVals_in_mask in [[0, 1], [0],[1], [False, True], [False], [True]],\
'Found unsupported pixel values in the given bad data mask for %s: %s Only the values True, False, 0 ' \ 'Found unsupported pixel values in the given bad data mask for %s: %s. Only the values True, False, 0 ' \
'and 1 are supported. ' % (self.basename, pixelVals_in_mask) 'and 1 are supported. ' % (self.basename, pixelVals_in_mask)
self._arr = ndarray.astype(np.bool)
...@@ -1282,6 +1288,8 @@ class NoDataMask(GeoArray): ...@@ -1282,6 +1288,8 @@ class NoDataMask(GeoArray):
bandnames=bandnames, nodata=nodata, progress=progress, q=q) bandnames=bandnames, nodata=nodata, progress=progress, q=q)
if self.is_inmem: if self.is_inmem:
# validate input data - before converting to bool
self._validate_array_values(self.arr)
self.arr = self.arr.astype(np.bool) self.arr = self.arr.astype(np.bool)
# del self._mask_nodata, self.mask_nodata # TODO delete property (requires deleter) # del self._mask_nodata, self.mask_nodata # TODO delete property (requires deleter)
...@@ -1298,14 +1306,18 @@ class NoDataMask(GeoArray): ...@@ -1298,14 +1306,18 @@ class NoDataMask(GeoArray):
@arr.setter @arr.setter
def arr(self, ndarray): def arr(self, ndarray):
assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array!" assert isinstance(ndarray, np.ndarray), "'arr' can only be set to a numpy array!"
pixelVals_in_mask = sorted(list(np.unique(ndarray))) self._validate_array_values(ndarray)
self._arr = ndarray.astype(np.bool)
def _validate_array_values(self, maskarray):
pixelVals_in_mask = sorted(list(np.unique(maskarray)))
assert len(pixelVals_in_mask) <= 2, 'Nodata mask must have only two pixel values (boolean) - 0 and 1 or ' \ assert len(pixelVals_in_mask) <= 2, 'Nodata mask must have only two pixel values (boolean) - 0 and 1 or ' \
'False and True! The given mask for %s contains the values %s.' % ( 'False and True! The given mask for %s contains the values %s.' % (
self.basename, pixelVals_in_mask) self.basename, pixelVals_in_mask)
assert pixelVals_in_mask in [[0, 1], [0], [1], [False, True], [False], [True]],\ assert pixelVals_in_mask in [[0, 1], [0], [1], [False, True], [False], [True]], \
'Found unsupported pixel values in the given Nodata mask for %s: %s Only the values True, False, 0 ' \ 'Found unsupported pixel values in the given Nodata mask for %s: %s. Only the values True, False, 0 ' \
'and 1 are supported. ' % (self.basename, pixelVals_in_mask) 'and 1 are supported. ' % (self.basename, pixelVals_in_mask)
self._arr = ndarray.astype(np.bool)
...@@ -1323,6 +1335,7 @@ class CloudMask(GeoArray): ...@@ -1323,6 +1335,7 @@ class CloudMask(GeoArray):
class MultiGeoArray(object): class MultiGeoArray(object):
def __init__(self, GeoArray_list): def __init__(self, GeoArray_list):
""" """
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment