Project 3: GPS Tracks

This project will definitely require a good understanding of everything we have learned this semester, especially structs, arrays, functions, sorting, header files, and linked lists.
Honor Policy Reminder:
The course policy and the instructions it references, most pertinently COMPSCIDEPTINST 1531.1D, spell out what kinds of assistance are permissible for programming projects. The instructor can give explicit permission for things that would otherwise not be allowed. For this project, you are allowed
  1. to get help from other current IC210 instructors as well as your own and from current IC210 MGSP leaders (any assistance must be documented though), and
  2. to use general purpose C++ resources online (though such use must be documented).
For this project, you are not allowed to do the following:
  1. Resources that might specifically address this project, say looking through code that implements GPS tracking, are not allowed.
  2. You are very specifically not allowed to look at previous semesters' IC210 or SI204 programming project or lab solutions that may have addressed similar issues.
Combining the instructions referenced in the course policy and the explicit permissions above, and what you get is summarized as:
Warning: Do not even vaguely talk about the contents of the project with your colleagues.

Grading

After the initial part on reading in a text file with the track data, which everyone has to do, the project is set up as a number of features, given below. 70% of your grade will be based on functionality (i.e., whether your program works as specified), mostly based on the test cases that you see when you submit, but perhaps also extra test cases according to the specifications of the project. The code you submit must work if you expect to earn a passing grade.

30% of your grade will be based on coding style, which includes:

Deadlines

Early bonus and late penalty

Submit

Package installation

You’ll need a few extra packages to make all the features possible, so start by running this command to install them from your Ubuntu terminal:
sudo apt install libxml2-dev libgtk-3-dev libcairo2-dev

Note, if that command doesn’t work, your package lists may be out of date. In that case, you should first update your with the following pair of commands. (This might take a few minutes, but it’s a good idea to update anyway.)

sudo apt update 
sudo apt full-upgrade

Starter files

Download p3files.tar into your project directory. Untar the file as follows:
tar -xvf p3files.tar
You will see a bunch of files.

Track files

Landmark files

Helper library files (DO NOT CHANGE THEM)

track.cpp

Makefile

Track file format

The plaintext track file run1.txt looks like the following:
760
38.923082 -76.560321 2017-04-02 12:00:35
38.923078 -76.560335 2017-04-02 12:00:36
38.923025 -76.560567 2017-04-02 12:00:44
38.922975 -76.560822 2017-04-02 12:00:51
...
Tracks are represented as a series of waypoints.

Dealing with GPS coordinates

The gps.h header file and gps.cpp function definitions file contain a struct gpsco and useful functions for storing and finding the distance between GPS coordinates.

Note:

Dealing with times

The timestamps for each waypoint are fully specified in the format YYYY-MM-DD HH:MM:SS.

In order to easily deal with these, you might want to make use of some things in the standard header library #include <ctime>. Look at Podcast Feeds Lab to remember how to use the tm struct, mktime, and difftime functions to calculate the difference between them in seconds.

Organizing and compiling your code

Your main function should go in a file called track.cpp. We also provided a Makefile so that you can compile your programs with all the correct libraries linked in by running:
make track

This will compile your track.cpp program along with gps.cpp. (Don't be intimidated by the very long g++ command that you see in the Makefile - that's why we gave you the Makefile!)

Add your own files

You are encouraged to add your own .h and .cpp files for other structs and groups of functions you write.

If you do that, you just need to add the names of your new .h and .c files to the Makefile at the end of HEADER_FILES and IMPLEMENTATION_FILES. For example, if you added mylib.h and mylib.cpp, your Makefile should look as follows:

HEADER_FILES = gps.h mylib.h
IMPLEMENTATION_FILES = gps.cpp mylib.cpp

and then when you run make track, it will always compile track.cpp along with any of the other .cpp files you have in that list, along with the many libraries needed, to produce an executable called track.

Note:

Program structure

The general program structure is described below.
  1. Your program will start by reading a track file.
  2. Your program should immediately open this file and read in the waypoints in that file, saving them into an array or linked list of structs. Note: See the note in the grading section about arrays versus linked lists. You can use arrays for a maximum of 4 features (85 points max) but you need to use a linked list if you want to implement more than 4 features (up to 100 points). Make your decision now, so you don't have to go back later and modify code for features you already completed.
  3. Each struct should contain the gps coordinates and the time of that waypoint. Probably you want to use the gpsco struct for the gps coordinates and the time_t type for the time.
  4. Your program should output the name of the file and the number of waypoints read from the file.
  5. Next your program will follow a series of commands.
    • Most of the features ask you to add the functionality for some command.
    • The quit command is used to exit your program.

