Commit 650c5b1d authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

revised GeoArray.__getitem__()

geo.raster.reproject:
- warp_ndarray(): added type hint

geo.coord_grid:
- is_point_on_grid(): fixed broken type hint

geo.coord_trafo:
- reproject_shapelyPoly():  fixed broken type hint

io.raster.GeoArrray.GeoArray:
- bandnames.getter: added assertion
- geotransform: bugfix in assertion
- revised __getitem__():
    - strings are now handled properly
    - bugfix: a requested 3rd dimension from 2D-Array is now only ignored if bands=1
- __setitem__(): self._arr_cache is now set here
- dump(): bugfix for wrong wite mode
- get_mapPos(): added type hint and revised docstring
- _clip_array_at_mapPos(): added type hint
- get_array_at_mapPos(): added type hint

- updated __version__
parent 23c7618a
......@@ -15,7 +15,7 @@ __all__=[#'compatibility',
'similarity',
'GeoArray']
__version__ = '20170116_01'
__version__ = '20170119_01'
__author__='Daniel Scheffler'
# Validate GDAL version
......
......@@ -37,7 +37,7 @@ def is_coord_grid_equal(gt,xgrid,ygrid):
def is_point_on_grid(pointXY,xgrid,ygrid):
# type: (tuple,np.array,np.array)
# type: (tuple,np.array,np.array) -> bool
"""Checks if a given point is exactly on the given coordinate grid.
:param pointXY: (X,Y) coordinates of the point to check
:param xgrid: numpy array defining the coordinate grid in x-direction
......
......@@ -313,7 +313,7 @@ def reproject_shapelyGeometry(shapelyGeometry, prj_src, prj_tgt):
def reproject_shapelyPoly(shapelyPoly, tgt_prj):
# type: (shapely.Polygon, str) -> shapely.Polygon
# type: (Polygon, str) -> Polygon
"""Repojects a shapely polygon that has LonLat coordinates into the given target projection.
:param shapelyPoly: <shapely.Polygon> the shapely polygon to be reprojected (must have lonlat coordinates)
:param tgt_prj: <str> WKT projection string (like GDAL projection)
......
......@@ -220,6 +220,7 @@ def warp_ndarray(ndarray, in_gt, in_prj, out_prj=None, out_dtype=None, out_gsd=(
rspAlg='near', in_nodata=None, out_nodata=None, in_alpha=False,
out_alpha=False, targetAlignedPixels=False, gcpList=None, polynomialOrder=None, options=None,
transformerOptions=None, warpOptions=None, CPUs=1, warpMemoryLimit=0, progress=True, q=False):
# type: () -> (np.ndarray, tuple, str)
"""
:param ndarray: the numpy array to be warped
......@@ -256,6 +257,7 @@ def warp_ndarray(ndarray, in_gt, in_prj, out_prj=None, out_dtype=None, out_gsd=(
:return:
"""
# TODO complete type hint
# TODO test if this function delivers the exact same output like console version, otherwise implment error_threshold=0.125
# how to implement: https://svn.osgeo.org/gdal/trunk/autotest/utilities/test_gdalwarp_lib.py
......
......@@ -69,7 +69,7 @@ class GeoArray(object):
"""
# FIXME implement compatibility to GDAL VRTs
# TODO implement compatibility to GDAL VRTs
if not (isinstance(path_or_array, (str, np.ndarray, GeoArray)) or
issubclass(getattr(path_or_array,'__class__'), GeoArray)):
raise ValueError("%s parameter 'arg' takes only string, np.ndarray or GeoArray(and subclass) instances. "
......@@ -146,20 +146,24 @@ class GeoArray(object):
return self._bandnames
else:
self._bandnames = OrderedDict(('B%s' % band, i) for i, band in enumerate(range(1, self.bands + 1)))
#{'B%s' % band: i for i, band in enumerate(range(1, self.bands + 1))}
return self._bandnames
@bandnames.setter
def bandnames(self, list_bandnames):
# type: (list) -> None
assert len(list_bandnames) == self.bands, \
'Number of given bandnames does not match number of bands in array.'
assert len(list(set([type(b) for b in list_bandnames]))) == 1 and type(list_bandnames[0] == 'str'), \
"'bandnames must be a set of strings. Got other datetypes in there.'"
bN_dict = OrderedDict((band, i) for i, band in enumerate(list_bandnames))
assert len(bN_dict) == self.bands, 'Bands must not have the same name. Received band list: %s' %list_bandnames
self._bandnames = bN_dict
if list_bandnames:
assert isinstance(list_bandnames, list), "A list must be given when setting the 'bandnames' attribute. " \
"Received %s." %type(list_bandnames)
assert len(list_bandnames) == self.bands, \
'Number of given bandnames does not match number of bands in array.'
assert len(list(set([type(b) for b in list_bandnames]))) == 1 and type(list_bandnames[0] == 'str'), \
"'bandnames must be a set of strings. Got other datetypes in there.'"
bN_dict = OrderedDict((band, i) for i, band in enumerate(list_bandnames))
assert len(bN_dict) == self.bands, 'Bands must not have the same name. Received band list: %s' %list_bandnames
self._bandnames = bN_dict
@property
......@@ -234,7 +238,7 @@ class GeoArray(object):
@geotransform.setter
def geotransform(self, gt):
assert isinstance(gt,(list,tuple)) and len(gt)==6, 'geotransform must be a list with 6 numbers. Got %s.' %gt
assert isinstance(gt,(list,tuple)) and len(gt)==6, 'geotransform must be a list with 6 numbers. Got %s.' %str(gt)
for i in gt: assert isinstance(i,(int,float)), "geotransform must contain only numbers. Got '%s'." %i
self._geotransform = gt
......@@ -499,7 +503,7 @@ class GeoArray(object):
if self.is_inmem:
return self.arr[:, :, given]
else:
getitem_params = [given]
return self.from_path(self.arg, [given])
elif isinstance(given, str):
# behave like a dictionary and return the corresponding band
......@@ -508,43 +512,54 @@ class GeoArray(object):
raise ValueError("'%s' is not a known band. Known bands are: %s"
% (given, ', '.join(list(self.bandnames.keys()))))
if self.is_inmem:
return self.arr[:, :, self.bandnames[given]]
return self.arr if self.ndim==2 else self.arr[:, :, self.bandnames[given]]
else:
getitem_params = [self.bandnames[given]]
return self.from_path(self.arg, [self.bandnames[given]])
else:
raise ValueError('String indices are only supported if %s has been instanced with bandnames given.'
%self.__class__.__name__)
elif isinstance(given, (tuple, list)) and len(given)==3 and self.ndim==2:
elif isinstance(given, (tuple, list)):
# handle requests like geoArr[[1,2],[3,4] -> not implemented in from_path if array is not in mem
types = [type(i) for i in given]
if list in types or tuple in types:
self.to_mem()
# in case a third dim is requested from 2D-array -> ignore 3rd dim
if self.is_inmem:
return self.arr[given[:2]]
else:
getitem_params = given[:2]
if len(given)==3:
# handle strings in the 3rd dim of 'given' -> convert them to a band index
if isinstance(given[2],str):
if self.bandnames:
if given[2] not in self.bandnames:
raise ValueError("'%s' is not a known band. Known bands are: %s"
% (given[2], ', '.join(list(self.bandnames.keys()))))
band_idx = self.bandnames[given[2]]
# NOTE: the string in the 3rd is ignored if ndim==2 and band_idx==0
if self.is_inmem:
return self.arr if (self.ndim==2 and band_idx==0) else self.arr[:, :, band_idx]
else:
getitem_params = given[:2] if (self.ndim==2 and band_idx==0) else given[:2]+(band_idx,)
return self.from_path(self.arg, getitem_params)
else:
raise ValueError(
'String indices are only supported if %s has been instanced with bandnames given.'
% self.__class__.__name__)
# in case a third dim is requested from 2D-array -> ignore 3rd dim if 3rd dim is 0
elif self.ndim==2 and given[2]==0:
if self.is_inmem:
return self.arr[given[:2]]
else:
return self.from_path(self.arg, given[:2])
# if nothing has been returned until here -> behave like a numpy array
if self.is_inmem:
return self.arr[given]
else:
if isinstance(given, (tuple, list)):
# handle requests like geoArr[[1,2],[3,4] -> not implemented in from_path if array is not in mem
types = [type(i) for i in given]
if list in types or tuple in types:
self.to_mem()
# behave like a numpy array
if self.is_inmem:
return self.arr[given]
else:
getitem_params = [given] if isinstance(given, slice) else given
if not self.is_inmem:
self._arr_cache = self.from_path(self.arg, getitem_params)
return self._arr_cache
getitem_params = [given] if isinstance(given, slice) else given
return self.from_path(self.arg, getitem_params)
def __setitem__(self, idx, array2set):
......@@ -743,6 +758,8 @@ class GeoArray(object):
if out_arr.shape==self.shape:
self.arr = out_arr
self._arr_cache = out_arr
return out_arr # TODO implement check of returned datatype (e.g. NoDataMask should always return np.bool
# TODO -> would be np.int8 if an int8 file is read from disk
......@@ -799,7 +816,7 @@ class GeoArray(object):
# type: (str) -> None
"""Serialize the whole object instance to disk using dill."""
import dill
with open(out_path,'w') as outF:
with open(out_path,'wb') as outF:
dill.dump(self,outF)
......@@ -1071,13 +1088,17 @@ class GeoArray(object):
def get_mapPos(self, mapBounds, mapBounds_prj, band2get=None, arr_gt=None, arr_prj=None, fillVal=None,
rspAlg='near', v=False): # TODO implement slice for indexing bands
"""
# type: (tuple, str, int, tuple, str, int, str, bool) -> (np.ndarray, tuple, str)
"""Returns the array data of GeoArray at a given geographic position.
NOTE: The given mapBounds are snapped to the pixel grid of GeoArray. If the given mapBounds include areas
outside of the extent of GeoArray, these areas are filled with the fill value of GeoArray.
:param mapBounds: xmin, ymin, xmax, ymax
:param mapBounds_prj:
:param mapBounds_prj: WKT projection string corresponding to mapBounds
:param band2get: band index of the band to be returned (full array if not given)
:param arr_gt:
:param arr_prj:
:param arr_gt: GDAL GeoTransform (taken from self if not given)
:param arr_prj: WKT projection string (taken from self if not given)
:param fillVal: nodata value
:param rspAlg: <str> Resampling method to use. Available methods are:
near, bilinear, cubic, cubicspline, lanczos, average, mode, max, min, med, q1, q2
......@@ -1335,6 +1356,7 @@ class MultiGeoArray(object):
def _clip_array_at_mapPos(arr, mapBounds, arr_gt, band2clip=None, fillVal=0):
# type: (np.ndarray, tuple, tuple, int, int) -> (np.ndarray, tuple)
"""
NOTE: asserts that mapBounds have the same projection like the coordinates in arr_gt
......@@ -1492,6 +1514,7 @@ def get_array_at_mapPosOLD(arr, arr_gt, arr_prj, mapBounds, mapBounds_prj, band2
def get_array_at_mapPos(arr, arr_gt, arr_prj, out_prj, mapBounds, mapBounds_prj=None, out_gsd=None, band2get=None,
fillVal=0, rspAlg='near'):
# type: (np.ndarray, tuple, str, str, tuple, str, tuple, int, int,str) -> (np.ndarray, tuple, str)
"""
:param arr:
......@@ -1500,6 +1523,7 @@ def get_array_at_mapPos(arr, arr_gt, arr_prj, out_prj, mapBounds, mapBounds_prj=
:param out_prj: output projection as WKT string
:param mapBounds: xmin, ymin, xmax, ymax
:param mapBounds_prj: the projection of the given map bounds (default: output projection)
:param out_gsd: (X,Y)
:param band2get: band index of the band to be returned (full array if not given)
:param fillVal:
:param rspAlg: <str> Resampling method to use. Available methods are:
......
Markdown is supported
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