Skip to content

Commit cff7219

Browse files
committed
Initial commit of GPS/control visualization
* Uses Folium to plot trajectory on a map * Output is a .html file with interactive information on trajectory
1 parent 518856f commit cff7219

5 files changed

+15462
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#! /usr/bin/env python
2+
3+
##########################################################################################
4+
# GPS_Visualization_Folium.py
5+
#
6+
# Script to read control decision data collected using during single waypoint trials
7+
# Adapted from a similar script used to process data from the Anaconda and Husky
8+
#
9+
# Uses Folium to generate maps of a GPS path
10+
# - https://github.com/python-visualization/folium
11+
# - Conda install - https://anaconda.org/ioos/folium
12+
#
13+
# NOTE: Plotting is set up for output, not viewing on screen.
14+
# So, it will likely be ugly on screen. The saved PDFs should look
15+
# better.
16+
#
17+
# Created: 06/11/14
18+
# - Joshua Vaughan
19+
20+
# - http://www.ucs.louisiana.edu/~jev9637
21+
#
22+
# Modified:
23+
# * 07/10/14 - Joshua Vaughan - [email protected]
24+
# - condensed batch processing and single run into this script, choose via boolean
25+
# - condensed "only IMU" data and "Control" data scripts into this one
26+
# - general code cleanup
27+
# * 09/12/15 - JEV - [email protected]
28+
# - conversion to Python 3
29+
# - begin conversion away from Anaconda data
30+
#
31+
##########################################################################################
32+
33+
import numpy as np
34+
35+
import folium
36+
import glob
37+
import tkinter as tk
38+
from tkinter.filedialog import askopenfilename, askdirectory
39+
40+
import geographic_calculations as geoCalc
41+
42+
43+
PRODUCE_FOLIUMMAP = True # Produce a Folium-based map?
44+
DRAW_WAYPOINTS = False # Draw the waypoints?
45+
BATCH = False # Batch processing?
46+
47+
48+
def create_map(data_filename):
49+
''' Actually creates the map '''
50+
waypoints = None
51+
# TODO: be more efficient
52+
with open(data_filename, 'rb') as data_file:
53+
data = np.genfromtxt(data_file, delimiter=',', skip_header = 1, dtype = 'float')
54+
55+
if np.shape(data)[1] == 14: # _controlHistory... file
56+
data_ok = True
57+
time = data[:,0]
58+
imu_heading = data[:,1]
59+
latitude = data[:,2]
60+
longitude = data[:,3]
61+
gps_heading = data[:,4]
62+
gps_speed = data[:,5]
63+
waypoint_number = data[:,6]
64+
waypoint_latitude = data[:,7]
65+
waypoint_longitude = data[:,8]
66+
distance_to_waypoint = data[:,9]
67+
bearing_to_waypoint = data[:,10]
68+
course_correction = data[:,11]
69+
turn_direction = data[:,12]
70+
control_from_PID = data[:,13]
71+
72+
_, waypoint_indices = np.unique(waypoint_number, return_index = True)
73+
74+
waypoints = np.vstack((waypoint_latitude[waypoint_indices],
75+
waypoint_longitude[waypoint_indices]))
76+
77+
waypoints = waypoints.T
78+
79+
elif np.shape(data)[1] == 19: # _rawIMUGPS... file
80+
data_ok = True
81+
time = data[:,0]
82+
quart0 = data[:,1]
83+
quart1 = data[:,2]
84+
quart2 = data[:,3]
85+
quart3 = data[:,4]
86+
x_accel = data[:,5]
87+
y_accel = data[:,6]
88+
z_accel = data[:,7]
89+
x_mag = data[:,8]
90+
y_mag = data[:,9]
91+
z_mag = data[:,10]
92+
roll = data[:,11]
93+
pitch = data[:,12]
94+
yad = data[:,13]
95+
imu_heading = data[:,14]
96+
latitude = data[:,15]
97+
longitude = data[:,16]
98+
gps_heading = data[:,17]
99+
gps_speed = data[:,18]
100+
101+
waypoints = None
102+
103+
else:
104+
data_ok = False
105+
print('\nImproper data length in file {}.'.format(data_filename))
106+
print('Skippping it... \n\n')
107+
108+
if data_ok: # If we have meaningful data, make the map
109+
# Define the start, target, and midpoint locations
110+
start = np.array([latitude[0], longitude[0]])
111+
112+
if waypoints is not None:
113+
target = waypoints[-1,:] # last waypoint is the target location
114+
else:
115+
target = np.array([latitude[-1], longitude[-1]])
116+
117+
118+
midpoint = geoCalc.calculate_midpoint(start, target)
119+
120+
121+
if PRODUCE_FOLIUMMAP:
122+
''' Create a folium map'''
123+
# Set up base map, centered on the midpoint between start and finish
124+
mymap = folium.Map(location = [midpoint[0], midpoint[1]], zoom_start=16)
125+
126+
lat_shaped = latitude.reshape(len(latitude),1)
127+
long_shaped = longitude.reshape(len(latitude),1)
128+
129+
# Draw a green circle with popup information at the start location
130+
mymap.circle_marker(location = [start[0], start[1]], radius = 10,
131+
popup = 'Start -- Lat, Lon: {:4.4f}, {:4.4f}'.format(start[0], start[1]),
132+
line_color = '#00FF00',
133+
fill_color = '#00FF00')
134+
135+
# Draw a red circle with popup information at the target location
136+
mymap.circle_marker(location = [target[0], target[1]], radius = 10,
137+
popup = 'Target -- Lat, Lon: {:4.4f}, {:4.4f}'.format(target[0], target[1]),
138+
line_color = '#FF0000',
139+
fill_color = '#FF0000')
140+
141+
if DRAW_WAYPOINTS:
142+
for index, waypoint in enumerate(waypoints):
143+
if index < len(waypoints)-1:
144+
# Draw white circles with popup information at each waypoint
145+
mymap.circle_marker(location = [waypoint[0],waypoint[1]],
146+
radius = 8,
147+
popup='Waypoint Num: {:.0f} -- Lat, Lon: {:4.4f}, {:4.4f}'.format(index+1, waypoint[0], waypoint[1]),
148+
line_color = '#FFFFFF',
149+
fill_color = '#FFFFFF')
150+
151+
#----- Draw the trial on a map ---------------------------------------------------
152+
path = np.hstack((lat_shaped,long_shaped))
153+
154+
# if path is large, downsample for plotting, plot only ~1000 points
155+
if np.shape(path)[0] > 1000:
156+
path = path[0::np.shape(path)[0]//1000]
157+
158+
# Uncomment below to draw the path line in addition to the data point bubbles above
159+
# mymap.line(path, line_color='#FF0000', line_weight=5)
160+
161+
# for each point on the path, draw a circle that contains system information
162+
# in a popup when clicked on
163+
for index, current_pos in enumerate(path):
164+
mymap.circle_marker(location = [current_pos[0], current_pos[1]], radius = 1,
165+
popup = 'Time: {:3.2f} s -- Lat, Lon: {:4.4f}, {:4.4f} -- Speed: {:3.2f} m/s -- Actual Heading: {:3.0f} deg -- Desired Heading: {:3.0f} deg -- Distance to Waypoint: {:.0f} m'.format(time[index], latitude[index], longitude[index], gps_speed[index], imu_heading[index], bearing_to_waypoint[index], distance_to_waypoint[index]),
166+
line_color = '#0000FF', fill_color = '#0000FF')
167+
168+
169+
# define filename - assumes that original datafile was .csv
170+
# TODO: make this more robust
171+
map_filename = data_filename.replace('csv', 'html')
172+
mymap.create_map(map_filename)
173+
174+
175+
176+
if __name__ == "__main__":
177+
if BATCH:
178+
root = tk.Tk()
179+
root.withdraw()
180+
file_path = askdirectory()
181+
182+
filename_pattern = file_path + "/*_controlHistory.csv"
183+
184+
for data_filename in glob.glob(filename_pattern):
185+
print(data_filename)
186+
create_map(data_filename)
187+
188+
else:
189+
root = tk.Tk()
190+
root.withdraw()
191+
192+
data_filename = askopenfilename()
193+
create_map(data_filename)

0 commit comments

Comments
 (0)