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

PolyGeo
65.5k29 gold badges115 silver badges349 bronze badges
asked Mar 29, 2023 at 11:35

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

answered Mar 30, 2023 at 7:54
1
  • I had a solution as you offer but OP uses the round cap type in the example. You use flat-type cap. Commented Mar 30, 2023 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

answered Mar 30, 2023 at 15:48
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. Commented Mar 30, 2023 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"]
Kadir Şahbaz
78.6k57 gold badges260 silver badges407 bronze badges
answered Mar 31, 2023 at 6:50

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.