Skip to content

Commit

Permalink
gdal/swig/python/gdal-utils/osgeo_utils/auxiliary/array_util.py - add…
Browse files Browse the repository at this point in the history
… ArrayLike, ScalarLike, ArrayOrScalarLike types (to be used instead of NumpyCompatibleArray, NumpyCompatibleArrayOrReal), removes numpy dependency

osr_util.py, gdallocationinfo.py, numpy_util.py - use new types
autotest/pyscripts/test_gdal_utils.py - test new types
  • Loading branch information
idanmiara committed May 4, 2021
1 parent 7816b68 commit a20194b
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 18 deletions.
34 changes: 30 additions & 4 deletions autotest/pyscripts/test_gdal_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
###############################################################################

import array
import os
from pathlib import Path

Expand All @@ -38,7 +38,7 @@

pytest.importorskip('osgeo_utils')

from osgeo_utils.auxiliary import util, raster_creation, base
from osgeo_utils.auxiliary import util, raster_creation, base, array_util

temp_files = []

Expand Down Expand Up @@ -89,13 +89,13 @@ def test_utils_py_1():
ds = util.open_ds(filename)
compression = util.get_image_structure_metadata(filename, 'COMPRESSION')
assert compression == 'DEFLATE'
ovr_count = util.get_ovr_count(ds)+1
ovr_count = util.get_ovr_count(ds) + 1
assert ovr_count == 3
pixel_size = util.get_pixel_size(ds)
assert pixel_size == (10, -10)

for i in range(-ovr_count, ovr_count):
assert util.get_ovr_idx(filename, ovr_idx=i) == (i if i >= 0 else ovr_count+i)
assert util.get_ovr_idx(filename, ovr_idx=i) == (i if i >= 0 else ovr_count + i)

for res, ovr in [(5, 0), (10, 0), (11, 0), (19.99, 0), (20, 1), (20.1, 1), (39, 1), (40, 2), (41, 2), (400, 2)]:
assert util.get_ovr_idx(filename, ovr_res=res) == ovr
Expand All @@ -110,6 +110,32 @@ def test_utils_py_1():
ds_list = None


def test_utils_arrays():
scalars = [7, 5.2]

for scalar in scalars:
assert isinstance(scalar, array_util.ScalarLike.__args__)
assert isinstance(scalar, array_util.ArrayOrScalarLike.__args__)

for vec in (scalars, tuple(scalars), array.array('d', scalars), array.array('i', [2, 3])):
assert isinstance(vec, array_util.ArrayLike.__args__)
assert isinstance(vec, array_util.ArrayOrScalarLike.__args__)

for not_vec in (None, {1: 2}):
assert not isinstance(not_vec, array_util.ArrayLike.__args__)
assert not isinstance(not_vec, array_util.ArrayOrScalarLike.__args__)


def test_utils_np_arrays():
np = pytest.importorskip('numpy')
vec_2d = [[1, 2, 3], [4, 5, 6]]

for dtype in (np.int8, np.int32, np.float64):
for vec in (vec_2d[0], vec_2d):
arr = np.array(vec, dtype=dtype)
assert isinstance(arr, array_util.ArrayLike.__args__)


def test_utils_py_cleanup():
for filename in temp_files:
try:
Expand Down
69 changes: 69 additions & 0 deletions gdal/swig/python/gdal-utils/osgeo_utils/auxiliary/array_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ******************************************************************************
#
# Project: GDAL utils.auxiliary
# Purpose: array and scalar related types functions
# Author: Idan Miara <[email protected]>
#
# ******************************************************************************
# Copyright (c) 2021, Idan Miara <[email protected]>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# ******************************************************************************
import array
import math
from typing import Union, Sequence, TYPE_CHECKING

ScalarLike = Union[
int,
float,
]

if TYPE_CHECKING:
# avoid "TypeError: Subscripted generics cannot be used with class and instance checks" while using isinstance()
ArrayLike = Union[
ScalarLike,
Sequence[ScalarLike],
array.array,
]
else:
ArrayLike = Union[
ScalarLike,
Sequence,
array.array,
]

try:
from numpy import ndarray
ArrayLike = Union[ArrayLike, ndarray]
except ImportError:
pass

ArrayOrScalarLike = Union[ArrayLike, ScalarLike]


def array_dist(x: ArrayOrScalarLike, y: ArrayOrScalarLike, is_max: bool = True) -> ScalarLike:
if isinstance(x, ScalarLike.__args__):
return abs(x-y)
try:
from osgeo_utils.auxiliary.numpy_util import array_dist as np_array_dist
return np_array_dist(x=x, y=y, is_max=is_max)
except ImportError as e:
return max(abs(a-b) for a, b in zip(x, y)) if is_max else max(abs(a-b) for a, b in zip(x, y))
17 changes: 12 additions & 5 deletions gdal/swig/python/gdal-utils/osgeo_utils/auxiliary/numpy_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,11 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# ******************************************************************************
import array
from numbers import Real
from typing import Union, Sequence

