validate GeometryField and cleanup intersection errors if possible
This commit is contained in:
parent
bf338c59d5
commit
a3d0f6dac3
2 changed files with 32 additions and 2 deletions
|
@ -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)))
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue