nooshu - Matt Hobbs' Web Development Blog

Kneeling on the shoulders of giants

Marker Cluster Calculator for Google Maps v3

If you’ve ever worked with the Google Maps API for a large number of markers I’d say you’ve run into this issue; how do you manage hundreds of markers on the map without killing your browser? Now admittedly over the past few years browsers have improved massively (thanks to the “browser wars 2.0″), but handling a large number of markers is a sure-fire way to make even modern browsers fall over.

So what’s the answer? Thankfully there are helper libraries that allow you to cluster markers together, so the browser only has to draw one icon, rather than fifty. An excellent article on the subject was written in 2010 by Luke Mahe and Chris Broadfoot of Google. In it they go into great detail on what the problem is, and how to solve it with a sprinkle of JavaScript magic.

For a recent project I required marker clustering, as there were 500+ markers to be rendered on the map. I decided to use the MarkerClusterer library to do the job. It applies grid-based clustering, each marker is added to its closest cluster group within the set bounds. Everything worked perfectly until the client requested a change to how many cluster groups there were. Luckily there is a setCalculator() method that allows you to modify how clusters are defined and influence the styling, but looking around the net I saw very little explanation of what exactly this calculator function was doing, so I’ve created a small demo and heavily commented the setCalculator function for future users.

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
/**
* Set our own custom marker cluster calculator
* It's important to remember that this function runs for EACH
* cluster individually.
* @param  {Array} markers Set of markers for this cluster.
* @param {Number} numStyles Number of styles we have to play with (set in mcOptions).
*/

markerCluster.setCalculator(function(markers, numStyles){
    //create an index for icon styles
    var index = 0,
    //Count the total number of markers in this cluster
        count = markers.length,
    //Set total to loop through (starts at total number)
        total = count;

    /**
     * While we still have markers, divide by a set number and
     * increase the index. Cluster moves up to a new style.
     *
     * The bigger the index, the more markers the cluster contains,
     * so the bigger the cluster.
     */

    while (total !== 0) {
        //Create a new total by dividing by a set number
        total = parseInt(total / 5, 10);
        //Increase the index and move up to the next style
        index++;
    }

    /**
     * Make sure we always return a valid index. E.g. If we only have
     * 5 styles, but the index is 8, this will make sure we return
     * 5. Returning an index of 8 wouldn't have a marker style.
     */

    index = Math.min(index, numStyles);

    //Tell MarkerCluster this clusters details (and how to style it)
    return {
        text: count + " ("+ index + ")",
        index: index
    };
});

Hopefully it’s all self-explanatory as to what the method is doing. Each cluster has its own index, this defines what icon is used. You can modify these icons and add your own, and also change how many cluster levels there will be.

You can view the simple demo here, here’s a direct link to the map JavaScript file.

A visualisation experiment using SoundCloud, Backbone.js, three.js and node.js

Client work, as always, has been busy over the past month or so; so it’s always refreshing to have a play with something a little different. It’s even better when you can actually combine the two! Visual Jazz Isobar decided to have a little code competition open to all the developers in the company. The brief was very simple: build something cool using an external API (bonus points for multiple API’s). Very open brief don’t you think? You only have to have a look at the list of API’s available on programmable web to see that.

As I’ve recently been looking at Backbone.js I decided to put what I’ve learned good use. The experiment evolved over a couple of weeks. I originally planned to use d3.js and last.fm but eventually settled on three.js and SoundCloud. My final submission looked like this:

Visualisation using Backbone.js, Node.js and Three.js

My little experiment using the SoundCloud API and three.js (with a sprinkling of node.js)

To be honest I could have built the experiment easily without using Backbone, but where is the challenge in that! I love the whole Model, View, Router set up. You do seem to be writing a lot of code a first for very little functionality, but once you are over the initial set up you can add features very quickly. I’ll definitely be using it again for future projects.

The visualisation itself makes use of the Web Audio API. The API is still being developed and standardized, because of that it only runs in Chrome at the moment. Three.js itself runs fine in Firefox, but no audio visualisation.

During development I stumbled across an issue with Chrome and the way the audio buffer works; for the data to load, the music files need to be on the same server as the application. This was a bit of a problem as I was using the SoundCloud API for music streaming. That’s where node.js and Nodester came to the rescue. With a little bit of node.js magic it’s fairly simple to create a proxy server and ‘bounce’ the stream from SoundCloud via the server to the visualisation. A big downside to this method is the music stream (mp3) has to be fully downloaded before the visualisation starts. For this reason only tracks under 6 minutes are returned as search results from SoundCloud. It’s not ideal but it work!

