Howdy Folks! I am back with another simple yet fun tutorial ;)

Do you know about ISS? ISS aka International Space Station is an artificial habitable satellite that orbits Earth roughly 16 times per day. In just one day, the station travels about the distance it would take to go from Earth to the moon and back! ISS is mainly used as a laboratory to perform some space-related experiments. At the time of writing this article, there are three people up on the space station right now. And another interesting fact is that, If the conditions are favorable, then you can see the ISS with your own naked eyes from Earth's surface! (I had seen it 3 times!). Sounds interesting right? But wait, how do I know where the ISS is currently flying over? How do I track it? 

That's exactly what we are going to do! Today, you are going to learn how to build a Real-Time International Space Station (ISS) Tracker using Javascript :) Hope you are ready for this new adventure...

The finished product would look something like this:

finished version

You can download the completed project from GitHub - ISS Tracker

Or you can get the final code at the end of each section too...

Prerequisites

I assume you are already familiar with basic Javascript (ES6). And you should have a decent understanding of HTML, CSS, and JSON. You should also know how to make a request to an API using fetch(). Knowledge about Promises is also expected.

But If you don't know some of them, don't worry, just continue. I will try my best to keep this tutorial simple. 

I am going to use "visual studio code" as my text editor. You are free to use whatever code editor you like. And finally, make sure you have installed Live Server by Ritwick Dey in visual studio code. If you don't have Live Server installed, then:

  • Open visual studio code
  • Goto Extensions tab (Ctrl + Shift + X)
  • Search for "Live Server" by Ritwick Dey
  • Then Install it

But wait, How we are going to build this thing?

Breaking Down the Logic

project structure breakdown

Logic is quite simple.

  • We need a map. Here I am using the Leaflet JS map. Because this is easy to set up than the Google map.
  • Inside the map, we will draw a marker. ISS image is going to be our marker.
  • Then for every 2 seconds, we will perform a GET request to the api.wheretheiss.at, which in turn will return all the information like current latitude, longitude, timezone, speed, altitude, and visibility of ISS.
  • Then simply update the position of the marker (ISS image) on the map and all the #details according to the information we get.

That's it! That's what we all want to do and trust me it's quite simple :) Now let's get started...

Initial Setups

Create a folder structure similar to below:

folder structure of our project

  • First, create the root folder which holds everything and name it as "ISS Tracker"
  • Then open this folder inside visual studio code.
  • Then directly inside this root folder, create the following:
    • index.html - this is going to hold all of our HTML.
    • styles.css - this will hold all of our CSS styles.
    • script.js - this is the most important file. This will hold all of our Javascript logic.
    • Finally, download this whole "img" folder --> img. Unzip it. Then put it directly inside our root folder. The "img" folder contains the "iss.png" image, which is going to be used as the marker on the map.

That's it. Now you should have a folder structure similar to above. Now the HTML part...

HTML

Open "index.html" and type the following:

<!DOCTYPE html>
<html>

  <head>
    <title>ISS Tracker</title>
    <link rel="stylesheet" href="styles.css" type="text/css" />
    <!-- CSS for our Map (Mandatory) -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" />
  </head>
  <body>


    <!-- Javascript for our Map (Mandatory) -->
    <!-- Make sure you put this AFTER Leaflet's CSS and before script.js -->
    <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
    <script src="script.js" type="text/javascript"></script>
  </body>

</html>

Above, besides the normal HTML code, have you noticed something else? We have two other important links here (for Leaflet CSS and Leaflet JS):

  • We linked to an external CSS stylesheet. This is mandatory. This is the Leaflet CSS file and it should be in the head section of your document.

Link to an external Leaflet CSS file

  • And at the bottom, above our "script.js" file link, we linked to an external Javascript file. This is also mandatory. This is the Leaflet Javascript file and also make sure you put this after Leaflet's CSS and before "script.js".

External link to Leaflet Javascript file

Now below the opening <body> tag, type the following:

    <div>
      <h1>ISS Tracker</h1>

      <!-- our Map goes here -->
      <div id="map-div">

      </div>

      <!-- Details about ISS -->
      <div id="details">
        <!-- Time -->
        <div>
          Time:
          <span class="time"></span>
        </div>
        <!-- Latitude -->
        <div>
          Latitude:
          <span class="latitude"></span>
        </div>
        <!-- Longitude -->
        <div>
          Longitude:
          <span class="longitude"></span>
        </div>
        <!-- Speed -->
        <div>
          Speed:
          <span class="speed"></span>
        </div>
        <!-- Altitude -->
        <div>
          Altitude:
          <span class="altitude"></span>
        </div>
        <!-- Visibility -->
        <div>
          Visibility:
          <span class="visibility"></span>
        </div>
      </div>
    </div>

