Search code examples
python-3.xgeometrygisgeopandasshapely

Is there way to find a list of point coordinates where 3 or more lines touch eachother using shapely and geopandas in python?


I have a Geopandas DataFrame with each row containing a shapely LineString. I need to find a list of points where three or more LineStrings touch each other. For example, in figure Input image, I need the coordinates of the point where the three colored lines meet.

After I find all the points where three lines intersect, I need to merge the three lines to form two lines: 1) Pink + Purple 2) Green + Purple. The overlap should be fine.

Any help would be highly appreciated. Thanks!


Solution

    • have used a few countries for source of LINESTRINGS
    • with this data set, there are only two points where there are three selected countries that intersect (as per visualisation)
    • key concept is inner join points to points, then find points that have more than required number of repetitions
    import geopandas as gpd
    import shapely.geometry
    import pandas as pd
    import plotly.express as px
    
    # some polygons
    # fmt: off
    gdf = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres")).loc[lambda d: d["iso_a3"].isin(["BEL", "LUX", "NLD", "DEU", "AUT","POL"]), ["geometry", "iso_a3"]].reset_index(drop=True)
    # fmt: on
    
    # change to linestrings as per question
    gdf["geometry"] = gdf["geometry"].apply(
        lambda p: shapely.geometry.LineString(p.exterior.coords)
    )
    
    # generate series of points from linestrings
    points = (
        gdf["geometry"].apply(lambda l: l.coords).rename("point").explode().reset_index()
    )
    
    # 1. join all points to all points
    # 2. exclude point to self
    points = (
        points.merge(points, on=["point"])
        .drop_duplicates()
        .loc[lambda d: d["index_x"] != d["index_y"]]
    )
    # now find points that appera N times
    n_times = (
        points.groupby(["point"])
        .size()
        .loc[lambda s: s >= 3]
        .reset_index()["point"]
        .apply(pd.Series)
    )
    
    # visualize to test...
    px.scatter_mapbox(n_times, lon=0, lat=1, mapbox_style="carto-positron",).update_traces(
        marker_size=20
    ).update_layout(mapbox={"layers": [{"source": gdf.__geo_interface__, "type": "line"}]})
    

    enter image description here