Z przesłanego spakowanego pliku shapefile do Geopandas DataFrame w aplikacji Django
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
, .shx
i .dbf
pliki. 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
Możesz użyć fiona.io.ZipMemoryFile
i 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, BytesCollection
jak 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())
Odpowiedź @ user2856 pomogła mi znaleźć rozwiązanie. Nie wiedziałbym o fiona.io.ZipMemoryFile
tym 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 BytesCollection
zamiast 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”.