4 Steps GPS Tracking System With PHP and Javascript

INTRODUCTION

WEB TRACKING MAGIC

Welcome to a tutorial on how to create a GPS tracking system with PHP and Javascript. Once upon a time in the dark ages of the Internet, GPS is hardly known, and people really didn’t bother about it very much. It was not until the invention of smartphones (and the GPS units inside them), that some smart monkeys realize how useful it is.

Tracking where the vehicle is at, when the next bus is coming, where the order is it, or just making sure that people have actually delivered to the right place. The possibilities are endless, and it is not just limited to the native apps – It is also possible to create web apps using PHP and Javascript. Read on to find out how.

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

 

 

PREAMBLE

DOWNLOAD & NOTES

First, here is the download link to the source code as promised.

 

SOURCE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

FOLDERS

Here is a quick introduction to how the folders are being organized in the zip file.

  • lib Where we store all the PHP library files.
  • SQL All the necessary SQL files to build the database. It can be safely deleted after you have imported all of them.

 

QUICK NOTES

  • Download and unzip into a folder.
  • Create a database and import all the files in the sql folder.
  • Change the database settings in lib/2a-config.php to your own.
  • 3-track.html is the client/rider tracking page, 4-admin.html is the demo admin page.
  • Missing files and AJAX errors?
    • Check that the file paths are correct in 3-track.html and 4-admin.html – All the <script> and <link> tags plus AJAX URL xhr.open('POST', "PATH/2c-ajax-track.php")
    • Captain obvious – AJAX will not work with file://, use http:// instead.
    • Open up the developer’s console (F12 in major browsers) and see the error message.

If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

WHERE’S THE MAP INTEGRATION!?

Getting way too many comments on “I need help with map integration”. But this tutorial will only cover the “technical GPS part”, not the map integration. Why?

  • Because there are more than a dozen of map services out there.
  • Everyone uses a different service, everyone has a different API.
  • Most good map services are not free these days – You have to pay to use their API.
  • APIs change over time, and it will take long tutorials to walk through every single one of those.
  • Most map services already have their own tutorials and documentation.
  • Most paid map services will come with technical support anyway.

In short, it is impossible to cover all the map services in this tutorial. It is impossible to help everyone, one-by-one. So yep, sorry if I don’t approve or answer your questions regarding map integration.

I simply cannot do projects and consultations free-of-charge, this one is strictly your own homework – But I have done another quick tutorial on MapBox though, link in the extra section below.

 

PRELUDE

OVERVIEW & ASSUMPTIONS

Before we go into the code, let us start with an overview of the entire system so that you don’t get lost. Also, some of my assumptions so that you know what to expect from this guide.

 

SYSTEM OVERVIEW

There are a lot of ways to use a GPS tracking system, and this guide will go through one of the most common ways to use it – Tracking various delivery riders. Not because we want to be control freaks, but to let customers know where their orders are at, and to properly compensate the riders for their mileage. There are 4 parts to this system:

  • Database – To hold the last-known locations of the riders.
  • Tracker – An app installed on the riders’ smartphone, or a website that they can visit. This one will send their GPS coordinates to the server.
  • Endpoint – Server-side PHP scripts to receive the coordinates, put them into the database, and to retrieve them from the database.
  • Admin – A page to show the current locations of the riders.

 

ASSUMPTIONS

Some of you guys should already have an existing project, so we will not reinvent another user system nor admin panel here. This guide will be purely touching on GPS tracking only. Also, this will be a barebones system to show you what is possible with web technology – No free lunches will be given to people who are working on paid projects. 😆

I shall also assume that most of you guys are already good enough code ninjas, comfortable with PHP, HTML, CSS, Javascript, AJAX, JSON, and all the concepts. We will not go into the tiny boring little details such as “what is GPS” in this guide.

 

 

STEP 1

THE DATABASE

Now that we are done with the overview of the system, let us start building the system by laying the foundation – The database.

 

LAST KNOWN LOCATION

