Rasteriza un shapefile con Geopandas o fiona - python

10

Necesito rasterizar un archivo de formas realmente simple un poco como este http://tinyurl.com/odfbanu . ¿Qué es solo un archivo de forma que contiene condados en los EE. UU. He visto esta respuesta anterior: GDAL RasterizeLayer no quema todos los polígonos en Raster? pero me preguntaba si hay una manera de hacerlo usando Geopandas o fiona y tal vez rasterio para la parte de escritura de tiff.

Entonces, mi objetivo es rasterizarlo y asignar un valor a cada polígono que comparta un valor común, LSAD en el ejemplo.

Entonces escribí el comienzo del código inspirado en shongololo en el hilo: ¿ Disolver polígonos basados ​​en atributos con Python (bien proporcionado, 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'

Cosas realmente fáciles, así que ahora me pregunto cómo puedo escribir esas formas en un tiff. Comencé a trabajar con Geopandas porque creía que era la mejor opción, pero si tienes una sugerencia de fiona, también estoy dispuesta.

Encontré un fragmento de código de rasterio que parece ser capaz de tomar una geometría bien formada y grabarlo en un nuevo ráster 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))
Usuario18981898198119
fuente
La respuesta es sobre GDALrasterize, precisamente estoy preguntando si alguien tiene una idea sobre hacer lo mismo usando Geopandas y rasterio. No es un duplicado
Usuario18981898198119
Encontré un fragmento de código que podría ayudar, publicación editada
usuario18981898198119

Respuestas:

19

Estás en el camino correcto y GeoDataFrame es una buena opción para rasterizar sobre Fiona. Fiona es un gran conjunto de herramientas, pero creo que el DataFrame se adapta mejor a los shapefiles y geometrías que los diccionarios anidados.

import geopandas as gpd
import rasterio
from rasterio import features

Configura tus nombres de archivo

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

Abra el archivo con GeoPANDAS read_file

counties = gpd.read_file(shp_fn)

Agregue la nueva columna (como en su código anterior)

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 el archivo ráster que desea usar como plantilla para grabar características usando rasterio

rst = rasterio.open(rst_fn)

copie y actualice los metadatos del ráster de entrada para la salida

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

Ahora grabe las características en el ráster y escríbalas

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)

La idea general es crear una iteración que contenga tuplas de (geometría, valor), donde la geometría es una geometría bien formada y el valor es lo que desea grabar en el ráster en la ubicación de esa geometría. Tanto Fiona como GeoPANDAS usan geometrías bien formadas para que tengas suerte allí. En este ejemplo, se usa un generador para iterar a través de los pares (geometría, valor) que se extrajeron del GeoDataFrame y se unieron mediante zip ().

Asegúrese de abrir el out_fnarchivo en w+modo, ya que tendrá que usarse para leer y escribir.

Michael Lindgren
fuente
1

geocube es una nueva herramienta diseñada específicamente para rasterizar datos de geopandas que envuelve rasterio. Simplifica el proceso y elimina la necesidad de un ráster de plantilla.

https://github.com/corteva/geocube

En el contexto del ejemplo anterior:

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")

La letra se puede establecer en el marco de datos de la siguiente manera:

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

Sin embargo, solo los valores numéricos se pueden rasterizar. Aquí hay un ejemplo categórico: https://corteva.github.io/geocube/stable/examples/categorical.html

Entonces, en lugar de usar eso, use los números en formato de cadena y conviértalos a enteros:

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

Luego, rasterice los datos:

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

ingrese la descripción de la imagen aquí

Por último, expórtelo a una trama:

cube.LSAD_NUM.rio.to_raster("lsad_num.tif")
muñeco de nieve2
fuente