Environment:
ArcGIS Pro 2.8 with SQL Server Enterprise Geodatabase
Background:
I'm working with ArcPy, and I've written a script tool that performs a few attribute edits. It does the edits to a versioned feature class (from a SQL Server Enterprise GDB).
Issue:
Everything works except the edit do not immediately show up in ArcGIS Pro. It requires the layer to be manually refreshed in order to display the new attributes in the table/labels/etc.
What was tried:
I have not been able to find a way to programmatically refresh the user's data source. I tried to use the updateConnectionProperties
pointing to both the map object and the layer object. I've also tried modifying the layer refresh interval and the caching in ArcGIS Pro. Neither of which seemed to do the trick.
Found that we have a C# function that is capable of getting the desired results, but I am trying for a Python equivalent.
How do I refresh a .sde connection to see updates to a feature class?
If there isn't a programmatic fix to this, I'm hoping that there's at least a setting I can modify in ArcGIS Pro to actually refresh the data from the server to pull the new attributes.
Sharing the script below:
import arcpy, sys, traceback, os
'''This script is referenced by a custom script tool to calculate the PIN Number and attribute it to the provided parcels layer. If the AKPAR is blank, user can also use tool dialog to attribute it.'''
def calcPin(parX, parY):
'''PIN calculation based on rounded X&Y values (NAD83 NC State Plane Coordinate System)'''
#Convert X/Y to string. Use Z fill to populate string to 6 total digits (adding preceeding 0 as needed)
zf_x = str(round(parX,0)).zfill(6)
zf_y = str(round(parY,0)).zfill(6)
#PIN formula provided in NC GIS List Serv & NCPMA handbook
cPin = str(zf_x)[1:2] + str(zf_y)[0:1]+str(zf_x)[2:3] + str(zf_y)[1:2]+ "-" + str(zf_x)[3:4] + str(zf_y)[2:3]+ "-" + str(zf_x)[4:5] + str(zf_y)[3:4]+str(zf_x)[5:6] + str(zf_y)[4:5]
return cPin
def largestPoly(ply_Set):
'''This returns the largest polygon among parts of a multi-part polygon feature'''
#Iterate through polygons and find largest among multipart feature
lastArea = 0
for p in ply_Set:
#Define feature point array as polygon in NAD83 NC SP
ply_p = arcpy.Polygon(p, sRef)
myArea = ply_p.getArea("PLANAR")
#If this polygon's area is greater than the last read, define it as the largest polygon (ply_largest variable)
if myArea > lastArea:
ply_largest = ply_p
lastArea = myArea
return ply_largest
def refreshLyr(rLayer):
'''This function doesn't achieve an actual refresh. This doesn't work'''
fakeConnection = 'Path'
rDict = {}
str_dataSource = rLayer.dataSource
conAry = conAry = str_dataSource.split(',')
for c in conAry:
cLst = c.split("=")
cProp = cLst[0]
cVal = cLst[1]
rDict[cProp] = cVal
rrDict = {}
rrDict['connection_info'] = rDict
#rLayer.updateConnectionProperties(rrDict, rrDict)
myProj = arcpy.mp.ArcGISProject("Current")
myMap = myProj.activeMap
myMap.updateConnectionProperties(rrDict, fakeConnection)
myMap.updateConnectionProperties(fakeConnection, rrDict)
#User parameters (parcels layer, akpar, calculated acres)
lyr_parcels = sys.argv[1]
str_akpar = sys.argv[2]
lrr_parcels = arcpy.GetParameter(0)
#Spatial reference of provided layer
dsc_parcels = arcpy.Describe(lyr_parcels)
sRef = dsc_parcels.spatialReference
str_fids = dsc_parcels.FIDSet
#Workspace of provided layer
str_ds = lrr_parcels.dataSource
str_catPath = dsc_parcels.catalogPath
str_ws = os.path.dirname(os.path.dirname(str_catPath))
#Check the parcel selection. If user doesn't have a selection, stop the script (otherwise it'll calculate ALL of the parcels!)
if not(str_fids):
arcpy.AddMessage("Tool did not complete. Provided layer does not contain a selection.")
sys.exit()
#check projection. If not in NAD 83 SP, stop the script.
if sRef.projectionCode != 43020:
arcpy.AddMessage("Tool did not complete. Provided layer is not in NAD 1983 StatePlane North Carolina FIPS 3200 Feet.")
sys.exit()
#start edit session
editSession = arcpy.da.Editor(str_ws)
editSession.startEditing(True,True)
#Use Update cursor to attribute the layer.
try:
with arcpy.da.UpdateCursor(lyr_parcels, ['AKPAR', 'PIN', 'FULL_AKPAR', 'SHAPE@']) as uCur:
for u in uCur:
#if user provided AKPAR, set it to the attribute value
if str_akpar != str(u[0]) and str_akpar !='#':
att_akpar = str_akpar
att_fakpar = str_akpar.zfill(7)
arcpy.AddMessage("Adding User's AKPAR")
#set attributes for AKPAR and Full AKPAR
u[0] = int(att_akpar)
u[2] = att_fakpar
#get point array of feature (single or multipart)
ply_plyList = u[3]
#if more than one polygon in feature, set ply_parcel to the largest polygon using above function.
if len(ply_plyList) > 1:
ply_parcel = largestPoly(u[3])
#if only ony polygon in feature, set ply_parcel to a polygon built off of it's point array
else:
ply_parcel = arcpy.Polygon(u[3][0], sRef)
#PIN Calculation. Using label point instead of centroid ensures that the subject point falls within the polygon.
pnt_label = ply_parcel.labelPoint
pnt_X = pnt_label.X
pnt_Y = pnt_label.Y
att_newPin = calcPin(pnt_X, pnt_Y)
#Area in acres
dbl_polyAcres = round(u[3].area/43560,2)
#write PIN attribute
u[1] = att_newPin
#update row.
editSession.startOperation()
uCur.updateRow(u)
editSession.stopOperation()
#Pop up with new PIN and calculated acreage.
arcpy.AddMessage("Parcel " + str(u[0]) + " New PIN: " + str(att_newPin))
arcpy.AddMessage("Parcel " + str(u[0]) + " Calculated Acres: " + str(dbl_polyAcres))
arcpy.AddMessage("\r\n")
#delete the cursor
del uCur
except:
arcpy.AddMessage("PIN Calcuation Error")
arcpy.AddMessage(traceback.format_exc())
finally:
#stop edit session
editSession.stopEditing(True)
#refresh data source (This doesn't work)
#refreshLyr(lrr_parcels)
arcpy.AddMessage("Complete")