## Introduction

I wrote the Python program Intersection_Viewer.py during a group project. The program generates a graphical representation of any road intersection from the data being used for the group project. The smallest angle between the roads at the intersection is identified and labeled. The graphics produced could be used in a report and Intersection_Viewer.py is faster than trying to produce similar results in ArcMap.

There is a text field in the lower left corner of the canvas that allows the identification number of an intersection to be entered for easy retrieval. The program retrieves the coordinates for the center of the intersection and coordinates along each road of the intersection from a table. The bearing that each road leaves the intersection is calculated and then used to plot a line for each road on a polar chart using matplotlib. Labels with the road identification number are placed on top of each road lines.

## Intersection_Viewer.py

```import arcpy
import math
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
import numpy as np
import operator

arcpy.env.workspace = project_db

data_table = "Intersection_XY"
fields = ['RID', 'Center_X', 'Center_Y',

# Calculate the bearing in degrees of a vector that goes through xy2 and has
# the origin xy1 .
def get_bearing(xy1, xy2):
x1, y1 = xy1
x2, y2 = xy2
dx = x2 - x1
dy = y2 - y1
degrees = 360 - (radian * 360) / (2 * math.pi)
else:
degrees = 360 - ((2 * math.pi + radian) * 360) / (2 * math.pi)
return degrees

# Returns a list of tuples (road id number, bearing direction) that is sorted
# by bearing degree (least to greatest).
where_clause = "IntersID = {}".format(intersection_id)
with arcpy.da.SearchCursor(data_table, fields, where_clause) as cursor:
for row in cursor:
rid, center_x, center_y, bearing_x, bearing_y = row
bearing = get_bearing((center_x, center_y), (bearing_x, bearing_y))

fig, ax = plt.subplots(subplot_kw=dict(polar=True))

# Returns the degrees to rotate the text labels so that they remain
def label_rotation(bearing):
if 0 <= bearing <= 180:
return 90 - bearing
else:
return 270 - bearing

# The function draw_chart clears the last chart and recreates it with a given
# intersection id (text).
def draw_chart(text):
ax.cla()
intersection = int(text)

# If road_bearings is [] then the intersection id is likely wrong.
print("There is not an intersection with that ID")
road_bearings = [("- SUCH -", 45), ("- EXISTS -", 135),
("- INTERSECTION -", 225), ("- NO -", 315)]
title = "*Unknown Intersection ID*"
else:
title = "Intersection {}".format(intersection)

# Add the title to the matplotlib window. Adjust the chart so it is
# oriented and labeled like a compass.
fig.canvas.set_window_title(title)
ax.set(
theta_zero_location='N',
theta_direction=-1,
xticklabels=['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'],
yticklabels=[],
yticks=[]
)
ax.spines['polar'].set_visible(False)

# The list road_bearings is sorted from least to greatest according to
# bearing. This sorting assures there exists no other roads between two
# adjacent elments of the road_bearings list. To find the angle between
# adjacent roads ir is only a matter of finding the difference between
# to_end, Calculates the angle between the last and first elements
angle_list = np.ediff1d(bearing_list,
to_end=360 - (bearing_list[-1] - bearing_list))

plot_item = [(r, b, a) for r, b, a
in zip(rid_list, bearing_list, angle_list)]
plot_item.sort(key=operator.itemgetter(2))

# The smallest_angle list contains the index integer for the road(s) in
# plot_item that has/have the smallest angle between the road next to it.
smallest_angle = []
# The first road will always be one of the smallest since the list is
# sorted. Two or more roads are considered the smallest if they're are all
# under .1 degree of each other.
for index, x in enumerate(plot_item):
if index == 0:
smallest = x
smallest_angle.append(index)
elif np.absolute(smallest - x) < 0.1:
smallest_angle.append(index)

# Go through each elment of plot_item so that the roads, labels and smallest
# angles are placed on the axes.
for index, x in enumerate(plot_item):
# The length of the road lines are randomly chosen within a range.
# This was done to make the finished chart visually feel more natural.
length = np.random.uniform(8, 10)

ax.plot((0, theta), (0, length), color='blue')

if index in smallest_angle:
# The smallest angle bar has to be placed using an angle between
# the two roads. It is then given the width of the angle between
facecolor='white',
edgecolor='black')
# Add the smallest angle label.
ax.annotate(str(round(x,2)) + "°",
(bisect_theta, 8.5),
rotation=label_rotation(np.degrees(bisect_theta)),
ha='center',
va='center',
family='serif',
fontsize='small',
backgroundcolor='white'
)
ax.annotate(str(x),(theta, length/2),
rotation=label_rotation(x),
ha='center',
va='center',
family='serif',
fontsize='small',
backgroundcolor='white'
)

draw_chart(29453)
axbox = plt.axes([0.2, 0, 0.2, 0.06])
axbox.axis('off')
text_box = TextBox(axbox, 'ID Input: ', initial='29453')
text_box.on_submit(draw_chart)
fig.show()
```