diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
index f448236346dcd6466fdadf4b59d6c98b73687473..58c85b7ec844400ec87cac021a2fc4a3c71b11dd 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
@@ -1373,17 +1373,13 @@ public enum ATTRIBUTE_TYPE {
 				bundle.getString("BlackboardAttribute.tskattachments.text"),
 				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
 		
-		TSK_GEO_DISTANCE_FROM_HOME_POINT(142, "TSK_GEO_DISTANCE_FROM_HOME_POINT", //NON-NLS
-				bundle.getString("BlackboardAttribute.tskdronehpdistance.text"),
-				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
-	
-		TSK_GEO_DISTANCE_TRAVELED(143, "TSK_GEO_DISTANCE_TRAVELED", //NON-NLS
-				bundle.getString("BlackboardAttribute.tskdronedistancetraveled.text"),
-				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE),
-		
-		TSK_GEO_TRACKPOINTS(144, "TSK_GEO_TRACKPOINTS",
+		TSK_GEO_TRACKPOINTS(142, "TSK_GEO_TRACKPOINTS",
 			bundle.getString("BlackboardAttribute.tskgeopath.text"),
 			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
+		
+		TSK_GEO_WAYPOINTS(143, "TSK_GEO_WAYPOINTS",
+			bundle.getString("BlackboardAttribute.tskgeowaypoints.text"),
+			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
 
 		;
 
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
index 3908626177bb626ce7028f5880bd815a01897b84..96584aa91e722ee42eeaae568c339dc7c13f736c 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
@@ -191,9 +191,8 @@ BlackboardAttribute.tskaccountsettings.text=Account Settings
 BlackboardAttribute.tskpasswordhint.text=Password Hint
 BlackboardAttribute.tskgroups.text=Groups
 BlackboardAttribute.tskattachments.text=Message Attachments
-BlackboardAttribute.tskdronehpdistance.text=Distance From Home Point
-BlackboardAttribute.tskdronedistancetraveled.text=Total Distance Traveled
-BlackboardAttribute.tskgeopath.text=Waypoint List
+BlackboardAttribute.tskgeopath.text=List of Track Points
+BlackboardAttribute.tskgeowaypoints.text=List of Waypoints
 AbstractFile.readLocal.exception.msg4.text=Error reading local file\: {0}
 AbstractFile.readLocal.exception.msg1.text=Error reading local file, local path is not set
 AbstractFile.readLocal.exception.msg2.text=Error reading local file, it does not exist at local path\: {0}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java
index 2d062e02b5d5172652f64e8403222d50613d652a..1fb7cd76292126b94ace0eb621e79f904a04ded4 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/TimelineEventTypes.java
@@ -21,13 +21,12 @@
 import com.google.common.net.InternetDomainName;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS;
-import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
-import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
 
 /**
  * Container class for various types of timeline events
@@ -57,6 +56,7 @@ static class URLArtifactEventType extends TimelineEventArtifactTypeSingleDescrip
 			super(typeID, displayName, superType, artifactType, timeAttribute, descriptionAttribute);
 		}
 
+		@Override
 		TimelineEventDescription parseDescription(String fullDescriptionRaw, String medDescriptionRaw, String shortDescriptionRaw) {
 			/**
 			 * Parses the full description from db, which is the full URL, to a
@@ -99,6 +99,7 @@ static class FilePathEventType extends TimelineEventTypeImpl {
 			super(typeID, displayName, eventTypeZoomLevel, superType);
 		}
 
+		@Override
 		TimelineEventDescription parseDescription(String fullDescription, String medDescription, String shortDescription) {
 			return parseFilePathDescription(fullDescription);
 		}
@@ -111,6 +112,7 @@ static class FilePathArtifactEventType extends TimelineEventArtifactTypeSingleDe
 			super(typeID, displayName, superType, artifactType, timeAttribute, descriptionAttribute);
 		}
 
+		@Override
 		TimelineEventDescription parseDescription(String fullDescriptionRaw, String medDescriptionRaw, String shortDescriptionRaw) {
 			return parseFilePathDescription(fullDescriptionRaw);
 		}
@@ -123,6 +125,8 @@ TimelineEventDescription parseDescription(String fullDescriptionRaw, String medD
 	 */
 	static class GPSTrackArtifactEventType extends TimelineEventArtifactTypeSingleDescription {
 		
+		private final TskGeoTrackpointsUtil trackpointUtil = new TskGeoTrackpointsUtil();
+		
 		GPSTrackArtifactEventType(int typeID, String displayName, TimelineEventType superType, BlackboardArtifact.Type artifactType, BlackboardAttribute.Type descriptionAttribute) {
 			// Passing TSK_GEO_TRACKPOINTS as the "time attribute" as more of a place filler, to avoid any null issues
 			super(typeID, displayName, superType, artifactType, new BlackboardAttribute.Type(TSK_GEO_TRACKPOINTS), descriptionAttribute);
@@ -138,15 +142,8 @@ public TimelineEventDescriptionWithTime makeEventDescription(BlackboardArtifact
 			}
 			
 			// Get the waypoint list "start time"
-			List<GeoTrackPoint> points = GeoTrackPoints.deserializePoints(attribute.getValueString());
-			Long startTime = null;
-			for (GeoTrackPoint point : points) {
-				// Points are in time order so return the first non-null time stamp
-				startTime = point.getTimeStamp();
-				if (startTime != null) {
-					break;
-				}
-			}
+			GeoTrackPointList pointsList = trackpointUtil.fromAttribute(attribute);
+			Long startTime = pointsList.getStartTime();
 			
 			// If we didn't find a startime do not create an event.
 			if (startTime == null) {
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
index 89af4887ff33a91ae54818b7bdef802c2aac6b2a..0e3f99f5a072d33678e470d91dfb72af8b68e6fe 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
@@ -18,7 +18,8 @@
  */
 package org.sleuthkit.datamodel.blackboardutils;
 
-import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
+import java.util.ArrayList;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
 import java.util.List;
 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
 import org.sleuthkit.datamodel.BlackboardArtifact;
@@ -26,7 +27,9 @@
 import org.sleuthkit.datamodel.Content;
 import org.sleuthkit.datamodel.SleuthkitCase;
 import org.sleuthkit.datamodel.TskCoreException;
-import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil;
 
 /**
  * Class to help ingest modules create Geolocation artifacts.
@@ -34,44 +37,112 @@
  */
 public final class GeoArtifactsHelper extends ArtifactHelperBase {
 
+	private final String programName;
+	private final TskGeoTrackpointsUtil trackPointAttributeUtil;
+	private final TskGeoWaypointsUtil waypointsAttributeUtil;
+
 	/**
 	 * Constructs a geolocation artifact helper for the given source file.
 	 *
-	 * @param caseDb			  Sleuthkit case db.
+	 * @param caseDb		Sleuthkit case db.
 	 * @param moduleName	Name of module using the helper.
-	 * @param srcFile			 Source file being processed by the module.
+	 * @param programName	Optional program name for TSK_PROG_NAME attribute, 
+	 *						nulls and empty string will be ignored.
+	 * @param srcFile		Source file being processed by the module.
 	 */
-	public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcFile) {
+	public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, String programName, Content srcFile) {
 		super(caseDb, moduleName, srcFile);
+		this.programName = programName;
+		trackPointAttributeUtil = new TskGeoTrackpointsUtil();
+		waypointsAttributeUtil = new TskGeoWaypointsUtil();
 	}
 
 	/**
-	 * Add a Track from a GPS device to the database.  A Track represents a series of points that the device
-	 * has traveled on.  This will create a TSK_GPS_TRACK artifact and add it to the case. 
+	 * Add a Track from a GPS device to the database. A Track represents a 
+	 * series of points that the device has traveled on. This will create a 
+	 * TSK_GPS_TRACK artifact and add it to the case.
 	 *
-	 * @param trackName	Name of GPS track, not required.  Pass in null if unknown. 
-	 * @param points	Set of GeoTrackPoints that the track traversed. Required.
+	 * @param trackName			Name of GPS track, not required.
+	 * @param points			List of GeoTrackPoints that the track traversed.
+	 *							Required.
+	 * @param moreAttributes	Optional list of other artifact attributes
 	 *
 	 * @return	TSK_GPS_TRACK artifact
 	 *
-	 * @throws TskCoreException		  If there is an error creating the artifact.
+	 * @throws TskCoreException		If there is an error creating the artifact.
 	 * @throws BlackboardException	If there is a problem posting the artifact
 	 */
-	public BlackboardArtifact addTrack(String trackName, List<GeoTrackPoint> points) throws TskCoreException, BlackboardException {
-		if (points == null) {
-			throw new IllegalArgumentException("GeoTrackPoint instance must be valid");
+	public BlackboardArtifact addTrack(String trackName, GeoTrackPointList points, List<BlackboardAttribute> moreAttributes) throws TskCoreException, BlackboardException {
+		
+		if(points == null) {
+			throw new IllegalArgumentException(String.format("addTrack was passed a null list of track points"));
 		}
-
+		
 		BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK);
+		List<BlackboardAttribute> attributes = new ArrayList<>();
 		if (trackName != null) {
-			artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), trackName));
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), trackName));
+		}
+
+		attributes.add(trackPointAttributeUtil.toAttribute(getModuleName(), points));
+
+		if (programName != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName));
+		}
+
+		if (moreAttributes != null) {
+			attributes.addAll(moreAttributes);
+		}
+		
+		artifact.addAttributes(attributes);
+
+		getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName());
+
+		return artifact;
+	}
+
+	/**
+	 * Add a Route from a GPS device to the database. This will create a 
+	 * TSK_GPS_ROUTE artifact and add it to the case.
+	 *
+	 * @param routeName			Optional route name
+	 * @param creationTime		Time the route was created, optional.
+	 * @param points			List of GeoWaypointList belonging to the route, required
+	 * @param moreAttributes	Optional list of other artifact attributes.
+	 *
+	 * @return TSK_GPS_ROUTE artifact
+	 *
+	 * @throws TskCoreException		If there is an error creating the artifact.
+	 * @throws BlackboardException	If there is a problem posting the artifact.
+	 */
+	public BlackboardArtifact addRoute(String routeName, Long creationTime, GeoWaypointList points, List<BlackboardAttribute> moreAttributes) throws TskCoreException, BlackboardException {
+
+		if (points == null) {
+			throw new IllegalArgumentException(String.format("addRoute was passed a null list of waypoints"));
+		}
+
+		BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE);
+		List<BlackboardAttribute> attributes = new ArrayList<>();
+
+		attributes.add(waypointsAttributeUtil.toAttribute(getModuleName(), points));
+		
+		if (routeName != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), routeName));
+		}
+
+		if (creationTime != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, getModuleName(), creationTime));
+		}
+
+		if (programName != null) {
+			attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, getModuleName(), programName));
+		}
+
+		if (moreAttributes != null) {
+			attributes.addAll(moreAttributes);
 		}
 
