JavaScript Extension : Geomapping with Leaflet JS

Ever since the dawn of time (or at least since the arrival of JavaScript Extensions in Oracle Intelligent Advisor), for whatever reason, the subject of Google Maps (geocoding, reverse geocoding, etc) has been one of the most read and downloaded on this site. But now, given that Google Maps (like Google APIs in general) requires registrations, license keys, and is subject to all sorts of restrictions  and throttling, developers have needed something else to impress their customers and colleagues with eye candy.

Geomapping with Leaflet JS and MapBox

So it’s appropriate therefore that this little post is all about Leaflet JS, “an open-source JavaScript library
for mobile-friendly interactive maps”. The Leaflet JS website is stuffed full of interesting examples and tutorials, so here goes for a short example of using this library in Oracle Policy Automation.

The setup is slightly different than working with Google Maps, but everything starts with the obvious step : download a library! In my case, I downloaded the library and CSS styles and dropped them into the resources folder. The files are tiny.

For the demonstration, my Oracle Policy Automation project contains only a handful of attributes, specifically enough to be able to create an address (street, town, zipcode, country) and a customer name (for a marker on the map).

The second element to handle in your code, is the image tile provider for your map. Working with Leaflet requires two things : Leaflet and a tile provider – in my case mapbox proved to be a good choice. It looks lovely. It requires a registration and access token, but it is very easy to obtain and you can try for free. Leaflet is entirely agnostic however, so if you want to use another tiling source, you can.

Apart from these steps, most of the code looks familiar : get the attribute values, pass them to the geocoding engine and then extract the latitude and longitude from the returned JSON object. Once you have that information,we use onreadystatechange to check that the page is loaded, and then draw the map, using latitude and longitude, and drop a custom marker at the location with a shadow and a popup window associated with the marker.

Because Geomapping with Leaflet JS this is based on OpenStreetMap, we need to be good citizens and ensure we give credit where credit is due. Let’s look at a simple example:

/**
 * Richard Napier The OPA Hub Website June 2019
 * Educational Example of Custom Label with Leaflet JS and MapBox
 * I will remember this is for demonstration purposes only
 */
OraclePolicyAutomation.AddExtension({
	customLabel: function (control, interview) {
		if (control.getProperty("name") == "xMap") {
			return {
				mount: function (el) {
					// Add CSS First
					var head = document.getElementsByTagName('head')[0];
					var linkcss = document.createElement('link');
					linkcss.id = "leafletcss";
					linkcss.rel = 'stylesheet';
					linkcss.type = 'text/css';
					linkcss.href = '${resources-root}/leaflet.css';
					linkcss.setAttribute('crossorigin', "anonymous");
					head.appendChild(linkcss);
					var div = document.createElement("div");
					div.id = "lblmap";
					div.style.visibility = 'visible';
					div.style.height = '370px';
					div.style.width = '400px';
					el.appendChild(div);
					var street = interview.getValue("street");
					var city = interview.getValue("city");
					var zip = interview.getValue("zip");
					var country = interview.getValue("country");
					var customer = interview.getValue("customer");
					var latitude;
					var longitude;
					var uri = street + "," + city + "," + zip + "," + country;
					var res = encodeURI(uri);
					var root = "https://api.mapbox.com/geocoding/v5/mapbox.places/" + res + ".json?access_token=pXXX";
					$.ajax({
						url: root,
						method: "GET",
						async: false,
						dataType: 'json'
					}).then(function (data) {
						//console.log("Cleaning Up Response");
						//for (var key in data.features) {
						if (data.features[0]) {
							var val = data.features[0];
							longitude = val.center[0];
							latitude = val.center[1];
							//console.log(" Returned " + latitude + " " + longitude);
							$.getScript("${resources-root}/leaflet.js", function () {
								//console.log("Script loaded");
								window.onerror = function (errorMsg, url, lineNumber, column, errorObj) {
									console.log('Error: ' + errorMsg + ' Script: ' + url + ' Line: ' + lineNumber
										 + ' Column: ' + column + ' StackTrace: ' + errorObj);
								}
								//console.log(document.readyState);
								//console.log(latitude + " " + longitude);
								//	document.onreadystatechange  = function () {
								//console.log("ReadyState Change Event");
								if (document.readyState === "complete") {
									//console.log("ReadyState is Complete");
									var siteIcon = L.icon({
											iconUrl: '${resources-root}/images/Logo_Marker.png',
											shadowUrl: '${resources-root}/images/Logo_Marker_Shadow.png',
											iconSize: [30, 40],
											shadowSize: [30, 40],
											iconAnchor: [0, 40],
											shadowAnchor: [0, 38],
											popupAnchor: [15, -20]
										});
									var myLatLng = new L.LatLng(latitude, longitude);
									var mymap = L.map('lblmap').setView(myLatLng, 13);
									L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
										attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
										maxZoom: 18,
										id: 'mapbox.streets',
										accessToken: 'pk.eyJ1IjoidGhlb3BhaHViIiwiYSI6ImNqdDJ3Y2liNDFvMjE0M3BpampzOG16cDYifQ.0ppcKabM2WhuaPT-kCGwYw'
									}).addTo(mymap);
									var marker = L.marker(new L.LatLng(latitude, longitude), {
											icon: siteIcon
										}).addTo(mymap);
									marker.bindPopup(customer + " is based here.");
								};
							
							});
						}
					
					});
					// Assumes JS is loaded
				},
				unmount: function (el) {
					if (control.getProperty("name") == "xMap") {
						
						 var myMap = document.getElementById("lblmap");
                        myMap.parentNode.removeChild(myMap);

					}
				}
			}
		}
	}
})

Lines 0-40 : Pretty standard stuff, setting up the CSS file and the encoded URL for the REST call which will get us the latitude and longitude.
Lines 41- 52 : Used to call the service and get / parse the response, giving us the latitude and longitude
Lines 53 – 64 : We need to be sure that we wait until the leaflet JavaScript file is loaded before doing anything else
Lines 67 – 75 : Set up a Custom Marker
Lines 77 – 83 : Add a Map Layer and Credits
Lines 84 – 87 : Add a Marker using a custom image and shadow
Lines 85 – 88 : Add a Popup to the Marker click, and use some Oracle Policy Automation data in the popup.

The rest is just clean-up and go home (the unmount).

Geomapping with Leaflet JS : The Result

The end result is a nicely presented map, with a marker at the correct address, and a cute popup window when the marker is clicked.

Geomapping with Leaflet JS MapBox Example

We’ll see what else we can do with this nice alternative library in the coming weeks. In the meantime, you will find a sample Project in the Online Store.

Author: Richard Napier

After 8 years in case management and ERP software roles, Richard Napier joined Siebel Systems in 1999 and took up the role of managing the nascent Siebel University in Southern Europe. He subsequently was Director of Business Development and Education for InFact Group (now part of Business & Decisions) for 8 years. He now runs Intelligent Advisor IT Consulting OÜ. Owner of intelligent-advisor.com, he also is Co-Founder of the Siebel Hub.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Intelligent Advisor IT Consulting Serving Customers Worldwide
Hide picture