IIR API Example
Fetching and Processing Data from the API
Visualize NGL Fractionator Shutdowns


This example shows how to fetch and process data from mutiple IIR API calls to create a visualization using Pure JavaScript along with the Google Maps API.

Live Demo

What to expect from the final output

Basic Initialization

Settings up the Document

In order to get the application working correctly, the document body needs 4 main elements:

  1. An div element to display the Map
  2. An empty table element to display the Data Table
  3. A script element to include the Google Maps Library
  4. A script element for all of the code needed to fetch and process data.

The first 2 elements need to have a unique id for the JavaScript selectors to point to the correct elements.

The table element should have both the thead and tbody sections that will be populated through the code.

The first script element that include the Google Maps Library will require an API key to from the Google Developer Console. You can find more information about Google Maps API Keys here.

The last script element is where all of the code will go to fetch and process the data from IIR API. For the sake of this demo, the script will be included in the main HTML document.

HTML:
<html>
  <head>
    <title>IIR API Demo</title>
  </head>
  <body>
    <div id="map"></div>
    <table id="table">
      <thead></thead>
      <tbody></tbody>
    </table>
    <script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_API_KEY]"></script>
    <script>
      // The magic goes here
    </script>
  </body>
</html>
Initializing the Map

To get the Map to display and be able to interact with it, there needs to be 3 items:

  1. A vairable that will store the map instance
  2. A function declared to initialize the map
  3. A call the the function to get the map to display

This code will be placed withing the script tags above on line 13.

The variable that stores the map is initially just a placeholder and will be set by the initialization function. This variable will continue to be used throughout the script to reference and manipulate the map.

The declared function initMap will draw the map in the element with the id demo-map with the defined center and zoom properties that result in a global view. You can find complete documentation of the Google Maps Initialization here.

Finally, initMap() is called to execute the declared function in the previous step and will result in the map being drawn into the supplied element.

JavaScript:
// UI variables
let map;

const initMap = () => {
  map = new google.maps.Map(document.getElementById('map'), {
    center: { lat: 26.422, lng: -14.921 },
    zoom: 1,
  });
};

initMap();

Get Initial Data

Making the First API Call

To make a successful API call, the following 3 items are required:

  1. An bearer token to authorize the API call
  2. An API endpoint of a specific data set
  3. A set of query parameters to identify a subset of the data

The bearer token is required for all API calls. It is passed with the headers: Authorization prameter. To generate or learn more about IIR Bearer tokens, take a look at the documentation here.

This example is going to query the offlineevents/summary endpoint to get a list of summarized offline events based on the query parameters sent to the endpoint.

The query parameters for this example include 4 types:

  1. eventKind
  2. eventStatusDesc
  3. unitTypeID
  4. industryCode

In the code provided, the API variables are added to the top of the script to hold the lengthy strings needed to make successful API calls.

A new function called getInitialData is added to fetch the first set of data needed. fetch() is used to make a POST request to the defined offlineEventsURL endpoint. When the fetch() completes its call, it returns a response to the .then() method which will convert the response to JSON and pass it to the next method.

The last .then() method in the chain will be where the code for processing the initial data will go.

The getInitialData() function gets called at the end of the initMap() function to keep the order of operations intact.

Note: The new JavaScript additions are added above initMap().

JavaScript:
// API variables
const barerToken = 'YOUR_BEARER_TOKEN',
      offlineEventsUrl = 'https://api.industrialinfo.com/idb/v1.1/offlineevents/summary?',
      offlineQueryParameters = 'eventKind=S&eventStatusDesc=Ongoing&unitTypeId=00203&industryCode=04';

// UI variables
let map;

const initMap = () => {
  ...

  getInitialData();
};

const getInitialData = () => {
  fetch(offlineEventsURL + offlineQueryParameters, {
    method: 'POST',
    headers: {
    Authorization: 'Bearer ' + bearerToken,
    },
    'Content-Type': 'application/json',
  })
  .then((response) => {
    return response.json()
  })
  .then((json) => {
    // process the JSON data response
  })
}

initMap();

Processing Initial Data

