My first morning at Scotch on the Rocks was a bit rough. Despite getting to bed at a decent time (10:30 or so) so I could catch up on my sleep, I ended up waking up around 3:30 or so in the morning and tossing and turning till 6. During that time an idea popped in my head for one more demo I could do for my jQuery Mobile presentation. I was thinking about building a simple mobile web site for the hotel. I thought it would be a simple example but also pretty practical. Of course, once I figured out a simple, static plan for the site an idea occurred to me. One of the important details for a hotel - obviously - is where they are. But what if you could not only tell folks where the hotel was but how you could get there? Here's my demo which shows just that. I'll warn you now - it's brittle as heck. It's really just a proof of concept. But it works (mostly) and I think it's pretty cool. Ready?
First, I'll begin by showing a few screen shots of the application I built. This is online and I'll share that link at the end, but for now, let's just look at pretty pictures.
My hotel mobile home page has three links: Contact Us, Find Us, and About. Contact Us has the basics - a phone number and an email address.
On a mobile device, clicking that phone number will automatically call the hotel. This is probably the #1 most important "service" a mobile web site can provide. Unfortunately far too few make this as simple. Let's skip the second link now and show the 3rd link:
Yeah, nothing important here. This is where you can stick the content the marketing department insists on that is most likely 100% useless to visitors. Let's face it -you know you will need to add it to the site. At least by making it the third link it isn't as distracting and doesn't interfere with the critical information.
Now that you've seen those pages - let's look at the code behind it. I'm not going to explain everything here - that's for another blog entry - but check out how simple the code is...
<div data-role="page" data-theme="e"> <div data-role="header">
<h1>Apex Hotels</h1>
</div> <div data-role="content">
<ul data-role="listview" data-inset="true">
<li data-role="list-divider">Welcome to Apex Hotels</li>
<li><a href="contact.html">Contact Us</a></li>
<li><a href="find.html">Find Us</a></li>
<li><a href="#about">About</a></li>
</ul>
</div> <div data-role="footer">
<h4>This is NOT a real Apex Hotels page.</h4>
</div> </div> <div data-role="page" id="about" data-theme="e"> <div data-role="header">
<h1>About</h1>
</div> <div data-role="content">
<p>
This is demo content.
</p>
</div> <div data-role="footer">
<h4>Page Footer</h4>
</div> </div> </body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Apex Hotels</title>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.css" />
<script src="http://code.jquery.com/jquery-1.5.min.js"></script>
<script src="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.js"></script>
</head>
<body>
That code covers the home page and the About page. Here is the code behind the Contact Us page.
<div data-role="header">
<h1>Contact Us</h1>
</div> <div data-role="content">
<p>
Reservations: <a href="tel:0845 365 0000">0845 365 0000</a><br/>
Email: <a href="mailto:reservations@apexhotels.co.uk">reservations@apexhotels.co.uk</a><br/>
</p>
</div> <div data-role="footer">
<h4>Page Footer</h4>
</div> </div>
<div data-role="page" data-theme="e">
Ok, now let's look at Find Us. This page does two things. First, it shows a simple map to the hotel. For this, I used the address of the hotel hosting the conference. Secondly, it provides a link that will be used for driving directions.
And here is the code behind that...
<div data-role="page" id="findusPage" data-theme="e"> <div data-role="header">
<h1>Find Us</h1>
</div> <div data-role="content">
<p>
Apex International Hotel, Edinburgh<br/>
31-35 Grassmarket<br/>
Edinburgh<br/>
EH1 2HS<br/>
Scotland<br/>
</p> <p>
<img src="http://maps.google.com/maps/api/staticmap?center=31-35+Grassmarket+Edinburgh+EH1+2HS+Scotland&zoom=15&size=200x200&maptype=roadmap
&sensor=false">
</p> <p>
<a data-role="button" id="drivingButton" data-theme="a">Get driving directions</a>
</p> </div> <div data-role="footer">
<h4>Page Footer</h4>
</div> </div>
By the way - notice that map there. It's using the static Google Maps API. Very simple, right? It would be better if I added a marker at the center address for the hotel. That wouldn't take long but for now, I'm skipping it. Ok, so how do we take that button and make it live? I went back to my main home page. This is always loaded first and therefore is a good place to store code like this. You can not - as far as I know, use $(document).ready within the page code above. This seems to work once, but not when the page is loaded again. Therefore we need to use the index page. jQuery Mobile gives us a few new events we can listen one - one of them being pagecreate. I used that and bound to the findUs page div to create the following code block:
});
$('#findusPage').live('pagecreate',function(event){
Ok - now let's get into the fun part. When I originally conceived of this demo, it occurred to me I couldn't do driving directions to the hotel since I was actually presenting at the hotel. That would be dumb. So while at the conference I decided to provide directions to Glasgow. Now that I'm back home, I decided on Baton Rouge instead. In order to get the directions though we need to figure out where we are. That's super easy on mobile browsers since you've got pretty universal support for geolocation:
navigator.geolocation.getCurrentPosition(function(pos){
var lat = pos.coords.latitude;
var lon = pos.coords.longitude;
As you can see, I simply fire off the geolocation request and grab the latitude and longitude in the response. To get driving directions, we need Google Maps. I added this to the top of my page:
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
And then back in my code I added a new instance of the directions service:
var dirApi = new google.maps.DirectionsService();
You can find the full API reference for this service here: http://code.google.com/apis/maps/documentation/javascript/reference.html#DirectionsService. But for now, we're going to make it simple. We create an object that will be used for our request.
dirReq = {};
dirReq.destination = "Baton Rouge, LA";
dirReq.origin = new google.maps.LatLng(lat,lon);
dirReq.travelMode = google.maps.DirectionsTravelMode.DRIVING;
Again - the use of Baton Route here is simply to make the demo work better for me. Ok, now for the fun part. I'm going to ask the direction service to create a route. The result is very complex, but essentially contains routes which contain steps. I'm going to assume we get one route and get the steps from there. I want to work with a much smaller set of data so I basically create my own, smaller and simpler copy:
dirApi.route(dirReq,function(dirResult,dirStatus) {
if (dirStatus != google.maps.DirectionsStatus.OK) {
alert("Driving API error: "+dirStatus);
}
//our steps are dirResult.routes[0].legs[0].steps
var steps = dirResult.routes[0].legs[0].steps;
var copy = dirResult.routes[0].copyrights;
console.dir(steps);
var niceSteps = [];
for (var i = 0; i < steps.length; i++) {
step = {};
step.text = steps[i].instructions;
step.distance = steps[i].distance.text;
step.duration = steps[i].duration.text;
step.endlat = steps[i].end_location.lat();
step.endlon = steps[i].end_location.lng();
niceSteps[niceSteps.length] = step;
}
Ok - so now we have - hopefully - a simple array of steps that include the text of the direction, the duration, the time, and even the longitude and latitude of where you end up. How do we present this to the user? We could simply dump all the steps onto the page. But that may be hard to work with on the device. What if instead we broke it into steps, like so....
Sweet, right? So how did I do it? The first thing I did was add a jQuery plugin to allow me to serialize data into JSON format. Once I did that, I was able to send the data to ColdFusion and store it:
$.post("store.cfm", {data:$.toJSON(niceSteps)}, function() {
$.mobile.changePage("driving.cfm");
});
All store.cfm does is take the JSON, deserialize that, and store it into the session scope.
<cfparam name="form.data" default="">
<cfset session.data = deserializeJSON(form.data)>
If you notice back in the JavaScript, when the post is done, we use a jQuery Mobile utility to load a new page. driving.cfm will simply paginate over the driving steps:
<cfparam name="url.step" default="1">
<cfset step = session.data[url.step]> <div data-role="page" data-theme="e"> <div data-role="header">
<h1>Driving Directions - Step <cfoutput>#url.step# of #arrayLen(session.data)#</cfoutput></h1>
</div> <div data-role="content">
<cfoutput>
<h1>Step #url.step#</h1>
<p>
<b>Duration:</b> #step.duration#<br/>
<b>Distance:</b> #step.distance#<br/>
</p>
<p>
#step.text#
</p> <p>
<img src="http://maps.google.com/maps/api/staticmap?center=#step.endlat#,#step.endlon#&zoom=14&size=200x200&maptype=roadmap
&sensor=false">
</p> <div data-inline="true">
<cfif url.step is 1>
<a href="" data-role="button" data-theme="c" data-inline="true">Previous</a>
<cfelse>
<a href="driving.cfm?step=#url.step-1#" data-role="button" data-inline="true">Previous</a>
</cfif>
<cfif url.step is arrayLen(session.data)>
<a href="" data-role="button" data-theme="c" data-inline="true">Next</a>
<cfelse>
<a href="driving.cfm?step=#url.step+1#" data-role="button" data-inline="true">Next</a>
</cfif>
</cfoutput> </div> <div data-role="footer">
<h4>Page Footer</h4>
</div> </div>
And that's it! As I said, this code is brittle. You will notice that if geolocation or driving directions fail we just use an Alert. There are much nicer things we could do. For example, we could ask the user where they are. If driving directions fail we could do what most places do and assume you are coming in from the closest major highway. But - hopefully you can see how easy it is to add in complex services like Google's driving directions within a simple jQuery Mobile site. To try this demo, hit the button below. But please - do not stress out if it fails for you. As I said, it is brittle, was built fast, etc, and at least kind of works.
I've included the full code for this demo as an attachment to this blog entry.
Archived Comments
I recently added the same info for a jQuery Mobile site for a client recently ;D
One good thing to note for telephone links: They should always include the country code. So for your number (in England), that would be: <a href="tel:+448453650000">0845 365 0000</a>. If someone is crazy enough to pay for international calls, this will work no matter what country you're calling from and, of course, from England as well.
Thanks Chris - I didn't think of that but it makes perfect sense.
This is utterly off-topic, and excuse the ramble, but that hotel is next to where I had my first encounter with computers.
While still at school in 1971 our maths teacher arranged evening sessions using Heriot-Watt University's computers, right next door to what is now Apex Hotel. Punched card input, and then a long wait until your rolled-up broadlisting printout came back with a rubber band round it. Assuming you hadn't made a typo on the punched cards.
I find it rather nice, but also kinda scary to see this demo of yours with it's shall we say more advanced I/O, just half a lifetime later. In 1971 you'd have been burned for witchcraft. Actually they used to have public hangings in the Grassmarket, although not in the '70's as far as I can remember
That may be off topic, but it's freaking cool. Thanks for sharing that Richard!
I wonder if you can somehow send address info to Google Navigation App on the phone.
If the app responds to URL schemes (um, 'scheme' may not be the best word, but like how you can use tel:// for example), then it would definitely be possible.
Well, the iPhone and Android phones will ask you how you want to open links that start with <a href="http://maps.google.at/etc">. Either in the browser or with the Google Maps App - which can then give you the directions from your current location ;D
Well a simple link to maps.google.com/maps?q=London on the iphone opens in the native map apps, and you can get directions from there. I dunno what is the best way to do it. As a user I prefer a lot the native map app, but there may be a lot of user not used to it and even not knowing how it works...
@cedric: I could see both being options. My way at least keeps you on the site which would make marketing happy ;) but the native apps will be slicker though.
Hi im trying to make this work, as a project for school, and so far I can get it to op the direction (driving.cfm) but all I get is
Step #url.step#
Duration: #step.duration#
Distance: #step.distance#
#step.text#
and the Jquery stuff.
am i missing something? tried to ad a google key, and tons of other stuff.. dont really know whats wrong :-/
by the way, i live in Europe, Denmark. don't know if it affects the maps in any way
Daniel
btw. nice scripting, a good challenge for me :)
It requires a ColdFusion server. If you are seeing the literals like that it implies it isn't installed on your server. In theory you could port it to PHP or Ruby pretty quickly.
ahh ok, thank you.
any hints or help about porting to php, i havent had any coldfusion before and im very new to Jquery....
Daniel
So to be clear, you know PHP, right?
Google has an official v3 plugin for jQM now. Pretty sure it didn't when you wrote this. http://code.google.com/p/jq...
I was able to whip up a directions from location fairly quickly at http://m.fullcityclient.com
If only it were this easy on Blackberry... eeeesh.
Nice. I did not know that. I assume the Driving Directions API part of my code isn't going to change though - just how you render the map. Even then - only if you want an interactive map.
Hi ray, I am getting Geolocation: undefined error
any ideas
What browser?
Mozilla, even chrome and opera too, IE 9 does not work as expected
i think the reason is i am using jquery version 1.7+ and live evnt is not supported, so i tried with delegate and on events, the alert did not popped up but neither the console message was displayed too. something is seriously missing i think
Not sure what to tell you. I just tried it and it worked fine in Chrome. Geolocation, as an object, works in all modern browsers. Should work in IE9 too.
But i think if you can try the same wih jquery 1.7+, That might show you some issue,
Oh, well, use the version of jQuery I used then. ;)
btw, it may work with jquery 1.5+ but if i use the old version, my Application is breakng
Donno why?
Have to check, i suppose
It definitely works with the version I used. Can't you just use that? I may be missing something here.
i do not know, but i just changed the event instead of live it should fire the on event which is new in jquery 1.7+, still ot firing, now nothing shows up, not even console, not even alert
$('#findusPage').on('pagecreate',function(event){
My question: how you are getting "pagecreate", in one of your posts, you used "pageshow"
:?
ok, clear abt the pageshow and pagecreate steps, i figured them out
Well, I dono, Your map example also do not work on 1.7 jquery version Plus
I'm sorry, what? What map example? And to be clear, I'm not sure what you are wanting me to help with here. If the code as written works with the jQuery library as written, then I'd use it... as written.
ok, i agree, i used jquery 1.5 and it works, but the new events in jquery 1.7 like props and other breaks when this used, all in all a good work done, but eventually to make itwork in jquery 1.7+, need to make few changes
Please Help me i'm having a problem:
i tried to run the DEMO on the SAMSUNG GALAXY TAB 7plus but unfortunately there was an error occured saying that DRIVING API ERROR: ZERO_RESULTS ive tried also running on desktop using all browser supported by jquery mobile but it happened again....PLEASE help me....Thanks
That means that - for whatever reason - it could not figure a way to give you directions from your location. My app should handle that nicer.
How to make it offline geolocation driving directions?because when youre not in a non wifi area you are not allowed to use the geolocation system
Offline is not the same as no wifi. Geolocation should work fine in no wifi if there is a gps device.
If not, you are kinda screwed. You can _ask_ the user for their location of course. Google's Driving Directions will accept that (of it not, you geolocate the address they give and use that).
how about using jquery mobile? is jquery mobile works even if thers no wifi?
jQuery Mobile is a UI framework. It has nothing to do with being online or off.
Great Raymond! Thanks, I'm trying to implement this on my website, hope it works, I'll come back and let you know later, but I will thank you know.
hey hi raymond,
its really great app but it is not running on my system ,i tried in both Mozilla and chrome but not working only...
when i clicked on "Get driving directions" button nothing is happening in url bar of a browser it just showing "this page contains elements from following site that tracking your location" expect this nothing is happening plz help me i need to impalement in my site ....
What do you see in the console?
Hi, I am looking for someone who can replicate the customizable html5 web app featured here: http://mobilepunchcardapp.c... I know it's probably a absic mobile website for developers, I have a wordpress plugin that creates a similar web app, but it's not what I need, I need the functionality advertised on the link above. Please can someone take a look, and let me know if they can do this for me? Thanks
Hi
Looks nice
Instead of using google maps....can i use my own image(ie my apartment), set markers and have directions appear lets say from my door to my room? Is this possible?
There is no reason why you couldn't skip the Google Static Map image and use your own.
Hi Raymond, and thanks for this awsome plugin.
I have a question. There is a way to draw the route like this http://hpneo.github.io/gmap...
Thanks
Yes. I'm willing to bet that plugin works with result sets from the directions API already.