jQuery in XPages #10 – JQVMAP (Vector Maps)

In this article I will demonstrate how to implement the JQVMAP jQuery plugin into an XPage. There are 4 maps to chose from but users are able to create their own and add more maps if they need to. In addition to the out of the box functionality I have added some XPage lookup data to demonstrate the integration potential. As I have written this article ideas and thoughts have come at me quick and fast and this is much larger than I had originally intended. If you need maps on your website this is a fantastic flexible solution.

JQVMAP

Demonstration

The XPages integration of JQVMAP is demonstrated here

This capability does not yet work properly in IE – Chrome/Safari/Firefox only.

Download

The demonstration database can be downloaded from the link above or from here

Introduction

JQVMAP is a jQuery plugin based on the original jVectorMap plugin. The plugin provides an abundance of parameterized options for the developer to create their own maps, add color, add interactivity and provide a very slick solution to a broad requirement of many customers, to provide mapping capabilities to their website.

JQVMAP in an XPage

Users are able to select either a country or state and pertinent information regarding their selection will be displayed on the page.

How does it work?

I added jQuery and the relevant map files to my database’s WebContent folder.
They are referenced in the XPage as follows:

	<xp:this.resources>
		<xp:script src="js/jquery-1.7.1.min.js" clientSide="true"></xp:script>
		<xp:script src="js/jquery.vmap.js" clientSide="true"></xp:script>
		<xp:script src="js/vmap/jquery.vmap.world.js" clientSide="true"></xp:script>
		<xp:script src="js/vmap/jquery.vmap.sampledata.js"
			clientSide="true"></xp:script>
		<xp:styleSheet href="css/jqvmap.css"></xp:styleSheet>
	</xp:this.resources>

Adding a Map

Implementing the basic JQVMAP is as simple as adding the following jQuery to your XPage

	jQuery(document).ready(function() {
		jQuery('#vmap').vectorMap({
		    map: 'germany_en'
		});
	});

There are many paramaters which can be added to the map, most of which are self explanatory. For a full list of all the parameters available check out the project on github.

Colorizing the map

The example comes with a sampledata.js file which contains 2010 GDP information and is formatted like this:

