Rasterize um shapefile com Geopandas ou fiona - python


10

Eu preciso rasterizar um shapefile realmente simples um pouco como este http://tinyurl.com/odfbanu . O GDAL RasterizeLayer não queima todos os polígonos em Raster? mas eu queria saber se existe uma maneira de fazer isso usando Geopandas ou fiona e talvez rasterio para a parte da escrita tiff.

Portanto, meu objetivo é rasterizá-lo e atribuir um valor a cada polígono que compartilhe um valor em comum, LSAD na amostra.

Então, eu escrevi o início do código inspirado por shongololo no tópico: Dissolvendo polígonos com base em atributos com Python (bem torneado, fiona)? .

from geopandas import GeoDataFrame

name_in = 'cb_2013_us_county_20m.shp'

#Open the file with geopandas
counties = GeoDataFrame.from_file(name_in)

#Add a column to the Geodataframe containing the new value
for i in range (len(counties)):
    LSAD = counties.at[i,'LSAD']
    if LSAD == 00 :
        counties['LSAD_NUM'] == 'A'
    elif LSAD == 03 :
        counties['LSAD_NUM'] == 'B'
    elif LSAD == 04 :
        counties['LSAD_NUM'] == 'C'
    elif LSAD == 05 :
        counties['LSAD_NUM'] == 'D'
    elif LSAD == 06 :
        counties['LSAD_NUM'] == 'E'
    elif LSAD == 13 :
        counties['LSAD_NUM'] == 'F'
    elif LSAD == 15 :
        counties['LSAD_NUM'] == 'G'  
    elif LSAD == 25 :
        counties['LSAD_NUM'] == 'I'          
    else :
        counties['LSAD_NUM'] == 'NA'

Coisas muito fáceis, então agora estou me perguntando como posso realmente escrever essas formas em um tiff. Comecei a trabalhar com os Geopandas, pois acreditava que essa era a melhor opção, mas se você tiver uma sugestão de fiona, também concordo.

Encontrei um código da rasterio que parece capaz de pegar uma geometria bem torneada e gravá-la em uma nova raster http://tinyurl.com/op49uek

# I guess that my goal should be to load my list of geometries under geometry to be able to pass it to rasterio later on
geometry = {'type':'Polygon','coordinates':[[(2,2),(2,4.25),(4.25,4.25),(4.25,2),(2,2)]]}

with rasterio.drivers():
    result = rasterize([geometry], out_shape=(rows, cols))
    with rasterio.open(
            "test.tif",
            'w',
            driver='GTiff',
            width=cols,
            height=rows,
            count=1,
            dtype=numpy.uint8,
            nodata=0,
            transform=IDENTITY,
            crs={'init': "EPSG:4326"}) as out:
                 out.write_band(1, result.astype(numpy.uint8))

A resposta é sobre GDALrasterize, estou precisamente perguntando se alguém tem uma idéia de fazer a mesma coisa usando Geopandas e rasterio. Não é uma duplicata #
18181898198119

Encontrou um pedaço de código que pode ajudar, post editado
User18981898198119

Respostas:


19

Você está no caminho certo e as geopandas GeoDataFrame são uma boa opção para rasterização sobre Fiona. Fiona é um ótimo conjunto de ferramentas, mas acho que o DataFrame é mais adequado para arquivos de forma e geometrias do que dicionários aninhados.

import geopandas as gpd
import rasterio
from rasterio import features

Configure seus nomes de arquivos

shp_fn = 'cb_2013_us_county_20m.shp'
rst_fn = 'template_raster.tif'
out_fn = './rasterized.tif'

Abra o arquivo com GeoPANDAS read_file

counties = gpd.read_file(shp_fn)

Adicione a nova coluna (como no código acima)

for i in range (len(counties)):
    LSAD = counties.at[i,'LSAD']
    if LSAD == 00 :
        counties['LSAD_NUM'] == 'A'
    elif LSAD == 03 :
        counties['LSAD_NUM'] == 'B'
    elif LSAD == 04 :
        counties['LSAD_NUM'] == 'C'
    elif LSAD == 05 :
        counties['LSAD_NUM'] == 'D'
    elif LSAD == 06 :
        counties['LSAD_NUM'] == 'E'
    elif LSAD == 13 :
        counties['LSAD_NUM'] == 'F'
    elif LSAD == 15 :
        counties['LSAD_NUM'] == 'G'  
    elif LSAD == 25 :
        counties['LSAD_NUM'] == 'I'          
    else :
        counties['LSAD_NUM'] == 'NA'

Abra o arquivo raster que você deseja usar como modelo para gravar recursos usando o rasterio

rst = rasterio.open(rst_fn)

copie e atualize os metadados da varredura de entrada para a saída

meta = rst.meta.copy()
meta.update(compress='lzw')

Agora grave os recursos na varredura e escreva-os

with rasterio.open(out_fn, 'w+', **meta) as out:
    out_arr = out.read(1)

    # this is where we create a generator of geom, value pairs to use in rasterizing
    shapes = ((geom,value) for geom, value in zip(counties.geometry, counties.LSAD_NUM))

    burned = features.rasterize(shapes=shapes, fill=0, out=out_arr, transform=out.transform)
    out.write_band(1, burned)

A idéia geral é criar uma tupla iterável contendo (geometria, valor), em que a geometria é uma geometria bem torneada e o valor é o que você deseja gravar na varredura na localização dessa geometria. Tanto Fiona quanto GeoPANDAS usam geometrias bem torneadas, para que você tenha sorte lá. Neste exemplo, um gerador é usado para iterar através dos pares (geometria, valor) que foram extraídos do GeoDataFrame e unidos usando zip ().

Certifique-se de abrir o out_fnarquivo no w+modo, pois ele terá que ser usado para leitura e gravação.


1

O geocube é uma nova ferramenta projetada especificamente para rasterizar dados de geopandas que agrupam o rasterio. Simplifica o processo e elimina a necessidade de uma varredura de modelo.

https://github.com/corteva/geocube

No contexto do exemplo acima:

from geocube.api.core import make_geocube
import geopandas

counties = geopandas.read_file("zip://cb_2013_us_county_20m.zip/cb_2013_us_county_20m.shp")

A letra pode ser definida no quadro de dados da seguinte maneira:

counties["LSAD_LETTER"] = 'NA'
lsad_letter = counties.LSAD_LETTER.copy()
lsad_letter[counties.LSAD=='00'] = 'A'
lsad_letter[counties.LSAD=='03'] = 'B'
lsad_letter[counties.LSAD=='04'] = 'C'
lsad_letter[counties.LSAD=='05'] = 'D'
lsad_letter[counties.LSAD=='06'] = 'E'
lsad_letter[counties.LSAD=='13'] = 'F'
lsad_letter[counties.LSAD=='15'] = 'G'
lsad_letter[counties.LSAD=='25'] = 'I'
counties["LSAD_LETTER"] = lsad_letter

No entanto, apenas valores numéricos podem ser rasterizados. Aqui está um exemplo categórico: https://corteva.github.io/geocube/stable/examples/categorical.html

Portanto, em vez de usar isso, use os números no formato de sequência e converta em números inteiros:

counties["LSAD_NUM"] = counties.LSAD.astype(int)

Em seguida, rasterize os dados:

cube = make_geocube(
    counties,
    measurements=["LSAD_NUM"],
    resolution=(1, -1),
)

insira a descrição da imagem aqui

Por fim, exporte-o para uma varredura:

cube.LSAD_NUM.rio.to_raster("lsad_num.tif")
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.