There is a function in the math module that is much simpler to use:
deg = math.degrees(math.atan2(dy,dx))
Note the 2 on the end on tan that improves the original range from 0 - 90 degrees. This extended function will provide a 0 - 360 degree bearing for a slope (in radians). There are another pair of functions to convert between degrees and radians math.degrees() and math.radians(). No need to do that yourself.
You can then just subtract the full bearing of each line to get all the angles between two lines. The dy and dx can be negative values.
I can see that you might need to sort the end vertices to find out the direction of the lines. You do not explain why you need a particular angle. Is this because it is related to the existing line direction or do you always want the South facing angle?
All this is too fiddly to do in the expression calculator. The time has come to learn a bit of Python to put all the steps in a script and update the fields using a cursor.
#-------------------------------------------------------------------------------
# Name: intersection_angle
# Purpose: calculate intersection angles
# should not be so hard!
# Author: kimo
#
# Created: 03/08/2016
# Copyright: (c) kimo 2016
# Licence: Creative Commons 3.0 New Zealand
#-------------------------------------------------------------------------------
# method
# input
# railway line, road lines
# output crossing points with angles, segments with bearings
# should select only those intersecting to remove redundant lines
# 1 intersect railways and roads to get intersection points
# 2 buffer intersection points, retain intersection id
# 3 intersect buffers with original rail and roads, not clip to tag id
#
# 4 for each line
# use geometry object to find ends and hence bearing
# write out to two dictionaries
# 5 use dictionaries to calculate angles and store back in points
import math
import arcpy
arcpy.env.workspace = "j:/temp/sample.gdb"
arcpy.env.overwriteOutput = True
# simplified set that have intersecting lines, no junctions at crossings
# single part and generally error free
inFeatures = ["road", "rail"]
# Get intersections as points and add extra fields for output
arcpy.analysis.Intersect(inFeatures, "crossing1", "", 0.01, "point")
print arcpy.GetMessages()
# select only road/rail intersections not road/road or rail/rail
# but ESRI don't 'do' node topology any more
# start by deleting duplicate points
arcpy.DeleteIdentical_management(in_dataset="crossing1", fields="Shape", xy_tolerance="", z_tolerance="0")
# just select intersection points on railway lines, handle railway junctions later
arcpy.management.MakeFeatureLayer('crossing1','cross_lay')
arcpy.management.SelectLayerByLocation('cross_lay','INTERSECT','rail',1)
arcpy.management.CopyFeatures('cross_lay','crossing2')
arcpy.management.AddField("crossing2","id",'INTEGER')
arcpy.management.AddField("crossing2","angle1",'FLOAT')
arcpy.management.AddField("crossing2","angle2",'FLOAT')
arcpy.management.DeleteField("crossing2",['type'])
# Add a serial ID, use a cursor not field calculator if we are in python anyway
# easier to debug, trap errors, understand, just as fast
id = 1
with arcpy.da.UpdateCursor("crossing2",['id']) as cur:
for row in cur:
row[0] = id
cur.updateRow(row)
id+=1
# Buffer crossings by 10 meters to get local angles
arcpy.analysis.Buffer("crossing2", "crossing10", "10 meters")
# Intersect original merge (not clip because ID attributes lost)
arcpy.analysis.Intersect(["road", "crossing10"], "roadrail_clip1")
print arcpy.GetMessages()
arcpy.analysis.Intersect(["rail", "crossing10"], "roadrail_clip2")
print arcpy.GetMessages()
arcpy.management.Merge(["roadrail_clip1","roadrail_clip2"],"roadrail_clip")
# each road, rail line will inherit crossing id and a type tag
# calculate bearings from ends at buffer intersections
arcpy.management.AddField("roadrail_clip","bearing",'FLOAT')
dCross_road = {}
dCross_rail = {}
with arcpy.da.UpdateCursor("roadrail_clip",['id','bearing','SHAPE@','type']) as cur:
for row in cur:
dx = row[2].lastPoint.X - row[2].firstPoint.X
dy = row[2].lastPoint.Y - row[2].firstPoint.Y
bearing = math.degrees(math.atan2(dy,dx))
# leave alone with original sense
row[1] = bearing
cur.updateRow(row)
# to compare bearings by crossing ID
# need to collect bearings by ID and type
if row[3] == 'road':
dCross_road[row[0]] = bearing
elif row[3] == 'rail':
dCross_rail[row[0]] = bearing
else:
pass
# no interest
# angles can be calculated from the two dictionaries
# if both entries exist
i = 0
dAngle = {}
for id in dCross_rail:
if dCross_rail.get(id,None) and dCross_road.get(id,None):
b_rail = dCross_rail[id]
b_road = dCross_road[id]
# to decide which angle is required (anticlockwise from the road)
# but still have to allow for two parts of the rail line
if b_rail
