2

I have a list of centroids that I buffer to 5000 meters radius. I need to take each resulting circle and run it against the remaining centroids(also buffered to 5000 m) and find the final shape after subtracting all intersections.

In other words, I need the circle shape A to iterate over the dataset, find all circle shape(s) that intersect with it (B and C) and then create a column with a geocoordinates of the polygon, that is resulting after all intersecting circle areas are subtracted (see image below) and if it does not intersect with any other cirlce(s), just add a geocoordinate for the entire circle:

enter image description here

Here is the code I have:

df.head()

# Output: 
#                cell_centroid  pop     id
#0  POINT(21.25602 55.903539)   9004.0  0
#1  POINT(21.119156 55.538406)  517.0   1
#2  POINT(21.249535 55.890204)  10369.0 2
#3  POINT(21.359934 55.904164)  800.0   3
#4  POINT(24.433042 56.104589)   
 
df['cell_centroid'] = df['cell_centroid'].apply(wkt.loads)

for i, r in df.iterrows():
    # take the centroid of interest and buffer it with a 5000 meter radius
    a = gpd.GeoSeries(r['cell_centroid'], crs = 4326).to_crs(crs = 3857).buffer(5000)
    # separate that centroid from df and save the remaining rows as a separate df
    df_dropped = df.drop(i, axis = 0)
    li = [] # an empty list to collect intersections
    # iterate over the separated df to find if the dropped centroid intersects with any of the remaining in the dataset

    for j, s in df_dropped.iterrows():
        # the centroid to be compared with
        b = gpd.GeoSeries(s['cell_centroid'], crs = 4326).to_crs(crs = 3857).buffer(5000)

        # check if the centroid1 intersects with centroid2
        if(a.intersects(b)[0] == True):
            tf = a.intersects(b)[0] 
        # add the intersects to the list
            li.append(tf)
        # create a list of just the True shapes(shapes with which the input shape intersects), using intersects list as an index
        new_df = df_dropped.loc[np.array(li)]

        if len(new_df) > 0:
            print('iteration {} : \n{}'.format(i, new_df))
        else:
            print('iteration {} is empty'.format(i))

        # iterate through True shapes dataset (new_df) and find the difference between input shape and each shape in the new_df.
        if len(new_df) > 1:
            iterator = 1
            for k, t in new_df.iterrows():
                c = gpd.GeoSeries(t['cell_centroid'], crs = 4326).to_crs(crs=3857).buffer(5000)
                tf = a.difference(c)
                col_name = 'intersect' + str(iterator)
                df.loc[:, col_name] = tf
                iterator += 1
        # for each centroid a, put each True value into a separate new column in the orginal dataset
        elif len(new_df) == 1:
            c = gpd.GeoSeries(t['cell_centroid'], crs = 4326).to_crs(crs=3857).buffer(5000)
            tf = a.difference(c)
            # for each centroid a, put each True value into a separate new column in the orginal dataset
            df['intersect1'] = tf
        else:
            continue
  

The error I am getting is:

  ---> 34         c = gpd.GeoSeries(t['cell_centroid'], crs = 4326).to_crs(crs=3857).buffer(5000)
       35         tf = a.difference(c)
       36         # for each centroid a, put each True value into a separate new column in the orginal dataset

 NameError: name 't' is not defined 

My questions are:

  1. Is this the most optimal way to calculate final shape (less all intersects) for a each buffered centroid?

  2. Why am I getting an error on line 34? t is defined at the beginning of the loop.

UPDATE:

I have changed the code as suggested in the answer:

df['cell_centroid'] = df['cell_centroid'].apply(wkt.loads)

gdf = gpd.GeoDataFrame(df, geometry="cell_centroid", crs = 4326).to_crs(crs=3857)

gdf["buffer"] = gdf.buffer(5000)
gdf["difference"] = None

gdf = gdf.set_geometry("buffer")

for i, row in gdf.iterrows():
    others = gdf[gdf["id"] != row["id"]]
    diff = row["buffer"].difference(others.cascaded_union)
    gdf.loc[i, "difference"] = diff

gdf = gdf.set_geometry("difference")

The error I am getting now is:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-122-ad757ced6ebf> in <module>
      3     diff = row["buffer"].difference(others.cascaded_union)
      4 #    gdf.loc[i, "difference"] = gpd.GeoSeries(diff)
----> 5     gdf.loc[i, "difference"] = diff
...

ValueError: Must have equal len keys and value when setting with an iterable'
5
  • @KadirŞahbaz done, thank you Jan 6 at 20:49
  • @KadirŞahbaz fixed, my apologies. Jan 6 at 21:04
  • Could you add a sample image representing what exactly you try to achieve. I read your question for a while, I cannot understand. I have not encountered the error you mentioned yet, because I get different errors before that line. Jan 6 at 21:35
  • @KadirŞahbaz done. Let me know if this makes sense Jan 6 at 21:59
  • @KadirŞahbaz I am getting an error using your solution. Please see the comment under your answer. Jan 13 at 21:20
4

In the case of len(new_df) == 1, since t is not defined in elif, you get the error. That means the first block of if never runs before you get the error. Even if the first block worked once, you would get the wrong result after all. Because t would be t coming from previous iteration, not from current iteration.

In case of len(new_df) > 1, first block of if statement processes. Since t is defined by for loop, you won't get an error for the first block of if.

To get rid of that error, add t = new_df.iloc[0] under elif before c = gpd.... You may encounter different errors though.


The more optimal script:

df['cell_centroid'] = df['cell_centroid'].apply(wkt.loads)

gdf = gpd.GeoDataFrame(df, geometry="cell_centroid", crs = 4326).to_crs(crs=3857)

gdf["buffer"] = gdf.buffer(5000)
gdf["difference"] = None

gdf = gdf.set_geometry("buffer")

for i, row in gdf.iterrows():
    others = gdf[gdf["id"] != row["id"]]
    diff = row["buffer"].difference(others.cascaded_union)
    gdf.loc[i, "difference"] = diff

gdf = gdf.set_geometry("difference")

gdf.plot() # I use a custom method to draw

The result for a toy data:

enter image description here

1
  • I am getting an ValueError at '----> 4 gdf.loc[i, "difference"] = diff' line: ValueError: Must have equal len keys and value when setting with an iterable Jan 13 at 20:13

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