logo

NJP

Creating an interactive United States Map

Import · Sep 27, 2016 · article

In this post we're going to create a Service Portal widget that displays a United States map that colors the individual states based on the amount of incidents opened in that state relative to the other states. We are going to use D3js to accomplish this. If you are unfamiliar with D3, check out this previous post introducing D3 to ServiceNow. This post will follow an example from the D3 community which can be found here.

To create our state map we will need to grab our incident data in our server script, create a dependency to a new UI script that contains the coordinates for the state map, and pass our data to the UI script from our client script.

Server script

In our server script, we will create an array of objects within the data object. This array will contain 50 objects; each representing a state. Once that array of objects is created, we will use a pair of GlideAggregate calls to retrieve the counts of open and closed incidents by location. We'll use these calls to populate our array of objects with the counts for each state.

Below is a screenshot of the server script along with the pasted script:

image

(function() {

// Create an array of state abbreviations in the same order as our UI script

var states = ["HI", "AK", "FL", "SC", "GA", "AL", "NC", "TN", "RI", "CT", "MA",

"ME", "NH", "VT", "NY", "NJ", "PA", "DE", "MD", "WV", "KY", "OH",

"MI", "WY", "MT", "ID", "WA", "DC", "TX", "CA", "AZ", "NV", "UT",

"CO", "NM", "OR", "ND", "SD", "NE", "IA", "MS", "IN", "IL", "MN",

"WI", "MO", "AR", "OK", "KS", "LA", "VA"];

// Create an array of objects in our data object with placeholder properties

data.states = [];

for (i=0;i<states.length;i++) {

data.states.push({state: states[i], open: 0, closed: 0});

}

// Find counts of open incidents by location

var openCount = new GlideAggregate('incident');

openCount.addQuery('active', 'true');

openCount.addAggregate('COUNT', 'location');

openCount.query();

while (openCount.next()) {

var openState = openCount.location.state;

var openStateCount = openCount.getAggregate('COUNT', 'location')*1;

for (i=0; i<data.states.length; i++) {

// Increase the open property if there is a match with a state

if (openState == data.states[i].state) {

data.states[i].open += openStateCount;

break;

}

}

}

// Find counts of closed incidents by location

var closedCount = new GlideAggregate('incident');

closedCount.addQuery('active', 'false');

closedCount.addAggregate('COUNT', 'location');

closedCount.query();

while (closedCount.next()) {

var closedState = closedCount.location.state;

var closedStateCount = closedCount.getAggregate('COUNT', 'location')*1;

for (i=0; i<data.states.length; i++) {

// Increase the closed property if there is a match with a state

if (closedState == data.states[i].state) {

data.states[i].closed += closedStateCount;

break;

}

}

}

})();

UI script

Similar to the widget dependency we created in the previous posts, we will create dependencies to call D3 and also to call a UI script that we will create. The code for our new UI script can be found here. It doesn't matter what you name this UI script as long as you remember what you name it; I named mine u_states. Once you have created this UI script, reference it with a widget script dependency.

Client Script

Our client script will be used to determine what color each state should be, define the HTML template that will be used as a tooltip, and make the call to our new UI script to actually draw our state map.

We will loop through the array of objects that we created in our server script and use the D3 interpolate to determine each state's color. The D3 interpolate allows us to define a range of two colors: one for the lowest incident density and one for the highest incident density. Based on a given state's incident count, its color will fall somewhere in this range. For this example we will use #ffffcc as our low color and #800026 as our high color, but you can use any colors you want.

Below is a screenshot of my client script along with the pasted script:

image

function() {

/* widget controller */

var c = this;

// Find the max number of open incidents in a single state

var maxOpen = d3.max(c.data.states, function(d) { return d.open; });

// Create object containing the state data and determine what color

// each state should be

var mapData ={};

c.data.states

.forEach(function(d){

mapData[d.state] = {color: d3.interpolate("#ffffcc", "#800026")(d.open/maxOpen), open: d.open, closed: d.closed};

});

// Define the HTML for the tooltip

function tooltipHtml(n, d){

return "

"+n+"

"+

"

"+

"

"+

"

"+

"

Open "+(d.open)+"
Closed "+(d.closed)+"
Total "+(d.open + d.closed)+"
";

}

// Calls the draw function from our u_states UI script which uses

// D3 to draw our map

uStates.draw("#statesvg", mapData, tooltipHtml);

d3.select(self.frameElement).style("height", "800px");

}

HTML

Below is the pasted HTML I used for this widget:

Incident State Map

CSS

Below is the pasted CSS I used for this widget:

.state{

fill: none;

stroke: #a9a9a9;

stroke-width: 1;

}

.state:hover{

fill-opacity:0.5;

}

tooltip {

position: absolute;

text-align: center;

padding: 20px;

margin: 10px;

font: 12px sans-serif;

background: lightsteelblue;

border: 1px;

border-radius: 2px;

pointer-events: none;

}

tooltip h4{

margin:0;

font-size:14px;

}

tooltip{

background:rgba(0,0,0,0.9);

border:1px solid grey;

border-radius:5px;

font-size:12px;

width:auto;

padding:4px;

color:white;

opacity:0;

}

tooltip table{

table-layout:fixed;

}

tooltip tr td{

padding:0;

margin:0;

}

tooltip tr td:nth-child(1){

width:50px;

}

tooltip tr td:nth-child(2){

text-align:center;

}

Final product

Below is a screenshot of my finished widget. Now that we have the basic framework for a map widget, we can theoretically create a widget with any map and use any data from ServiceNow. The main part that we skipped for this post is the actual creation of the SVG map coordinates, but there are plenty of online resources that help with that process (potentially a future post?).

image

Mitch Stutler

VividCharts Founder

vividcharts.com

linkedin.com/in/mitchellstutler

twitter.com/mitchstutler

Sources

- https://d3js.org/

- US State Map

View original source

https://www.servicenow.com/community/developer-blog/creating-an-interactive-united-states-map/ba-p/2291856