Wednesday 30 March 2016

Using Google Maps API with ServiceNow

This post summarizes the process of using Google Maps API with ServiceNow. With the withdrawal of NPAPI API, the Google Earth plugin went out of the market. To combat the situation, we used Google Maps Satellite view to provide almost the same image.
The points defined are –
  • Using Google Maps API with SNOW
  • Extracting Attachment Data to Table
  • Using Markers and Overlays
  • Setting up the data on the map


Over six years ago, Google has introduced the Google Earth API enabling developers to build rich 3D mapping applications in the browser using JavaScript. And over the years, developers have built quite a number of fascinating applications.
However, the Earth API is built on a technology called the NPAPI plugin framework, and recently, for security reasons, both Chrome and Firefox have announced that they’re removing support for this framework. These security reasons, combined with dwindling cross-platform support (particularly on mobile devices), had made building applications that leverage the Earth API untenable for developers.
Therefore, Google decided to retire the Google Earth API. The API was supported for one more year and then turned off on December 12, 2015.

Due to this reason, all the existing reports which were using Google Earth API will no longer be functional now. We have to come up with an alternative.


Without NPAPI plugin framework, we cannot have Google Earth. Chrome are Firefox are leading browsers and are extensively used by almost everyone so we cannot take the risk of not supporting these browsers. Also, we cannot guarantee that for how long other browsers will keep on supporting this plugin.
ServiceNow was facing the same issue. We can have map – based reports in ServiceNow as well and many people can have it using the Google Earth API. All that would have stopped working.
We had such reports as well. These reports are used to show the statuses or coverage zones of various sites planted at different geographical regions.

Below is a screenshot of the earlier Google Earth view.

 
These reports have to be map – based to provide a clear picture of the areas and regions. This Google Earth plugin uses a KML (Keyhole Markup Language) file with the plotting data in it being similar to XML tags to plot the images on Map with the desired shape and size. It contains the east, west, north and south point coordinates where the four corner lies and Google Earth plots them in the same fashion.


To combat the situation, the alternative we are using is the Satellite view of Google Maps. This is quite similar to the Google Earth view but it is not a three – dimensional view. We can use the same KMLs for plotting the data by fetching their data to a table and passing that data to the Google Maps API functions.
The KML file was present as an attachment to a particular table record. We fetched the data from that KML to a SNOW table using XMLHelper. XMLHelper is used to fetch data from XML files and since the structure of a KML file is similar to an XML file, we can use the same for KMLs as well. Below is the code snippet used –

getParametersFromXml: function(soapRequestXML, nodeBody ) //soapRequestXML is the complete string in KML and nodeBody is the parent tag
{
                        var soapObject = new XMLHelper(soapRequestXML).toObject();
                        this.getRecordObj(soapObject, nodeBody);
                        return this.recordObject;
            },
           
            getRecordObj: function(obj, requestBodyElement){
                        for (x in obj){
                                    if(x==requestBodyElement){
                                                this.recordObject = obj[x];
                                    }
                                    this.getRecordObj(obj[x], requestBodyElement);
                        }
            }
Once you have the returned value from this function, you can dot – walk on it to reach your particular tag whose value is desired.

This way, the data can be stored in any desired SNOW table per tag. Every row in that table will contain a marker to be plotted on the map.

Now, we will actually plot the data on map using Google Map API functions. For that, we will need an array of all the markers that need to be plotted. In our case, we had different checkboxes and dropdown menus to choose the report to be displayed. Hence, there was different set of markers for every selection. We created a GlideRecord of all the records in the table that are to be plotted and created an item out of it. Below is the code snippet used –

var markerArray = this.newItem("marker"); //Create an array and name it marker
favs.setAttribute("name", gr.u_ name); //Set the values you need

Now, we will fetch this array in our UI page and plot the values on the map. This functionality is provided by various functions in the API according to various needs. The one we used was OverlayView and Marker. First we created the map –

myMap = new google.maps.Map(document.getElementById('map'), {
                        zoom: zoom,
                        center: goldCoast,
                        mapTypeId: mapType
            });

This map object must be global since we’ll be using it at various points. Next, below is the code to create an OverlayView.

DebugOverlay.prototype = new google.maps.OverlayView();

function DebugOverlay(bounds, image, myMap) { //bounds is an object of google.maps.LatLng type which contains Latitude and Longitude value separated by comma, image is the reference of the image to be plotted and myMap is the reference to our map object
           
            this.bounds_ = bounds;
            this.image_ = image;
            this.map_ = myMap;
            this.div_ = null;
            this.setMap(myMap);
}

