From 34631d26b25041310a7908eecafdb7c3d1ffe00f Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 12 Aug 2014 11:06:56 -0700 Subject: [PATCH] Added support for null geometries for to_file and from_file functions - Closes-Bug: #138 --- examples/null_geom.geojson | 9 +++++++++ geopandas/geodataframe.py | 9 ++++----- tests/test_geodataframe.py | 13 ++++++++++++- 3 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 examples/null_geom.geojson diff --git a/examples/null_geom.geojson b/examples/null_geom.geojson new file mode 100644 index 0000000..9bbbadc --- /dev/null +++ b/examples/null_geom.geojson @@ -0,0 +1,9 @@ +{ +"type": "FeatureCollection", +"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, + +"features": [ +{ "type": "Feature", "properties": { "Name": "Null Geometry" }, "geometry": null }, +{ "type": "Feature", "properties": { "Name": "SF to NY" }, "geometry": { "type": "LineString", "coordinates": [ [ -122.4051293283311, 37.786780113640894 ], [ -73.859832357849271, 40.487594916296196 ] ] } } +] +} diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py index 4831a82..adb2350 100644 --- a/geopandas/geodataframe.py +++ b/geopandas/geodataframe.py @@ -68,7 +68,6 @@ class GeoDataFrame(GeoPandasBase, DataFrame): if not isinstance(col, (list, np.ndarray, Series)): raise ValueError("Must use a list-like to set the geometry" " property") - self.set_geometry(col, inplace=True) geometry = property(fget=_get_geometry, fset=_set_geometry, @@ -177,7 +176,7 @@ class GeoDataFrame(GeoPandasBase, DataFrame): else: f = f - d = {'geometry': shape(f['geometry'])} + d = {'geometry': shape(f['geometry']) if f['geometry'] else None} d.update(f['properties']) rows.append(d) df = GeoDataFrame.from_dict(rows) @@ -326,8 +325,8 @@ class GeoDataFrame(GeoPandasBase, DataFrame): 'type': 'Feature', 'properties': dict((k, v) for k, v in iteritems(row) if k != 'geometry'), - 'geometry': mapping(row['geometry'])} - + 'geometry': mapping(row['geometry']) if row['geometry'] + else None} properties = OrderedDict([(col, convert_type(_type)) for col, _type in zip(self.columns, self.dtypes) if col != 'geometry']) # Need to check geom_types before we write to file... @@ -335,7 +334,7 @@ class GeoDataFrame(GeoPandasBase, DataFrame): # Point, LineString, or Polygon geom_types = self['geometry'].geom_type.unique() from os.path import commonprefix # To find longest common prefix - geom_type = commonprefix([g[::-1] for g in geom_types])[::-1] # Reverse + geom_type = commonprefix([g[::-1] for g in geom_types if g])[::-1] if geom_type == '': # No common suffix = mixed geometry types raise ValueError("Geometry column cannot contains mutiple " "geometry types when writing to file.") diff --git a/tests/test_geodataframe.py b/tests/test_geodataframe.py index 730e9cc..458ff91 100644 --- a/tests/test_geodataframe.py +++ b/tests/test_geodataframe.py @@ -30,6 +30,8 @@ class TestDataFrame(unittest.TestCase): self.df2 = GeoDataFrame([ {'geometry': Point(x, y), 'value1': x + y, 'value2': x * y} for x, y in zip(range(N), range(N))], crs=self.crs) + self.df3 = read_file('examples/null_geom.geojson') + self.line_paths = self.df3['Name'] def tearDown(self): shutil.rmtree(self.tempdir) @@ -290,12 +292,21 @@ class TestDataFrame(unittest.TestCase): """ Test to_file and from_file """ tempfilename = os.path.join(self.tempdir, 'boros.shp') self.df.to_file(tempfilename) - # Read layer back in? + # Read layer back in df = GeoDataFrame.from_file(tempfilename) self.assertTrue('geometry' in df) self.assertTrue(len(df) == 5) self.assertTrue(np.alltrue(df['BoroName'].values == self.boros)) + # Write layer with null geometry out to file + tempfilename = os.path.join(self.tempdir, 'null_geom.shp') + self.df3.to_file(tempfilename) + # Read layer back in + df3 = GeoDataFrame.from_file(tempfilename) + self.assertTrue('geometry' in df3) + self.assertTrue(len(df3) == 2) + self.assertTrue(np.alltrue(df3['Name'].values == self.line_paths)) + def test_to_file_types(self): """ Test various integer type columns (GH#93) """ tempfilename = os.path.join(self.tempdir, 'int.shp')