This script utilizes the following libraries:
KML_DIR: Location output files will be written to
CV_FILE: Location of part 1 BSM synthetic trajectories file
%matplotlib inline
# Standard
from datetime import datetime, timedelta
import math
# External
import matplotlib.pyplot as plt
import matplotlib.patheffects as PathEffects
import pandas as pd
from bits.time import unix2str
# Constants
KML_DIR = 'examples/'
CV_FILE = 'VehicleCountByTime.csv'
RSU_FILE = 'VehicleCountInRSURange.csv'
The following variables are used to assist in creating the charts:
xs_time : stores cumulative timesetps, to be used as x-axis valuesy_all : stores cumulative total number of vehivles at a given time, to be used as y-axis valuesy_rsu_all : stores cumulative total number of vehicles in rsu coverage at a given time, to be used as y-axis valuesy_not_rsu_all : stores cumulative total number of out of rsu coverage at a given time, to be used as y-axis valueskml_body : stores kml content# global variables for the x-axis and y-axis of matplotlib charts
xs_time = []
y_all = []
y_rsu_all = []
y_not_rsu_all = []
# global variable for kml body
kml_body = ""
plots : number of plots to be drawngx_time : number of timestamps in current time window, to be used as x-axisy_current : average number of vehicles in current time window, to be used as y-axismin_local : minimum time value of current time windowmax_local : maximum time value of current time windowabs_min : earliest time value in entire datasetmin_label : earliest time value in current time windowmax_label : latest time value in current time windowabs_min_label : different format of abs_minpng_filename : name of png filekml_min : minimum time of current time window in kml formatkml_max : maximum time of current time window in kml formaty_current_in_rsu : average number of vehicles within rsu coverage in current time window, to be used as x-axisy_current_not_in_rsu : average number of vehicles not within rsu coverage in current time window, to be used as x-axis***Note the kml and png files must be in the same directory
def writer(plots, gx_time, y_current, min_local, max_local, abs_min, min_label, max_label, abs_min_label,
png_filename, kml_min, kml_max, y_current_in_rsu=None, y_current_not_in_rsu=None):
global xs_time, y_all, y_rsu_all, y_not_rsu_all, kml_body
# Create pyplot figure
plt.rcParams['axes.labelweight'] = 'bold' # make all axes text bold
if plots == 4:
fig1, (ax1, ax2, ax3, ax4) = plt.subplots(nrows=1, ncols=4, sharex=False, sharey=False)
else:
fig1, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, sharex=False, sharey=False)
fig1.set_figwidth(15)
fig1.set_figheight(3)
# Round up to nearest 10
y_max = max(y_all)
y_max = int(math.ceil(y_max / 10.0)) * 10
# Plots are created in the code below
# Total number of vehicles in 10 min. window
ax1.plot(gx_time, y_current, linewidth=3.5, color='green', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()])
ax1.axis([min_local, max_local, 0, y_max])
ax1.set_ylabel("Total Active Vehicles", color='black', fontsize=14)
ax1.set_xlabel(min_label + " to " + max_label, color='black', fontsize=14)
ax1.spines['bottom'].set_color('black')
ax1.spines['bottom'].set_linewidth(3)
ax1.spines['top'].set_color('black')
ax1.spines['top'].set_linewidth(3)
ax1.spines['left'].set_color('black')
ax1.spines['left'].set_linewidth(3)
ax1.spines['right'].set_color('black')
ax1.spines['right'].set_linewidth(3)
ax1.tick_params(axis='x', colors='black')
ax1.tick_params(axis='y', colors='black', labelsize=14)
ax1.set_facecolor('white')
# Cumulative total number of vehicles
ax2.plot(xs_time, y_all, linewidth=2.5, color='green', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()])
ax2.axis([abs_min, max_local, 0, y_max])
ax2.set_ylabel("Total Vehicles", color='black', fontsize=14)
ax2.set_xlabel(abs_min_label + " to " + max_label, color='black', fontsize=14)
ax2.spines['bottom'].set_color('black')
ax2.spines['bottom'].set_linewidth(3)
ax2.spines['top'].set_color('black')
ax2.spines['top'].set_linewidth(3)
ax2.spines['left'].set_color('black')
ax2.spines['left'].set_linewidth(3)
ax2.spines['right'].set_color('black')
ax2.spines['right'].set_linewidth(3)
ax2.tick_params(axis='x', colors='black')
ax2.tick_params(axis='y', colors='black', labelsize=14)
ax2.set_facecolor('white')
# hide all the x labels
ax1.set_xticklabels([])
ax2.set_xticklabels([])
if plots == 4:
# Number of vehicles in coverage of rsu in 10 min. window
ax3.plot(gx_time, y_current_in_rsu, linewidth=2.5, color='blue', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()], label='In Coverage')
ax3.plot(gx_time, y_current_not_in_rsu, linewidth=2.5, color='red', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()], label='Out of Coverage')
ax3.axis([min_local, max_local, 0, y_max])
ax3.set_ylabel("Active RSU Coverage", color='black', fontsize=14)
ax3.set_xlabel(abs_min_label + " to " + max_label, color='black', fontsize=14)
ax3.spines['bottom'].set_color('black')
ax3.spines['bottom'].set_linewidth(3)
ax3.spines['top'].set_color('black')
ax3.spines['top'].set_linewidth(3)
ax3.spines['left'].set_color('black')
ax3.spines['left'].set_linewidth(3)
ax3.spines['right'].set_color('black')
ax3.spines['right'].set_linewidth(3)
ax3.tick_params(axis='x', colors='black')
ax3.tick_params(axis='y', colors='black', labelsize=14)
ax3.set_facecolor('white')
ax3.legend(loc='upper left')
# Cumulative number of vehicles in coverage of rsu
ax4.plot(xs_time, y_rsu_all, linewidth=2.5, color='blue', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()], label='In Coverage')
ax4.plot(xs_time, y_not_rsu_all, linewidth=2.5, color='red', path_effects=[PathEffects.SimpleLineShadow(foreground='w'), PathEffects.Normal()], label='Out of Coverage')
ax4.axis([abs_min, max_local, 0, y_max])
ax4.set_ylabel("Total RSU Coverage", color='black', fontsize=14)
ax4.set_xlabel(abs_min_label + " to " + max_label, color='black', fontsize=14)
ax4.spines['bottom'].set_color('black')
ax4.spines['bottom'].set_linewidth(3)
ax4.spines['top'].set_color('black')
ax4.spines['top'].set_linewidth(3)
ax4.spines['left'].set_color('black')
ax4.spines['left'].set_linewidth(3)
ax4.spines['right'].set_color('black')
ax4.spines['right'].set_linewidth(3)
ax4.tick_params(axis='x', colors='black')
ax4.tick_params(axis='y', colors='black', labelsize=14)
ax4.set_facecolor('white')
ax4.legend(loc='upper left')
# hide all the x labels
ax3.set_xticklabels([])
ax4.set_xticklabels([])
# use tight_layout() to avoid overlapping text
fig1.tight_layout()
# save figure
fig1.savefig(KML_DIR + png_filename + '.png', bbox_inches='tight', dpi=65, transparent=False)
# clear & close everything to avoid memory leaks
plt.close()
fig1.clf()
plt.clf()
kml_body += '''<ScreenOverlay>
<name>HUD Histogram</name>
<TimeSpan>
<begin>''' + kml_min + '''</begin>
<end>''' + kml_max + '''</end>
</TimeSpan>
<Icon> <href>''' + png_filename + '''.png</href>
</Icon>
<overlayXY x="0.05" y="0.05" xunits="fraction" yunits="fraction"/>
<screenXY x="0.05" y="0.05" xunits="fraction" yunits="fraction"/>
<rotationXY x="0.5" y="0.5" xunits="fraction" yunits="fraction"/>
<size x="0" y="0" xunits="fraction" yunits="fraction"/>
</ScreenOverlay>
'''
df_hour : dataframe of current time windowmin_time : minimum time of dataframemax_time : maximum time of dataframabs_min_time : minimum time of entire datasetpng_filename : png file naming conventionrsu_df_hour : dataframe of current time window for rsu trajectory datadef plotter(df_hour, min_time, max_time, abs_min_time, png_filename, rsu_df_hour=None):
"""
Draws charts as png files and creates a kml file to display them
"""
# reference variables for x and y axes for total tracks graph and kml body
global xs_time, y_all, y_rsu_all, y_not_rsu_all, kml_body
# variables for x and y axes for total active tracks graph
gx_time = []
y_current = []
y_current_in_rsu = []
y_current_not_in_rsu = []
# groups min and max time
max_group_time = max_time
current_min_time = min_time
current_max_time = current_min_time + 600 # Further divide by every minute for points
# x axis range values. Converted from utc tick to datetime format
min_local = unix2str(min_time/10)
max_local = unix2str(max_time/10)
# x axis min for total tracks graph
abs_min = unix2str(abs_min_time/10)
# x axis labels. Converted from tick to est datetime format
abs_min_label = str(datetime.strptime(unix2str(abs_min_time/10, format='%H:%M:%S'), '%H:%M:%S') - timedelta(hours=5))
abs_min_label = abs_min_label.split()[1]
min_label = str(datetime.strptime(unix2str(min_time/10, format='%H:%M:%S'), '%H:%M:%S') - timedelta(hours=5))
min_label = min_label.split()[1]
max_label = str(datetime.strptime(unix2str(max_time/10, format='%H:%M:%S'), '%H:%M:%S') - timedelta(hours=5))
max_label = max_label.split()[1]
# kml begin and end times. Dictates when the graphs are displayed. KML time format
kml_min = unix2str(min_time/10, format='%Y-%m-%dT%H:%M:%SZ')
kml_max = str(datetime.strptime(unix2str(max_time/10, format='%Y-%m-%dT%H:%M:%SZ'), '%Y-%m-%dT%H:%M:%SZ') - timedelta(minutes=1))
kml_max = kml_max.split()[0] + 'T' + kml_max.split()[1] + 'Z'
# Plot point every minute
while current_min_time <= max_group_time:
df_minute = df_hour[(df_hour['tick'] >= current_min_time) & (df_hour['tick'] <= current_max_time)]
# Compute point values
num_total_current = df_minute['Number of Vehicles'].mean()
# Replace nan instances with 0
if math.isnan(num_total_current):
num_total_current = 0
if rsu_df_hour is not None:
rsu_df_minute = rsu_df_hour[(rsu_df_hour['tick'] >= current_min_time) & (rsu_df_hour['tick'] <= current_max_time)]
num_total_rsu_current = rsu_df_minute['True'].mean()
num_total_not_in_rsu_current = rsu_df_minute['False'].mean()
if math.isnan(num_total_rsu_current):
num_total_current = 0
if math.isnan(num_total_not_in_rsu_current):
num_total_current = 0
# Append data onto the x & y axes to be displayed
gx_time.append(unix2str(current_min_time/10)) # Used for active total
xs_time.append(unix2str(current_min_time/10)) # Used for total
y_current.append(num_total_current) # Used for active total
y_all.append(num_total_current) # Used for total
if rsu_df_hour is not None:
y_current_in_rsu.append(num_total_rsu_current) # Used for active number of vehicles in RSU
y_current_not_in_rsu.append(num_total_not_in_rsu_current) # Used for active number of vehicles out of RSU
y_rsu_all.append(num_total_rsu_current) # Used for total number of vehicles in RSU
y_not_rsu_all.append(num_total_not_in_rsu_current) # Used for total number of vehicles out of RSU
current_min_time = current_max_time
current_max_time += 600
if rsu_df_hour is not None:
plots = 4
writer(plots, gx_time, y_current, min_local, max_local, abs_min, min_label, max_label, abs_min_label,
png_filename, kml_min, kml_max, y_current_in_rsu, y_current_not_in_rsu)
else:
plots = 2
writer(plots, gx_time, y_current, min_local, max_local, abs_min, min_label, max_label, abs_min_label,
png_filename, kml_min, kml_max)
def kml_writer(rsu=False):
global kml_body
# Create kml file
kml_file = KML_DIR + 'HUD.kml'
with open(kml_file, "w") as f:
# Add header to file
kmlhead = '<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2"' + \
' xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">\n' + '<Document>\n'
# Load data
dfFromRecords = pd.read_csv(CV_FILE) # Number of vehicles at a given time
if rsu:
dfFromRSU = pd.read_csv(RSU_FILE) # Number of vehicles in and out of range of RSU at given time
counter = 0
# Rename time column
dfFromRecords.rename(columns={'time_received': 'tick'}, inplace=True)
if rsu:
dfFromRSU.rename(columns={'time_received': 'tick'}, inplace=True)
# Every tick is by decisecond
min_time = dfFromRecords['tick'].min()
max_time = dfFromRecords['tick'].max()
current_min_time = min_time
current_max_time = current_min_time + 6000 # Every 10 minutes
# Divide dataframe every 10 minutes and draw a new graph
while current_min_time < max_time:
df = dfFromRecords[(dfFromRecords['tick'] >= current_min_time) & (dfFromRecords['tick'] <= current_max_time)]
if rsu:
rsu_df = dfFromRSU[(dfFromRSU['tick'] >= current_min_time) & (dfFromRSU['tick'] <= current_max_time)]
counter += 1
png_filename = "HUD" + str(counter)
if rsu:
plotter(df, current_min_time, current_max_time, min_time, png_filename, rsu_df) # draw the HUD
else:
plotter(df, current_min_time, current_max_time, min_time, png_filename) # draw the HUD
current_min_time = current_max_time
current_max_time += 6000
kml = kmlhead + kml_body + '</Document>\n </kml>'
f.write(kml)
kml_writer(rsu=True)