For example:

~$ ./track
File: ride1.txt
Opened ride1.txt with 1814 waypoints
command: quit

Feature 1: Linked list storage

Implement the command linked.

To implement this feature, you have to store tracks as a linked list of waypoints and then use the linked list for the other features as well.

Once you implement this feature, your program should ignore the number at the beginning of the .txt track files and just read until the end of file. In other words, implementing the linked list feature will make your code work correctly even if the number at the beginning of the .txt file is wrong.

Your program should output the correct number of waypoints, even if the number at the begining of the file is wrong.

To document that you have implemented this feature (and make sure your instructor looks for it to give you credit), add a function linked to your track program that just prints out the word “yes”, as in:

~$ ./track
File: run1.txt
Opened run1.txt with 760 waypoints
command: linked
yes
command: quit

Note: Recall that each feature can be implemented independently of the other ones and in any order. Check the other features as well and then determine when you want to implement this feature.

Feature 2: Statistics

Implement the command stats, which reports the total distance, total time, average speed in miles per hour, and average pace in minutes per mile.

Example runs are shown below:

~$ ./track
File: run2.txt
Opened run2.txt with 327 waypoints
command: stats
Total time: 33 minutes, 51 seconds
Total distance: 4.02977 miles
Average speed: 7.14286 mph
Average pace: 8 minutes, 23.9995 seconds per mile 
command: quit
~$ ./track
File: ride1.txt
Opened ride1.txt with 1814 waypoints
command: stats
Total time: 1 hours, 14 minutes, 9 seconds
Total distance: 25.5691 miles
Average speed: 20.6897 mph
Average pace: 2 minutes, 53.9994 seconds per mile 
command: quit
  • The total distance is the sum of the distances between each pair of consecutive waypoints.
  • The average speed is the total distance divided by the total time (in miles per hour).
  • The average pace is the total time divided by the total distance (in seconds per mile).
  • Print the times for total time and average pace as "X hours, Y minutes, Z seconds". Except if the time is less than 1 hour, then just print "Y minutes, Z seconds".

Feature 3: Landmarks

The landmarks file will start with a line containing the number of landmarks, followed by lines that contain the latitude, longitude (in degrees), and name of each landmark. For example, land1.txt looks as follows:
130
57.64 -134.35 Admiralty_Island
40.7144 -74.0042 African_Burial_Ground
42.416 -103.728 Agate_Fossil_Beds
...

Your task

Implement a landmarks function that reads in a file of landmarks (an array is fine) and reports the minimum distance on the track to each landmark, starting with the closest landmark.

Examples are shown below:

~$ ./track
File: run1.txt
Opened run1.txt with 760 waypoints  
command: landmarks land2.txt
London_Town 1.61996 miles
Thomas_Point 5.2619 miles
USNA 5.78583 miles
Mount_Misery 9.33067 miles
NSA 16.673 miles
Smith_Island 68.6522 miles
command: quit
~$ ./track
File: run2.txt
Opened run2.txt with 327 waypoints  
command: landmarks land2.txt
USNA 0.144812 miles
London_Town 3.94861 miles
Thomas_Point 4.96613 miles
Mount_Misery 5.35822 miles
NSA 16.9579 miles
Smith_Island 72.8357 miles
command: quit

  • The distance to each landmark is defined as the shortest distance from any waypoint in the track to that landmark.
  • In particular, the closest waypoint might be different for each landmark, and your program should account for that.
  • Warning: The final list of landmarks must be sorted from closest landmark to furthest landmark before printing them out. You don't need to use a linked list for sorting -- using an array for sorting is fine.

Feature 4: Fastest mile

Implement a fastest function that prompts for a target distance (in miles) and then reports the fastest time within the track for that distance.

Conceptually, imagine yourself jogging for say 5 miles. While jogging, you wondered how fast you could run for a mile (that is, the target distance is a mile in this case). So, you ran with the top speed for a mile at some point. Now, coming back with your GPS track data, you need to extract that one-mile-segment where you ran fastest and find out the elapsed time for that segment.

Definitions (read carefully!)
  • The distance that the user enters in the fastest command is called the target distance.
  • The cumulative distance of two waypoints is defined as the sum of the distances between all intermediate waypoints.
  • The fastest time for the target distance is the least time between two waypoints whose cumulative distance is at least the target distance.

