From 5ce660cd65a4ffcf74d94ba987fe03a16b7436a0 Mon Sep 17 00:00:00 2001
From: Sergey Fedoseev <fedoseev.sergey@gmail.com>
Date: Fri, 10 Jun 2016 11:50:07 +0500
Subject: [PATCH] Fixed #25940 -- Added OGRGeometry.from_gml() and
 GEOSGeometry.from_gml().

---
 django/contrib/gis/gdal/geometries.py      |  5 +++++
 django/contrib/gis/gdal/prototypes/geom.py |  1 +
 django/contrib/gis/geos/geometry.py        |  4 ++++
 docs/ref/contrib/gis/gdal.txt              |  6 ++++++
 docs/ref/contrib/gis/geos.txt              |  6 ++++++
 docs/releases/1.11.txt                     |  3 ++-
 tests/gis_tests/gdal_tests/test_geom.py    | 10 ++++++++++
 tests/gis_tests/geos_tests/test_geos.py    | 10 ++++++++++
 8 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/django/contrib/gis/gdal/geometries.py b/django/contrib/gis/gdal/geometries.py
index 1d76da18d8..4407e147e5 100644
--- a/django/contrib/gis/gdal/geometries.py
+++ b/django/contrib/gis/gdal/geometries.py
@@ -52,6 +52,7 @@ from django.contrib.gis.gdal.prototypes import geom as capi, srs as srs_api
 from django.contrib.gis.gdal.srs import CoordTransform, SpatialReference
 from django.contrib.gis.geometry.regex import hex_regex, json_regex, wkt_regex
 from django.utils import six
+from django.utils.encoding import force_bytes
 from django.utils.six.moves import range
 
 
@@ -150,6 +151,10 @@ class OGRGeometry(GDALBase):
         return OGRGeometry('POLYGON((%s %s, %s %s, %s %s, %s %s, %s %s))' % (
             x0, y0, x0, y1, x1, y1, x1, y0, x0, y0))
 
+    @classmethod
+    def from_gml(cls, gml_string):
+        return cls(capi.from_gml(force_bytes(gml_string)))
+
     # ### Geometry set-like operations ###
     # g = g1 | g2
     def __or__(self, other):
diff --git a/django/contrib/gis/gdal/prototypes/geom.py b/django/contrib/gis/gdal/prototypes/geom.py
index d1c43a47c2..aac8b99bae 100644
--- a/django/contrib/gis/gdal/prototypes/geom.py
+++ b/django/contrib/gis/gdal/prototypes/geom.py
@@ -44,6 +44,7 @@ getz = pnt_func(lgdal.OGR_G_GetZ)
 # Geometry creation routines.
 from_wkb = geom_output(lgdal.OGR_G_CreateFromWkb, [c_char_p, c_void_p, POINTER(c_void_p), c_int], offset=-2)
 from_wkt = geom_output(lgdal.OGR_G_CreateFromWkt, [POINTER(c_char_p), c_void_p, POINTER(c_void_p)], offset=-1)
+from_gml = geom_output(lgdal.OGR_G_CreateFromGML, [c_char_p])
 create_geom = geom_output(lgdal.OGR_G_CreateGeometry, [c_int])
 clone_geom = geom_output(lgdal.OGR_G_Clone, [c_void_p])
 get_geom_ref = geom_output(lgdal.OGR_G_GetGeometryRef, [c_void_p, c_int])
diff --git a/django/contrib/gis/geos/geometry.py b/django/contrib/gis/geos/geometry.py
index d0a9455659..66f78e70de 100644
--- a/django/contrib/gis/geos/geometry.py
+++ b/django/contrib/gis/geos/geometry.py
@@ -166,6 +166,10 @@ class GEOSGeometry(GEOSBase, ListMixin):
         self.ptr = ptr
         self._post_init(srid)
 
