StructureCMS

April 20, 2010

Google Maps – Create your own store locator

Filed under: AJAX, Web Development — joel.cass @ 12:43 pm

Recently I have had to create a store locator for a website I have been working on. On the outset, it sounds easy. Simply add a number of points to the map and calculate the distance between a point of reference and the points you have added.

Then the question comes up: How do you calculate distance? As the crow flies, or by road directions?

To test this out, I created a Google Maps Distance Calculator.

Road directions work, and are not too difficult to get from the API. However, they are dead slow and require callbacks. Sometimes a request may take up to 30 seconds, which means that it could take ages to render the distance between 10 or so points. Furthermore, sometimes the distance cannot be calculated at all.

So the more efficent and reliable way is to draw a line and measure it. That’s much easier and does not require a callback method:

var polyline = new GPolyline([
		point1.getLatLng(), // starting point
		point2.getLatLng() // finishing point
	],
	"#0000FF", // color
	1 // opacity
);
numDistance = polyline.getLength(); // in metres

I could go on about the intricacies of storing and sorting an array of points, but I won’t. Instead I have created a script that can do this for you. First off, you will need to get a google maps key. This is easy, you can do it here. Then, you need to call the google maps V2 API as follows:

<head>
...
<script type="text/javascript" src="http://www.google.com/jsapi?key=__________________KEY_________________"></script>
<script type="text/javascript">
	// initialise google maps
	google.load("maps", "2");
</script>
</head>
....

You will need to add some divs for containing the map and listing areas. By default, the map element is assumed to be a <div> tag and the listing container is a <table> tag.

<body>
...
	<div id="map"></div>
	<table id="table"></table>
...
</body>

(We will get into how you can change the listing container later)

Then, you will need to include the pointSorter.js script as follows:

<head>
...
</script>
<script type="text/javascript" src="pointSorter.js"></script>
<script type="text/javascript">
	// set target divs
	pointSorter.setDiv("map");
	pointSorter.setListingContainer("table");

	// load maps, point sorter etc
	google.setOnLoadCallback(function () {
		// initialise point sorter
		pointSorter.init();

		// add points
		...
	});
</script>
...
</head>

You may have noticed that I have not entered anything below “// add points” in the block above. There are two ways to add points:

// add point by coordinate
pointSorter.addPoint(
	new GLatLng(37.423156,-122.084917),
	"Google Inc.","<strong>Google Inc. Head Office</strong>"
);

// add point by address
pointSorter.addPointByAddress(
	"701 First Avenue, Sunnyvale, California",
	"<strong>Yahoo! Head Office</strong>"
);

Both methods take similar arguments. The first takes a GLatLng (point) object + a description, whilst the second takes an address and a description. The first method is much faster than the second as no request has to be made to the server to look up an address.

