@@ -18,15 +18,29 @@ class GeometryField(Field):
18
18
"""
19
19
type_name = 'GeometryField'
20
20
21
- def __init__ (self , ** kwargs ):
21
+ def __init__ (self , precision = None , remove_duplicates = False , ** kwargs ):
22
+ self .precision = precision
23
+ self .remove_dupes = remove_duplicates
22
24
super (GeometryField , self ).__init__ (** kwargs )
23
25
self .style = {'base_template' : 'textarea.html' }
24
26
25
27
def to_representation (self , value ):
26
28
if isinstance (value , dict ) or value is None :
27
29
return value
28
30
# we expect value to be a GEOSGeometry instance
29
- return GeoJsonDict (value .geojson )
31
+ geojson = GeoJsonDict (value .geojson )
32
+ if geojson ['type' ] == 'GeometryCollection' :
33
+ geometries = geojson .get ('geometries' )
34
+ else :
35
+ geometries = [geojson ]
36
+ for geometry in geometries :
37
+ if self .precision is not None :
38
+ geometry ['coordinates' ] = self ._recursive_round (
39
+ geometry ['coordinates' ], self .precision )
40
+ if self .remove_dupes :
41
+ geometry ['coordinates' ] = self ._rm_redundant_points (
42
+ geometry ['coordinates' ], geometry ['type' ])
43
+ return geojson
30
44
31
45
def to_internal_value (self , value ):
32
46
if value == '' or value is None :
@@ -48,6 +62,40 @@ def validate_empty_values(self, data):
48
62
self .fail ('required' )
49
63
return super (GeometryField , self ).validate_empty_values (data )
50
64
65
+ def _recursive_round (self , value , precision ):
66
+ """
67
+ Round all numbers within an array or nested arrays
68
+ value: number or nested array of numbers
69
+ precision: integer valueue of number of decimals to keep
70
+ """
71
+ if hasattr (value , '__iter__' ):
72
+ return tuple (self ._recursive_round (v , precision ) for v in value )
73
+ return round (value , precision )
74
+
75
+ def _rm_redundant_points (self , geometry , geo_type ):
76
+ """
77
+ Remove redundant coordinate pairs from geometry
78
+ geometry: array of coordinates or nested-array of coordinates
79
+ geo_type: GeoJSON type attribute for provided geometry, used to
80
+ determine structure of provided `geometry` argument
81
+ """
82
+ if geo_type in ('MultiPoint' , 'LineString' ):
83
+ close = (geo_type == 'LineString' )
84
+ output = []
85
+ for coord in geometry :
86
+ coord = tuple (coord )
87
+ if not output or coord != output [- 1 ]:
88
+ output .append (coord )
89
+ if close and len (output ) == 1 :
90
+ output .append (output [0 ])
91
+ return tuple (output )
92
+ if geo_type in ('MultiLineString' , 'Polygon' ):
93
+ return [
94
+ self ._rm_redundant_points (c , 'LineString' ) for c in geometry ]
95
+ if geo_type == 'MultiPolygon' :
96
+ return [self ._rm_redundant_points (c , 'Polygon' ) for c in geometry ]
97
+ return geometry
98
+
51
99
52
100
class GeometrySerializerMethodField (SerializerMethodField ):
53
101
def to_representation (self , value ):
0 commit comments