When the competition results came in I was placed second; not first but hey I learnt a lot in the process! You can take a look at my little experiment here.

Auto-spacing input field text

On a recent project, a set of wireframes landed on my desk which contained some “interesting” ideas, particularly when it came to form fields. One that really stood out was having a credit card number auto-space as the user types it into a single input box. This of course would depend completely on what type of card they are entering, as different cards have different layouts. American Express for example have a [4, 6, 5] layout, where as Visa use a [4, 4, 4, 4] number layout.

Thankfully this functionality was eventually removed from the UI specifications after concerns from myself and others were raised. Just to make this perfectly clear; I think this is a bad idea, and here is why:

  • Users are very wary when it comes entering their credit card details. Spaces magically appearing in their credit card number could freak a few of them out.
  • Most e-commerce website I’ve ever used ask for the credit card number as one long string of numbers with no spaces. Auto-spacing goes against this convention, so a user may actually go back and try to remove the spaces (I know I would).
  • Validation scripts for credit card numbers usually ask for the input to have no spaces, or will remove any spaces on submit.
  • If you do want to space out the credit card number, why not use different input boxes for each set of numbers? It looks cleaner and is simpler to implement. Just have your JavaScript automatically jump between boxes as the user types.

Even though this functionality was removed, I decided to build a prototype of it in action because… well because I can and I was bored. :) Please, (please!), whatever you do don’t use this code in production. I created it as an experiment and should be treated that way. It’s very simple to get around the auto-spacing and then all hell breaks loose! (not really, but it is easy to break).

The spacing is implemented by counting the length of the string inside the input field, but you could also monitor the current position of the cursor. Anyone wishing to do this see this excellent set of answers on Stackoverflow.

You can see the prototype here. Remember, don’t use it. It’s not big and it’s not clever :)

Using the Fullscreen and Pointer Lock API’s

This post is a couple of months late due to real life getting in the way, but I finally got around to experimenting with the Fullscreen and Pointer Lock API (formally known as the Mouse Lock API). Both are still in the experimental stage, Pointer Lock especially. To get it to work you will have to be running the latest Canary build of Chrome (currently 19.0.1067.0) and enable it via about:flags. I’ve had no luck getting Pointer Lock working in the latest version of Firefox and Aurora, even though most documentation on the API is written by Mozilla. Maybe there’s a setting I’m missing?

So what exactly do the Fullscreen and Pointer Lock API’s do you may ask. Well Fullscreen is obvious; it allows you to view any web content in fullscreen mode, removing the browser UI for distraction free reading. Pointer Lock gives a developer access to raw mouse movement data, not just the absolute position of the cursor. You are no longer limited to the size of the browser window, so you can scroll continuously in one direction without having to stop. Where both of these API’s come in very handy is the development of HTML5 games. It’s now possible to create a games like Quake 3 directly in the browser with usable controls… impressive!

To have a play with both API’s I put together a little experiment using three.js and a panoramic image.

Fullscreen, Pointer Lock and three.js

What a beautiful day in Sydney, just round the corner from where I live.

I started by building on an excellent blog post by Paul Lewis called “Create your own environment maps”. Using Microsoft’s Photosynth App and Blender it’s dead simple to create a panoramic image. After adapting Paul’s code to my needs, I added the Fullscreen and Pointer lock API’s. To enable Fullscreen mode press enter. If you have a compatible browser, the browser UI will disappear and you will enter fullscreen mode. If Pointer Lock is available, a prompt will appear asking for permission to hide your cursor. You will now be able to scroll left and right continuously without having to stop, if Pointer Lock isn’t available the demo will drop back to standard mouse co-ordinates.

A quick tip for working with both of these API’s: use a shim like that made by Brandon Jones, it will save you a lot of cross browser pain! Browser vendors are still changing the way both of these API’s are implemented, so until this stabilises, using a shim is the way to go. Big thank you to Paul and Brandon for their excellent contributions.

HTML5 date input type on mobile

Whilst developing a new mobile only website, the UX team specified in the wireframes that a datepicker was needed for an input field; but nowhere else on the site was a datepicker used, so I really wanted to avoid using a JavaScript heavy solution. I couldn’t justify including so much extra JavaScript, CSS and images for so little usage. So what other options do we have?

