-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support ingesting zipped shapefile, geojson
If the payload's shape is a zipped shapefile or a geojson, they will be parsed as shapes and added to the database. In the case of geojson, the file is parsed and ingested directly. In the case of zipped shapefiles, we save them to a temporary directory, then load them via fiona and extract the shape from within. fiona cannot read files from memory, and the uploaded files could potentially be really heavy, so doing so helps manage the load. These are the only two formats we are supporting at the moment, more may be added in the future. This assumes a single Polygon input. Support for MultiPolygons will be added in #127.
- Loading branch information
Showing
3 changed files
with
41 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,44 @@ | ||
from rest_framework.fields import FileField | ||
import os | ||
import fiona | ||
import json | ||
import tempfile | ||
|
||
from django.contrib.gis.geos import Polygon | ||
from pathlib import Path | ||
from django.contrib.gis.geos import GEOSGeometry | ||
from rest_framework.fields import FileField | ||
from rest_framework.serializers import ValidationError | ||
|
||
|
||
class ShapefileField(FileField): | ||
def to_internal_value(self, data): | ||
# shapefile = super().to_internal_value(data) | ||
return Polygon() | ||
if data.name.endswith(".zip"): | ||
# Treat like a zipped shapefile | ||
try: | ||
tmpdir = tempfile.mkdtemp() | ||
tmpfile = Path(f"{tmpdir}/{data.name}") | ||
with open(tmpfile, "wb+") as f: | ||
for chunk in data.chunks(): | ||
f.write(chunk) | ||
|
||
with fiona.open(f"zip://{tmpfile}") as f: | ||
# TODO Capture all features in the shapefile, not just first | ||
geojson = json.dumps(f[0]["geometry"]) | ||
return GEOSGeometry(geojson) | ||
|
||
except Exception: | ||
raise ValidationError( | ||
f"Could not parse {data.name} as a zipped shapefile." | ||
) | ||
|
||
finally: | ||
os.remove(tmpfile) | ||
os.rmdir(tmpdir) | ||
|
||
if data.name.endswith(".geojson"): | ||
geojson = data.read() | ||
return GEOSGeometry(geojson) | ||
|
||
raise ValidationError( | ||
f"Incompatible file: {data.name}." | ||
" Must be either a zipped shapefile, or a geojson." | ||
) |