Select to view content in your preferred language

How to select 3 points per polygon grid feature from another feature class

783
4
Jump to solution
10-09-2024 03:09 AM
Labels (1)
AndrewHankinson
Regular Contributor

I have survey data which is collected on a 1ha grid square, within each grid will be 4 survey points. 

What I am looking to do is select 3 points that fall within a larger grid square, 10ha. 

With how the data has been collected there are roughly 36 points per 10ha grid square. 

The points selected can be random.

I thought about creating random points and some sort of buffer but that is just a no go and also thought about select by attribute but the data doesn't lend itself to getting specifically 3 survey points selected using any one attribute. 

I've dropped a screenshot of how the survey appears with a 10ha polygon grid for reference. 

Any suggestions are welcome. 

 

0 Kudos
1 Solution

Accepted Solutions
VinceE
by
Frequent Contributor

Not sure if I understand your exact needs, but you can try something like below. I think a more efficient method would be doing a spatial join in memory and then working with a pandas Data Frame to select three points from each subgroup, but this may get you started.

Code below will randomly select exactly three points within each "box".

VinceE_0-1728507127223.png

 

import sys
import numpy as np

# a list of point IDs to eventually export
point_oids_to_export = []

# Iterate over your sampling boxes
with arcpy.da.SearchCursor('SamplingPointBox', "OID@") as scurs:
    for oid, in scurs:
        # Select the current box. Select all the points with that box.
        box_selection = arcpy.management.SelectLayerByAttribute('SamplingPointBox', 'NEW_SELECTION', f"OID = {oid}")
        point_selection = arcpy.management.SelectLayerByLocation('SamplingPoint', 'INTERSECT', 'SamplingPointBox')
        
        # Get a list of the OIDs of the points within the box.
        oidValueList = [r[0] for r in arcpy.da.SearchCursor(point_selection, ["OID@"])]
        
        # Select three of those OIDs, not selecting the same one twice.
        rng = np.random.default_rng()
        chosen_oids = rng.choice(oidValueList, 3, False, None, 0, False)
        
        # Add those three new points to the running list.
        point_oids_to_export.extend(chosen_oids)

# Select all the collected points based on the long list of OIDs
query = f"OBJECTID IN {tuple(point_oids_to_export)}"
arcpy.management.SelectLayerByAttribute('SamplingPoint', 'NEW_SELECTION', query)

## export your selection from here, or add "arcpy.conversion.ExportFeatures()"

 

View solution in original post

4 Replies
VinceE
by
Frequent Contributor

Not sure if I understand your exact needs, but you can try something like below. I think a more efficient method would be doing a spatial join in memory and then working with a pandas Data Frame to select three points from each subgroup, but this may get you started.

Code below will randomly select exactly three points within each "box".

VinceE_0-1728507127223.png

 

import sys
import numpy as np

# a list of point IDs to eventually export
point_oids_to_export = []

# Iterate over your sampling boxes
with arcpy.da.SearchCursor('SamplingPointBox', "OID@") as scurs:
    for oid, in scurs:
        # Select the current box. Select all the points with that box.
        box_selection = arcpy.management.SelectLayerByAttribute('SamplingPointBox', 'NEW_SELECTION', f"OID = {oid}")
        point_selection = arcpy.management.SelectLayerByLocation('SamplingPoint', 'INTERSECT', 'SamplingPointBox')
        
        # Get a list of the OIDs of the points within the box.
        oidValueList = [r[0] for r in arcpy.da.SearchCursor(point_selection, ["OID@"])]
        
        # Select three of those OIDs, not selecting the same one twice.
        rng = np.random.default_rng()
        chosen_oids = rng.choice(oidValueList, 3, False, None, 0, False)
        
        # Add those three new points to the running list.
        point_oids_to_export.extend(chosen_oids)

# Select all the collected points based on the long list of OIDs
query = f"OBJECTID IN {tuple(point_oids_to_export)}"
arcpy.management.SelectLayerByAttribute('SamplingPoint', 'NEW_SELECTION', query)

## export your selection from here, or add "arcpy.conversion.ExportFeatures()"

 

VinceE
by
Frequent Contributor

For posterity, this seems like a much more efficient option (working with Pandas Spatial Data Frames, instead of Cursors and SelectionByLocation/Attribute):

def new_method(pnt: Union[str, os.PathLike],    # full path to points
               box: Union[str, os.PathLike],    # full path to boxes
               box_id: str,                     # field name containing Box ID
               sample_count: int,               # how many points per box?
               output: Union[str, os.PathLike]  # full path to output FC
               ) -> None:                       # function has no return
    """SpatJoin points with boxes. Convert to SDF. Groups points by joined BOX ID.
    Select 3 points per group. Exports sampled Spatial Data Frame back to FC."""
    pnt_box_join = arcpy.analysis.SpatialJoin(pnt, box, r"memory\pnt_box_join").getOutput(0)

    # Convert FC to SDF. Drop unused columns. GroupBy BOX_ID column.
    # Without replacing, sample X# rows from each group. Export SDF back to FC.
    sdf = pd.DataFrame.spatial.from_featureclass(pnt_box_join)
    sdf = sdf.drop(columns=["Join_Count", "TARGET_FID", "Shape_Length", "Shape_Area"])
    sampled_sdf = sdf.groupby(box_id, group_keys=False).sample(n=sample_count)
    sampled_sdf.spatial.to_featureclass(location=output, sanitize_columns=False)
    print("SAMPLED SDF -> FC EXPORTED")

 

VinceE_0-1729265179912.png

 

0 Kudos
AndrewHankinson
Regular Contributor

Morning @VinceE , 

Thanks very much for replying and sorry for the slow response. 

I think you have understood my question perfectly. I have created some test data to see how the above works, and it works well. The only thing that came up was that if there were less than 3 points in a box then it would stop at that point. But that makes sense since its looking to select 3 points so this would be impossible if there aren't at least 3 to begin with. 

This is great, a real help and gives me something to learn from. Thanks again. 


VinceE
by
Frequent Contributor

Sure, happy to help you work through anything further if you have additional questions!