From 10c53385f85aa424e83c65a002d5ca1679d2dd71 Mon Sep 17 00:00:00 2001
From: Claude Paroz <claude@2xlibre.net>
Date: Sat, 16 Apr 2016 10:50:24 +0200
Subject: [PATCH] Fixed #26510 -- Allowed dim/trim/precision as WKTWriter init
 arguments

Thanks Tim Graham for the review.
---
 django/contrib/gis/geos/geometry.py      |  6 ++---
 django/contrib/gis/geos/prototypes/io.py | 31 ++++++++++++++++++------
 docs/ref/contrib/gis/geos.txt            | 25 ++++++++++++++++---
 tests/gis_tests/geos_tests/test_io.py    |  8 +++++-
 4 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py
index f66effd756..744d89275f 100644
--- a/django/contrib/gis/geos/geometry.py
+++ b/django/contrib/gis/geos/geometry.py
@@ -408,7 +408,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
     @property
     def wkt(self):
         "Returns the WKT (Well-Known Text) representation of this Geometry."
-        return wkt_w(3 if self.hasz else 2).write(self).decode()
+        return wkt_w(dim=3 if self.hasz else 2).write(self).decode()
 
     @property
     def hex(self):
@@ -419,7 +419,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
         """
         # A possible faster, all-python, implementation:
         #  str(self.wkb).encode('hex')
-        return wkb_w(3 if self.hasz else 2).write_hex(self)
+        return wkb_w(dim=3 if self.hasz else 2).write_hex(self)
 
     @property
     def hexewkb(self):
@@ -428,7 +428,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
         extension of the WKB specification that includes SRID value that are
         a part of this geometry.
         """
-        return ewkb_w(3 if self.hasz else 2).write_hex(self)
+        return ewkb_w(dim=3 if self.hasz else 2).write_hex(self)
 
     @property
     def json(self):
diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py
index ef59f93a98..161541d6f4 100644
--- a/django/contrib/gis/geos/prototypes/io.py
+++ b/django/contrib/gis/geos/prototypes/io.py
@@ -170,6 +170,14 @@ class WKTWriter(IOBase):
     _trim = False
     _precision = None
 
+    def __init__(self, dim=2, trim=False, precision=None):
+        super(WKTWriter, self).__init__()
+        if bool(trim) != self._trim:
+            self.trim = trim
+        if precision is not None:
+            self.precision = precision
+        self.outdim = dim
+
     def write(self, geom):
         "Returns the WKT representation of the given geometry."
         return wkt_writer_write(self.ptr, geom.ptr)
@@ -212,6 +220,10 @@ class WKBWriter(IOBase):
     _destructor = wkb_writer_destroy
     ptr_type = WKB_WRITE_PTR
 
