简体   繁体   中英

Split LINESTRING at given POINT using Shapely split function

I have a geopandas dataframe that looks likes this:

level_0                                    id  \
0           0  028f342a-b26f-4e36-b5d1-25d3428cac2f   
1           1  028f342a-b26f-4e36-b5d1-25d3428cac2f   
2           2  028f342a-b26f-4e36-b5d1-25d3428cac2f   
3           3  028f342a-b26f-4e36-b5d1-25d3428cac2f   
4           4  028f342a-b26f-4e36-b5d1-25d3428cac2f   
...       ...                                   ...   
2959     2959  fef00a1e-a823-47fc-b6e4-4e5885cc587b   
2960     2960  fef00a1e-a823-47fc-b6e4-4e5885cc587b   
2961     2961  fef00a1e-a823-47fc-b6e4-4e5885cc587b   
2962     2962  fef00a1e-a823-47fc-b6e4-4e5885cc587b   
2963     2963  fef00a1e-a823-47fc-b6e4-4e5885cc587b   

                                  edges.id  \
0     28cd1b27-dda1-4ba8-a79c-042e8af6a1d3   
1     28cd1b27-dda1-4ba8-a79c-042e8af6a1d3   
2     28cd1b27-dda1-4ba8-a79c-042e8af6a1d3   
3     28cd1b27-dda1-4ba8-a79c-042e8af6a1d3   
4     2cb0816e-de32-47ef-9695-eeb1f15e5771   
...                                    ...   
2959  e0f9373d-0943-4eda-9ab2-0390eb0035a3   
2960  e3eba3f8-0a1a-45a0-b3b1-cd9edcf50128   
2961  e3eba3f8-0a1a-45a0-b3b1-cd9edcf50128   
2962  e3eba3f8-0a1a-45a0-b3b1-cd9edcf50128   
2963  e3eba3f8-0a1a-45a0-b3b1-cd9edcf50128   

                                      geometry_obstacle  index  x_polygon  \
0     LINESTRING (32.46686 3.50903, 33.78148 3.50903...    106      32.47   
1     LINESTRING (32.46686 3.50903, 33.78148 3.50903...    106      33.78   
2     LINESTRING (32.46686 3.50903, 33.78148 3.50903...    106      33.78   
3     LINESTRING (32.46686 3.50903, 33.78148 3.50903...    106      32.47   
4     LINESTRING (32.46686 3.50903, 33.78148 3.50903...    106      32.47   
...                                                 ...    ...        ...   
2959  LINESTRING (4.77078 36.10261, 8.12194 36.10261...     48       4.77   
2960  LINESTRING (4.77078 36.10261, 8.12194 36.10261...     48       4.77   
2961  LINESTRING (4.77078 36.10261, 8.12194 36.10261...     48       8.12   
2962  LINESTRING (4.77078 36.10261, 8.12194 36.10261...     48       8.12   
2963  LINESTRING (4.77078 36.10261, 8.12194 36.10261...     48       4.77   

      y_polygon                  geometry  
0          3.51  POINT (32.46686 3.50903)  
1          3.51  POINT (33.78148 3.50903)  
2          6.16  POINT (33.78148 6.16054)  
3          6.16  POINT (32.46686 6.16054)  
4          3.51  POINT (32.46686 3.50903)  
...         ...                       ...  
2959      37.10  POINT (4.77078 37.10083)  
2960      36.10  POINT (4.77078 36.10261)  
2961      36.10  POINT (8.12194 36.10261)  
2962      37.10  POINT (8.12194 37.10083)  
2963      37.10  POINT (4.77078 37.10083)  

[2964 rows x 8 columns]

It contains two geometries, one being a linestring (they are all closed polygons, all of them rectangles), the other being points (the edges of the polygons). What I am trying to do is the split the linestrings at the points, thus splitting the polygons/lenstrings into segments (ie the sides of the polygons).

I tried to do the following thing:

from shapely.ops import split
df = df.assign(New = lambda x: sp(x['geometry_obstacle'],x['geometry']))

but I get the following error:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-30-77a268925585> in <module>
      2 for lev in lev:
      3     df = geostore_obstacles_geometry_new[geostore_obstacles_geometry_new['level_0']==lev]
----> 4     df = df.assign(New = lambda x: sp(x['geometry_obstacle'],x['geometry']))
      5     New.append(df)

~\Anaconda3\envs\conda-qgis\lib\site-packages\pandas\core\frame.py in assign(self, **kwargs)
   3828 
   3829         for k, v in kwargs.items():
-> 3830             data[k] = com.apply_if_callable(v, data)
   3831         return data
   3832 

~\Anaconda3\envs\conda-qgis\lib\site-packages\pandas\core\common.py in apply_if_callable(maybe_callable, obj, **kwargs)
    327     """
    328     if callable(maybe_callable):
--> 329         return maybe_callable(obj, **kwargs)
    330 
    331     return maybe_callable

<ipython-input-30-77a268925585> in <lambda>(x)
      2 for lev in lev:
      3     df = geostore_obstacles_geometry_new[geostore_obstacles_geometry_new['level_0']==lev]
----> 4     df = df.assign(New = lambda x: sp(x['geometry_obstacle'],x['geometry']))
      5     New.append(df)

~\Anaconda3\envs\conda-qgis\lib\site-packages\shapely\ops.py in split(geom, splitter)
    468         """
    469 
--> 470         if geom.type in ('MultiLineString', 'MultiPolygon'):
    471              return GeometryCollection([i for part in geom.geoms for i in SplitOp.split(part, splitter).geoms])
    472 

~\Anaconda3\envs\conda-qgis\lib\site-packages\pandas\core\generic.py in __nonzero__(self)
   1440     @final
   1441     def __nonzero__(self):
-> 1442         raise ValueError(
   1443             f"The truth value of a {type(self).__name__} is ambiguous. "
   1444             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

I suspect (I might very well be wrong) that split tries all combinations.Therefore, I tried to apply split line by line:

lev = list(geostore_obstacles_geometry_new.level_0)
New = []
for lev in lev:
    df = geostore_obstacles_geometry_new[geostore_obstacles_geometry_new['level_0']==lev]
    df = df.assign(New = lambda x: sp(x['geometry_obstacle'],x['geometry']))
    New.append(df)

but I still get the same error.

How can I solve this? Any other approach is ok.

Since you seem to have duplicated line geometries with individual point geometries, I would start with a non-vectorized solution.

Everytime you call df.assign you're (attempting) to define the entire column all at once (ie, as many times as your loop would be called if it didn't raise an error).

So in this case, I would use df.apply along axis = 1. I'll also wrap split into a function that accepts individual rows of dataframe so that we don't need nested lambda functions


from shapely.ops import split

def get_side_of_rect(row, linecol="geometry_obstacle", pointcol="geometry"):
    return split(row[linecol], row[pointcol])

gdf = (
    gdf.assign(sides=lambda df: df.apply(get_side_of_rect, axis=1))
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM