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." + )