6

I want to create a valid Shape object in shapely which has properties but has no geometry.

e.g. For creating features to be saved into a shapefile. All features have attributes/properties but some have null/empty geometry.

I am currently using the workaround of creating an empty GeometryCollection.

s = shapely.geometry.collection.GeometryCollection()
s.properties = {"prop1":"val1"}

Is there a more suitable/valid approach or a better shapely geometry class for storing null/empty geometry?

2 Answers 2

6

You mix two thinks, Shapely and the GeoJSON format.

By definition, Shapely process geometric objects and does not deal with attributes

from shapely.geometry import GeometryCollection, mapping
s = GeometryCollection()
# GeoJSON format
geo = mapping(s)
print geo
{'type': 'GeometryCollection', 'geometries': []}

Now if you want to add attributes, you can use

1) a simple dictionary

feature = {}
feature['geometry'] = GeometryCollection()
feature['properties'] = {"prop1":"val1"}
print feature
{'geometry': <shapely.geometry.collection.GeometryCollection object at 0x107b594d0>, 'properties': {'prop1': 'val1'}}

2) the geo_interface (GeoJSON like) protocol used by Shapely, Fiona, among others (see Python Geo_interface applications))

geojson = {"type":"feature","geometry":geo,  "properties":  {"prop1":"val1"}}
print geojson
{'geometry': {'type': 'GeometryCollection', 'geometries': []}, 'type': 'feature', 'properties': {'prop1': 'val1'}}

And you don't need shapely for that, simply use Python dictionaries

geo = {'type': 'GeometryCollection', 'geometries': []}
geojson = {"type":"feature","geometry":geo,  "properties":  {"prop1":"val1"}}

3) If you have many features

geojsons = [{"type":"feature","geometry":geo,  "properties":  {"prop1":"val1"}},{"type":"feature","geometry":geo,  "properties":  {"prop1":"val2"}},{"type":"feature","geometry":geo,  "properties":  {"prop1":"val3"}},{"type":"feature","geometry":geo,  "properties":  {"prop1":"val4"}}]
result = {"type": "FeatureCollection",  "features":geojsons,}
print result
{'type': 'FeatureCollection', 'features': [{'geometry': {'type': 'GeometryCollection', 'geometries': []}, 'type': 'feature', 'properties': {'prop1': 'val1'}}, {'geometry': {'type': 'GeometryCollection', 'geometries': []}, 'type': 'feature', 'properties': {'prop1': 'val2'}}, {'geometry': {'type': 'GeometryCollection', 'geometries': []}, 'type': 'feature', 'properties': {'prop1': 'val3'}}, {'geometry': {'type': 'GeometryCollection', 'geometries': []}, 'type': 'feature', 'properties': {'prop1': 'val4'}}]}

But a shapefile without geometry makes no sense, why not use a simple database or a GeoPandas GeoDataFrame ?

import pandas as pd
import geopandas as gpd
df = pd.DataFrame({'prop1' : ['Val1','Val2','Val3']})
geometry = [GeometryCollection(),GeometryCollection(),GeometryCollection()]
# convert the DataFrame to a GeoDataFrame
geo_df = gpd.GeoDataFrame(df,geometry=geometry)
print geo_df
   prop1  geometry
0  Val1   ()
1  Val2   ()
2  Val3   ()

If you want to add real geometries after, simply change the values of the 'geometry' tag in geojsons or the 'geometry' column in the GeoDataFrame and you can save the resulting shapefile with Fiona or directly with GeoPandas

 geo_df['geometry'] = geo_df['geometry'].apply(lambda x : Point(1,2))
 print geo_df
   prop1     geometry
 0  Val1  POINT (1 2)
 1  Val2  POINT (1 2)
 2  Val3  POINT (1 2)

Save the resulting shapefile

geo_df.to_file('out.shp', driver='ESRI Shapefile')
2
  • Many thanks @gene this was useful to help untangle/understand the different tools. I am still interested to know if there is a way to create a valid shapely shape with Null geometry. I believe this might help interoperability with PostGIS/MySQL, which allow multiple records in the same table, where some have actual geometry and others are simply Null. e.g. Perhaps something like s = shapely.geometry.collection.BaseGeometry()? Or perhaps an empty GeometryCollection already allows this? May 1, 2016 at 10:41
  • 1
    The purpose of Shapely is not to work with empty geometries ( valid shapely shape with Null geometry) and tables (multiple records). For that you can simply use GeoJSON. You can eventually create an empty GeometryCollection (GeometryCollection() and fill it after
    – gene
    May 1, 2016 at 18:29
5

Creating an empty geometry collection is the recommended way to get a null geometry.

From The Shapely User Manual:

An “empty” feature is one with a point set that coincides with the empty set; not None, but like set([]). Empty features can be created by calling the various constructors with no arguments. Almost no operations are supported by empty features.

Hence, what you have been doing is the standard way to create a Null geometry in Shapely. You can make it a little bit shorter:

from shapely.geometry import Point # or Polygon, LineString, etc. It doesn't matter.
empty = Point()
print empty # prints "GEOMETRYCOLLECTION EMPTY"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.