1.. _windowrw:
2
3Windowed reading and writing
4============================
5
6Beginning in rasterio 0.3, you can read and write "windows" of raster files.
7This feature allows you to work on rasters that are larger than your
8computers RAM or process chunks of large rasters in parallel.
9
10Windows
11-------
12
13A window is a view onto a rectangular subset of a raster dataset and is
14described in rasterio by column and row offsets and width and height
15in pixels. These may be ints or floats.
16
17.. code-block:: python
18
19   from rasterio.windows import Window
20
21   Window(col_off, row_off, width, height)
22
23Windows may also be constructed from numpy array index tuples or slice objects.
24Only int values are permitted in these cases.
25
26.. code-block:: python
27
28   Window.from_slices((row_start, row_stop), (col_start, col_stop))
29   Window.from_slices(slice(row_start, row_stop), slice(col_start, col_stop))
30
31If height and width keyword arguments are passed to ``from_slices``, relative
32and open-ended slices may be used.
33
34.. code-block:: python
35
36   Window.from_slices(slice(None), slice(None), height=100, width=100)
37   # Window(col_off=0.0, row_off=0.0, width=100.0, height=100.0)
38
39   Window.from_slices(slice(10, -10), slice(10, -10), height=100, width=100)
40   # Window(col_off=10, row_off=10, width=80, height=80)
41
42Reading
43-------
44
45Here is an example of reading a 256 row x 512 column subset of the rasterio
46test file.
47
48.. code-block:: pycon
49
50    >>> import rasterio
51    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
52    ...     w = src.read(1, window=Window(0, 0, 512, 256))
53    ...
54    >>> print(w.shape)
55    (256, 512)
56
57Writing
58-------
59
60Writing works similarly. The following creates a blank 500 column x 300 row
61GeoTIFF and plops 37,500 pixels with value 127 into a window 30 pixels down from
62and 50 pixels to the right of the upper left corner of the GeoTIFF.
63
64.. code-block:: python
65
66    image = numpy.ones((150, 250), dtype=rasterio.ubyte) * 127
67
68    with rasterio.open(
69            '/tmp/example.tif', 'w',
70            driver='GTiff', width=500, height=300, count=1,
71            dtype=image.dtype) as dst:
72        dst.write(image, window=Window(50, 30, 250, 150), indexes=1)
73
74The result:
75
76.. image:: http://farm6.staticflickr.com/5503/11378078386_cbe2fde02e_o_d.png
77   :width: 500
78   :height: 300
79
80Decimation
81----------
82
83If the write window is smaller than the data, the data will be decimated.
84Below, the window is scaled to one third of the source image.
85
86.. code-block:: python
87
88    with rasterio.open('tests/data/RGB.byte.tif') as src:
89        b, g, r = (src.read(k) for k in (1, 2, 3))
90    # src.height = 718, src.width = 791
91
92    write_window = Window.from_slices((30, 269), (50, 313))
93    # write_window.height = 239, write_window.width = 263
94
95    with rasterio.open(
96            '/tmp/example.tif', 'w',
97            driver='GTiff', width=500, height=300, count=3,
98            dtype=r.dtype) as dst:
99        for k, arr in [(1, b), (2, g), (3, r)]:
100            dst.write(arr, indexes=k, window=write_window)
101
102And the result:
103
104.. image:: http://farm4.staticflickr.com/3804/11378361126_c034743079_o_d.png
105   :width: 500
106   :height: 300
107
108Data windows
109------------
110
111Sometimes it is desirable to crop off an outer boundary of NODATA values around
112a dataset:
113
114.. code-block:: python
115
116    from rasterio.windows import get_data_window
117
118    with rasterio.open('tests/data/RGB.byte.tif') as src:
119        window = get_data_window(src.read(1, masked=True))
120        # window = Window(col_off=13, row_off=3, width=757, height=711)
121
122        kwargs = src.meta.copy()
123        kwargs.update({
124            'height': window.height,
125            'width': window.width,
126            'transform': rasterio.windows.transform(window, src.transform)})
127
128        with rasterio.open('/tmp/cropped.tif', 'w', **kwargs) as dst:
129            dst.write(src.read(window=window))
130
131Window transforms
132-----------------
133
134The affine transform of a window can be accessed using a dataset's
135``window_transform`` method:
136
137.. code-block:: pycon
138
139    >>> import rasterio
140    >>> from rasterio.windows import Window
141    >>> win = Window(256, 256, 128, 128)
142    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
143    ...     src_transform = src.transform
144    ...     win_transform = src.window_transform(win)
145    ...
146    >>> print(src_transform)
147    | 300.04, 0.00, 101985.00|
148    | 0.00,-300.04, 2826915.00|
149    | 0.00, 0.00, 1.00|
150    >>> print(win_transform)
151    | 300.04, 0.00, 178794.71|
152    | 0.00,-300.04, 2750104.30|
153    | 0.00, 0.00, 1.00|
154
155Window utilities
156----------------
157
158Basic union and intersection operations are available for windows, to
159streamline operations across dynamically created windows for a series of bands
160or datasets with the same full extent.
161
162.. code-block:: python
163
164    >>> from rasterio import windows
165    >>> # Full window is ((0, 1000), (0, 500))
166    >>> window1 = Window(10, 100, 490, 400)
167    >>> window2 = Window(50, 10, 200, 140)
168    >>> windows.union(window1, window2)
169    Window(col_off=10, row_off=10, width=490, height=490)
170    >>> windows.intersection(window1, window2)
171    Window(col_off=50, row_off=100, width=200, height=50)
172
173
174Blocks
175------
176
177Raster datasets are generally composed of multiple blocks of data and
178windowed reads and writes are most efficient when the windows match the
179dataset's own block structure. When a file is opened to read, the shape
180of blocks for any band can be had from the block_shapes property.
181
182.. code-block:: pycon
183
184    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
185    ...     for i, shape in enumerate(src.block_shapes, 1):
186    ...         print((i, shape))
187    ...
188    (1, (3, 791))
189    (2, (3, 791))
190    (3, (3, 791))
191
192
193The block windows themselves can be had from the block_windows function.
194
195.. code-block:: pycon
196
197    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
198    ...     for ji, window in src.block_windows(1):
199    ...         print((ji, window))
200    ...
201    ((0, 0), ((0, 3), (0, 791)))
202    ((1, 0), ((3, 6), (0, 791)))
203    ...
204
205This function returns an iterator that yields a pair of values. The second is
206a window tuple that can be used in calls to `read` or `write`. The first
207is the pair of row and column indexes of this block within all blocks of the
208dataset.
209
210You may read windows of data from a file block-by-block like this.
211
212.. code-block:: pycon
213
214    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
215    ...     for ji, window in src.block_windows(1):
216    ...         r = src.read(1, window=window)
217    ...         print(r.shape)
218    ...         break
219    ...
220    (3, 791)
221
222Well-bred files have identically blocked bands, but GDAL allows otherwise and
223it's a good idea to test this assumption in your code.
224
225.. code-block:: pycon
226
227    >>> with rasterio.open('tests/data/RGB.byte.tif') as src:
228    ...     assert len(set(src.block_shapes)) == 1
229    ...     for ji, window in src.block_windows(1):
230    ...         b, g, r = (src.read(k, window=window) for k in (1, 2, 3))
231    ...         print((ji, r.shape, g.shape, b.shape))
232    ...         break
233    ...
234    ((0, 0), (3, 791), (3, 791), (3, 791))
235
236The block_shapes property is a band-ordered list of block shapes and
237`set(src.block_shapes)` gives you the set of unique shapes. Asserting that
238there is only one item in the set is effectively the same as asserting that all
239bands have the same block structure. If they do, you can use the same windows
240for each.
241