From c984e2bc15aa41de87404d6dd5d7e64f1a8e5038 Mon Sep 17 00:00:00 2001
From: Sergey Fedoseev <fedoseev.sergey@gmail.com>
Date: Sat, 5 Dec 2015 19:05:52 +0500
Subject: [PATCH] Fixed #25869 -- Added trim and precision properties to
 WKTWriter.

---
 django/contrib/gis/geos/prototypes/io.py | 27 ++++++++++++++++
 docs/ref/contrib/gis/geos.txt            | 38 ++++++++++++++++++++++
 docs/releases/1.10.txt                   |  5 +++
 tests/gis_tests/geos_tests/test_io.py    | 41 ++++++++++++++++++++++--
 4 files changed, 108 insertions(+), 3 deletions(-)

diff --git a/django/contrib/gis/geos/prototypes/io.py b/django/contrib/gis/geos/prototypes/io.py
index ed28bcdccd..87aec30605 100644
--- a/django/contrib/gis/geos/prototypes/io.py
+++ b/django/contrib/gis/geos/prototypes/io.py
@@ -54,6 +54,9 @@ wkt_writer_set_outdim = GEOSFuncFactory(
     'GEOSWKTWriter_setOutputDimension', argtypes=[WKT_WRITE_PTR, c_int]
 )
 
+wkt_writer_set_trim = GEOSFuncFactory('GEOSWKTWriter_setTrim', argtypes=[WKT_WRITE_PTR, c_char])
+wkt_writer_set_precision = GEOSFuncFactory('GEOSWKTWriter_setRoundingPrecision', argtypes=[WKT_WRITE_PTR, c_int])
+
 # WKBReader routines
 wkb_reader_create = GEOSFuncFactory('GEOSWKBReader_create', restype=WKB_READ_PTR)
 wkb_reader_destroy = GEOSFuncFactory('GEOSWKBReader_destroy', argtypes=[WKB_READ_PTR])
@@ -164,6 +167,9 @@ class WKTWriter(IOBase):
     _destructor = wkt_writer_destroy
     ptr_type = WKT_WRITE_PTR
 
+    _trim = False
+    _precision = None
+
     def write(self, geom):
         "Returns the WKT representation of the given geometry."
         return wkt_writer_write(self.ptr, geom.ptr)
@@ -178,6 +184,27 @@ class WKTWriter(IOBase):
             raise ValueError('WKT output dimension must be 2 or 3')
         wkt_writer_set_outdim(self.ptr, new_dim)
 
+    @property
+    def trim(self):
+        return self._trim
+
+    @trim.setter
+    def trim(self, flag):
+        self._trim = bool(flag)
+        wkt_writer_set_trim(self.ptr, b'\x01' if flag else b'\x00')
+
+    @property
+    def precision(self):
+        return self._precision
+
+    @precision.setter
+    def precision(self, precision):
+        if isinstance(precision, int) and precision >= 0 or precision is None:
+            self._precision = precision
+            wkt_writer_set_precision(self.ptr, -1 if precision is None else precision)
+        else:
+            raise AttributeError('WKT output rounding precision must be non-negative integer or None.')
+
 
 class WKBWriter(IOBase):
     _constructor = wkb_writer_create
diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt
index 476ea7e233..ddf4f0706f 100644
--- a/docs/ref/contrib/gis/geos.txt
+++ b/docs/ref/contrib/gis/geos.txt
@@ -1052,6 +1052,44 @@ Returns the WKT of the given geometry. Example::
     >>> wkt_w.write(pnt)
     'POINT (1.0000000000000000 1.0000000000000000)'
 