Preparing data for the Data Table

Now that the data has returned from the first fetch call, the resulting JSON has 2 main sections:

  1. The top level summary of the results
  2. The list of data requested from the previous step

The top level summary has information such as the totla result count, the returned result count, and the offset (used for pagination).

For this example, the list of data is all that will be used. Each API endpoint will have a different parameter for the returned data set. Since Offline Events was requested, the parameter is offlineEvents.

In the code provided, a new variable is added to identify the Data Table element and a new function called displayTableData is added to process the data and display in the table. It expects 1 parameter to be supplied: data

This function adds a table row (tr) with table header cells (th) to the inside of the empty thead of the table element.

Then, the function loops through the JSON data using forEach and adds table rows (tr) with table data cells (td) to the inside of the empty tbody of the table element.

The tbody loop uses template literals to access the information in each object of the returned dataset, eg: ${event.unitName}.

The displayTableData() function gets called at the end of the final .then() method of getInitialData() function to keep the order of operations intact.

Note: The new JavaScript additions are added above initMap().

Returned JSON:
{
  limit: 100,
  offlineEvents: Array(3) [ {…}, {…}, {…} ],
  offset: 0,
  resultCount: 3,
  totalCount: 3
}
JavaScript:
// UI variables
let map,
  table = document.getElementById('table');

const getInitialData = () => {
  fetch(...)
  .then(...)
  .then((json) => {
    displayTableData(json.offlineEvents);
  });
}

const displayTableData = (data) => {
  // add table head row
  table.tHead.innerHTML = `
  <tr>
    <th>Unit Name</th>
    <th>Unit ID</th>
    <th>Offline Capacity</th>
    <th>Event Type</th>
  </tr>`;

  // add table body rows
  table.tBodies[0].innerHTML = '';
  data.forEach((event) => {
    table.tBodies[0].innerHTML += `
    <tr>
      <td>${event.unitName}</td>
      <td>${event.unitId}</td>
      <td>${event.offlineCapacity.capacityOffline} ${event.offlineCapacity.uom}</td>
      <td>${event.eventType}</td>
    </tr>`;
  });
}

initMap();

Get secondary Data

Making the Second API Call

Same as the first API call, the following 3 items are required: the bearer token, an API endpoint and a set of query parameters

In the code provided, a new API variable is added to the top of the script to hold the endpoint for the plant details call.

Two new functions are added. The first one, extractPlantIDs, is used to create an array of unique plant IDs based on a the data set provided to it. The second one, getPlantData, preforms similar to the getInitialData function above.

The only difference between the functions extractPlantIDs and getPlantData, is that the query parameter string is built within the function of getPlantData by joining the array of unique plant IDs.

The getPlantData() function gets called at the end of the final .then() method of getInitialData() function to keep the order of operations intact.

Note: The new JavaScript additions are added above initMap().

JavaScript:
// API variables
const ...,
      plantDetailsUrl = 'https://api.industrialinfo.com/idb/v1.1/plants/detail?';

const getInitialData = () => {
  fetch(...)
  .then(...)
  .then((json) => {
    ...
    getPlantData(extractPlantIDs(json.offlineEvents));
  });
}

const extractPlantIDs = (data) => {
  return [
    ...new Set(
      data.map((result) => {
        return result.plantId
      })
    )
  ];
}

const getPlantData = (data) => {
  const plantIdQueryString = 'plantId=' + data.join('&plantId=')

  fetch(plantDetailsURL + plantIdQueryString, {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + bearerToken,
    },
      'Content-Type': 'application/json',
  })
  .then((response) => {
    return response.json()
  })
  .then((json) => {
    // process plant details response
  });
}

initMap();

Display Data

Putting the Data on the Map

The final step of this example is to process and display the returned plant data on the map.

With this latest API endpoint, notice that the parameter for the returned data set has changed. Since Plants was requested, the parameter is now plants.

In the code provided, a new function called displayPlantData is added to process the data and display the markers. It expects 1 parameter to be supplied: data

