0

I want to create a buffer (20km) from a line, and then separate them into two polygons with the line is boundary between two polygons (picture demo below. The desired output would be two polygons such as polygon 1 and 2). I tried to use geopandas but it does not work as I expected.

Here is sample code and data.

import geopandas as gpd
# the line vector data
data=gpd.read_file("https://raw.githubusercontent.com/tuyenhavan/test_data/main/Thailand_Cambodia_Line.json")
# make buffer. Data are already in UTM 48N system
data_explode=data.explode(index_parts=True)
# buffer 20km
buffer_20km=data_explode.buffer(20000)

geo_df=gpd.GeoDataFrame(geometry=buffer_20km)

dissolve=geo_df.dissolve()

enter image description here

3 Answers 3

4

You can use shapelys split:

import geopandas as gpd
from shapely.ops import split

df = gpd.read_file(r"C:/TEMP/a_line.shp")
crs = df.crs #Save crs, it gets lost in the splitting

#When buffering the buffer gets slighty longer than the line and the split doesnt work.
#I want to buffer by 700, so first +705 then -5 to make the line extend the buffer ends
#or the splitting doesnt work.
df["buff"] = df.geometry.buffer(705, cap_style=2).buffer(-5) #Create a buffer geometry column


#Split each buffer by the line, and create a list of the split parts
df["split_parts"] = df.apply(lambda x: [part for part in split(x["buff"], x["geometry"]).geoms], axis=1)
df = df.explode(column="split_parts") #Explode to rows

df = df.set_geometry("split_parts") #Set the geometry of df to the split
df = df.drop(["geometry","buff"], axis="columns").rename_geometry("geometry") #Drop the old geometries or they can cause problems when saving to file
df.crs = crs
df.to_file(r"C:/TEMP/a_line_buffered_split.shp")

enter image description here

1
  • I had a solution as you offer but OP uses the round cap type in the example. You use flat-type cap. Mar 30 at 21:39
3

we also suggest this solution based on two one-sided buffers. See also here: Buffering one side of a line

import shapely 
import geopandas as gpd

# Create line
line = gpd.GeoSeries(shapely.geometry.LineString([(0, 2), (4, 2)]))

# First create a buffer for only one side (positive)
polygon1 = line.buffer(1, single_sided=True)

# Then create a buffer for the other side (negative)
polygon2 = line.buffer(-1, single_sided=True)

ax = polygon1.plot(facecolor="lightblue")
polygon2.plot(ax=ax, facecolor="darkblue")
line.plot(ax=ax, edgecolor="red", linewidth=5)

Result:

enter image description here

1
  • I thought using singlesided=True option, but the source line in the question has 12000 segments and this option takes a very long time. I don't know why. However standard buffering takes only 1-2 second on the same line. Mar 30 at 21:31
1

Maybe just take the difference of the original line and the buffer, to get the polygon you wanted and seperate them afterwards, like:

import geopandas as gpd

# read in the line data
line_data = gpd.read_file("https://raw.githubusercontent.com/tuyenhavan/test_data/main/Thailand_Cambodia_Line.json")

# buffer the line
buffered_line = line_data.buffer(20000)

# calculate the difference between the buffered line and the original line
difference = buffered_line.difference(line_data.unary_union)

# convert the difference to a GeoDataFrame
difference_df = gpd.GeoDataFrame(geometry=difference)

# split the difference into two polygons by calculating the centroid of the line
centroid = line_data.unary_union.centroid
difference_df["side"] = difference_df.geometry.centroid.x.apply(lambda x: "left" if x < centroid.x else "right")

# separate the difference into two GeoDataFrames based on the "side" column
left_polygon = difference_df[difference_df.side == "left"]
right_polygon = difference_df[difference_df.side == "right"]

Your Answer

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

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