We have an outer <div></div>. Inside that, there is an:

  • <h1> - holds our title.
  • <div id="map-div"> </div> - our map goes here.
  • <div id="details"> </div> - We put all other details inside this div, which we get from the API. Now, inside this div, we only had some placeholders and their corresponding text like this:

placeholder for time we get in future from API

placholder for latitude

We will later fill this up using Javascript, when we get the actual values from the API.

That's it our HTML part is done. Here is the final code for the "index.html" file. Make sure the code you typed up to this point inside the "index.html" file is exactly like the following:

<!DOCTYPE html>
<html>

  <head>
    <title>ISS Tracker</title>
    <link rel="stylesheet" href="styles.css" type="text/css" />
    <!-- CSS for our Map (Mandatory) -->
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin="" />
  </head>
  <body>

    <div>
      <h1>ISS Tracker</h1>

      <!-- our Map goes here -->
      <div id="map-div">

      </div>

      <!-- Details about ISS -->
      <div id="details">
        <!-- Time -->
        <div>
          Time:
          <span class="time"></span>
        </div>
        <!-- Latitude -->
        <div>
          Latitude:
          <span class="latitude"></span>
        </div>
        <!-- Longitude -->
        <div>
          Longitude:
          <span class="longitude"></span>
        </div>
        <!-- Speed -->
        <div>
          Speed:
          <span class="speed"></span>
        </div>
        <!-- Altitude -->
        <div>
          Altitude:
          <span class="altitude"></span>
        </div>
        <!-- Visibility -->
        <div>
          Visibility:
          <span class="visibility"></span>
        </div>
      </div>
    </div>

    <!-- Javascript for our Map (Mandatory) -->
    <!-- Make sure you put this AFTER Leaflet's CSS and before script.js -->
    <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
    <script src="script.js" type="text/javascript"></script>
  </body>

</html>

Now Open it with Live Server by right-clicking on the "index.html" file (inside visual studio code), then scroll down and click on the "Open with Live Server" option and take a look at it at the browser. You can't see any styling now. Because we only linked the "styles.css" file but didn't write any CSS styles. So now let's do that. Let's add some styles...

CSS

You can either copy or type these styles. Because CSS is not our main focus here.

Open the "styles.css" file you created and type or copy the following:

* {
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

/* body */
body {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  background: linear-gradient(to right, #00c6ff, #0072ff);
  font-family: monospace;
  font-size: 0.8rem;
  text-align: center;
}

/* h1 */
h1 {
  margin-bottom: 30px;
  font-size: 3rem;
  color: #fff;
}

/* #map */
/* this is mandatory */
#map-div {
  width: 800px;
  height: 350px;
  box-shadow: 7px -2px 8px -1px rgba(0,0,0,0.7);
  border-top-right-radius: 3px;
  border-top-left-radius: 3px;
}

/* #details */
#details {
  background-color: #2B32B2;
  padding: 10px;
  color: #fff;
  display: flex;
  width: 800px;
  max-width: 800px;
  box-shadow: 7px -2px 8px -1px rgba(0,0,0,0.7);
  border-bottom-right-radius: 3px;
  border-bottom-left-radius: 3px;
}

/* all div inside #details */
#details div {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}

/* all span */
span {
  font-size: 1rem;
  margin-top: 10px;
}

If you take a look at the browser now, you can see the page with the styles. But no map, no pieces of information, no space station. So let's get them all using Javascript...

Javascript

Open the "script.js" file and copy the following comments first, because they will help you to put the codes in the proper places. Yes, you can put your code wherever you like, but I like to do everything in the order.

So copy the following inside "script.js" file:

// grab all elements needed


/* default latitude and longitude. Here lat and long is for "London" */


// set iss.png image as Marker


// drawing map interface on #map-div


// add map tiles from Mapbox's Static Tiles API
/* Make sure you replaced 'your.mapbox.access.token' with your Mapbox API accessToken, otherwise the Map willnot show anything */
/* to get Mapbox API accessToken --> https://account.mapbox.com/access-tokens/ (do Signup or SignIn) */


// adding the Marker to map


// findISS() function definition


// updateISS() function definition


/* call findISS() initially to immediately set the ISS location */


// call findISS() for every 2 seconds

Now the next thing we are going to do is to grab a reference for all the necessary HTML elements from the DOM.

Grabbing the necessary elements

The elements we are going to grab are the <span> elements with the class .latitude, .longitude, .time, .speed, .altitude, and .visibility . Because these are the places where we will be displaying all the details we get from the API.

Type the following code at the very top under this comment - '// grab all elements needed':

let latitudeText = document.querySelector('.latitude');
let longitudeText = document.querySelector('.longitude');
let timeText = document.querySelector('.time');
let speedText = document.querySelector('.speed');
let altitudeText = document.querySelector('.altitude');
let visibilityText = document.querySelector('.visibility');

Setting Up the Map

