|
5 | 5 | "metadata": {}, |
6 | 6 | "source": [ |
7 | 7 | "# Inspecting Satellite Imagery using Rasterio\n", |
8 | | - "## A first look at analyzing satellite data with Python\n", |
| 8 | + "## A first look at satellite data with Python\n", |
9 | 9 | "\n", |
10 | | - "At this point, you've explored different ways of searching for, filtering, and downloading satellite imagery. Now let's use one of these acquired datasets and dig into it a bit with Python.\n", |
| 10 | + "At this point, you will have learned different ways of searching for, filtering, and downloading satellite imagery. Now let's use one of these acquired datasets and dig into it a bit with Python.\n", |
11 | 11 | "\n", |
12 | | - "Here we're going to use a Python library called `rasterio`: you may be familiar with it already, or perhaps with the related C library, `GDAL`." |
| 12 | + "Here we're going to use a Python library called `rasterio`: you may be familiar with it already, or perhaps with the related C library, `GDAL`. If you've used `numpy` before, working with `rasterio` will feel very familiar." |
13 | 13 | ] |
14 | 14 | }, |
15 | 15 | { |
16 | 16 | "cell_type": "code", |
17 | | - "execution_count": 50, |
| 17 | + "execution_count": null, |
18 | 18 | "metadata": {}, |
19 | 19 | "outputs": [], |
20 | 20 | "source": [ |
21 | 21 | "import rasterio\n", |
22 | | - "satdat = rasterio.open(\"example.tif\")" |
| 22 | + "\n", |
| 23 | + "# feel free to replace 'example' with your own GeoTIFF:\n", |
| 24 | + "# note that this Notebook will assume we're working with PlanetScope 4-band imagery\n", |
| 25 | + "image_file = \"example.tif\"\n", |
| 26 | + "\n", |
| 27 | + "satdat = rasterio.open(image_file)" |
23 | 28 | ] |
24 | 29 | }, |
25 | 30 | { |
|
32 | 37 | }, |
33 | 38 | { |
34 | 39 | "cell_type": "code", |
35 | | - "execution_count": 51, |
| 40 | + "execution_count": null, |
36 | 41 | "metadata": {}, |
37 | | - "outputs": [ |
38 | | - { |
39 | | - "data": { |
40 | | - "text/plain": [ |
41 | | - "BoundingBox(left=540759.0, bottom=3754401.0, right=568047.0, top=3767985.0)" |
42 | | - ] |
43 | | - }, |
44 | | - "execution_count": 51, |
45 | | - "metadata": {}, |
46 | | - "output_type": "execute_result" |
47 | | - } |
48 | | - ], |
| 42 | + "outputs": [], |
49 | 43 | "source": [ |
50 | 44 | "# Get the bounding box of this GeoTIFF\n", |
| 45 | + "\n", |
51 | 46 | "satdat.bounds" |
52 | 47 | ] |
53 | 48 | }, |
54 | 49 | { |
55 | 50 | "cell_type": "code", |
56 | | - "execution_count": 63, |
| 51 | + "execution_count": null, |
57 | 52 | "metadata": {}, |
58 | | - "outputs": [ |
59 | | - { |
60 | | - "name": "stdout", |
61 | | - "output_type": "stream", |
62 | | - "text": [ |
63 | | - "Width: 27288.0, Height: 13584.0\n" |
64 | | - ] |
65 | | - } |
66 | | - ], |
| 53 | + "outputs": [], |
67 | 54 | "source": [ |
68 | | - "# Get dimensions, in map units (here, that's meters)\n", |
| 55 | + "# Get dimensions, in map units (using the example GeoTIFF, that's meters)\n", |
69 | 56 | "\n", |
70 | 57 | "width = satdat.bounds.right - satdat.bounds.left\n", |
71 | 58 | "height = satdat.bounds.top - satdat.bounds.bottom\n", |
|
75 | 62 | }, |
76 | 63 | { |
77 | 64 | "cell_type": "code", |
78 | | - "execution_count": 64, |
| 65 | + "execution_count": null, |
79 | 66 | "metadata": {}, |
80 | | - "outputs": [ |
81 | | - { |
82 | | - "name": "stdout", |
83 | | - "output_type": "stream", |
84 | | - "text": [ |
85 | | - "Width: 9096, Height: 4528\n" |
86 | | - ] |
87 | | - } |
88 | | - ], |
| 67 | + "outputs": [], |
89 | 68 | "source": [ |
90 | 69 | "# Get dimensions, in pixels\n", |
| 70 | + "\n", |
91 | 71 | "px_width = satdat.width\n", |
92 | 72 | "px_height = satdat.height\n", |
93 | 73 | "print(\"Width: {}, Height: {}\".format(px_width, px_height))" |
94 | 74 | ] |
95 | 75 | }, |
96 | 76 | { |
97 | 77 | "cell_type": "code", |
98 | | - "execution_count": 65, |
| 78 | + "execution_count": null, |
99 | 79 | "metadata": {}, |
100 | | - "outputs": [ |
101 | | - { |
102 | | - "data": { |
103 | | - "text/plain": [ |
104 | | - "(3.0, 3.0)" |
105 | | - ] |
106 | | - }, |
107 | | - "execution_count": 65, |
108 | | - "metadata": {}, |
109 | | - "output_type": "execute_result" |
110 | | - } |
111 | | - ], |
| 80 | + "outputs": [], |
112 | 81 | "source": [ |
113 | 82 | "# How many meters to a pixel?\n", |
114 | 83 | "\n", |
|
120 | 89 | }, |
121 | 90 | { |
122 | 91 | "cell_type": "code", |
123 | | - "execution_count": 66, |
| 92 | + "execution_count": null, |
124 | 93 | "metadata": {}, |
125 | | - "outputs": [ |
126 | | - { |
127 | | - "data": { |
128 | | - "text/plain": [ |
129 | | - "CRS({'init': 'epsg:32611'})" |
130 | | - ] |
131 | | - }, |
132 | | - "execution_count": 66, |
133 | | - "metadata": {}, |
134 | | - "output_type": "execute_result" |
135 | | - } |
136 | | - ], |
| 94 | + "outputs": [], |
137 | 95 | "source": [ |
138 | 96 | "# Get coordinate reference system\n", |
| 97 | + "\n", |
139 | 98 | "satdat.crs" |
140 | 99 | ] |
141 | 100 | }, |
142 | 101 | { |
143 | 102 | "cell_type": "code", |
144 | | - "execution_count": 59, |
| 103 | + "execution_count": null, |
145 | 104 | "metadata": {}, |
146 | | - "outputs": [ |
147 | | - { |
148 | | - "name": "stdout", |
149 | | - "output_type": "stream", |
150 | | - "text": [ |
151 | | - "Top left corner coordinates: (540759.0, 3767985.0)\n", |
152 | | - "Bottom right corner coordinates: (568047.0, 3754401.0)\n" |
153 | | - ] |
154 | | - }, |
155 | | - { |
156 | | - "name": "stderr", |
157 | | - "output_type": "stream", |
158 | | - "text": [ |
159 | | - "/opt/conda/lib/python3.6/site-packages/IPython/core/interactiveshell.py:2910: FutureWarning: The value of this property will change in version 1.0. Please see https://github.com/mapbox/rasterio/issues/86 for details.\n", |
160 | | - " exec(code_obj, self.user_global_ns, self.user_ns)\n" |
161 | | - ] |
162 | | - } |
163 | | - ], |
| 105 | + "outputs": [], |
164 | 106 | "source": [ |
165 | 107 | "# Get coordinates of top-left & bottom right corners\n", |
166 | | - "# NOTE: how to do this depends on your Rasterio version:\n", |
167 | | - "# the below example may generate a FutureWarning, which is safe to ignore here\n", |
168 | 108 | "\n", |
169 | | - "try:\n", |
170 | | - " topleft = satdat.transform * (0, 0)\n", |
171 | | - " botright = satdat.transform * (width, height)\n", |
172 | | - " \n", |
173 | | - "except TypeError:\n", |
174 | | - " topleft = satdat.affine * (0, 0)\n", |
175 | | - " botright = satdat.affine * (width, height)\n", |
| 109 | + "# NOTE: how to do this depends on your Rasterio version --\n", |
| 110 | + "# if you're running against a pre-1.0 release use satdat.affine instead\n", |
| 111 | + "\n", |
| 112 | + "topleft = satdat.transform * (0, 0)\n", |
| 113 | + "botright = satdat.transform * (width, height)\n", |
176 | 114 | " \n", |
177 | 115 | "print(\"Top left corner coordinates: {}\".format(topleft))\n", |
178 | 116 | "print(\"Bottom right corner coordinates: {}\".format(botright))" |
179 | 117 | ] |
180 | 118 | }, |
| 119 | + { |
| 120 | + "cell_type": "code", |
| 121 | + "execution_count": null, |
| 122 | + "metadata": {}, |
| 123 | + "outputs": [], |
| 124 | + "source": [ |
| 125 | + "# Another way of viewing most of the previous values:\n", |
| 126 | + "# Get the basic metadata of this GeoTIFF\n", |
| 127 | + "\n", |
| 128 | + "satdat.meta" |
| 129 | + ] |
| 130 | + }, |
181 | 131 | { |
182 | 132 | "cell_type": "markdown", |
183 | 133 | "metadata": {}, |
184 | 134 | "source": [ |
185 | 135 | "## Bands\n", |
186 | | - "So far, we haven't done too much raster-specific work yet. Since we know we're inspecting a multispectral satellite image, let's see what we can learn about its bands." |
| 136 | + "So far, we haven't done too much geospatial-raster-specific work yet. Since we know we're inspecting a multispectral satellite image, let's see what we can learn about its bands." |
187 | 137 | ] |
188 | 138 | }, |
189 | 139 | { |
|
193 | 143 | "outputs": [], |
194 | 144 | "source": [ |
195 | 145 | "# Get the number of bands by listing their indices\n", |
| 146 | + "\n", |
196 | 147 | "satdat.indexes" |
197 | 148 | ] |
198 | 149 | }, |
|
214 | 165 | "blue = satdat.read(1)\n", |
215 | 166 | "green = satdat.read(2)\n", |
216 | 167 | "red = satdat.read(3)\n", |
217 | | - "nir = satdat.read(4)" |
| 168 | + "nir = satdat.read(4)\n", |
| 169 | + "\n", |
| 170 | + "# or:\n", |
| 171 | + "# blue, green, red, nir = satdat.read()" |
| 172 | + ] |
| 173 | + }, |
| 174 | + { |
| 175 | + "cell_type": "markdown", |
| 176 | + "metadata": {}, |
| 177 | + "source": [ |
| 178 | + "## Pixels\n", |
| 179 | + "\n", |
| 180 | + "In a raster dataset, each pixel has a value. Pixels are arranged in a grid, and pixels representing equivalent data have the same value:\n", |
| 181 | + "\n", |
| 182 | + "" |
218 | 183 | ] |
219 | 184 | }, |
220 | 185 | { |
|
225 | 190 | "source": [ |
226 | 191 | "# bands are stored as numpy arrays\n", |
227 | 192 | "\n", |
228 | | - "type(blue)" |
| 193 | + "print(type(blue))" |
229 | 194 | ] |
230 | 195 | }, |
231 | 196 | { |
|
234 | 199 | "metadata": {}, |
235 | 200 | "outputs": [], |
236 | 201 | "source": [ |
237 | | - "blue" |
| 202 | + "# how many dimensions would a raster have?\n", |
| 203 | + "\n", |
| 204 | + "print(blue.ndim)" |
238 | 205 | ] |
239 | 206 | }, |
240 | 207 | { |
|
243 | 210 | "metadata": {}, |
244 | 211 | "outputs": [], |
245 | 212 | "source": [ |
246 | | - "# Output min & max pixel values in each band\n", |
| 213 | + "# take a look at the summarized array\n", |
247 | 214 | "\n", |
248 | | - "print(blue.min(), blue.max())\n", |
249 | | - "print(green.min(), green.max())\n", |
250 | | - "print(red.min(), red.max())\n", |
251 | | - "print(nir.min(), nir.max())" |
| 215 | + "print(blue)" |
252 | 216 | ] |
253 | 217 | }, |
254 | 218 | { |
255 | | - "cell_type": "markdown", |
| 219 | + "cell_type": "code", |
| 220 | + "execution_count": null, |
256 | 221 | "metadata": {}, |
| 222 | + "outputs": [], |
257 | 223 | "source": [ |
258 | | - "## Pixels" |
| 224 | + "# Output a min & max pixel value in each band\n", |
| 225 | + "\n", |
| 226 | + "# we'll need to use numpy directly for this\n", |
| 227 | + "import numpy\n", |
| 228 | + "\n", |
| 229 | + "print(numpy.amin(blue), numpy.amax(blue))\n", |
| 230 | + "print(numpy.amin(green), numpy.amax(green))\n", |
| 231 | + "print(numpy.amin(red), numpy.amax(red))\n", |
| 232 | + "print(numpy.amin(nir), numpy.amax(nir))" |
259 | 233 | ] |
260 | 234 | }, |
261 | 235 | { |
262 | 236 | "cell_type": "code", |
263 | | - "execution_count": 61, |
| 237 | + "execution_count": null, |
264 | 238 | "metadata": {}, |
265 | | - "outputs": [ |
266 | | - { |
267 | | - "data": { |
268 | | - "text/plain": [ |
269 | | - "6382" |
270 | | - ] |
271 | | - }, |
272 | | - "execution_count": 61, |
273 | | - "metadata": {}, |
274 | | - "output_type": "execute_result" |
275 | | - } |
276 | | - ], |
| 239 | + "outputs": [], |
277 | 240 | "source": [ |
278 | | - "# Let's grab the pixel 5km east and 5km south of the upper left corner\n", |
| 241 | + "# Let's grab the pixel 2km east and 2km south of the upper left corner\n", |
279 | 242 | "\n", |
280 | 243 | "# get the pixel \n", |
281 | | - "px_x = satdat.bounds.left + 5000\n", |
282 | | - "px_y = satdat.bounds.top - 5000\n", |
| 244 | + "px_x = satdat.bounds.left + 2000\n", |
| 245 | + "px_y = satdat.bounds.top - 2000\n", |
283 | 246 | "\n", |
284 | 247 | "row, col = satdat.index(px_x, px_y)\n", |
285 | 248 | "\n", |
286 | | - "# Now let's look at the value of Band1 (\"blue\") at this pixel\n", |
287 | | - "blue[row, col]" |
| 249 | + "# Now let's look at the value of each band at this pixel\n", |
| 250 | + "print(\"Red: {}\".format(red[row, col]))\n", |
| 251 | + "print(\"Green: {}\".format(green[row, col]))\n", |
| 252 | + "print(\"Blue: {}\".format(blue[row, col]))\n", |
| 253 | + "print(\"NIR: {}\".format(nir[row, col]))" |
288 | 254 | ] |
289 | | - }, |
290 | | - { |
291 | | - "cell_type": "code", |
292 | | - "execution_count": null, |
293 | | - "metadata": {}, |
294 | | - "outputs": [], |
295 | | - "source": [] |
296 | 255 | } |
297 | 256 | ], |
298 | 257 | "metadata": { |
|
311 | 270 | "name": "python", |
312 | 271 | "nbconvert_exporter": "python", |
313 | 272 | "pygments_lexer": "ipython3", |
314 | | - "version": "3.6.2" |
| 273 | + "version": "3.6.3" |
315 | 274 | } |
316 | 275 | }, |
317 | 276 | "nbformat": 4, |
|
0 commit comments