# Definición de ruta de archivos, bandas S2B de 10 m de
# resolución espacial
lista.tif_10 <- paste0("Sentinel2/S2B_MSIL1C_20180124T152629_N0206_R025_T18NWL_20180124T202241.SAFE/GRANULE/L1C_T18NWL_A004627_20180124T152632/RT_T18NWL_20180124T152629_B0",
c(2, 3, 4, 8), ".tif")
# Cargue de bandas
bog_S2_10 <- stack(lista.tif_10)
# Cambio de nombre de las bandas espectrales
names(bog_S2_10) <- c("Blue", "Green", "Red", "NIR")
bog_S2_10
## class : RasterStack
## dimensions : 10980, 10980, 120560400, 4 (nrow, ncol, ncell, nlayers)
## resolution : 10, 10 (x, y)
## extent : 499980, 609780, 490200, 6e+05 (xmin, xmax, ymin, ymax)
## crs : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names : Blue, Green, Red, NIR
# Ahora para la imagen Landsat 5
lista.tif_30 <- paste0("Landsat/LT04_L1TP_008057_19880322_20170209_01_T1_B",
c(1, 2, 3, 4, 5, 7), ".tif")
bog_land_30 <- stack(lista.tif_30)
names(bog_land_30) <- c("Azul", "Verde", "Rojo", "NIR", "SWIR1",
"SWIR2")
bog_land_30
## class : RasterStack
## dimensions : 6901, 7731, 53351631, 6 (nrow, ncol, ncell, nlayers)
## resolution : 30, 30 (x, y)
## extent : 441585, 673515, 374685, 581715 (xmin, xmax, ymin, ymax)
## crs : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names : Azul, Verde, Rojo, NIR, SWIR1, SWIR2
## min values : 0, 0, 0, 0, 0, 0
## max values : 255, 255, 255, 255, 255, 255
mtlfile <- "Landsat/LT04_L1TP_008057_19880322_20170209_01_T1_MTL.txt"
metadata <- readMeta(mtlfile)
lsat <- stackMeta(metadata)
lsat_cal <- radCor(lsat, metaData = metadata, method = "apref")
lsat_cal <- dropLayer(lsat_cal, 6)
writeRaster(lsat_cal, "lsat_cal_1988.tif", overwrite = TRUE)
names(lsat_cal) <- c("Blue", "Green", "Red", "NIR", "SWIR1",
"SWIR2")
lsat_cal
## class : RasterStack
## dimensions : 6901, 7731, 53351631, 6 (nrow, ncol, ncell, nlayers)
## resolution : 30, 30 (x, y)
## extent : 441585, 673515, 374685, 581715 (xmin, xmax, ymin, ymax)
## crs : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## names : Blue, Green, Red, NIR, SWIR1, SWIR2
## min values : 0, 0, 0, 0, 0, 0
## max values : 0.3423814, 0.7435552, 0.6653971, 0.8323059, 0.5509866, 0.7758228
plotRGB(bog_S2_10, r = 3, g = 2, b = 1, axes = T, stretch = "lin",
main = "Composición Color natural Sentinel 2B 2018")
plotRGB(bog_S2_10, r = 4, g = 3, b = 2, axes = T, stretch = "lin",
main = "Composición Falso Color Sentinel 2B 2018")
plotRGB(lsat_cal, r = 3, g = 2, b = 1, axes = T, stretch = "lin",
main = "Composición Color natural Landsat 1988")
plotRGB(lsat_cal, r = 4, g = 3, b = 2, axes = T, stretch = "lin",
main = "Composición Color natural Landsat 1988")
pairs(bog_S2_10, main = "Análisis multi-banda Sentinel 2B")
lsat88 <- stack("D:/Magister en Geomática/2 Semestre/Percepción Remota Avanzada Teoría/Primera Entrega/lsat_cal_1988.tif")
names(lsat88) <- c("Azul", "Verde", "Rojo", "NIR", "SWIR1", "SWIR2")
pairs(lsat88, main = "Análisis multi-banda Landsat")
` ## Recorte del área de estudio
## Llamada del Shapefile
reserva_lim <- shapefile("Shapefiles/Reserva")
## Reproyección para que las coordenadas de ambos archivos
## coincidan para el recorte
reserva_proj <- spTransform(reserva_lim, crs(bog_S2_10))
## Raster recortado
reserva_10_clip <- crop(bog_S2_10, extent(reserva_proj))
## Raster recortado con shapefile como máscara
reserva_10_mask <- mask(reserva_10_clip, reserva_proj)
plotRGB(reserva_10_mask, main = "Imagen Sentinel 2B 2018 con máscara de shapefile = 10 m",
r = 3, g = 2, b = 1, stretch = "lin", axes = TRUE, ylab = "Norte",
xlab = "Este")
## Ahora con la imagen Landsat 5
reserva_30_clip <- crop(lsat_cal, extent(reserva_proj))
reserva_30_mask <- mask(reserva_30_clip, reserva_proj)
plotRGB(reserva_30_mask, main = "Imagen Landsat 5 1988 con máscara de shapefile = 30 m",
r = 3, g = 2, b = 1, stretch = "lin", axes = TRUE, ylab = "Norte",
xlab = "Este")
ndvi <- (reserva_10_mask[["NIR"]] - reserva_10_mask[["Red"]])/(reserva_10_mask[["NIR"]] +
reserva_10_mask[["Red"]])
nr <- getValues(ndvi) ## Conversión de raster a vector/matriz
str(nr)
## num [1:980100] 0.715 0.713 0.697 0.679 0.701 ...
set.seed(99)
# 10 clusters, 500 iteraciones, empezar con 5 sets aleatorios
# utilizando el método Lloyd
kmcluster <- kmeans(na.omit(nr), centers = 12, iter.max = 500,
nstart = 5, algorithm = "Lloyd")
str(kmcluster)
## List of 9
## $ cluster : int [1:980100] 11 11 11 11 11 11 11 12 12 11 ...
## $ centers : num [1:12, 1] 0.1519 0.0774 0.4875 0.4055 0.2372 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:12] "1" "2" "3" "4" ...
## .. ..$ : NULL
## $ totss : num 72257
## $ withinss : num [1:12] 37.5 39.1 37.5 39 39.5 ...
## $ tot.withinss: num 484
## $ betweenss : num 71774
## $ size : int [1:12] 70026 95335 69382 67553 69045 80042 66696 79412 63419 113225 ...
## $ iter : int 305
## $ ifault : NULL
## - attr(*, "class")= chr "kmeans"
# objetos NDVI para establecer los valores de cluster en un
# nuevo raster
knr <- raster(ndvi)
values(knr) <- kmcluster$cluster
knr
## class : RasterLayer
## dimensions : 990, 990, 980100 (nrow, ncol, ncell)
## resolution : 10, 10 (x, y)
## extent : 598020, 607920, 522600, 532500 (xmin, xmax, ymin, ymax)
## crs : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## source : memory
## names : layer
## values : 1, 12 (min, max)
mycolor <- c("#006633", "#333333", "#00ff00", "#00ffff", "#0000ff",
"#ff3399", "#ff9900") #(1-Zonas naturales, 2-vías, 3-pastos, 4-agua artificial, 5-cuerpos de agua, 6-cultivos, 7-Zonas artificiales)
plot(ndvi, col = rev(terrain.colors(7)), main = "Sentinel 2B-NDVI")
plot(knr, main = "Clasificación No Supervisada Sentinel 2b",
col = mycolor)
ndvi88 <- (reserva_30_mask[["NIR"]] - reserva_30_mask[["Red"]])/(reserva_30_mask[["NIR"]] +
reserva_30_mask[["Red"]])
nr88 <- getValues(ndvi88) ## Conversión de raster a vector/matriz
str(nr88)
## num [1:108900] 0.503 0.441 0.434 0.496 0.503 ...
set.seed(99)
# 10 clusters, 500 iteraciones, empezar con 5 sets aleatorios
# utilizando el método Lloyd
kmcluster88 <- kmeans(na.omit(nr88), centers = 12, iter.max = 500,
nstart = 5, algorithm = "Lloyd")
str(kmcluster88)
## List of 9
## $ cluster : int [1:108900] 3 4 4 3 3 4 4 4 11 4 ...
## $ centers : num [1:12, 1] 0.367 0.73 0.518 0.42 0.612 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:12] "1" "2" "3" "4" ...
## .. ..$ : NULL
## $ totss : num 2650
## $ withinss : num [1:12] 2.08 3.45 2.4 2.26 2.82 ...
## $ tot.withinss: num 32.6
## $ betweenss : num 2618
## $ size : int [1:12] 8310 6351 13380 10242 13154 6050 10291 6968 14241 3305 ...
## $ iter : int 76
## $ ifault : NULL
## - attr(*, "class")= chr "kmeans"
# objetos NDVI para establecer los valores de cluster en un
# nuevo raster
knr88 <- raster(ndvi88)
values(knr88) <- kmcluster88$cluster
knr88
## class : RasterLayer
## dimensions : 330, 330, 108900 (nrow, ncol, ncell)
## resolution : 30, 30 (x, y)
## extent : 598005, 607905, 522615, 532515 (xmin, xmax, ymin, ymax)
## crs : +proj=utm +zone=18 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
## source : memory
## names : layer
## values : 1, 12 (min, max)
mycolor <- c("#006633", "#333333", "#00ff00", "#00ffff", "#0000ff",
"#ff3399", "#ff9900") #(1-Zonas naturales, 2-vías, 3-pastos, 4-agua artificial, 5-cuerpos de agua, 6-cultivos, 7-Zonas artificiales)
plot(ndvi88, col = rev(terrain.colors(7)), main = "Landsat 5-NDVI")
plot(knr88, main = "Clasificación No Supervisada Landsat 5",
col = mycolor)
mycolor1 <- c("#00ff00", "#ff3399", "#006633", "#ff9900", "#0000ff") # Pastos, Cultivos, Z. naturales, Z. artificiales, Agua
mycolor2 <- c("#0000ff", "#ff9900", "#ff3399", "#00ff00", "#333333",
"#006633")
reclas2018 <- stack("CNSR_S2b.tif")
reclas1988 <- stack("CNSR_lsat.tif")
TVDH_lim <- shapefile("Shapefiles/TVDH")
TVDH_proj <- spTransform(TVDH_lim, crs(reclas2018))
TVDH_2018_clip <- crop(reclas2018, extent(TVDH_proj))
TVDH_2018_mask <- mask(TVDH_2018_clip, TVDH_proj)
TVDH_proj <- spTransform(TVDH_lim, crs(reclas1988))
TVDH_1988_clip <- crop(reclas1988, extent(TVDH_proj))
TVDH_1988_mask <- mask(TVDH_1988_clip, TVDH_proj)
plot(TVDH_1988_mask, main = "Reclasificación Landsat 5 1988",
col = mycolor1)
plot(TVDH_2018_mask, main = "Reclasificación Sentinel 2b 2018",
col = mycolor2)
nlcd <- brick("D:/Magister en Geomática/2 Semestre/Percepción Remota Avanzada Teoría/Primera Entrega/CSReserva2.tif")
names(nlcd) <- "Clasificacion2018"
nlcdclass <- c("Cuerpos de agua", "Pastos", "Zonas artificiales",
"Cultivos", "Zonas naturales", "Agua artifical", "Vías")
classdf <- data.frame(classvalue1 = c(1, 3, 6, 8, 14, 16, 26),
classnames1 = nlcdclass)
classcolor <- c("#0000ff", "#00ff00", "#ff9900", "#ff3399", "#006633",
"#0000ff", "#333333")
# Now we ratify (RAT = 'Raster Attribute Table') the ncld2011
# (define RasterLayer as a categorical variable). This is
# helpful for plotting.
cobTVDH <- nlcd[[1]]
cobTVDH <- ratify(cobTVDH)
rat <- levels(cobTVDH)[[1]]
rat$landcover <- nlcdclass
levels(cobTVDH) <- rat
set.seed(99)
# Sampling
samp2018 <- sampleStratified(cobTVDH, size = 200, na.rm = TRUE,
sp = TRUE)
table(samp2018$Clasificacion2018)
##
## 1 3 6 8 14 16 26
## 200 200 200 200 200 200 200
plt <- levelplot(cobTVDH, col.regions = classcolor, main = "Distribución de sitios de entrenamiento 2018")
print(plt + layer(sp.points(samp2018, pch = 3, cex = 0.5, col = 1)))
nlcd88 <- brick("D:/Magister en Geomática/2 Semestre/Percepción Remota Avanzada Teoría/Primera Entrega/CS88.tif")
names(nlcd88) <- "Clasificacion1998"
nlcd88class <- c("Zonas naturales", "Zonas artificiales", "Cultivos",
"Pastos", "Cuerpos de agua")
class88df <- data.frame(classvalue1 = c(17, 21, 24, 29, 40),
classnames1 = nlcd88class)
classcolor1 <- c("#006633", "#ff9900", "#ff3399", "#00ff00",
"#0000ff")
# Now we ratify (RAT = 'Raster Attribute Table') the ncld2011
# (define RasterLayer as a categorical variable). This is
# helpful for plotting.
cobTVDH88 <- nlcd88[[1]]
cobTVDH88 <- ratify(cobTVDH88)
rat88 <- levels(cobTVDH88)[[1]]
rat88$landcover <- nlcd88class
levels(cobTVDH88) <- rat88
set.seed(99)
# Muestreo
samp1988 <- sampleStratified(cobTVDH88, size = 200, na.rm = TRUE,
sp = TRUE)
table(samp1988$Clasificacion1998)
##
## 17 21 24 29 40
## 200 200 200 200 200
plt <- levelplot(cobTVDH88, col.regions = classcolor1, main = "Distribución de sitios de entrenamiento 1988")
print(plt + layer(sp.points(samp1988, pch = 3, cex = 0.5, col = 1)))
A partir de aquí se muestran conjuntamente Landsat y Sentinel
# Extract the layer values for the locations
sampvals <- extract(reserva_10_mask, samp2018, df = TRUE)
# sampvals no longer has the spatial information. To keep the
# spatial information you use `sp=TRUE` argument in the
# `extract` function. drop the ID column
sampvals <- sampvals[, -1]
# combine the class information with extracted values
sampdata <- data.frame(classvalue = samp2018@data$Clasificacion2018,
sampvals)
sampvals88 <- extract(reserva_30_mask, samp1988, df = TRUE)
sampvals88 <- sampvals88[, -1]
sampdata88 <- data.frame(classvalue = samp1988@data$Clasificacion1998,
sampvals88)
# Train the model
cart <- rpart(as.factor(classvalue) ~ ., data = sampdata, method = "class",
minsplit = 5)
# print(model.class) Plot the trained classification tree
plot(cart, uniform = TRUE, main = "Árbol de clasificación 2018")
text(cart, cex = 0.8)
# Train the model
cart88 <- rpart(as.factor(classvalue) ~ ., data = sampdata88,
method = "class", minsplit = 5)
plot(cart88, uniform = TRUE, main = "Árbol de clasificación 1988")
text(cart88, cex = 0.8)
pr2019 <- predict(reserva_10_mask, cart, type = "class")
pr2019 <- ratify(pr2019)
rat <- levels(pr2019)[[1]]
rat$legend <- classdf$classnames1
levels(pr2019) <- rat
levelplot(pr2019, maxpixels = 1e+06, col.regions = classcolor,
scales = list(draw = FALSE), main = "Decisión de árbol de clasificación para sentinel 2B 2018")
mycolor3 <- c("#006633", "#ff9900", "#ff3399", "#00ff00", "#0000ff")
pr2019_88 <- predict(reserva_30_mask, cart88, type = "class")
pr2019_88 <- ratify(pr2019_88)
rat88 <- levels(pr2019_88)[[1]]
rat88$legend <- class88df$classnames1
levels(pr2019_88) <- rat88
levelplot(pr2019_88, maxpixels = 1e+06, col.regions = mycolor3,
scales = list(draw = FALSE), main = "Decisión de árbol de clasificación para Landsat 1988")
TVDH_lim <- shapefile("Shapefiles/TVDH")
TVDH_proj_2 <- spTransform(TVDH_lim, crs(reserva_10_mask))
TVDH_pr2019_clip <- crop(pr2019, extent(TVDH_proj_2))
TVDH_pr2019_mask <- mask(TVDH_pr2019_clip, TVDH_proj_2)
TVDH_proj_3 <- spTransform(TVDH_lim, crs(reserva_30_mask))
TVDH_pr2019_88_clip <- crop(pr2019_88, extent(TVDH_proj_3))
TVDH_pr2019_88_mask <- mask(TVDH_pr2019_88_clip, TVDH_proj_3)
par(mfrow = c(1, 2))
plot(TVDH_pr2019_mask, main = "Clasificación Supervisada 2018",
col = mycolor1)
plot(TVDH_pr2019_88_mask, main = "Clasificación Supervisada 1988",
col = mycolor2)
## K-fold cross validation
set.seed(99)
j <- kfold(sampdata, k = 5, by = sampdata$classvalue)
table(j)
## j
## 1 2 3 4 5
## 280 280 280 280 280
x <- list()
for (k in 1:5) {
train <- sampdata[j != k, ]
test <- sampdata[j == k, ]
cart <- rpart(as.factor(classvalue) ~ ., data = train, method = "class",
minsplit = 5)
pclass <- predict(cart, test, type = "class")
# crear data.frame usando la referencia y predicción
x[[k]] <- cbind(test$classvalue, as.integer(pclass))
}
# Combinación de las cinco listas de elementos en un
# data.frame
y <- do.call(rbind, x)
y <- data.frame(y)
colnames(y) <- c("observed", "predicted")
conmat <- table(y)
# change the name of the classes
colnames(conmat) <- classdf$classnames
rownames(conmat) <- classdf$classnames
conmat
## predicted
## observed Cuerpos de agua Pastos Zonas artificiales Cultivos
## Cuerpos de agua 184 0 2 6
## Pastos 0 159 0 30
## Zonas artificiales 3 4 151 10
## Cultivos 12 61 14 60
## Zonas naturales 0 1 0 7
## Agua artifical 10 0 8 0
## Vías 0 3 19 14
## predicted
## observed Zonas naturales Agua artifical Vías
## Cuerpos de agua 0 8 0
## Pastos 0 0 11
## Zonas artificiales 7 10 15
## Cultivos 30 1 22
## Zonas naturales 192 0 0
## Agua artifical 0 182 0
## Vías 0 0 164
n <- sum(conmat)
n
## [1] 1400
# número de casos correctamente clasificados por clases
diag <- diag(conmat)
# Overall Accuracy
OA <- sum(diag)/n
OA
## [1] 0.78
## Kappa
# Casos de verdad observada por clases
rowsums <- apply(conmat, 1, sum)
p <- rowsums/n
# Casos predichos por clase
colsums <- apply(conmat, 2, sum)
q <- colsums/n
expAccuracy <- sum(p * q)
kappa <- (OA - expAccuracy)/(1 - expAccuracy)
kappa
## [1] 0.7433333
# Exactitud del productor
PA <- diag/colsums
# User accuracy
UA <- diag/rowsums
outAcc <- data.frame(producerAccuracy = PA, userAccuracy = UA)
outAcc
## producerAccuracy userAccuracy
## Cuerpos de agua 0.8803828 0.920
## Pastos 0.6973684 0.795
## Zonas artificiales 0.7783505 0.755
## Cultivos 0.4724409 0.300
## Zonas naturales 0.8384279 0.960
## Agua artifical 0.9054726 0.910
## Vías 0.7735849 0.820
Los resultados presentados en este informe son de carácter académico y aún requieren de otras consideraciones para aproximarse a la realidad de la reserva TVDH. El uso de otros algoritmos de clasificación como el Random Forest o Redes Neuronales, complementados con herramientas más sólidas de series de tiempo y el uso de la reflectancia del fondo de la atmòsfera (BOA), pueden representar un mejor escenario de la reserva para delinear las estrategias que debe seguir la ciudad para la protección de este espacio natural, estratégico e importante para el ambiente de la ciudad de Bogotá y la calidad de vida de sus habitantes. Protejan la Reserva Thomas van der Hammen
Fundación Humedales de Bogotá (2016). Reserva Thomás Van der Hammen, hogar de aves y naturaleza que un alcalde llama potrero. Tomado de: ´` = Problemática van der Hammen
Pérez P. A. (2000). La estructura ecológica principal de la sabana de Bogotá. Sociedad Geográfica de Colombia. Academia de Ciencias Geográficas. 1 – 37.
Reserva Forestal del Norte de Bogotá (2017). Descripción de la Reserva. Tomado de: Reserva TVDH
Torres N., Angélica & Niño M. Freddy (2018). Los servicios ecosistémicos provistos por la reserva. Análisis de los tres escenarios. En: Bogotá y la reserva Thomas van der Hammen. Aportes desde la Economía Ecológica (Villamil et al. 2018). Instituto de Estudios Ambientales y Universidad Nacional de Colombia. 80 pp.