sql/1a-track.sql
CREATE TABLE `gps_track` (
  `rider_id` int(11) NOT NULL,
  `track_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `track_lng` decimal(11,7) NOT NULL,
  `track_lat` decimal(11,7) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `gps_track`
  ADD PRIMARY KEY (`rider_id`),
  ADD KEY `track_time` (`track_time`);
COMMIT;
FieldDescription
rider_idPrimary key, the rider ID. This could also be the ID of whatever you want to track.
track_timeTime the rider last “checked in”.
track_lngLongitude.
track_latLatitude.

Yep, that is all we need, to store the last-known locations of the riders.

 

RIDER TABLE

sql/1b-riders.sql
CREATE TABLE `riders` (
  `rider_id` int(11) NOT NULL,
  `rider_name` varchar(255) NOT NULL,
  `rider_tel` varchar(64) NOT NULL,
  `rider_email` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `riders`
  ADD PRIMARY KEY (`rider_id`),
  ADD UNIQUE KEY `rider_email` (`rider_email`);

ALTER TABLE `riders`
  MODIFY `rider_id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT; 

This is not exactly used in the system. But for the sake of completeness of this tutorial, here is an example rider database that one may have.

FieldDescription
rider_idPrimary key and auto-increment, The rider ID.
rider_nameThe rider’s name.
rider_telThe rider’s telephone number.
rider_emailThe rider’s email address.

 

 

STEP 2

ENDPOINT

Next, we will establish another piece of the foundations – The endpoint of the GPS tracking system which will receive and serve the GPS coordinates.

 

CONFIG FILE

lib/2a-config.php
<?php
// MUTE NOTICES
error_reporting(E_ALL & ~E_NOTICE);

// DATABASE SETTINGS - CHANGE THESE TO YOUR OWN
define('DB_HOST', 'localhost');
define('DB_NAME', 'test');
define('DB_CHARSET', 'utf8');
define('DB_USER', 'root');
define('DB_PASSWORD', '');

// AUTO FILE PATH
define('PATH_LIB', __DIR__ . DIRECTORY_SEPARATOR);
?>

The very first script that we will create is the config file, a place for us to put all the settings into. Please remember to change the database settings to your own.

 

GPS TRACKING LIBRARY

lib/2b-lib-track.php
<?php
class Track {
  /* [DATABASE HELPER FUNCTIONS] */
  protected $pdo = null;
  protected $stmt = null;
  public $lastID = null;

  function __construct () {
  // __construct() : connect to the database
  // PARAM : DB_HOST, DB_CHARSET, DB_NAME, DB_USER, DB_PASSWORD

    // ATTEMPT CONNECT
    try {
      $str = "mysql:host=" . DB_HOST . ";charset=" . DB_CHARSET;
      if (defined('DB_NAME')) { $str .= ";dbname=" . DB_NAME; }
      $this->pdo = new PDO(
        $str, DB_USER, DB_PASSWORD, [
          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
          PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          PDO::ATTR_EMULATE_PREPARES => false
        ]
      );
    }

    // ERROR - CRITICAL STOP - THROW ERROR MESSAGE
    catch (Exception $ex) {
      print_r($ex);
      die();
    }
  }

  function __destruct () {
  // __destruct() : close connection when done

    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }

  function exec ($sql, $data=null) {
  // exec() : run insert, replace, update, delete query
  // PARAM $sql : SQL query
  //       $data : array of data

    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($data);
      $this->lastID = $this->pdo->lastInsertId();
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    $this->stmt = null;
    return true;
  }

  function fetchAll ($sql, $cond=null, $key=null, $value=null) {
  // fetchAll() : perform select query (multiple rows expected)
  // PARAM $sql : SQL query
  //       $cond : array of conditions
  //       $key : sort in this $key=>data order, optional
  //       $value : $key must be provided. If string provided, sort in $key=>$value order. If function provided, will be a custom sort.

    $result = [];
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($cond);
      // Sort in given order
      if (isset($key)) {
        if (isset($value)) {
          if (is_callable($value)) {
            while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
              $result[$row[$key]] = $value($row);
            }
          } else {
            while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
              $result[$row[$key]] = $row[$value];
            }
          }
        } else {
          while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
            $result[$row[$key]] = $row;
          }
        }
      }
      // No key-value sort order
      else {
        $result = $this->stmt->fetchAll();
      }
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    // Return result
    $this->stmt = null;
    return count($result)==0 ? false : $result ;
  }

  function fetch ($sql, $cond=null, $sort=null) {
  // fetch() : perform select query (single row expected)
  //           returns an array of column => value
  // PARAM $sql : SQL query
  //       $cond : array of conditions
  //       $sort : custom sort function

    $result = [];
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($cond);
      if (is_callable($sort)) {
        while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
          $result = $sort($row);
        }
      } else {
        while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
          $result = $row;
        }
      }
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    // Return result
    $this->stmt = null;
    return count($result)==0 ? false : $result ;
  }

  /* [TRACKING FUNCTIONS] */
  function update ($id, $lng, $lat) {
  // update() : update rider coordinates
  // PARAM $id : rider ID
  //       $lng : longitude
  //       $lat : latitude

    return $this->exec(
      "REPLACE INTO `gps_track` (`rider_id`, `track_time`, `track_lng`, `track_lat`) VALUES (?, ?, ?, ?)",
      [$id, date("Y-m-d H:i:s"), $lng, $lat]
    );
  }

  function get ($id) {
  // get() : get rider coordinates
  // PARAM $id : rider ID

    return $this->fetch(
      "SELECT * FROM `gps_track` WHERE `rider_id`=?",
      [$id]
    );
  }

  function getAll () {
  // getAll() : get all the rider locations
  // !! You might want to implement an "on active duty" flag in your own system
  // !! Just so that only the relevant riders are extracted

    return $this->fetchAll(
      "SELECT * FROM `gps_track`", null, "rider_id"
    );
  }
}
?>

