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


This example shows how to fetch data from two IIR IDB API calls to create a visualization using simple JavaScript along with the Google Maps API.

First, the "Offline Event Summary API" is used to retrieve the current NGL fractionator shutdown events. The basic information is shown on the table.

Next, the "Plant Detail API" is called to get the associated units/plants and to display their locations on the map.

Live Demo

Current Global NGL Fractionator Shutdowns

1. Basic Setup

1.1 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="demo-map"></div>
    <table id="demo-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>
1.2 Initializing the Map

The Google map function 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.

JavaScript:
var map = new google.maps.Map(document.getElementById('demo-map'), {
    center: { lat: 26.422, lng: -14.921 },
    zoom: 1,
    zoomControl: true,
    mapTypeControl: false,
    scaleControl: false,
    streetViewControl: false,
    rotateControl: false,
    fullscreenControl: false,
  });
  
var table = document.getElementById('demo-table');

2. Process Offline Event Data

2.1 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 NGL Fractionator Shutdowns include the following four:

  1. eventKind=S
  2. eventStatusDesc=Ongoing
  3. unitTypeID=00203
  4. industryCode=04

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 then calls displayTableData() first to display the offline events table data.

Also, the next method processPlantData() would then be used to retrieve plant data while looping through offlinve event records. (see 3. Process Plant Data)

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

function getOfflineEventData () {
  //Use ES6 Fetch API 
  fetch(offlineEventsURL + offlineQueryParameters, {
    method: 'POST',
    headers: {
    Authorization: 'Bearer ' + bearerToken,
    },
    'Content-Type': 'application/json',
  })
  .then((response) => {
    return response.json()
  })
  .then((json) => {
    displayTableData(json.offlineEvents);
    
    processPlantData(json.offlineEvents); //see 3.1 Making the Second API Call
  })
}


2.2 Display Offline Event Data for the 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}.

Returned Offline Event JSON (snippet):
{
    "limit": 100,
    "offset": 0,
    "resultCount": 3,
    "totalCount": 3,
    "offlineEvents": [
        {
            "eventType": "Unplanned",
            "unitId": 2084883,
            "unitName": "Mont Belvieu MB-3L",
            "plantId": 3078786,
            "offlineCapacity": {
                "productId": "010098",
                "productDesc": "Mechanical Output",
                "unitCapacity": 10,
                "capacityOffline": 10,
                "uom": "MW"
            },
            ....                  
        },
        {...},
        {...}
    ]
JavaScript:

function 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 = '';
  
  //loop through each offline event record
  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>`;
  });
}


3. Process Plant Data

3.1 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

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, processPlantData(), preforms similar to the processOfflineEventData() function above.

extractPlantIDs(data) would build a url parameter string for plantId such as "plantId=3026102&plantId=1007868&plantId=1027431&plantId=3038621" and then joined with plantDetailsUrl.

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

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

function processPlantData (data) {
  var 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);
  });
}

3.2 Putting Plant 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 processPlantData() function to keep the order of operations intact.

Returned Plant JSON: (snippet)
{
    "limit": 50,
    "offset": 0,
    "resultCount": 1,
    "totalCount": 1,
    "plants": [
        {
            "plantId": 1000012,
            "plantName": "Houston Sulfuric Acid",
            "plantStatusDesc": "Operational",
            "longitude": -95.26879,
            "latitude": 29.720048,
            ...
        }
     ]    
}            
JavaScript:

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


The Final Product

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

    // UI variables
    var map = new google.maps.Map(document.getElementById('demo-map'), {
        center: { lat: 26.422, lng: -14.921 },
        zoom: 1,
        zoomControl: true,
        mapTypeControl: false,
        scaleControl: false,
        streetViewControl: false,
        rotateControl: false,
        fullscreenControl: false,
    });      
    var table = document.getElementById('demo-table');

    function processOfflineEventData()    
    {    
      fetch(offlineEventsURL, {
        method: 'POST',
        headers: {
          Authorization: 'Bearer ' + bearerToken,
        },
        'Content-Type': 'application/json',
      })
      .then((response) => {
          return response.json()
      })
      .then((json) => {
          displayTableData(json.offlineEvents)
          processPlantData(extractPlantIDs(json.offlineEvents))
       })
    }

    function 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>`;
        });
    }

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

    function processPlantData(data)
    {
      var 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) => {
          displayMapData(json.plants)
        })
    }

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

    processOfflineEventData();

    </script>
  </body>
</html>