Z przesłanego spakowanego pliku shapefile do Geopandas DataFrame w aplikacji Django

Jan 05 2021

Próbuję dojść do punktu, w którym mogę szybko przefiltrować tysiące punktów w pliku shapefile. Moja aplikacja Django prosi o skompresowanego shapefile przesłać, gdzie plik zip zawiera co najmniej .shp, .shxi .dbfpliki. W moim widoku Django plik zip wygląda następująco:

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>

Zakładając, że Geopandy to najlepsza opcja do wydajnego filtrowania / maskowania (jeśli się mylę, zdecydowanie jestem otwarty na sugestie), nie jestem pewien, jak przejść z bieżącego stanu do Geopandas DataFrame. Kiedy próbuję użyć read_file()metody

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

Otrzymuję następujący błąd:

fiona.errors.DriverError: no driver

W geopandas.read_file() docs stwierdzić:

Absolutna lub względna ścieżka do pliku lub adresu URL do otwarcia albo dowolny obiekt z read()metodą (na przykład otwarty plik lub StringIO)

Nie jestem pewien, jak uzyskać to, co mam, w odpowiednim formacie dla read_file()metody.

Uwaga: maskowanie i filtrowanie, które chcę wykonać, dotyczą danych atrybutów, a nie geometrii.

Odpowiedzi

4 user2856 Jan 05 2021 at 06:19

Możesz użyć fiona.io.ZipMemoryFilei gpd.GeoDataFrame.from_features.

Przykład:

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

Uwaga, pierwotnie nie uwzględniłem, BytesCollectionjak stwierdził programista fiona w komentarzu do mojej poprzedniej odpowiedzi, że klasa prawdopodobnie zostanie wycofana. Jeśli jednak go używasz, nie powinieneś tego potrzebować ZipMemoryFile. To działa dla mnie:

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

Odpowiedź @ user2856 pomogła mi znaleźć rozwiązanie. Nie wiedziałbym o fiona.io.ZipMemoryFiletym i to doprowadziło mnie do tej odpowiedzi . Połączenie tych dwóch rozwiązań dało mi:

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

Mam teraz dane shapefile w GeoDataFrame.

Dla każdego, kto jest ciekawy, powodem, dla którego poszedłem BytesCollectionzamiast tego, było memfile.open()to, że nie mogłem dostać się memfile.open()do pracy. Wystąpiłby błąd informujący, że w .open()metodzie brakuje argumentu pozycyjnego „ścieżka”.