diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
index a4ba698b3ca283fa2ff24534e650c59b90e00606..ca96479e47afbe5f663552fbbc48fdde523d946e 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardArtifact.java
@@ -1,7 +1,7 @@
 /*
  * Sleuth Kit Data Model
  *
- * Copyright 2011-2019 Basis Technology Corp.
+ * Copyright 2011-2020 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -1228,7 +1228,10 @@ public enum ARTIFACT_TYPE implements SleuthkitVisitableItem {
 		 * Stores metadata about an object.
 		 */
 		TSK_METADATA(57, "TSK_METADATA", //NON-NLS
-				bundle.getString("BlackboardArtifact.tskMetadata.text"));
+				bundle.getString("BlackboardArtifact.tskMetadata.text")),
+		
+		TSK_GPS_TRACK(58, "TSK_GPS_TRACK",
+				bundle.getString("BlackboardArtifact.tskTrack.text"));
 
 		private final String label;
 		private final int typeId;
diff --git a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
index 93d5f4d5ab2c6bd295aba1c3355d845bfccb3b52..f448236346dcd6466fdadf4b59d6c98b73687473 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/BlackboardAttribute.java
@@ -1,7 +1,7 @@
 /*
  * Sleuth Kit Data Model
  *
- * Copyright 2011-2019 Basis Technology Corp.
+ * Copyright 2011-2020 Basis Technology Corp.
  * Contact: carrier <at> sleuthkit <dot> org
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -1371,8 +1371,20 @@ public enum ATTRIBUTE_TYPE {
 		
 		TSK_ATTACHMENTS (141, "TSK_ATTACHMENTS", 
 				bundle.getString("BlackboardAttribute.tskattachments.text"),
-				TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON);
+				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",
+			bundle.getString("BlackboardAttribute.tskgeopath.text"),
+			TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON),
+
 		;
 
 		private final int typeID;
diff --git a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
index ece4a3767815020281d967aede5b5e5a204efd53..dfcd926e960a73f339742f659befcc9decf2baee 100644
--- a/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
+++ b/bindings/java/src/org/sleuthkit/datamodel/Bundle.properties
@@ -53,6 +53,7 @@ BlackboardArtifact.tskWebCache.text=Web Cache
 BlackboardArtifact.tskClipboardContent.text=Clipboard Content
 BlackboardArtifact.tskUserContentSuspected.text=User Content Suspected
 BlackboardArtifact.tskMetadata.text=Metadata
+BlackboardArtifact.tskTrack.text=GPS Track
 BlackboardArtifact.shortDescriptionDate.text=at {0}
 BlackboardArtifact.tskAssociatedObject.text=Associated Object
 BlackboardAttribute.tskAccountType.text=Account Type
@@ -190,6 +191,9 @@ 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
 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/blackboardutils/GeoArtifactsHelper.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
index 4a19e2a4d180f3b72b3bef04bea9f852c9a63611..61e4c6daa3f6df22b1c751c7b57da64d1819cb08 100755
--- a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/GeoArtifactsHelper.java
@@ -18,18 +18,21 @@
  */
 package org.sleuthkit.datamodel.blackboardutils;
 
+import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
 import org.sleuthkit.datamodel.BlackboardArtifact;
 import org.sleuthkit.datamodel.BlackboardAttribute;
 import org.sleuthkit.datamodel.Content;
 import org.sleuthkit.datamodel.SleuthkitCase;
 import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoint.GeoTrackPoint;
 
 /**
- * Class to help ingest modules create Geolocation artifacts. 
+ * Class to help ingest modules create Geolocation artifacts.
  *
  */
 public final class GeoArtifactsHelper extends ArtifactHelperBase {
@@ -44,7 +47,40 @@ public final class GeoArtifactsHelper extends ArtifactHelperBase {
 	public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcFile) {
 		super(caseDb, moduleName, srcFile);
 	}
-	
+
+	/**
+	 * Creates and adds a TSK_GPS_TRACK artifact to the case with specified
+	 * attributes and posts the artifact to the Blackboard.
+	 *
+	 * @param trackName	Name of GPS track, not required
+	 * @param points		  GeoTrackPoints, required.
+	 *
+	 * @return	TSK_GPS_TRACK 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");
+		}
+
+		BlackboardArtifact artifact = getContent().newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK);
+		if (trackName != null) {
+			artifact.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, getModuleName(), trackName));
+		}
+
+		artifact.addAttribute(
+				new BlackboardAttribute(
+						BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_TRACKPOINTS,
+						getModuleName(),
+						GeoTrackPoints.serializePoints(points)));
+
+		getSleuthkitCase().getBlackboard().postArtifact(artifact, getModuleName());
+
+		return artifact;
+	}
+
 	/**
 	 * Adds a TSK_GPS_TRACKPOINT artifact.
 	 *
@@ -58,7 +94,7 @@ public GeoArtifactsHelper(SleuthkitCase caseDb, String moduleName, Content srcFi
 	 *
 	 * @return GPS trackpoint artifact added
 	 *
-	 * @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 addGPSTrackpoint(double latitude, double longitude,
@@ -84,7 +120,7 @@ public BlackboardArtifact addGPSTrackpoint(double latitude, double longitude,
 	 *
 	 * @return GPS trackpoint artifact added
 	 *
-	 * @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 addGPSTrackpoint(double latitude, double longitude, long timeStamp, String name, String programName,
@@ -115,5 +151,5 @@ public BlackboardArtifact addGPSTrackpoint(double latitude, double longitude, lo
 		// return the artifact
 		return gpsTrackpointArtifact;
 	}
-	
+
 }
diff --git a/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
new file mode 100755
index 0000000000000000000000000000000000000000..21474995e57360bf5102f1283424ec6ec86bcdfc
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoTrackPoints.java
@@ -0,0 +1,91 @@
+/*
+ * 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
new file mode 100755
index 0000000000000000000000000000000000000000..14fb0358d991a7a3c495c849a02361337d9d6bde
--- /dev/null
+++ b/bindings/java/src/org/sleuthkit/datamodel/blackboardutils/attributes/GeoWaypoint.java
@@ -0,0 +1,179 @@
+/*
+ * 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());
+			}
+		}
+	}
+
+}