Assignment 3: Heatmap for GPS Track

Objectives

Introduction

A heatmap is a graphical representation of a 2-D array using different colors to represent different ranges of values. Heatmaps are useful in a variety of applications, including web usability, sports analytics, and many geographic applications. Our application is search-and-rescue: given a GPS track, we want to generate a heatmap to see where in the search area the track hasn't been as often so that we can focus future search efforts on those locations.

A GPS track is a collection of trackpoints each containing a location and timestamp. Trackpoints within tracks are ordered by increasing timestamp and can be organized into multiple segments within the same track (a tracking device might, for example, start a new segment each time the user pauses the device, starts a new lap, or when the devices loses the signal from the satellites).

segment 0: (2.6,0.2,1) (2.2,0.8,2) (2.1,1.2,3) (1.7,1.3,4) (1.4,1.1,5) (1.3,0.9,6) (0.8,1.0,7) (0.7,1.1,8) (0.2,1.1,9) (0.0,1.5,10) (0.1,1.7,11); segment 1: (0.0,2.2,20) (0.3,2.2,21); segment 2: (1.2,2.3,25) (1.4,2.1,26) (1.9,2.1,28); segment 3: (2.7,2.0,32) (3.1,2.2.33) (3.3,1.7,34) (3.1,1.3,36) (3.2,0.7,38) (2.8,0.3,39); segment 4: (3.3,0.0,50) (4.2,0.3,52) (4.9,0.8,54) (4.9,1.6,57) (4.9,2.4,59) (4.8,3.1,60) (3.9,3.3,63) (2.9,3.4,65)
A track with 5 segments. Labels indicate the segment indices.
Arrows indicate the direction of increasing time along
a segment, and the bold segment is the current (last) segment.

Operations on segments allow adding points to the current (last) segment, starting a new segment, merging segments, and querying segments.

segment 4 becomes: (3.3,0.0,50) (4.2,0.3,52) (4.9,0.8,54) (4.9,1.6,57) (4.9,2.4,59) (4.8,3.1,60) (3.9,3.3,63) (2.9,3.4,65) (1.8,3.9,67)
The track above after adding the red track point.
adding segment 5: (2.5,0.0,80) (2.2,0.3,81)
The previous track after starting a new segment and
adding two track points to it (the pink then the red).
segment 1 becomes: (0.0,2.2,20) (0.3,2.2,21) (1.2,2.3,25) (1.4,2.1,26) (1.9,2.1,28) (2.7,2.0,32) (3.1,2.2.33) (3.3,1.7,34) (3.1,1.3,36) (3.2,0.7,38) (2.8,0.3,39); segment 4 is renumbered segment 2 and segment 5 is renumbered segment 3
The previous track after merging segments from 1
up to but not including 4.

