Skip to content

Commit 9b5879f

Browse files
authored
[feature] Added Bounding Box support for remaining GeoJSON types
Add bounding box to GeoJSON object created by GeometryField. .
1 parent 4a79813 commit 9b5879f

File tree

3 files changed

+106
-3
lines changed

3 files changed

+106
-3
lines changed

README.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Provides a ``GeometryField``, which is a subclass of Django Rest Framework
7979
geometry fields, providing custom ``to_native`` and ``from_native``
8080
methods for GeoJSON input/output.
8181

82-
This field takes two optional arguments:
82+
This field takes three optional arguments:
8383

8484
``precision``: Passes coordinates through Python's builtin ``round()`` function (`docs
8585
<https://docs.python.org/3/library/functions.html#round>`_), rounding values to
@@ -92,7 +92,9 @@ polygon geometries. This is particularly useful when used with the ``precision``
9292
argument, as the likelihood of duplicate coordinates increase as precision of
9393
coordinates are reduced.
9494

95-
**Note:** While both above arguments are designed to reduce the
95+
``auto_bbox``: If True, the GeoJSON object will include a `bounding box <https://datatracker.ietf.org/doc/html/rfc7946#section-5>`_, which is the smallest possible rectangle enclosing the geometry.
96+
97+
**Note:** While ``precision`` and ``remove_duplicates`` are designed to reduce the
9698
byte size of the API response, they will also increase the processing time
9799
required to render the response. This will likely be negligible for small GeoJSON
98100
responses but may become an issue for large responses.

rest_framework_gis/fields.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,14 @@ class GeometryField(Field):
1717

1818
type_name = 'GeometryField'
1919

20-
def __init__(self, precision=None, remove_duplicates=False, **kwargs):
20+
def __init__(
21+
self, precision=None, remove_duplicates=False, auto_bbox=False, **kwargs
22+
):
23+
"""
24+
:param auto_bbox: Whether the GeoJSON object should include a bounding box
25+
"""
2126
self.precision = precision
27+
self.auto_bbox = auto_bbox
2228
self.remove_dupes = remove_duplicates
2329
super().__init__(**kwargs)
2430
self.style.setdefault('base_template', 'textarea.html')
@@ -45,6 +51,9 @@ def to_representation(self, value):
4551
geometry['coordinates'] = self._rm_redundant_points(
4652
geometry['coordinates'], geometry['type']
4753
)
54+
55+
if self.auto_bbox:
56+
geojson["bbox"] = value.extent
4857
return geojson
4958

5059
def to_internal_value(self, value):

tests/django_restframework_gis_tests/test_fields.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,3 +586,95 @@ def test_rm_redundant_GeometryCollection(self):
586586
}
587587
},
588588
)
589+
590+
591+
class TestBoundingBox(BaseTestCase):
592+
def test_boundingbox_Point(self):
593+
model = self.get_instance(Point)
594+
Serializer = self.create_serializer(auto_bbox=True)
595+
data = Serializer(model).data
596+
self.assertEqual(
597+
self.normalize(data),
598+
{
599+
'geometry': {
600+
"type": "Point",
601+
"bbox": [-105.0162, 39.5742, -105.0162, 39.5742],
602+
"coordinates": [-105.0162, 39.5742],
603+
}
604+
},
605+
)
606+
607+
def test_boundingbox_MultiPoint(self):
608+
model = self.get_instance(MultiPoint)
609+
Serializer = self.create_serializer(auto_bbox=True)
610+
data = Serializer(model).data
611+
self.assertEqual(
612+
self.normalize(data),
613+
{
614+
"geometry": {
615+
"type": "MultiPoint",
616+
"bbox": [-105.0162, 35.049, -80.6665, 39.5742],
617+
"coordinates": [
618+
[-105.0162, 39.5742],
619+
[-80.6665, 35.0539],
620+
[-80.6665, 35.0539],
621+
[-80.672, 35.049],
622+
],
623+
}
624+
},
625+
)
626+
627+
def test_boundingbox_LineString(self):
628+
model = self.get_instance(LineString)
629+
Serializer = self.create_serializer(auto_bbox=True)
630+
data = Serializer(model).data
631+
self.assertEqual(
632+
self.normalize(data),
633+
{
634+
"geometry": {
635+
"type": "LineString",
636+
"bbox": [-101.7443, 38.8739, -97.6354, 39.33],
637+
"coordinates": [
638+
[-101.7443, 39.3215],
639+
[-101.4021, 39.3300],
640+
[-101.4038, 39.3300],
641+
[-101.4038, 39.3300],
642+
[-97.6354, 38.8739],
643+
],
644+
}
645+
},
646+
)
647+
648+
def test_boundingbox_Polygon(self):
649+
model = self.get_instance(Polygon)
650+
Serializer = self.create_serializer(auto_bbox=True)
651+
data = Serializer(model).data
652+
self.assertEqual(
653+
self.normalize(data),
654+
{
655+
"geometry": {
656+
"type": "Polygon",
657+
"bbox": [-84.3228, 34.985, -82.5677, 36.0335],
658+
"coordinates": [
659+
[
660+
[-84.3228, 34.9895],
661+
[-82.6062, 36.0335],
662+
[-82.6062, 35.9913],
663+
[-82.6062, 35.9791],
664+
[-82.5787, 35.9613],
665+
[-82.5787, 35.9613], # Dupe
666+
[-82.5677, 35.9513],
667+
[-84.2211, 34.9850],
668+
[-84.3228, 34.9895],
669+
],
670+
[
671+
[-75.6903, 35.7420],
672+
[-75.5914, 35.7420],
673+
[-75.5914, 35.7420], # Dupe
674+
[-75.7067, 35.7420],
675+
[-75.6903, 35.7420],
676+
],
677+
],
678+
}
679+
},
680+
)

0 commit comments

Comments
 (0)