-		artifact.addAttribute(
-				new BlackboardAttribute(
-						BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS,
-						getModuleName(),
-						GeoTrackPoints.serializePoints(points)));
+		artifact.addAttributes(attributes);
 
 		getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName());
 
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java
new file mode 100755
index 0000000000000000000000000000000000000000..d32b264a56a6309498990d944c4125089b9bb48a
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/BlackboardAttributeUtil.java
@@ -0,0 +1,48 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel.blackboardutils.attributes;
+
+import org.sleuthkit.datamodel.BlackboardAttribute;
+
+/**
+ * An interface for Utility classes to implement for translating
+ * BlackboardAttributes to and from a particular format. Initial use case is for
+ * BlackboardAttributes of type TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON.
+ */
+public interface BlackboardAttributeUtil<T> {
+
+	/**
+	 * Translates the value of type T to a attribute.
+	 *
+	 * @param moduleName	Name of module creating the artifact
+	 * @param value			Object to Translate to attribute
+	 *
+	 * @return BlackboardAttribute created from value
+	 */
+	BlackboardAttribute toAttribute(String moduleName, T value);
+
+	/**
+	 * Translates a attribute to an object of type T.
+	 *
+	 * @param attribute The attribute to be translated to T
+	 *
+	 * @return A new instance of T created from the attribute
+	 */
+	T fromAttribute(BlackboardAttribute attribute);
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
deleted file mode 100755
index 21474995e57360bf5102f1283424ec6ec86bcdfc..0000000000000000000000000000000000000000
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Sleuth Kit Data Model
- *
- * Copyright 2020 Basis Technology Corp.
- * Contact: carrier <at> sleuthkit <dot> org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *	 http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.datamodel.blackboardutils.attributes;
-
-import com.google.gson.Gson;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
-
-/**
- * Helper class to make it easier to serialize and deserialize the list of track
- * points with Gson.
- *
- */
-public final class GeoTrackPoints {
-
-	private final List<GeoTrackPoint> points;
-
-	/**
-	 * Deserialize the given list of GeoTrackPoints.
-	 *
-	 * @param jsonString JSon string of track points.
-	 *
-	 * @return	Timestamp ordered list of GeoTrackPoints, empty list will be
-	 *         returned if jsonString is null or empty.
-	 */
-	public static List<GeoTrackPoint> deserializePoints(String jsonString) {
-		if (jsonString == null || jsonString.isEmpty()) {
-			return new ArrayList<>();
-		}
-
-		GeoTrackPoints trackPoints = (new Gson()).fromJson(jsonString, GeoTrackPoints.class);
-		return trackPoints.getTimeOrderedPoints();
-	}
-
-	/**
-	 * Serialize the given list of GeoTrackPoints.
-	 *
-	 * @param points List of GeoTrackPoints
-	 *
-	 * @return	JSon formatted string is returned or empty string if points was
-	 *         null
-	 */
-	public static String serializePoints(List<GeoTrackPoint> points) {
-		if (points == null) {
-			return "";
-		}
-
-		Gson gson = new Gson();
-		return gson.toJson(new GeoTrackPoints(points));
-	}
-
-	/**
-	 * Constructs a new instance with the give list of GeoTrackPoints.
-	 *
-	 * @param points
-	 */
-	private GeoTrackPoints(List<GeoTrackPoint> points) {
-		if (points == null) {
-			throw new IllegalArgumentException("Invalid list of track points passed to constructor");
-		}
-
-		this.points = points;
-	}
-
-	/**
-	 * Returns a timestamp ordered copy of the points list.
-	 *
-	 * @return timestamp
-	 */
-	private List<GeoTrackPoint> getTimeOrderedPoints() {
-		return points.stream().sorted().collect(Collectors.toCollection(ArrayList::new));
-	}
-}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java
deleted file mode 100755
index 14fb0358d991a7a3c495c849a02361337d9d6bde..0000000000000000000000000000000000000000
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Sleuth Kit Data Model
- *
- * Copyright 2020 Basis Technology Corp.
- * Contact: carrier <at> sleuthkit <dot> org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *	 http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.datamodel.blackboardutils.attributes;
-
-import com.google.gson.annotations.SerializedName;
-
-/**
- * Class that represents a single waypoint made up of longitude, latitude, and
- * altitude.
- */
-public class GeoWaypoint {
-
-	@SerializedName("TSK_GEO_LATITUDE")
-	private final Double latitude;
-	@SerializedName("TSK_GEO_LONGITUDE")
-	private final Double longitude;
-	@SerializedName("TSK_GEO_ALTITUDE")
-	private final Double altitude;
-
-	/**
-	 * Creates a GeoWaypoint instance.
-	 *
-	 * @param latitude  The latitude, required
-	 * @param longitude The longitude, required
-	 * @param altitude  The altitude, can be null
-	 */
-	public GeoWaypoint(Double latitude, Double longitude, Double altitude) {
-		if (latitude == null || longitude == null) {
-			throw new IllegalArgumentException("Null cordinate value passed to waypoint constructor");
-		}
-
-		this.latitude = latitude;
-		this.longitude = longitude;
-		this.altitude = altitude;
-	}
-
-	/**
-	 * Returns latitude of the waypoint.
-	 *
-	 * @return Double latitude value
-	 */
-	public Double getLatitude() {
-		return latitude;
-	}
-
-	/**
-	 * Returns longitude of the waypoint.
-	 *
-	 * @return Double longitude value
-	 */
-	public Double getLongitude() {
-		return longitude;
-	}
-
-	/**
-	 * Get the altitude if available for this waypoint.
-	 *
-	 * @return Double altitude value, maybe null if not available or applicable
-	 */
-	public Double getAltitude() {
-		return altitude;
-	}
-
-	/**
-	 * A GeoTrackPoint is a Waypoint with more detailed information about the
-	 * point.
-	 *
-	 */
-	public final static class GeoTrackPoint extends GeoWaypoint implements Comparable<GeoTrackPoint> {
-
-		@SerializedName("TSK_GEO_VELOCITY")
-		private final Double velocity;
-		@SerializedName("TSK_GEO_DISTANCE_FROM_HOME_POINT")
-		private final Double distanceFromHP;
-		@SerializedName("TSK_GEO_DISTANCE_TRAVELED")
-		private final Double distanceTraveled;
-		@SerializedName("TSK_DATETIME")
-		private final Long timestamp;
-
-		/**
-		 * Constructs a GeoTrackPoint with the given attributes.
-		 *
-		 * @param latitude			      Latitude of the trackpoint, required
-		 * @param longitude			     Longitude of the trackpoint, required
-		 * @param altitude			      Altitude of the trackpoint, maybe null
-		 * @param velocity			      Velocity in meters/sec, maybe null
-		 * @param distanceFromHP	  Trackpoint distance from an established "home
-		 *                         point", maybe null if not applicable
-		 * @param distanceTraveled	Overall distance traveled in meters at the
-		 *                         time this trackpoint was created, maybe null
-		 *                         if not applicable
-		 * @param timestamp			     Trackpoint creation time, maybe null if not
-		 *                         applicable
-		 */
-		public GeoTrackPoint(Double latitude,
-				Double longitude,
-				Double altitude,
-				Double velocity,
-				Double distanceFromHP,
-				Double distanceTraveled,
-				Long timestamp) {
-			super(latitude, longitude, altitude);
-			this.velocity = velocity;
-			this.distanceFromHP = distanceFromHP;
-			this.distanceTraveled = distanceTraveled;
-			this.timestamp = timestamp;
-		}
-
-		/**
-		 * Returns velocity of the point.
-		 *
-		 * @return Double velocity value, maybe null if not available or
-		 *         applicable
-		 */
-		public Double getVelocity() {
-			return velocity;
-		}
-
-		/**
-		 * Returns distance from home point for the point.
-		 *
-		 * @return Double velocity distance from home point, maybe null if not
-		 *         available or applicable
-		 */
-		public Double getDistanceFromHP() {
-			return distanceFromHP;
-		}
-
-		/**
-		 * Returns distance traveled for the point.
-		 *
-		 * @return Double distance traveled value, maybe null if not available
-		 *         or applicable
-		 */
-		public Double getDistanceTraveled() {
-			return distanceTraveled;
-		}
-
-		/**
-		 * Returns the time stamp (seconds from java/unix epoch) of the track
-		 * point.
-		 *
-		 * @return time stamp of the track point, or null if not available
-		 */
-		public Long getTimeStamp() {
-			return timestamp;
-		}
-
-		@Override
-		public int compareTo(GeoTrackPoint otherTP) {
-			Long otherTimeStamp = otherTP.getTimeStamp();
-
-			if (timestamp == null && otherTimeStamp != null) {
-				return -1;
-			} else if (timestamp != null && otherTimeStamp == null) {
-				return 1;
-			} else {
-				return timestamp.compareTo(otherTP.getTimeStamp());
-			}
-		}
-	}
-
-}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java
new file mode 100755
index 0000000000000000000000000000000000000000..ad434f5c7d3e1da86cbf7620629b9fb0a9c9b835
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoTrackpointsUtil.java
@@ -0,0 +1,330 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel.blackboardutils.attributes;
+
+import com.google.gson.Gson;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.sleuthkit.datamodel.BlackboardAttribute;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoTrackpointsUtil.GeoTrackPointList.GeoTrackPoint;
+
+/**
+ * Utility class for translating TSK_GEO_TRACKPOINTS attribute values to
+ * GeoTrackPointList objects and GeoTrackPointList to BlackboardAttributes.
+ */
+public final class TskGeoTrackpointsUtil implements BlackboardAttributeUtil<GeoTrackPointList> {
+
+	@Override
+	public BlackboardAttribute toAttribute(String moduleName, GeoTrackPointList value) {
+
+		if (value == null) {
+			throw new IllegalArgumentException("toAttribute was passed a null list");
+		}
+
+		return new BlackboardAttribute(
+				BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS,
+				moduleName,
+				toJSON(value));
+	}
+
+	@Override
+	public GeoTrackPointList fromAttribute(BlackboardAttribute attribute) {
+		if (attribute == null) {
+			throw new IllegalArgumentException("fromAttribute was passed a null attribute");
+		}
+
+		BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
+		if (type != BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS) {
+			throw new IllegalArgumentException(String.format("Invalid attribute of type %s passed to fromAttribute method. Attribute of type TSK_GEO_TRACKPOINTS is required", type.getDisplayName()));
+		}
+
+		return fromJSON(attribute.getValueString());
+	}
+
+	/**
+	 * Creates a GeoTrackPointList from the given JSON string.
+	 *
+	 * @param jsonString JSon string of track points.
+	 *
+	 * @return	Timestamp ordered list of GeoTrackPoints, empty list will be
+	 *			returned if jsonString is null or empty.
+	 */
+	private static GeoTrackPointList fromJSON(String jsonString) {
+		if (jsonString == null || jsonString.isEmpty()) {
+			return null;
+		}
+
+		return (new Gson()).fromJson(jsonString, GeoTrackPointList.class);
+	}
+
+	/**
+	 * Returns a JSON string representing the given object.
+	 *
+	 * @return JSON string
+	 */
+	private static String toJSON(GeoTrackPointList pointList) {
+		Gson gson = new Gson();
+		return gson.toJson(pointList);
+	}
+
+	/**
+	 * A list of GeoTrackPoints.
+	 */
+	public static class GeoTrackPointList implements Iterable<GeoTrackPointList.GeoTrackPoint> {
+
+		private final List<GeoTrackPoint> pointList;
+
+		/**
+		 * Construct an empty GeoTrackPointList.
+		 */
+		public GeoTrackPointList() {
+			pointList = new ArrayList<>();
+		}
+
+		/**
+		 * Construct a new instance with the given list of GeoTrackPoint
+		 * objects.
+		 *
+		 * @param points List of track points, cannot be null.
+		 */
+		public GeoTrackPointList(List<GeoTrackPoint> points) {
+			if (points == null) {
+				throw new IllegalArgumentException("Constructor was passed a null list");
+			}
+
+			pointList = points;
+		}
+
+		/**
+		 * Add a point to the list of track points.
+		 *
+		 * @param point A point to add to the track point list, cannot be null.
+		 */
+		public void addPoint(GeoTrackPoint point) {
+			if (point == null) {
+				throw new IllegalArgumentException("addPoint was passed a null list");
+			}
+
+			pointList.add(point);
+		}
+
+		/**
+		 * Adds a new point with the given attributes.
+		 *
+		 * @param latitude			    Latitude of the trackpoint, required
+		 * @param longitude			    Longitude of the trackpoint, required
+		 * @param altitude			    Altitude of the trackpoint, maybe null
+		 * @param name				    Name of trackpoint, maybe null
+		 * @param velocity				Velocity in meters/sec, maybe null
+		 * @param distanceFromHomePoint	Track point distance from an established
+		 *                              "home point", may be null if not
+		 *                              applicable
+		 * @param distanceTraveled			   Overall distance traveled in meters at
+		 *                              the time this trackpoint was created,
+		 *                              maybe null if not applicable
+		 * @param timestamp					        Trackpoint creation time, maybe null if
+		 *                              not applicable
+		 */
+		public void addPoint(Double latitude,
+				Double longitude,
+				Double altitude,
+				String name,
+				Double velocity,
+				Double distanceFromHomePoint,
+				Double distanceTraveled,
+				Long timestamp) {
+			pointList.add(new GeoTrackPoint(
+					latitude,
+					longitude,
+					altitude,
+					name,
+					velocity,
+					distanceFromHomePoint,
+					distanceTraveled,
+					timestamp));
+		}
+
+		/**
+		 * Returns an iterator over the points in this GeoTrackPointList.
+		 *
+		 * @return An iterator over the elements of the list.
+		 */
+		@Override
+		public Iterator<GeoTrackPoint> iterator() {
+			return pointList.iterator();
+		}
+
+		/**
+		 * Returns true if this list contains no points.
+		 *
+		 * @return True if this list contains no points.
+		 */
+		public boolean isEmpty() {
+			return pointList.isEmpty();
+		}
+
+		/**
+		 * Return the start time for the track.
+		 *
+		 * @return First non-null time stamp or null, if one was not found.
+		 */
+		public Long getStartTime() {
+			List<GeoTrackPoint> orderedPoints = getTimeOrderedPoints();
+			if (orderedPoints != null) {
+				for (GeoTrackPoint point : orderedPoints) {
+					if (point.getTimeStamp() != null) {
+						return point.getTimeStamp();
+					}
+				}
+			}
+			return null;
+		}
+
+		/**
+		 * Return the ends time for the track.
+		 *
+		 * @return First non-null time stamp or null, if one was not found.
+		 */
+		public Long getEndTime() {
+			List<GeoTrackPoint> orderedPoints = getTimeOrderedPoints();
+			if (orderedPoints != null) {
+				for (int index = orderedPoints.size() - 1; index >= 0; index--) {
+					GeoTrackPoint point = orderedPoints.get(index);
+					if (point.getTimeStamp() != null) {
+						return point.getTimeStamp();
+					}
+				}
+			}
+			return null;
+		}
+
+		/**
+		 * Returns a timestamp ordered copy of the points list.
+		 *
+		 * @return List of points sorted by timestamps.
+		 */
+		private List<GeoTrackPoint> getTimeOrderedPoints() {
+			return pointList.stream().sorted().collect(Collectors.toCollection(ArrayList::new));
+		}
+
+		/**
+		 * A GeoTrackPoint is a Waypoint with more detailed information about
+		 * the point.
+		 *
+		 */
+		public final static class GeoTrackPoint extends TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint implements Comparable<GeoTrackPointList.GeoTrackPoint> {
+
+			private final Double velocity;
+			private final Double distanceFromHomePoint;
+			private final Double distanceTraveled;
+			private final Long timestamp;
+
+			/**
+			 * Constructs a GeoTrackPoint with the given attributes.
+			 *
+			 * @param latitude				Latitude of the track point, required
+			 * @param longitude				Longitude of the track point,
+			 *                              required
+			 * @param altitude			    Altitude of the track point, may be
+			 *                              null
+			 * @param name				    Name of track point, may be null
+			 * @param velocity			    Velocity in meters/sec, may be null
+			 * @param distanceFromHomePoint	Track point distance from an
+			 *                              established "home point", maybe null
+			 *                              if not applicable
+			 * @param distanceTraveled	    Overall distance traveled in meters
+			 *                              at the time this track point was
+			 *                              created, maybe null if not
+			 *                              applicable
+			 * @param timestamp			    Track point creation time, maybe
+			 *                              null if not applicable
+			 */
+			public GeoTrackPoint(Double latitude,
+					Double longitude,
+					Double altitude,
+					String name,
+					Double velocity,
+					Double distanceFromHomePoint,
+					Double distanceTraveled,
+					Long timestamp) {
+				super(latitude, longitude, altitude, name);
+				this.velocity = velocity;
+				this.distanceFromHomePoint = distanceFromHomePoint;
+				this.distanceTraveled = distanceTraveled;
+				this.timestamp = timestamp;
+			}
+
+			/**
+			 * Returns velocity of the point.
+			 *
+			 * @return Double velocity value, maybe null if not available or
+			 *         applicable
+			 */
+			public Double getVelocity() {
+				return velocity;
+			}
+
+			/**
+			 * Returns distance from home point for the point.
+			 *
+			 * @return Double velocity distance from home point, maybe null if
+			 *         not available or applicable
+			 */
+			public Double getDistanceFromHomePoint() {
+				return distanceFromHomePoint;
+			}
+
+			/**
+			 * Returns distance traveled for the point.
+			 *
+			 * @return Double distance traveled value, maybe null if not
+			 *         available or applicable
+			 */
+			public Double getDistanceTraveled() {
+				return distanceTraveled;
+			}
+
+			/**
+			 * Returns the time stamp (seconds from java/unix epoch) of the
+			 * track point.
+			 *
+			 * @return time stamp of the track point, or null if not available
+			 */
+			public Long getTimeStamp() {
+				return timestamp;
+			}
+
+			@Override
+			public int compareTo(GeoTrackPointList.GeoTrackPoint otherTP) {
+				Long otherTimeStamp = otherTP.getTimeStamp();
+
+				if (timestamp == null && otherTimeStamp != null) {
+					return -1;
+				} else if (timestamp != null && otherTimeStamp == null) {
+					return 1;
+				} else {
+					return timestamp.compareTo(otherTP.getTimeStamp());
+				}
+			}
+		}
+	}
+}
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java
new file mode 100755
index 0000000000000000000000000000000000000000..08d5a1c6bd6a77ab67bf0992c5a73a2d4e58ebe5
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/TskGeoWaypointsUtil.java
@@ -0,0 +1,197 @@
+/*
+ * Sleuth Kit Data Model
+ *
+ * Copyright 2020 Basis Technology Corp.
+ * Contact: carrier <at> sleuthkit <dot> org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	 http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.datamodel.blackboardutils.attributes;
+
+import com.google.gson.Gson;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.sleuthkit.datamodel.BlackboardAttribute;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList;
+import org.sleuthkit.datamodel.blackboardutils.attributes.TskGeoWaypointsUtil.GeoWaypointList.GeoWaypoint;
+
+/**
+ * Utility class for Translating TSK_GEO_WAYPOINTS attribute values to
+ * GeoWaypointList objects and GeoWaypointList to BlackboardAttributes.
+ */
+public final class TskGeoWaypointsUtil implements BlackboardAttributeUtil<GeoWaypointList> {
+
+	@Override
+	public BlackboardAttribute toAttribute(String moduleName, GeoWaypointList value) {
+
+		if (value == null) {
+			throw new IllegalArgumentException("toAttribute was pass a null list");
+		}
+
+		return new BlackboardAttribute(
+				BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS,
+				moduleName,
+				toJSON(value));
+	}
+
+	@Override
+	public GeoWaypointList fromAttribute(BlackboardAttribute attribute) {
+		if (attribute == null) {
+			throw new IllegalArgumentException("fromAttribute was pass a null list");
+		}
+
+		BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
+		if (type != BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_WAYPOINTS) {
+			throw new IllegalArgumentException(String.format("Invalid attribute of type %s passed to fromAttribute method. Attribute of type TSK_GEO_WAYPOINTS is required", type.getDisplayName()));
+		}
+
+		return fromJSON(attribute.getValueString());
+	}
+
+	/**
+	 * Deserialize the given list of GeoTrackPoints.
+	 *
+	 * @param jsonString JSon string of track points.
+	 *
+	 * @return	Timestamp ordered list of GeoTrackPoints, empty list will be
+	 *         returned if jsonString is null or empty.
+	 */
+	private static GeoWaypointList fromJSON(String jsonString) {
+		if (jsonString == null || jsonString.isEmpty()) {
+			return null;
+		}
+
+		return (new Gson()).fromJson(jsonString, GeoWaypointList.class);
+	}
+
+	/**
+	 * Returns a JSON string can than be used as the TSK_GEO_TRACKPOINTS
+	 * attribute of the TSK_GPS_TRACK artifact.
+	 *
+	 * @return JSON string
+	 */
+	private static String toJSON(GeoWaypointList pointList) {
+		Gson gson = new Gson();
+		return gson.toJson(pointList);
+	}
+
+	/**
+	 * Helper class to make it easier to serialize and deserialize the list of
+	 * waypoints points with json.
+	 *
+	 */
+	public static final class GeoWaypointList implements Iterable<GeoWaypointList.GeoWaypoint> {
+
+		private final List<GeoWaypoint> points;
+
+		public GeoWaypointList() {
+			points = new ArrayList<>();
+		}
+
+		/**
+		 * Adds a point to the list of waypoints.
+		 *
+		 * @param latitude  The latitude, required
+		 * @param longitude The longitude, required
+		 * @param altitude  The altitude, can be null
+		 * @param name		A name for the point, can be null
+		 */
+		public void addPoint(Double latitude, Double longitude, Double altitude, String name) {
+			points.add(new GeoWaypoint(latitude, longitude, altitude, name));
+		}
+
+		/**
+		 * Returns true if this list contains no points.
+		 *
+		 * @return True if this list contains no points.
+		 */
+		public boolean isEmpty() {
+			return points.isEmpty();
+		}
+
+		@Override
+		public Iterator<GeoWaypointList.GeoWaypoint> iterator() {
+			return points.iterator();
+		}
+
+		/**
+		 * Class that represents a single waypoint made up of longitude,
+		 * latitude, and altitude.
+		 */
+		public static class GeoWaypoint {
+
+			private final Double latitude;
+			private final Double longitude;
+			private final Double altitude;
+			private final String name;
+
+			/**
+			 * Creates a GeoWaypoint instance.
+			 *
+			 * @param latitude  The latitude, required
+			 * @param longitude The longitude, required
+			 * @param altitude  The altitude, can be null
+			 * @param name		A name for the waypoint, optional
+			 */
+			public GeoWaypoint(Double latitude, Double longitude, Double altitude, String name) {
+				if (latitude == null || longitude == null) {
+					throw new IllegalArgumentException("Constructor was passed null coordinate");
+				}
+
+				this.latitude = latitude;
+				this.longitude = longitude;
+				this.altitude = altitude;
+				this.name = name;
+			}
+
+			/**
+			 * Returns latitude of the waypoint.
+			 *
+			 * @return Double latitude value
+			 */
+			public Double getLatitude() {
+				return latitude;
+			}
+
+			/**
+			 * Returns longitude of the waypoint.
+			 *
+			 * @return Double longitude value
+			 */
+			public Double getLongitude() {
+				return longitude;
+			}
+
+			/**
+			 * Get the altitude if available for this waypoint.
+			 *
+			 * @return Double altitude value, may be null if not available or
+			 *         applicable
+			 */
+			public Double getAltitude() {
+				return altitude;
+			}
+
+			/**
+			 * Returns the name for this waypoint.
+			 *
+			 * @return	Returns waypoint name, may be null if not available or
+			 *         applicable.
+			 */
+			public String getName() {
+				return name;
+			}
+		}
+	}
+}