Python geoprocessing script to spatial join to layer and calculate existing fields

351
8
04-17-2024 07:04 AM
Tiff
by
Occasional Contributor III

I have a very repetitive workflow that I would like to try to automate but am a beginner coder. I have looked into this before via ModelBuilder but I struggle with it because the field that I'm using to calculate an existing field is joined from the join layer, so the "naming" of that field tends to be unique and not exist before the join happens.

My workflow is as follows:

In a dozen layers, I have the same schema and fields that are calculated based off of various boundary layers. For example, fields would be: Boundary 1, Boundary 2, Boundary 3, Boundary 4, and so forth. Each of those boundary fields are pulled from polygon layers, and one line layer where join is based on 50 ft distance. Once I spatially join the boundary layer(s) to the target layer, I calculate those Boundary 1-4 fields based on the matching field in the joined layer. Because there are multiple boundary layers, I have to do this one by one for every single layer.

This seems like a VERY common workflow so I am wondering if anyone has any starting scripts or advice on the best ways to script this. For example, arcpy, ArcGIS API for Python, geopandas, maybe? Thank you!

0 Kudos
8 Replies
BlakeTerhune
MVP Regular Contributor

If you can use geoprocessing tools to complete the workflow, it can be automated. If you have a basic working knowledge of Python, you can copy the Python command from Pro and paste it into your IDE to get the code started. Alternatively, if you already have a model built, you can export the model to Python. You would be free to edit variable names and utilize the memory workspace.

I'm having trouble fully understanding the workflow from your description so please follow-up with any specific, detailed questions you have.

Tiff
by
Occasional Contributor III

Thanks for this @BlakeTerhune ! I recall not even being able to do the workflow in ModelBuilder because the joined field I'm relying on for the calculation technically doesn't exist at the start of the model, if that makes sense. That's the issue I'm running into when I think about starting to automate it.

So more specifically, if the layer I want to join is a parks layer, I want to calculate the park name for all of my target layers. And when you calculate the field manually, the calculate expression will be something like [LayerName]_AddSpatialJoin_5.PARKNAME. Not sure if that makes sense, but I keep thinking that's the limitation to automating it.

0 Kudos
BlakeTerhune
MVP Regular Contributor

The Calculate Field tool in ArcGIS Pro will now create the field if it doesn't already exist, which is handy and saves a step. Either way, you should be able to have access to the new field in model builder and Python. Without more details, I can't advise further. If you are able to provide a detailed description of your starting tables and what you want the output to be, we might be able to provide some code examples.

0 Kudos
Tiff
by
Occasional Contributor III

Hi @BlakeTerhune, thank you! sorry for the confusion. It's not that the field I want to calculate doesn't exist, it's the field that I'm using to calculate (from the join layer) that doesn't yet exist UNTIL the join happens.

Here is a workflow example:

1. Two layers - 1) playgrounds, and 2) parks - both polygons.
2. Right click playgrounds layer in Pro -> Joins and Relates -> Add Spatial Join. The target features are playgrounds, join features are parks, with match option of intersect or within.
3. Once joined, all fields from parks layer are (temporarily) joined to the playgrounds layer. Using the "Park Name" field of the parks layer, I want to calculate the "Park Name" field (already existing) of the playgrounds layer. That way, I can capture what park each playground is located in via the attribute table.

I hope that makes sense! This is the only and best way I know how to do this process in Pro right now. I'm hoping to automate this somehow, as I have about a dozen "target" layers similar to playgrounds above, as well as multiple "join" layers (beyond just parks). So you can see how this process is very repetitive if done manually!

0 Kudos
BlakeTerhune
MVP Regular Contributor

Yes, this workflow can be automated. However, it's not efficient to calculate the park name field in playgrounds on a regular basis. Since this is something you need to happen regularly, I recommend creating a relationship class between park and playground and utilize Attribute Rules to automatically populate the park name. This has the benefit of always being updated immediately when the data changes instead of only when you run the script.

0 Kudos
Tiff
by
Occasional Contributor III

Thanks very much again, @BlakeTerhune. I don't think I can create the relationship class easily because we have a ton of other layers that we would need related to parks, so the data management of that would be difficult.

We do have a similar workflow to the spatial join using Arcade in ArcGIS Online. However, that only applies to edits made in AGOL so in the next few months we will be exploring attribute rules to do this (currently somewhat relying on Oracle triggers). However, attribute rules are only supported for feature classes in enterprise geodatabases, and not for AGOL hosted feature layers. So that is the current dilemma. We have SO many of these layers like I mentioned, so it's really something that needs to be a reproducible and sustainable workflow long-term.

0 Kudos
BlakeTerhune
MVP Regular Contributor

What version of ArcGIS Pro are you using?

0 Kudos
TonyAlmeida
Occasional Contributor II

maybe something like this,

 

 

# Set your workspace (folder or geodatabase)
arcpy.env.workspace = r"workspace"  # Change this to your actual workspace

# Define the paths to your layers
playgrounds = "playgrounds.shp"  # your playgrounds layer path
parks = "parks.shp"  #  your parks layer path

# Define the output layer for the spatial join
output_layer = "playgrounds_with_parks.shp"  # Adjust name as needed

try:
    # Perform the spatial join
    arcpy.analysis.SpatialJoin(
        target_features=playgrounds,
        join_features=parks,
        out_feature_class=output_layer,
        join_type="KEEP_ALL",
        match_option="INTERSECT",  # Use the option that you need.
        field_mapping=""  
    )
    
    # Assuming the park name field in both original layers is "ParkName"
    # When joined, the field from parks might be renamed. Let's assume it's renamed to "ParkName_1".
    # If unsure, check the spaital join layer

    # Calculate the "Park Name" in the playgrounds layer from the joined park name
    arcpy.management.CalculateField(
        in_table=output_layer,
        field="ParkName",  # The field in the playgrounds layer to update
        expression="!ParkName_1!",  # The joined park name field from the parks layer
        expression_type="PYTHON3"
    )

except arcpy.ExecuteError:
    print(arcpy.GetMessages())
except Exception as e:
    print(str(e))

 

0 Kudos