The displayPlantData() function loops through the returned plant data using forEach and creates a Google Maps Marker on the map with an accompanying InfoWindow bound together with a click listener. Follow these links for more information on Google Maps Markers and InfoWindows.

The displayPlantData() function gets called at the end of the final .then() method of getPlantData() function to keep the order of operations intact.

Note: The new JavaScript additions are added above initMap().

Returned JSON:
{
  limit: 100,
  plants: Array(3) [ {…}, {…}, {…} ],
  offset: 0,
  resultCount: 3,
  totalCount: 3
}
JavaScript:
const getPlantData = () => {
  fetch(...)
  .then(...)
  .then((json) => {
    displayPlantData(json.plants);
  });
}

const displayPlantData = (data) => {
  data.forEach((plant) => {
    const marker = new google.maps.Marker({
      position: { lat: plant.latitude, lng: plant.longitude },
      map: map,
      title: plant.plantName
    });
    const infowindow = new google.maps.InfoWindow({
      content: plant.plantName
    });
    marker.addListener('click', () => {
      infowindow.open(map, marker);
    });
  });
}

initMap();

The Final Product

Final HTML & JavaScript:
<html>
  <head>
    <title>IIR API Demo</title>
  </head>
  <body>
    <div id="map"></div>
    <table id="table">
      <thead></thead>
      <tbody></tbody>
    </table>
    <script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_API_KEY]"></script>
    <script>
      // API variables
      const barerToken = 'YOUR_BEARER_TOKEN',
        offlineEventsUrl = 'https://api.industrialinfo.com/idb/v1.1/offlineevents/summary?',
        offlineQueryParameters = 'eventKind=S&eventStatusDesc=Ongoing&unitTypeId=00203&industryCode=04',
        plantDetailsUrl = 'https://api.industrialinfo.com/idb/v1.1/plants/detail?';

      // UI variables
      let map,
        table = document.getElementById('table');

      const initMap = () => {
        map = new google.maps.Map(document.getElementById('map'), {
          center: { lat: 26.422, lng: -14.921 },
          zoom: 1,
        });

        getInitialData();
      };

      const getInitialData = () => {
        fetch(offlineEventsURL + offlineQueryParameters, {
          method: 'POST',
          headers: {
          Authorization: 'Bearer ' + bearerToken,
          },
          'Content-Type': 'application/json',
        })
        .then((response) => {
          return response.json()
        })
        .then((json) => {
          displayTableData(json.offlineEvents);
        });
      }

      const displayTableData = (data) => {
        // add table head row
        table.tHead.innerHTML = `
        <tr>
          <th>Unit Name</th>
          <th>Unit ID</th>
          <th>Offline Capacity</th>
          <th>Event Type</th>
        </tr>`;

        // add table body rows
        table.tBodies[0].innerHTML = '';
        data.forEach((event) => {
          table.tBodies[0].innerHTML += `
          <tr>
            <td>${event.unitName}</td>
            <td>${event.unitId}</td>
            <td>${event.offlineCapacity.capacityOffline} ${event.offlineCapacity.uom}</td>
            <td>${event.eventType}</td>
          </tr>`;
        });
      }

      const extractPlantIDs = (data) => {
        return [
          ...new Set(
            data.map((result) => {
              return result.plantId
            })
          )
        ];
      }
     
      const getPlantData = (data) => {
        const plantIdQueryString = 'plantId=' + data.join('&plantId=')

        fetch(plantDetailsURL + plantIdQueryString, {
          method: 'POST',
          headers: {
            Authorization: 'Bearer ' + bearerToken,
          },
            'Content-Type': 'application/json',
        })
        .then((response) => {
          return response.json()
        })
        .then((json) => {
          displayPlantData(json.plants);
        })
      }

      const displayPlantData = (data) => {
        data.forEach((plant) => {
          const marker = new google.maps.Marker({
            position: { lat: plant.latitude, lng: plant.longitude },
            map: map,
            title: plant.plantName
          });
          const infowindow = new google.maps.InfoWindow({
            content: plant.plantName
          });
          marker.addListener('click', () => {
            infowindow.open(map, marker);
          });
        });
      }

      initMap();
    </script>
  </body>
</html>