Repository: SciTools/cartopy

Issue:
Add point support to project_geometry
The method `project_geometry` currently supports the following geometry types [(approx line 77 of crs.py)](https://github.com/SciTools/cartopy/blob/master/lib/cartopy/crs.py):

```
    _method_map = {
        'LineString': '_project_line_string',
        'LinearRing': '_project_linear_ring',
        'Polygon': '_project_polygon',
        'MultiLineString': '_project_multiline',
        'MultiPolygon': '_project_multipolygon',
    }
```

It would be nice to support Point types...



Discussion:

Resolving patch:
diff --git a/lib/cartopy/crs.py b/lib/cartopy/crs.py
--- a/lib/cartopy/crs.py
+++ b/lib/cartopy/crs.py
@@ -77,9 +77,11 @@ class Projection(CRS):
     __metaclass__ = ABCMeta
 
     _method_map = {
+        'Point': '_project_point',
         'LineString': '_project_line_string',
         'LinearRing': '_project_linear_ring',
         'Polygon': '_project_polygon',
+        'MultiPoint': '_project_multipoint',
         'MultiLineString': '_project_multiline',
         'MultiPolygon': '_project_multipolygon',
     }
@@ -163,6 +165,9 @@ def project_geometry(self, geometry, src_crs=None):
                              'type {!r}'.format(geom_type))
         return getattr(self, method_name)(geometry, src_crs)
 
+    def _project_point(self, point, src_crs):
+        return sgeom.Point(*self.transform_point(point.x, point.y, src_crs))
+
     def _project_line_string(self, geometry, src_crs):
         return cartopy.trace.project_linear(geometry, src_crs, self)
 
@@ -233,6 +238,15 @@ def _project_linear_ring(self, linear_ring, src_crs):
 
         return result_geometry
 
+    def _project_multipoint(self, geometry, src_crs):
+        geoms = []
+        for geom in geometry.geoms:
+            geoms.append(self._project_point(geom, src_crs))
+        if geoms:
+            return sgeom.MultiPoint(geoms)
+        else:
+            return sgeom.MultiPoint()
+
     def _project_multiline(self, geometry, src_crs):
         geoms = []
         for geom in geometry.geoms:


Test patch:
diff --git a/lib/cartopy/tests/test_crs.py b/lib/cartopy/tests/test_crs.py
--- a/lib/cartopy/tests/test_crs.py
+++ b/lib/cartopy/tests/test_crs.py
@@ -23,7 +23,7 @@
 import numpy as np
 from numpy.testing import assert_array_almost_equal as assert_arr_almost_eq
 from nose.tools import assert_equal
-
+import shapely.geometry as sgeom
 
 import cartopy.crs as ccrs
 
@@ -152,6 +152,22 @@ def test_globe(self):
         assert_arr_almost_eq(rugby_pt, (1400915, 1741319), decimal=0)
         assert_arr_almost_eq(footy_pt, (155657, 193479), decimal=0)
 
+    def test_project_point(self):
+        point = sgeom.Point([0, 45])
+        multi_point = sgeom.MultiPoint([point, sgeom.Point([180, 45])])
+
+        pc = ccrs.PlateCarree()
+        pc_rotated = ccrs.PlateCarree(central_longitude=180)
+
+        result = pc_rotated.project_geometry(point, pc)
+        assert_arr_almost_eq(result.xy, [[-180.], [45.]])
+
+        result = pc_rotated.project_geometry(multi_point, pc)
+        self.assertIsInstance(result, sgeom.MultiPoint)
+        self.assertEqual(len(result), 2)
+        assert_arr_almost_eq(result[0].xy, [[-180.], [45.]])
+        assert_arr_almost_eq(result[1].xy, [[0], [45.]])
+
 
 def test_pickle():
     # check that we can pickle a simple CRS