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

""" Rasterio IO helpers 

""" 

import logging 

from pathlib import Path 

 

import rasterio 

import xarray as xr 

 

from .. import xarray_accessor 

from ..gis import projections 

 

logger = logging.getLogger(__name__) 

 

 

#: Default Rasterio driver format 

DEFAULT_RASTERIO_DRIVER = 'GTiff' 

#: Attributes to keep from output of ``xarray.open_rasterio`` 

RASTERIO_ATTR_WHITELIST = ('nodatavals', ) 

 

 

def xarray_to_rasterio(xarr, path, driver=DEFAULT_RASTERIO_DRIVER, 

crs=None, transform=None, 

nodata=None, 

**meta): 

""" Save a DataArray to a rasterio/GDAL dataset 

Parameters 

---------- 

xarr : xarray.DataArray 

2D or 3D DataArray to save. Shape is assumed to be 

``(width, height, )`` for 2D or ``(count, width, height, )`` for 

3D arrays 

path : str or Path 

Save DataArray to this file path 

driver : str, optional 

Rasterio dataset driver 

crs : str, dict, or rasterio.crs.CRS, optional 

Optionally, provide CRS information about ``xarr``. Will 

try to read from ``xarr`` if not provided 

transform : affine.Affine, optional 

Optionally, provide affine transform information about ``xarr``. Will 

try to read from ``xarr`` if not provided 

nodata : int or float, optional 

No data value to set 

**meta 

Additional keyword arguments to :py:func:`rasterio.open`. Useful for 

specifying block sizes, color interpretation, and other metadata. 

 

Returns 

------- 

path : Path 

Saved file path 

 

Raises 

------ 

ValueError 

Raised if ``xarr`` is not 2D or 3D 

""" 

if not isinstance(xarr, xr.DataArray): 

raise TypeError('Can only save 2D or 3D ``xarray.DataArray``s to ' 

'rasterio/GDAL datasets') 

 

xarr, meta_ = _prepare_xarray_for_rasterio(xarr, crs, transform, 

driver=driver, **meta) 

dim_band = xarr.dims[0] 

 

# TODO: support some kind of block writing if chunked (dask array) 

with rasterio.open(str(path), 'w', **meta_) as dst: 

# Write data 

dst.write(xarr.values) 

 

# Write 1st dim ("band") coordinate names as band descriptions 

dst.descriptions = xarr.coords[dim_band].values 

 

# Write attrs as tags (except "grid_mapping") 

tags = { 

k: v for k, v in xarr.attrs.items() 

if k not in ('grid_mapping', ) 

} 

dst.update_tags(**tags) 

 

if nodata is not None: 

dst.nodata = nodata 

 

return Path(path) 

 

 

def _prepare_xarray_for_rasterio(xarr, crs=None, transform=None, 

dim_y=None, dim_x=None, dim_band=None, 

**meta): 

# Expand 2D to 3D before processing 

dim_band = dim_band or 'band' 

if xarr.ndim == 2: 

xarr = xarr.expand_dims(dim_band) 

xarr.coords['band'] = [xarr.name] if xarr.name else ['Band_1'] 

elif xarr.ndim != 3: 

raise ValueError('Can only save 2D or 3D DataArrays') 

 

if dim_band not in xarr.dims: 

raise KeyError(f'Cannot find band dimension "{dim_band}" in dims') 

 

if crs is None: 

crs = xarr.stems.crs 

 

if transform is None: 

transform = xarr.stems.transform 

 

if dim_x is None and dim_y is None: 

dim_x, dim_y = projections.cf_xy_coord_names(crs) 

 

dims_ = dict(zip(xarr.dims, xarr.shape)) 

 

meta_ = { 

'driver': DEFAULT_RASTERIO_DRIVER, 

'count': dims_[dim_band], 

'width': dims_[dim_x], 

'height': dims_[dim_y], 

'dtype': xarr.dtype, 

'crs': crs, 

'transform': transform 

} 

meta_.update(meta) 

 

return xarr, meta_