Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

polygon field added #155

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,70 @@ point = {
}
serializer = PointFieldSerializer(data={'created': now, 'point': point})
```
## PolygonField

Polygon field for GeoDjango

**Signature:** `PolygonField()`
- It takes a list of orderly pair arrays, representing points which all together are supposed to make a polygon. example:

A polygon without inner ring represented in 2d array format

[
[51.778564453125, 35.59925232772949], [50.1470947265625, 34.80929324176267],
[52.6080322265625, 34.492975402501536],[51.778564453125, 35.59925232772949]
]

The same polygon represented in 3d array format

[
[
[51.778564453125, 35.59925232772949], [50.1470947265625, 34.80929324176267],
[52.6080322265625, 34.492975402501536],[51.778564453125, 35.59925232772949]
]
]

A polygon with inner ring (first element representing exterior and the second one interior ring)


[
[ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ],
[ [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 2 ] , [ 2 , 2 ] ]
]


**Example:**

```python
# serializer

from drf_extra_fields.geo_fields import PolygonField

class PolygonFieldSerializer(serializers.Serializer):
polygon = PolygonField(required=False)
created = serializers.DateTimeField()

# use the serializer
polygon = [
[
51.778564453125,
35.59925232772949
],
[
50.1470947265625,
34.80929324176267
],
[
52.6080322265625,
34.492975402501536
],
[
51.778564453125,
35.59925232772949
]
]
serializer = PolygonFieldSerializer(data={'created': now, 'polygon': polygon})
```


# RangeField
Expand Down
117 changes: 114 additions & 3 deletions drf_extra_fields/geo_fields.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import json
from django.contrib.gis.geos import GEOSGeometry
from django.contrib.gis.geos import GEOSGeometry, polygon
from django.contrib.gis.geos.error import GEOSException
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers

EMPTY_VALUES = (None, '', [], (), {})
from django.contrib.gis.geos import Polygon


class PointField(serializers.Field):
"""
A field for handling GeoDjango Point fields as a json format.
Expected input format:
{
"latitude": 49.8782482189424,
"longitude": 24.452545489
"latitude": 49.8782482189424,
"longitude": 24.452545489
}

"""
Expand Down Expand Up @@ -76,3 +77,113 @@ def to_representation(self, value):
value['latitude'] = smart_str(value.pop('latitude'))

return value



class PolygonField(serializers.Field):
"""
A field for handling GeoDjango PolyGone fields as a array format.
Expected input format:
{
[
[
51.778564453125,
35.59925232772949
],
[
50.1470947265625,
34.80929324176267
],
[
52.6080322265625,
34.492975402501536
],
[
51.778564453125,
35.59925232772949
]
]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't support polygon with inner rings.

How about we use the following format with the key coordinates?

Polygon with single ring (The way you implemented.)

coordinates : [
     [ [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 2 ] , [ 2 , 2 ] ]
  ]

Polygon with multiple rings (The one that is not supported at the moment.)

coordinates : [
     [ [ 0 , 0 ] , [ 3 , 6 ] , [ 6 , 1 ] , [ 0 , 0 ] ],
     [ [ 2 , 2 ] , [ 3 , 3 ] , [ 4 , 2 ] , [ 2 , 2 ] ]
  ]

From geojson specification

The "coordinates" member must be an array of LinearRing coordinate arrays. For Polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.

Please see django documentation for usage.


"""
type_name = 'PolygonField'
type_label = 'polygon'

default_error_messages = {
'invalid': _('Enter a valid polygon.'),
}

def __init__(self, *args, **kwargs):
super(PolygonField, self).__init__(*args, **kwargs)

def to_internal_value(self, value):
"""
Parse array data and return a polygon object
"""
if value in EMPTY_VALUES and not self.required:
return None


polygon_type = None

try:
new_value = []

if len(value)>2:
# a polygon without the ring in a 2-d array
for item in value:
item = list(map(float, item))
new_value.append(item)

elif len(value)==2:
# a polygon with inner ring
polygon_type = 'with_inner_ring'

for i in range(2):
# a loop of 2 iterations. one per each ring
ring_array = []
for item in value[i]:
item = list(map(float, item))
ring_array.append(item)
new_value.append(ring_array)

elif len(value)==1:
# a polygon without the ring in a 3-d array. not supported by django, should be converted to 2-d
for item in value[0]:
item = list(map(float, item))
new_value.append(item)

except ValueError as e:
print(e)
self.fail('invalid')

try:
if polygon_type=='with_inner_ring':
# for polygons with inner ring you should pass the exterior and interior ring seperated with comma to Polygon
return Polygon(new_value[0], new_value[1])

else:
return Polygon(new_value)

except (GEOSException, ValueError, TypeError) as e:
print(e)
print(new_value)
self.fail('invalid')



def to_representation(self, value):
"""
Transform Polygon object to array of arrays.
"""
if value is None:
return value

if isinstance(value, GEOSGeometry):
value = json.loads(value.geojson)['coordinates']


return {
'coordinates': value
}

Loading