Next, To set up our map, we need a default latitude, longitude, and zoom level. Let's use London's lat and long and a zoom level of 8.

Type this below '/* default latitude and longitude. Here lat and long is for "London" */':

let lat = 51.505;
let long = -0.09;
let zoomLevel = 8;

Now let's create our map. As I told earlier, we are going to set up a Leaflet JS map.

Type this below '// drawing map interface on #map-div':

const map = L.map('map-div').setView([lat, long], zoomLevel);
  • L.map() creates a Leaftlet map. It expects an HTML element's id as an argument.  Here we are giving HTML element with the id map-div.
  • Then we use .setView() to set the map's view. Note that the setView() call will return the map object and here we are storing it on a map constant.

Now you will see something like this:

empty map

Hey, But where is the map? The map is there, but we didn't set up any "map tiles" (texture). That's why nothing is showing up on the map. The map tiles are what act as the texture above the map. You can set up a street view map tile, or a satellite view map tile as you like.

Map Tiles and Mapbox API

The Leaftlet doesn't provide map tiles. It advises us to use Mapbox's Static Tiles API.

In order to use map tiles from Mapbox, you must also request an access token. The process is simple.

To get your Mapbox Static Tiles API "accessToken":

  • Click on the above "request an access token" link.
  • If you already had an account on Mapbox, then Sign in. Else Sign up.
  • After going through all the process of Signup or SignIn, you will be able to see your API "accessToken" under "Default public token" on the main page of Mapbox.

 

accessToken

  • Now copy your "accessToken" aka "Default public token" to your clipboard.

Next, let's continue coding...

Type the following code below these comments:

type the code below this comment

L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
  attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
  maxZoom: 18,
  id: 'mapbox/streets-v11',
  tileSize: 512,
  zoomOffset: -1,
  accessToken: 'your.mapbox.access.token'
}).addTo(map);
  • Creating a tile layer usually involves setting the URL template for the tile images. They contain both URLs and placeholders.
  • Then as a Javascript object, we give the values for the placeholders and tiles. Here the values are:
    •  attribution text - attribution is obligatory as per the copyright notice.
    • the maxZoom level of the layer.
    • then id: 'mapbox/streets-v11'.
    • specify the tileSize: 512.
    • then zoomOffset of -1
    • Finally, our "accessToken". Replace 'your.mapbox.access.token' with your Mapbox API "accessToken" you earlier copied.

replace token

access token

my access token censored lol

Now your map would work awesome :)  Take a look at it in the browser.

output with map and map tiles

You can try replacing mapbox/streets-v11 with mapbox/satellite-v9, and see what happens.

ISS Marker

Now Let's add the marker. Our "iss.png" image is going to be our marker.

So let's first define it and then render it on the map.

Type the following code below '// set iss.png image as Marker':

const icon = L.icon({
  iconUrl: './img/iss.png',
  iconSize: [90, 45],
  iconAnchor: [25, 94],
  popupAnchor: [20, -86]
});

Then below this '// adding the Marker to map' comment, type this:

const marker = L.marker([lat, long], { icon: icon }).addTo(map);

Woohoo! Our ISS Marker will be now visible on top of our default location London:

map with ISS marker

Let's Move it, move it, move it...

Now make an API request to api.wheretheiss.at and get the current position and other details about ISS. Let's make a function to do this task.

Type this below '// findISS() function definition':

function findISS() {
  fetch("https://api.wheretheiss.at/v1/satellites/25544")
  .then(response => response.json())
  .then(data => {
    lat = data.latitude.toFixed(2);
    long = data.longitude.toFixed(2);
    // convert seconds to milliseconds, then to UTC format
    const timestamp = new Date(data.timestamp * 1000).toUTCString();
    const speed = data.velocity.toFixed(2);
    const altitude = data.altitude.toFixed(2);
    const visibility = data.visibility;

    // call updateISS() function to update things
    updateISS(lat, long, timestamp, speed, altitude, visibility);
  })
  .catch(e => console.log(e));
}

The explanation for the above code:

  • Inside our findISS() function, we use fetch() to perform API request. The fetch() will take some time to finish. Because, it would take some time to perform the API requests and get the response back, right? We are dealing with Javascript Promises here.
  • So after the request has been finished, the callback function inside the first .then() will get activated. I used the ES6 fat arrow function as the callback function. Note one thing, that our response will not be in the JSON format. So after converting it into JSON by doing response.json() we then return it to the next .then().
  • Inside the next .then() we accept the converted JSON and extract the necessary data. Our JSON object will look something like this:

json response

  • And we don't need all of the data inside JSON. We only need the following:

needed infos from JSON

  • That's what we are doing inside the callback function in the second .then().
  • After extracting all the needed data, we then call the updateISS() function which takes all these data and updates the DOM according to that.
  • Finally, we have a .catch() statement here. This will handle the error if something goes wrong and just console.log() the error.