See the right for example runs.

~$ ./track
File: ride3.txt
Opened ride3.txt with 13188 waypoints
command:fastest 0.5
50 seconds
command: fastest 10
29 minutes, 56 seconds
command: fastest 40
3 hours, 5 minutes, 10 seconds
command: quit
~$ ./track
File: run1.txt
Opened run1.txt with 760 waypoints
command: fastest 1
7 minutes, 9 seconds
command: fastest 10
1 hours, 22 minutes, 16 seconds
command: quit

Clarifying the definitions

To illustrate the above definitions better, let's consider a toy example.
Suppose that the program receives the following command:

fastest 1.1
Suppose the track file has 5 waypoints, and the following information (we ignore units for simplicity) is extracted from the track file.

                              distance         time 
--------------------------------------------------------
between waypoints 0 and 1       2.0             1.5
between waypoints 1 and 2       0.3             0.2
between waypoints 2 and 3       0.8             1.1
between waypoints 3 and 4       0.9             0.9

Feature 5: Visual picture

Implement a visual function that displays a visual depiction of the current track in a new window. You don’t need to overlay it over any actual maps, just make a line drawing with a line between each pair of consecutive points in the track.

You can check any of the gpx files in the sample files (which match with the corresponding txt file) by uploading to websites such as http://www.gpsvisualizer.com/. Your image will be simpler because it’s not overlaid on Google Maps, but this should give you an idea of what the shape should look like.

In order to pop up a window in your program, you will use the GTK+ library along with a drawing library called Cairo.

These are really big libraries that are used to create many Linux applications, and using them and reading the documentation can be difficult. That’s part of what this assignment is testing!

Sample GTK program

To help get you started, check out this example program gtkexample.cpp that opens a window in GTK and draws a line in the window. To compile this program, just download it to your project directory and then do

make gtkexample
To run the program after you compiled it, from the command line, just do
./gtkexample

Warning: If you get an error message when running ./gtkexample on your laptop and a window does not open up, your VcXsrv is likely not running. See the instructions under "Launch VcXsrv" section in the course setup to launch the server (you only need to do steps 1 and 2)

Notes

  • Your program should go back to asking for the command: after the user closes the GTK+ window.
  • Orient the map with north pointing up. You can assume the path doesn’t cross over the north or south pole, or the anti-meridian.
  • You don't have to account for the curvature of the globe here. Treat each latitue and longitude as a coordinate point and simply scale the coordinates so that the whole picture fits tightly into the 800x800 grid.

    Here is an example run from the command line:

    ~$ ./track
    File: run2.txt
    Opened run2.txt with 327 waypoints
    command: visual
    command: quit
    

    Which should result in the right window being shown:

Shrinking the size of shapes

Please run the following command:
export GDK_SCALE=0.5
For example, to execute the above sample run with showing the picture in a smaller scale:
~$ export GDK_SCALE=0.5
~$ ./track
File: run2.txt
Opened run2.txt with 327 waypoints
command: visual
command: quit

Feature 6: GPX file input

Note: You are required to use a linked list for this feature!

Modify your program so that if the user enters a filename that ends with .gpx, then an alternate format is accepted.

GPX files

GPX is a real file format used by many fitness tracking apps and devices. If you have a Garmin or similar device, you can download any of your tracks as .gpx files. The full specs are here, but since we just want the location and time for each waypoint, it will be a bit easier.

The file format for GPX files is called XML.

XML format (read carefully!)

XML is similar to HTML if you remember that from your SY110 class. Each part of the XML has some "attributes" and some "children". For example, consider the following XML document.

<outer>
  <inner attribute1="cool" font="times" attribute3="yellow">
    <item1>Item 1 contents</item1>
    <item2>Item 2 contents</item2>
  </inner>
</outer>

Run xmlexample.cpp with GPX files

This example program xmlexample.cpp will read in an XML file like the small example above and report on the various elements and attributes. To compile it, type
make xmlexample

Write your code

The xmlexample.cpp program uses a standard library called libxml2 in order to read the XML files. The libxml2 library parses files as a “tree” of XML elements, which are naturally nested inside each other.

Example runs

~$ ./track
File: ride1.gpx
Opened ride1.gpx with 1814 waypoints
command: quit
~$ ./track
File: ride2.gpx
Opened ride2.gpx with 4584 waypoints
command: quit
~$ ./track
File: ride3.gpx
Opened ride3.gpx with 13188 waypoints
command: quit