+    @classmethod
+    def from_gml(cls, gml_string):
+        return gdal.OGRGeometry.from_gml(gml_string).geos
+
     # Comparison operators
     def __eq__(self, other):
         """
diff --git a/docs/ref/contrib/gis/gdal.txt b/docs/ref/contrib/gis/gdal.txt
index da81ce5e03..9bd2df8dad 100644
--- a/docs/ref/contrib/gis/gdal.txt
+++ b/docs/ref/contrib/gis/gdal.txt
@@ -448,6 +448,12 @@ coordinate transformation::
 
     __ http://www.gdal.org/classOGRGeometry.html
 
+    .. classmethod:: from_gml(gml_string)
+
+    .. versionadded:: 1.11
+
+    Constructs an :class:`OGRGeometry` from the given GML string.
+
     .. classmethod:: from_bbox(bbox)
 
     Constructs a :class:`Polygon` from the given bounding-box (a 4-tuple).
diff --git a/docs/ref/contrib/gis/geos.txt b/docs/ref/contrib/gis/geos.txt
index 811a9ba946..e6d609736e 100644
--- a/docs/ref/contrib/gis/geos.txt
+++ b/docs/ref/contrib/gis/geos.txt
@@ -198,6 +198,12 @@ WKB / EWKB               ``buffer``
 GeoJSON                  ``str`` or ``unicode``
 =======================  ======================
 
+.. classmethod:: GEOSGeometry.from_gml(gml_string)
+
+    .. versionadded:: 1.11
+
+    Constructs a :class:`GEOSGeometry` from the given GML string.
+
 Properties
 ~~~~~~~~~~
 
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index 9a1cc81d8d..001fb1ead1 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -74,7 +74,8 @@ Minor features
 :mod:`django.contrib.gis`
 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* The new :meth:`.GEOSGeometry.from_gml` and :meth:`.OGRGeometry.from_gml`
+  methods allow creating geometries from GML.
 
 :mod:`django.contrib.messages`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/gis_tests/gdal_tests/test_geom.py b/tests/gis_tests/gdal_tests/test_geom.py
index 163399289a..7ec1b4b990 100644
--- a/tests/gis_tests/gdal_tests/test_geom.py
+++ b/tests/gis_tests/gdal_tests/test_geom.py
@@ -543,3 +543,13 @@ class OGRGeomTest(unittest.TestCase, TestDataMixin):
             OGRGeometry('POINT(0.5 0.5)').within(OGRGeometry('POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))')), True
         )
         self.assertIs(OGRGeometry('POINT(0 0)').within(OGRGeometry('POINT(0 1)')), False)
+
+    def test_from_gml(self):
+        self.assertEqual(
+            OGRGeometry('POINT(0 0)'),
+            OGRGeometry.from_gml(
+                '<gml:Point gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">'
+                '    <gml:pos srsDimension="2">0 0</gml:pos>'
+                '</gml:Point>'
+            ),
+        )
diff --git a/tests/gis_tests/geos_tests/test_geos.py b/tests/gis_tests/geos_tests/test_geos.py
index 36457f4f51..a7724de9bc 100644
--- a/tests/gis_tests/geos_tests/test_geos.py
+++ b/tests/gis_tests/geos_tests/test_geos.py
@@ -1300,6 +1300,16 @@ class GEOSTest(SimpleTestCase, TestDataMixin):
             self.assertEqual(m.group('version'), v_geos)
             self.assertEqual(m.group('capi_version'), v_capi)
 
+    def test_from_gml(self):
+        self.assertEqual(
+            GEOSGeometry('POINT(0 0)'),
+            GEOSGeometry.from_gml(
+                '<gml:Point gml:id="p21" srsName="http://www.opengis.net/def/crs/EPSG/0/4326">'
+                '    <gml:pos srsDimension="2">0 0</gml:pos>'
+                '</gml:Point>'
+            ),
+        )
+
     @ignore_warnings(category=RemovedInDjango20Warning)
     def test_deprecated_srid_getters_setters(self):
         p = Point(1, 2, srid=123)