Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

""" Helpers with data masks (clouds, qa/qc, etc) 

 

 

References 

---------- 

 

* https://github.com/USGS-EROS/landsat-ldope-tools/blob/master/src/unpack_collection_bits.c 

 

""" 

from functools import singledispatch 

import logging 

 

import dask.array as da 

import numpy as np 

import xarray as xr 

from xarray.core.computation import apply_ufunc 

 

from .compat import toolz as tz 

from .utils import register_multi_singledispatch 

 

 

@singledispatch 

def checkbit(data, offset, width=1, value=3): 

""" Unpack a bit into True/False 

 

Parameters 

---------- 

data : np.ndarray, da.Array, xr.DataArray 

Bitpacked array data 

offset : int 

Bit offset 

width : int, optional 

Width of bitpacking (e.g., 1=>[0, 1], 2=>[0, 1, 2, 3]) 

value : int, optional 

If ``width > 1``, specify the value required to be True (e.g., 3 for 

'high', 2 for 'medium', etc. in some codings) 

 

Returns 

------- 

array-like, dtype=bool 

True or False value of unpacked bit(s) 

""" 

raise TypeError('Only supported for NumPy/Dask arrays or xarray.DataArray') 

 

 

@checkbit.register(np.ndarray) 

def _checkbit_nparray(data, offset, width=1, value=3): 

if data.dtype.kind != 'i': 

data = data.astype(int) 

if width == 1: 

return (np.right_shift(data, offset) & 0x01) == 1 

elif width == 2: 

return (np.right_shift(data,offset) & 0x03) >= value 

else: 

raise ValueError("Only works on 1-2 bit sizes") 

 

 

@checkbit.register(da.Array) 

def _checkbit_darray(data, offset, width=1, value=3): 

return da.map_blocks(_checkbit_nparray, 

data, offset, 

width=width, value=value, # kwargs to function 

dtype=bool) 

 

 

@checkbit.register(xr.DataArray) 

def _checkbit_xarray(data, offset, width=1, value=3): 

return xr.apply_ufunc( 

checkbit, 

data, 

dask='allowed', 

kwargs={'offset': offset, 'width': width, 'value': value} 

) 

 

 

@singledispatch 

def bitpack_to_coding(bitpack, bitinfo, fill=0, dtype=None): 

""" Unpack a bipacked QA/QC band to some coding (e.g. CFMask) 

 

Parameters 

---------- 

bitpack : np.ndarray, dask.array.Array, or xr.DataArray 

Bitpacked data 

bitinfo : Dict[int, Sequence[Tuple[Int, Int, Int]] 

A dict mapping output codes to bit unpacking info(s) 

(offsets, widths, and values) for use in unpacking. Should be ordered 

in order of preference with values listed first possibly overwriting 

later ones 

fill : int, float, etc, optional 

Fill value to initialize array with (e.g., your "clear" value, since 

qa/qc bands usually indicate issues with data) 

dtype : np.dtype, optional 

Output NumPy datatype. If ``None``, output will be same 

datatype as input ``bitpack`` 

 

Returns 

------- 

array 

CFmask look-alike array 

""" 

raise TypeError('Only supported for NumPy/Dask arrays or xarray.DataArray') 

 

 

@bitpack_to_coding.register(np.ndarray) 

def _bitpack_to_coding_nparray(bitpack, bitinfo, fill=0, dtype=None): 

coding_ = np.full_like(bitpack, fill, dtype=dtype) 

 

for code, offsets in reversed(list(bitinfo.items())): 

for offset in offsets: 

unpack = checkbit(bitpack, *offset) 

coding_[unpack] = code 

return coding_ 

 

 

@bitpack_to_coding.register(da.Array) 

def _bitpack_to_coding_darray(bitpack, bitinfo, fill=0, dtype=None): 

func = tz.curry(_bitpack_to_coding_nparray)(dtype=dtype, fill=fill) 

coding_ = da.map_blocks( 

func, bitpack, bitinfo, 

dtype=dtype) # this dtype goes to da.map_blocks 

return coding_ 

 

 

@bitpack_to_coding.register(xr.DataArray) 

def _bitpack_to_coding_xrarray(bitpack, bitinfo, fill=0, dtype=None): 

out = xr.core.computation.apply_ufunc( 

bitpack_to_coding, 

bitpack, 

dask='allowed', 

kwargs={'bitinfo': bitinfo, 'fill': fill, 'dtype': dtype} 

) 

out.attrs['bitinfo'] = bitinfo 

return out