5

I'm trying to get to a point where I can quickly filter thousands of points in a shapefile. My Django application asks for a zipped shapefile to upload, where the zipped file contains at least the .shp, .shx, and .dbf files. Once in my Django view, the zip file is as follows:

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>

Assuming Geopandas is the best option for efficient filtering/masking (if I'm wrong, I'm definitely open to suggestions), I'm not sure how to go from current state to a Geopandas DataFrame. When I try to use the read_file() method

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

I get the following error:

fiona.errors.DriverError: no driver

The geopandas.read_file() docs state:

Either the absolute or relative path to the file or URL to be opened, or any object with a read() method (such as an open file or StringIO)

I'm not sure how to get what I have into an appropriate format for the read_file() method.

Note: The masking and filtering I'm looking to perform are on attribute data and not the geometry.

4

You can use fiona.io.ZipMemoryFile and gpd.GeoDataFrame.from_features.

Example:

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

Note, I originally didn't include the BytesCollection as the fiona developer stated in a comment on my previous answer that the class would likely be deprecated. However, if you use it, you shouldn't need ZipMemoryFile. This works for me:

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

@user2856's answer got me half way to a solution. I would not have known about fiona.io.ZipMemoryFile, and that led me to this answer. Combining the two solutions gave me:

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

I now have my shapefile data in a GeoDataFrame.

For anyone who's curious, the reason I went with BytesCollection instead of memfile.open() is because I couldn't get memfile.open() to work. It would throw an error saying the .open() method was missing a 'path' positional argument.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.