Repository: Toblerity/Shapely
Pull request #366 (2016-02-18 00:36:23)

Implemented GEOSHausdorffDistance
This PR implements GEOHausdorffDistance. In simple terms the hausdorff distance is the furthest distance between any point on one geometry to any point on another.

https://en.wikipedia.org/wiki/Hausdorff_distance

Available in GEOS 3.2.x.


Patch:
diff --git a/docs/manual.rst b/docs/manual.rst
index 7aa5ad4..71ac673 100644
--- a/docs/manual.rst
+++ b/docs/manual.rst
@@ -207,6 +207,23 @@ General Attributes and Methods
   >>> Point(0,0).distance(Point(1,1))
   1.4142135623730951
 
+.. method:: object.hausdorff_distance(other)
+
+  Returns the Hausdorff distance (``float``) to the `other` geometric object.
+  The Hausdorff distance is the furthest distance from any point on the first
+  geometry to any point on the second geometry.
+
+  `New in Shapely 1.6.0`
+
+.. code-block:: pycon
+
+  >>> point = Point(1, 1)
+  >>> line = LineString([(2, 0), (2, 4), (3, 4)])
+  >>> point.hausdorff_distance(line)
+  3.605551275463989
+  >>> point.distance(Point(3, 4))
+  3.605551275463989
+
 .. method:: object.representative_point()
 
   Returns a cheaply computed point that is guaranteed to be within the
diff --git a/shapely/ctypes_declarations.py b/shapely/ctypes_declarations.py
index ac38fc5..b86edca 100644
--- a/shapely/ctypes_declarations.py
+++ b/shapely/ctypes_declarations.py
@@ -402,6 +402,10 @@ def prototype(lgeos, geos_version):
     lgeos.GEOSDistance.restype = c_int
     lgeos.GEOSDistance.argtypes = [c_void_p, c_void_p, c_void_p]
 
+    if geos_version >= (3, 2, 0):
+        lgeos.GEOSHausdorffDistance.restype = c_int
+        lgeos.GEOSHausdorffDistance.argtypes = [c_void_p, c_void_p, c_void_p]
+
     '''
     Reader and Writer APIs
     '''
diff --git a/shapely/geometry/base.py b/shapely/geometry/base.py
index 4d4e922..ed5c318 100644
--- a/shapely/geometry/base.py
+++ b/shapely/geometry/base.py
@@ -424,6 +424,10 @@ class BaseGeometry(object):
         """Unitless distance to other geometry (float)"""
         return self.impl['distance'](self, other)
 
+    def hausdorff_distance(self, other):
+        """Unitless hausdorff distance to other geometry (float)"""
+        return self.impl['hausdorff_distance'](self, other)
+
     @property
     def length(self):
         """Unitless length of the geometry (float)"""
diff --git a/shapely/geos.py b/shapely/geos.py
index c767d72..bf17f30 100644
--- a/shapely/geos.py
+++ b/shapely/geos.py
@@ -651,6 +651,7 @@ class LGEOS320(LGEOS311):
         self.methods['interpolate_normalized'] = \
             self.GEOSInterpolateNormalized
         self.methods['buffer_with_style'] = self.GEOSBufferWithStyle
+        self.methods['hausdorff_distance'] = self.GEOSHausdorffDistance
 
 
 class LGEOS330(LGEOS320):
diff --git a/shapely/impl.py b/shapely/impl.py
index 828e0ae..99a2d49 100644
--- a/shapely/impl.py
+++ b/shapely/impl.py
@@ -140,6 +140,7 @@ IMPL320 = {
     'interpolate_normalized': (InterpolateOp, 'interpolate_normalized'),
     'interpolate': (InterpolateOp, 'interpolate'),
     'buffer_with_style': (UnaryTopologicalOp, 'buffer_with_style'),
+    'hausdorff_distance': (BinaryRealProperty, 'hausdorff_distance'),
     }
 
 IMPL330 = {


Test patch:
diff --git a/tests/test_operations.py b/tests/test_operations.py
index 3377646..f25f06a 100644
--- a/tests/test_operations.py
+++ b/tests/test_operations.py
@@ -1,8 +1,9 @@
 from . import unittest
 import pytest
-from shapely.geometry import Point, Polygon, MultiPoint, GeometryCollection
+from shapely.geometry import Point, LineString, Polygon, MultiPoint, \
+                             GeometryCollection
 from shapely.wkt import loads
-from shapely.geos import TopologicalError
+from shapely.geos import TopologicalError, geos_version
 
 class OperationsTestCase(unittest.TestCase):
 
@@ -73,5 +74,13 @@ class OperationsTestCase(unittest.TestCase):
         with pytest.raises(TopologicalError):
             invalid_polygon.relate(invalid_polygon)
 
+    @unittest.skipIf(geos_version < (3, 2, 0), 'GEOS 3.2.0 required')
+    def test_hausdorff_distance(self):
+        point = Point(1, 1)
+        line = LineString([(2, 0), (2, 4), (3, 4)])
+
+        distance = point.hausdorff_distance(line)
+        self.assertEqual(distance, point.distance(Point(3, 4)))
+
 def test_suite():
     return unittest.TestLoader().loadTestsFromTestCase(OperationsTestCase)