We called the updateISS() function but never created it yet, so let's do that.

Type the following code below '// updateISS() function definition':

function updateISS(lat, long, timestamp, speed, altitude, visibility) {
  // updates Marker's lat and long on map
  marker.setLatLng([lat, long]);
  // updates map view according to Marker's new position
  map.setView([lat, long]);
  // updates other element's value
  latitudeText.innerText = lat;
  longitudeText.innerText = long;
  timeText.innerText = timestamp;
  speedText.innerText = `${speed} km/hr`;
  altitudeText.innerText = `${altitude} km`;
  visibilityText.innerText = visibility;
}
  • This function updates marker's latitude and longitude on the map.
  • Then updates the map's view according to the marker's new position.
  • Finally, it sets the .innerText of other HTML element's inside the #details div with the new values we got.

Now call the findISS() function by typing the following code below '/* call findISS() initially to immediately set the ISS location */':

findISS();

 But this will move our ISS only once. To move it once for every 2 seconds, type the following code below '// call findISS() for every 2 seconds':

setInterval(findISS, 2000);

Did you notice something? We had reached the end!!!

Now if you take a look at the browser, you will see your Real-Time ISS Tracker in action!! Go check it out.

If you are only seeing blue color inside your map, fear not, Your ISS is now flying over oceans. So scroll back with your mouse to zoom out of the map then you will start seeing other places!

And one final side note here, the position of our ISS will not be 100% accurate. Because we are setting the ISS position using the information we get from the API. And we can't expect the API to return 100% accurate details. So if you find some other API that returns a more accurate position than this, then feel free to use that. And if you are changing the API, then also remember to extract the details accordingly.

Here is the final code for the "script.js" file. Make sure the code you typed up to this point inside the "script.js" is exactly like the following:

// grab all elements needed
let latitudeText = document.querySelector('.latitude');
let longitudeText = document.querySelector('.longitude');
let timeText = document.querySelector('.time');
let speedText = document.querySelector('.speed');
let altitudeText = document.querySelector('.altitude');
let visibilityText = document.querySelector('.visibility');

/* default latitude and longitude. Here lat and long is for "London" */
let lat = 51.505;
let long = -0.09;
let zoomLevel = 8;

// set iss.png image as Marker
const icon = L.icon({
  iconUrl: './img/iss.png',
  iconSize: [90, 45],
  iconAnchor: [25, 94],
  popupAnchor: [20, -86]
});

// drawing map interface on #map-div
const map = L.map('map-div').setView([lat, long], zoomLevel);

// add map tiles from Mapbox's Static Tiles API
/* Make sure you replaced 'your.mapbox.access.token' with your Mapbox API accessToken, otherwise the Map willnot show anything */
/* to get Mapbox API accessToken --> https://account.mapbox.com/access-tokens/ (do Signup or SignIn) */
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
  attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
  maxZoom: 18,
  id: 'mapbox/streets-v11',
  tileSize: 512,
  zoomOffset: -1,
  accessToken: 'pk.eyJ1IjoiYWsyOCIsImEiOiJjazl6YTJrZ2gwN2Z6M21wYWhqbHV1dXFhIn0.2s7LanKmroqsWPY2q2pQfQ'
}).addTo(map);

// adding the Marker to map
const marker = L.marker([lat, long], { icon: icon }).addTo(map);

// findISS() function definition
function findISS() {
  fetch("https://api.wheretheiss.at/v1/satellites/25544")
  .then(response => response.json())
  .then(data => {
    lat = data.latitude.toFixed(2);
    long = data.longitude.toFixed(2);
    // convert seconds to milliseconds, then to UTC format
    const timestamp = new Date(data.timestamp * 1000).toUTCString();
    const speed = data.velocity.toFixed(2);
    const altitude = data.altitude.toFixed(2);
    const visibility = data.visibility;

    // call updateISS() function to update things
    updateISS(lat, long, timestamp, speed, altitude, visibility);
  })
  .catch(e => console.log(e));
}

// updateISS() function definition
function updateISS(lat, long, timestamp, speed, altitude, visibility) {
  // updates Marker's lat and long on map
  marker.setLatLng([lat, long]);
  // updates map view according to Marker's new position
  map.setView([lat, long]);
  // updates other element's value
  latitudeText.innerText = lat;
  longitudeText.innerText = long;
  timeText.innerText = timestamp;
  speedText.innerText = `${speed} km/hr`;
  altitudeText.innerText = `${altitude} km`;
  visibilityText.innerText = visibility;
}

/* call findISS() initially to immediately set the ISS location */
findISS();

// call findISS() for every 2 seconds
setInterval(findISS, 2000);

Wrapping Up

I hope you enjoyed this tutorial. If you had any doubts, then please comment them below. Thank you ;)