You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A Python script I wrote to add data to a point from a polygon via an "in-memory" spatial join (i.e., there are no intermediate outputs or unnecessary fields added to target table). See the script notes for more info.
It could probably use some error handling and cleanup, but it works as-is.
# AttributePointsFromPolys.py
# Andrew L. Wunderlich
# andrew.wunderlich@tn.gov
# 6/29/2022
# 7/5/2023 Updated to skip NULL values and updated dialog
# 2/24/2023 Ported to ArcGIS Pro GeMS toolbox
# Tool uses an in-memory spatial join to transfer an attribute from a SOURCE polygon class
# to the user-specified attribute of a TARGET point class which intersects it. If a TARGET
# point feature has no intersecting SOURCE polygon feature, or if the value in the attribute of
# the intersecting source polygon is NULL, that point feature is skipped.
#
# This tool was originally written to accompany and enhance the GeMS tools for geologic map databases.
# Several of the point feature classes specified by the GeMS schema require that the attribute
# "MapUnit" be populated based on the corresponding MapUnitPoly feature the point intersects.
# A Spatial Join can be done to get this information; however, the output of that tool must then
# be transferred to the original feature class by way of a table join and field calculation.
# This tool takes four inputs: a SOURCE polygon feature class, a TARGET point feature class, and
# once those have been selected, a field in the SOURCE to COPY values FROM and a field in the TARGET
# to WRITE those values. Upon running the tool, a spatial join (done temporarily in memory)
# stores the polygon attribute values from the spatial join that correspond to the target point
# feature OIDs, and finally transfers those values to the target point class, all without creating
# an intermediate output or requiring any additional user action.
# This tool DOES NOT check the type of field, the field length, or whether the values
# that are attempting to be transferred are valid for the target field. This may be implemented
# in a future version, however, at this time field type mismatches will undoubtedly cause unhandled
# exceptions to occur.
# This tool modifies the TARGET point dataset ONLY. ALWAYS back up your datasets before using
# a tool that modifies any of the inputs!!
# Add Script setup:
# Name: AttributePointsFromPoly
# Label: Attribute Points From Polygon
# Description: Tool uses an in-memory spatial join to transfer an attribute from a polygon class
# to the user-specified attribute of a point class which intersects it.
# Store relative paths: YES
# ArcGIS Pro Tool Properties required Parameters:
# The feature class parameters make Point and Polygon features the only valid types
# The field parameters are populated contingent upon the feature classes chosen:
# Source Polygons - Feature Class; Required; Input; Filter: Feature type-Polygon
# Source Polygons Attribute (to be COPIED) - Field; Required; Input; Dependency: Source_Polygons
# Target Points - Feature Class; Required; Input; Filter: Feature Type-Point
# Target Points Attribute (to be WRITTEN) - Field; Required; Input; Dependency: Target_Points
#
# Also paste the following code into the Validation parameters replacing the default
# "def initializeParameters(self)" section:
# def initializeParameters(self):
# # Customize parameter properties.
# # This gets called when the tool is opened.
# self.params[0].filter.list = ["polygon"]
# self.params[2].filter.list = ["point"]
# return
import arcpy
from os import path
import sys
import GeMS_utilityFunctions as guf
# Function to find field names used for debugging purposes
def FindField(fc, myfield):
fieldList = arcpy.ListFields(fc)
for field in fieldList:
if str.lower(str(field.name)) == str.lower(myfield):
guf.addMsgAndPrint(" " + fc + " contains field name: " + myfield, 0)
# Use these when running from toolbox
fc_join_polys = sys.argv[1]
fld_join_polys = sys.argv[2]
fc_target_points = sys.argv[3]
fld_target_points = sys.argv[4]
# Use these when testing script
# fc_target_points = 'C:\\temp\\AttributePointFromPolyTESTING\\OrientationPointsTEST01.shp'
# fc_join_polys = 'C:\\temp\\AttributePointFromPolyTESTING\\MapUnitPolysTEST01.shp'
# fld_target_points = 'MapUnit'
# fld_join_polys = 'MapUnit'
# Allow overwrite! Must do even when writing to memory
arcpy.env.overwriteOutput = True
# Save the original name of the Source field
fld_orig_join_polys = fld_join_polys
# Create a temporary point feature class to store the spatial join
temp_sj_points = arcpy.CreateFeatureclass_management('in_memory', 'temp_sj', 'Point')
# Do the spatial join between the target points and the polygons
arcpy.SpatialJoin_analysis(fc_target_points, fc_join_polys, temp_sj_points, 'JOIN_ONE_TO_ONE', 'KEEP_ALL', None,
'INTERSECT', '0')
# For debug purposes, check the list of fields in the temp_sj_points
# testFields = arcpy.ListFields(temp_sj_points)
# guf.addMsgAndPrint('\rFields present in the temporary spatial join output (points):', 0)
# for field in testFields:
# guf.addMsgAndPrint(' {0} is a type of {1} with a length of {2}'
# .format(field.name, field.type, field.length), 0)
# Set the current workspace
arcpy.env.workspace = path.dirname(fc_target_points)
guf.addMsgAndPrint('\rCurrent workspace: ' + arcpy.env.workspace, 0)
# Retrieve the field list from the spatial join
fieldList = arcpy.ListFields(fc_target_points)
for field in fieldList:
if str.lower(str(field.name)) == str.lower(fld_join_polys): # Check if the field name already exists
guf.addMsgAndPrint('\rTarget point features ' + path.split(fc_target_points)[1] + ' already has a field named \"' +
fld_join_polys + '\"...', 0)
# Add an '_1' to the end of the name if the field already exists
# MUST do this when from and to attribute field names are the same
fld_join_polys = fld_join_polys + '_1'
guf.addMsgAndPrint('\r Spatial join will rename Source field to \"' +
fld_join_polys + '\" in the temporary spatial join point features...', 0)
# Get the field list from the spatial join. The polygon OID will always be called 'TARGET_FID' in the join output
sourceFieldsList = ['TARGET_FID', fld_join_polys]
guf.addMsgAndPrint('\rSpatial join of ' + path.split(fc_target_points)[1] + ' and ' + path.split(fc_join_polys)[1] +
' complete...\r\n Polygon ObjectIDs and \"' + fld_orig_join_polys + '\" values transferred to fields'
' created by the join [Source Polygon OID, attribute]: ' + str(sourceFieldsList), 0)
# Get the field list of the target points
updateFieldsList = ['OID@', fld_target_points]
guf.addMsgAndPrint('\rTarget point feature fields to match and transfer to are [Target Point OID, attribute]: ' + str(updateFieldsList) + '\r\n', 0)
# Populate the value array from the fields in the temporary join feature class
# based on the 'TARGET_FID' field generated by the join and the values present
# in the user-specified source field that was populated by the join.
valueDict = {r[0]: (r[1:]) for r in arcpy.da.SearchCursor(temp_sj_points, sourceFieldsList)}
# USED FOR DEBUG: Value dump to see what's in there... output actual values to user below during iteration
# guf.addMsgAndPrint('\rValues: ' + str(valueDict), 0)
guf.addMsgAndPrint('Begin transferring attributes...\r', 0)
# Go through the target features and match up the OID with the TARGET_FID
with arcpy.da.UpdateCursor(fc_target_points, updateFieldsList) as updateRows:
for updateRow in updateRows:
keyValue = updateRow[0]
if keyValue in valueDict:
for n in range(1, len(sourceFieldsList)):
if valueDict[keyValue][n - 1] is None:
# Skips features that don't have a match in the spatial join or contain a value of NULL
guf.addMsgAndPrint(' ' + fld_orig_join_polys + ' value is NULL for ' +
path.split(fc_target_points)[1] + ' OID ' + str(keyValue) + ' (Point does not '
'intersect any polygons or joined attribute value is NULL); Skipping...', 0)
else:
# Should use a try-except here to catch type mismatches...
updateRow[n] = valueDict[keyValue][n - 1]
guf.addMsgAndPrint(' ' + fld_orig_join_polys + ' value transferred to ' +
path.split(fc_target_points)[1] + ' OID ' + str(keyValue) + '; ' + fld_target_points
+ ' = ' + str(valueDict[keyValue][n - 1]), 0)
# Write the output value to the target row attribute
updateRows.updateRow(updateRow)
# Delete the valueDict array and the temporary join
del valueDict
arcpy.Delete_management(temp_sj_points)
guf.addMsgAndPrint(' ', 0)
This discussion was converted from issue #56 on June 05, 2023 21:01.
Heading
Bold
Italic
Quote
Code
Link
Numbered list
Unordered list
Task list
Attach files
Mention
Reference
Menu
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
A Python script I wrote to add data to a point from a polygon via an "in-memory" spatial join (i.e., there are no intermediate outputs or unnecessary fields added to target table). See the script notes for more info.
It could probably use some error handling and cleanup, but it works as-is.
Beta Was this translation helpful? Give feedback.
All reactions