Dallo shapefile zippato caricato a Geopandas DataFrame nell'applicazione Django

Jan 05 2021

Sto cercando di arrivare a un punto in cui posso filtrare rapidamente migliaia di punti in uno shapefile. La mia applicazione Django chiede uno shapefile zip da caricare, in cui il file zip contiene almeno i .shp, .shxe .dbffile. Una volta nella mia vista Django, il file zip è il seguente:

request.FILES['file'] > <InMemoryUploadedFile: test.zip (application/x-zip-compressed)>

type(request.FILES['file']) > <class 'django.core.files.uploadedfile.InMemoryUploadedFile'>

request.FILES['file'].file > <_io.BytesIO object at 0x0000028E29F8FE00>

Supponendo che Geopandas sia l'opzione migliore per un filtraggio / mascheramento efficiente (se sbaglio, sono decisamente aperto ai suggerimenti), non sono sicuro di come passare dallo stato corrente a un DataFrame Geopandas. Quando provo a usare il read_file()metodo

import geopandas as gpd
gpd.read_file(request.FILES['file'].file)

Ottengo il seguente errore:

fiona.errors.DriverError: no driver

I geopandas.read_file() documenti affermano:

Il percorso assoluto o relativo del file o dell'URL da aprire o qualsiasi oggetto con un read()metodo (come un file aperto o StringIO)

Non sono sicuro di come ottenere ciò che ho in un formato appropriato per il read_file()metodo.

Nota: il mascheramento e il filtro che sto cercando di eseguire sono sui dati degli attributi e non sulla geometria.

Risposte

4 user2856 Jan 05 2021 at 06:19

Puoi usare fiona.io.ZipMemoryFilee gpd.GeoDataFrame.from_features.

Esempio:

import geopandas as gpd
import io
from fiona.io import ZipMemoryFile

# Just to create a BytesIO object for the demo,
# similar to your request.FILES['file'].file
zipshp = io.BytesIO(open('test.zip', 'rb').read())

with (ZipMemoryFile(zipshp)) as memfile:
    with memfile.open() as src:
        crs = src.crs
        gdf = gpd.GeoDataFrame.from_features(src, crs=crs)
        print(gdf.head())

Nota, originariamente non includevo il, BytesCollectioncome ha dichiarato lo sviluppatore Fiona in un commento alla mia precedente risposta, che la classe sarebbe probabilmente deprecata. Tuttavia, se lo usi, non dovresti averne bisogno ZipMemoryFile. Questo funziona per me:

import geopandas as gpd
import io
import fiona


zipshp = io.BytesIO(open('test.zip', 'rb').read())

with fiona.BytesCollection(zipshp.read()) as src:
    crs = src.crs
    gdf = gpd.GeoDataFrame.from_features(src, crs=crs)
    print(gdf.head())
1 GISUser9 Jan 06 2021 at 00:04

La risposta di @ user2856 mi ha portato a metà strada verso una soluzione. Non avrei saputo fiona.io.ZipMemoryFile, e questo mi ha portato a questa risposta . La combinazione delle due soluzioni mi ha dato:

with ZipMemoryFile(request.FILES['file'].file) as memfile:
    with fiona.BytesCollection(memfile._initial_bytes) as f:
        gdf = gpd.GeoDataFrame.from_features(f, crs='epsg:4326')
        print(gdf.head())

Ora ho i miei dati shapefile in un GeoDataFrame.

Per chiunque sia curioso, il motivo per cui sono andato al BytesCollectionposto di memfile.open()è perché non riuscivo memfile.open()a lavorare. Avrebbe generato un errore che indicava che al .open()metodo mancava un argomento posizionale "percorso".