De shapefile comprimido subido a Geopandas DataFrame en la aplicación Django
Estoy tratando de llegar a un punto en el que pueda filtrar rápidamente miles de puntos en un shapefile. Mi aplicación Django pide un archivo de forma comprimida a cargar, en el que el archivo comprimido contiene, al menos, las .shp
, .shx
y .dbf
archivos. Una vez en mi vista de Django, el archivo zip es el siguiente:
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>
Suponiendo que Geopandas es la mejor opción para un filtrado / enmascaramiento eficiente (si me equivoco, definitivamente estoy abierto a sugerencias), no estoy seguro de cómo pasar del estado actual a un DataFrame de Geopandas. Cuando trato de usar el read_file()
método
import geopandas as gpd
gpd.read_file(request.FILES['file'].file)
Obtuve el siguiente error:
fiona.errors.DriverError: no driver
Los geopandas.read_file()
documentos dicen:
Ya sea la ruta absoluta o relativa al archivo o URL que se abrirá, o cualquier objeto con un
read()
método (como un archivo abierto o StringIO)
No estoy seguro de cómo obtener lo que tengo en un formato apropiado para el read_file()
método.
Nota: El enmascaramiento y el filtrado que busco realizar se basan en datos de atributos y no en la geometría.
Respuestas
Puede utilizar fiona.io.ZipMemoryFile
y gpd.GeoDataFrame.from_features
.
Ejemplo:
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())
Tenga en cuenta que originalmente no BytesCollection
incluí el, como dijo el desarrollador de fiona en un comentario en mi respuesta anterior, que la clase probablemente quedaría obsoleta. Sin embargo, si lo usa, no debería necesitarlo ZipMemoryFile
. Esto funciona para mi:
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())
La respuesta de @ user2856 me llevó a la mitad de la solución. No lo hubiera sabido fiona.io.ZipMemoryFile
, y eso me llevó a esta respuesta . La combinación de las dos soluciones me dio:
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())
Ahora tengo mis datos de shapefile en un GeoDataFrame.
Para cualquiera que tenga curiosidad, la razón por la que fui en BytesCollection
lugar de memfile.open()
es porque no pude ir memfile.open()
al trabajo. Lanzaría un error diciendo que al .open()
método le faltaba un argumento posicional de 'ruta'.