diff --git a/src/app/src/components/ModalSections/FileUpload.js b/src/app/src/components/ModalSections/FileUpload.js
index e2158fc9..6340a29d 100644
--- a/src/app/src/components/ModalSections/FileUpload.js
+++ b/src/app/src/components/ModalSections/FileUpload.js
@@ -135,7 +135,7 @@ function UploadBox({ addFiles }) {
ACCEPTED FILES
- Shapefiles: .SHP, .SHX and .DBF
+ Shapes: Shapefile ZIP, GEOJSON
Reference Images: JPEG, PNG
diff --git a/src/app/src/constants.js b/src/app/src/constants.js
index d301d2f1..089b419c 100644
--- a/src/app/src/constants.js
+++ b/src/app/src/constants.js
@@ -86,7 +86,7 @@ export const ROLES = {
export const REFERENCE_IMAGE_MIME_TYPES = ['image/png', 'image/jpeg'];
export const REFERENCE_IMAGE_FILE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];
-export const SHAPE_FILE_EXTENSIONS = ['.shp'];
+export const SHAPE_FILE_EXTENSIONS = ['.zip', '.geojson'];
export const FILE_UPLOAD_ACCEPT_STRING = [
...REFERENCE_IMAGE_MIME_TYPES,
diff --git a/src/django/api/fields.py b/src/django/api/fields.py
index ef72bb03..2ed569bb 100644
--- a/src/django/api/fields.py
+++ b/src/django/api/fields.py
@@ -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."
+ )