Coverage for py_tools_ds/numeric/vector.py: 100%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

41 statements  

1# -*- coding: utf-8 -*- 

2 

3# py_tools_ds - A collection of geospatial data analysis tools that simplify standard 

4# operations when handling geospatial raster and vector data as well as projections. 

5# 

6# Copyright (C) 2016-2021 

7# - Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de) 

8# - Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences Potsdam, 

9# Germany (https://www.gfz-potsdam.de/) 

10# 

11# This software was developed within the context of the GeoMultiSens project funded 

12# by the German Federal Ministry of Education and Research 

13# (project grant code: 01 IS 14 010 A-C). 

14# 

15# Licensed under the Apache License, Version 2.0 (the "License"); 

16# you may not use this file except in compliance with the License. 

17# You may obtain a copy of the License at 

18# 

19# http://www.apache.org/licenses/LICENSE-2.0 

20# 

21# Unless required by applicable law or agreed to in writing, software 

22# distributed under the License is distributed on an "AS IS" BASIS, 

23# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

24# See the License for the specific language governing permissions and 

25# limitations under the License. 

26 

27import collections 

28import numpy as np 

29import bisect 

30from typing import Iterable # noqa F401 # flake8 issue 

31 

32__author__ = "Daniel Scheffler" 

33 

34 

35def find_nearest(array, value, roundAlg='auto', extrapolate=False, exclude_val=False, tolerance=0): 

36 # type: (Iterable, float, str, bool, bool, float) -> float 

37 """Find the value of an array nearest to a another single value. 

38 

39 NOTE: In case of extrapolation an EQUALLY INCREMENTED array (like a coordinate grid) is assumed! 

40 

41 :param array: array or list of numbers 

42 :param value: a number 

43 :param roundAlg: <str> 'auto', 'on', 'off' 

44 :param extrapolate: extrapolate the given array if the given value is outside the array 

45 :param exclude_val: exclude the given value from possible return values 

46 :param tolerance: tolerance (with array = [10, 20, 30] and value=19.9 and roundAlg='off' and tolerance=0.1, 20 

47 is returned) 

48 """ 

49 assert roundAlg in ['auto', 'on', 'off'] 

50 assert isinstance(array, list) or (isinstance(array, np.ndarray) and len(array.shape) == 1) 

51 array = sorted(list(array)) 

52 

53 if exclude_val and value in array: 

54 array.remove(value) 

55 

56 if extrapolate: 

57 increment = abs(array[1] - array[0]) 

58 if value > max(array): # expand array until value 

59 array = np.arange(min(array), value + 2 * increment, increment) # 2 * inc to make array_sub work below 

60 if value < min(array): # negatively expand array until value 

61 array = (np.arange(max(array), value - 2 * increment, -increment))[::-1] 

62 elif value < min(array) or value > max(array): 

63 raise ValueError('Value %s is outside of the given array.' % value) 

64 

65 if roundAlg == 'auto': 

66 diffs = np.abs(np.array(array) - value) 

67 minDiff = diffs.min() 

68 minIdx = diffs.argmin() 

69 isMiddleVal = collections.Counter(diffs)[minDiff] > 1 # value exactly between its both neighbours 

70 idx = minIdx if not isMiddleVal else bisect.bisect_left(array, value) 

71 out = array[idx] 

72 elif roundAlg == 'off': 

73 idx = bisect.bisect_left(array, value) 

74 if array[idx] == value: 

75 out = value # exact hit 

76 else: 

77 idx -= 1 

78 out = array[idx] # round off 

79 else: # roundAlg == 'on' 

80 idx = bisect.bisect_left(array, value) 

81 out = array[idx] 

82 

83 if tolerance: 

84 array_sub = array[idx - 1: idx + 2] 

85 diffs = np.abs(np.array(array_sub) - value) 

86 inTol = diffs <= tolerance 

87 

88 if True in inTol: 

89 out = array_sub[np.argwhere(inTol.astype(int) == 1)[0][0]] 

90 

91 return out