Du fichier de formes compressé téléchargé à Geopandas DataFrame dans l'application Django

Jan 05 2021

J'essaie d'arriver à un point où je peux filtrer rapidement des milliers de points dans un fichier de formes. Mon application Django demande un shapefile zippé à télécharger, où le fichier compressé contient au moins .shp, .shxet les .dbffichiers. Une fois dans ma vue Django, le fichier zip est le suivant:

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>

En supposant que Geopandas est la meilleure option pour un filtrage / masquage efficace (si je me trompe, je suis définitivement ouvert aux suggestions), je ne sais pas comment passer de l'état actuel à un Geopandas DataFrame. Quand j'essaye d'utiliser la read_file()méthode

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

J'obtiens l'erreur suivante:

fiona.errors.DriverError: no driver

L' état de la geopandas.read_file() documentation :

Soit le chemin absolu ou relatif du fichier ou de l'URL à ouvrir, soit tout objet avec une read()méthode (comme un fichier ouvert ou StringIO)

Je ne sais pas comment obtenir ce que j'ai dans un format approprié pour la read_file()méthode.

Remarque: Le masquage et le filtrage que je cherche à effectuer sont sur les données d'attribut et non sur la géométrie.

Réponses

4 user2856 Jan 05 2021 at 06:19

Vous pouvez utiliser fiona.io.ZipMemoryFileet gpd.GeoDataFrame.from_features.

Exemple:

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

Notez BytesCollectionqu'à l' origine, je n'ai pas inclus le comme le développeur fiona l'a déclaré dans un commentaire sur ma réponse précédente que la classe serait probablement obsolète. Cependant, si vous l'utilisez, vous ne devriez pas en avoir besoin ZipMemoryFile. Cela fonctionne pour moi:

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 réponse de @ user2856 m'a amené à mi-chemin vers une solution. Je ne l'aurais pas su fiona.io.ZipMemoryFile, et cela m'a conduit à cette réponse . La combinaison des deux solutions m'a donné:

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

J'ai maintenant mes données de fichier de formes dans un GeoDataFrame.

Pour tous ceux qui sont curieux, la raison pour laquelle je suis allé avec BytesCollectionplutôt que memfile.open()parce que je ne pouvais pas me rendre memfile.open()au travail. Cela lèverait une erreur indiquant que la .open()méthode manquait un argument de position «chemin».