+    def __init__(self, dim=2):
+        super(WKBWriter, self).__init__()
+        self.outdim = dim
+
     def write(self, geom):
         "Returns the WKB representation of the given geometry."
         return six.memoryview(wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
@@ -280,10 +292,13 @@ def wkt_r():
     return thread_context.wkt_r
 
 
-def wkt_w(dim=2):
+def wkt_w(dim=2, trim=False, precision=None):
     if not thread_context.wkt_w:
-        thread_context.wkt_w = WKTWriter()
-    thread_context.wkt_w.outdim = dim
+        thread_context.wkt_w = WKTWriter(dim=dim, trim=trim, precision=precision)
+    else:
+        thread_context.wkt_w.outdim = dim
+        thread_context.wkt_w.trim = trim
+        thread_context.wkt_w.precision = precision
     return thread_context.wkt_w
 
 
@@ -295,14 +310,16 @@ def wkb_r():
 
 def wkb_w(dim=2):
     if not thread_context.wkb_w:
-        thread_context.wkb_w = WKBWriter()
-    thread_context.wkb_w.outdim = dim
+        thread_context.wkb_w = WKBWriter(dim=dim)
+    else:
+        thread_context.wkb_w.outdim = dim
     return thread_context.wkb_w
 
 
 def ewkb_w(dim=2):
     if not thread_context.ewkb_w:
-        thread_context.ewkb_w = WKBWriter()
+        thread_context.ewkb_w = WKBWriter(dim=dim)
         thread_context.ewkb_w.srid = True
-    thread_context.ewkb_w.outdim = dim
+    else:
+        thread_context.ewkb_w.outdim = dim
     return thread_context.ewkb_w
diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt
index 2806ca061a..71d8952e27 100644
--- a/docs/ref/contrib/gis/geos.txt
+++ b/docs/ref/contrib/gis/geos.txt
@@ -957,12 +957,18 @@ WKB or WKT of the given geometry.  In addition, :class:`WKBWriter` objects
 also have properties that may be used to change the byte order, and or
 include the SRID value (in other words, EWKB).
 
-.. class:: WKBWriter
+.. class:: WKBWriter(dim=2)
 
 ``WKBWriter`` provides the most control over its output.  By default it
 returns OGC-compliant WKB when its ``write`` method is called.  However,
 it has properties that allow for the creation of EWKB, a superset of the
-WKB standard that includes additional information.
+WKB standard that includes additional information. See the
+:attr:`WKBWriter.outdim` documentation for more details about the ``dim``
+argument.
+
+.. versionchanged:: 1.10
+
+    The ability to pass the ``dim`` argument to the constructor was added.
 
 .. method:: WKBWriter.write(geom)
 
@@ -1047,7 +1053,16 @@ geometry should be included with the WKB representation.  Example::
     >>> wkb_w.write_hex(pnt)
     '0101000020E6100000000000000000F03F000000000000F03F'
 
-.. class:: WKTWriter
+.. class:: WKTWriter(dim=2, trim=False, precision=None)
+
+    This class allows outputting the WKT representation of a geometry. See the
+    :attr:`WKBWriter.outdim`, :attr:`trim`, and :attr:`precision` attributes for
+    details about the constructor arguments.
+
+    .. versionchanged:: 1.10
+
+        The ability to pass the ``dim``, ``trim``, and ``precision`` arguments
+        to the constructor was added.
 
 .. method:: WKTWriter.write(geom)
 
@@ -1059,6 +1074,10 @@ Returns the WKT of the given geometry. Example::
     >>> wkt_w.write(pnt)
     'POINT (1.0000000000000000 1.0000000000000000)'
 
+.. attribute:: WKTWriter.outdim
+
+    See :attr:`WKBWriter.outdim`.
+
 .. attribute:: WKTWriter.trim
 
 .. versionadded:: 1.10
diff --git a/tests/gis_tests/geos_tests/test_io.py b/tests/gis_tests/geos_tests/test_io.py
index e761cd61a3..10762c0821 100644
--- a/tests/gis_tests/geos_tests/test_io.py
+++ b/tests/gis_tests/geos_tests/test_io.py
@@ -42,6 +42,12 @@ class GEOSIOTest(SimpleTestCase):
         ref_wkt = 'POINT (5.0000000000000000 23.0000000000000000)'
         self.assertEqual(ref_wkt, wkt_w.write(ref).decode())
 
+    def test_wktwriter_constructor_arguments(self):
+        wkt_w = WKTWriter(dim=3, trim=True, precision=3)
+        ref = GEOSGeometry('POINT (5.34562 23 1.5)')
+        ref_wkt = 'POINT Z (5.35 23 1.5)'
+        self.assertEqual(ref_wkt, wkt_w.write(ref).decode())
+
     def test03_wkbreader(self):
         # Creating a WKBReader instance
         wkb_r = WKBReader()
@@ -101,7 +107,7 @@ class GEOSIOTest(SimpleTestCase):
 
         # Ensuring bad output dimensions are not accepted
         for bad_outdim in (-1, 0, 1, 4, 423, 'foo', None):
-            with self.assertRaises(ValueError):
+            with self.assertRaisesMessage(ValueError, 'WKB output dimension must be 2 or 3'):
                 wkb_w.outdim = bad_outdim
 
         # Now setting the output dimensions to be 3