소스 검색

Fixed #28960 -- Added GEOSGeometry.buffer_with_style().

Stanislav Karpov 7 년 전
부모
커밋
6d794fb762

+ 1 - 0
AUTHORS

@@ -752,6 +752,7 @@ answer newbie questions, and generally made Django that much better:
     Srinivas Reddy Thatiparthy <thatiparthysreenivas@gmail.com>
     Stanislas Guerra <stan@slashdev.me>
     Stanislaus Madueke
+    Stanislav Karpov <work@stkrp.ru>
     starrynight <cmorgh@gmail.com>
     Stefane Fermgier <sf@fermigier.com>
     Stefano Rivera <stefano@rivera.za.net>

+ 12 - 0
django/contrib/gis/geos/geometry.py

@@ -499,6 +499,18 @@ class GEOSGeometryBase(GEOSBase):
         """
         return self._topology(capi.geos_buffer(self.ptr, width, quadsegs))
 
+    def buffer_with_style(self, width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0):
+        """
+        Same as buffer() but allows customizing the style of the buffer.
+
+        End cap style can be round (1), flat (2), or square (3).
+        Join style can be round (1), mitre (2), or bevel (3).
+        Mitre ratio limit only affects mitered join style.
+        """
+        return self._topology(
+            capi.geos_bufferwithstyle(self.ptr, width, quadsegs, end_cap_style, join_style, mitre_limit),
+        )
+
     @property
     def centroid(self):
         """

+ 1 - 0
django/contrib/gis/geos/prototypes/topology.py

@@ -21,6 +21,7 @@ class Topology(GEOSFuncFactory):
 # Topology Routines
 geos_boundary = Topology('GEOSBoundary')
 geos_buffer = Topology('GEOSBuffer', argtypes=[GEOM_PTR, c_double, c_int])
+geos_bufferwithstyle = Topology('GEOSBufferWithStyle', argtypes=[GEOM_PTR, c_double, c_int, c_int, c_int, c_double])
 geos_centroid = Topology('GEOSGetCentroid')
 geos_convexhull = Topology('GEOSConvexHull')
 geos_difference = Topology('GEOSDifference', argtypes=[GEOM_PTR, GEOM_PTR])

+ 10 - 0
docs/ref/contrib/gis/geos.txt

@@ -496,6 +496,16 @@ Topological Methods
     optional ``quadsegs`` keyword sets the number of segments used to
     approximate a quarter circle (defaults is 8).
 
+.. method:: GEOSGeometry.buffer_with_style(width, quadsegs=8, end_cap_style=1, join_style=1, mitre_limit=5.0)
+
+    .. versionadded:: 2.1
+
+    Same as :meth:`buffer`, but allows customizing the style of the buffer.
+
+    * ``end_cap_style`` can be round (``1``), flat (``2``), or square (``3``).
+    * ``join_style`` can be round (``1``), mitre (``2``), or bevel (``3``).
+    * Mitre ratio limit (``mitre_limit``) only affects mitered join style.
+
 .. method:: GEOSGeometry.difference(other)
 
     Returns a :class:`GEOSGeometry` representing the points making up this

+ 3 - 1
docs/releases/2.1.txt

@@ -70,7 +70,9 @@ Minor features
 :mod:`django.contrib.gis`
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* The new :meth:`.GEOSGeometry.buffer_with_style` method is a version of
+  :meth:`~.GEOSGeometry.buffer` that allows customizing the style of the
+  buffer.
 
 :mod:`django.contrib.messages`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ 1 - 0
docs/spelling_wordlist

@@ -407,6 +407,7 @@ minified
 minify
 mis
 misconfiguration
+mitre
 mixin
 mixins
 modelforms

+ 18 - 0
tests/gis_tests/data/geometries.json

@@ -78,6 +78,24 @@
        "width": 2.0, "quadsegs": 8
       }
   ],