from osgeo import gdal, gdal_array
import numpy as np

NumpyCompatibleArray = Union[np.ndarray, array.array, Sequence]
NumpyCompatibleArrayOrReal = Union[NumpyCompatibleArray, Real]
from osgeo_utils.auxiliary.array_util import ArrayOrScalarLike, ScalarLike


def GDALTypeCodeToNumericTypeCodeEx(buf_type, signed_byte, default=None):
Expand All @@ -53,3 +49,14 @@ def GDALTypeCodeAndNumericTypeCodeFromDataSet(ds):
signed_byte = ds.GetRasterBand(1).GetMetadataItem('PIXELTYPE', 'IMAGE_STRUCTURE') == 'SIGNEDBYTE'
np_typecode = GDALTypeCodeToNumericTypeCodeEx(buf_type, signed_byte=signed_byte, default=np.float32)
return buf_type, np_typecode


def array_dist(x: ArrayOrScalarLike, y: ArrayOrScalarLike, is_max: bool = True) -> ScalarLike:
if isinstance(x, ScalarLike.__args__):
return abs(x-y)
if not isinstance(x, np.ndarray):
x = np.array(x)
if not isinstance(y, np.ndarray):
y = np.array(y)
diff = np.abs(x-y)
return np.max(diff) if is_max else np.min(diff)
6 changes: 3 additions & 3 deletions gdal/swig/python/gdal-utils/osgeo_utils/auxiliary/osr_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import osgeo
from osgeo import osr, ogr, gdal

from osgeo_utils.auxiliary.numpy_util import NumpyCompatibleArray
from osgeo_utils.auxiliary.array_util import ArrayLike

AnySRS = Union[str, int, osr.SpatialReference, gdal.Dataset]

Expand Down Expand Up @@ -78,8 +78,8 @@ def get_transform(src_srs: AnySRS, tgt_srs: AnySRS) -> Optional[osr.CoordinateTr


def transform_points(ct: Optional[osr.CoordinateTransformation],
x: NumpyCompatibleArray, y: NumpyCompatibleArray, z: Optional[NumpyCompatibleArray] = None) -> \
Tuple[NumpyCompatibleArray, NumpyCompatibleArray, Optional[NumpyCompatibleArray]]:
x: ArrayLike, y: ArrayLike, z: Optional[ArrayLike] = None) -> \
Tuple[ArrayLike, ArrayLike, Optional[ArrayLike]]:
if ct is not None:
if z is None:
for idx, (x0, y0) in enumerate(zip(x, y)):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
from osgeo import gdalconst, osr, gdal

from osgeo_utils.auxiliary.base import is_path_like
from osgeo_utils.auxiliary.numpy_util import GDALTypeCodeAndNumericTypeCodeFromDataSet, NumpyCompatibleArrayOrReal, \
NumpyCompatibleArray
from osgeo_utils.auxiliary.array_util import ArrayLike, ArrayOrScalarLike
from osgeo_utils.auxiliary.numpy_util import GDALTypeCodeAndNumericTypeCodeFromDataSet
from osgeo_utils.auxiliary.osr_util import transform_points, AnySRS, get_transform, get_srs
from osgeo_utils.auxiliary.util import PathOrDS, open_ds, get_bands, get_scales_and_offsets, get_band_nums
from osgeo_utils.auxiliary.gdal_argparse import GDALArgumentParser, GDALScript
Expand All @@ -67,7 +67,7 @@ class LocationInfoOutput(Enum):


def gdallocationinfo(filename_or_ds: PathOrDS,
x: NumpyCompatibleArrayOrReal, y: NumpyCompatibleArrayOrReal,
x: ArrayOrScalarLike, y: ArrayOrScalarLike,
gis_order: bool = False,
open_options: Optional[dict] = None,
ovr_idx: Optional[int] = None,
Expand All @@ -82,9 +82,9 @@ def gdallocationinfo(filename_or_ds: PathOrDS,
filename = filename_or_ds if is_path_like(filename_or_ds) else ''
if ds is None:
raise Exception(f'Could not open {filename}.')
if not isinstance(x, NumpyCompatibleArray.__args__):
if not isinstance(x, ArrayLike.__args__):
x = [x]
if not isinstance(y, NumpyCompatibleArray.__args__):
if not isinstance(y, ArrayLike.__args__):
y = [y]
if len(x) != len(y):
raise Exception(f'len(x)={len(x)} should be the same as len(y)={len(y)}')
Expand Down Expand Up @@ -161,7 +161,7 @@ def gdallocationinfo(filename_or_ds: PathOrDS,


def gdallocationinfo_util(filename_or_ds: PathOrDS,
x: NumpyCompatibleArrayOrReal, y: NumpyCompatibleArrayOrReal,
x: ArrayOrScalarLike, y: ArrayOrScalarLike,
open_options: Optional[dict] = None,
band_nums: Optional[Sequence[int]] = None,
resample_alg=gdalconst.GRIORA_NearestNeighbour,
Expand Down

0 comments on commit a20194b

Please sign in to comment.