var sample_data = {“af”:”16.63″,”al”:”11.58″,”dz”:”158.97″,”ao”:”85.81″,”ag”:”1.1″,”ar”:”351.02″,”am”:”8.83″,”au”:”1219.72″,”at”:”366.26″,”az”:”52.17″,”bs”:”7.54″,”bh”:”21.73″,”bd”:”105.4″,”bb”:”3.96″,”by”:”52.89″,”be”:”461.33″,”bz”:”1.43″,”bj”:”6.49″,”bt”:”1.4″,”bo”:”19.18″,”ba”:”16.2″,”bw”:”12.5″,”br”:”2023.53″,”bn”:”11.96″,”bg”:”44.84″,”bf”:”8.67″,”bi”:”1.47″ etc etc

When the map is built the data is passed into the plugin via these parameters

		    values: sample_data,
		    scaleColors: ['#C8EEFF', '#006491'],

The plugin then determines the color scale between the two passed in color parameters and scales each region based on the data provided. And quite frankly that rocks, bigtime! Changing the scale colors can do this to your maps and is very effective if you are looking to show coal usage or rainforest or anything where color enhances the information

Colorizing the map
Colorizing the map

So where’s the XPage stuff?

I have added some XPage functionality to the world map in two ways – a partial refresh and a REST service.

As the user mouses over a region there is a callback to the REST service to provide me some JSON from the database. Adding the REST service creates the JSON from the vwCountryDoc view

<xe:restService id="restService1" pathInfo="countryLookup">
 <xe:this.service>
 <xe:viewJsonService viewName="vwCountryDoc" var="entry"
 contentType="text/plain" count="1" defaultColumns="true">
 <xe:this.keys>
 <![CDATA[#{javascript:return facesContext.getExternalContext().getRequest().getParameter("code")}]]>
 </xe:this.keys>
 </xe:viewJsonService>
 </xe:this.service>
 </xe:restService>

creates http://demo.xomino.com/xomino/jQinX.nsf/xJQVMAP.xsp/countryLookup?code=gb

  {
      "@entryid":"77-A19E1314CB4B2C0B852579FE0047FF23",
      "@unid":"A19E1314CB4B2C0B852579FE0047FF23",
      "@noteid":"505E",
      "@position":"77",
      "@siblings":248,
      "@form":"CountryDoc",
      "CountryCode":"GB",
      "Name":"United Kingdom",
      "POP":"62,262,000",
      "Areakm":"242,900",
      "Aream":"93,800",
      "Life":"79.4",
      "HighestPeak":"Ben Nevis\t1,344 m (4,409 ft)\t"
  }

We add onRegionOver and onRegionOut callbacks to the map creation and with that we are able to read the JSON, parse the information (building a table) into a hidden span, and show it/hide it depending on the callback.

		jQuery('#vmap').vectorMap({
		    map: '#{javascript: getComponent("comboBox1").getValue()}',
		    backgroundColor: '#333333',
		    color: '#ffffff',
		    hoverOpacity: 0.7,
		    selectedColor: '#666666',
		    enableZoom: true,
		    showTooltip: false,
		    values: sample_data,
		    scaleColors: ['#C8EEFF', '#006491'],
		    normalizeFunction: 'polynomial',
		    onRegionOver: function (event, code, region){
		    	url="xJQVMAP.xsp/countryLookup?code="+code;
	    		//get the REST stream
	    	   	$.getJSON(url,
		    		function (data){
				    	temp=region;
				    	temp=temp.replace(/ /g, '_');
				    	var theTable='<table cellpadding=2 border=0 class="hoverTable"><tr><td>'
				    	theTable += '<img src="http://www.flagslist.com/clist/flags/'+temp+'.png" height=30 width=60 border=0/></td>'
		    			theTable += "<td><h1 style='color: white'>"+data[0].Name+"</h1></td></tr>"
		    			theTable += "<tr><td>Population</td><td>"+data[0].POP+"</td></tr>"
		    			theTable += "<tr><td>Life Expectency</td><td>"+data[0].Life+"</td></tr>"
		    			theTable += "<tr><td>Area km/<sup>2</sup></td><td>"+data[0].Areakm+"</td></tr>"
		    			theTable += "<tr><td>Highest Peak</td><td>"+data[0].HighestPeak+"</td></tr>"
			    		theTable+= "</table>"
			    		$(".hoverShow").html(theTable).css('display', 'block')
				    	console.log(theTable)
			    	});
		   		},
		   	onRegionOut: 	function(element, code, region)
		   		{
		   			$(".hoverShow").html('&amp;nsbp;').css('display', 'none')
		   		},
		    onRegionClick: function(element, code, region)
			    {
			    	//message only relevent to the world map
			    	var temp="#{javascript: getComponent("comboBox1").getValue()}"
			    	if (temp != "world_en"){ return false}
				    var message = 'You clicked "'
			            + region
			            + '" which has the code: '
			            + code.toUpperCase()
			            + "\n\n2010 GDP:"
			            + sample_data;
			        alert(message);
			        sTemp='<img src="http://www.flagslist.com/clist/flags/'+region.replace(/ /g, '_')+'.png" height=30 width=60 border=0/>'
			        $("[id$=code1]").val(code);
			        $('#flag').html(sTemp)
			        $("[id$=button1]").click()
			    }
		});
	});

And this creates the Top Left table in the example

Displaying information on the fly using JSON REST data
Displaying information on the fly using JSON REST data

You will see there is an onRegionClick event as well. When the user clicks on the region I am triggering a hidden button to be clicked and that runs a partiaRefresh on the bottom left section which displays information from the database via computed fields. This also pulls data from the sampledata.js and displays the GDP value

			<xp:table id="countryData" style="width: 700px" cellpadding="5">
				<xp:tr>

					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td style="width: 100px"></xp:td>
					<xp:td>
						<div id="flag" style="width: 200px">
							<xp:text escape="false" id="computedField4">
								<xp:this.value escaped="false">
									<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
var temp1 = @DbLookup(@DbName(), "vwCountryDoc", temp, 2);

return '<img src="http://www.flagslist.com/clist/flags/'+temp1.replace(/ /g, '_')+'.png" height=30 width=60 border=0 />'
}]]>
								</xp:this.value>
							</xp:text>

						</div>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Country Code:</xp:td>
					<xp:td>
						<xp:inputText id="code1" defaultValue="GB"
							style="border: 1px solid white">

						</xp:inputText>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Name:</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField1">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 2)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Population</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField2">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 3)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
				<xp:tr>
					<xp:this.rendered><![CDATA[#{javascript:return (getComponent('comboBox1').getValue() == "world_en")
}]]></xp:this.rendered>
					<xp:td>Area</xp:td>
					<xp:td>
						<xp:text escape="true" id="computedField3">
							<xp:this.value>
								<![CDATA[#{javascript:var temp = getComponent("code1").getValue();
				@DbLookup(@DbName(), "vwCountryDoc", temp, 4)}]]>
							</xp:this.value>
						</xp:text>
					</xp:td>
				</xp:tr>
			</xp:table>

This builds the popup and bottom left information

Reading JSON data deom a js file and using a partialRefresh to display information
Reading JSON data from a .js file and using a partialRefresh to display information

CallBack events
Because of the callback events we are also able to add/display change other data on the screen. Taken directly from the site we have the following events available.

  • onLabelShow function(element, label, code)
    • Callback function which will be called before label is shown. Label DOM object and country code will be passed to the callback as arguments.
  • onRegionOver function(element, code, region)
    • Callback function which will be called when the mouse cursor enters the region path. Country code will be passed to the callback as argument.
  • onRegionOut function(element, code, region)
    • Callback function which will be called when the mouse cursor leaves the region path. Country code will be passed to the callback as argument.
  • onRegionClick function(element, code, region)
    • Callback function which will be called when the user clicks the region path. Country code will be passed to the callback as argument.
Conclusion
This article grew and grew as I was writing it, the possibilities for this capability are astounding and to think that it is free (MIT license) and takes less than an hour to get up and running it is phenomenal. I have really enjoyed making this article and believe me when I say it could have been a lot longer 🙂

Demonstration

The XPages integration of JQVMAP is demonstrated here

Download

The demonstration database can be downloaded from the link above or from here

3 thoughts on “jQuery in XPages #10 – JQVMAP (Vector Maps)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s