Given a track and a grid size, we can create a heatmap by counting the number of track points within each grid cell (note that we count track points and don't consider the time between those track points).

a grid over the track with cell borders at x=0.0,1.0,2.0,3.0,4.0,5.0 and y=0.0,1.0,2.0,3.0
A grid superimposed on a track.
track point count within each cell: [[0,1,5,2,2],[5,2,1,2,1],[2,3,1,1,1],[0,1,1,1,1]]
The resulting heatmap. Darker colors
represent more track points within the cell.

Overview

Write a program called Heatmap that produces an ASCII-art heatmap from GPS track data read from standard input. We will run the Heatmap program with a command line like
./Heatmap 0.010 0.010 .:xX@# 1 < /c/cs223/hw3/Tests/example.in
  
where the command-line arguments are

Standard input will contain track point data, one track point per line and with segments within a track separated by blank lines in a format like

-48.0020 121.0260 1500000000000
-48.0080 121.0220 1500010000000
-48.0120 121.0210 1500025000000
-48.0130 121.0170 1500040000000
-48.0110 121.0140 1500045000000
-48.0095 121.0130 1500052000000
-48.0105 121.0080 1500058000000
-48.0110 121.0070 1500065000000
-48.0115 121.0020 1500072000000
-48.0150 121.0000 1500080000000
-48.0170 121.0010 1500095000000

-48.0220 121.0000 1500201000000
-48.0225 121.0030 1500210000000

-48.0235 121.0120 1500240000000
-48.0210 121.0140 1500245000000
-48.0215 121.0190 1500252000000

-48.0205 121.0270 1500268000000
-48.0220 121.0310 1500280000000
-48.0175 121.0330 1500288000000
-48.0130 121.0310 1500295000000
-48.0070 121.0320 1500300000000
-48.0030 121.0280 1500305000000

-48.0000 121.0330 1500500000000
-48.0030 121.0420 1500505000000
-48.0080 121.0490 1500510000000
-48.0165 121.0490 1500520000000
-48.0245 121.0490 1500530000000
-48.0310 121.0480 1500540000000
-48.0335 121.0390 1500547000000
-48.0340 121.0290 1500554000000
-48.0380 121.0180 1500562000000

-48.0000 121.0250 1500700000000
-48.0035 121.0220 1500707000000
  

The corresponding heatmap is then written to standard output in a format like

.:#xx
#x:x:
xX:::
.::::
  
where, for this example . represents a cell containing 0 track points, : represents a cell containing 1 track points, x represents a cell containing 2 track points, X represents a cell containing 3 track points, @ represents a cell containing 4 track points, and # represents a cell containing 5 or more track points.

If the command to run the program had been

./Heatmap 0.010 0.010 .o0 3 < /c/cs223/hw3/Tests/example.in
  
the the output would be
..o..
o....
.o...
.....
where . represents a cell containing between 0 and 2 track points (inclusive), o represents a cell containing between 3 and 5 track points (inclusive), and 0 represents a cell containing between 6 or more track points (there aren't any in this example).

Implementing the track ADT

The header file /c/cs223/hw3/Required/track.h specifies the public interface for a track abstract data type (ADT), which models a GPS track. You must implement that ADT and it will be tested separately. Refer to the documentation in the comments in the header file for the required behavior of each function. You may not change the header file, but you may include other helper functions that are not visible to other modules in your implementation file. The /c/cs223/hw3/Required directory also contains the required header and implementation files for location and trackpoint ADTs and you may not change those either.

Your track ADT will be tested with a series of unit tests that test individual functions. An incomplete suite of unit tests is implemented in /c/cs223/hw3/Required/track_unit.c, which will be replaced with a complete test suite for the private test script. The public and private unit test modules will each contain a main function that you must link with your track implementation to produce an executable called Unit that executes the unit tests. Examine the public track_unit.c to see how the individual tests work.

Details

Command-line arguments

Standard input

Building the heatmap

Output

Efficiency

All time bounds assume that $n$ is the number of total points in the track, $m$ is the number of segments, $p$ is the number of cells in the heatmap, and that malloc and free work in $O(1)$ time (note that this assumption does not extends to calloc, realloc or similar functions).

Testing

When the input (standard input or command-line arguments) are invalid, the output of your program is ignored and it must not crash or hang. Your Heatmap program must not produce any errors when tested with Valgrind, whether the input is valid or not. The unit test program Unit must not result in errors from Valgrind for any test. None of the public or private unit tests will violate the preconditions of the track ADT functions, and so the behavior of your implementations of those functions may be anything (including crashing and/or hanging). You may assume that memory allocations succeed, and so no error-checking is required for calls to malloc.

Violations of the abstraction provided by declaring trackpoint and track as opaque structs will be considered a violation of good programming style and design.

Hints

Submissions

Submit your makefile, log file, and source code necessary to build the executables Heatmap and Unit using your makefile. Your makefile should have rules with each of those executables as their targets. Your makefile should assume that the files from /c/cs223/hw3/Required have been copied into the current directory but not that they are still in their original locations, and you should not submit those files (if you do, they will be overwritten with the copies from /c/cs223/hw3/Required).