Repository: geopandas/geopandas

Issue:
VIS: support plotting of MultiPoints
See https://gis.stackexchange.com/questions/237786/ploting-multipoint-with-geopandas


Discussion:

Resolving patch:
diff --git a/geopandas/plotting.py b/geopandas/plotting.py
--- a/geopandas/plotting.py
+++ b/geopandas/plotting.py
@@ -163,13 +163,13 @@ def plot_point_collection(ax, geoms, values=None, color=None,
                           cmap=None, vmin=None, vmax=None,
                           marker='o', markersize=None, **kwargs):
     """
-    Plots a collection of Point geometries to `ax`
+    Plots a collection of Point and MultiPoint geometries to `ax`
 
     Parameters
     ----------
     ax : matplotlib.axes.Axes
         where shapes will be plotted
-    geoms : sequence of `N` Points
+    geoms : sequence of `N` Points or MultiPoints
 
     values : a sequence of `N` values, optional
         Values mapped to colors using vmin, vmax, and cmap.
@@ -186,8 +186,11 @@ def plot_point_collection(ax, geoms, values=None, color=None,
     if values is not None and color is not None:
         raise ValueError("Can only specify one of 'values' and 'color' kwargs")
 
-    x = geoms.x.values
-    y = geoms.y.values
+    geoms, values = _flatten_multi_geoms(geoms, values)
+    if None in values:
+        values = None
+    x = [p.x for p in geoms]
+    y = [p.y for p in geoms]
 
     # matplotlib 1.4 does not support c=None, and < 2.0 does not support s=None
     if values is not None:
@@ -269,7 +272,8 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
                           | (geom_types == 'MultiPolygon'))
     line_idx = np.asarray((geom_types == 'LineString')
                           | (geom_types == 'MultiLineString'))
-    point_idx = np.asarray(geom_types == 'Point')
+    point_idx = np.asarray((geom_types == 'Point')
+                          | (geom_types == 'MultiPoint'))
 
     # plot all Polygons and all MultiPolygon components in the same collection
     polys = s.geometry[poly_idx]
@@ -437,7 +441,8 @@ def plot_dataframe(df, column=None, cmap=None, color=None, ax=None,
                           | (geom_types == 'MultiPolygon'))
     line_idx = np.asarray((geom_types == 'LineString')
                           | (geom_types == 'MultiLineString'))
-    point_idx = np.asarray(geom_types == 'Point')
+    point_idx = np.asarray((geom_types == 'Point')
+                          | (geom_types == 'MultiPoint'))
 
     # plot all Polygons and all MultiPolygon components in the same collection
     polys = df.geometry[poly_idx]


Test patch:
diff --git a/geopandas/tests/test_plotting.py b/geopandas/tests/test_plotting.py
--- a/geopandas/tests/test_plotting.py
+++ b/geopandas/tests/test_plotting.py
@@ -9,7 +9,7 @@
 matplotlib.use('Agg')
 import matplotlib.pyplot as plt
 from shapely.affinity import rotate
-from shapely.geometry import MultiPolygon, Polygon, LineString, Point
+from shapely.geometry import MultiPolygon, Polygon, LineString, Point, MultiPoint
 
 from geopandas import GeoSeries, GeoDataFrame, read_file
 
@@ -34,9 +34,15 @@ class TestPointPlotting:
     def setup_method(self):
         self.N = 10
         self.points = GeoSeries(Point(i, i) for i in range(self.N))
+
         values = np.arange(self.N)
         self.df = GeoDataFrame({'geometry': self.points, 'values': values})
 
+        multipoint1 = MultiPoint(self.points)
+        multipoint2 = rotate(multipoint1, 90)
+        self.df2 = GeoDataFrame({'geometry': [multipoint1, multipoint2],
+                                 'values': [0, 1]})
+
     def test_figsize(self):
 
         ax = self.points.plot(figsize=(1, 1))
@@ -167,6 +173,20 @@ def test_empty_plot(self):
             ax = df.plot()
         assert len(ax.collections) == 0
 
+    def test_multipoints(self):
+
+        # MultiPoints
+        ax = self.df2.plot()
+        _check_colors(4, ax.collections[0].get_facecolors(),
+                      [MPL_DFT_COLOR] * 4)
+
+
+        ax = self.df2.plot(column='values')
+        cmap = plt.get_cmap()
+        expected_colors = [cmap(0)]* self.N + [cmap(1)] * self.N
+        _check_colors(2, ax.collections[0].get_facecolors(),
+                      expected_colors)
+
 class TestPointZPlotting:
 
     def setup_method(self):