Do arquivo de forma compactado enviado ao Geopandas DataFrame no aplicativo Django
Estou tentando chegar a um ponto em que posso filtrar rapidamente milhares de pontos em um shapefile. A minha aplicação Django pede um shapefile zipado para upload, onde o arquivo compactado contém pelo menos os .shp
, .shx
e .dbf
arquivos. Uma vez na visualização do Django, o arquivo zip é o seguinte:
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>
Supondo que o Geopandas seja a melhor opção para filtragem / mascaramento eficiente (se estiver errado, estou aberto a sugestões), não tenho certeza de como ir do estado atual para um DataFrame do Geopandas. Quando tento usar o read_file()
método
import geopandas as gpd
gpd.read_file(request.FILES['file'].file)
Eu obtenho o seguinte erro:
fiona.errors.DriverError: no driver
O estado dos geopandas.read_file()
documentos :
Tanto o caminho absoluto ou relativo para o arquivo ou URL a ser aberto, ou qualquer objeto com um
read()
método (como um arquivo aberto ou StringIO)
Não tenho certeza de como colocar o que tenho em um formato apropriado para o read_file()
método.
Observação: o mascaramento e a filtragem que estou tentando realizar estão nos dados de atributo e não na geometria.
Respostas
Você pode usar fiona.io.ZipMemoryFile
e gpd.GeoDataFrame.from_features
.
Exemplo:
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, originalmente não incluí o BytesCollection
como o desenvolvedor fiona declarou em um comentário sobre minha resposta anterior que a classe provavelmente seria descontinuada. No entanto, se você usá-lo, não será necessário ZipMemoryFile
. Isso funciona para mim:
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())
A resposta de @ user2856 me ajudou a chegar a uma solução. Eu não teria sabido fiona.io.ZipMemoryFile
, e isso me levou a esta resposta . Combinar as duas soluções me deu:
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())
Agora tenho meus dados do arquivo de forma em um GeoDataFrame.
Para quem está curioso, optei por seguir em BytesCollection
vez de memfile.open()
porque não consegui memfile.open()
trabalhar. Ele geraria um erro dizendo que o .open()
método não tinha um argumento posicional de 'caminho'.