Vectorized creation of shapely Polygons from GeoPandas DataFrame
Question:
I have a GeoDataFrame with a point geometry.
From the point geometry, I want to define a square polygon geometry in a quite straightforward manner.
Given a point, the point should be the left bottom corner in a square with sides 250 units of length.
I.e, left bottom corner is the current point, right bottom corner is the current point + 250 on the x axis etc.
My naive way of doing this is the following:
Create the corners as new columns in the GeoDataFrame:
After that, I try to define a new columns as:
gdf['POLY'] = shapely.Geometry([gdf['BOTTOM_LEFT'], gdf['BOTTOM_RIGHT'], gdf['TOP_LEFT'], gdf['TOP_RIGHT']])
But this returns the following error message:
AttributeError: 'list' object has no attribute '__array_interface__'
Answers:
Let’s assume you have a GeoDataFrame with only single point. It is called gdf
and it looks as follows:
X Y geometry
0 5 6 POINT (5.00000 6.00000)
You can access the x and y components of the point using the following lambda function:
#Access x and y components of point geometry
X = gdf.geometry.apply(lambda x: x.x)
Y = gdf.geometry.apply(lambda x: x.y)
Now you can create a square object using shapely.geometry.Polygon
. You need to specify the four vertices of the square. You can do it using:
gdf_square = shapely.geometry.Polygon([[X[0], Y[0]],
[X[0]+250, Y[0]],
[X[0]+250, Y[0]+250],
[X[0], Y[0]+250]])
You can get a square polygon object as shown below:
Note that if you have many points in the GeoDataFrame, modify the last function such that it creates the square polygon for point in each row one by one.
Your implementation is close, but you can’t call shapely.geometry.Polygon with an array of points – it can only be done one at a time. So the trick is to use df.apply
to call Polygon on every row of the DataFrame:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[s['BOTTOM_LEFT'], s['BOTTOM_RIGHT'], s['TOP_LEFT'], s['TOP_RIGHT']],
axis=1,
)
)
You could do that with your original point using translate
:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[
s['POINT'],
s['POINT'].translate(xoff=250),
s['POINT'].translate(yoff=250, xoff=250),
s['POINT'].translate(yoff=250),
],
axis=1,
)
)
In my case it was more than 5 times faster to build the triangles using list comprehension than using geopandas.apply :
polys = [Polygon(((x, y), (x, y+d), (x+d, y+d), (x+d, y))) for x in xs for y in ys]
gdf = gpd.GeoDataFrame(geometry=polys)
I have a GeoDataFrame with a point geometry.
From the point geometry, I want to define a square polygon geometry in a quite straightforward manner.
Given a point, the point should be the left bottom corner in a square with sides 250 units of length.
I.e, left bottom corner is the current point, right bottom corner is the current point + 250 on the x axis etc.
My naive way of doing this is the following:
Create the corners as new columns in the GeoDataFrame:
After that, I try to define a new columns as:
gdf['POLY'] = shapely.Geometry([gdf['BOTTOM_LEFT'], gdf['BOTTOM_RIGHT'], gdf['TOP_LEFT'], gdf['TOP_RIGHT']])
But this returns the following error message:
AttributeError: 'list' object has no attribute '__array_interface__'
Let’s assume you have a GeoDataFrame with only single point. It is called gdf
and it looks as follows:
X Y geometry
0 5 6 POINT (5.00000 6.00000)
You can access the x and y components of the point using the following lambda function:
#Access x and y components of point geometry
X = gdf.geometry.apply(lambda x: x.x)
Y = gdf.geometry.apply(lambda x: x.y)
Now you can create a square object using shapely.geometry.Polygon
. You need to specify the four vertices of the square. You can do it using:
gdf_square = shapely.geometry.Polygon([[X[0], Y[0]],
[X[0]+250, Y[0]],
[X[0]+250, Y[0]+250],
[X[0], Y[0]+250]])
You can get a square polygon object as shown below:
Note that if you have many points in the GeoDataFrame, modify the last function such that it creates the square polygon for point in each row one by one.
Your implementation is close, but you can’t call shapely.geometry.Polygon with an array of points – it can only be done one at a time. So the trick is to use df.apply
to call Polygon on every row of the DataFrame:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[s['BOTTOM_LEFT'], s['BOTTOM_RIGHT'], s['TOP_LEFT'], s['TOP_RIGHT']],
axis=1,
)
)
You could do that with your original point using translate
:
gdf['geometry'] = gdf.apply(
lambda s: shapely.geometry.Polygon(
[
s['POINT'],
s['POINT'].translate(xoff=250),
s['POINT'].translate(yoff=250, xoff=250),
s['POINT'].translate(yoff=250),
],
axis=1,
)
)
In my case it was more than 5 times faster to build the triangles using list comprehension than using geopandas.apply :
polys = [Polygon(((x, y), (x, y+d), (x+d, y+d), (x+d, y))) for x in xs for y in ys]
gdf = gpd.GeoDataFrame(geometry=polys)