+.. attribute:: WKTWriter.trim
+
+.. versionadded:: 1.10
+
+This property is used to enable or disable trimming of
+unnecessary decimals.
+
+    >>> from django.contrib.gis.geos import Point, WKTWriter
+    >>> pnt = Point(1, 1)
+    >>> wkt_w = WKTWriter()
+    >>> wkt_w.trim
+    False
+    >>> wkt_w.write(pnt)
+    'POINT (1.0000000000000000 1.0000000000000000)'
+    >>> wkt_w.trim = True
+    >>> wkt_w.write(pnt)
+    'POINT (1 1)'
+
+.. attribute:: WKTWriter.precision
+
+.. versionadded:: 1.10
+
+This property controls the rounding precision of coordinates;
+if set to ``None`` rounding is disabled.
+
+    >>> from django.contrib.gis.geos import Point, WKTWriter
+    >>> pnt = Point(1.44, 1.66)
+    >>> wkt_w = WKTWriter()
+    >>> print(wkt_w.precision)
+    None
+    >>> wkt_w.write(pnt)
+    'POINT (1.4399999999999999 1.6599999999999999)'
+    >>> wkt_w.precision = 0
+    >>> wkt_w.write(pnt)
+    'POINT (1 2)'
+    >>> wkt_w.precision = 1
+    >>> wkt_w.write(pnt)
+    'POINT (1.4 1.7)'
 
 .. rubric:: Footnotes
 .. [#fnogc] *See* `PostGIS EWKB, EWKT and Canonical Forms <http://postgis.net/docs/using_postgis_dbmanagement.html#EWKB_EWKT>`_, PostGIS documentation at Ch. 4.1.2.
diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt
index 64b92a26b6..7be22b12f4 100644
--- a/docs/releases/1.10.txt
+++ b/docs/releases/1.10.txt
@@ -94,6 +94,11 @@ Minor features
 
 * Added support for instantiating empty GEOS geometries.
 
+* The new :attr:`~django.contrib.gis.geos.WKTWriter.trim` and
+  :attr:`~django.contrib.gis.geos.WKTWriter.precision` properties
+  of :class:`~django.contrib.gis.geos.WKTWriter` allow controlling
+  output of the fractional part of the coordinates in WKT.
+
 :mod:`django.contrib.messages`
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/tests/gis_tests/geos_tests/test_io.py b/tests/gis_tests/geos_tests/test_io.py
index 59cb54b6b4..920767ded4 100644
--- a/tests/gis_tests/geos_tests/test_io.py
+++ b/tests/gis_tests/geos_tests/test_io.py
@@ -1,17 +1,17 @@
 from __future__ import unicode_literals
 
 import binascii
-import unittest
 from unittest import skipUnless
 
 from django.contrib.gis.geos import (
-    HAS_GEOS, GEOSGeometry, WKBReader, WKBWriter, WKTReader, WKTWriter,
+    HAS_GEOS, GEOSGeometry, Point, WKBReader, WKBWriter, WKTReader, WKTWriter,
 )
+from django.test import SimpleTestCase
 from django.utils.six import memoryview
 
 
 @skipUnless(HAS_GEOS, "Geos is required.")
-class GEOSIOTest(unittest.TestCase):
+class GEOSIOTest(SimpleTestCase):
 
     def test01_wktreader(self):
         # Creating a WKTReader instance
@@ -109,3 +109,38 @@ class GEOSIOTest(unittest.TestCase):
         wkb_w.srid = True
         self.assertEqual(hex3d_srid, wkb_w.write_hex(g))
         self.assertEqual(wkb3d_srid, wkb_w.write(g))
+
+    def test_wkt_writer_trim(self):
+        wkt_w = WKTWriter()
+        self.assertFalse(wkt_w.trim)
+        self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1.0000000000000000 1.0000000000000000)')
+
+        wkt_w.trim = True
+        self.assertTrue(wkt_w.trim)
+        self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1 1)')
+        self.assertEqual(wkt_w.write(Point(1.1, 1)), b'POINT (1.1 1)')
+        self.assertEqual(wkt_w.write(Point(1. / 3, 1)), b'POINT (0.3333333333333333 1)')
+
+        wkt_w.trim = False
+        self.assertFalse(wkt_w.trim)
+        self.assertEqual(wkt_w.write(Point(1, 1)), b'POINT (1.0000000000000000 1.0000000000000000)')
+
+    def test_wkt_writer_precision(self):
+        wkt_w = WKTWriter()
+        self.assertEqual(wkt_w.precision, None)
+        self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3333333333333333 0.6666666666666666)')
+
+        wkt_w.precision = 1
+        self.assertEqual(wkt_w.precision, 1)
+        self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3 0.7)')
+
+        wkt_w.precision = 0
+        self.assertEqual(wkt_w.precision, 0)
+        self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0 1)')
+
+        wkt_w.precision = None
+        self.assertEqual(wkt_w.precision, None)
+        self.assertEqual(wkt_w.write(Point(1. / 3, 2. / 3)), b'POINT (0.3333333333333333 0.6666666666666666)')
+
+        with self.assertRaisesMessage(AttributeError, 'WKT output rounding precision must be '):
+            wkt_w.precision = 'potato'