From 1cacbf5f7095c952de8c2b453f37920f81cacea9 Mon Sep 17 00:00:00 2001 From: Jacob Wasserman Date: Tue, 12 Nov 2013 19:54:25 -0500 Subject: [PATCH 1/2] BUG: Fix GeoSeries constructor for multi geoms Because Multi* geometries are iterable, they get expanded when passed into np.array(). Protect against this by passing in all base geometries as a single element list. They will not be expanded into their individual parts. Switch on older versions of pandas when Series overrode __new__ instead of __init__. --- geopandas/geoseries.py | 10 ++++++++++ tests/test_geoseries.py | 22 +++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/geopandas/geoseries.py b/geopandas/geoseries.py index 42b6ecb..dd6ef27 100644 --- a/geopandas/geoseries.py +++ b/geopandas/geoseries.py @@ -27,13 +27,20 @@ def _is_empty(x): except: return False +def _convert_array_args(args): + if len(args) == 1 and isinstance(args[0], BaseGeometry): + args = ([args[0]],) + return args class GeoSeries(Series): """A Series object designed to store shapely geometry objects.""" _metadata = ['name', 'crs'] def __new__(cls, *args, **kwargs): + if OLD_PANDAS: + args = _convert_array_args(args) kwargs.pop('crs', None) + arr = Series.__new__(cls, *args, **kwargs) if type(arr) is GeoSeries: return arr @@ -41,7 +48,10 @@ class GeoSeries(Series): return arr.view(GeoSeries) def __init__(self, *args, **kwargs): + if not OLD_PANDAS: + args = _convert_array_args(args) crs = kwargs.pop('crs', None) + super(GeoSeries, self).__init__(*args, **kwargs) self.crs = crs diff --git a/tests/test_geoseries.py b/tests/test_geoseries.py index 2e86dcd..22b15b4 100644 --- a/tests/test_geoseries.py +++ b/tests/test_geoseries.py @@ -5,7 +5,8 @@ import unittest import numpy as np from numpy.testing import assert_array_equal from pandas import Series -from shapely.geometry import Polygon, Point, LineString +from shapely.geometry import (Polygon, Point, LineString, + MultiPoint, MultiLineString, MultiPolygon) from shapely.geometry.base import BaseGeometry from geopandas import GeoSeries from pandas import Series @@ -41,6 +42,25 @@ class TestSeries(unittest.TestCase): def tearDown(self): shutil.rmtree(self.tempdir) + def test_single_geom_constructor(self): + p = Point(1,2) + line = LineString([(2, 3), (4, 5), (5, 6)]) + poly = Polygon([(0, 0), (1, 0), (1, 1)], + [[(.1, .1), (.9, .1), (.9, .9)]]) + mp = MultiPoint([(1, 2), (3, 4), (5, 6)]) + mline = MultiLineString([[(1, 2), (3, 4), (5, 6)], [(7, 8), (9, 10)]]) + + poly2 = Polygon([(1, 1), (1, -1), (-1, -1), (-1, 1)], + [[(.5, .5), (.5, -.5), (-.5, -.5), (-.5, .5)]]) + mpoly = MultiPolygon([poly, poly2]) + + geoms = [p, line, poly, mp, mline, mpoly] + + for g in geoms: + gs = GeoSeries(g) + self.assert_(len(gs) == 1) + self.assert_(gs.iloc[0] is g) + def test_area(self): self.assertTrue(type(self.g1.area) is Series) assert_array_equal(self.g1.area.values, np.array([0.5, 1.0])) From bb58ce7c3543c4060c062e68aab1141067a29334 Mon Sep 17 00:00:00 2001 From: Jacob Wasserman Date: Tue, 12 Nov 2013 20:30:55 -0500 Subject: [PATCH 2/2] TST: Test GeoSeries constructor with index Make sure that when a single geometry is passed in and the index contains multiple elements, the geometry is replicated in the GeoSeries. As suggested by @jtratner in Issue #70. --- tests/test_geoseries.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_geoseries.py b/tests/test_geoseries.py index 22b15b4..422df2e 100644 --- a/tests/test_geoseries.py +++ b/tests/test_geoseries.py @@ -55,12 +55,18 @@ class TestSeries(unittest.TestCase): mpoly = MultiPolygon([poly, poly2]) geoms = [p, line, poly, mp, mline, mpoly] + index = ['a', 'b', 'c', 'd'] for g in geoms: gs = GeoSeries(g) self.assert_(len(gs) == 1) self.assert_(gs.iloc[0] is g) + gs = GeoSeries(g, index=index) + self.assert_(len(gs) == len(index)) + for x in gs: + self.assert_(x is g) + def test_area(self): self.assertTrue(type(self.g1.area) is Series) assert_array_equal(self.g1.area.values, np.array([0.5, 1.0]))