Well there is a jQuery plug-in called Mobiscroll which is around 16KB is size and works on iOS and Android devices. Unfortunately there’s no mention of Windows-based mobiles which is a real shame, as it’s almost a perfect solution. The other option we have is to take advantage of some cutting edge HTML5 form input types; the date input type. Support is nowhere near 100% yet, but if you approach its usage from a progressive enhancement standpoint, it works well. On mobile browsers that support it, the user gets a fancy datepicker that’s very intuitive; browsers that don’t simply drop back to a standard text input. It’s worth noting that Modernizr can’t detect that date inputs create a datepicker, since Modernizr can’t do it I assume it isn’t possible. Damn!

An example of the date input type on iPhone, iPad and iPods.

The date input type as displayed on iOS mobile Webkit devices.

Once I’d decided on how the date picker was going to work (HTML5) it was time to implement it and test it across my available mobile browsers. Implementation was dead simple:

1
2
<!-- Fancy datepicker on supported devices, standard text input on unsupported -->
<input type="date" name="fancyDate" id="fancyDate">

Where things got a little tricky (and annoying) was the client-side validation of the input. Since the wireframes contained lots of forms with a varying range of input types and requirements, I decided to go with quite a heavyweight validation script (around 21KB minified). This turned out to be a very good decision in the end, as there are a few quirks to the date input element.

First thing I had to consider was how were users going to input the date when they don’t have the fancy datepicker? The most intuitive way (at least for me) is they simply enter in the format dd/mm/yyyy. Great, that’s sorted! And guess what, when a user uses the fancy datepicker it also returns a value of dd/mm/yyyy…. no, no it doesn’t! It looks like that’s the value that will be returned as that’s how it is displayed, but no, it’s never that easy. Webkit actually returns the date in the format yyyy-mm-dd, not quite what I was expecting! Unfortunately this breaks our validation as it is looking for a format of dd/mm/yyyy!

So what’s the fix? Well you could take the easy route and make your non-fancy datepicker users enter the date in the yyyy-mm-dd format; but that just feels wrong. No user is going to expect to enter a date like that, it’s completely unintuitive! No I’m afraid it’s going to take a little hack to fix this issue. The hack involves using a second input field of type “text”; when a user interacts with the fancy datepicker or enters in the date manually, the date is “cleaned up” and passed along to the second input field. You then do your form validation against this second field. You can see an example of this working here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * iOS input type="date" returns a value of yyyy-mm-dd even when
 * it displays dd/mm/yyyy. This breaks validation. Fix for this using a second input.
 * Keyup event for testing in a browser, not needed for mobile.
 */

$("input[type='date']").on("blur keyup", function(e){
    var $this = $(this),
        value = $this.val();

    //Does the input have "-", if so it is from the webkit datepicker, fix it
    if(value.indexOf("-") !== -1){
        var cleanDateArray = value.split('-');
        value = cleanDateArray[2] + "/" + cleanDateArray[1] + "/" + cleanDateArray[0];
    }

    //Set the hidden value to validate on, trigger the blur and keyup event for validation as you type
    $("#hiddenDateField").val(value).trigger("blur").trigger("keyup");
});

A couple of points to note about using this method. Even though we aren’t validating the datepicker input, we still have to set “date: false” in the validation rules. This is because the validation plug-in automatically tries to validate date inputs. Validation fails because it doesn’t like the dd/mm/yyyy format. I’ve also used the dateITA method to validate the date on the “hidden” input (you can find this in the additional-methods.min.js file). This additional method is more robust than the plug-ins standard date validation. A minor pitfall with this hack is that setting the second input to type “hidden” will stop its validation; this is due to the validation plug-in ignoring hidden inputs. So to hide the input you can set “display: none” in the CSS (urghh I know, I feel dirty too!).

So there you have it, that’s how I solved validating a date input on mobile with fancy datepicker and standard text fallback. It’s not ideal by any means, but it works. Another solution would be writing a validator Regex that accepts both dd/mm/yyyy and yyyy-mm-dd date formats, but this means returning both types of dates to the server on submit. Due to server-side constraints with the project, this isn’t an option (boo!). Is there a much simpler method that I’m completely missing? If so please leave a comment! :)

Currently on page 1 of 2512345Last »