By now (after some style tweaking), your code may be looking something like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>
	<title>Maps Test</title>
	<style type="text/css">
		body { font-family:sans-serif; font-size:0.8em; }
		/* ID Selectors */
		#map {
			width:70%;
			height:500px;
			float:left;
		}
		#table {
			margin-top:1em;
			width:30%;
			height:500px;
			overflow-y:auto;
			float:right;
		}
		/* classes */
		.info { color:#008000; }
	</style>
	<script type="text/javascript" src="http://www.google.com/jsapi?key=_____________KEY____________"></script>
	<script type="text/javascript" src="pointSorter.js"></script>
	<script type="text/javascript">
	// initialise google maps
	google.load("maps", "2");

	// set target divs
	pointSorter.setDiv("map");
	pointSorter.setListingContainer("table");

	// load maps, point sorter etc
	google.setOnLoadCallback(function () {
		// initialise point sorter
		pointSorter.init();

		// add point by coordinate
		pointSorter.addPoint(
			new GLatLng(37.423156,-122.084917),
			"Google Inc.","<strong>Google Inc. Head Office</strong>"
		);

		// add point by address
		pointSorter.addPointByAddress(
			"701 First Avenue, Sunnyvale, California",
			"<strong>Yahoo! Head Office</strong>"
		);

	});
	</script>
</head>

<body>

<div id="map"></div>
<table id="table"></table>

</body>
</html>

If you run this in your browser, you should see a map appear with two locations shown. Clicking on either will reveal the name of the location.

Now the only thing left to do is allow users to enter in an address. This is as simple as adding the method and form below. When the form is “submitted”, a search is made for the address.

<script type="text/javascript">
	...
	function setLocation (location) {
		// set point of reference
		pointSorter.setPointOfReference(
			location,
			"<strong>Reference Location</strong><p>"+location+"</p>"
		);
	}
	</script>
</head>

<body>

<form action="" onsubmit="setLocation(this.address.value);return false;">
	<input type="text" name="address" />
	<input type="submit" value="Set Reference Location" />
</form>
...
</html>

When the form is submitted, a new point is added to the map and the distances are shown.

Here are some other tips about the script.

You can set icons for the map pointers by using the following methods:

// set icon for reference location
pointSorter.setHomeIcon("http://www.google.com/mapfiles/dd-start.png", 20, 34);
// set icon for surrounding point locations
pointSorter.setPointerIcon("http://www.google.com/mapfiles/dd-end.png", 20, 34);

If you have a lot of locations, you can limit the display to only list locations within a certain distance from the reference point, e.g. 100km:

// distance in kilometres
pointSorter.setDistanceThreshold(100)

if you are not going to use a table, you need to set the item template for listing items. The example below allows your items to be listed inside an unordered list (<ul>) tag:

pointSorter.setItemTemplate(
	"
  • " + " {description} ({distance_direct}km)" + "
  • " );

    November 30, 2009

    StructureCMS 1.3 Released.

    Filed under: StructureCMS, Web Development — joel.cass @ 3:43 pm

    Well, it’s been a while in the making, but the third point-release of StructureCMS has been released.

    What’s new?

    • Password Encryption
    • Installer Script
    • Latest TinyMCE
    • Integrated SWFUpload functionality with TinyMCE
    • Site settings node
    • Themes, including a suckerfish interface and a new lo-fi “classic” theme currently used on jozza.net
    • Admin display tweaks (logout link, tweaked buttons, menus etc)

    Give it a shot and let me know what you think!

    Project Home Page: http://code.google.com/p/structure-cms/

    Direct Download: http://structure-cms.googlecode.com/files/structurecms-1.3.zip

    Generating a memorable string and other CAPTCHA tips

    Filed under: Web Development — joel.cass @ 2:52 pm

    Recently I was asked to create a CAPTCHA interface for a form. Other than the pretty cool browser plugins already available, support for CAPTCHA in ColdFusion is pretty easy, considering that CF9 supports it with the CFIMAGE tag and tags such as LylaCaptcha have been around for a while.

    I’m not going to provide details on your implementation, but much rather some useful tips for adding CAPTCHA to your page.

    Firstly, make sure that users have to use CAPTCHA as little as possible. If a user has successfully posted a form with CAPTCHA once, assume that they are human for the rest of the session, or at least until what they wanted to post has been validated and completed. There is nothing more annoying than having enter CAPTCHA over and over again.

    Secondly, if other options are available, use them. For example, if you are targeting “robot” submissions, add a hidden field, and enforce validation that the field must be empty. Common robots just fill in all fields with crap containing URLs and advertisements for viagra. They rarely leave a field blank, so this method can work.

    Another option might be to just ask the user a question, e.g. “what colour is the sky”? Or “please enter the word “yes” below. You could even have a button populates the answer using javascript to save the user some time.

    So, if you’ve investigated the options above and still have to use CAPTCHA, here something that can make it easier for users to remember the string.

    Consider the two CAPTCHA’s below. Which one is easier to remember?

    FgSaNF or GaTsIF?

    jGuSlR or kArDeF?

    ajfhes or heslor?

    The secret comes from the ordering of the letters – the first sets (green) are simply random. The second set (red) follows a consonant-vowel-consonant pattern. Many words follow this pattern (dog, cat, cure, full, wombat, copcar, tophat, hello, cassette, mustache to name a few), so it’s easier for users to understand.

    I have some example code below can help in recreating these patterns:

    JavaScript:

    var alphas = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ";
    var vowels = "aeiouAEIOU";
    var aryChars = [alphas, vowels, alphas];
    
    function createCaptchaString (length) {
    	var strReturn = "";
    	var strChars = "";
    	for (var i = 0; i < length; i++) {
    		strChars = aryChars[i % aryChars.length];
    		strReturn += strChars.substr( Math.floor(Math.random() * strChars.length), 1);
    	}
    	return strReturn;
    }
    

    Note that alphas and vowels are represented by the two strings declared at the top. Currently all characters can appear as likely as each other. One could make some characters more likely to appear than others by inreasing the numbers of characters in each string, e.g. "aaaaaeeeeeeeiiiioouuu" would make "a" and "e" more likely to appear than "o" or "u".

    To take things even further, consonants such as "th", "ch", "ng" could be added to the list, but this would require the addition of a delimiter to the mix.

    August 27, 2008

    The sidebar is gay

    Filed under: Web Development — Tags: — joel.cass @ 12:14 pm

    The concept of a sidebar that contains descriptive content next to the main content in a HTML website is so gay. Sidebars are just stupid and they should have been banned from HTML. You see, no-one ever reads them. They violate the concept of flow in a document and pretty much just piss off all the people who have spent sleepless nights trying to get a 3 column fluid layout working in all browsers just because some selfishly naive designer thought it was a “good idea” at the time.

    Once again, sidebars are so gay. No one ever reads them. They are a waste of time.

    Edit: Interesting to note my site is now completely sidebar based. Oh how times change.

    August 18, 2008

    Is ColdFusion Dead?

    Filed under: ColdFusion, Web Development — Tags: — joel.cass @ 7:32 am

    I have been a long term developer and supporter of the ColdFusion cause. I have been developing in the language for more than 10 years and have come to appreciate it as a convenient and reliable way to build web applications.

    What is ColdFusion, may you ask? Well it is an application server language. In it’s day, people paid for this kind of thing, but these days application server languages are mainly free and/or cheap to install. However, Cold Fusion is not free, and it’s not that cheap. Anyway, that is beside the point.

    The thing is, ColdFusion has not changed much in the last 5 years, well not in my mind, at least. When ColdFusion MX came out in 2004 it was groundbreaking, extraordinary. The whole thing was re-written in Java. While it was not as fast as it’s predecessors it became reliable, and so many new things were added on – component support, web services, native Java support – it was such a breakthrough that it made any later release seem like a bugfix more than anything else.

    And in my mind that is exactly what CF7 was – a better working version of ColdFusion MX. Sure, it had some fluffy cfdocument tags thrown in and some minor syntax improvements but nothing revolutionary. CF8 introduced .net support and image manipulation, as well as better performance, but let’s face it, most people were using .net via web services (which is more standardised albeit less efficient) and using tools like CFC_Image to manipulate images. Furthermore, developers had started to adapt their practices to match the performance limitations of CF7, which, involved implementation of best practices anyway. So, CF8 wasn’t of any big help, to me at least.

    It leads me to consider that Adobe has effectively “shelved” ColdFusion – some people are baffled by this concept but let me explain. Consider that Adobe sells 1000 copies of ColdFusion every year – lets face it that even this figure is ambitious considering that most people don’t need to buy a copy of CF every year and that many new projects are looking to start in Java or .net these days. Anyway, 1000 copies equals around 4 million dollars, given that all copies are the enterprise version.

    However, Adobe would only receive half of this due to retail markup and distribution costs. Out of this comes marketing, admin, and support costs, which might leave around a million dollars for Adobe to play with. Consider that the average developer costs 100K per year and say that they have 5 or 6. Then there might be a manager or two, or even a tech lead who gets a little more – after all this Adobe might get a little profit – then there’s taxes… Which leads me on.

    Adobe ask for around 25K for an enterprise license of LiveCycle, which, like ColdFusion is a Java based app that generates business documents (no, really) – Yet they can only ask around 4K for a ColdFusion license – why would this be so if Adobe hadn’t realised that about 4K is all they are going to get before they start scaring developers off? So it leaves them between a rock and a hard place.

    Given that ColdFusion is pretty well established as an app server, in that it is relatively bug free and reasonably reliable – do they even need to continue development, or just shelve it and use it to push their *other* front end products? I would be leaning toward the second option.

    Which leads me to the next point. In my mind, ColdFusion is “old hat” – while it was excellent in the 90’s and pretty good for the first half of this century, it’s becoming out-competed by the likes of .net and Java – not due to ease of development nor performance, but simply because more people out there know Java and .net – these languages are taught widely and are more trusted, simply due to the reputation of their respective vendors.

    As said earlier, Adobe are between a rock and a hard place – and this is supply and demand in action – if they made it cheaper, more people would use it. However, then they would need to increase support and invest in their reputation as an application services vendor and for that they would need money, hence they would need to increase prices and effectively kill demand.