This one looks intimidating at first, but keep calm and analyze – There are only 2 parts to the tracking library.

  • The top half is database functions that will do all the SQL heavy lifting work.
  • The bottom half is the actual tracking related functions.
Database Helper Functions
FunctionDescription
__constructThe constructor, automatically connects to the database when the object is created.
__destructThe destructor, automatically closes the database connection when the object is destroyed.
execRuns a single insert, replace, update, or delete query.
fetchAllRun a select query. Returns an associative array of with multiple rows of results.
fetchRun a select query. Returns an associative array of column > value.
Tracking Related Functions
FunctionDescription
updateUpdate the location of the given rider.
getGet the last known location of the given rider.
getAllGet all rider locations.

 

 

AJAX HANDLER (OR ENDPOINT)

2c-ajax-track.php
<?php
// INIT
require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "2a-config.php";
require PATH_LIB . "2b-lib-track.php";
$trackLib = new Track();

// HANDLE REQUESTS
// !! You might want to restrict access
// !! Implement your own user sessions and security checks
switch ($_POST['req']) {
  // INVALID REQUEST
  default:
    echo json_encode([
      "status" => 0,
      "message" => "Invalid request"
    ]);
    break;

  // UPDATE RIDER LOCATION
  case "update":
    $pass = $trackLib->update($_POST['rider_id'], $_POST['lng'], $_POST['lat']);
    echo json_encode([
      "status" => $pass ? 1 : 0,
      "message" => $pass ? "OK" : $trackLib->error
    ]);
    break;

  // GET RIDER LOCATION
  case "get":
    $location = $trackLib->get($_POST['rider_id']);
    echo json_encode([
      "status" => is_array($location) ? 1 : 0,
      "message" => $location
    ]);
    break;

  // GET ALL RIDER LOCATIONS
  case "getAll":
    $location = $trackLib->getAll();
    echo json_encode([
      "status" => is_array($location) ? 1 : 0,
      "message" => $location
    ]);
    break;
}

Lastly, here is the actual endpoint, which simply uses the libraries that we have previously created. How it works is very simple. We only need to pass in a $_POST['req'] to specify what we need, followed by the required parameters.

RequestDescription
updateUpdate the location of the given rider. Parameters:

  • rider_id
  • lng
  • lat
getGet the last known location of a given rider. Parameters:

  • rider_id
getAllGet all the last-known locations of the riders.

 

STEP 3

TRACKER

Now that we have all the foundations ready, we have to build a simple tracker that will update the server of the rider’s current locations.

 

THE TRACKER SCRIPT

3-track.html
<!DOCTYPE html>
<html>
  <head>
    <title>
      Javascript Geolocation Tracking Demo
    </title>
    <script>
      var track = {
        display : null, // Holder for the <p> element, for visual feedback
        rider : 999, // Rider ID - Hardcode this somewhere in your own system session or in the web app
        delay : 20000, // Delay in between each position update, in milliseconds
        timer : null, // Holder for the interval object
        update : function () {
        // track.update() : update server of current location

          navigator.geolocation.getCurrentPosition(function (pos) {
            // AJAX DATA
            var data = new FormData();
            data.append('req', 'update');
            data.append('rider_id', track.rider);
            data.append('lat', pos.coords.latitude);
            data.append('lng', pos.coords.longitude);

            // AJAX
            var xhr = new XMLHttpRequest();
            xhr.open('POST', "2c-ajax-track.php", true);
            xhr.onload = function () {
              var res = JSON.parse(this.response);
              // OK
              if (res.status==1) {
                track.display.innerHTML = "Lat: " + pos.coords.latitude + " Lng: " + pos.coords.longitude;
              }
              // ERROR
              else {
                track.display.innerHTML = res.message;
              }
            };
            xhr.send(data);
          });
        }
      };

      // INIT ON PAGE LOAD
      window.addEventListener("load", function(){
        track.display = document.getElementById("display");
        if (navigator.geolocation) {
          // Set on an interval so that you don't drain the smartphone battery
          // Nor kill the server for the matter
          track.update();
          setInterval(track.update, track.delay);
        } else {
          track.display.innerHTML = "Geolocation is not supported by your browser!";
        }
      });
    </script>
  </head>
  <body>
    <p id="display"></p>
    <p>
      This is a demo tracking page.
      You will normally create a rider login page, or convert this into a webapp.
      For those who do not know - Check out Apache Cordova and Ionic.
    </p>
  </body>
