def createUniqueIdentifier(): '''filters through records and calculates an incremental Unique Identifier for routes that are not border routes, to handle Y's, eyebrows, and splits that would cause complex routes''' workspaceLocation = gdb #MakeFeatureLayer_management(lyr,"RCL_Particles",where_clause="COUNTY_L = COUNTY_R AND STATE_L = STATE_R AND ( L_F_ADD =0 OR L_T_ADD =0 OR R_F_ADD =0 OR R_T_ADD =0)") featureClassName = lyr #from arcpy.da import SearchCursor as daSearchCursor, UpdateCursor as daUpdateCursor, Editor as daEditor alphabetListForConversion = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] newCursor = daSearchCursor(featureClassName, uniqueIdInFields) searchList = list() for searchRow in newCursor: searchList.append(list(searchRow)) # Transforms the row tuple into a list so it can be edited. if "newCursor" in locals(): del newCursor else: pass matchCount = 0 matchList = list() for testRow in searchList: if (testRow[1] == testRow[2] and testRow[3] == testRow[4] and (str(testRow[5]) == "0" or str(testRow[6]) == "0" or str(testRow[7]) == "0" or str(testRow[8]) == "0")): matchCount += 1 matchList.append(testRow) matchedRowDictionary = dict() for matchedRow in matchList: matchedRowContainer = list() # If the key already exists, assign the previous list of lists # to the list container, then append the new list # before updating the new value to that key in the dictionary. if matchedRow[10] in matchedRowDictionary: matchedRowContainer = matchedRowDictionary[matchedRow[10]] matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer # Otherwise, the key needs to be created # with the value, the list container, having only # one list contained within it for now. else: matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer for LRSKey in matchedRowDictionary: outRowContainer = matchedRowDictionary[LRSKey] # Sort based on length outRowContainer = sorted(outRowContainer, key = lambda sortingRow: sortingRow[11]) countVariable = 0 # Start at 0 for unique values LRSVariable = "" for outRowIndex, outRow in enumerate(outRowContainer): # Is this the first list/row in the key's list container? # If so, then set the Resolution_Order to 0 if outRowIndex == 0: outRow[9] = 0 else: countVariable += 1 if countVariable in [1, 2, 3, 4, 5, 6, 7, 8, 9]: outRow[9] = countVariable elif countVariable >= 10 and countVariable <= 34: outRow[9] = alphabetListForConversion[countVariable - 10] # Converts countVariable to an alpha character, without the letter "O". else: print "The count Variable is above 34. Ran out of numbers and letters to use as unique values." LRSVariable = outRow[10] LRSVariableShortened = str(LRSVariable[:-1]) # Returns the LRSVariable without the last character. LRSVariable = LRSVariableShortened + str(outRow[9]) outRow[10] = LRSVariable outRowString = "" for outRowElement in outRow: outRowString = outRowString + str(outRowElement) + " " print outRowString outRowContainer[outRowIndex] = outRow matchedRowDictionary[LRSKey] = outRowContainer newEditingSession = daEditor(workspaceLocation) newEditingSession.startEditing() newEditingSession.startOperation() newCursor = daUpdateCursor(featureClassName, uniqueIdOutFields) # @UndefinedVariable for existingRow in newCursor: formattedOutRow = list() if existingRow[2] in matchedRowDictionary.keys(): outRowContainer = matchedRowDictionary[existingRow[2]] for outRow in outRowContainer: if existingRow[0] == outRow[0]: # Test for matching OBJECTID fields. formattedOutRow.append(outRow[0]) formattedOutRow.append(outRow[9]) formattedOutRow.append(outRow[10]) newCursor.updateRow(formattedOutRow) else: pass else: pass newEditingSession.stopOperation() newEditingSession.stopEditing(True) if "newCursor" in locals(): del newCursor else: pass
def RoadinName(roadFeatures, nameExclusions): """This module corrects the road names in the soundex code where the road is named like Road A or Road 12 """ # Need to add logic to remove the ST from roads like 3RD ST and make sure that this translates to 0003 # and not 003. fieldList = ['OBJECTID', 'RD', 'Soundex'] #Placeholder. Recompiled in nameExclusions for loop. listMatchString = re.compile(r'^WEST', re.IGNORECASE) roadNameString = '' roadPreSoundexString = '' roadSoundexString = '' holderList = list() testMatch0 = None testMatch1 = None testMatch2 = None testMatch3 = None # Get the data from the geodatabase so that it can be used in the next part of the function. cursor = daSearchCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: listRow = list(row) holderList.append(listRow) # Clean up if "cursor" in locals(): del cursor else: pass if "row" in locals(): del row else: pass # Matches any group of 3 alpha characters in the string, ignoring case. tripleAlphaMatchString = re.compile(r'[A-Z][A-Z][A-Z]', re.IGNORECASE) # Matches 1 or 2 alpha characters at the start of a string, ignoring case. singleOrDoubleAlphaMatchString = re.compile(r'^[A-Z]$|^[A-Z][A-Z]$', re.IGNORECASE) # Matches 1 to 4 digits at the start of a string, probably no reason to ignore case in the check. singleToQuadNumberMatchString = re.compile(r'^[0-9]$|^[0-9][0-9]$|^[0-9][0-9][0-9]$|^[0-9][0-9][0-9][0-9]$') anyNumberMatchString = re.compile(r'[0-9]', re.IGNORECASE) # For roads that don't match a name exclusion: singleOrDoubleNumberThenAlphaMatchString = re.compile(r'^[0-9][0-9][A-Z]$|^[0-9][A-Z]$', re.IGNORECASE) # For roads that don't match a name exclusion and should be normally Numdexed. firstCharacterNumberString = re.compile(r'^[0-9]') ## Significant structure change here 2014-11-05. ## Watch for possible bugs. ## ## Added Numdex logic to this part, which ## caused some issues. ## ## Flattened the loops out here so that it only ## does a run through the ## <for heldRow in holderList> ## loop once instead of doing it once per ## entry in the nameExclusions list via ## <for excludedText in nameExclusions> ## ## Runs faster now. After changing the regex string ## to be dynamically generated prior to compilation ## and using \b{0}\b as part of the pattern, ## errors *seem* to be gone. stringToCompile = "" # Perform some regex on the strings to produce a new soundex in certain cases. for i, excludedText in enumerate(nameExclusions): #shift left start excludedText = str(excludedText) excludedText = excludedText.upper() #listMatchString = re.compile(r'^{0}\s'.format(re.escape(excludedText)), re.IGNORECASE) ## Old version, pre-dynamic generation. if i == 0: stringToCompile = r'^\b{0}\b\ '.format(re.escape(excludedText)) else: stringToCompile = stringToCompile + r'|^\b{0}\b\ '.format(re.escape(excludedText)) print i listMatchString = re.compile(stringToCompile, re.IGNORECASE) print "stringToCompile = " + str(stringToCompile) for heldRow in holderList: roadNameString = '' roadPreSoundexString = '' roadSoundexString = '' roadNameString = str(heldRow[1]) roadNameString = roadNameString.upper() roadNameString = roadNameString.replace(".", "") exclusionMatch = listMatchString.search(roadNameString) if exclusionMatch != None: # Testing for excluded Road Names such as "Road" and "CR" in "Road 25" and "CR 2500". # Get the characters from the end of the testMatch to the end of the string. # Should return a string that starts with a space. roadPreSoundexString = roadNameString[exclusionMatch.end():] # Replace with a search for " " by group in regex. roadPreSoundexString = roadPreSoundexString.replace(" ", "") roadPreSoundexString = roadPreSoundexString.replace(" ", "") # then loop through the groups to replace with "" so that any number # of spaces can be removed. print "roadNameString = " + str(roadNameString) print "roadPreSoundexString = " + str(roadPreSoundexString) # Do subbing for #ST, #ND, #RD, #TH etc... for numberEnding in ordinalNumberEndings: nonsensitiveReplace = re.compile(r'[0-9]{0}'.format(re.escape(numberEnding), re.IGNORECASE)) replaceMatch = nonsensitiveReplace.search(roadNameString) if replaceMatch != None: roadPreSoundexString = re.sub(replaceMatch.group(0), "", roadPreSoundexString) else: pass # Replace with regex string that matches spaces as groups, then loop through groups to replace. roadPreSoundexString = roadPreSoundexString.replace(" ", "") roadPreSoundexString = roadPreSoundexString.replace(" ", "") testMatch0 = None testMatch0 = tripleAlphaMatchString.search(roadPreSoundexString) testMatch1 = None testMatch1 = singleOrDoubleAlphaMatchString.search(roadPreSoundexString) testMatch2 = None testMatch2 = singleToQuadNumberMatchString.search(roadPreSoundexString) testMatch3 = None testMatch3 = anyNumberMatchString.search(roadPreSoundexString) if testMatch0 != None: roadSoundexString = soundex(roadPreSoundexString) # Slice the roadSoundexString to remove the first character, but keep the rest. if len(roadSoundexString) >= 4: roadSoundexString = roadSoundexString[1:4] # The next line looks complicated, but exclusionMatch.group(0)[0:1] # is actually just getting the first letter of the first matched pattern. roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString elif len(roadSoundexString) == 3: roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString elif len(roadSoundexString) == 2 or len(roadSoundexString) == 1: roadSoundexString = roadSoundexString.zfill(3) roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString else: pass heldRow[2] = roadSoundexString elif testMatch1 != None: # Road A, Road BB, or similar. roadPreSoundexString = roadPreSoundexString[testMatch1.start():testMatch1.end()] if len(roadPreSoundexString) > 2: pass elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString else: pass elif testMatch2 != None: roadPreSoundexString = roadPreSoundexString[testMatch2.start():testMatch2.end()] if len(roadPreSoundexString) > 4: pass elif len(roadPreSoundexString) == 4: # Slice the string to include only the first 3 characters, as slice end is non-inclusive. roadSoundexString = roadPreSoundexString[:4] roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 3: roadSoundexString = roadPreSoundexString roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = exclusionMatch.group(0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString else: pass else: pass else: roadNameString = heldRow[1] testMatch4 = None testMatch4 = singleOrDoubleNumberThenAlphaMatchString.search(roadNameString) testMatch5 = None testMatch5 = firstCharacterNumberString.search(roadNameString) # Numdex with one or two numbers, then alpha. if testMatch4 != None: roadPreSoundexString = roadNameString[testMatch4.start():] roadSoundexString = roadPreSoundexString.zfill(4) heldRow[2] = roadSoundexString # Normal Numdex if there were not one or two numbers, then alpha, but the string starts with a number. elif testMatch5 != None: numerical_re = re.compile("[A-Z]|[^0-9][^0-9][^0-9][^0-9]") roadPreSoundexString = roadNameString.replace(" ", "") roadSoundexString = re.sub(numerical_re,"", roadPreSoundexString.zfill(4)) if len(roadSoundexString) > 4: roadSoundexString = roadSoundexString[:5] else: pass roadSoundexString = roadSoundexString.zfill(4) heldRow[2] = roadSoundexString else: # Check for AA, BB, EE, etc without an excluded name in front of it if len(roadNameString) == 2: if roadNameString[0] == roadNameString[1]: roadPreSoundexString = roadNameString roadSoundexString = roadPreSoundexString.zfill(4) else: # Normal Soundex roadPreSoundexString = roadNameString roadSoundexString = soundex(roadPreSoundexString) heldRow[2] = roadSoundexString # shift left end try: # Start an edit session for this workspace because the centerline # feature class participates in a topology. editSession = daEditor(gdb) editSession.startEditing(False, False) editSession.startOperation() print "Editing started." cursor = daUpdateCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: for heldRow in holderList: # N^2 looping, try not to place print statements inside this block. if str(row[0]) == str(heldRow[0]): cursor.updateRow(heldRow) else: pass editSession.stopOperation() editSession.stopEditing(True) print "Editing complete." except Exception as e: print "Failed to update the Soundex values." print e.message print GetMessages(2) finally: # Clean up if "cursor" in locals(): del cursor else: pass if "row" in locals(): del row else: pass
def extendAndIntersectRoadFeatures(): # Place the operations that extend each road line segment by a certain distance here. # Should extend all the features that exist in the post-erase dataset. Might be more difficult # to calculate the angle of these lines accurately, but it should be easier to figure out # than trying to get the lines to split correctly with the buggy SplitLineAtPoint tool. # Extend the lines in both directions by X_Val Feet. print "Starting to extend and intersect road features." if arcpy.Exists(createdExtensionLines): arcpy.Delete_management(createdExtensionLines) else: pass arcpy.CreateFeatureclass_management(gdb, "createdExtensionLines", "POLYLINE", "", "", "", spatialReferenceProjection) # Add a column for roadname called roadNameForSplit. arcpy.AddField_management(createdExtensionLines, "roadNameForSplit", "TEXT", "", "", "55") # Add a column which stores the angle to display a label called called LabelAngle. arcpy.AddField_management(createdExtensionLines, "LabelAngle", "DOUBLE", "", "", "") # Change to double. # Add a column which stores the County Number. arcpy.AddField_management(createdExtensionLines, "County_Number", "DOUBLE", "", "", "") # Hard coded the buffer distance to 22176 instead of calculating based on SQRT_DIV8 field value. extensionDistance = 22176 # (4.2 * 5280) roadLinesToInsertList = list() roadLinesList = getRoadLinesList() for roadLinesItem in roadLinesList: roadNameToUse = roadLinesItem[3] countyNumber = roadLinesItem[4] linePointsArray = arcpy.Array() firstPointTuple = (roadLinesItem[2].firstPoint.X, roadLinesItem[2].firstPoint.Y) lastPointTuple = (roadLinesItem[2].lastPoint.X, roadLinesItem[2].lastPoint.Y) # Make this a two-step process. # Might... maybe, possibly, be as simple as # adding _1 to the end of the first set of variables, # adding _2 to the end of the second set of variables, # then making the extensions in both directions # and creating a new line that has the endpoints # from both sides as it's first and last point. # if necessary, could add the other points in between # but probably not necessary just for generating # an intersection point. yValue_1 = -(lastPointTuple[1] - firstPointTuple[1]) # made y value negative xValue_1 = lastPointTuple[0] - firstPointTuple[0] lineDirectionAngle_1 = math.degrees(math.atan2(xValue_1, yValue_1)) # reversed x and y lineDirectionAngle_1 = -(((lineDirectionAngle_1 + 180) % 360) - 180) # correction for certain quadrants #print "lineDirectionAngle: " + str(lineDirectionAngle_1) origin_x_1 = firstPointTuple[0] origin_y_1 = firstPointTuple[1] yValue_2 = -(firstPointTuple[1] - lastPointTuple[1]) # made y value negative xValue_2 = firstPointTuple[0] - lastPointTuple[0] lineDirectionAngle_2 = math.degrees(math.atan2(xValue_2, yValue_2)) # reversed x and y lineDirectionAngle_2 = -(((lineDirectionAngle_2 + 180) % 360) - 180) # correction for certain quadrants #print "lineDirectionAngle: " + str(lineDirectionAngle_2) origin_x_2 = lastPointTuple[0] origin_y_2 = lastPointTuple[1] (disp_x_1, disp_y_1) = (extensionDistance * math.sin(math.radians(lineDirectionAngle_1)), extensionDistance * math.cos(math.radians(lineDirectionAngle_1))) (end_x_1, end_y_1) = (origin_x_1 + disp_x_1, origin_y_1 + disp_y_1) (disp_x_2, disp_y_2) = (extensionDistance * math.sin(math.radians(lineDirectionAngle_2)), extensionDistance * math.cos(math.radians(lineDirectionAngle_2))) (end_x_2, end_y_2) = (origin_x_2 + disp_x_2, origin_y_2 + disp_y_2) startPoint = arcpy.Point() endPoint = arcpy.Point() startPoint.ID = 0 startPoint.X = end_x_1 startPoint.Y = end_y_1 endPoint.ID = 1 endPoint.X = end_x_2 endPoint.Y = end_y_2 linePointsArray.add(startPoint) linePointsArray.add(endPoint) newLineFeature = arcpy.Polyline(linePointsArray) # Need to create an extension for both ends of the line and add them # to the array. #newLineFeature = createdExtensionLinesCursor.newRow() #newLineFeature.SHAPE = linePointsArray lineDirectionOutput = "0" if lineDirectionAngle_1 > 0: lineDirectionOutput = lineDirectionAngle_1 elif lineDirectionAngle_2 > 0: lineDirectionOutput = lineDirectionAngle_2 else: pass roadLinesToInsertList.append([newLineFeature, roadNameToUse, lineDirectionOutput, countyNumber]) #createdExtensionLinesCursor.insertRow([newLineFeature, roadNameToUse, lineDirectionOutput]) if "newLineFeature" in locals(): del newLineFeature else: pass # Consider building this as a separate list and then just looping # through the list to put it into the cursor instead # of doing logic and inserting into the cursor at the same place. #start editing session newEditingSession = daEditor(gdb) newEditingSession.startEditing() newEditingSession.startOperation() createdExtensionLinesCursor = daInsertCursor(createdExtensionLines, ["SHAPE@", "roadNameForSplit", "LabelAngle", "County_Number"]) for roadLinesToInsertItem in roadLinesToInsertList: createdExtensionLinesCursor.insertRow(roadLinesToInsertItem) # End editing session newEditingSession.stopOperation() newEditingSession.stopEditing(True) if "createdExtensionLinesCursor" in locals(): del createdExtensionLinesCursor else: pass if arcpy.Exists("countyRoadNameRosette"): arcpy.Delete_management("countyRoadNameRosette") else: pass arcpy.CreateFeatureclass_management(gdb, "countyRoadNameRosette", "POINT", "", "", "", spatialReferenceProjection) arcpy.AddField_management(countyRoadNameRosette, "roadNameForSplit", "TEXT", "", "", "55") arcpy.AddField_management(countyRoadNameRosette, "LabelAngle", "DOUBLE", "", "", "") # Change to double. arcpy.AddField_management(countyRoadNameRosette, "County_Number", "DOUBLE", "", "", "") arcpy.AddField_management(countyRoadNameRosette, "COUNTY_NAME", "TEXT", "", "", "55") # Now then, need to check for the existence # of and delete the point intersection layer # if it exists. # Then, recreate it and the proper fields. inMemoryCountyBorderExtension = "aCountyBorderExtensionBuffer" inMemoryExtensionLines = "aLoadedExtensionLines" # Temporary layer, use CopyFeatures_management to persist to disk. arcpy.MakeFeatureLayer_management(countyBorderFeature, inMemoryCountyBorderExtension) # County Border extension feature # Temporary layer, use CopyFeatures_management to persist to disk. arcpy.MakeFeatureLayer_management(createdExtensionLines, inMemoryExtensionLines) # Line extension feature borderFeatureList = getBorderFeatureList() borderFeatureList = sorted(borderFeatureList, key=lambda feature: feature[4]) for borderFeature in borderFeatureList: borderFeatureName = borderFeature[3] borderFeatureNumber = borderFeature[4] print "borderFeatureName: " + str(borderFeatureName) + " & borderFeatureNumber: " + str(int(borderFeatureNumber)) countyBorderWhereClause = ' "COUNTY_NUMBER" = ' + str(int(borderFeatureNumber)) + ' ' arcpy.SelectLayerByAttribute_management(inMemoryCountyBorderExtension, "NEW_SELECTION", countyBorderWhereClause) countyBorderSelectionCount = arcpy.GetCount_management(inMemoryCountyBorderExtension) print "County Borders Selected: " + str(countyBorderSelectionCount) # Had to single-quote the borderFeatureNumber because it is stored as a string in the table. # Unsingle quoted because it was changed to a float. extensionLinesWhereClause = ' "COUNTY_NUMBER" = ' + str(int(borderFeatureNumber)) + ' ' arcpy.SelectLayerByAttribute_management(inMemoryExtensionLines, "NEW_SELECTION", extensionLinesWhereClause) extensionLineSelectionCount = arcpy.GetCount_management(inMemoryExtensionLines) print "Extension Lines Selected: " + str(extensionLineSelectionCount) if arcpy.Exists("tempRoadNameRosette"): arcpy.Delete_management("tempRoadNameRosette") else: pass if arcpy.Exists("tempRoadNameRosetteSinglePoint"): arcpy.Delete_management("tempRoadNameRosetteSinglePoint") else: pass arcpy.Intersect_analysis([inMemoryCountyBorderExtension, inMemoryExtensionLines], tempRoadNameRosette, "ALL", "", "POINT") # Intersect to an output temp layer. # Next, need to loop through all of the counties. # Get the county number and use it to select # a county extension buffer in the county # extension buffers layer. # Then, use the county number to select # all of the lines for that county # in the extension lines layer. # Then, export those to a temp layer in the fgdb. # Change multipoint to singlepoint. # Was working until I moved from gisprod to sdedev for the data source. # not sure why. Check to make sure projections match. try: # Run the tool to create a new fc with only singlepart features arcpy.MultipartToSinglepart_management(tempRoadNameRosette, tempRoadNameRosetteSinglePoint) # Check if there is a different number of features in the output # than there was in the input inCount = int(arcpy.GetCount_management(tempRoadNameRosette).getOutput(0)) outCount = int(arcpy.GetCount_management(tempRoadNameRosetteSinglePoint).getOutput(0)) if inCount != outCount: print "Found " + str(outCount - inCount) + " multipart features." #print "inCount, including multipart = " + str(inCount) #print "outCount, singlepart only = " + str(outCount) else: print "No multipart features were found" except arcpy.ExecuteError: print arcpy.GetMessages() except Exception as e: print e print "Appending the temp point layer to the county point intersection layer." arcpy.Append_management([tempRoadNameRosetteSinglePoint], countyRoadNameRosette, "NO_TEST") # K, worked once. Just need to change LabelAngle to a float and it might be what # I want... except for too slow, but that's why we have the multiprocessing module. # So add multiprocessing capability after it correctly creates the desired output. # Will need to change the way the temp files are handled/named, but should be # fairly simple. -- Getting the multiprocessing module to work might be less # simple, but still probably worth it for the speed increase. # For multiprocessing: have each county receive a random number after its temp file, # i.e. try: write feature with random number, except retry with different number. # then, read all of the temp file # Then, print a completion message # and check the points out in a map. print "Done adding points to the countyRoadNameRosette feature class." print "Normalizing the label angle values."
def createUniqueIdentifier(): '''filters through records and calculates an incremental Unique Identifier for routes that are not border routes, to handle Y's, eyebrows, and splits that would cause complex routes''' workspaceLocation = gdb #MakeFeatureLayer_management(lyr,"RCL_Particles",where_clause="COUNTY_L = COUNTY_R AND STATE_L = STATE_R AND ( L_F_ADD =0 OR L_T_ADD =0 OR R_F_ADD =0 OR R_T_ADD =0)") featureClassName = lyr #from arcpy.da import SearchCursor as daSearchCursor, UpdateCursor as daUpdateCursor, Editor as daEditor alphabetListForConversion = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ] newCursor = daSearchCursor(featureClassName, uniqueIdInFields) searchList = list() for searchRow in newCursor: searchList.append( list(searchRow) ) # Transforms the row tuple into a list so it can be edited. if "newCursor" in locals(): del newCursor else: pass matchCount = 0 matchList = list() for testRow in searchList: if (testRow[1] == testRow[2] and testRow[3] == testRow[4] and (str(testRow[5]) == "0" or str(testRow[6]) == "0" or str(testRow[7]) == "0" or str(testRow[8]) == "0")): matchCount += 1 matchList.append(testRow) matchedRowDictionary = dict() for matchedRow in matchList: matchedRowContainer = list() # If the key already exists, assign the previous list of lists # to the list container, then append the new list # before updating the new value to that key in the dictionary. if matchedRow[10] in matchedRowDictionary: matchedRowContainer = matchedRowDictionary[matchedRow[10]] matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer # Otherwise, the key needs to be created # with the value, the list container, having only # one list contained within it for now. else: matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer for LRSKey in matchedRowDictionary: outRowContainer = matchedRowDictionary[LRSKey] # Sort based on length outRowContainer = sorted(outRowContainer, key=lambda sortingRow: sortingRow[11]) countVariable = 0 # Start at 0 for unique values LRSVariable = "" for outRowIndex, outRow in enumerate(outRowContainer): # Is this the first list/row in the key's list container? # If so, then set the Resolution_Order to 0 if outRowIndex == 0: outRow[9] = 0 else: countVariable += 1 if countVariable in [1, 2, 3, 4, 5, 6, 7, 8, 9]: outRow[9] = countVariable elif countVariable >= 10 and countVariable <= 34: outRow[9] = alphabetListForConversion[ countVariable - 10] # Converts countVariable to an alpha character, without the letter "O". else: print "The count Variable is above 34. Ran out of numbers and letters to use as unique values." LRSVariable = outRow[10] LRSVariableShortened = str( LRSVariable[:-1] ) # Returns the LRSVariable without the last character. LRSVariable = LRSVariableShortened + str(outRow[9]) outRow[10] = LRSVariable outRowString = "" for outRowElement in outRow: outRowString = outRowString + str(outRowElement) + " " print outRowString outRowContainer[outRowIndex] = outRow matchedRowDictionary[LRSKey] = outRowContainer newEditingSession = daEditor(workspaceLocation) newEditingSession.startEditing() newEditingSession.startOperation() newCursor = daUpdateCursor(featureClassName, uniqueIdOutFields) # @UndefinedVariable for existingRow in newCursor: formattedOutRow = list() if existingRow[2] in matchedRowDictionary.keys(): outRowContainer = matchedRowDictionary[existingRow[2]] for outRow in outRowContainer: if existingRow[0] == outRow[ 0]: # Test for matching OBJECTID fields. formattedOutRow.append(outRow[0]) formattedOutRow.append(outRow[9]) formattedOutRow.append(outRow[10]) newCursor.updateRow(formattedOutRow) else: pass else: pass newEditingSession.stopOperation() newEditingSession.stopEditing(True) if "newCursor" in locals(): del newCursor else: pass
def RoadinName(roadFeatures, nameExclusions): """This module corrects the road names in the soundex code where the road is named like Road A or Road 12 """ # Need to add logic to remove the ST from roads like 3RD ST and make sure that this translates to 0003 # and not 003. fieldList = ['OBJECTID', 'RD', 'Soundex'] #Placeholder. Recompiled in nameExclusions for loop. listMatchString = re.compile(r'^WEST', re.IGNORECASE) roadNameString = '' roadPreSoundexString = '' roadSoundexString = '' holderList = list() testMatch0 = None testMatch1 = None testMatch2 = None testMatch3 = None # Get the data from the geodatabase so that it can be used in the next part of the function. cursor = daSearchCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: listRow = list(row) holderList.append(listRow) # Clean up if "cursor" in locals(): del cursor else: pass if "row" in locals(): del row else: pass # Matches any group of 3 alpha characters in the string, ignoring case. tripleAlphaMatchString = re.compile(r'[A-Z][A-Z][A-Z]', re.IGNORECASE) # Matches 1 or 2 alpha characters at the start of a string, ignoring case. singleOrDoubleAlphaMatchString = re.compile(r'^[A-Z]$|^[A-Z][A-Z]$', re.IGNORECASE) # Matches 1 to 4 digits at the start of a string, probably no reason to ignore case in the check. singleToQuadNumberMatchString = re.compile( r'^[0-9]$|^[0-9][0-9]$|^[0-9][0-9][0-9]$|^[0-9][0-9][0-9][0-9]$') anyNumberMatchString = re.compile(r'[0-9]', re.IGNORECASE) # For roads that don't match a name exclusion: singleOrDoubleNumberThenAlphaMatchString = re.compile( r'^[0-9][0-9][A-Z]$|^[0-9][A-Z]$', re.IGNORECASE) # For roads that don't match a name exclusion and should be normally Numdexed. firstCharacterNumberString = re.compile(r'^[0-9]') ## Significant structure change here 2014-11-05. ## Watch for possible bugs. ## ## Added Numdex logic to this part, which ## caused some issues. ## ## Flattened the loops out here so that it only ## does a run through the ## <for heldRow in holderList> ## loop once instead of doing it once per ## entry in the nameExclusions list via ## <for excludedText in nameExclusions> ## ## Runs faster now. After changing the regex string ## to be dynamically generated prior to compilation ## and using \b{0}\b as part of the pattern, ## errors *seem* to be gone. stringToCompile = "" # Perform some regex on the strings to produce a new soundex in certain cases. for i, excludedText in enumerate(nameExclusions): #shift left start excludedText = str(excludedText) excludedText = excludedText.upper() #listMatchString = re.compile(r'^{0}\s'.format(re.escape(excludedText)), re.IGNORECASE) ## Old version, pre-dynamic generation. if i == 0: stringToCompile = r'^\b{0}\b\ '.format(re.escape(excludedText)) else: stringToCompile = stringToCompile + r'|^\b{0}\b\ '.format( re.escape(excludedText)) print i listMatchString = re.compile(stringToCompile, re.IGNORECASE) print "stringToCompile = " + str(stringToCompile) for heldRow in holderList: roadNameString = '' roadPreSoundexString = '' roadSoundexString = '' roadNameString = str(heldRow[1]) roadNameString = roadNameString.upper() roadNameString = roadNameString.replace(".", "") exclusionMatch = listMatchString.search(roadNameString) if exclusionMatch != None: # Testing for excluded Road Names such as "Road" and "CR" in "Road 25" and "CR 2500". # Get the characters from the end of the testMatch to the end of the string. # Should return a string that starts with a space. roadPreSoundexString = roadNameString[exclusionMatch.end():] # Replace with a search for " " by group in regex. roadPreSoundexString = roadPreSoundexString.replace(" ", "") roadPreSoundexString = roadPreSoundexString.replace(" ", "") # then loop through the groups to replace with "" so that any number # of spaces can be removed. print "roadNameString = " + str(roadNameString) print "roadPreSoundexString = " + str(roadPreSoundexString) # Do subbing for #ST, #ND, #RD, #TH etc... for numberEnding in ordinalNumberEndings: nonsensitiveReplace = re.compile(r'[0-9]{0}'.format( re.escape(numberEnding), re.IGNORECASE)) replaceMatch = nonsensitiveReplace.search(roadNameString) if replaceMatch != None: roadPreSoundexString = re.sub(replaceMatch.group(0), "", roadPreSoundexString) else: pass # Replace with regex string that matches spaces as groups, then loop through groups to replace. roadPreSoundexString = roadPreSoundexString.replace(" ", "") roadPreSoundexString = roadPreSoundexString.replace(" ", "") testMatch0 = None testMatch0 = tripleAlphaMatchString.search(roadPreSoundexString) testMatch1 = None testMatch1 = singleOrDoubleAlphaMatchString.search( roadPreSoundexString) testMatch2 = None testMatch2 = singleToQuadNumberMatchString.search( roadPreSoundexString) testMatch3 = None testMatch3 = anyNumberMatchString.search(roadPreSoundexString) if testMatch0 != None: roadSoundexString = soundex(roadPreSoundexString) # Slice the roadSoundexString to remove the first character, but keep the rest. if len(roadSoundexString) >= 4: roadSoundexString = roadSoundexString[1:4] # The next line looks complicated, but exclusionMatch.group(0)[0:1] # is actually just getting the first letter of the first matched pattern. roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString elif len(roadSoundexString) == 3: roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString elif len(roadSoundexString) == 2 or len( roadSoundexString) == 1: roadSoundexString = roadSoundexString.zfill(3) roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString else: pass heldRow[2] = roadSoundexString elif testMatch1 != None: # Road A, Road BB, or similar. roadPreSoundexString = roadPreSoundexString[testMatch1.start( ):testMatch1.end()] if len(roadPreSoundexString) > 2: pass elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString else: pass elif testMatch2 != None: roadPreSoundexString = roadPreSoundexString[testMatch2.start( ):testMatch2.end()] if len(roadPreSoundexString) > 4: pass elif len(roadPreSoundexString) == 4: # Slice the string to include only the first 3 characters, as slice end is non-inclusive. roadSoundexString = roadPreSoundexString[:4] roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 3: roadSoundexString = roadPreSoundexString roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = exclusionMatch.group( 0)[0:1] + roadSoundexString heldRow[2] = roadSoundexString else: pass else: pass else: roadNameString = heldRow[1] testMatch4 = None testMatch4 = singleOrDoubleNumberThenAlphaMatchString.search( roadNameString) testMatch5 = None testMatch5 = firstCharacterNumberString.search(roadNameString) # Numdex with one or two numbers, then alpha. if testMatch4 != None: roadPreSoundexString = roadNameString[testMatch4.start():] roadSoundexString = roadPreSoundexString.zfill(4) heldRow[2] = roadSoundexString # Normal Numdex if there were not one or two numbers, then alpha, but the string starts with a number. elif testMatch5 != None: numerical_re = re.compile("[A-Z]|[^0-9][^0-9][^0-9][^0-9]") roadPreSoundexString = roadNameString.replace(" ", "") roadSoundexString = re.sub(numerical_re, "", roadPreSoundexString.zfill(4)) if len(roadSoundexString) > 4: roadSoundexString = roadSoundexString[:5] else: pass roadSoundexString = roadSoundexString.zfill(4) heldRow[2] = roadSoundexString else: # Check for AA, BB, EE, etc without an excluded name in front of it if len(roadNameString) == 2: if roadNameString[0] == roadNameString[1]: roadPreSoundexString = roadNameString roadSoundexString = roadPreSoundexString.zfill(4) else: # Normal Soundex roadPreSoundexString = roadNameString roadSoundexString = soundex(roadPreSoundexString) heldRow[2] = roadSoundexString # shift left end try: # Start an edit session for this workspace because the centerline # feature class participates in a topology. editSession = daEditor(gdb) editSession.startEditing(False, False) editSession.startOperation() print "Editing started." cursor = daUpdateCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: for heldRow in holderList: # N^2 looping, try not to place print statements inside this block. if str(row[0]) == str(heldRow[0]): cursor.updateRow(heldRow) else: pass editSession.stopOperation() editSession.stopEditing(True) print "Editing complete." except Exception as e: print "Failed to update the Soundex values." print e.message print GetMessages(2) finally: # Clean up if "cursor" in locals(): del cursor else: pass if "row" in locals(): del row else: pass
def soundex(s): """ Encode a string using Soundex. Takes a string and returns its Soundex representation.""" replacementString = "" #Added a "." here, should replace correctly now. replacementDict = {"A":"1", "E":"2", "H":"3", "I":"4", "O":"5", "U":"6", "W":"7", "Y":"8", ".":""} if len(s) == 2: if s[0] == s[1]:# Only affects one very specific road name type. Kind of a narrow fix. for keyName in replacementDict: if keyName == str(s[1].upper(): replacementString = replacementDict[keyName] enc = str(str(s[0]) + replacementString).zfill(4) return enc else: pass else: pass elif len(s) == 1: enc = str(s[0]).zfill(4) return enc elif len(s) == 0: enc = str("x").zfill(4) return enc else: pass s = normalize(s) last = None enc = s[0] for c in s[1:]: if len(enc) == 4: break if charsubs[c] != last: enc += charsubs[c] last = charsubs[c] while len(enc) < 4: enc += '0' return enc def numdex(s): """this module applies soundex to named streets, and pads the numbered streets with zeros, keeping the numbering system intact""" if s[0] in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.']: # I don't think having a '.' here will do anything unless the road name is ".SomeName" since it # only checks the first character of the string. numerical_re = re.compile("[A-Z]|[^0-9][^0-9][^0-9][^0-9]") s=re.sub(numerical_re,"", s.zfill(4)) return s.zfill(4) else: return soundex(s) def StreetNetworkCheck(gdb): """removes street centerlines from the topology and creates geometric network, then checks geometric network connectivity""" from arcpy import ListDatasets, VerifyAndRepairGeometricNetworkConnectivity_management, RemoveFeatureClassFromTopology_management, CreateGeometricNetwork_management, FindDisconnectedFeaturesInGeometricNetwork_management #print topo fd = ListDatasets(gdb) print fd[0] geonet = fd[0]+"\Street_Network" #print geonet if Exists(geonet): print "Street Geometric Network Already Exists" else: RemoveFeatureClassFromTopology_management(topo, "RoadCenterline") CreateGeometricNetwork_management(fd, "Street_Network", "RoadCenterline SIMPLE_EDGE NO", "#", "#", "#", "#", "#") FindDisconnectedFeaturesInGeometricNetwork_management(fd+"/RoadCenterline", "Roads_Disconnected") StreetLogfile = reviewpath+"/KDOTReview/"+ntpath.basename(ng911)+".log" VerifyAndRepairGeometricNetworkConnectivity_management(geonet, StreetLogfile, "VERIFY_ONLY", "EXHAUSTIVE_CHECK", "0, 0, 10000000, 10000000") def ConflateKDOTrestart(gdb, DOTRoads): """Conflation restart for selecting KDOT roads to conflate to the NG911 Network""" MakeFeatureLayer_management(DOTRoads+"/KDOT_HPMS_2012","KDOT_Roads","#","#","#") MakeFeatureLayer_management(checkfile+"/RoadCenterline","RoadCenterline","#","#","#") SelectLayerByLocation_management("KDOT_Roads","INTERSECT","RoadCenterline","60 Feet","NEW_SELECTION") FeatureClassToFeatureClass_conversion("KDOT_Roads",checkfile+r"/NG911","KDOT_Roads_Review","#","#","#") def ConflateKDOT(gdb, DOTRoads): """detects road centerline changes and transfers the HPMS key field from the KDOT roads via ESRI conflation tools""" from arcpy import TransferAttributes_edit, DetectFeatureChanges_management, RubbersheetFeatures_edit, GenerateRubbersheetLinks_edit, RubbersheetFeatures_edit checkfile = gdb spatialtolerance = "20 feet" MakeFeatureLayer_management(DOTRoads+"/KDOT_HPMS_2012","KDOT_Roads","#","#","#") MakeFeatureLayer_management(checkfile+"/RoadCenterline","RoadCenterline","#","#","#") if Exists(checkfile+r"/NG911/KDOT_Roads_Review"): print "selection of KDOT roads for conflation already exists" else: SelectLayerByLocation_management("KDOT_Roads","INTERSECT","RoadCenterline","60 Feet","NEW_SELECTION") FeatureClassToFeatureClass_conversion("KDOT_Roads",checkfile+r"/NG911","KDOT_Roads_Review","#","#","#") MakeFeatureLayer_management(checkfile+"/KDOT_Roads_Review","KDOT_Roads_Review","#","#","#") GenerateRubbersheetLinks_edit("KDOT_Roads_Review","RoadCenterline",checkfile+r"/NG911/RoadLinks",spatialtolerance,"ROUTE_ID LRSKEY",checkfile+r"/RoadMatchTbl") MakeFeatureLayer_management(checkfile+"/NG911/RoadLinks","RoadLinks","#","#","#") MakeFeatureLayer_management(checkfile+"/NG911/RoadLinks_pnt","RoadLinks_pnt","#","#","#") RubbersheetFeatures_edit("KDOT_Roads_Review","RoadLinks","RoadLinks_pnt","LINEAR") DetectFeatureChanges_management("KDOT_Roads_Review","RoadCenterline",checkfile+r"/NG911/RoadDifference",spatialtolerance,"#",checkfile+r"/RoadDifTbl",spatialtolerance,"#") MakeFeatureLayer_management(checkfile+"/NG911/RoadDifference","RoadDifference","#","#","#") TransferAttributes_edit("KDOT_Roads_Review","RoadCenterline","YEAR_RECORD;ROUTE_ID",spatialtolerance,"#",checkfile+r"/LRS_MATCH") def addAdminFields(lyr, Alias): try: AddIndex(lyr,"SEGID;COUNTY_L;COUNTY_R;MUNI_L;MUNI_R","RCL_INDEX","NON_UNIQUE","NON_ASCENDING") except: print "indexed" FieldList3=("KDOT_COUNTY_R", "KDOT_COUNTY_L","KDOT_CITY_R", "KDOT_CITY_L", 'UniqueNo' ) for field in FieldList3: addField(lyr, field, "TEXT", "#", "#", "3") FieldList1=('KDOT_ADMO', 'KDOTPreType', 'PreCode', 'SuffCode', 'TDirCode') for field in FieldList1: addField(lyr, field, "TEXT", "#", "#", "1") addField(lyr, "Soundex", "TEXT", "#", "#", "5") addField(lyr, "RID", "TEXT", "#", "#", "26") addField(lyr, "KDOT_START_DATE", "DATE") addField(lyr, "KDOT_END_DATE", "DATE") addField(lyr, "SHAPE_MILES", "Double", "#", "#", "#" ) addField(Alias, "KDOT_PREFIX", "TEXT", "#", "#", "1" ) addField(Alias, "KDOT_CODE", "LONG" ) addField(Alias, "KDOT_ROUTENAME", "TEXT", "#", "#", "3" ) def CalcAdminFields(lyr, Kdotdbfp): """Populate Admin Fields with Default or Derived values""" CalcField(lyr,"UniqueNo",'000',"PYTHON_9.3","#") CalcField(lyr,"KDOT_START_DATE","1/1/1901","PYTHON_9.3","#") CalcField(lyr,"KDOTPreType","!ROUTE_ID![3]","PYTHON_9.3","#") #PreType is a conflated field, consider changing this to calculate from NENA fields TableView(lyr, "NewPretype", "KDOTPreType is Null") CalcField("NewPretype","KDOTPreType","'L'","PYTHON_9.3","#") CalcField(lyr,"KDOT_ADMO","'X'","PYTHON_9.3","#") CalcField(lyr,"PreCode","0","PYTHON_9.3","#") CalcField(lyr,"KDOT_CITY_L","999","PYTHON_9.3","#") CalcField(lyr,"KDOT_CITY_R","999","PYTHON_9.3","#") CalcField(lyr,"TDirCode","0","PYTHON_9.3","#") CalcField(lyr,"SHAPE_MILES","!Shape_Length!/5280.010560021","PYTHON_9.3","#") #There are slightly more than 5280 miles per US Survey foot TableView(Kdotdbfp+"\\NG911_RdDir", "NG911_RdDir") JoinTbl(lyr,"PRD","NG911_RdDir", "RoadDir", "KEEP_COMMON") CalcField(lyr,"PreCode","!NG911_RdDir.RdDirCode!","PYTHON_9.3","#") removeJoin(lyr) TableView(Kdotdbfp+"\NG911_RdTypes", "NG911_RdTypes") CalcField(lyr,"SuffCode","0","PYTHON_9.3","#") JoinTbl(lyr,"STS","NG911_RdTypes", "RoadTypes", "KEEP_COMMON") CalcField(lyr,"SuffCode","!NG911_RdTypes.LRS_CODE_TXT!","PYTHON_9.3","#") removeJoin(lyr) def CountyCode(lyr): """Codify the County number for LRS (based on right side of street based on addressing direction, calculated for LEFT and RIGHT from NG911)""" TableView(Kdotdbfp+"\NG911_County", "NG911_County") JoinTbl(lyr,"COUNTY_L","NG911_County", "CountyName", "KEEP_COMMON") CalcField(lyr,"KDOT_COUNTY_L","!NG911_County.CountyNumber!","PYTHON_9.3","#") removeJoin(lyr) JoinTbl(lyr,"COUNTY_R","NG911_County", "CountyName", "KEEP_COMMON") CalcField(lyr,"KDOT_COUNTY_R","!NG911_County.CountyNumber!","PYTHON_9.3","#") removeJoin(lyr) def CityCodes(lyr, Kdotdbfp): """Codify the City Limit\city number for LRS , calculated for LEFT and RIGHT from NG911)""" TableView(Kdotdbfp+"\City_Limits", "City_Limits") JoinTbl(lyr,"MUNI_R","City_Limits", "CITY", "KEEP_COMMON") CalcField(lyr,"KDOT_CITY_R","str(!City_Limits.CITY_CD!).zfill(3)","PYTHON_9.3","#") removeJoin(lyr) JoinTbl(lyr,"MUNI_L","City_Limits", "CITY", "KEEP_COMMON") CalcField(lyr,"KDOT_CITY_L","str(!City_Limits.CITY_CD!).zfill(3)","PYTHON_9.3","#") removeJoin(lyr) TableView(lyr, "CityRoads", "KDOT_CITY_R = KDOT_CITY_L AND KDOT_CITY_R not like '999'") CalcField("CityRoads","KDOT_ADMO","'W'","PYTHON_9.3","#") def RoadinName1(lyr): """This module corrects the road names in the soundex code where the road is named like Road A or Road 12 """ TableView(lyr,"ROAD_NAME","RD LIKE 'ROAD %'") CalcField("ROAD_NAME","Soundex",""""R"+!RD![5:].zfill(3)""","PYTHON_9.3","#") TableView(lyr,"RD_NAME","RD LIKE 'RD %'") CalcField(lyr,"Soundex","""("R"+!RD![1:5]).zfill(3)""","PYTHON_9.3","#") def RoadinName(roadFeatures, nameExclusions): """This module corrects the road names in the soundex code where the road is named like Road A or Road 12 """ fieldList = ['OBJECTID', 'RD', 'Soundex'] listMatchString = re.compile(r'^WEST', re.IGNORECASE) roadNameString = '' roadPreSoundexString = '' roadSoundexString = '' testMatch = None testMatch1 = None testMatch2 = None # Get the data from the geodatabase so that it can be used in the next part of the function. cursor = daSearchCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: listRow = list(row) holderList.append(listRow) # Clean up if cursor: del cursor else: pass if row: del row else: pass # Perform some regex on the strings to produce a new soundex in certain cases. for excludedText in nameExclusions: excludedText = str(excludedText) excludedText = excludedText.upper() listMatchString = re.compile(r'^{0}\s'.format(re.escape(excludedText)), re.IGNORECASE) # Matches 1 or 2 alpha characters at the start of a string, ignoring case. singleOrDoubleAlphaMatchString = re.compile(r'^[a-z]$|^[a-z][a-z]$', re.IGNORECASE) # Matches 1 to 4 digits at the start of a string, probably no reason to ignore case in the check. singleToQuadNumberMatchString = re.compile(r'^[0-9]$|^[0-9][0-9]$|^[0-9][0-9][0-9]$|^[0-9][0-9][0-9][0-9]$') for heldRow in holderList: roadNameString = str(heldRow[1]) roadNameString = roadNameString.upper() testMatch = listMatchString.search(roadNameString) if testMatch != None: roadPreSoundexString = roadNameString[testMatch.end():] roadPreSoundexString = roadPreSoundexString.replace(" ", "") # Do subbing for #ST, #ND, #RD, #TH etc... for numberEnding in ordinalNumberEndings: nonsensitiveReplace = re.compile(r'[0-9]{0}'.format(re.escape(numberEnding), re.IGNORECASE)) replaceMatch = nonsensitiveReplace.search(roadNameString) if replaceMatch != None: roadPreSoundexString = re.sub(replaceMatch.group(0), replaceMatch.group(0)[0:1], roadPreSoundexString) else: pass # Test for the following conditions: # A, AA as in Road A, RD AA testMatch1 = None testMatch1 = singleOrDoubleAlphaMatchString.search(roadPreSoundexString) # Test for the following conditions: # 1, 10, 100, 1000 as in Road 1, RD 10, Road 100, CR1000 testMatch2 = None testMatch2 = singleToQuadNumberMatchString.search(roadPreSoundexString) if testMatch1 != None: # Road A, Road BB, or similar. roadPreSoundexString = roadPreSoundexString[testMatch1.start():testMatch1.end()] if len(roadPreSoundexString) > 2: pass elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString # Adds the first letter from the excluded text to the start of the Soundex string. roadSoundexString = excludedText[0:1] + roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = excludedText[0:1] + roadSoundexString else: pass elif(testMatch2 != None): roadPreSoundexString = roadPreSoundexString[testMatch2.start():testMatch2.end()] if len(roadPreSoundexString) > 4: pass elif len(roadPreSoundexString) == 4: # Slice the string to include only the first 3 characters. roadSoundexString = roadPreSoundexString[:4] # Add the first letter from the excluded text to the start of the Soundex string. roadSoundexString = excludedText[0:1] + roadSoundexString elif len(roadPreSoundexString) == 3: roadSoundexString = roadPreSoundexString roadSoundexString = excludedText[0:1] + roadSoundexString elif len(roadPreSoundexString) == 2: roadSoundexString = "0" + roadPreSoundexString roadSoundexString = excludedText[0:1] + roadSoundexString elif len(roadPreSoundexString) == 1: roadSoundexString = "00" + roadPreSoundexString roadSoundexString = excludedText[0:1] + roadSoundexString else: pass # One of the excluded texts was found at the start of the name, but it was not followed by # A, AA, 1, 20, 340, 5670, etc... # Instead something like "Road Hitch" or "RD Empire" # Do soundex normally, but replace the first character with the first character from the # excluded text. else: roadSoundexString = soundex(roadPreSoundexString) # Slice the roadSoundexString to remove the first character, but keep the rest. roadSoundexString = roadSoundexString[1:] # Add the first letter from the excluded text to the start of the Soundex string. roadSoundexString = excludedText[0:1] + roadSoundexString # Assign the new road soundex string to the held row's third slot, heldRow[2], # to be used in an update cursor to update the data in the geodatabase. heldRow[2] = roadSoundexString else: pass # Start an edit session for this workspace because the centerline # feature class participates in a topology. editSession = daEditor(gdb) editSession.startEditing(False, False) editSession.startOperation() cursor = daUpdateCursor(roadFeatures, fieldList) # @UndefinedVariable for row in cursor: for heldRow in holderList: if row[0] == heldRow[0]: cursor.updateRow(heldRow) else: pass # Clean up if cursor: del cursor else: pass if row: del row else: pass editSession.stopOperation() editSession.stopEditing(True) def RouteCalc(lyr, soundexNameExclusions): """calculate what should be a nearly unique LRS Route key based on the decoding and street name soundex/numdex function""" #CalcField(lyr,"Soundex","numdex(!RD!)","PYTHON_9.3","#") RoadinName(lyr, soundexNameExclusions) CalcField(lyr, "RID", "str(!KDOT_COUNTY_R!)+str(!KDOT_COUNTY_L!)+str(!KDOT_CITY_R!)+str(!KDOT_CITY_L!)+str(!PreCode!) + !Soundex! + str(!SuffCode!)+str(!UniqueNo!)+str(!TDirCode!)","PYTHON_9.3","#") # Instead of calling numdex here, rewrite and incorporate numdex and soundex functionality into the RoadinName function. def AliasCalc(Alias, DOTRoads): CalcField(Alias, "KDOT_PREFIX", "!LABEL![0]","PYTHON_9.3","#") CalcField(Alias,"KDOT_ROUTENAME","""!A_RD![1:].replace("S","").zfill(3)""","PYTHON_9.3","#") TableView(DOTRoads+"\KDOT_RoutePre", "KDOT_RoutePre") JoinTbl("RoadAlias", "KDOT_PREFIX", "KDOT_RoutePre", "LRSPrefix", "KEEP_COMMON") CalcField("RoadAlias","RoadAlias.KDOT_CODE","!KDOT_RoutePre.PreCode!","PYTHON_9.3","#") removeJoin("RoadAlias") def HighwayCalc(lyr, gdb, Alias): """Pull out State Highways to preserve KDOT LRS Key (CANSYS FORMAT - non directional CRAD)""" if Exists(gdb+"\RoadAlias_Sort"): Delete(gdb+"\RoadAlias_Sort") else: pass Sort_management(Alias,gdb+"\RoadAlias_Sort","KDOT_CODE ASCENDING;KDOT_ROUTENAME ASCENDING","UR") #Heiarchy did not sort or calc correctly for Cheyenne County, US36 over K161 1st #Sot and join doesnt accomplish primary route key designsation... but calculaing over hte heirarchy should... #Remember to check the primary route heirarchy calculates correctly where US rides US and I rides I Heriarchy = ["K", "U", "I"] for routeClass in Heriarchy: rideselect = "KDOT_PREFIX LIKE '"+routeClass+"%'" print rideselect, routeClass TableView(gdb+"\RoadAlias_Sort", "RoadAlias_Sort", rideselect) JoinTbl(lyr,"SEGID","RoadAlias_Sort", "SEGID", "KEEP_COMMON") CalcField(lyr,lyr+".KDOTPreType","!RoadAlias_Sort.KDOT_PREFIX!","PYTHON_9.3","#") CalcField(lyr,lyr+".Soundex","!RoadAlias_Sort.KDOT_PREFIX!+!RoadAlias_Sort.KDOT_ROUTENAME!","PYTHON_9.3","#") CalcField(lyr,"KDOT_ADMO","'S'","PYTHON_9.3","#") CalcField(lyr,"PreCode","0","PYTHON_9.3","#") removeJoin(lyr) CalcField(lyr, "RID", "str(!KDOT_COUNTY_R!)+str(!KDOT_COUNTY_L!)+str(!KDOT_CITY_R!)+str(!KDOT_CITY_L!)+str(!PreCode!) + !Soundex! + str(!SuffCode!)+str(!UniqueNo!)+str(!TDirCode!)","PYTHON_9.3","#") CalcField(lyr, "LRSKEY", "str(!RID!)", "PYTHON_9.3","#") def ScratchCalcs(): CalcField("RoadCenterline","RoadCenterline.Soundex","""!RoadAlias.A_RD![0] + !RoadAlias.A_RD![1:].replace("S","").zfill(3)""","PYTHON_9.3","#") CalcField(in_table="RoadCenterline",field="RoadCenterline.KDOTPreType",expression="!RoadAlias.A_RD![0] ",expression_type="PYTHON_9.3",code_block="#") CalcField(in_table="RoadCenterline",field="RoadCenterline.PreCode",expression="'0'",expression_type="PYTHON_9.3",code_block="#") CalcField(in_table="RoadCenterline",field="RoadCenterline.KDOT_ADMO",expression="'S'",expression_type="PYTHON_9.3",code_block="#") def LRS_Tester(): """makes the LRS route layer and dissolves the NG911 fields to LRS event tables""" # Replace a layer/table view name with a path to a dataset (which can be a layer file) or create the layer/table view within the script # The following inputs are layers or table views: "RoadCenterline" from arcpy import Dissolve_management as dissolve CalcField(lyr, "LRSKEY", "str(!KDOT_COUNTY_R!)+str(!KDOT_COUNTY_L!)+str(!KDOT_CITY_R!)+str(!KDOT_CITY_L!)+str(!PreCode!) + !Soundex! + str(!SuffCode!)+str(!UniqueNo!)+str(!TDirCode!)","PYTHON_9.3","#") CalcField(lyr, "RID", "str(!KDOT_COUNTY_R!)+str(!KDOT_COUNTY_L!)+str(!KDOT_CITY_R!)+str(!KDOT_CITY_L!)+str(!PreCode!) + !Soundex! + str(!SuffCode!)+str(!UniqueNo!)+str(!TDirCode!)","PYTHON_9.3","#") env.overwriteOutput = 1 dissolve(lyr,gdb+"/NG911/RCLD1","LRSKEY","SEGID COUNT;L_F_ADD MIN;L_T_ADD MAX;L_F_ADD RANGE;L_T_ADD RANGE;SHAPE_MILES SUM","MULTI_PART","DISSOLVE_LINES") dissolve(lyr,gdb+"/NG911/RCLD2","LRSKEY","SEGID COUNT;L_F_ADD MIN;L_T_ADD MAX;L_F_ADD RANGE;L_T_ADD RANGE;SHAPE_MILES SUM","MULTI_PART","UNSPLIT_LINES") #MakeRouteLayer_na() pass uniqueIdInFields = ["OBJECTID", "COUNTY_L", "COUNTY_R", "STATE_L", "STATE_R", "L_F_ADD", "L_T_ADD", "R_F_ADD", "R_T_ADD", "UniqueNo", "LRSKEY", "SHAPE_MILES"] uniqueIdOutFields = ["OBJECTID", "UniqueNo", "LRSKEY"] def createUniqueIdentifier(gdb, lyr, inFieldNamesList, outFieldNamesList): '''filters through records and calculates an incremental Unique Identifier for routes that are not border routes, to handle Y's, eyebrows, and splits that would cause complex routes''' workspaceLocation = gdb #MakeFeatureLayer_management(lyr,"RCL_Particles",where_clause="COUNTY_L = COUNTY_R AND STATE_L = STATE_R AND ( L_F_ADD =0 OR L_T_ADD =0 OR R_F_ADD =0 OR R_T_ADD =0)") featureClassName = lyr from arcpy.da import SearchCursor as daSearchCursor, UpdateCursor as daUpdateCursor, Editor as daEditor alphabetListForConversion = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"] newCursor = daSearchCursor(featureClassName, inFieldNamesList) searchList = list() for searchRow in newCursor: searchList.append(list(searchRow)) # Transforms the row tuple into a list so it can be edited. if "newCursor" in locals(): del newCursor else: pass matchCount = 0 matchList = list() for testRow in searchList: if (testRow[1] == testRow[2] and testRow[3] == testRow[4] and (str(testRow[5]) == "0" or str(testRow[6]) == "0" or str(testRow[7]) == "0" or str(testRow[8]) == "0")): matchCount += 1 matchList.append(testRow) matchedRowDictionary = dict() for matchedRow in matchList: matchedRowContainer = list() # If the key already exists, assign the previous list of lists # to the list container, then append the new list # before updating the new value to that key in the dictionary. if matchedRow[10] in matchedRowDictionary: matchedRowContainer = matchedRowDictionary[matchedRow[10]] matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer # Otherwise, the key needs to be created # with the value, the list container, having only # one list contained within it for now. else: matchedRowContainer.append(matchedRow) matchedRowDictionary[matchedRow[10]] = matchedRowContainer for LRSKey in matchedRowDictionary: outRowContainer = matchedRowDictionary[LRSKey] # Sort based on length outRowContainer = sorted(outRowContainer, key = lambda sortingRow: sortingRow[11]) countVariable = 0 # Start at 0 for unique values LRSVariable = "" for outRowIndex, outRow in enumerate(outRowContainer): # Is this the first list/row in the key's list container? # If so, then set the Resolution_Order to 0 if outRowIndex == 0: outRow[9] = 0 else: countVariable += 1 if countVariable in [1, 2, 3, 4, 5, 6, 7, 8, 9]: outRow[9] = countVariable elif countVariable >= 10 and countVariable <= 34: outRow[9] = alphabetListForConversion[countVariable - 10] # Converts countVariable to an alpha character, without the letter "O". else: print "The count Variable is above 34. Ran out of numbers and letters to use as unique values." LRSVariable = outRow[10] LRSVariableShortened = str(LRSVariable[:-1]) # Returns the LRSVariable without the last character. LRSVariable = LRSVariableShortened + str(outRow[9]) outRow[10] = LRSVariable outRowString = "" for outRowElement in outRow: outRowString = outRowString + str(outRowElement) + " " print outRowString outRowContainer[outRowIndex] = outRow matchedRowDictionary[LRSKey] = outRowContainer newEditingSession = daEditor(workspaceLocation) newEditingSession.startEditing() newEditingSession.startOperation() newCursor = daUpdateCursor(featureClassName, outFieldNamesList) # @UndefinedVariable for existingRow in newCursor: formattedOutRow = list() if existingRow[2] in matchedRowDictionary.keys(): outRowContainer = matchedRowDictionary[existingRow[2]] for outRow in outRowContainer: if existingRow[0] == outRow[0]: # Test for matching OBJECTID fields. formattedOutRow.append(outRow[0]) formattedOutRow.append(outRow[9]) formattedOutRow.append(outRow[10]) newCursor.updateRow(formattedOutRow) else: pass else: pass newEditingSession.stopOperation() newEditingSession.stopEditing(True) if "newCursor" in locals(): del newCursor else: pass #ConflateKDOT(gdb, DOTRoads) #addAdminFields(lyr, Alias) #CalcAdminFields(lyr, Kdotdbfp) #CountyCode(lyr) #CityCodes(lyr, Kdotdbfp) RouteCalc(lyr, soundexNameExclusions) #AliasCalc(Alias, DOTRoads) #HighwayCalc(lyr, gdb, Alias) #StreetNetworkCheck(gdb) #createUniqueIdentifier(gdb, lyr, uniqueIdInFields, uniqueIdOutFields) #LRS_Tester()