Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 1 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Esta é sua última história gratuita somente para membros neste mês. Faça upgrade para acesso ilimitado. Python para geociências: fusão raster, recorte e reprojeção com rasterio Aprenda a executar a reprojeção raster, recorte e mesclagem usando o pacote rasterio para Python Maurício Cordeiro Seguir 3 de abril · 10 min de leitura Introduction Welcome back for the 5th part of this series. On the previous Python for Geosciences post (here), we learned how to work with bit masks provided by satellite imagery, 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 2 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... specifically for the case of the Landsat 8. It is very handful to mask undesired pixels or to select specific targets of interest. However, the pixel classification list of Level 2A processors is not always the same, or they are not reliable enough, and sometimes we need to use a mask provided by a different source/provider. Last week, for example, I had prepare some water masks from the Global Water Surface Dataset [1], available to download here, to superpose with data from 5 different Sentinel 2 tiles. The Global Water Surface Dataset is provided by the European Commission and "… maps the location and temporal distribution of water surfaces at the global scale over the past 3.6 decades, and provides statistics on their extent and change to support better informed water-management decisionmaking.". It is a very handful database for global water-related research. As I had only 5 sites to analyze, I decided to do all the pre-processing manually using QGIS. By preprocessing, I mean manually reprojecting, clipping and merging whenever necessary. However, all of this could be automated by using a package that we have already seen, the rasterio. With that in mind, I decided to write about it. Let’s go. Step 1- Understanding the problem First step is to understand the problem we are facing. Why should we care about these things if we already know how to open images and manipulate them as Arrays in Python? In the previous posts we used the MNDWI index to create a water masks for our raster image of the Amazon region. Now, suppose we want to compare our results with a map of occurrence of water, like the one provided by the Global Water Surface. Let’s start by downloading the reference maps from https://global-surfacewater.appspot.com/download. The data is available in tiles divided by 10 x 10 degrees of latitude and longitude. The area we are interested is divided in two different tiles, 70W 0N and 60W 0N. We can download them manually by clicking in the corresponding tile and selecting the occurrence link that appears just bellow (Figure 1). The two tiles we need are marked in orange. 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 3 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Figure 1: Global Water Surface Dataset grid. Source: https://global-surface-water.appspot.com/download Assim que tivermos baixado os dados, podemos abrir esses dois blocos usando rasterio . Além disso, também carregaremos nossa imagem usando load_landsat_image , definido nos posts anteriores, mas com uma modificação muito simples. Em vez de retornar apenas o dicionário de imagens com as bandas como matrizes, também retornaremos um dicionário com os conjuntos de dados (eles serão importantes na próxima seção). No final, usamos o subplots função de matplotlib para exibir as imagens carregadas recentemente. 1 from rasterio.warp import reproject 2 3 water1_reproj, water1_reproj_trans = reproject(source=rasterio.band(water1_ds, 1), 4 dst_crs=image_ds['B2'].profile['crs'], 5 dst_resolution=(30, 30) 6 ) 7 8 water2_reproj, water2_reproj_trans = reproject(source=rasterio.band(water2_ds, 1), 9 10 11 pfg_05_02.py hosted with ❤ by GitHub dst_crs=image_ds['B2'].profile['crs'], dst_resolution=(30, 30) ) view raw 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 4 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Saída de código. A primeira coisa que você deve ter notado é que os arrays têm tamanhos diferentes. Podemos imprimir as formas para verificar: imprimir (rgb.shape, water1_array.shape, water2_array.shape) resultado: (7761, 7601, 3) (40000, 40000) (40000, 40000) Então, como podemos sobrepor e comparar essas matrizes? Primeiro, precisamos entender por que eles parecem tão diferentes. Etapa 2 - Sistemas de referência de coordenadas Quando carregamos uma imagem raster de satélite em um array Python, é apenas isso, um array. Não temos informações sobre a localização desses pixels na Terra. Para isso, duas coisas são necessárias: • Um sistema (lógica) para mapear localizações terrestres em coordenadas (Sistema de Referência de Coordenadas); e • Algo que nos diga onde neste sistema de coordenadas está nosso array (GeoTransformation). A dará aqui uma breve introdução aos CRSs para entender como manipular corretamente nossa imagem raster, mas existem outros bons recursos online se você quiser se aprofundar neste assunto. 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 5 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Tipos de sistemas de referência de coordenadas Os sistemas de referência de coordenadas, ou sistemas de referência espacial, descrevem como podemos mapear a superfície da Terra em coordenadas. Existem dois tipos principais: • Sistemas de Coordenadas Geográficas: no GCS, o modelo matemático tenta combinar a posição na Terra aproximando a Terra de um elipsóide (qual dependerá do Datum que estamos usando). As coordenadas são então medidas em graus (latitude e longitude). Os GCS são os melhores para análise global (Figura 2-a), no entanto, as distâncias (e áreas) são distorcidas. Por exemplo, um grau de longitude ao redor do equador tem uma distância diferente de um grau de longitude próximo aos pólos; • Sistemas de Coordenadas Projetadas: o PCS representa a superfície da Terra (ou uma parte dela) em um sistema de coordenadas bidimensional plano (por exemplo, um pedaço de papel plano ou tela de computador). É necessário usar uma projeção de mapa, para transformar a Terra de sua forma esférica (3D) para uma forma plana (2D). Existem três tipos principais de projeções cartográficas que podem ser visualizadas na Figura 2-b (cilíndrica, plana e cônica). Eles foram concebidos para diferentes objetivos: preservar ângulos, preservar áreas, etc. Figura 2: (a) Exemplo de Sistema de Coordenadas Geográficas, fonte: ESRI; (b) exemplos de projeções de mapas, fonte: UCGIScience ( https://gistbok.ucgis.org/bok-topics/map-projections ) Embora nossos arrays carregados recentemente não tenham nenhuma informação 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 6 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... geográfica, vamos acessar o conjunto de dados carregado por rasterio . O atributo profile é um dicionário, e se o imprimirmos (abaixo), podemos ver que uma das chaves é "crs" (em negrito ). Observe que as máscaras de ocorrência de água têm = CRS.from_epsg(4326) e a imagem da paisagem tem CRS CRS = CRS.from_epsg(32620) . imprimir ('Imagem: \ n', imagem_ds ['B2']. perfil) print ('Máscara de água 1: \ n', water1_ds.profile) print ('Máscara de água 2: \ n', water2_ds.profile) Resultado: Imagem: {'driver': 'GTiff', 'dtype': 'uint16', 'nodata': 0,0, 'largura': 7601, 'altura': 7761, 'contagem': 1, 'crs': CRS.from_epsg (32620 ) , 'transformar': afim (30,0, 0,0, 658185,0, 0.0, -30.0, -203985.0), 'blockxsize': 256, 'blockysize': 256, 'tiled': True, 'compress': 'deflate', 'interleave': 'band'} Máscara de água 1: {'driver': 'GTiff', 'dtype': 'uint8', 'nodata': Nenhum, 'largura': 40000, 'altura': 40000, 'contagem': 1, 'crs': CRS.from_epsg (4326 ) , 'transformar': afim (0,00025, 0,0, -70,0, 0.0, -0.00025, 0.0), 'blockxsize': 256, 'blockysize': 256, 'tiled': True, 'compress': 'lzw', 'intercalar': 'band'} Máscara de água 2: {'driver': 'GTiff', 'dtype': 'uint8', 'nodata': Nenhum, 'largura': 40000, 'altura': 40000, 'contagem': 1, 'crs': CRS.from_epsg (4326 ) , 'transformar': afim (0,00025, 0,0, -60,0, 0.0, -0.00025, 0.0), 'blockxsize': 256, 'blockysize': 256, 'tiled': True, 'compress': 'lzw', 'intercalar': 'band'} Observe que temos apenas um código, chamado EPSG. EPSG é um registro público de sistemas de coordenadas e cada um recebe um número exclusivo (o código EPSG). Nota: EPSG significa European Petroleum Survey Group e é uma organização que mantém um banco de dados de parâmetros geodésicos com padrão códigos , os códigos EPSG (http://epsg.org) . Se pesquisarmos na base de dados ESPG (ou google), por esses códigos EPSG, podemos ter a descrição correta de cada CRS que temos. • EPSG 4326: WGS 84 Geographic • EPSG 32620: Zona UTM projetada 20N GeoTransformação 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 7 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Agora que sabemos quais são os CRSs de nossas imagens, ainda precisamos entender como podemos mapear os índices de array (linhas e colunas) para as coordenadas geográficas de nosso CRS. O método mais comum é considerar nosso array como uma grade regular com espaçamento uniforme (cada célula tem o mesmo tamanho). Nesse caso, basta saber a localização correta de uma célula (canto superior esquerdo) e as dimensões da célula (largura e altura) nas unidades CRS (graus para o CRS geográfico e metros para o UTM, por exemplo). O mapeamento do índice da matriz (linha, coluna) em coordenadas CRSs é feito pela GeoTransformation. Esta transformação também é armazenada no perfil do conjunto de dados e se a aplicarmos a qualquer posição do conjunto de dados, receberemos sua coordenada CRS correspondente. Vamos tentar aplicar a transformação de qualquer banda (B2, por exemplo) à primeira célula superior esquerda (0, 0) e à célula inferior direita (largura, altura). Para aplicá-lo, podemos usar AffineMatrix * (coluna, linha) e os resultados serão (x, y) nas unidades CRSs. Então, podemos comparar o resultado com as extensões do conjunto de dados e confirmar se eles correspondem perfeitamente. 1 # top left cell 2 first_cell = (0, 0) 3 # bottom right cell 4 last_cell = (image_ds['B2'].profile['width'], image_ds['B2'].profile['height']) 5 6 print(image_ds['B2'].profile['transform'] * first_cell) 7 print(image_ds['B2'].profile['transform'] * last_cell) 8 9 print(image_ds['B2'].bounds) pfg_05_03.py hosted with ❤ by GitHub view raw (658185.0, -203985.0) (886215.0, -436815.0) BoundingBox (left = 658185.0, bottom = -436815.0, right = 886215.0, top = -203985.0) Etapa 3 - Reprojetando Agora que sabemos como funciona um CRS, é fácil entender por que não podemos simplesmente comparar os pixels de nossos arrays. Portanto, a primeira coisa que 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 8 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... temos que fazer é converter os arrays para o mesmo CRS. À medida que vai querer sobrepor a máscara de água em nossa imagem Amazon, estamos indo para converter as máscaras de água a partir EPSG 4326 para EPSG 32620. Para realizar a reprojeção usaremos o Reproject função definida no warp módulo do RasterIO . Passaremos como argumentos, a fonte como um objeto de banda rasterio (essa é a razão da rasterio.band aplicada ao conjunto de dados), o CRS desejado e a resolução de destino. 1 from rasterio.warp import reproject 2 3 water1_reproj, water1_reproj_trans = reproject(source=rasterio.band(water1_ds, 1), 4 dst_crs=image_ds['B2'].profile['crs'], 5 dst_resolution=(30, 30) 6 ) 7 8 water2_reproj, water2_reproj_trans = reproject(source=rasterio.band(water2_ds, 1), 9 10 11 dst_crs=image_ds['B2'].profile['crs'], dst_resolution=(30, 30) ) pfg_05_02.py hosted with ❤ by GitHub view raw Step 4- Merging Para tornar as coisas um pouco mais difíceis aqui, nossa imagem do Landsat 8 está dividida em dois blocos de máscara de água diferentes, portanto, não podemos simplesmente cortar a parte que desejamos. Primeiro, mesclaremos as duas máscaras de água em uma. Isso será feito usando o merge função de rasterio.merge módulo. Nota: A função de mesclagem requer dois conjuntos de dados rasterio, no entanto, temos os arrays reprojetados (e as transformações) em seu lugar. Nesse caso, temos que gravar nossos resultados reprojetados em novos conjuntos de dados. Em vez de gravar novos conjuntos de dados no arquivo, usaremos a rasterio.io.MemoryFile classe para manter tudo na memória. Vamos definir uma create_dataset função que receberá o array, o CRS e a transformação para retornar um na memória rasterio conjunto de dados . Depois de termos nossos conjuntos de dados, seremos capazes de chamar a função de mesclagem. No final, exibiremos a máscara de água mesclada. 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 9 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... 1 from rasterio.io import MemoryFile 2 from rasterio.merge import merge 3 4 def create_dataset(data, crs, transform): 5 # Receives a 2D array, a transform and a crs to create a rasterio dataset 6 memfile = MemoryFile() 7 dataset = memfile.open(driver='GTiff', height=data.shape[0], width=data.shape[1], count 8 9 transform=transform, dtype=data.dtype) dataset.write(data, 1) 10 11 return dataset 12 13 water1_reproj_ds = create_dataset(water1_reproj[0], image_ds['B2'].profile['crs'], water1_reproj_tra 14 water2_reproj_ds = create_dataset(water2_reproj[0], image_ds['B2'].profile['crs'], water2_reproj_tra 15 16 merged, transf = merge([water1_reproj_ds, water2_reproj_ds]) 17 18 plt.figure(figsize=(12,6)) 19 plt.imshow(merged[0]) pfg_05_04.py hosted with ❤ by GitHub view raw Saída de código. A fusão parece ter funcionado bem, mas a questão é: Onde fica a nossa Área de Interesse (região de Manaus) aqui? 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 10 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Etapa 5 - Corte Agora que temos um grande mapa de ocorrência de água mesclado no mesmo CRS da imagem do Landsat 8, é hora de recortá-lo usando as extensões de nossa Área de Interesse. Portanto, a primeira coisa a fazer agora é obter as extensões da imagem Landsat. Vimos na Etapa 2 que podemos usar o método bounds para obter as extensões de um conjunto de dados. No entanto, o método de cultivo de rasterio recebe um GeoJSON como entrada para fazer o cultivo. Podemos extrair o GeoJSON de nossa imagem landsat fazendo isso usando a rasterio.features.shapes função , que nos retornará um GeoJSON das formas que ele identifica em um determinado conjunto de dados. Como queremos a imagem inteira, podemos passar apenas um array vazio com o mesmo formato da imagem Landsat. O truque aqui é que as funções de formas retornam um “gerador”, que nos permite iterar os vários resultados. Não entraremos em detalhes de rendimento e geradores aqui, mas como esperamos apenas uma saída (o polígono com a imagem inteira), usaremos a incorporada do python próxima função , assim: próximo (formas (np.zeros_like (img ['B2']), transformar = imagem_ds ['B2']. perfil ['transformar'])) Resultados: ({'tipo': 'Polígono', 'coordenadas': [[(658185.0, -203985.0), (658185.0, -436815.0), (886215.0, -436815.0), (886215.0, -203985.0), (658185.0, -203985.0)]]}, 0.0) Como antes, a corte função de requer um conjunto de dados como argumento, então vamos criar o merged_ds antecipadamente. Em seguida, recortaremos as extensões que acabamos de recuperar e exibiremos os resultados. 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 11 de 16 1 from rasterio.features import shapes 2 from rasterio.mask import mask https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... 3 4 # get the extents of the Landsat image as a geometry 5 extents, _ = next(shapes(np.zeros_like(img['B2']), transform=image_ds['B2'].profile['transform' 6 7 # create a dataset of the merged water mask 8 merged_ds = create_dataset(merged[0], image_ds['B2'].profile['crs'], transf) 9 10 # creop the water mask to the Landsat extents 11 cropped, crop_transf = mask(merged_ds, [extents], crop=True) 12 13 plt.figure(figsize=(10, 10)) 14 plt.imshow(cropped[0]) pfg_05_05.py hosted with ❤ by GitHub view raw 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 12 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Saída de código. Etapa 6 - Superpondo as camadas Em novo, podemos usar a plot_masked_rgb função definida na parte 4 para sobrepor a máscara com toda a imagem Landsat. Um pequeno ajuste é necessário no cropped forma, como a do rasterio máscara função de retornou pixel adicional que pode ser descartado. Como a máscara de ocorrência de água da Água de Superfície Global é fornecida em uma escala de 0-100 (100 significa 100% de ocorrência de água ao longo do tempo) e esperamos uma máscara VERDADEIRO / FALSO, podemos usar o operando lógico maior do que para obter os pixels com mais de 80% de água: mask = cropped[0]>80 . Além disso, iremos ignorar os pixels que não têm valor na imagem Landsat, através do comando mask = np.where(img[‘B2’] == 0, 0, mask) e traçar nossa suposta imagem, como de costume. 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 13 de 16 1 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... def plot_masked_rgb(red, green, blue, mask, color_mask=(1, 0, 0), transparency=0.5, brightness 2 3 # to improve our visualization, we will increase the brightness of our values 4 max_value = np.max([red.max(), green.max(), blue.max()]) 5 red = red / max_value * brightness 6 green = green / max_value * brightness 7 blue = blue / max_value * brightness 8 9 red = np.where(mask==True, red*transparency+color_mask[0]*(1-transparency), red) 10 green = np.where(mask==True, green*transparency+color_mask[1]*(1-transparency), green 11 blue = np.where(mask==True, blue*transparency+color_mask[2]*(1-transparency), blue) 12 13 rgb = np.stack([red, green, blue], axis=2) 14 15 return rgb 16 17 mask = cropped[0]>80 18 shape = img['B2'].shape 19 mask = mask[:shape[0], :shape[1]] 20 21 mask = np.where(img['B2'] == 0, 0, mask) 22 23 masked_rgb = plot_masked_rgb(red=img['B4'], 24 green=img['B3'], 25 blue=img['B2'], 26 mask=mask, 27 color_mask=(0, 0, 1), 28 brightness=3 29 ) 30 31 plt.figure(figsize=(15, 15)) 32 plt.imshow(masked_rgb) pfg_05_06.py hosted with ❤ by GitHub view raw 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 14 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Saída de código. Caderno Como sempre, aqui está o bloco de notas com todo o código necessário para executar esses exemplos. Sign up for Analytics Vidhya News Bytes By Analytics Vidhya 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 15 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... Latest news from Analytics Vidhya on our Hackathons and some of our best articles! Take a look. Get this newsletter Python Programming About Write Geoscience Emails will be sent to geografianoturnolaranjeiras@gmail.com. Not you? Remote Sensing Satellite Imagery Tutorial Help Legal Get the Medium app pfg_05_nb01.ipynb hosted with ❤ by GitHub view raw Conclusão Hoje, apresentamos uma visão geral de alguns conceitos importantes sobre dados geoespaciais, como Sistemas de Referência de Coordenadas e GeoTransforms e como mover de um sistema para outro usando o rasterio pacote . As tarefas de reprojetar, recortar e mesclar geralmente são necessárias quando precisamos comparar dados salvos em diferentes sistemas de projeção. Eles podem ser feitos usando software GIS como QGIS ou ArcGIS da ESRI, mas com Python é possível escrever uma cadeia para automatizar essas etapas. Hope you have enjoyed, stay tuned and see you next story! Previous Posts Link for the previous posts of Python for Geosciences series: 07/10/2021, 08:55 Mesclagem, recorte e reprojeção de raster | Analytics Vidhya 16 de 16 https://medium.com/analytics-vidhya/python-for-geosciences-raster-mer... • Part 1- Working with Satellite Images • Part 2- Satellite Image Analysis • Part 3- Spectral Analysis • Part 4- Raster bit masks explained • Part 6- Scatter plots and PDF Reports References: [1] Pekel, J.-F.; Cottam, A.; Gorelick, N.; Belward, A. S. High-Resolution Mapping of Global Surface Water and Its Long-Term Changes. Nature 2016, 540 (7633), 418–422. https://doi.org/10.1038/nature20584. 07/10/2021, 08:55