</html>

Yep, that is the gist of it. Of course, in your own real-life project, you will want to have a proper login system and identify your riders. Either that or you always have the option to create an Android/iOS app.

 

 

STEP 4

ADMIN

The final piece of the puzzle is to have an admin page that will extract the locations of the riders and show them on a map.

 

THE CENTRAL CONTROL

4-admin.html
<!DOCTYPE html>
<html>
  <head>
    <title>
      PHP Javascript Tracking Demo
    </title>
    <style>
    #map {
      width: 100%;
      height: 300px;
      background: #f2f2f2;
    }
    </style>
    <script>
      var track = {
        map : null, // Holder for HTML map element
        delay : 50000, // Delay in between each location refresh

        show : function () {
        // track.show() : get location data from server and update map
        // Sadly, Google Maps API is not free.
        // Check out more on their website if you want to use their maps on your app.
        // https://developers.google.com/maps/documentation/

          // AJAX DATA
          var data = new FormData();
          data.append('req', 'getAll');

          // AJAX
          var xhr = new XMLHttpRequest();
          xhr.open('POST', "2c-ajax-track.php", true);
          xhr.onload = function () {
            var res = JSON.parse(this.response);
            // OK
            // @TODO - UPDATE YOUR MAP PINS OR WHATEVER CONTROLS YOU WANT
            if (res.status==1) {
              map.innerHTML = "";
              for (var rid in res.message) {
                var rider = res.message[rid];
                // rider.track_lng
                // rider.track_lat
                // rider.track_time
                var dummy = document.createElement("div");
                dummy.innerHTML = "Rider ID " + rid + " Lng " + rider.track_lng + " Lat " + rider.track_lat + " Updated " + rider.track_time;
                map.appendChild(dummy);
              }
            }
            // ERROR
            else {
              track.map.innerHTML = res.message;
            }
          };
          xhr.send(data);
        }
      };

      window.addEventListener("load", function(){
        track.map = document.getElementById("map");
        track.show();
        setInterval(track.show, track.delay);
      });
    </script>
  </head>
  <body>
    <div id="map"></div>
  </body>
</html>

So where the heck did the map go? Well, it’s all up to your own integration and decision – Google Maps, Apple Maps, Here Maps, etc…

 

EXTRA

USEFUL BITS & LINKS

That’s all for the code, and here are a few small extras and links that may be useful to you.

 

LIMITATIONS & REAL-TIME TRACKING?

Hey, this is not a real-time system! Of course, it is not. While it is possible to do so, we have to consider the technical limitations, and is it worth doing.

  • Firstly, real-time will drain out the smartphone batteries fast.
  • Secondly, you will need a powerful network infrastructure to support a lot of “always-on” socket connections.
  • Lastly, a very robust server system that is capable of crunching real-time data.

So aye… If someone is willing to shell out that kind of money and think that it is beneficial for their business… Then why not?

 

LINKS & REFERENCES

Here’s a tutorial that I did on MapBox if you are interested.

How To Get GPS Coordinates & Generate Map In Javascript

 

 

EXTRA

VIDEO TUTORIAL

For you guys who want more, here’s the video tutorial, and shameless self-promotion – Subscribe to the Code Boxx YouTube channel for more!

 

YOUTUBE TUTORIAL

 

CLOSING

WHAT’S NEXT?

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

12 thoughts on “4 Steps GPS Tracking System With PHP and Javascript”

    1. Not too sure if leaving an email to invite spam is a good idea… But I will respect that.

      Anyway, go through the entire tutorial and what you are looking for is already there.

  1. Thank you very much you’ve been so helpful
    i have to do a symfony project about geo-tracking application which include dealing with alerts like geofencing, eco-driving and fuel saving.
    if you make a tutorial about these issues that would be very kind of you and thank you again sharing the knowledge is better than sharing monney.

    1. Edit – OpenStreetMap does not provide “direct end-user service”. Look for other map providers such as Mapbox and Thnuderforest that uses maps from OpenStreetMap.

    1. That is a very difficult question, all already answered in the “quick start” section. If you are really new, please do some of your own research and play around with the basic server management stuff first – Set up a simple HTML page, create a database, import dummy database, etc…

Leave a Comment

Your email address will not be published. Required fields are marked *