//Below are the various functions which will be called at different times by the constructor and destructor
DebugOverlay.prototype.onAdd = function() {
           
            var div = document.createElement('div');
            div.style.borderStyle = 'none';
            div.style.borderWidth = '0px';
            div.style.position = 'absolute';
            var img = document.createElement('img');
            img.src = this.image_;
            img.style.width = '100%';
            img.style.height = '100%';
            img.style.opacity = '0.20';
            img.style.position = 'absolute';
            div.appendChild(img);
            this.div_ = div;
            var panes = this.getPanes();
            panes.overlayLayer.appendChild(div);
};

DebugOverlay.prototype.draw = function() {
            var overlayProjection = this.getProjection();
            var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
            var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());
            var div = this.div_;
            div.style.left = sw.x + 'px';
            div.style.top = ne.y + 'px';
            div.style.width = (ne.x - sw.x) + 'px';
            div.style.height = (sw.y - ne.y) + 'px';
};

DebugOverlay.prototype.updateBounds = function(bounds){
            this.bounds_ = bounds;
            this.draw();
};

DebugOverlay.prototype.onRemove = function() {
            this.div_.parentNode.removeChild(this.div_);
            this.div_ = null;
};

To create the markers, the point where we are to show the image on one particular coordinate and not over an area, we can use the below code –

function addMarker(location, icon, title, disp) { //
            var marker = new google.maps.Marker({
                        position: location,
                        map: myMap,
                        icon: icon,
                        title: disp
            });
           
            if(title != '') //create a popup dialog showing the relevant information
                        {
                        var infowindow = new google.maps.InfoWindow({
                                    content: title
                        });
                        marker.addListener('click', function() {
                                    infowindow.open(myMap, marker);
                        });
            }
           
            markers.push(marker);
}

We can use Google Maps API over SNOW to create map – based reports using list data. To show the markers we need the longitude and latitude of the point and to create an overlay, the east, west, north and south points are required. You can control the type of map from Satellite, Terrain or Hybrid, the zoom – level and the center of the map on load. The data can be refreshed into the table itself to update. There is so much more in the API that can be used as per the requirements.

This can help users to demonstrate any location – based reports and findings.




  1. https://developers.google.com/maps/documentation/javascript/examples/
  2. http://gmaps-samples-v3.googlecode.com/svn/trunk/map_events/map_events.html
  3. https://community.servicenow.com/thread/154141
  4. http://tuhrig.de/visualizing-kml-files-in-google-maps/

While every effort has been made to provide all the information and the correct things, please feel free to reach out in case you find any issues or need further information. :) :)

Monday 28 March 2016

Document ID field in ServiceNow

Hey There!

Today we'll discuss an interesting thing provided by ServiceNow. At times we need to specify a reference column without knowing what will the table be. It might be an Incident to refer, or a change, or some other user defined table. But our very own reference field doesn't let us do that.

For that, ServiceNow has provided as the Document ID field. A Document ID field is of two types -

1. In which we can let the user specify the table name in another column created by us - known as Dependent column.

2. A field in which we have a table and a field drop-downs to choose from.

I'll show you how.

USING DEPENDENT COLUMN


First, we will go to the table where we want the Document ID field. Create a column of type Table Name with your choice of name. This will be used as the dependent column where the user will specify the table name to reference.

Next, create a column of type Document ID. Save it. Under the column Dependent Field, check the box Use Dependent Field. Under the drop down Dependent on Field, choose the field you created for table name to reference.


And...its done :) ...when you'll go ahead and add a new record to the table you'll be required to add the table name and then you can go ahead and choose the record from that particular table to refer to.



Without using the Dependent Column

Just don't check the column Use Dependent Column and add the values by selecting the table and record.

Feel free to post any queries under the comments section :)

Monday 7 March 2016

Selective Hiding: Domain Separation for List Data

Domain separation is the technique by which we let users view only a selective range of data. The data visibility is defined by the domain in which the user is residing and we add a similar column to our concerned table as Domain. The filtering is done by ServiceNow by matching the domains and letting a user view only the data that falls into his domain,

For this, what we may do as an advanced step is separating the users based on their company and adding a Company field in our table which will decide the domain view. I'll tell you how!!

First, create a column named domain....do not save before you read this fully. Column type will be Domain as well. Change the column name from u_domain to sys_domain before you save anything.

After you'll save, ServiceNow will create a column named Domain Path for its reference. Now, all you need to do is convert your Company name to Domain. For this, we will be writing a Business Rule which will run before Insert and Update.

Below is the script you need to add in Advanced tab -

var defdomain = current.u_company; //the value from company field - sys_id since it is a ref field
var chgdomain = ""; //variable to store domain value

var compcheck = new GlideRecord('core_company');
compcheck.addQuery('sys_id', defdomain); //find the record of the company
compcheck.query();
if (compcheck.next()) {
chgdomain = compcheck.sys_domain.getDisplayValue(); //get the domain value from company table
}

updatesoftdomain();
function updatesoftdomain()
{
current.sys_domain = chgdomain;
current.update();
}


Done Done Done...!! :)