I need to dissolve all Linestrings from a geodataframe
that intersect each other after attending some tabular conditions: 1)have the same ID
and 2) have the same opening
year. The data represent a road database and I am working with geopandas
. The data look like this:
id opening geometry
0 30 2020 LINESTRING (45.01679 -12.12937, 45.01681 -12...
1 101 1999 LINESTRING (37.02849 -10.65968, 37.02849 -10...
2 30 2019 LINESTRING (47.10667 -15.49339, 47.10665 -15...
3 101 1999 LINESTRING (41.64170 -12.45764, 41.64180 -12...
4 135 2020 LINESTRING (45.31902 -9.76800, 45.31907 -9.7...
I have tried the following code:
import geopandas as gpd
import numpy as np
import shapely
gdf_sof = gpd.read_file('your_path/your_file')
# Concatenate the conected segments
gdf = gpd.GeoDataFrame()
for id in gdf_sof.id.unique():
print(id)
unique_id = gdf_sof.loc[gdf_sof.id == id]
print(unique_id.shape)
gdf_sof_id = gpd.GeoDataFrame()
for dt in unique_id.opening.unique():
print(dt)
unique_oppening = unique_id.loc[unique_id.opening == dt]
dissolved = gpd.geoseries.GeoSeries([geom for geom in unique_oppening.unary_union.geoms])
gdf_sof_id = pd.concat([gdf_sof_id, dissolved], ignore_index=True, axis=0)
gdf.concat(gdf_sof_id, ignore_index=True, axis=0, inplace=True)
gdf.shape
But got this error message:
---------------------------------------------------------------------------
AttributeError: 'LineString' object has no attribute 'geoms'
I also tried the following code based on @Pieter's comment, but the result isn't satisfactory. I used both linemerge
and shapely.get_parts
.
import geopandas as gpd
import numpy as np
import pandas as pd
import shapely
from shapely.ops import linemerge
gdf_sof = gpd.read_file('your_path/your_file')
# Dissolve the connected segments
gdf = gpd.GeoDataFrame()
for id in gdf_sof.id.unique():
unique_id = gdf_sof.loc[gdf_sof.id == id]
gdf_sof_id = gpd.GeoDataFrame()
for dt in unique_id.opening.unique():
unique_opening = unique_id.loc[unique_id.opening == dt]
shp = shapely.get_parts(unique_opening.geometry)
dissolved = linemerge(shp)
gdf_dissolved = gpd.GeoDataFrame({'id': [id],
'opening': [dt],
'geometry':dissolved},
geometry = 'geometry',
crs = gdf_sof.crs)
gdf_sof_id = pd.concat([gdf_sof_id, gdf_dissolved], ignore_index=True, axis=0)
gdf = pd.concat([gdf, gdf_sof_id], ignore_index=True, axis=0)
gdf.shape
My starting gdf (gdf_sof
) has the shape (7366, 3)
, and the final gdf
has a shape of (973, 3)
. However, the final gdf (973,3)
dissolved segments that don't touch.
All highlighted segments in the image above are just one row in the attribute table. I need them separated!
I found a solution:
# Marge segments based on tabular info ('id' and 'dt')
gdf = gpd.GeoDataFrame()
for id in gdf_sof.id.unique():
unique_id = gdf_sof.loc[gdf_sof.id == id]
gdf_sof_id = gpd.GeoDataFrame()
for dt in unique_id.opening.unique():
unique_opening = unique_id.loc[unique_id.opening == dt]
shp = shapely.get_parts(unique_opening.geometry)
dissolved = linemerge(shp)
gdf_dissolved = gpd.GeoDataFrame({'id': [id],
'opening': [dt],
'geometry':dissolved},
geometry = 'geometry',
crs = gdf_sof.crs)
gdf_sof_id = pd.concat([gdf_sof_id, gdf_dissolved], ignore_index=True, axis=0)
gdf = pd.concat([gdf, gdf_sof_id], ignore_index=True, axis=0)
# show the shape
gdf.shape
The gdf
has few lines. The linemerge()
function merged segments that don't touch each other, as shown in the second part of the question.
Then, we need to separate parts in geoms:
gdf_final = gpd.GeoDataFrame()
for mls in gdf.geometry.index:
#print(mls)
#print(gdf.geometry[mls].geom_type)
if gdf.geometry[mls].geom_type == 'MultiLineString':
gdf_multi = gpd.GeoDataFrame()
for part in gdf.geometry[mls].geoms:
gdf_part = gpd.GeoDataFrame({'id': [gdf.id[mls]],
'opening': [gdf.opening[mls]],
'geometry':part},
geometry = 'geometry',
crs = gdf_sof.crs)
gdf_multi = pd.concat([gdf_multi, gdf_part], ignore_index=True, axis=0)
gdf_final = pd.concat([gdf_final, gdf_multi], ignore_index=True, axis=0)
else:
gdf_final = pd.concat([gdf_final, gdf.iloc[mls:mls+1]], ignore_index=True, axis=0)
gdf_final