-
Notifications
You must be signed in to change notification settings - Fork 3
/
P2_rasterPackageIntroduction_II-v1.html
386 lines (323 loc) · 17.9 KB
/
P2_rasterPackageIntroduction_II-v1.html
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="pandoc" />
<meta name="author" content="Joao Goncalves" />
<meta name="date" content="2017-11-17" />
<title>P2 Spatial data analysis: introduction to raster processing (part-2)</title>
<script src="P2_rasterPackageIntroduction_II-v1_files/jquery-1.11.3/jquery.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="P2_rasterPackageIntroduction_II-v1_files/bootstrap-3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<script src="P2_rasterPackageIntroduction_II-v1_files/bootstrap-3.3.5/js/bootstrap.min.js"></script>
<script src="P2_rasterPackageIntroduction_II-v1_files/bootstrap-3.3.5/shim/html5shiv.min.js"></script>
<script src="P2_rasterPackageIntroduction_II-v1_files/bootstrap-3.3.5/shim/respond.min.js"></script>
<script src="P2_rasterPackageIntroduction_II-v1_files/navigation-1.1/tabsets.js"></script>
<link href="P2_rasterPackageIntroduction_II-v1_files/highlightjs-1.1/default.css" rel="stylesheet" />
<script src="P2_rasterPackageIntroduction_II-v1_files/highlightjs-1.1/highlight.js"></script>
<style type="text/css">code{white-space: pre;}</style>
<style type="text/css">
pre:not([class]) {
background-color: white;
}
</style>
<script type="text/javascript">
if (window.hljs && document.readyState && document.readyState === "complete") {
window.setTimeout(function() {
hljs.initHighlighting();
}, 0);
}
</script>
<style type="text/css">
h1 {
font-size: 34px;
}
h1.title {
font-size: 38px;
}
h2 {
font-size: 30px;
}
h3 {
font-size: 24px;
}
h4 {
font-size: 18px;
}
h5 {
font-size: 16px;
}
h6 {
font-size: 12px;
}
.table th:not([align]) {
text-align: left;
}
</style>
</head>
<body>
<style type = "text/css">
.main-container {
max-width: 940px;
margin-left: auto;
margin-right: auto;
}
code {
color: inherit;
background-color: rgba(0, 0, 0, 0.04);
}
img {
max-width:100%;
height: auto;
}
.tabbed-pane {
padding-top: 12px;
}
button.code-folding-btn:focus {
outline: none;
}
</style>
<div class="container-fluid main-container">
<!-- tabsets -->
<script>
$(document).ready(function () {
window.buildTabsets("TOC");
});
</script>
<!-- code folding -->
<div class="fluid-row" id="header">
<h1 class="title toc-ignore">P2 Spatial data analysis: introduction to raster processing (part-2)</h1>
<h4 class="author"><em>Joao Goncalves</em></h4>
<h4 class="date"><em>17 November 2017</em></h4>
</div>
<div id="background" class="section level3">
<h3>Background</h3>
<hr />
<p>In the second part of this tutorial series on spatial data analysis using the <code>raster</code> package, we will explore new functionalities, namely:</p>
<ul>
<li>Raster algebra,<br />
</li>
<li>Cropping<br />
</li>
<li>Reprojection and resampling.</li>
</ul>
<p>We will also introduce a new type of object named <code>RasterStack</code>, which, in its essence, is a collection of <code>RasterLayer</code> objects with the same spatial extent, resolution and coordinate reference system (CRS).</p>
<p>For more information information on raster data processing see <a href="http://r-exercises.com/tags/raster-data">here</a> and <a href="https://www.r-exercises.com/2017/11/29/spatial-data-analysis-introduction-to-raster-processing-part-1">here</a>.</p>
</div>
<div id="rasterstack-objects-and-raster-algebra" class="section level3">
<h3>RasterStack objects and raster algebra</h3>
<hr />
<p>We will start this tutorial by downloading the sample raster data and creating a <code>RasterStack</code> composed by multiple image files. One satellite scene from <a href="https://landsat.gsfc.nasa.gov/landsat-8/landsat-8-bands/">Landsat 8</a> will be used for this purpose. The data contains <a href="http://glcf.umd.edu/data/gls_SR/">surface reflectance</a> information for seven spectral bands (or layers, following the terminology for <code>RasterStack</code> objects) in GeoTIFF file format.</p>
<p>The following table summarizes info on Landsat 8 spectral bands used in this tutorial.</p>
<table>
<thead>
<tr class="header">
<th>Band #</th>
<th>Band name</th>
<th>Wavelength (micrometers)</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Band 1</td>
<td>Ultra Blue</td>
<td>0.435 - 0.451</td>
</tr>
<tr class="even">
<td>Band 2</td>
<td>Blue</td>
<td>0.452 - 0.512</td>
</tr>
<tr class="odd">
<td>Band 3</td>
<td>Green</td>
<td>0.533 - 0.590</td>
</tr>
<tr class="even">
<td>Band 4</td>
<td>Red</td>
<td>0.636 - 0.673</td>
</tr>
<tr class="odd">
<td>Band 5</td>
<td>Near Infrared (NIR)</td>
<td>0.851 - 0.879</td>
</tr>
<tr class="even">
<td>Band 6</td>
<td>Shortwave Infrared (SWIR) 1</td>
<td>1.566 - 1.651</td>
</tr>
<tr class="odd">
<td>Band 7</td>
<td>Shortwave Infrared (SWIR) 2</td>
<td>2.107 - 2.294</td>
</tr>
</tbody>
</table>
<p>Landsat 8 spatial resolution (or pixel size) is equal to 30 meters. Valid reflecance decimal values are typically within 0.00 - 1.00 but, for decreasing file size, the valid range is multiplied by a 10<sup>4</sup> scaling factor to be in integer range 0 - 10000. Image acqusition date is the 15<sup>th</sup> of July 2015.</p>
<pre class="r"><code>library(raster)
## Create a folder named data-raw inside the working directory to place downloaded data
if(!dir.exists("./data-raw")) dir.create("./data-raw")
## If you run into download problems try changing: method = "wget"
download.file("https://raw.githubusercontent.com/joaofgoncalves/R_exercises_raster_tutorial/master/data/LT8_PNPG.zip", "./data-raw/LT8_PNPG.zip", method = "auto")
## Uncompress the zip file
unzip("./data-raw/LT8_PNPG.zip", exdir = "./data-raw")</code></pre>
<p>With the data downloaded and uncompressed we can now generate a <code>RasterStack</code> object. The <code>stack</code> function accepts a character vector as input containing the paths to each raster layer. To generate this we will use the <code>list.files</code> function.</p>
<pre class="r"><code># Get file paths and check/print the list
fp <- list.files(path = "./data-raw", pattern = ".tif$", full.names = TRUE)
print(fp)</code></pre>
<pre><code>## [1] "./data-raw/LC82040312015193LGN00_sr_band1.tif"
## [2] "./data-raw/LC82040312015193LGN00_sr_band2.tif"
## [3] "./data-raw/LC82040312015193LGN00_sr_band3.tif"
## [4] "./data-raw/LC82040312015193LGN00_sr_band4.tif"
## [5] "./data-raw/LC82040312015193LGN00_sr_band5.tif"
## [6] "./data-raw/LC82040312015193LGN00_sr_band6.tif"
## [7] "./data-raw/LC82040312015193LGN00_sr_band7.tif"</code></pre>
<pre class="r"><code># Create the raster stack and print basic info
rst <- stack(fp)
print(rst)</code></pre>
<pre><code>## class : RasterStack
## dimensions : 1545, 1480, 2286600, 7 (nrow, ncol, ncell, nlayers)
## resolution : 30, 30 (x, y)
## extent : 549615, 594015, 4613355, 4659705 (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=utm +zone=29 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names : LC8204031//0_sr_band1, LC8204031//0_sr_band2, LC8204031//0_sr_band3, LC8204031//0_sr_band4, LC8204031//0_sr_band5, LC8204031//0_sr_band6, LC8204031//0_sr_band7
## min values : -27, -1, 29, -86, -216, -212, -102
## max values : 3170, 3556, 4296, 4931, 6904, 7413, 6696</code></pre>
<p>Changing raster layer names (usually difficult to read, as we saw above) is really straightforward. Also, if necessary, using simple names makes it easier to access layers <em>‘by name’</em> in the <code>RasterStack</code>.</p>
<pre class="r"><code>names(rst) <- paste("b",1:7,sep="")</code></pre>
<p>Let’s check if the data is being stored in memory:</p>
<pre class="r"><code>inMemory(rst)</code></pre>
<pre><code>## [1] FALSE</code></pre>
<p>Similarly <code>RasterLayer</code> objects, by default (and unless necessary), a <code>RasterStack</code> object only holds metadata and connections to the actual data to spare memory.</p>
<p>Now, let’s plot the data for a fast visualization.</p>
<pre class="r"><code>plot(rst)</code></pre>
<div class="figure">
<img src="https://raw.githubusercontent.com/joaofgoncalves/R_exercises_raster_tutorial/master/img/plot_data-1.png" />
</div>
<p>Notice how each layer has a separated tile in the plot.</p>
<p><strong>.: Raster algebra</strong></p>
<p>Now we can proceed to doing some raster algebra calculations. We will accomplish this by using three different methods: (i) direct, (ii) <code>calc</code> function, and, (iii) <code>overlay</code> function. In this example we will calculate the Normalized Difference Vegetation Index (NDVI) using the red (b4) and the near-infrared (NIR; b5) bands as:</p>
<p>NDVI = (NIR - Red) / (NIR + red)</p>
<p><strong>.: Method #1</strong> (direct)</p>
<p>This method allows to directly use the raster layers in the stack called by their indices (or names). Typical operands (e.g., <code>+</code>, <code>-</code>, <code>/</code>, <code>*</code>) can be used, as well, as functions (e.g., <code>sqrt</code>, <code>log</code>, <code>cos</code>). However, since processing occurs all at once in memory, you must be sure that your data fits into RAM.</p>
<pre class="r"><code># Calling raster layers by index
ndvi <- (rst[[5]] - rst[[4]]) / (rst[[5]] + rst[[4]])
# Or calling by name
ndvi <- (rst[["b5"]] - rst[["b4"]]) / (rst[["b5"]] + rst[["b4"]])</code></pre>
<p>Notice how the data type of the input rasters and the final raster (a ratio) are different (from integer to float; see <code>?dataType</code> for details):</p>
<pre class="r"><code>dataType(rst)</code></pre>
<pre><code>## [1] "INT2S" "INT2S" "INT2U" "INT2S" "INT2S" "INT2S" "INT2S"</code></pre>
<pre class="r"><code>dataType(ndvi)</code></pre>
<pre><code>## [1] "FLT4S"</code></pre>
<p><strong>.: Method #2</strong> (calc function)</p>
<p>For large objects <code>calc</code> will compute values by raster chunks thus saving memory. This means that for the result of the defined function to be correct it should not depend on having access to all values at once.</p>
<pre class="r"><code>calcNDVI_1 <- function(x) return((x[[5]] - x[[4]]) / (x[[5]] + x[[4]]))
ndvi1 <- calc(rst, fun = calcNDVI_1)</code></pre>
<p><strong>.: Method #3</strong> (overlay function)</p>
<p>The overlay function allows to combine two (or more) <code>Raster*</code> objects and it should be more efficient when using large raster datasets that cannot be loaded into memory (similarly to <code>calc</code>).</p>
<pre class="r"><code>calcNDVI_2 <- function(x, y) return((x - y) / (x + y))
ndvi2 <- overlay(x = rst[[5]], y = rst[[4]], fun = calcNDVI_2)</code></pre>
<p>Overall, using the first method is not advisable in cases were raster data is “big”. In those cases, it is recommendable to use more “memory-friendly” methods such as <code>calc</code> or <code>overlay</code>. Also, as a general rule, if a calculation needs to use multiple individual layers separately (sometimes in different objects) it will be easier to set up in <code>overlay</code> rather than in <code>calc</code>.</p>
<p>Plotting the NDVI data requires some fine tunning because some ‘strange’ values appeared. Note that NDVI range is between -1.00 and 1.00. In the summary below notice how ‘resistant’ measures (quartiles) are fine but not the extremes. For NDVI, values closer to 1 represent higher vegetation cover.</p>
<pre class="r"><code># NDVI summary
summary(ndvi)
# Set values outside the 'normal' range as NA's
# Indexing for RasterLayers works similarly to matrix or data frame objects
ndvi[ndvi < -1] <- NA
ndvi[ndvi > 1] <- NA
# Plot NDVI
plot(ndvi, main="NDVI Peneda-Geres National Park", xlab = "X-coordinates", ylab = "Y-coordinates")</code></pre>
<div class="figure">
<img src="https://raw.githubusercontent.com/joaofgoncalves/R_exercises_raster_tutorial/master/img/ndvi_range-1.png" />
</div>
<p>It is also fairly easy to perform logical operations. For example creating a NDVI mask with values above 0.4:</p>
<pre class="r"><code>ndviMask <- ndvi > 0.4
plot(ndviMask, main="NDVI mask", xlab = "X-coordinates", ylab = "Y-coordinates")</code></pre>
<div class="figure">
<img src="https://raw.githubusercontent.com/joaofgoncalves/R_exercises_raster_tutorial/master/img/raster_algebra_logical_ops-1.png" />
</div>
<p>This creates a new boolean raster with 0’s for pixels that are equal or below 0.4, and, 1’s for values above 0.4. Very useful for separating vegetated from non-vegetated surfaces! :-)</p>
</div>
<div id="raster-cropping" class="section level3">
<h3>Raster cropping</h3>
<hr />
<p>Often we want to crop (or clip) a raster data set for a specific area of study. For doing that, the <code>raster</code> package uses the <code>crop</code> function which accepts as input a <code>Raster*</code> object and an <code>Extent</code> object used to define the new bounding coordinates (see <code>?extent</code> for more details).</p>
<pre class="r"><code># Bounding coordinates
xmin <- 554615
xmax <- 589015
ymin <- 4618355
ymax <- 4654705
# Create the extent object by defining the bounding coordinates
newExtent <- extent(xmin, xmax, ymin, ymax)
# Crop
cropRst <- crop(rst, newExtent)</code></pre>
</div>
<div id="raster-reprojection-and-resampling" class="section level3">
<h3>Raster reprojection and resampling</h3>
<hr />
<p>Often, after downloading some raster data (e.g., satellite imagery) for a given area it is needed to change its coordinate reference system (CRS). <code>projectRaster</code> function allows to project the values of a <code>Raster*</code> object to a new one with another CRS. It is possible to do this by providing the new projection info as a single argument (a <code>CRS</code> object); in this case the function sets the extent and resolution of the new object. To assure that the newly created object lines up with other datasets, you can provide a target <code>Raster*</code> object with the properties that the input data should be projected to. <code>projectRaster</code> also allows to change the spatial resolution (or pixel size) of the input raster.</p>
<p>In the first example we will keep the same Datum as in the original data but change from a projected CRS (in Universal Transverse Mercator - UTM 29N) to a geographic lat/lon CRS. Notice how the pixel size is not constant across the x and y axes.</p>
<pre class="r"><code># Create an object of class CRS with the target reference system
targetCRS <- CRS("+init=epsg:4326")
# Reproject
ndvi_ReprojWGS84 <- projectRaster(ndvi, method = "ngb", crs = targetCRS)
print(ndvi_ReprojWGS84)</code></pre>
<pre><code>## class : RasterLayer
## dimensions : 1575, 1504, 2368800 (nrow, ncol, ncell)
## resolution : 0.000362, 0.00027 (x, y)
## extent : -8.40579, -7.861342, 41.6645, 42.08975 (xmin, xmax, ymin, ymax)
## coord. ref. : +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
## data source : in memory
## names : layer
## values : -18, 40 (min, max)</code></pre>
<p>In this second example we will change the data to the Portuguese official CRS: Datum ETRS 1989, Projection Transverse Mercator, Ellipsoid GRS 1980 (see <a href="http://spatialreference.org/ref/epsg/3763/">here</a> more details).</p>
<pre class="r"><code># Create an object of class CRS with the target reference system
targetCRS <- CRS("+proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs ")
# Reproject
ndvi_ReprojETRS89 <- projectRaster(ndvi, method = "ngb", crs = targetCRS)
print(ndvi_ReprojETRS89)</code></pre>
<pre><code>## class : RasterLayer
## dimensions : 1570, 1506, 2364420 (nrow, ncol, ncell)
## resolution : 30, 30 (x, y)
## extent : -22707.33, 22472.67, 221784.7, 268884.7 (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs
## data source : in memory
## names : layer
## values : -18, 40 (min, max)</code></pre>
<p>Now, let’s change the resolution from the initial 30m of Landsat 8, to 25m (‘downsampling’). For this purpose we use the <code>res</code> parameter:</p>
<pre class="r"><code>ndvi_ReprojETRS89_20m <- projectRaster(ndvi, res = 25, method = "ngb", crs = targetCRS)
print(ndvi_ReprojETRS89_20m)</code></pre>
<pre><code>## class : RasterLayer
## dimensions : 1882, 1805, 3397010 (nrow, ncol, ncell)
## resolution : 25, 25 (x, y)
## extent : -22682.33, 22442.67, 221809.7, 268859.7 (xmin, xmax, ymin, ymax)
## coord. ref. : +proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs
## data source : in memory
## names : layer
## values : -18, 40 (min, max)</code></pre>
<p>This concludes our exploration of the raster package for this post. Hope you find it useful! 😄 👍 👍</p>
</div>
</div>
<script>
// add bootstrap table styles to pandoc tables
function bootstrapStylePandocTables() {
$('tr.header').parent('thead').parent('table').addClass('table table-condensed');
}
$(document).ready(function () {
bootstrapStylePandocTables();
});
</script>
<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
(function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
document.getElementsByTagName("head")[0].appendChild(script);
})();
</script>
</body>
</html>