validate GeometryField and cleanup intersection errors if possible

This commit is contained in:
Laura Klünder 2016-11-26 23:28:46 +01:00
parent bf338c59d5
commit a3d0f6dac3
2 changed files with 32 additions and 2 deletions

View file

@ -1,19 +1,32 @@
import json import json
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from shapely import validation
from shapely.geometry import mapping, shape from shapely.geometry import mapping, shape
from shapely.geometry.base import BaseGeometry
from c3nav.mapdata.utils import format_geojson from c3nav.mapdata.utils import clean_geometry, format_geojson
def validate_geometry(geometry):
if not isinstance(geometry, BaseGeometry):
raise ValidationError('GeometryField expexted a Shapely BaseGeometry child-class.')
if not geometry.is_valid:
raise ValidationError('Invalid geometry: %s' % validation.explain_validity(geometry))
class GeometryField(models.TextField): class GeometryField(models.TextField):
default_validators = [validate_geometry]
def from_db_value(self, value, expression, connection, context): def from_db_value(self, value, expression, connection, context):
if value is None: if value is None:
return value return value
return shape(json.loads(value)) return shape(json.loads(value))
def to_python(self, value): def to_python(self, value):
return shape(json.loads(value)) return clean_geometry(shape(json.loads(value)))
def get_prep_value(self, value): def get_prep_value(self, value):
return json.dumps(format_geojson(mapping(value))) return json.dumps(format_geojson(mapping(value)))

View file

@ -1,6 +1,8 @@
import json import json
from collections import OrderedDict from collections import OrderedDict
from shapely.geometry import Polygon
def _preencode(data, magic_marker, in_coords=False): def _preencode(data, magic_marker, in_coords=False):
if isinstance(data, dict): if isinstance(data, dict):
@ -45,3 +47,18 @@ def round_coordinates(data):
return tuple(round_coordinates(item) for item in data) return tuple(round_coordinates(item) for item in data)
else: else:
return round(data, 2) return round(data, 2)
def clean_geometry(geometry):
if geometry.is_valid:
return geometry
if isinstance(geometry, Polygon):
p = Polygon(list(geometry.exterior.coords))
for interior in geometry.interiors:
p = p.difference(Polygon(list(interior.coords)))
if p.is_valid:
return p
return geometry