CFMobile Example – Using Geolocation APIs in ColdFusion Splendor

I was going to cover a use-case of taking picture in a mobile app and uploading it to ColdFusion Server in this post, but I found some issues in packaging an app that used Geolocation APIs when helping someone and thought that I would cover geolocation first.

This sample application is very simple – it gets your current location and displays a marker at that location on the Goole Map APIs. Here is how the application looks –

2014_02_27_image1BTW, the screen shot is of this application running in iOS PhoneGap Shell application that I had talked about in Simplify Mobile Application Development Using ColdFusion.  And it is not showing my location, but the default location that iOS Simulator returns in response to calling geolocation.getCurrentPosition function of PhoneGap.

However on an actual device it returns real location.

 

 

The code (CFML) is very simple and looks very much like JS code using PhoneGap APIs –

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
DOCTYPE html>

<html>
	<head>
		<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />

		<meta HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE">

	    <style type="text/css">
	    	html { height: 100% }
	      	body { height: 100%; margin: 0; padding: 0 }
	      	#map-canvas { height: 100% }
	    </style>

		<script src="http://maps.googleapis.com/maps/api/js?sensor=true"></script>

		<script src="jquery-2.0.3.min.js" ></script>

	    <script >
	    	$(document).ready(function(){
	    		//do nothing for now
	    	});

	    	function displayMessage(msg)
	    	{
	    		$("#msgDiv").html(msg);
	    	}
	    </script>
	</head>

	<body>
		<div id="msgDiv"></div>

		<div id="map-canvas"/>
	</body>
</html>

<cfclientsettings enabledeviceapi="true">
<cfclient>
	<cfscript>
		mapEnableHighAccuracy = false; //If true, API will use GPS else will use mobile network to get current position.

		try
		{
			myMap = new google.maps.Map(document.getElementById("map-canvas"));

			mapMarker = new google.maps.Marker({
			    map: myMap,
			    title:"My Location",
	            draggable: true
			});

			getCurrentPosition (function(position) {
				//Got current position
				displayMap(myMap,position,mapMarker);

				//Watch for change in the position
				cfclient.geolocation.watchPosition(function(pos) {

					displayMessage("Location changed - " + pos.coords.latitude + "," +
						pos.coords.longitude);

					displayMap(myMap,pos,mapMarker);
				},
				{maximumAge: 3000, timeout: 5000, enableHighAccuracy: mapEnableHighAccuracy });
			}, 0, 0 /*indefinite wating time*/);
		}
		catch (any e)
		{
			alert("Error : " + e.message);
		}

		function displayMap(map, position, marker)
		{
			var latLong = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
			var mapOptions = {
          		center: latLong,
          		zoom: 15
        	};

        	map.setOptions(mapOptions);
        	marker.setPosition(latLong);
		}

		//Value of 0 for maxWaritingTime means keep trying indefinitely. 
		function getCurrentPosition (callback, totalWaitingTime, maxWaitingTime)
		{
			if (maxWaitingTime > 0 && totalWaitingTime > maxWaitingTime)
			{
				displayMessage("Timed out. Failed to get the current position");
				return;
			}

			var options = {maximumAge: 3000, timeout: 1000, enableHighAccuracy: mapEnableHighAccuracy };

			try
			{
				if (totalWaitingTime <= 0)
					displayMessage("Getting current position");

				var currPos = cfclient.geolocation.getCurrentPosition(options);

				displayMessage("Got the current position");

				callback(currPos);
			}
			catch (any e)
			{
				totalWaitingTime += options.timeout;
				displayMessage("Retrying to get the current position - " + totalWaitingTime + " - " + e.message);
				//assume it is timeout
				setTimeout(function(){
					getCurrentPosition(callback, totalWaitingTime, maxWaitingTime);
				},100);
			}
		}
	</cfscript>
</cfclient>

The page contains two divs (on line# 32 & 34), one for displaying messages (at the top of the page) and the other one displays Goole Map. On line# 38 we have
<cfclientsettings enabledeviceapi=”true”>
This statement tells ColdFusion that the application uses PhoneGap device APIs and all PG related JS file should be included in the generated code. It also creates an event handler for PG’s deviceready and puts all the code in cfclient in this event handler. So this setting (tag cfclientsettings) must precede any cfclient code that use device APIs.

I have initialized a variable, mapEnableHighAccuracy, to false on line# 41. This variable will be used to tell geolocation API to use mobile network to get current location. If set to true, then GPS would be used to get current location.

From line# 45-51 I create Google Map and Marker objects and initialize them.

On line#53 I call getCurrentLocation function. This function takes three arguments – the first one is callback function which is called when device API returns current location, the second one is total time elapsed since our first attempt to get position, and the last argument is maximum time to wait. If the last argument is 0, then we till try indefinitely. This would be useful if there is delay in getting current position.

Once I get current location, on line# 55 I call displayMap function to display marker at that location and then on line# 58 I start watching for change in current location i.e. if you move carrying your mobile. And when the current location changes, I update the marker by calling displayMap again.

In getCurrentPosition function (starts at line# 86), I call cfclient.geolocation.getCurrentPosition, on line# 101, after setting a few options. If this call times out, it throws an exception. In catch block I then retry – from line# 112-114. If I get location successfully then I invoke callback function passed to this function on line#105, which will execute function passed as argument on line#53.

If you open this application in Shell application (Android or iOS) then everything should work fine, because Shell application is created with all PG plugins and user access flags. But it did not work when I packaged the app in Thunder and opened it on my mobile.

Here is how I had packaged the app –

  • Right click on the project in Navigator and select Properties.
  • Click “ColdFusion Mobile Project” from the list on the left side
  • Select “Resource Selection” tab and select only index.cfm and jqeury file. Do not select Application.cfm
  • Select PhoneGap tab. We need to add PhoneGap features that this application is using. So click New button. PG option dialog shows up.
  • Select Feature from the list on the left side. From the drop-down list select name of the feature. For this application I selected http://api.phonegap.com/1.0/network and http://api.phonegap.com/1.0/network.
    Note that this UI is not very friendly and we have modified it post public beta.
  • Click OK to close options dialog and then click OK to close Project properties.
  • I had already set PhoneGap build server details in Preferences (Window->Preferences->ColdFusion->PhoneGap)
  • Then right-click on the project in the Navigator view and select Generate PhoneGap Build

Many of the above steps (except steps to add features) are explained in the video in my post Simplify Mobile Application Development Using ColdFusion.

When the build was ready, I installed it on my mobile and got a blank page. So obviously there is a bug in packaging and we are fixing it.

But can you get it working with a few additional settings –  add media feature (http://api.phonegap.com/1.0/media) and gap:plugin for geolocation. I have already explained how to add a feature. Adding gap plug-in is also easy.
Again open up project properties and select ColdFusion Mobile Project. Select PhoneGap tab and click New button. Select plug-in from the list on the left. Then add name of the plugin, for this application it is org.apache.cordova.geolocation
2014_02_27_image2

And my project property looks like this after adding three features and one gap plugin –
2014_02_27_image3

After above changes, the application worked fine on my mobile.

As I said before, we have made configuration of features and plugins easier in Thunder post public beta release.

Download the project and Android APK for this application.

-Ram Kulkarni

Leave a Reply