+  "buffer_with_style_geoms": [
+      {"wkt": "POINT (0 0)",
+       "buffer_wkt": "POLYGON EMPTY",
+       "width": 5.0, "end_cap_style": 2, "join_style": 2
+      },
+      {"wkt": "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))",
+       "buffer_wkt": "POLYGON ((-2 -2, -2 12, 12 12, 12 -2, -2 -2))",
+       "width": 2.0, "end_cap_style": 2, "join_style": 2
+      },
+      {"wkt": "LINESTRING (0 0, 10 0)",
+       "buffer_wkt": "POLYGON ((10 2, 10 -2, 0 -2, 0 2, 10 2))",
+       "width": 2.0, "end_cap_style": 2, "join_style": 2
+      },
+      {"wkt": "LINESTRING (0 0, 10 0, 10 10, 0 10)",
+       "buffer_wkt": "POLYGON ((8 2, 8 8, 0 8, 0 12, 12 12, 12 -2, 0 -2, 0 2, 8 2))",
+       "width": 2.0, "end_cap_style": 2, "join_style": 2
+      }
+  ],
   "relate_geoms": [
       {"wkt_a": "MULTIPOINT(80 70, 20 20, 200 170, 140 120)",
        "wkt_b": "MULTIPOINT(80 170, 140 120, 200 80, 80 70)",

+ 45 - 9
tests/gis_tests/geos_tests/test_geos.py

@@ -1,4 +1,5 @@
 import ctypes
+import itertools
 import json
 import pickle
 import random
@@ -650,21 +651,56 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
             self.assertEqual(d1, a)
 
     def test_buffer(self):
-        "Testing buffer()."
-        for bg in self.geometries.buffer_geoms:
+        bg = self.geometries.buffer_geoms[0]
+        g = fromstr(bg.wkt)
+
+        # Can't use a floating-point for the number of quadsegs.
+        with self.assertRaises(ctypes.ArgumentError):
+            g.buffer(bg.width, quadsegs=1.1)
+
+        self._test_buffer(self.geometries.buffer_geoms, 'buffer')
+
+    def test_buffer_with_style(self):
+        bg = self.geometries.buffer_with_style_geoms[0]
+        g = fromstr(bg.wkt)
+
+        # Can't use a floating-point for the number of quadsegs.
+        with self.assertRaises(ctypes.ArgumentError):
+            g.buffer_with_style(bg.width, quadsegs=1.1)
+
+        # Can't use a floating-point for the end cap style.
+        with self.assertRaises(ctypes.ArgumentError):
+            g.buffer_with_style(bg.width, end_cap_style=1.2)
+        # Can't use a end cap style that is not in the enum.
+        with self.assertRaises(GEOSException):
+            g.buffer_with_style(bg.width, end_cap_style=55)
+
+        # Can't use a floating-point for the join style.
+        with self.assertRaises(ctypes.ArgumentError):
+            g.buffer_with_style(bg.width, join_style=1.3)
+        # Can't use a join style that is not in the enum.
+        with self.assertRaises(GEOSException):
+            g.buffer_with_style(bg.width, join_style=66)
+
+        self._test_buffer(
+            itertools.chain(self.geometries.buffer_geoms, self.geometries.buffer_with_style_geoms),
+            'buffer_with_style',
+        )
+
+    def _test_buffer(self, geometries, buffer_method_name):
+        for bg in geometries:
             g = fromstr(bg.wkt)
 
             # The buffer we expect
             exp_buf = fromstr(bg.buffer_wkt)
-            quadsegs = bg.quadsegs
-            width = bg.width
-
-            # Can't use a floating-point for the number of quadsegs.
-            with self.assertRaises(ctypes.ArgumentError):
-                g.buffer(width, float(quadsegs))
 
             # Constructing our buffer
-            buf = g.buffer(width, quadsegs)
+            buf_kwargs = {
+                kwarg_name: getattr(bg, kwarg_name)
+                for kwarg_name in ('width', 'quadsegs', 'end_cap_style', 'join_style', 'mitre_limit')
+                if hasattr(bg, kwarg_name)
+            }
+            buf = getattr(g, buffer_method_name)(**buf_kwargs)
             self.assertEqual(exp_buf.num_coords, buf.num_coords)
             self.assertEqual(len(exp_buf), len(buf))