Address Point Labeling Script

14623
4
Jump to solution
12-28-2012 05:16 AM
by Anonymous User
Not applicable
Original User: joelschlager

The goal of this project is to rotate address point labels (house number specifically) to be perpendicular to the street to which they belong (purely by street name comparison thus far).  Inputs are an address point (point) feature class and a road centerline (polyline) feature class. The output is a point feature class with a field called "angle" populated with a calculated angle for labeling (the input address point feature class has this "angle" field as well but isn't calculated).  My problem is with the logic within the ???while??? loop as I tried to account for cul-de-sacs and dead ends but found that it also included the ends of all feature parts.  Is there a way to test if a feature part is connected to another feature part?  Any help/guidance is appreciated (an illustration and sample data are provided).  **NOTE** I had to make the street names match exactly at this point as I have not added tests to check for differences yet.
#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
1 Solution

Accepted Solutions
by Anonymous User
Not applicable
Original User: joelschlager

I made some adjustments to the original code.  They are:

1.) removed the calculations for dead ends and cul-de-sacs as they were causing address points at the end of feature parts but not necessarily at the end of a feature to label incorrectly

2.) removed the "else" statement that followed the check for similar street names as it was causing the nested list "vertexList" to be out of range

This isn't the optimal solution as it still does not account for address points at the end of features, but it does clear up the original issue I had.  The field "STREET_B" is the field associated with the address point layer that holds the street name, while "ST_BOTH" is the field associated with the street centerline layer that holds the street name.  Both are compared but I have not added code yet to handle situations where an address point street name is misspelled/not matched with a centerline street name.  Updated code is posted for reference.

#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

View solution in original post

0 Kudos
4 Replies
by Anonymous User
Not applicable
Original User: joelschlager

I made some adjustments to the original code.  They are:

1.) removed the calculations for dead ends and cul-de-sacs as they were causing address points at the end of feature parts but not necessarily at the end of a feature to label incorrectly

2.) removed the "else" statement that followed the check for similar street names as it was causing the nested list "vertexList" to be out of range

This isn't the optimal solution as it still does not account for address points at the end of features, but it does clear up the original issue I had.  The field "STREET_B" is the field associated with the address point layer that holds the street name, while "ST_BOTH" is the field associated with the street centerline layer that holds the street name.  Both are compared but I have not added code yet to handle situations where an address point street name is misspelled/not matched with a centerline street name.  Updated code is posted for reference.

#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
0 Kudos
WendySilva
New Contributor

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!

0 Kudos
WendySilva
New Contributor
I can't get the script to work.  Being new with python, I can't get it to work and I don't know how to fix it so that it does work.  Please tell me how I'm supposed to use this script.
0 Kudos
JonathanOelschlager
New Contributor

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.

  • Make sure that street names for both address points and street centerlines match as best as can be - this will capture more points to rotate
  • Densify street centerlines - what I did was take our street centerlines (in SDE) and export this feature class to a shapefile as this densifies true curves, etc., then I exported this shapefile to a local FGDB
  • Make sure to check field names in your address point and street centerline files - match them in the script to what you have in your address point and street centerline files

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

0 Kudos