Published on

How to Batch Calculate Angles of Lines in ArcGIS Pro/ArcMap

This article teaches you how to batch calculate angles for line features in ArcGIS Pro or ArcMap.

Table of Contents

Overview

The following instructions will help you calculate angles (azimuths) for input lines from their start to end points. Multiple types of angle calculations are provided so you can choose the one that suits you.

Make sure your data is using a projected coordinate system before you start, otherwise the calculations will be inaccurate.

The overall workflow:

  1. Add a field to the lines attribute table for the angle values. You can find the appropriate Data Type (Field Type in ArcMap) for the new field at your chosen angle type section below.

ArcGIS Pro (make sure to use an appropriate data type):

Adding a field in ArcGIS Pro

ArcMap (make sure to use an appropriate field type):

Adding a field in ArcMap
  1. Open the Calculate Field tool by right-clicking the new field's name or by searching for the tool by name and selecting your Input Table and the new field's Field Name.
  2. Select Python 3 as the Expression Type (Python_9.3 in ArcMap).
  3. Enter the following code as your Expression (the input with your field's name and the 'equals' sign):
calculate_angle([!Shape.firstpoint.X!, !Shape.lastpoint.X!], [!Shape.firstpoint.Y!, !Shape.lastpoint.Y!])
  1. Copy the code from your chosen angle type section and paste it to the Code Block input. If you are using Field Calculator in ArcMap, select Python as the parser type and Show Codeblock first. Your tool window should look something like this:

ArcGIS Pro:

Calculate Field in ArcGIS Pro

ArcMap:

Calculate Field in ArcMap
Field Calculator in ArcMap
  1. When you click Run (OK in ArcMap), the field should get populated with the angle values.

Geographic Angles

Geographic Angles
Data type: Float (or Double for more precision)
def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle <= 90:
    return 90 - angle
  return 360 + (90 - angle)

Geographic Angles (Counterclockwise)

Geographic Angles (Counterclockwise)
Data type: Float (or Double for more precision)
def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle <= 90:
    return 270 + angle
  return angle - 90

Arithmetic Angles

Arithmetic Angles
Data type: Float (or Double for more precision)
def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle >= 0:
    return angle
  return 360 + angle

Arithmetic Angles (-180° to 180°)

Arithmetic Angles (-180° to 180°)
Data type: Float (or Double for more precision)
def calculate_angle(x, y):
  return math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))

Quadrantal Bearings

Quadrantal Bearings
Data type: Text

Note: If you don't want the bearings to be rounded, remove the round methods from the code block.

def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle <= 90:
    angle = round(90 - angle)
  else:
    angle = round(360 + (90 - angle))

  if angle >= 0 and angle < 90:
    return "N " + str(angle) + "° E"
  elif angle >= 90 and angle < 180:
    return "S " + str(180 - angle) + "° E"
  elif angle >= 180 and angle < 270:
    return "S " + str(angle - 180) + "° W"
  return "N " + str(360 - angle) + "° W"

Milliradians (Mils)

Milliradians (Mils)
Data type: Short
def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle <= 90:
    angle = 90 - angle
  else:
    angle = 360 + (90 - angle)

  mil = round(angle / 360 * 6400)
  if mil == 6400:
    return 0
  return mil

Milliradians (Mils) As Text

Milliradians (Mils) As Text
Data type: Text (with the length of five characters)
def calculate_angle(x, y):
  angle = math.degrees(math.atan2((y[1] - y[0]), (x[1] - x[0])))
  if angle <= 90:
    angle = 90 - angle
  else:
    angle = 360 + (90 - angle)

  mil = str(int(round(angle / 360 * 6400) + 0.5))
  if mil == "6400":
    mil = "0"

  mil_text = (4 - len(mil)) * "0" + mil
  return mil_text[0:2] + "-" + mil_text[2:]