#Import modules import arcpy import math from arcpy import env #Overwrite output to the output feature class env.overwriteOutput = True #Assign variables env.workspace = r"Local_FGDB_Path" addptFC = "TEST_ADD_PTS" clFC = "Street_Test" OutFC = "Output_Add_Pts" #See if in_memory layers exist - delete them if they do if arcpy.Exists("addpts"): arcpy.Delete_management("addpts") if arcpy.Exists("CLs"): arcpy.Delete_management("CLs") #Create in memory feature layers of input feature classes arcpy.MakeFeatureLayer_management(addptFC, "addpts") arcpy.MakeFeatureLayer_management(clFC, "CLs") #Create UpdateCursor addptrows = arcpy.UpdateCursor("addpts", "", "", "STREET_B", "STREET_B A") #Set variable to "shape" field addptDesc = arcpy.Describe("addpts") addptSFN = addptDesc.ShapeFieldName #Field that holds the shape info clDesc = arcpy.Describe("CLs") clSFN = clDesc.ShapeFieldName #Field that holds the shape info #Begin loop for address points for addptrow in addptrows: addptValue = addptrow.getValue("STREET_B") #Create searchCursor for temporary centerline feature layer clrows = arcpy.SearchCursor("CLs", "", "", "ST_BOTH", "ST_BOTH A") #Set variable for feature addptFEAT = addptrow.getValue(addptSFN) #Set variable for feature part addptPART = addptFEAT.getPart() #Set variables for "X" and "Y" coordinates for feature part addptX = addptPART.X addptY = addptPART.Y #Create empty list to store midpoint distances and angles of parts - compare all values to find which feature #with similar street name is closest to address point closestMid = [] #Begin loop for centerlines for clrow in clrows: clValue = clrow.getValue("ST_BOTH") vertexList = [] #Find all like street names between address points and centerlines if addptValue == clValue: #Set variable for feature clFEAT = clrow.getValue(clSFN) #Set variable for feature part clPART = clFEAT.getPart(0) #Set variables for "X" and "Y" coordinates for feature part for clpartObj in clPART: vertexList.append((clpartObj.X, clpartObj.Y)) vertexCount = len(vertexList) if vertexCount < 2: break #If only one vertex then exit loop (no midpoint to calculate) z = 0 #Loop through feature parts and get coordinates for each vertex while z + 2 <= vertexCount: #That is until you run out of pairs to count #Get vertices for segment x1 = vertexList[0] y1 = vertexList [1] x2 = vertexList[z + 1][0] y2 = vertexList[z + 1][1] #Calculate the midpoint of the line segment midX = (x1 + x2)/2 midY = (y1 + y2)/2 #Calculate the distance from the address point to the midpoint of the part distMid = math.sqrt(((addptX - midX)**2) + ((addptY - midY)**2)) if x1 == vertexList[0][0] or x2 == vertexList[len(vertexList)-1][0]: #These delta values calculated between midpoint of feature part and address point deltaX = midX - addptX deltaY = midY - addptY #Calculate label angle for address point addptAngle = (57.2957795 * math.atan2(deltaY, deltaX)) else: #These delta values calculated on the feature parts deltaX = x2 - x1 deltaY = y2 - y1 #Calculate label angle for address point #("90" added so that point angle is perpendicular to line angle) addptAngle = (90 + (57.2957795 * math.atan2(deltaY, deltaX))) #"closestMid" is the nested list storing: # 1) Distance from address point to midpoint of feature part, # 2) Address point label angle closestMid.append((distMid, addptAngle)) z += 1 ####Set up condition so that when "addptValue" does not have a match that ####addptValue is copied to a text file #####NOTE: Make sure that an address point is retreived here if it isn't spelled correctly #####or if it does not legitimately have a centerline in the county elif addptValue != clValue: clrows.next() #Delete the list holding the coordinates of all vertices for all feature parts del vertexList[:] #This is where the closest value for "closestMid" is finalized finalAngle = 0 #The angle the address point will take closestValue = 0 #The closest midpoint-to-address-point distance #Sort the list "closestMid" and assign the first value of midpoint distance and #address point angle to variables closestMid.sort() closestValue = closestMid[0][0] finalAngle = closestMid[0][1] #Write the address point angle to the "angle" field and update the row within #the temporary feature layer "addpts" addptrow.angle = finalAngle addptrows.updateRow(addptrow) #Delete the list holding the midpoint values and address point label angle values #and delete the row and cursor for the centerline feature layer del closestMid[:] del clrow, clrows #Copy the temporary feature layer "addpts" to the feature class "OutFC" arcpy.CopyFeatures_management("addpts", OutFC) #Delete the row and cursor for the address point feature layer del addptrow, addptrows
Solved! Go to Solution.
#Import modules import arcpy import math from arcpy import env #Overwrite output to the output feature class env.overwriteOutput = True #Assign variables env.workspace = r"Insert_Local_FGDB_Here" addptFC = "TEST_ADD_PTS" clFC = "Street_Test" OutFC = "Output_Add_Pts" #See if in_memory layers exist - delete them if they do if arcpy.Exists("addpts"): arcpy.Delete_management("addpts") if arcpy.Exists("CLs"): arcpy.Delete_management("CLs") #Create in memory feature layers of input feature classes arcpy.MakeFeatureLayer_management(addptFC, "addpts") arcpy.MakeFeatureLayer_management(clFC, "CLs") #Create UpdateCursor addptrows = arcpy.UpdateCursor("addpts", "", "", "STREET_B", "STREET_B A") #Set variable to "shape" field addptDesc = arcpy.Describe("addpts") addptSFN = addptDesc.ShapeFieldName #Field that holds the shape info clDesc = arcpy.Describe("CLs") clSFN = clDesc.ShapeFieldName #Field that holds the shape info #Begin loop for address points for addptrow in addptrows: addptValue = addptrow.getValue("STREET_B") #Create searchCursor for temporary centerline feature layer clrows = arcpy.SearchCursor("CLs", "", "", "ST_BOTH", "ST_BOTH A") #Set variable for feature addptFEAT = addptrow.getValue(addptSFN) #Set variable for feature part addptPART = addptFEAT.getPart() #Set variables for "X" and "Y" coordinates for feature part addptX = addptPART.X addptY = addptPART.Y #Create empty list to store midpoint distances and angles of parts - compare all values to find which feature #with similar street name is closest to address point closestMid = [] #Begin loop for centerlines for clrow in clrows: clValue = clrow.getValue("ST_BOTH") vertexList = [] #Find all like street names between address points and centerlines if addptValue == clValue: #Set variable for feature clFEAT = clrow.getValue(clSFN) #Set variable for feature part clPART = clFEAT.getPart(0) #Set variables for "X" and "Y" coordinates for feature part for clpartObj in clPART: vertexList.append((clpartObj.X, clpartObj.Y)) vertexCount = len(vertexList) if vertexCount < 2: break #If only one vertex then exit loop (no midpoint to calculate) z = 0 #Loop through feature parts and get coordinates for each vertex while z + 2 <= vertexCount: #That is until you run out of pairs to count #Get vertices for segment x1 = vertexList[0] y1 = vertexList [1] x2 = vertexList[z + 1][0] y2 = vertexList[z + 1][1] #Calculate the midpoint of the line segment midX = (x1 + x2)/2 midY = (y1 + y2)/2 #Calculate the distance from the address point to the midpoint of the part distMid = math.sqrt(((addptX - midX)**2) + ((addptY - midY)**2)) #These delta values calculated on the feature parts deltaX = x2 - x1 deltaY = y2 - y1 #Calculate label angle for address point #("90" added so that point angle is perpendicular to line angle) addptAngle = (90 + (57.2957795 * math.atan2(deltaY, deltaX))) #"closestMid" is the nested list storing: # 1) Distance from address point to midpoint of feature part, # 2) Address point label angle closestMid.append((distMid, addptAngle)) z += 1 #Delete the list holding the coordinates of all vertices for all feature parts del vertexList[:] #This is where the closest value for "closestMid" is finalized finalAngle = 0 #The angle the address point will take closestValue = 0 #The closest midpoint-to-address-point distance #Sort the list "closestMid" and assign the first value of midpoint distance and #address point angle to variables closestMid.sort() closestValue = closestMid[0][0] finalAngle = closestMid[0][1] #Write the address point angle to the "angle" field and update the row within #the temporary feature layer "addpts" addptrow.angle = finalAngle addptrows.updateRow(addptrow) #Delete the list holding the midpoint values and address point label angle values #and delete the row and cursor for the centerline feature layer del closestMid[:] del clrow, clrows #Copy the temporary feature layer "addpts" to the feature class "OutFC" arcpy.CopyFeatures_management("addpts", OutFC) #Delete the row and cursor for the address point feature layer del addptrow, addptrows
#Import modules import arcpy import math from arcpy import env #Overwrite output to the output feature class env.overwriteOutput = True #Assign variables env.workspace = r"Insert_Local_FGDB_Here" addptFC = "TEST_ADD_PTS" clFC = "Street_Test" OutFC = "Output_Add_Pts" #See if in_memory layers exist - delete them if they do if arcpy.Exists("addpts"): arcpy.Delete_management("addpts") if arcpy.Exists("CLs"): arcpy.Delete_management("CLs") #Create in memory feature layers of input feature classes arcpy.MakeFeatureLayer_management(addptFC, "addpts") arcpy.MakeFeatureLayer_management(clFC, "CLs") #Create UpdateCursor addptrows = arcpy.UpdateCursor("addpts", "", "", "STREET_B", "STREET_B A") #Set variable to "shape" field addptDesc = arcpy.Describe("addpts") addptSFN = addptDesc.ShapeFieldName #Field that holds the shape info clDesc = arcpy.Describe("CLs") clSFN = clDesc.ShapeFieldName #Field that holds the shape info #Begin loop for address points for addptrow in addptrows: addptValue = addptrow.getValue("STREET_B") #Create searchCursor for temporary centerline feature layer clrows = arcpy.SearchCursor("CLs", "", "", "ST_BOTH", "ST_BOTH A") #Set variable for feature addptFEAT = addptrow.getValue(addptSFN) #Set variable for feature part addptPART = addptFEAT.getPart() #Set variables for "X" and "Y" coordinates for feature part addptX = addptPART.X addptY = addptPART.Y #Create empty list to store midpoint distances and angles of parts - compare all values to find which feature #with similar street name is closest to address point closestMid = [] #Begin loop for centerlines for clrow in clrows: clValue = clrow.getValue("ST_BOTH") vertexList = [] #Find all like street names between address points and centerlines if addptValue == clValue: #Set variable for feature clFEAT = clrow.getValue(clSFN) #Set variable for feature part clPART = clFEAT.getPart(0) #Set variables for "X" and "Y" coordinates for feature part for clpartObj in clPART: vertexList.append((clpartObj.X, clpartObj.Y)) vertexCount = len(vertexList) if vertexCount < 2: break #If only one vertex then exit loop (no midpoint to calculate) z = 0 #Loop through feature parts and get coordinates for each vertex while z + 2 <= vertexCount: #That is until you run out of pairs to count #Get vertices for segment x1 = vertexList[0] y1 = vertexList [1] x2 = vertexList[z + 1][0] y2 = vertexList[z + 1][1] #Calculate the midpoint of the line segment midX = (x1 + x2)/2 midY = (y1 + y2)/2 #Calculate the distance from the address point to the midpoint of the part distMid = math.sqrt(((addptX - midX)**2) + ((addptY - midY)**2)) #These delta values calculated on the feature parts deltaX = x2 - x1 deltaY = y2 - y1 #Calculate label angle for address point #("90" added so that point angle is perpendicular to line angle) addptAngle = (90 + (57.2957795 * math.atan2(deltaY, deltaX))) #"closestMid" is the nested list storing: # 1) Distance from address point to midpoint of feature part, # 2) Address point label angle closestMid.append((distMid, addptAngle)) z += 1 #Delete the list holding the coordinates of all vertices for all feature parts del vertexList[:] #This is where the closest value for "closestMid" is finalized finalAngle = 0 #The angle the address point will take closestValue = 0 #The closest midpoint-to-address-point distance #Sort the list "closestMid" and assign the first value of midpoint distance and #address point angle to variables closestMid.sort() closestValue = closestMid[0][0] finalAngle = closestMid[0][1] #Write the address point angle to the "angle" field and update the row within #the temporary feature layer "addpts" addptrow.angle = finalAngle addptrows.updateRow(addptrow) #Delete the list holding the midpoint values and address point label angle values #and delete the row and cursor for the centerline feature layer del closestMid[:] del clrow, clrows #Copy the temporary feature layer "addpts" to the feature class "OutFC" arcpy.CopyFeatures_management("addpts", OutFC) #Delete the row and cursor for the address point feature layer del addptrow, addptrows
Jonathan Oelschlager Could you give me some tips on how to make this work? I'm new to python and would love to be able to use this with my streets and address points!
My apologies for not responding sooner. I revisited this project recently and put together a summary of items that this program needs, as well as a few details of how I went about it.
The street centerline file had to be densified as the script could not handle true curves/arcs. I essentially dissected the centerlines into feature parts, then to vertex pairs (X,Y) for each part. From this the script calculates the midpoint of each feature part from the centerlines, calculates the distance from each midpoint to the current address point (using a cursor), then calculates the angle of the feature part. "90" was added to the angle calculation to get the address point angle to be perpendicular to the street centerline. (Edit - the reasoning for getting feature part midpoints and comparing them to address points is to find the closest feature part to the address point. Then, once the closest is determined, the angle of the feature part + 90 was transferred to the angle of the address point)
Once I wrote the script and made the necessary edits to get it going, then I checked a few areas that I knew would not label like I wanted. Cul-de-sacs are not handled properly so I handled those separately (unless the street centerline was drawn to fit the entire circle of the cul-de-sac - see attached image), and dead ends were manually edited. All in all, I was able to rotate approximately 85% of roughly 50,000 address points we have with this script.
Note: Python 2.6 was used within ArcGIS 10.0 SP 4 for this script