<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.thecodetrain.co.uk/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>The Code Train</title>
	
	<link>http://thecodetrain.co.uk</link>
	<description>Where Neil Crosby talks about coding on the train...</description>
	<lastBuildDate>Sun, 03 Apr 2011 18:15:12 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.thecodetrain.co.uk/TheCodeTrain" /><feedburner:info uri="thecodetrain" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Making PHP CodeSniffer use JSLint via Rhino</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/QRvGrDy54iI/</link>
		<comments>http://thecodetrain.co.uk/2011/04/making-php-codesniffer-use-jslint-via-rhino/#comments</comments>
		<pubDate>Sun, 03 Apr 2011 18:15:12 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jslint]]></category>
		<category><![CDATA[php codesniffer]]></category>
		<category><![CDATA[phpcs]]></category>
		<category><![CDATA[rhino]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=505</guid>
		<description><![CDATA[<p>Last week at work, we started writing some JavaScript for the project we&#8217;re working on. Up until now we&#8217;ve been very much focusing on core functionality, but we&#8217;re at the stage now where writing some JavaScript makes sense.  One of the things that we&#8217;ve been doing up un til this point, and has been making sense for us, is to use <a href="http://pear.php.net/package/PHP_CodeSniffer/">PHP Code Sniffer</a> to help make sure that the PHP and CSS we&#8217;re writing adheres to known standards and has a maintainable shape.</p>

<p>So, since PHP Code Sniffer can also run <a href="http://www.jslint.com/">JSLint</a>, we thought we&#8217;d turn on JavaScript sniffing as well.  It all sounds so easy, doesn&#8217;t it?</p>

<p>Ultimately, it is pretty easy to get PHP Code Sniffer running JSLint, but there were a whole bunch of hurdles that ended up taking me a couple of days to get over. So, I thought a blog post was in order. Here&#8217;s what you need to do:</p>

<ol>
<li><p>Have a working installation of Java.</p></li>
<li><p>Have a working copy of PHP Code Sniffer, installed via PEAR.</p></li>
<li><p>Have a working installation of <a href="http://www.mozilla.org/rhino/">Rhino</a>.</p>

<p>Depending on your system you&#8217;ll want to do one of two things:</p>

<ul>
<li><p>The simplest option is to install a Rhino binary, by doing something like the following on your system:</p>

<pre><code>apt-get install rhino
</code></pre>

<p>This will put a <code>rhino</code> script on your executable path, and you&#8217;re good to go.</p></li>
<li><p>Alternatively, you&#8217;ll need to <a href="http://www.mozilla.org/rhino/download.html">download Rhino</a> from Mozilla&#8217;s website. Unfortunately, this download doesn&#8217;t contain a <code>rhino</code> passthrough script (which PHP Code Sniffer will later need), so we&#8217;ll need to create one. Thankfully it&#8217;s pretty simple, and can look like this:</p>

<pre><code>#!/bin/sh
java -jar `dirname $0`/js.jar $@
</code></pre>

<p>Here, we&#8217;ve made the assumption that we&#8217;ve put the <code>rhino</code> shell script and the <code>js.jar</code> file (the only file we need from the download) into the same directory.</p></li>
</ul></li>
<li><p>Create a JSLint file that Rhino can use.</p>

<p>For this, you&#8217;ll need to travel back in time to before Crockford stopped supporting Rhino. </p>

<ol>
<li><p>Download the <a href="https://github.com/douglascrockford/JSLint/raw/ca120a731db548c0014320fa0c196edc613536ae/rhino.js">last version of the <code>rhino.js</code></a> file from GitHub.</p></li>
<li><p>Download the <a href="https://github.com/douglascrockford/JSLint/raw/master/fulljslint.js">latest version of <code>fulljslint.js</code></a> from Github.</p></li>
<li><p>Concatenate the two files together.</p>

<pre><code>cat fulljslint.js rhino.js &gt; ultimatejslint.js
</code></pre></li>
</ol></li>
<li><p>Tell PHP Code Sniffer where your <code>rhino</code> and <code>ultimatejslint.js</code> files are:</p>

<pre><code>phpcs --config-set rhino_path /path/to/rhino
phpcs --config-set jslint_path /path/to/jslint.js
</code></pre></li>
<li><p>Run PHP Code Sniffer with a standard that knows about JSLint.  </p>

<ul>
<li><p>Either use the Squiz standard: </p>

<pre><code>phpcs --Standard=Squiz .
</code></pre></li>
<li><p>Or, add the Squiz JSLint Sniff to your own standard. </p>

<p>In your standard&#8217;s <code>getIncludedSniffs()</code> function, add the following Sniff to the returned array:</p>

<pre><code>'Squiz/Sniffs/Debug/JSLint.php'
</code></pre></li>
</ul></li>
</ol>

<p>And that&#8217;s it. There&#8217;s a few steps, but once you&#8217;ve done them once, JSLint issues will show up as warning in your reporting.  If you want to change the flags that JSLint has turned on, you&#8217;ll need to update the array at the start of the <code>rhino.js</code> file.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>Last week at work, we started writing some JavaScript for the project we&#8217;re working on. Up until now we&#8217;ve been very much focusing on core functionality, but we&#8217;re at the stage now where writing some JavaScript makes sense.  One of the things that we&#8217;ve been doing up un til this point, and has been making sense for us, is to use <a href="http://pear.php.net/package/PHP_CodeSniffer/">PHP Code Sniffer</a> to help make sure that the PHP and CSS we&#8217;re writing adheres to known standards and has a maintainable shape.</p>

<p>So, since PHP Code Sniffer can also run <a href="http://www.jslint.com/">JSLint</a>, we thought we&#8217;d turn on JavaScript sniffing as well.  It all sounds so easy, doesn&#8217;t it?</p>

<p>Ultimately, it is pretty easy to get PHP Code Sniffer running JSLint, but there were a whole bunch of hurdles that ended up taking me a couple of days to get over. So, I thought a blog post was in order. Here&#8217;s what you need to do:</p>

<ol>
<li><p>Have a working installation of Java.</p></li>
<li><p>Have a working copy of PHP Code Sniffer, installed via PEAR.</p></li>
<li><p>Have a working installation of <a href="http://www.mozilla.org/rhino/">Rhino</a>.</p>

<p>Depending on your system you&#8217;ll want to do one of two things:</p>

<ul>
<li><p>The simplest option is to install a Rhino binary, by doing something like the following on your system:</p>

<pre><code>apt-get install rhino
</code></pre>

<p>This will put a <code>rhino</code> script on your executable path, and you&#8217;re good to go.</p></li>
<li><p>Alternatively, you&#8217;ll need to <a href="http://www.mozilla.org/rhino/download.html">download Rhino</a> from Mozilla&#8217;s website. Unfortunately, this download doesn&#8217;t contain a <code>rhino</code> passthrough script (which PHP Code Sniffer will later need), so we&#8217;ll need to create one. Thankfully it&#8217;s pretty simple, and can look like this:</p>

<pre><code>#!/bin/sh
java -jar `dirname $0`/js.jar $@
</code></pre>

<p>Here, we&#8217;ve made the assumption that we&#8217;ve put the <code>rhino</code> shell script and the <code>js.jar</code> file (the only file we need from the download) into the same directory.</p></li>
</ul></li>
<li><p>Create a JSLint file that Rhino can use.</p>

<p>For this, you&#8217;ll need to travel back in time to before Crockford stopped supporting Rhino. </p>

<ol>
<li><p>Download the <a href="https://github.com/douglascrockford/JSLint/raw/ca120a731db548c0014320fa0c196edc613536ae/rhino.js">last version of the <code>rhino.js</code></a> file from GitHub.</p></li>
<li><p>Download the <a href="https://github.com/douglascrockford/JSLint/raw/master/fulljslint.js">latest version of <code>fulljslint.js</code></a> from Github.</p></li>
<li><p>Concatenate the two files together.</p>

<pre><code>cat fulljslint.js rhino.js &gt; ultimatejslint.js
</code></pre></li>
</ol></li>
<li><p>Tell PHP Code Sniffer where your <code>rhino</code> and <code>ultimatejslint.js</code> files are:</p>

<pre><code>phpcs --config-set rhino_path /path/to/rhino
phpcs --config-set jslint_path /path/to/jslint.js
</code></pre></li>
<li><p>Run PHP Code Sniffer with a standard that knows about JSLint.  </p>

<ul>
<li><p>Either use the Squiz standard: </p>

<pre><code>phpcs --Standard=Squiz .
</code></pre></li>
<li><p>Or, add the Squiz JSLint Sniff to your own standard. </p>

<p>In your standard&#8217;s <code>getIncludedSniffs()</code> function, add the following Sniff to the returned array:</p>

<pre><code>'Squiz/Sniffs/Debug/JSLint.php'
</code></pre></li>
</ul></li>
</ol>

<p>And that&#8217;s it. There&#8217;s a few steps, but once you&#8217;ve done them once, JSLint issues will show up as warning in your reporting.  If you want to change the flags that JSLint has turned on, you&#8217;ll need to update the array at the start of the <code>rhino.js</code> file.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/QRvGrDy54iI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2011/04/making-php-codesniffer-use-jslint-via-rhino/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2011/04/making-php-codesniffer-use-jslint-via-rhino/</feedburner:origLink></item>
		<item>
		<title>Dev Checks – Improving code quality by getting developers to look at each other’s work</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/9yDROqGdfoo/</link>
		<comments>http://thecodetrain.co.uk/2011/01/dev-checks/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 21:44:20 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[article]]></category>
		<category><![CDATA[bbc]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[code review]]></category>
		<category><![CDATA[dev check]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[team]]></category>
		<category><![CDATA[yahoo]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=492</guid>
		<description><![CDATA[<p>I&#8217;ve been working on the BBC&#8217;s Homepage for about a year now, leading a team of five front-end developers to create a high quality product.</p>

<p>One of the processes that I introduced to the team about six months ago was that of a formal &#8220;dev check&#8221; after every task that we completed from our backlog.  Whilst we call this process a dev check within our team, it could just as easily be called a peer review, or mini code review.</p>

<p>Up until the point that I introduced the dev check it had not been uncommon for a single person to work on a task, and for code to go through the testing environments and onto live without a single other human ever reading their code. Not the best situation to be in, and it meant that we ended up having more annoying bugs showing up on our test environments than we should have.  These bugs were often things that a simple bit of human scrutiny would have caught, and we ended up wasting time going back and fixing them after the fact.  Working this way also meant that we ended up with certain areas of the homepage that could only ever be worked on by certain people, because they were the only ones who had ever seen the code.</p>

<p>Like I said; not the best situation to be in.</p>

<p>So, six months ago, we introduced the formal dev check &#8211; a step to be completed for every single code related task in our backlog.  And, I&#8217;m happy to say it&#8217;s been a very successful process for us &#8211; we now have far fewer bugs getting to test, and we have a lot more shared knowledge within the team.</p>

<h2>What is a Dev Check?</h2>

<blockquote>
  <p>Before any task is committed to trunk, a developer who didn&#8217;t work on it must say that they are happy with  how it&#8217;s been completed.</p>
</blockquote>

<p>I&#8217;m hoping that if you&#8217;re still reading at this point that you&#8217;re wanting to find out what goes into a successful dev check&#8230;</p>

<ol>
<li><p>The task is &#8220;completed&#8221; by Rachel.</p>

<p>At this point she has not committed anything to the trunk of the project. Depending on the size of the task, she might have created a development branch which she&#8217;ll have been committing to.</p></li>
<li><p>Rachel asks another member of the development team to come and do a dev check. On this occasion she asks Jon.</p></li>
<li><p>Jon comes over to Rachel&#8217;s desk where she will talk through all the changes she has made.</p>

<p>As part of the discussion, Rachel may realise that some of the things she thought made sense in fact didn&#8217;t. This is great, and she&#8217;ll make the changes necessary and continue with the dev check.</p></li>
<li><p>After Rachel has finished explaining what she&#8217;s done for the task, and Jon&#8217;s asked as many questions as he feels is necessary he&#8217;ll either give the go ahead to commit the code to the trunk, or he&#8217;ll request various changes to be made to the codebase.</p></li>
<li><p>Rachel will then commit to trunk, build in the team&#8217;s test environment and pass off to the testers.</p></li>
</ol>

<p>And that&#8217;s a dev check.</p>

<p>Out of this process Jon has learnt about a bit of the codebase that he might not otherwise have seen, Jo realised that she needed to shore up a couple of her tests, and the codebase has come out feeling more healthy.  This is A Good Thing™.</p>

<h2>What sort of things will the dev checker ask?</h2>

<p>Up above there in the &#8220;What is a Dev Check&#8221; section you&#8217;ll have read about Jon asking &#8220;as many questions as he feels is necessary&#8221;, and I&#8217;m sure you&#8217;ll have said to yourself &#8220;But Neil, what are those questions? How will I know what&#8217;s sensible to ask?&#8221;.</p>

<p>Well, you&#8217;re in luck. By a process of trial and error, as a team we&#8217;ve come up with a bunch of questions that, if asked, will help to make sure that you both understand what&#8217;s going on and can confirm that the code written is of a high quality.</p>

<ul>
<li><p>Have new tests been written?</p>

<p>These might be Unit Tests, or Automated Front-End Tests, or Security Tests. What matters is that valid tests have been written to cover the new functionality. You should also make sure that any pre-existing tests make sense.</p></li>
<li><p>Do all the tests still pass?</p>

<p>It should be a no-brainer, but of course you should make sure that all the tests still pass. There&#8217;s nothing worse that breaking The Build for the entire team just because you forgot to run a bunch of tests on your development machine first.</p></li>
<li><p>Are standards adhered to?</p>

<ul>
<li>PHP Code Sniffer</li>
<li>HTML/CSS Validation</li>
<li>JSLint</li>
<li>Whatever else works in your team</li>
</ul>

<p>These are important to us. We fail our builds if our code isn&#8217;t of high enough quality. Just sayin&#8217;.</p></li>
<li><p>Does what you&#8217;ve written work across all the Level One browsers?</p>

<p>Obviously this one&#8217;s a bit web development specific as it is, but it&#8217;s important in other spheres as well. Every piece of software that&#8217;s written is expected to run in one or more environments, and you should at least smoke test in a known subset of them to make sure there are no nasty surprises for your dedicated testing team.</p></li>
<li><p>Can I see your diffs?</p>

<p>Again, fairly obvious, but you should take a look to see what&#8217;s changed since the last time the code was checked in. Don&#8217;t just rely on the developer to remember what they&#8217;ve changed &#8211; I know I can&#8217;t remember half the time. This is what machines are good at &#8211; make use of them!</p></li>
<li><p>Did any of the changes that were made affect any other code?</p>

<p>This one should be obvious as well, but unfortunately it isn&#8217;t. It&#8217;s all too easy to look at the lines of code that have changed without looking at the later lines which make use of them. Don&#8217;t forget to look at the whole system.</p></li>
<li><p>Is there anything that concerns you?</p>

<p>The final question to ask, and in many cases the most important one. Oftentimes a developer will know that something smells a bit fishy, but keeps quiet about it in the hope that they won&#8217;t get called on it.  By simply asking these questions though, we&#8217;ve found that these things end up getting talked about, and we end up with better code because of it.</p></li>
</ul>

<h2>How long does a dev check take?</h2>

<p>After reading all that, I wouldn&#8217;t be surprised if you&#8217;re thinking &#8220;Woah! But how long does that all take? That seems like a long list of questions…&#8221;.</p>

<p>Well, in my team, we find that a dev check will take a little less than 10% of the time that the code and tests took to write initially. So, if the task took an hour to complete, the dev check would take roughly 5 minutes. A three hour task would take 15 minutes, and so on.  It honestly doesn&#8217;t take much time once you get into the swing of things.</p>

<h2>The Power of Veto</h2>

<p>One of the most important things about dev checks is that everyone on the team is equal. Rachel may be the team leader, but Jon can still challenge her code and say that actually she hasn&#8217;t done everything required to &#8220;pass&#8221; the dev check.</p>

<p>The person performing a dev check <em>always</em> has power of veto to say that things, however small, need improving before code is allowed to be committed to the trunk.</p>

<p>Ultimately, the team is responsible for its own output, and everyone should be invested in that.</p>

<h2>What do dev checks achieve?</h2>

<p>As I&#8217;ve mentioned above, there are two main benefits to dev checking:</p>

<ul>
<li>Fewer bugs</li>
<li>More knowledge of the codebase by more of the team.</li>
</ul>

<p>Essentially the team will be more effective.</p>

<h2>How do you get buy in?</h2>

<p>Whilst there are obvious benefits to dev checks, sometimes you&#8217;ll need to gain buy-in from people before you can get started. Maybe it&#8217;ll be team members who think that this extra process is unnecessary, or maybe it&#8217;ll be The Boss who thinks that it&#8217;ll all just take far too long. What do you say to them?</p>

<h3>Getting Buy In From Team Members?</h3>

<ul>
<li>Everyone is equal.</li>
<li>Anyone is able to say that a dev check cannot yet be completed.</li>
<li>The team is responsible for the quality of the team&#8217;s output, not just one person.</li>
<li>You won&#8217;t end up getting stuck on a horrible portion of the codebase any more, just because you were the last person to write code for it.</li>
</ul>

<h3>Getting Buy In From The Boss?</h3>

<p>If you&#8217;re lucky, you won&#8217;t need to get buy-in from The Boss, but in case you do, here are a couple of pointers:</p>

<ul>
<li>Quality is improved, leading to fewer bugs and happier users.</li>
<li>The amount of time spent on dev checking is smaller than than that which would be spent fixing the bugs that would otherwise get through. The project will go live sooner, and you&#8217;ll have spent less money.</li>
</ul>

<h2>Improving the Dev Check</h2>

<blockquote>
  <p>Any bug that is found is an opportunity to learn and improve our process.</p>
</blockquote>

<p>Of course, sometimes bugs will still get through. When they do, you need to sit down and work out why that happened.</p>

<p>In the old world, we used to just accept that bugs happened, that they&#8217;d get found, and we would fix them. No more! Now, any bug that is found is an opportunity to learn and improve our process.</p>

<p>A real example of this happening came a few weeks ago. Tina had been doing her thing, updating a script that we use to help us with our release management. This was dev checked by Bradley, and as far as he was concerned the changes that had been made were good to go. So, he ticked off the dev check, and Tina carried on with her next task.</p>

<p>Fast forward to a week later, and Paul was getting <del>down on the floor</del> ready to give a presentation which required a screenshot of the script running. Unfortunately, it no longer worked &#8211; shock horror! </p>

<p>What had happened was that Bradley hadn&#8217;t realised that the line of code that was changed had impacted on further lines in the script. Moreover, he&#8217;d only seen the script run against the additional new requirements, and hadn&#8217;t confirmed that it still ran against the original requirements. Whoops.</p>

<p>What came out of this was an improvement to our dev checks &#8211; if code has changed we need to make sure that nothing else in our code has been affected by that change. It sounds simple, but it was a failing that we had, so we improved upon it.</p>

<h2>Why not just have a big code review?</h2>

<p>Big code reviews are great, and are something I very much enjoyed participating in when I was at Yahoo!. Unfortunately, whilst they&#8217;re very useful they&#8217;re also pretty time consuming, and require a fair amount of setup and teardown time in order to be useful.</p>

<p>Dev checks, on the other hand, are simple to get going with, only take a few minutes, and only have to involve a couple of people.</p>

<p>That&#8217;s not to say that you should get rid of larger code reviews. In my opinion they should still happen, but only for larger chunks of code. The formal dev check gives you the opportunity to review code and share knowledge often and with low cost.</p>

<h2>Go forth and Dev Check!</h2>

<p>And that&#8217;s it.  If you&#8217;re working in a team that has bugs, or has bottlenecks of a single person being the only person who knows about certain areas of your product, then start dev checking. You won&#8217;t regret it.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been working on the BBC&#8217;s Homepage for about a year now, leading a team of five front-end developers to create a high quality product.</p>

<p>One of the processes that I introduced to the team about six months ago was that of a formal &#8220;dev check&#8221; after every task that we completed from our backlog.  Whilst we call this process a dev check within our team, it could just as easily be called a peer review, or mini code review.</p>

<p>Up until the point that I introduced the dev check it had not been uncommon for a single person to work on a task, and for code to go through the testing environments and onto live without a single other human ever reading their code. Not the best situation to be in, and it meant that we ended up having more annoying bugs showing up on our test environments than we should have.  These bugs were often things that a simple bit of human scrutiny would have caught, and we ended up wasting time going back and fixing them after the fact.  Working this way also meant that we ended up with certain areas of the homepage that could only ever be worked on by certain people, because they were the only ones who had ever seen the code.</p>

<p>Like I said; not the best situation to be in.</p>

<p>So, six months ago, we introduced the formal dev check &#8211; a step to be completed for every single code related task in our backlog.  And, I&#8217;m happy to say it&#8217;s been a very successful process for us &#8211; we now have far fewer bugs getting to test, and we have a lot more shared knowledge within the team.</p>

<h2>What is a Dev Check?</h2>

<blockquote>
  <p>Before any task is committed to trunk, a developer who didn&#8217;t work on it must say that they are happy with  how it&#8217;s been completed.</p>
</blockquote>

<p>I&#8217;m hoping that if you&#8217;re still reading at this point that you&#8217;re wanting to find out what goes into a successful dev check&#8230;</p>

<ol>
<li><p>The task is &#8220;completed&#8221; by Rachel.</p>

<p>At this point she has not committed anything to the trunk of the project. Depending on the size of the task, she might have created a development branch which she&#8217;ll have been committing to.</p></li>
<li><p>Rachel asks another member of the development team to come and do a dev check. On this occasion she asks Jon.</p></li>
<li><p>Jon comes over to Rachel&#8217;s desk where she will talk through all the changes she has made.</p>

<p>As part of the discussion, Rachel may realise that some of the things she thought made sense in fact didn&#8217;t. This is great, and she&#8217;ll make the changes necessary and continue with the dev check.</p></li>
<li><p>After Rachel has finished explaining what she&#8217;s done for the task, and Jon&#8217;s asked as many questions as he feels is necessary he&#8217;ll either give the go ahead to commit the code to the trunk, or he&#8217;ll request various changes to be made to the codebase.</p></li>
<li><p>Rachel will then commit to trunk, build in the team&#8217;s test environment and pass off to the testers.</p></li>
</ol>

<p>And that&#8217;s a dev check.</p>

<p>Out of this process Jon has learnt about a bit of the codebase that he might not otherwise have seen, Jo realised that she needed to shore up a couple of her tests, and the codebase has come out feeling more healthy.  This is A Good Thing™.</p>

<h2>What sort of things will the dev checker ask?</h2>

<p>Up above there in the &#8220;What is a Dev Check&#8221; section you&#8217;ll have read about Jon asking &#8220;as many questions as he feels is necessary&#8221;, and I&#8217;m sure you&#8217;ll have said to yourself &#8220;But Neil, what are those questions? How will I know what&#8217;s sensible to ask?&#8221;.</p>

<p>Well, you&#8217;re in luck. By a process of trial and error, as a team we&#8217;ve come up with a bunch of questions that, if asked, will help to make sure that you both understand what&#8217;s going on and can confirm that the code written is of a high quality.</p>

<ul>
<li><p>Have new tests been written?</p>

<p>These might be Unit Tests, or Automated Front-End Tests, or Security Tests. What matters is that valid tests have been written to cover the new functionality. You should also make sure that any pre-existing tests make sense.</p></li>
<li><p>Do all the tests still pass?</p>

<p>It should be a no-brainer, but of course you should make sure that all the tests still pass. There&#8217;s nothing worse that breaking The Build for the entire team just because you forgot to run a bunch of tests on your development machine first.</p></li>
<li><p>Are standards adhered to?</p>

<ul>
<li>PHP Code Sniffer</li>
<li>HTML/CSS Validation</li>
<li>JSLint</li>
<li>Whatever else works in your team</li>
</ul>

<p>These are important to us. We fail our builds if our code isn&#8217;t of high enough quality. Just sayin&#8217;.</p></li>
<li><p>Does what you&#8217;ve written work across all the Level One browsers?</p>

<p>Obviously this one&#8217;s a bit web development specific as it is, but it&#8217;s important in other spheres as well. Every piece of software that&#8217;s written is expected to run in one or more environments, and you should at least smoke test in a known subset of them to make sure there are no nasty surprises for your dedicated testing team.</p></li>
<li><p>Can I see your diffs?</p>

<p>Again, fairly obvious, but you should take a look to see what&#8217;s changed since the last time the code was checked in. Don&#8217;t just rely on the developer to remember what they&#8217;ve changed &#8211; I know I can&#8217;t remember half the time. This is what machines are good at &#8211; make use of them!</p></li>
<li><p>Did any of the changes that were made affect any other code?</p>

<p>This one should be obvious as well, but unfortunately it isn&#8217;t. It&#8217;s all too easy to look at the lines of code that have changed without looking at the later lines which make use of them. Don&#8217;t forget to look at the whole system.</p></li>
<li><p>Is there anything that concerns you?</p>

<p>The final question to ask, and in many cases the most important one. Oftentimes a developer will know that something smells a bit fishy, but keeps quiet about it in the hope that they won&#8217;t get called on it.  By simply asking these questions though, we&#8217;ve found that these things end up getting talked about, and we end up with better code because of it.</p></li>
</ul>

<h2>How long does a dev check take?</h2>

<p>After reading all that, I wouldn&#8217;t be surprised if you&#8217;re thinking &#8220;Woah! But how long does that all take? That seems like a long list of questions…&#8221;.</p>

<p>Well, in my team, we find that a dev check will take a little less than 10% of the time that the code and tests took to write initially. So, if the task took an hour to complete, the dev check would take roughly 5 minutes. A three hour task would take 15 minutes, and so on.  It honestly doesn&#8217;t take much time once you get into the swing of things.</p>

<h2>The Power of Veto</h2>

<p>One of the most important things about dev checks is that everyone on the team is equal. Rachel may be the team leader, but Jon can still challenge her code and say that actually she hasn&#8217;t done everything required to &#8220;pass&#8221; the dev check.</p>

<p>The person performing a dev check <em>always</em> has power of veto to say that things, however small, need improving before code is allowed to be committed to the trunk.</p>

<p>Ultimately, the team is responsible for its own output, and everyone should be invested in that.</p>

<h2>What do dev checks achieve?</h2>

<p>As I&#8217;ve mentioned above, there are two main benefits to dev checking:</p>

<ul>
<li>Fewer bugs</li>
<li>More knowledge of the codebase by more of the team.</li>
</ul>

<p>Essentially the team will be more effective.</p>

<h2>How do you get buy in?</h2>

<p>Whilst there are obvious benefits to dev checks, sometimes you&#8217;ll need to gain buy-in from people before you can get started. Maybe it&#8217;ll be team members who think that this extra process is unnecessary, or maybe it&#8217;ll be The Boss who thinks that it&#8217;ll all just take far too long. What do you say to them?</p>

<h3>Getting Buy In From Team Members?</h3>

<ul>
<li>Everyone is equal.</li>
<li>Anyone is able to say that a dev check cannot yet be completed.</li>
<li>The team is responsible for the quality of the team&#8217;s output, not just one person.</li>
<li>You won&#8217;t end up getting stuck on a horrible portion of the codebase any more, just because you were the last person to write code for it.</li>
</ul>

<h3>Getting Buy In From The Boss?</h3>

<p>If you&#8217;re lucky, you won&#8217;t need to get buy-in from The Boss, but in case you do, here are a couple of pointers:</p>

<ul>
<li>Quality is improved, leading to fewer bugs and happier users.</li>
<li>The amount of time spent on dev checking is smaller than than that which would be spent fixing the bugs that would otherwise get through. The project will go live sooner, and you&#8217;ll have spent less money.</li>
</ul>

<h2>Improving the Dev Check</h2>

<blockquote>
  <p>Any bug that is found is an opportunity to learn and improve our process.</p>
</blockquote>

<p>Of course, sometimes bugs will still get through. When they do, you need to sit down and work out why that happened.</p>

<p>In the old world, we used to just accept that bugs happened, that they&#8217;d get found, and we would fix them. No more! Now, any bug that is found is an opportunity to learn and improve our process.</p>

<p>A real example of this happening came a few weeks ago. Tina had been doing her thing, updating a script that we use to help us with our release management. This was dev checked by Bradley, and as far as he was concerned the changes that had been made were good to go. So, he ticked off the dev check, and Tina carried on with her next task.</p>

<p>Fast forward to a week later, and Paul was getting <del>down on the floor</del> ready to give a presentation which required a screenshot of the script running. Unfortunately, it no longer worked &#8211; shock horror! </p>

<p>What had happened was that Bradley hadn&#8217;t realised that the line of code that was changed had impacted on further lines in the script. Moreover, he&#8217;d only seen the script run against the additional new requirements, and hadn&#8217;t confirmed that it still ran against the original requirements. Whoops.</p>

<p>What came out of this was an improvement to our dev checks &#8211; if code has changed we need to make sure that nothing else in our code has been affected by that change. It sounds simple, but it was a failing that we had, so we improved upon it.</p>

<h2>Why not just have a big code review?</h2>

<p>Big code reviews are great, and are something I very much enjoyed participating in when I was at Yahoo!. Unfortunately, whilst they&#8217;re very useful they&#8217;re also pretty time consuming, and require a fair amount of setup and teardown time in order to be useful.</p>

<p>Dev checks, on the other hand, are simple to get going with, only take a few minutes, and only have to involve a couple of people.</p>

<p>That&#8217;s not to say that you should get rid of larger code reviews. In my opinion they should still happen, but only for larger chunks of code. The formal dev check gives you the opportunity to review code and share knowledge often and with low cost.</p>

<h2>Go forth and Dev Check!</h2>

<p>And that&#8217;s it.  If you&#8217;re working in a team that has bugs, or has bottlenecks of a single person being the only person who knows about certain areas of your product, then start dev checking. You won&#8217;t regret it.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/9yDROqGdfoo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2011/01/dev-checks/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2011/01/dev-checks/</feedburner:origLink></item>
		<item>
		<title>Using last.fm tags to make awesome playlists in your local iTunes library</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/pm-HuIgSqg4/</link>
		<comments>http://thecodetrain.co.uk/2010/08/using-last-fm-tags-to-make-awesome-playlists-in-your-local-itunes-library/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 18:00:15 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[applescript]]></category>
		<category><![CDATA[itunes]]></category>
		<category><![CDATA[last.fm]]></category>
		<category><![CDATA[shell script]]></category>
		<category><![CDATA[tags]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=484</guid>
		<description><![CDATA[<p>As I&#8217;ve mentioned previously, I have a large music collection that I manage using iTunes.  One of the problems with any large collection is the curation and management that goes with it.  I carry my iPhone around with me and I listen to music on it, but even the biggest iPhone available would hold only  fifth of my collection.  So, I need techniques for pulling interesting music from my iTunes library into my iPhone.</p>

<p>Up until now I&#8217;ve used a collection of Smart Playlists which take into account my ratings, when I last listened to songs and a whole bunch of other information.  At the last count, I had over 70 Smart Playlists feeding into each other to generate the playlists which finally get Synced over to my iPhone.</p>

<p>But there was something missing.  Whilst I could generate playlists based on the mid-90s simplicity of the &#8220;Genre&#8221; tag, that tag is by design only able to identify one piece of information.  So I wouldn&#8217;t be able to find, for example, all tracks in my collection that are &#8220;rock&#8221;, &#8220;funny&#8221; and performed by a &#8220;female&#8221;.  And even if I could, I wouldn&#8217;t want to go and tag 40,000 tracks by hand. That way madness lies.</p>

<p>It turns out there&#8217;s a fairly simple solution to this &#8211; last.fm allows users to tag any and all tracks it knows about, and it keeps track of how many times each track has been tagged with each tag.  So, it&#8217;s entirely possible to grab the top 15 tags for each track in my collection, add that data to my tracks somehow, and then query that.</p>

<p>The somehow turns out to be pretty simple too &#8211; the ID3 &#8220;comment&#8221; field is there for the taking, and by design is expected to be larger than any other field.  Excellent.  So that&#8217;s the &#8220;where to store&#8221; sorted, now how about &#8220;how to store so that the data is queryable&#8221;?</p>

<p>For this I took <a href="http://www.xml.com/pub/a/2007/09/04/parsing-microformats.html?page=2">a trick out of Brian Suda&#8217;s book</a>.  A couple of years back I took a look at how he&#8217;d written his microformats parser using XSLT to be able to check for classes an element may or may not have &#8211; the trick being to wrap strings of classes with a space at the beginning and end so that every class <em>always</em> had spaces surrounding it.  I used the same trick for storing my tag data, also wrapping it in a &#8216;lfm&#8217; square bracketed enclosure to separate it from any other data in the comment.</p>

<p>So, the data I saved in the comment looked a little something like this:</p>

<pre><code>[lfm: tag1 tag2 tag23 tag3 ]
</code></pre>

<p>You&#8217;re probably starting to see why wrapping in spaces is important now.  Because iTunes&#8217; Smart Playlist system can only perform simple string matching (Apple doesn&#8217;t seem to like regexes &#8211; AppleScript can&#8217;t do them natively either), you need some way of targetting the beginning and end of tags. Without those spaces, a query of &#8220;<code>Comments contains 'tag2'</code>&#8221; would return you both <code>tag2</code> and <code>tag23</code>, which no-one wants.  By requesting &#8220;<code>Comments contains ' tag2 '</code>&#8221; instead (note the extra spaces) you end up just being given <code>tag2</code>.</p>

<p>All this thinking is packaged up into a <a href="http://github.com/NeilCrosby/itunes-helpers/commit/b94cf1f2c28bdc2b83129b458e0e6bc42573afdb">couple of scripts</a> (though obviously take <a href="http://github.com/NeilCrosby/itunes-helpers/tree/master/playlist/lastfm/">the latest code</a> if you want to actually use it yourself) I wrote a couple of days ago that takes a given playlist from your iTunes library, asks last.fm for tags for each of the songs in that playlist, and then adds that data to the library using AppleScript.</p>

<h2>Problems?</h2>

<p>There are, unfortunately, a couple of issues with the script at the moment.  First off, because last.fm allows anything and everything to be given as a tag, they can contain characters that need to be normalised out.  Right now I&#8217;m doing that very simply (spaces to dashes mostly), but I should revisit the code and harden that normalisation &#8211; I&#8217;ve seen tagging bail out a couple of times because of unexpected data.</p>

<p>Next up, the fact that last.fm allows tags to be incredibly long coupled with the fact that under ID3 v2.x the comments field can only be 256 characters long means that it&#8217;s possible to run into issues with tagsets being too long to fit into the comments field.  Add that to the fact that some songs in my collection legitimately contain comments already and that I want to add tags non-destructively, and we&#8217;re ending up with not much space for tags.  So, an improvement that I&#8217;m planning on making to the script is to make sure that tags try to take up no more than the space that&#8217;s legitimately available to them.</p>

<p>The final issue that I&#8217;ve been having lives with last.fm&#8217;s track.getTopTags API method.  Unfortunately at present the track.getTopTags API method does not perform any auto-correction of track names, which means that if you try to get the tags for something that does get auto-corrected by the system then you end up with the tags for the uncorrected track name. A subtle distinction, but it probably means that we end up with unmaintained tags that haven&#8217;t been touched for the year or so since auto-correction was turned on. A possible solution here if last.fm choose not to &#8220;fix&#8221; this behaviour (it could be argued that this is correct behaviour) would be to make a request out to Musicbrainz to get an ID for the track and then pass that into track.getTopTags instead.  But that seems like a whole bunch of extra work for a not massive gain.</p>

<h2>The final Smart Playlist</h2>

<p>So, what about that Smart Playlist? After all, that&#8217;s the thing that all this work was done for.</p>

<p>In iTunes, select <code>File</code>, <code>New Smart Playlist</code>, and then enter the following:</p>

<pre><code>Match all of the following rules:
    Comments contains ' female '
    Comments contains ' rock ' ...
    any of the following rules:
        Comments contains ' comedy '
        Comments contains ' funny '
</code></pre>

<p>This cunning playlist uses a Smart Playlist feature I wasn&#8217;t aware of until yesterday &#8211; rule groups.  Up until now, I&#8217;d been pulling in other Smart Playlists as sources to perform the same job.</p>

<p>To create a new rule group in a Smart Playlist, simply click an ellipsis button in the Smart Playlist interface, rather than clicking on the plus button.</p>

<p>Essentially what these rule groups give you is the ability to nest rulesets, and generate very complex playlists that don&#8217;t have half their rules hidden in external playlists.  In the playlist above we&#8217;re simply asking for all tracks tagged with &#8220;female&#8221;, &#8220;rock&#8221; and either &#8220;funny&#8221; or &#8220;comedy&#8221;. So essentially &#8220;female comedy rock&#8221;.  At present this playlist returns me no items from my collection, but not everything&#8217;s been tagged yet.  I&#8217;m hopeful.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>As I&#8217;ve mentioned previously, I have a large music collection that I manage using iTunes.  One of the problems with any large collection is the curation and management that goes with it.  I carry my iPhone around with me and I listen to music on it, but even the biggest iPhone available would hold only  fifth of my collection.  So, I need techniques for pulling interesting music from my iTunes library into my iPhone.</p>

<p>Up until now I&#8217;ve used a collection of Smart Playlists which take into account my ratings, when I last listened to songs and a whole bunch of other information.  At the last count, I had over 70 Smart Playlists feeding into each other to generate the playlists which finally get Synced over to my iPhone.</p>

<p>But there was something missing.  Whilst I could generate playlists based on the mid-90s simplicity of the &#8220;Genre&#8221; tag, that tag is by design only able to identify one piece of information.  So I wouldn&#8217;t be able to find, for example, all tracks in my collection that are &#8220;rock&#8221;, &#8220;funny&#8221; and performed by a &#8220;female&#8221;.  And even if I could, I wouldn&#8217;t want to go and tag 40,000 tracks by hand. That way madness lies.</p>

<p>It turns out there&#8217;s a fairly simple solution to this &#8211; last.fm allows users to tag any and all tracks it knows about, and it keeps track of how many times each track has been tagged with each tag.  So, it&#8217;s entirely possible to grab the top 15 tags for each track in my collection, add that data to my tracks somehow, and then query that.</p>

<p>The somehow turns out to be pretty simple too &#8211; the ID3 &#8220;comment&#8221; field is there for the taking, and by design is expected to be larger than any other field.  Excellent.  So that&#8217;s the &#8220;where to store&#8221; sorted, now how about &#8220;how to store so that the data is queryable&#8221;?</p>

<p>For this I took <a href="http://www.xml.com/pub/a/2007/09/04/parsing-microformats.html?page=2">a trick out of Brian Suda&#8217;s book</a>.  A couple of years back I took a look at how he&#8217;d written his microformats parser using XSLT to be able to check for classes an element may or may not have &#8211; the trick being to wrap strings of classes with a space at the beginning and end so that every class <em>always</em> had spaces surrounding it.  I used the same trick for storing my tag data, also wrapping it in a &#8216;lfm&#8217; square bracketed enclosure to separate it from any other data in the comment.</p>

<p>So, the data I saved in the comment looked a little something like this:</p>

<pre><code>[lfm: tag1 tag2 tag23 tag3 ]
</code></pre>

<p>You&#8217;re probably starting to see why wrapping in spaces is important now.  Because iTunes&#8217; Smart Playlist system can only perform simple string matching (Apple doesn&#8217;t seem to like regexes &#8211; AppleScript can&#8217;t do them natively either), you need some way of targetting the beginning and end of tags. Without those spaces, a query of &#8220;<code>Comments contains 'tag2'</code>&#8221; would return you both <code>tag2</code> and <code>tag23</code>, which no-one wants.  By requesting &#8220;<code>Comments contains ' tag2 '</code>&#8221; instead (note the extra spaces) you end up just being given <code>tag2</code>.</p>

<p>All this thinking is packaged up into a <a href="http://github.com/NeilCrosby/itunes-helpers/commit/b94cf1f2c28bdc2b83129b458e0e6bc42573afdb">couple of scripts</a> (though obviously take <a href="http://github.com/NeilCrosby/itunes-helpers/tree/master/playlist/lastfm/">the latest code</a> if you want to actually use it yourself) I wrote a couple of days ago that takes a given playlist from your iTunes library, asks last.fm for tags for each of the songs in that playlist, and then adds that data to the library using AppleScript.</p>

<h2>Problems?</h2>

<p>There are, unfortunately, a couple of issues with the script at the moment.  First off, because last.fm allows anything and everything to be given as a tag, they can contain characters that need to be normalised out.  Right now I&#8217;m doing that very simply (spaces to dashes mostly), but I should revisit the code and harden that normalisation &#8211; I&#8217;ve seen tagging bail out a couple of times because of unexpected data.</p>

<p>Next up, the fact that last.fm allows tags to be incredibly long coupled with the fact that under ID3 v2.x the comments field can only be 256 characters long means that it&#8217;s possible to run into issues with tagsets being too long to fit into the comments field.  Add that to the fact that some songs in my collection legitimately contain comments already and that I want to add tags non-destructively, and we&#8217;re ending up with not much space for tags.  So, an improvement that I&#8217;m planning on making to the script is to make sure that tags try to take up no more than the space that&#8217;s legitimately available to them.</p>

<p>The final issue that I&#8217;ve been having lives with last.fm&#8217;s track.getTopTags API method.  Unfortunately at present the track.getTopTags API method does not perform any auto-correction of track names, which means that if you try to get the tags for something that does get auto-corrected by the system then you end up with the tags for the uncorrected track name. A subtle distinction, but it probably means that we end up with unmaintained tags that haven&#8217;t been touched for the year or so since auto-correction was turned on. A possible solution here if last.fm choose not to &#8220;fix&#8221; this behaviour (it could be argued that this is correct behaviour) would be to make a request out to Musicbrainz to get an ID for the track and then pass that into track.getTopTags instead.  But that seems like a whole bunch of extra work for a not massive gain.</p>

<h2>The final Smart Playlist</h2>

<p>So, what about that Smart Playlist? After all, that&#8217;s the thing that all this work was done for.</p>

<p>In iTunes, select <code>File</code>, <code>New Smart Playlist</code>, and then enter the following:</p>

<pre><code>Match all of the following rules:
    Comments contains ' female '
    Comments contains ' rock ' ...
    any of the following rules:
        Comments contains ' comedy '
        Comments contains ' funny '
</code></pre>

<p>This cunning playlist uses a Smart Playlist feature I wasn&#8217;t aware of until yesterday &#8211; rule groups.  Up until now, I&#8217;d been pulling in other Smart Playlists as sources to perform the same job.</p>

<p>To create a new rule group in a Smart Playlist, simply click an ellipsis button in the Smart Playlist interface, rather than clicking on the plus button.</p>

<p>Essentially what these rule groups give you is the ability to nest rulesets, and generate very complex playlists that don&#8217;t have half their rules hidden in external playlists.  In the playlist above we&#8217;re simply asking for all tracks tagged with &#8220;female&#8221;, &#8220;rock&#8221; and either &#8220;funny&#8221; or &#8220;comedy&#8221;. So essentially &#8220;female comedy rock&#8221;.  At present this playlist returns me no items from my collection, but not everything&#8217;s been tagged yet.  I&#8217;m hopeful.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/pm-HuIgSqg4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/08/using-last-fm-tags-to-make-awesome-playlists-in-your-local-itunes-library/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/08/using-last-fm-tags-to-make-awesome-playlists-in-your-local-itunes-library/</feedburner:origLink></item>
		<item>
		<title>Review: Cordless Dog’s “Stay”</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/TJ_vXUAuMu4/</link>
		<comments>http://thecodetrain.co.uk/2010/08/review-cordless-dogs-stay/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 08:39:43 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[application]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[positive]]></category>
		<category><![CDATA[review]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=475</guid>
		<description><![CDATA[<p>One of the problems with being an Apple laptop user who sometimes connects to external displays is the lack of any built in management of where different applications should live on those different displays.  </p>

<p>If you&#8217;re anything like me, as soon as you connect your laptop to an external display you&#8217;ll end up moving your applications around into a better position and size so that you can work happily. Then, when you disconnect your external display to go back onto the road OSX will do its best to put them somewhere sensible on your smaller laptop screen, but fails horribly.</p>

<p>Enter Cordless Dog&#8217;s &#8220;<a href="http://cordlessdog.com/stay/">Stay</a>&#8221; application.</p>

<p>Stay is a deceptively simple (at least the way I&#8217;m using it) application that lives in your Mac&#8217;s taskbar.  First, set up your applications so that they&#8217;re positioned as you want them, then choose &#8220;Store Windows for all Applications&#8221; from the app&#8217;s dropdown menu. If you then want to restore applications to their stored position just pull down the menu again and choose &#8220;Restore Windows&#8221;.  Simple.</p>

<p>To make things even more simple though, pop open the application preferences. There aren&#8217;t many options here (and there don&#8217;t need to be), but for my money it makes sense to turn on &#8220;Start Stay at login&#8221;, &#8220;Restore Windows as displays are connected and disconnected&#8221; and &#8220;Restore Windows as applications are launched&#8221;.  And suddenly, as if by magic, application positioning with multiple displays starts working in the way it always should have done.</p>

<p>Cordless Dog&#8217;s &#8220;<a href="http://cordlessdog.com/stay/">Stay</a>&#8221; application costs a measly $15, and is worth every cent.  Good job, Cordless Dog chaps!</p>

<h2>UPDATE</h2>

<p>As <a href="https://twitter.com/fatbusinessman/status/21316857798">David Thompson pointed out</a>, I should probably point out that currently Stay and Spaces don&#8217;t currently live marvellously happily together. If you&#8217;re a user of Spaces (I&#8217;m not), you should probably wait for <a href="http://cordlessdog.com/stay/faq/">a future release of Stay</a>.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>One of the problems with being an Apple laptop user who sometimes connects to external displays is the lack of any built in management of where different applications should live on those different displays.  </p>

<p>If you&#8217;re anything like me, as soon as you connect your laptop to an external display you&#8217;ll end up moving your applications around into a better position and size so that you can work happily. Then, when you disconnect your external display to go back onto the road OSX will do its best to put them somewhere sensible on your smaller laptop screen, but fails horribly.</p>

<p>Enter Cordless Dog&#8217;s &#8220;<a href="http://cordlessdog.com/stay/">Stay</a>&#8221; application.</p>

<p>Stay is a deceptively simple (at least the way I&#8217;m using it) application that lives in your Mac&#8217;s taskbar.  First, set up your applications so that they&#8217;re positioned as you want them, then choose &#8220;Store Windows for all Applications&#8221; from the app&#8217;s dropdown menu. If you then want to restore applications to their stored position just pull down the menu again and choose &#8220;Restore Windows&#8221;.  Simple.</p>

<p>To make things even more simple though, pop open the application preferences. There aren&#8217;t many options here (and there don&#8217;t need to be), but for my money it makes sense to turn on &#8220;Start Stay at login&#8221;, &#8220;Restore Windows as displays are connected and disconnected&#8221; and &#8220;Restore Windows as applications are launched&#8221;.  And suddenly, as if by magic, application positioning with multiple displays starts working in the way it always should have done.</p>

<p>Cordless Dog&#8217;s &#8220;<a href="http://cordlessdog.com/stay/">Stay</a>&#8221; application costs a measly $15, and is worth every cent.  Good job, Cordless Dog chaps!</p>

<h2>UPDATE</h2>

<p>As <a href="https://twitter.com/fatbusinessman/status/21316857798">David Thompson pointed out</a>, I should probably point out that currently Stay and Spaces don&#8217;t currently live marvellously happily together. If you&#8217;re a user of Spaces (I&#8217;m not), you should probably wait for <a href="http://cordlessdog.com/stay/faq/">a future release of Stay</a>.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/TJ_vXUAuMu4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/08/review-cordless-dogs-stay/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/08/review-cordless-dogs-stay/</feedburner:origLink></item>
		<item>
		<title>Packratius, and the inevitable overwhelming of del.icio.us</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/RT_0H1oZ1j8/</link>
		<comments>http://thecodetrain.co.uk/2010/06/packratius-and-the-inevitable-overwhelming-of-del-icio-us/#comments</comments>
		<pubDate>Tue, 08 Jun 2010 20:22:32 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[del.icio.us]]></category>
		<category><![CDATA[hack]]></category>
		<category><![CDATA[packrati.us]]></category>
		<category><![CDATA[pipes]]></category>
		<category><![CDATA[yahoo pipes]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=455</guid>
		<description><![CDATA[<p>I&#8217;ve been using <a href="http://delicious.com">del.icio.us</a> for years. I started using it well before it was bought up by Yahoo!, and for a long time my primary use for it was to store my bookmarks in an always available location &#8211; I used many computers, and not having my bookmarks tied to a single machine always seemed like a good idea.</p>

<p>As time went on, I started paying attention to the &#8220;Most Popular&#8221; feeds on the homepage, and even managed to get on there myself a few times.  It was a good way to find interesting techy links.  Then, when the friends network facility was introduced I started following the links that my friends were saving as well.  All was well.</p>

<p>However, I never really saved as many of the links that I found interesting as I could have done.  There were plenty of occasions when I remembered telling people about particular pages because they were useful, but not being able to find them again myself.  Oh, how I wished I&#8217;d added them to del.icio.us.</p>

<p>The problem is that I&#8217;m lazy.  So, I was really happy when <a href="http://packrati.us">Packratius</a> peeked its nose up to the cage bars.  The idea behind Packratius is that it&#8217;ll keep an eye on your twitter activity and automagically add any links you tweet to del.icio.us (by default with a <code>via:packrati.us</code> tag).  It&#8217;s deceptively simple, and something that deeply interested me &#8211; I immediately signed up.</p>

<h2>A good idea gone overwhelming</h2>

<p>As it turns out, so did quite a few of my friends.  Overnight, my del.icio.us network feed went from being a carefully curated list of links to a mishmash of <a href="http://www.bbc.co.uk/food/recipes/carrotsglazedwithcum_80467">Carrots Glazed with Cum</a>, gowalla updates and twitpics.</p>

<p>Now, to its credit, Packratius has <a href="http://packrati.us/preferences">options</a> to allow you to not auto-delish links tweeted by any twitter clients that you define.  This allows you to not auto tweet twitpics and the like.  Of course, telling it to stop tweeting those things from your own twitter account is great, but it doesn&#8217;t stop you from seeing those updates from other people (/me looks pointedly at <a href="http://simonjobling.com">Simon Jobling</a>).  So, I needed to find a way to split these auto-delished URLs off from the main list.</p>

<p>As it turns out, whilst del.icio.us&#8217; searching abilities are pretty powerful, you can&#8217;t do a simply negative search.  So whilst you could say &#8220;Show me everything in my network feed tagged with &#8216;kittens&#8217; but not &#8216;via:packrati.us&#8217;&#8221; you can&#8217;t say &#8220;Show me everything in my network feed not tagged with &#8216;via:packrati.us&#8217;&#8221;.  Thankfully I came up with a solution to this &#8211; I created a Yahoo! Pipe called &#8220;<a href="http://pipes.yahoo.com/pipes/pipe.info?_id=537a07fd888c1c38dedcb59f6b5f8101">del.icio.us network feed minus packratius</a>&#8220;. (Catchy, huh?)</p>

<p>It&#8217;s a fairly simple pipe, with the following aims:</p>

<ul>
<li>Provide a feed of your del.icio.us network with items tagged with <code>via:packrati.us</code> removed.</li>
<li>Easily customisable to show different peoples&#8217; network feeds.  </li>
</ul>

<p>If you&#8217;ve been having similar problems with Packratius overwhelming your del.icio.us network feed, then give it a go.  I know <a href="http://kulor.com/">James Broad</a> has been finding Packratius overwhelming &#8211; it was his <a href="http://twitter.com/kulor/status/15596311122">tweet</a> that inspired me to finally write this post up, in the hope of benefitting a few more people.</p>

<h2>How does the pipe work?</h2>

<p>Like I said, the pipe&#8217;s fairly simple &#8211; most of the complexity comes from generating the URL to load a given user&#8217;s del.icio.us network feed.</p>

<p>Once we&#8217;ve obtained the network feed, the only thing we need to do is discard all the feed items that contain <code>via:packrati.us</code> as a tag.  Handily, del.icio.us provides us this information in its feeds:</p>

<pre><code>&lt;item&gt;
  …
  &lt;link&gt;

http://wwp.greenwichmeantime.com/time-zone/africa/south-africa/

  &lt;/link&gt;
  &lt;description&gt;
    South Africa is in Southern Africa, 
    at the southern tip of the continent of Africa.
    http:// bit.ly/am78yK
    #statingthebleedingobvious
  &lt;/description&gt;
  &lt;category domain="http://delicious.com/simey_j/"&gt;
    statingthebleedingobvious
  &lt;/category&gt;
  &lt;category domain="http://delicious.com/simey_j/"&gt;
    via:packrati.us
  &lt;/category&gt;
&lt;/item&gt;
</code></pre>

<p>In the feed item above (cut down for space), we can see that as well as <code>via:packrati.us</code>, a <code>statingthebleedingobvious</code> tag has been automatically generated from the hashtag in the original tweet.  This is a great feature of Packratius, but it did mean that I ended up having to do more than a very simple filter to filter out the <code>via:packrati.us</code> tagged feed items.</p>

<p>Instead what I did was create a <code>Filter</code> block in pipes with rules that looked at the first 6 tags on each feed item (the idea being that there isn&#8217;t enough space in a tweet for more than 6 or so tags). If any of them matched <code>via:packrati.us</code> then they&#8217;re discarded from the feed.</p>

<p>The filter I created looked something a little like this:</p>

<blockquote>
  <p>Filter</p>
  
  <p>Block items that match any of the following:</p>
  
  <ul>
  <li>item.category.0.content is via:packrati.us</li>
  <li>item.category.1.content is via:packrati.us</li>
  <li>item.category.2.content is via:packrati.us</li>
  <li>item.category.3.content is via:packrati.us</li>
  <li>item.category.4.content is via:packrati.us</li>
  <li>item.category.5.content is via:packrati.us</li>
  <li>item.category.content is via:packrati.us</li>
  </ul>
</blockquote>

<p>Those first six filter rules cover the instance of more than one tag being applied to the item, and the final filter covers the intances where only a single tag exists.  Simple.</p>

<p>And that&#8217;s all there is to it.  Of course, writing this Pipe wouldn&#8217;t be necessary if del.icio.us allowed us to do a simple negative search. Still, it&#8217;s good that it&#8217;s nice and easy to generate <a href="http://pipes.yahoo.com/pipes/pipe.info?_id=537a07fd888c1c38dedcb59f6b5f8101">this new feed</a> using Pipes.</p>

<p>Hopefully it&#8217;ll be useful to a couple of you.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been using <a href="http://delicious.com">del.icio.us</a> for years. I started using it well before it was bought up by Yahoo!, and for a long time my primary use for it was to store my bookmarks in an always available location &#8211; I used many computers, and not having my bookmarks tied to a single machine always seemed like a good idea.</p>

<p>As time went on, I started paying attention to the &#8220;Most Popular&#8221; feeds on the homepage, and even managed to get on there myself a few times.  It was a good way to find interesting techy links.  Then, when the friends network facility was introduced I started following the links that my friends were saving as well.  All was well.</p>

<p>However, I never really saved as many of the links that I found interesting as I could have done.  There were plenty of occasions when I remembered telling people about particular pages because they were useful, but not being able to find them again myself.  Oh, how I wished I&#8217;d added them to del.icio.us.</p>

<p>The problem is that I&#8217;m lazy.  So, I was really happy when <a href="http://packrati.us">Packratius</a> peeked its nose up to the cage bars.  The idea behind Packratius is that it&#8217;ll keep an eye on your twitter activity and automagically add any links you tweet to del.icio.us (by default with a <code>via:packrati.us</code> tag).  It&#8217;s deceptively simple, and something that deeply interested me &#8211; I immediately signed up.</p>

<h2>A good idea gone overwhelming</h2>

<p>As it turns out, so did quite a few of my friends.  Overnight, my del.icio.us network feed went from being a carefully curated list of links to a mishmash of <a href="http://www.bbc.co.uk/food/recipes/carrotsglazedwithcum_80467">Carrots Glazed with Cum</a>, gowalla updates and twitpics.</p>

<p>Now, to its credit, Packratius has <a href="http://packrati.us/preferences">options</a> to allow you to not auto-delish links tweeted by any twitter clients that you define.  This allows you to not auto tweet twitpics and the like.  Of course, telling it to stop tweeting those things from your own twitter account is great, but it doesn&#8217;t stop you from seeing those updates from other people (/me looks pointedly at <a href="http://simonjobling.com">Simon Jobling</a>).  So, I needed to find a way to split these auto-delished URLs off from the main list.</p>

<p>As it turns out, whilst del.icio.us&#8217; searching abilities are pretty powerful, you can&#8217;t do a simply negative search.  So whilst you could say &#8220;Show me everything in my network feed tagged with &#8216;kittens&#8217; but not &#8216;via:packrati.us&#8217;&#8221; you can&#8217;t say &#8220;Show me everything in my network feed not tagged with &#8216;via:packrati.us&#8217;&#8221;.  Thankfully I came up with a solution to this &#8211; I created a Yahoo! Pipe called &#8220;<a href="http://pipes.yahoo.com/pipes/pipe.info?_id=537a07fd888c1c38dedcb59f6b5f8101">del.icio.us network feed minus packratius</a>&#8220;. (Catchy, huh?)</p>

<p>It&#8217;s a fairly simple pipe, with the following aims:</p>

<ul>
<li>Provide a feed of your del.icio.us network with items tagged with <code>via:packrati.us</code> removed.</li>
<li>Easily customisable to show different peoples&#8217; network feeds.  </li>
</ul>

<p>If you&#8217;ve been having similar problems with Packratius overwhelming your del.icio.us network feed, then give it a go.  I know <a href="http://kulor.com/">James Broad</a> has been finding Packratius overwhelming &#8211; it was his <a href="http://twitter.com/kulor/status/15596311122">tweet</a> that inspired me to finally write this post up, in the hope of benefitting a few more people.</p>

<h2>How does the pipe work?</h2>

<p>Like I said, the pipe&#8217;s fairly simple &#8211; most of the complexity comes from generating the URL to load a given user&#8217;s del.icio.us network feed.</p>

<p>Once we&#8217;ve obtained the network feed, the only thing we need to do is discard all the feed items that contain <code>via:packrati.us</code> as a tag.  Handily, del.icio.us provides us this information in its feeds:</p>

<pre><code>&lt;item&gt;
  …
  &lt;link&gt;

http://wwp.greenwichmeantime.com/time-zone/africa/south-africa/

  &lt;/link&gt;
  &lt;description&gt;
    South Africa is in Southern Africa, 
    at the southern tip of the continent of Africa.
    http:// bit.ly/am78yK
    #statingthebleedingobvious
  &lt;/description&gt;
  &lt;category domain="http://delicious.com/simey_j/"&gt;
    statingthebleedingobvious
  &lt;/category&gt;
  &lt;category domain="http://delicious.com/simey_j/"&gt;
    via:packrati.us
  &lt;/category&gt;
&lt;/item&gt;
</code></pre>

<p>In the feed item above (cut down for space), we can see that as well as <code>via:packrati.us</code>, a <code>statingthebleedingobvious</code> tag has been automatically generated from the hashtag in the original tweet.  This is a great feature of Packratius, but it did mean that I ended up having to do more than a very simple filter to filter out the <code>via:packrati.us</code> tagged feed items.</p>

<p>Instead what I did was create a <code>Filter</code> block in pipes with rules that looked at the first 6 tags on each feed item (the idea being that there isn&#8217;t enough space in a tweet for more than 6 or so tags). If any of them matched <code>via:packrati.us</code> then they&#8217;re discarded from the feed.</p>

<p>The filter I created looked something a little like this:</p>

<blockquote>
  <p>Filter</p>
  
  <p>Block items that match any of the following:</p>
  
  <ul>
  <li>item.category.0.content is via:packrati.us</li>
  <li>item.category.1.content is via:packrati.us</li>
  <li>item.category.2.content is via:packrati.us</li>
  <li>item.category.3.content is via:packrati.us</li>
  <li>item.category.4.content is via:packrati.us</li>
  <li>item.category.5.content is via:packrati.us</li>
  <li>item.category.content is via:packrati.us</li>
  </ul>
</blockquote>

<p>Those first six filter rules cover the instance of more than one tag being applied to the item, and the final filter covers the intances where only a single tag exists.  Simple.</p>

<p>And that&#8217;s all there is to it.  Of course, writing this Pipe wouldn&#8217;t be necessary if del.icio.us allowed us to do a simple negative search. Still, it&#8217;s good that it&#8217;s nice and easy to generate <a href="http://pipes.yahoo.com/pipes/pipe.info?_id=537a07fd888c1c38dedcb59f6b5f8101">this new feed</a> using Pipes.</p>

<p>Hopefully it&#8217;ll be useful to a couple of you.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/RT_0H1oZ1j8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/06/packratius-and-the-inevitable-overwhelming-of-del-icio-us/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/06/packratius-and-the-inevitable-overwhelming-of-del-icio-us/</feedburner:origLink></item>
		<item>
		<title>Geolocation &amp; Beer: Part 4 – Finding the Beer</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/LZDu1ldw8JY/</link>
		<comments>http://thecodetrain.co.uk/2010/06/geolocation-and-beer-part-4-finding-the-beer/#comments</comments>
		<pubDate>Wed, 02 Jun 2010 20:24:18 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[apc]]></category>
		<category><![CDATA[beer]]></category>
		<category><![CDATA[caching]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[geolocation]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=449</guid>
		<description><![CDATA[<p>Now, I know I said there were only going to be three parts to this series, but I&#8217;ve just realised that I missed out a quite important part of how I built <a href="http://beernear.me">Beer Near Me</a> &#8211; how I found the beer!</p>

<p>Even though Beer Near Me was only ever really built as a weekend project intended to help me learn about using the <code>navigator.geolocation</code> API, it was still pretty important to get some data to display on a map showing where the local beer was. After all, without that what would the point be?</p>

<h2>Gathering the Data</h2>

<p>To gather the local beer data I make several searches using the <a href="http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_local">Google Local Search API</a> &#8211; one that covers pubs, one for cocktail bars, one for off-licences, and so on.  The searches I make to Google&#8217;s local search look something a little like this:</p>

<pre><code>http://ajax.googleapis.com/ajax/services/search/local
    ?v=1.0
    sll={$sll}
    radius=1
    q={$thing}%20loc:{$sll}
    key={$apiKey}
</code></pre>

<p>Lets walk through those query string parameters…</p>

<ul>
<li><p><code>sll</code> doesn&#8217;t actually stand for anything according to the docs, but denotes the centre point of of the search you&#8217;d like to perform.  It&#8217;s a comma separated latitude/longitude value, so you&#8217;d use something like <code>52.2282962,0.1537945</code> for this parameter.</p></li>
<li><p><code>radius</code> is the radius around the centre point that you&#8217;d like to search within, in miles.</p></li>
<li><p><code>q</code> is the most important parameter by far though; being the query term for the thing you&#8217;re wanting to search for.  </p>

<p>You&#8217;ll notice that I&#8217;ve added a <code>loc:</code> onto the query.  It turns out that without this undocumented extra bit of data, Google really doesn&#8217;t return much from its local searches.  Before <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">last week&#8217;s post</a> I wasn&#8217;t using this extra qualifier, and whilst my searches were being centered around the expected point they weren&#8217;t returning much data. </p>

<p>A case in point was around The Bricklayers Arms. Without the <code>loc:</code> qualifier The Bricklayers Arms was nowhere to be found.  With the qualifier it suddenly started being returned by the searches to Google, along with a whole bunch of other local pubs.</p></li>
</ul>

<h2>Interpreting the Data</h2>

<p>So, if that&#8217;s the search we make, what does the data we get back look like?</p>

<pre><code>{
    "responseData": {
        "results": [
            {
                "GsearchResultClass":"GlocalSearch",
                "lat":"59.334743",
                "lng":"18.063034",
                "accuracy":"8",
                "title":"\u003cb\u003ePub\u003c/b\u003e",
                "titleNoFormatting":"Pub",
                "streetAddress":"Hötorget 13",
                "city":"Stockholm",
                "country":"Sweden",
                …
                "phoneNumbers":[
                    {"type":"","number":"08-782 19 30"},
                    {"type":"","number":"08-789 19 30"}
                ],
                "addressLines":[
                    "Hötorget 13",
                    "111 57 Stockholm, Sweden"
                ]
            },
            {
                // another result
            }
        ]
    }, 
    "responseDetails": null, 
    "responseStatus": 200
}
</code></pre>

<p>(The data above is, by necessity, just a snippet of what&#8217;s actually returned.)</p>

<p>You&#8217;ll notice first of all that each result we get has a <code>GsearchResultClass</code> of <code>GlocalSearch</code> set.  This allows easy checking of the data type returned.  For our searches, the result class will always be <code>GlocalSearch</code>, but other searches would return values for web searches, images searches and more.</p>

<p>Moving on to the data that actually interests us, each item has the expected <code>lat</code> and <code>lng</code> attributes, along with other useful smidgens of data such as <code>title</code>, <code>streetAddress</code> etc.  </p>

<p>Of particular note are the <code>title</code> and <code>titleNoFormatting</code> attributes.  You&#8217;ll notice that within the <code>title</code> field the word &#8220;pub&#8221; is encapsulated in encoded bold tags.  Two things to note here;  first, &#8220;pub&#8221; was the thing we searched for, so this gets emboldened in the standard display that Google would like to use for results (but we&#8217;re still provided <code>titleNoFormatting</code> if we&#8217;d like to have access to an unformatted string). Second, any formatting applied by google in its API results will be encoded in this way.  In Beer Near Me, I take the easy way out for the data I&#8217;m needing, and use <code>titleNoFormatting</code>.</p>

<p>Moving away from the data that comes from a single API request, lets take a quick look at what happens once we move into the world of performing lots of searches at once.  Since with Beer Near Me I&#8217;m making multiple calls to Google to gather beer related locations, there&#8217;s the pretty large possibility that more than one of the searches will return the same place within its results.  The deduping I do on Beer Near Me is very simple in this case &#8211; I simply keep track of the latitudes and longitudes of all the places that have been returned.  That way if any locations turn up in a position that&#8217;s already had data assigned it it I can ignore it.  It&#8217;s not perfect, but it does the job well enough.</p>

<h2>Caching the Data</h2>

<p>The last thing I want to briefly mention that I&#8217;m doing with the data is caching it using PHP&#8217;s <a href="http://php.net/apc">APC</a>.</p>

<p>This was the first time I&#8217;d attempted using APC on one of my own projects, so I had to install it on my Ubuntu based dev box.  I did that following <a href="http://www.debian-administration.org/articles/574">these instructions</a>.</p>

<p>The reason for using APC to cache the calls to Google&#8217;s Local Search is simple &#8211; network calls are expensive and slow.  With on average five calls to Local Search occurring on each page load on Beer Near Me you&#8217;d hope that they wouldn&#8217;t be having to go across network every single time.   So, I implemented a very simple cache check:</p>

<pre><code>$url = "http://ajax.google…q=somesearch"
$urlHash = md5($url);

// Try and pull the data from cache
$results = apc_fetch($urlHash);

// If we don't find the data in cache we'll have to make
// an expensive call across the network.
if (!$results) {
    // Get the data however you'd normally get it
    …
    $results = curl_exec($ch);

    // now store the data in APC
    apc_store(
        $urlHash, // Key to identify the cache by
        $results, // Data from our curl call to store 
        $ttl      // Seconds - how long we'll cache for
    );
}
</code></pre>

<p>As you can see above, I&#8217;m only using two APC methods &#8211; <code>apc_fetch</code> and <code>apc_store</code>.  Really, this is all you generally need for simple caching; one method for getting the data if it already exists, and one for storing it in the cache if you had to get the data from elsewhere.</p>

<p>With that in place, any further requests to that same location will be a lot faster, since we&#8217;ll have stored the results locally.  Result.  For Beer Near Me I can afford to be fairly aggressive with our caching, since for the most parts new pubs and cocktail bars won&#8217;t spring up over the course of a few minutes and become immediately available in Google&#8217;s search results.</p>

<h2>No more locations</h2>

<p>And that&#8217;s it.  As far as I&#8217;m concerned, that&#8217;s everything I&#8217;ve done so far on Beer Near Me.  It&#8217;s been a fun little weekend project, and whilst there&#8217;s more I&#8217;d like to do with it at some point, I think I&#8217;m going to leave it alone for a few weeks now.  After all, <a href="http://sciencehackday.com">Science HackDay</a> is in just over two weeks time, and no doubt I&#8217;ll get wonderfully caught up in something new for that.  Hopefully whatever I hack on will even be useful.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>Now, I know I said there were only going to be three parts to this series, but I&#8217;ve just realised that I missed out a quite important part of how I built <a href="http://beernear.me">Beer Near Me</a> &#8211; how I found the beer!</p>

<p>Even though Beer Near Me was only ever really built as a weekend project intended to help me learn about using the <code>navigator.geolocation</code> API, it was still pretty important to get some data to display on a map showing where the local beer was. After all, without that what would the point be?</p>

<h2>Gathering the Data</h2>

<p>To gather the local beer data I make several searches using the <a href="http://code.google.com/apis/ajaxsearch/documentation/reference.html#_fonje_local">Google Local Search API</a> &#8211; one that covers pubs, one for cocktail bars, one for off-licences, and so on.  The searches I make to Google&#8217;s local search look something a little like this:</p>

<pre><code>http://ajax.googleapis.com/ajax/services/search/local
    ?v=1.0
    sll={$sll}
    radius=1
    q={$thing}%20loc:{$sll}
    key={$apiKey}
</code></pre>

<p>Lets walk through those query string parameters…</p>

<ul>
<li><p><code>sll</code> doesn&#8217;t actually stand for anything according to the docs, but denotes the centre point of of the search you&#8217;d like to perform.  It&#8217;s a comma separated latitude/longitude value, so you&#8217;d use something like <code>52.2282962,0.1537945</code> for this parameter.</p></li>
<li><p><code>radius</code> is the radius around the centre point that you&#8217;d like to search within, in miles.</p></li>
<li><p><code>q</code> is the most important parameter by far though; being the query term for the thing you&#8217;re wanting to search for.  </p>

<p>You&#8217;ll notice that I&#8217;ve added a <code>loc:</code> onto the query.  It turns out that without this undocumented extra bit of data, Google really doesn&#8217;t return much from its local searches.  Before <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">last week&#8217;s post</a> I wasn&#8217;t using this extra qualifier, and whilst my searches were being centered around the expected point they weren&#8217;t returning much data. </p>

<p>A case in point was around The Bricklayers Arms. Without the <code>loc:</code> qualifier The Bricklayers Arms was nowhere to be found.  With the qualifier it suddenly started being returned by the searches to Google, along with a whole bunch of other local pubs.</p></li>
</ul>

<h2>Interpreting the Data</h2>

<p>So, if that&#8217;s the search we make, what does the data we get back look like?</p>

<pre><code>{
    "responseData": {
        "results": [
            {
                "GsearchResultClass":"GlocalSearch",
                "lat":"59.334743",
                "lng":"18.063034",
                "accuracy":"8",
                "title":"\u003cb\u003ePub\u003c/b\u003e",
                "titleNoFormatting":"Pub",
                "streetAddress":"Hötorget 13",
                "city":"Stockholm",
                "country":"Sweden",
                …
                "phoneNumbers":[
                    {"type":"","number":"08-782 19 30"},
                    {"type":"","number":"08-789 19 30"}
                ],
                "addressLines":[
                    "Hötorget 13",
                    "111 57 Stockholm, Sweden"
                ]
            },
            {
                // another result
            }
        ]
    }, 
    "responseDetails": null, 
    "responseStatus": 200
}
</code></pre>

<p>(The data above is, by necessity, just a snippet of what&#8217;s actually returned.)</p>

<p>You&#8217;ll notice first of all that each result we get has a <code>GsearchResultClass</code> of <code>GlocalSearch</code> set.  This allows easy checking of the data type returned.  For our searches, the result class will always be <code>GlocalSearch</code>, but other searches would return values for web searches, images searches and more.</p>

<p>Moving on to the data that actually interests us, each item has the expected <code>lat</code> and <code>lng</code> attributes, along with other useful smidgens of data such as <code>title</code>, <code>streetAddress</code> etc.  </p>

<p>Of particular note are the <code>title</code> and <code>titleNoFormatting</code> attributes.  You&#8217;ll notice that within the <code>title</code> field the word &#8220;pub&#8221; is encapsulated in encoded bold tags.  Two things to note here;  first, &#8220;pub&#8221; was the thing we searched for, so this gets emboldened in the standard display that Google would like to use for results (but we&#8217;re still provided <code>titleNoFormatting</code> if we&#8217;d like to have access to an unformatted string). Second, any formatting applied by google in its API results will be encoded in this way.  In Beer Near Me, I take the easy way out for the data I&#8217;m needing, and use <code>titleNoFormatting</code>.</p>

<p>Moving away from the data that comes from a single API request, lets take a quick look at what happens once we move into the world of performing lots of searches at once.  Since with Beer Near Me I&#8217;m making multiple calls to Google to gather beer related locations, there&#8217;s the pretty large possibility that more than one of the searches will return the same place within its results.  The deduping I do on Beer Near Me is very simple in this case &#8211; I simply keep track of the latitudes and longitudes of all the places that have been returned.  That way if any locations turn up in a position that&#8217;s already had data assigned it it I can ignore it.  It&#8217;s not perfect, but it does the job well enough.</p>

<h2>Caching the Data</h2>

<p>The last thing I want to briefly mention that I&#8217;m doing with the data is caching it using PHP&#8217;s <a href="http://php.net/apc">APC</a>.</p>

<p>This was the first time I&#8217;d attempted using APC on one of my own projects, so I had to install it on my Ubuntu based dev box.  I did that following <a href="http://www.debian-administration.org/articles/574">these instructions</a>.</p>

<p>The reason for using APC to cache the calls to Google&#8217;s Local Search is simple &#8211; network calls are expensive and slow.  With on average five calls to Local Search occurring on each page load on Beer Near Me you&#8217;d hope that they wouldn&#8217;t be having to go across network every single time.   So, I implemented a very simple cache check:</p>

<pre><code>$url = "http://ajax.google…q=somesearch"
$urlHash = md5($url);

// Try and pull the data from cache
$results = apc_fetch($urlHash);

// If we don't find the data in cache we'll have to make
// an expensive call across the network.
if (!$results) {
    // Get the data however you'd normally get it
    …
    $results = curl_exec($ch);

    // now store the data in APC
    apc_store(
        $urlHash, // Key to identify the cache by
        $results, // Data from our curl call to store 
        $ttl      // Seconds - how long we'll cache for
    );
}
</code></pre>

<p>As you can see above, I&#8217;m only using two APC methods &#8211; <code>apc_fetch</code> and <code>apc_store</code>.  Really, this is all you generally need for simple caching; one method for getting the data if it already exists, and one for storing it in the cache if you had to get the data from elsewhere.</p>

<p>With that in place, any further requests to that same location will be a lot faster, since we&#8217;ll have stored the results locally.  Result.  For Beer Near Me I can afford to be fairly aggressive with our caching, since for the most parts new pubs and cocktail bars won&#8217;t spring up over the course of a few minutes and become immediately available in Google&#8217;s search results.</p>

<h2>No more locations</h2>

<p>And that&#8217;s it.  As far as I&#8217;m concerned, that&#8217;s everything I&#8217;ve done so far on Beer Near Me.  It&#8217;s been a fun little weekend project, and whilst there&#8217;s more I&#8217;d like to do with it at some point, I think I&#8217;m going to leave it alone for a few weeks now.  After all, <a href="http://sciencehackday.com">Science HackDay</a> is in just over two weeks time, and no doubt I&#8217;ll get wonderfully caught up in something new for that.  Hopefully whatever I hack on will even be useful.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/LZDu1ldw8JY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/06/geolocation-and-beer-part-4-finding-the-beer/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/06/geolocation-and-beer-part-4-finding-the-beer/</feedburner:origLink></item>
		<item>
		<title>Geolocation &amp; Beer: Part 3 – Static Maps</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/iZqu4-YnFuk/</link>
		<comments>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/#comments</comments>
		<pubDate>Mon, 17 May 2010 20:10:43 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[map]]></category>
		<category><![CDATA[progressive enhancement]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=391</guid>
		<description><![CDATA[<p>The final thing to do now that you&#8217;ve <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/">found the user&#8217;s latitude and longitude</a> and <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">told them where they are in a human readable format</a> is to put all the data you&#8217;ve gathered onto a map.  </p>

<p>Now, there are lots of tutorials out there for the JavaScript based Google Maps API.  So, if you want to learn how to use the JavaScript based API, then I urge you to pop off and look at one of those.  For <a href="http://beernear.me">Beer Near Me</a>, I started off using that API.  However, for various reasons (slow to download over the phone network, scrolling issues on the iPhone), I decided to move away from that and towards the <a href="http://code.google.com/apis/maps/documentation/staticmaps/">Google Static Map API</a>. The big win for me in using the Static Map API is that it means the user only has to download a single image into their browser, and all the hard work of putting the map together is done on the server-side.  The negatives are that you don&#8217;t get nice clickable areas on the map for free, and you don&#8217;t get to scroll around easily.</p>

<p>For me, the positives outweighed the negatives, so I went with the static map.</p>

<pre><code>http://maps.google.com/maps/api/staticmap
    ?sensor=true
    &amp;size=320x300
    &amp;zoom=15
    &amp;maptype=roadmap
    &amp;mobile=true
    &amp;center=lat,lon
</code></pre>

<p>As you can see, although there are a whole bunch of parameters needed to put a map together, they are all fairly simply.  Lets walk through them one at a time:</p>

<p><img src="http://maps.google.com/maps/api/staticmap?sensor=true&#038;size=320x300&#038;zoom=15&#038;maptype=roadmap&#038;mobile=true&#038;center=52.2282962,0.1537945" alt="A simple static google map" class="sidenote"></p>

<ul>
<li><code>sensor</code>: Required; either true or false. This simply tells google whether or not we used a sensor to generate this maps&#8217;s position. For us, this will always be true.</li>
<li><code>size</code>: Very simple, just width, an &#8216;x&#8217;, and then height in pixels.</li>
<li><code>zoom</code>: How zoomed in you are. 15 gives you a pretty zoomed in map.</li>
<li><code>maptype</code>: For us, a roadmap is what we want.  You can also choose satellite or terrain, the same as if you were using the Google Maps site.</li>
<li><code>mobile</code>: Setting this to true allegedly gives you slightly simplified maps that have a lower weight.</li>
<li><code>center</code>: Finally, where the centre of our maps should be.  Simple.</li>
</ul>

<p>Put it all together and you get something a little like <a href="http://maps.google.com/maps/api/staticmap?sensor=true&amp;size=320x300&amp;zoom=15&amp;maptype=roadmap&amp;mobile=true&amp;center=52.2282962,0.1537945">this</a>.</p>

<h2>Adding some custom markers</h2>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.022.jpg" alt="Getting a static map"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.024.jpg" alt="Custom markers"></p>

<p>But, that&#8217;s a bit boring.  Normal Google Maps let you add all sorts of custom icons.  Well, we can do that too, by adding one or more <code>markers</code> parameters to the image URL.  So, to add three icons to a map, two of them the same and the third different, you&#8217;d add something like the following to the above URL.</p>

<pre><code>markers=
    icon:http:%3A%2F%2Furl.encoded%2Fimage.png
    |shadow:false
    |52.2282962,0.1537945
    |52.134152,-0.486364
&amp;markers=
    icon:http:%3A%2F%2Furl.encoded%2Fotherimage.gif
    |shadow:false
    |52.135472,-0.491835
</code></pre>

<p>And that&#8217;s all there is to it.  After you whack a load of markers onto the URL you&#8217;ll end up with something like the <a href="http://beernear.me/coord/51.517819/-0.13150/">Tottenham Court Road on Beer Near Me</a>, where you can see, amongst all the other drinking establishments, The Bricklayers Arms &#8211; the home of <a href="http://www.pubstandards.co.uk/">Pub Standards</a> (the middle thursday of every month &#8211; tell your friends).</p>

<h2>Making the map clickable</h2>

<p>You might also notice on that page that if you click or tap on any of the icons on the map that you end with a popup telling you about the place. But how is this possible? Aren&#8217;t we using Google&#8217;s Static map API?</p>

<p>The answer is &#8220;yes, we&#8217;re still using Google&#8217;s Static map API&#8221;, but we&#8217;re using a little sliver of progressive enhancement to layer data on top of it.  If you turn JavaScript and CSS off for a moment, you&#8217;ll notice a couple of lists underneath the map image.  The first contains links which have targets pointing to the relevant items in the second list.  So, with no CSS or JavaScript clicking the links will move you through the page to the data.  Useful, but not yet useful enough.</p>

<p>So, what if the user has CSS enabled? What can we do then?  Well, what I chose to do on Beer Near me was to position each of those links over the icon they were assigned to.  First off, all the icons were the same size, so I created some base CSS that I could hang the final positions off:</p>

<pre><code>#map_canvas {
    position: relative;
}

#map_canvas .marker,
#map_canvas .marker a {
    display: block;
    width: 30px;
    height: 30px;
    overflow: hidden;
    text-indent: -9000em;
    list-style: none;
}

#map_canvas .marker {
    position: absolute;
}
</code></pre>

<p>Of course, if we just did that then all the clickable areas on the map would be dumped up in the top right of the map, and no-one would be happy.  So, we need to work out where to put those <code>top</code> and <code>left</code> directives.</p>

<h3>Warning. Hard-coded Assumptions Incoming</h3>

<p>Now, it turns out that if you&#8217;re zoomed in to a Google zoom of 15 then each pixel equates to roughly 0.0000266667 units of latitude and 0.0000421875 units of longitude.  So, we can use this to work out where we&#8217;d like to position our clickable elements.</p>

<p>Lets take an example:</p>

<blockquote>
  <p>You have created a map that is 320 pixels wide and 300 pixels high, and zoomed to a level of 15.  The centre of this map is at <code>51.510953,-0.133012</code>.</p>
  
  <p>A public drinking establishment (De Hems) exists at <code>51.512081,-0.131288</code>.  Assuming a 30&#215;30 clickable area with its centre at De Hem&#8217;s location, what should the top and left co-ordinates of the clickable area be?</p>
</blockquote>

<p>On Beer Near Me, I work out the numbers using some code that looks something like the following.</p>

<pre><code>$mapHeightPix = 300;
$latPerPixel  = 0.0000266667;

$centreLat    = 51.510953;
$locationLat  = 51.512081;
$latDiff      = $locationLat - $centreLat;

$topPix = ($mapHeightPix / 2) - (int)($latDiff / $latPerPixel) - 15;
</code></pre>

<p>Unsurprisingly, there&#8217;s something remarkably similar for longitudes.  (If you&#8217;re wondering, the answer was <code>top: 93px; left: 185px;</code>).</p>

<p>Now, with that code in place to position the clickable areas on the map users can click and be taken to the relevant item further down the page.  On Beer Near Me I then layered on top some JavaScript to do a simple popup (again, look at the <a href="http://beernear.me/coord/51.517819/-0.13150/">Tottenham Court Road</a> page), but I&#8217;ll leave that as an exercise for the reader.</p>

<p>And that&#8217;s essentially that.  What started as a simple weekend project to learn how to use the <code>navigator.geolocation</code> API ballooned slightly to encompass Google&#8217;s local search API and static maps APIs, and I ended up with <a href="http://beernear.me">something slightly useful</a>.  Maybe in a year or two I&#8217;ll have even made it pretty.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>The final thing to do now that you&#8217;ve <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/">found the user&#8217;s latitude and longitude</a> and <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">told them where they are in a human readable format</a> is to put all the data you&#8217;ve gathered onto a map.  </p>

<p>Now, there are lots of tutorials out there for the JavaScript based Google Maps API.  So, if you want to learn how to use the JavaScript based API, then I urge you to pop off and look at one of those.  For <a href="http://beernear.me">Beer Near Me</a>, I started off using that API.  However, for various reasons (slow to download over the phone network, scrolling issues on the iPhone), I decided to move away from that and towards the <a href="http://code.google.com/apis/maps/documentation/staticmaps/">Google Static Map API</a>. The big win for me in using the Static Map API is that it means the user only has to download a single image into their browser, and all the hard work of putting the map together is done on the server-side.  The negatives are that you don&#8217;t get nice clickable areas on the map for free, and you don&#8217;t get to scroll around easily.</p>

<p>For me, the positives outweighed the negatives, so I went with the static map.</p>

<pre><code>http://maps.google.com/maps/api/staticmap
    ?sensor=true
    &amp;size=320x300
    &amp;zoom=15
    &amp;maptype=roadmap
    &amp;mobile=true
    &amp;center=lat,lon
</code></pre>

<p>As you can see, although there are a whole bunch of parameters needed to put a map together, they are all fairly simply.  Lets walk through them one at a time:</p>

<p><img src="http://maps.google.com/maps/api/staticmap?sensor=true&#038;size=320x300&#038;zoom=15&#038;maptype=roadmap&#038;mobile=true&#038;center=52.2282962,0.1537945" alt="A simple static google map" class="sidenote"></p>

<ul>
<li><code>sensor</code>: Required; either true or false. This simply tells google whether or not we used a sensor to generate this maps&#8217;s position. For us, this will always be true.</li>
<li><code>size</code>: Very simple, just width, an &#8216;x&#8217;, and then height in pixels.</li>
<li><code>zoom</code>: How zoomed in you are. 15 gives you a pretty zoomed in map.</li>
<li><code>maptype</code>: For us, a roadmap is what we want.  You can also choose satellite or terrain, the same as if you were using the Google Maps site.</li>
<li><code>mobile</code>: Setting this to true allegedly gives you slightly simplified maps that have a lower weight.</li>
<li><code>center</code>: Finally, where the centre of our maps should be.  Simple.</li>
</ul>

<p>Put it all together and you get something a little like <a href="http://maps.google.com/maps/api/staticmap?sensor=true&amp;size=320x300&amp;zoom=15&amp;maptype=roadmap&amp;mobile=true&amp;center=52.2282962,0.1537945">this</a>.</p>

<h2>Adding some custom markers</h2>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.022.jpg" alt="Getting a static map"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.024.jpg" alt="Custom markers"></p>

<p>But, that&#8217;s a bit boring.  Normal Google Maps let you add all sorts of custom icons.  Well, we can do that too, by adding one or more <code>markers</code> parameters to the image URL.  So, to add three icons to a map, two of them the same and the third different, you&#8217;d add something like the following to the above URL.</p>

<pre><code>markers=
    icon:http:%3A%2F%2Furl.encoded%2Fimage.png
    |shadow:false
    |52.2282962,0.1537945
    |52.134152,-0.486364
&amp;markers=
    icon:http:%3A%2F%2Furl.encoded%2Fotherimage.gif
    |shadow:false
    |52.135472,-0.491835
</code></pre>

<p>And that&#8217;s all there is to it.  After you whack a load of markers onto the URL you&#8217;ll end up with something like the <a href="http://beernear.me/coord/51.517819/-0.13150/">Tottenham Court Road on Beer Near Me</a>, where you can see, amongst all the other drinking establishments, The Bricklayers Arms &#8211; the home of <a href="http://www.pubstandards.co.uk/">Pub Standards</a> (the middle thursday of every month &#8211; tell your friends).</p>

<h2>Making the map clickable</h2>

<p>You might also notice on that page that if you click or tap on any of the icons on the map that you end with a popup telling you about the place. But how is this possible? Aren&#8217;t we using Google&#8217;s Static map API?</p>

<p>The answer is &#8220;yes, we&#8217;re still using Google&#8217;s Static map API&#8221;, but we&#8217;re using a little sliver of progressive enhancement to layer data on top of it.  If you turn JavaScript and CSS off for a moment, you&#8217;ll notice a couple of lists underneath the map image.  The first contains links which have targets pointing to the relevant items in the second list.  So, with no CSS or JavaScript clicking the links will move you through the page to the data.  Useful, but not yet useful enough.</p>

<p>So, what if the user has CSS enabled? What can we do then?  Well, what I chose to do on Beer Near me was to position each of those links over the icon they were assigned to.  First off, all the icons were the same size, so I created some base CSS that I could hang the final positions off:</p>

<pre><code>#map_canvas {
    position: relative;
}

#map_canvas .marker,
#map_canvas .marker a {
    display: block;
    width: 30px;
    height: 30px;
    overflow: hidden;
    text-indent: -9000em;
    list-style: none;
}

#map_canvas .marker {
    position: absolute;
}
</code></pre>

<p>Of course, if we just did that then all the clickable areas on the map would be dumped up in the top right of the map, and no-one would be happy.  So, we need to work out where to put those <code>top</code> and <code>left</code> directives.</p>

<h3>Warning. Hard-coded Assumptions Incoming</h3>

<p>Now, it turns out that if you&#8217;re zoomed in to a Google zoom of 15 then each pixel equates to roughly 0.0000266667 units of latitude and 0.0000421875 units of longitude.  So, we can use this to work out where we&#8217;d like to position our clickable elements.</p>

<p>Lets take an example:</p>

<blockquote>
  <p>You have created a map that is 320 pixels wide and 300 pixels high, and zoomed to a level of 15.  The centre of this map is at <code>51.510953,-0.133012</code>.</p>
  
  <p>A public drinking establishment (De Hems) exists at <code>51.512081,-0.131288</code>.  Assuming a 30&#215;30 clickable area with its centre at De Hem&#8217;s location, what should the top and left co-ordinates of the clickable area be?</p>
</blockquote>

<p>On Beer Near Me, I work out the numbers using some code that looks something like the following.</p>

<pre><code>$mapHeightPix = 300;
$latPerPixel  = 0.0000266667;

$centreLat    = 51.510953;
$locationLat  = 51.512081;
$latDiff      = $locationLat - $centreLat;

$topPix = ($mapHeightPix / 2) - (int)($latDiff / $latPerPixel) - 15;
</code></pre>

<p>Unsurprisingly, there&#8217;s something remarkably similar for longitudes.  (If you&#8217;re wondering, the answer was <code>top: 93px; left: 185px;</code>).</p>

<p>Now, with that code in place to position the clickable areas on the map users can click and be taken to the relevant item further down the page.  On Beer Near Me I then layered on top some JavaScript to do a simple popup (again, look at the <a href="http://beernear.me/coord/51.517819/-0.13150/">Tottenham Court Road</a> page), but I&#8217;ll leave that as an exercise for the reader.</p>

<p>And that&#8217;s essentially that.  What started as a simple weekend project to learn how to use the <code>navigator.geolocation</code> API ballooned slightly to encompass Google&#8217;s local search API and static maps APIs, and I ended up with <a href="http://beernear.me">something slightly useful</a>.  Maybe in a year or two I&#8217;ll have even made it pretty.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/iZqu4-YnFuk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/</feedburner:origLink></item>
		<item>
		<title>Geolocation &amp; Beer: Part 2 – Telling the User where they are</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/hOwkNYFOEnc/</link>
		<comments>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/#comments</comments>
		<pubDate>Thu, 13 May 2010 22:00:23 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[beer]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[javascript]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=387</guid>
		<description><![CDATA[<p>In the last entry, I wrote about how we can <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/">use the <code>navigator.geolocation</code> API to find a user&#8217;s current location</a>.  In today&#8217;s post I&#8217;m going to continue on to talk about taking that location and turning it into a piece of human readable text that tells the user where they are.</p>

<p>After all, having a latitude and longitude is all well and good, but as humans we tend not to think that way.  So, it would be quite nice to give the user a street address identifying their location.  As it turns out, this is pretty easy &#8211; the <a href="http://code.google.com/apis/ajaxsearch/documentation/reference.html#_intro_fonje">Google Local Search API</a> has this functionality baked in.</p>

<pre><code>http://ajax.googleapis.com/ajax/services/search/local
    ?v=1.0
    q=lat,lon
    key=yourgoogleapikey
</code></pre>

<p>If you add your comma separated latitude and longitude to the structure above, Google will very helpfully return you back a load of data that tells you where it thinks you are:</p>

<pre><code>{
    "responseData": {
        "results": [{
            …
            "title": "Kelly Langley…",
            "streetAddress": "Cowley Road, Cavendish House…",
            "city": "Cambridge",
            …
        }]
    }
}
</code></pre>

<p>This is only a chunk of the data that gets returned by Google, but hopefully it should be enough to give you a taste of what&#8217;s available. The obvious thing to do at this point is grab the street address, and maybe the city, and output them as a string to show the user.  This is what <a href="http://beernear.me">Beer Near Me</a> is currently doing.</p>

<p><img src='http://thecodetrain.co.uk/images/geolocation/geolocation.018.jpg' alt='Getting an address'> <img src='http://thecodetrain.co.uk/images/geolocation/geolocation.019.jpg' alt='Bob of location data'></p>

<p>One piece of data we haven&#8217;t used yet though is the <code>accuracy</code> data that is returned by <code>navigator.geolocation.getCurrentPosition</code>.  You&#8217;ll remember from the last post that <code>accuracy</code> is measured in metres and shows the confidence that the browser has of the location it&#8217;s returned.  So, if <code>accuracy</code> is given as 500, then the user might be anywhere within a half kilometre radius of the given location &#8211; not particularly accurate, and saying that they were on a specific street would be misleading.  If, on the other hand, the <code>accuracy</code> given was 25 then the user would be expected to be within a 25 metre radius of the given location &#8211; pretty damn accurate.</p>

<p>So, what can we do with this? Well, if we&#8217;re not happy that the <code>accuracy</code> is good enough then we could tell the user they were &#8220;Somewhere in <code>city</code>&#8221; instead of simply showing them the street name of their location.  On Beer Near Me, I could also stop the &#8220;Find me some beer&#8221; button from working until the <code>accuracy</code> was good enough.</p>

<p>So, that&#8217;s how I&#8217;m dealing with showing the user where they are in a nice human readable way on <a href="http://beernear.me">Beer Near Me</a> &#8211; it&#8217;s pretty easy, right?  In the next entry I&#8217;ll finish off talking about the <code>navigator.geolocation</code> API by <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">putting the user&#8217;s location onto a static Google Map</a>.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>In the last entry, I wrote about how we can <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/">use the <code>navigator.geolocation</code> API to find a user&#8217;s current location</a>.  In today&#8217;s post I&#8217;m going to continue on to talk about taking that location and turning it into a piece of human readable text that tells the user where they are.</p>

<p>After all, having a latitude and longitude is all well and good, but as humans we tend not to think that way.  So, it would be quite nice to give the user a street address identifying their location.  As it turns out, this is pretty easy &#8211; the <a href="http://code.google.com/apis/ajaxsearch/documentation/reference.html#_intro_fonje">Google Local Search API</a> has this functionality baked in.</p>

<pre><code>http://ajax.googleapis.com/ajax/services/search/local
    ?v=1.0
    q=lat,lon
    key=yourgoogleapikey
</code></pre>

<p>If you add your comma separated latitude and longitude to the structure above, Google will very helpfully return you back a load of data that tells you where it thinks you are:</p>

<pre><code>{
    "responseData": {
        "results": [{
            …
            "title": "Kelly Langley…",
            "streetAddress": "Cowley Road, Cavendish House…",
            "city": "Cambridge",
            …
        }]
    }
}
</code></pre>

<p>This is only a chunk of the data that gets returned by Google, but hopefully it should be enough to give you a taste of what&#8217;s available. The obvious thing to do at this point is grab the street address, and maybe the city, and output them as a string to show the user.  This is what <a href="http://beernear.me">Beer Near Me</a> is currently doing.</p>

<p><img src='http://thecodetrain.co.uk/images/geolocation/geolocation.018.jpg' alt='Getting an address'> <img src='http://thecodetrain.co.uk/images/geolocation/geolocation.019.jpg' alt='Bob of location data'></p>

<p>One piece of data we haven&#8217;t used yet though is the <code>accuracy</code> data that is returned by <code>navigator.geolocation.getCurrentPosition</code>.  You&#8217;ll remember from the last post that <code>accuracy</code> is measured in metres and shows the confidence that the browser has of the location it&#8217;s returned.  So, if <code>accuracy</code> is given as 500, then the user might be anywhere within a half kilometre radius of the given location &#8211; not particularly accurate, and saying that they were on a specific street would be misleading.  If, on the other hand, the <code>accuracy</code> given was 25 then the user would be expected to be within a 25 metre radius of the given location &#8211; pretty damn accurate.</p>

<p>So, what can we do with this? Well, if we&#8217;re not happy that the <code>accuracy</code> is good enough then we could tell the user they were &#8220;Somewhere in <code>city</code>&#8221; instead of simply showing them the street name of their location.  On Beer Near Me, I could also stop the &#8220;Find me some beer&#8221; button from working until the <code>accuracy</code> was good enough.</p>

<p>So, that&#8217;s how I&#8217;m dealing with showing the user where they are in a nice human readable way on <a href="http://beernear.me">Beer Near Me</a> &#8211; it&#8217;s pretty easy, right?  In the next entry I&#8217;ll finish off talking about the <code>navigator.geolocation</code> API by <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">putting the user&#8217;s location onto a static Google Map</a>.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/hOwkNYFOEnc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/</feedburner:origLink></item>
		<item>
		<title>Geolocation &amp; Beer: Part 1 – Finding the user</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/ERzzqZ9HSAU/</link>
		<comments>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/#comments</comments>
		<pubDate>Wed, 05 May 2010 22:10:37 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[barcamb]]></category>
		<category><![CDATA[barcamp]]></category>
		<category><![CDATA[beer]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[w3c]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=389</guid>
		<description><![CDATA[<p>I recently attended <a href="http://barcamb.ltheobald.co.uk/">BarCamb3</a>, a BarCamp held at the lovely <a href="http://www.red-gate.com/">Red Gate Software</a> offices in Cambridge.  My main contribution to the event was talking about a silly little weekend learning project I&#8217;ve been working on &#8211; <a href="http://beernear.me/">Beer Near Me</a>.  </p>

<p>I&#8217;ve split the presentation I gave up into three separate blog posts, which I&#8217;ll be putting up over the next few days.  This one will cover basic use of the <code>navigator.geolocation</code> API, with the next two showing you how to use the data that gives you to <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">find out where the user actually is</a>, and to <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">plot that data using Google&#8217;s static maps API</a>.</p>

<p>To start, I should explain that Beer Near Me was only ever intended to be used as a platform to be used to help me learn about the <a href="http://dev.w3.org/geo/api/spec-source.html"><code>navigator.geolocation</code> API</a> and how it works.  All the data that gets shown by the site was initially kind of secondary to me, as was the map that I use to show it off.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.001.png" alt="GeoLocation &#038; Beer"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.009.png" alt="What will I talk about?"></p>

<p>Of course, <code>navigator.geolocation</code> turned out to be far easier to work with than I expected.</p>

<h2>Support for navigator.geolocation</h2>

<p>The first thing to be aware of is that right now, support for <code>navigator.geolocation</code> is pretty poor.  If you&#8217;re using Firefox 3.5+ or Mobile Safari you can use it out of the box, and you can <a href="http://www.wait-till-i.com/2010/03/04/google-chrome-getting-navigator-geolocation/">turn on geolocation support in Chrome</a>, but apart from that you&#8217;re plain out of luck.  Since the idea behind Beer Near Me was to learn about <code>navigator.geolocation</code> that means that right now I&#8217;m not supporting any other APIs.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.011.png" alt="navigator.geolocation support"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.012.jpg" alt="Other APIs are available"></p>

<p>It&#8217;s not all doom and gloom though.  There are implementations of the API available for use in other browsers as plugins &#8211; for example <a href="http://en.wikipedia.org/wiki/Mozilla_Geode">Geode</a> will get you support in earlier versions of Firefox 3, and Google Gears opens up the ability to play with geolocation in some other browsers.  There&#8217;s also <a href="http://www.loki.com">Loki</a> (a Skyhook based plugin) that will get you support over a whole bunch of browsers but uses a differently specified API. Of course, I&#8217;ve not actually used any of these, so your guess is as good as mine as to whether they are any good or not.  I seem to remember Loki looking interesting a couple of years ago.</p>

<h2>Finding a location</h2>

<p>So, how to actually use <code>navigator.geolocation</code>?  Well, a good place to start would be checking whether the browser supports it.  We&#8217;ll do this in JavaScript, since that&#8217;s the only place we can reliably perform the check.</p>

<pre><code>if (navigator.geolocation) {
    // do geolocation
} else {
    // say sorry to the user
}
</code></pre>

<p>Pretty simple.  It&#8217;s your standard JavaScript functionality check.  If we have the functionality available then we can actually do something with it.</p>

<pre><code>navigator.geolocation.getCurrentPosition(
    foundLocation, // a user generated function
    noLocation,    // yup, user generated too
    {
        enableHighAccuracy: true,
        maximumAge: 1800,
        timeout: 30
    }
);
</code></pre>

<p>Here, we&#8217;re calling <code>getCurrentLocation()</code> with two callback functions and some options.  Once the browser (and the user) has decided whether or not to return a location, either the <code>foundLocation</code> or <code>noLocation</code> functions that you get to write are called, and the user&#8217;s location is passed into them.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.013.jpg" alt="GeoLocation check"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.014.jpg" alt="getCurrentPosition"></p>

<p>Before we get to those callback functions though, lets talk for a minute about the options we&#8217;re passing into <code>getCurrentPosition()</code>.  Essentially, what we&#8217;re saying in the code above is:</p>

<blockquote>
  <p>Try to give me highly accurate positioning if possible. I&#8217;d like to actually know where I am.</p>
  
  <p>Oh, and if you have a position cached that&#8217;s no older than 30 minutes, could you give me that immediately please?  I know that kind of contradicts what I said a moment ago, but I know better than you.</p>
  
  <p>And one more thing &#8211; if it takes you longer than 30 seconds to work out where you are, just give up.  There&#8217;s a good fellow.</p>
</blockquote>

<p>And that&#8217;s it really, with that little bit of code we&#8217;ve set up the browser to do some magic and try and work out where it is.</p>

<h2>Watching for changes to the user&#8217;s location</h2>

<p>The only problem with this is that the iPhone in particular seems a bit rubbish at following these instructions.  The first result it sends back seems to be uniformly Not Good, and generally quite old.  So what can we do about that?  Thankfully, there&#8217;s an easy answer provided to us in the API &#8211; the <code>watchPosition()</code> function.  This uses exactly the same parameters as <code>getCurrentLocation()</code> and can simply be dropped in as a replacement for it.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.015.jpg" alt="Keep finding the user"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.016.jpg" alt="You've got a location!"></p>

<p><code>watchPosition</code> does what you&#8217;d expect it to do.  Instead of finding a location once, it will keep watching the user&#8217;s location and call the success function whenever the location changes.  The thing that <code>watchPosition()</code> is most useful for though, is getting more accurate locations as the user&#8217;s device tries different location finding techniques.</p>

<p>You see, different devices will try and locate themselves in different ways.  Some of the techniques they&#8217;ll use will be be quick, some slow, and some very expensive when it comes to battery life.  For example, here&#8217;s what the iPhone does, as I understand it:</p>

<ol>
<li><p>First, the iPhone will look to see if there&#8217;s a location cached in its memory and give you that if available.</p></li>
<li><p>If you&#8217;re still watching for new locations, it will then try and grab a general location from the cell towers it knows about.</p>

<p>Depending on the number of towers available, this might give you really good, or really bad accuracy.  For example, in Bedford (where I live), using cell tower triangulation I get an accuracy which basically covers the entire town.  Not particularly useful.  It is quick though, and doesn&#8217;t really cost much in terms of power.</p></li>
<li><p>If the cell tower triangulation wasn&#8217;t awesome then the iPhone will gather information about all the WIFI hotspots it can see.  </p>

<p>This data gets sent to Skyhook, which does a whole bunch of triangulation and sends back a result.  This is obviously slower than just hitting the cell towers, but it can give you a really accurate result depending on where you are (better than GPS in some built up areas).</p></li>
<li><p>Finally, if you&#8217;re still looking for a location, the iPhone will fire up the GPS.  </p>

<p>This is expensive, slow, and drains the battery, so is left as a last resort.  It generally gives the best quality results though.</p></li>
</ol>

<p>So, you see, things aren&#8217;t necessarily as simple as asking for a position.  A whole bunch of stuff can be going on in the background, and grabbing the best location the user&#8217;s device knows about as time goes on can be very useful.</p>

<h2>Using the location we&#8217;ve found</h2>

<p>So, now that we&#8217;re requesting a location, what do we do once we actually get one?  Lets have a look.</p>

<pre><code>foundLocation = function(position) {
    // position.coords.latitude
    // position.coords.longitude
    // position.coords.accuracy
    // and more…
}
</code></pre>

<p>As you can see, the function we call on location finding success gets passed a single parameter &#8211; <a href="http://dev.w3.org/geo/api/spec-source.html#position_interface"><code>position</code></a>.  This object contains a <code>coords</code> object, and a <code>timestamp</code> representing the time the location was determined.  It&#8217;s the <code>coords</code> object we&#8217;re interested in right now, since that contains the latitude, longitude and accuracy (measured in metres) of the user&#8217;s current location &#8211; all the information a grown chap could need for finding some beer.  Other applications might make use of the altitude, heading and speed information, but that&#8217;s not vital when looking for beer, so I&#8217;m ignoring it for now.</p>

<h2>Putting it all together</h2>

<p>Quickly putting all this together, here&#8217;s some JavaScript you can copy and paste into a page to see this all in action.</p>

<pre><code>/*global document, navigator */
var beerLocation = {
    foundLocation: function (position) {
        beerLocation.setPara(
            "Lat = " + position.coords.latitude + 
            ", lon = " + position.coords.longitude + 
            ", accuracy = " + position.coords.accuracy + "m");
    },

    noLocation: function () {
        beerLocation.setPara("Boo! You didn't share your location!");
    },

    noGeoLocation: function () {
        beerLocation.setPara(
            "Sorry, but your browser doesn't support geolocation.");
    },

    setPara: function (text) {
        var p = document.getElementById('location-info');
        if (!p) {
            p = document.createElement('p');
            p.id = 'location-info';
            document.body.appendChild(p);
        }

        p.innerHTML = text;
    }
};

if (navigator.geolocation) {
    navigator.geolocation.watchPosition(
        beerLocation.foundLocation, 
        beerLocation.noLocation,
        {
            enableHighAccuracy: true,
            maximumAge: 120000
        }
    );
} else {
    beerLocation.noGeoLocation();
}
</code></pre>

<p>Alternatively, take a look at it in situ and in action on my <a href="http://projects.thecodetrain.co.uk/geolocation/">geolocation test page</a>.  The location shown by this page will keep updating as you move around or your device&#8217;s accuracy improves.  For best results, try it out on an iPhone.</p>

<h2>And finally</h2>

<p>And that&#8217;s how easy it is to find a user&#8217;s location and keep updating it.  You just need to do something with the latitude and longitude once you&#8217;ve found it.</p>

<p>In <a href="http://beernear.me/">Beer Near Me</a> I do two things with the location once it&#8217;s been found &#8211; I <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">tell the user the street address of the location</a>, and I display it on a map.  And, of course, I&#8217;ll be writing about how I&#8217;m doing that in the next couple of posts.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>I recently attended <a href="http://barcamb.ltheobald.co.uk/">BarCamb3</a>, a BarCamp held at the lovely <a href="http://www.red-gate.com/">Red Gate Software</a> offices in Cambridge.  My main contribution to the event was talking about a silly little weekend learning project I&#8217;ve been working on &#8211; <a href="http://beernear.me/">Beer Near Me</a>.  </p>

<p>I&#8217;ve split the presentation I gave up into three separate blog posts, which I&#8217;ll be putting up over the next few days.  This one will cover basic use of the <code>navigator.geolocation</code> API, with the next two showing you how to use the data that gives you to <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">find out where the user actually is</a>, and to <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-3-static-maps/">plot that data using Google&#8217;s static maps API</a>.</p>

<p>To start, I should explain that Beer Near Me was only ever intended to be used as a platform to be used to help me learn about the <a href="http://dev.w3.org/geo/api/spec-source.html"><code>navigator.geolocation</code> API</a> and how it works.  All the data that gets shown by the site was initially kind of secondary to me, as was the map that I use to show it off.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.001.png" alt="GeoLocation &#038; Beer"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.009.png" alt="What will I talk about?"></p>

<p>Of course, <code>navigator.geolocation</code> turned out to be far easier to work with than I expected.</p>

<h2>Support for navigator.geolocation</h2>

<p>The first thing to be aware of is that right now, support for <code>navigator.geolocation</code> is pretty poor.  If you&#8217;re using Firefox 3.5+ or Mobile Safari you can use it out of the box, and you can <a href="http://www.wait-till-i.com/2010/03/04/google-chrome-getting-navigator-geolocation/">turn on geolocation support in Chrome</a>, but apart from that you&#8217;re plain out of luck.  Since the idea behind Beer Near Me was to learn about <code>navigator.geolocation</code> that means that right now I&#8217;m not supporting any other APIs.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.011.png" alt="navigator.geolocation support"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.012.jpg" alt="Other APIs are available"></p>

<p>It&#8217;s not all doom and gloom though.  There are implementations of the API available for use in other browsers as plugins &#8211; for example <a href="http://en.wikipedia.org/wiki/Mozilla_Geode">Geode</a> will get you support in earlier versions of Firefox 3, and Google Gears opens up the ability to play with geolocation in some other browsers.  There&#8217;s also <a href="http://www.loki.com">Loki</a> (a Skyhook based plugin) that will get you support over a whole bunch of browsers but uses a differently specified API. Of course, I&#8217;ve not actually used any of these, so your guess is as good as mine as to whether they are any good or not.  I seem to remember Loki looking interesting a couple of years ago.</p>

<h2>Finding a location</h2>

<p>So, how to actually use <code>navigator.geolocation</code>?  Well, a good place to start would be checking whether the browser supports it.  We&#8217;ll do this in JavaScript, since that&#8217;s the only place we can reliably perform the check.</p>

<pre><code>if (navigator.geolocation) {
    // do geolocation
} else {
    // say sorry to the user
}
</code></pre>

<p>Pretty simple.  It&#8217;s your standard JavaScript functionality check.  If we have the functionality available then we can actually do something with it.</p>

<pre><code>navigator.geolocation.getCurrentPosition(
    foundLocation, // a user generated function
    noLocation,    // yup, user generated too
    {
        enableHighAccuracy: true,
        maximumAge: 1800,
        timeout: 30
    }
);
</code></pre>

<p>Here, we&#8217;re calling <code>getCurrentLocation()</code> with two callback functions and some options.  Once the browser (and the user) has decided whether or not to return a location, either the <code>foundLocation</code> or <code>noLocation</code> functions that you get to write are called, and the user&#8217;s location is passed into them.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.013.jpg" alt="GeoLocation check"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.014.jpg" alt="getCurrentPosition"></p>

<p>Before we get to those callback functions though, lets talk for a minute about the options we&#8217;re passing into <code>getCurrentPosition()</code>.  Essentially, what we&#8217;re saying in the code above is:</p>

<blockquote>
  <p>Try to give me highly accurate positioning if possible. I&#8217;d like to actually know where I am.</p>
  
  <p>Oh, and if you have a position cached that&#8217;s no older than 30 minutes, could you give me that immediately please?  I know that kind of contradicts what I said a moment ago, but I know better than you.</p>
  
  <p>And one more thing &#8211; if it takes you longer than 30 seconds to work out where you are, just give up.  There&#8217;s a good fellow.</p>
</blockquote>

<p>And that&#8217;s it really, with that little bit of code we&#8217;ve set up the browser to do some magic and try and work out where it is.</p>

<h2>Watching for changes to the user&#8217;s location</h2>

<p>The only problem with this is that the iPhone in particular seems a bit rubbish at following these instructions.  The first result it sends back seems to be uniformly Not Good, and generally quite old.  So what can we do about that?  Thankfully, there&#8217;s an easy answer provided to us in the API &#8211; the <code>watchPosition()</code> function.  This uses exactly the same parameters as <code>getCurrentLocation()</code> and can simply be dropped in as a replacement for it.</p>

<p><img src="http://thecodetrain.co.uk/images/geolocation/geolocation.015.jpg" alt="Keep finding the user"> <img src="http://thecodetrain.co.uk/images/geolocation/geolocation.016.jpg" alt="You've got a location!"></p>

<p><code>watchPosition</code> does what you&#8217;d expect it to do.  Instead of finding a location once, it will keep watching the user&#8217;s location and call the success function whenever the location changes.  The thing that <code>watchPosition()</code> is most useful for though, is getting more accurate locations as the user&#8217;s device tries different location finding techniques.</p>

<p>You see, different devices will try and locate themselves in different ways.  Some of the techniques they&#8217;ll use will be be quick, some slow, and some very expensive when it comes to battery life.  For example, here&#8217;s what the iPhone does, as I understand it:</p>

<ol>
<li><p>First, the iPhone will look to see if there&#8217;s a location cached in its memory and give you that if available.</p></li>
<li><p>If you&#8217;re still watching for new locations, it will then try and grab a general location from the cell towers it knows about.</p>

<p>Depending on the number of towers available, this might give you really good, or really bad accuracy.  For example, in Bedford (where I live), using cell tower triangulation I get an accuracy which basically covers the entire town.  Not particularly useful.  It is quick though, and doesn&#8217;t really cost much in terms of power.</p></li>
<li><p>If the cell tower triangulation wasn&#8217;t awesome then the iPhone will gather information about all the WIFI hotspots it can see.  </p>

<p>This data gets sent to Skyhook, which does a whole bunch of triangulation and sends back a result.  This is obviously slower than just hitting the cell towers, but it can give you a really accurate result depending on where you are (better than GPS in some built up areas).</p></li>
<li><p>Finally, if you&#8217;re still looking for a location, the iPhone will fire up the GPS.  </p>

<p>This is expensive, slow, and drains the battery, so is left as a last resort.  It generally gives the best quality results though.</p></li>
</ol>

<p>So, you see, things aren&#8217;t necessarily as simple as asking for a position.  A whole bunch of stuff can be going on in the background, and grabbing the best location the user&#8217;s device knows about as time goes on can be very useful.</p>

<h2>Using the location we&#8217;ve found</h2>

<p>So, now that we&#8217;re requesting a location, what do we do once we actually get one?  Lets have a look.</p>

<pre><code>foundLocation = function(position) {
    // position.coords.latitude
    // position.coords.longitude
    // position.coords.accuracy
    // and more…
}
</code></pre>

<p>As you can see, the function we call on location finding success gets passed a single parameter &#8211; <a href="http://dev.w3.org/geo/api/spec-source.html#position_interface"><code>position</code></a>.  This object contains a <code>coords</code> object, and a <code>timestamp</code> representing the time the location was determined.  It&#8217;s the <code>coords</code> object we&#8217;re interested in right now, since that contains the latitude, longitude and accuracy (measured in metres) of the user&#8217;s current location &#8211; all the information a grown chap could need for finding some beer.  Other applications might make use of the altitude, heading and speed information, but that&#8217;s not vital when looking for beer, so I&#8217;m ignoring it for now.</p>

<h2>Putting it all together</h2>

<p>Quickly putting all this together, here&#8217;s some JavaScript you can copy and paste into a page to see this all in action.</p>

<pre><code>/*global document, navigator */
var beerLocation = {
    foundLocation: function (position) {
        beerLocation.setPara(
            "Lat = " + position.coords.latitude + 
            ", lon = " + position.coords.longitude + 
            ", accuracy = " + position.coords.accuracy + "m");
    },

    noLocation: function () {
        beerLocation.setPara("Boo! You didn't share your location!");
    },

    noGeoLocation: function () {
        beerLocation.setPara(
            "Sorry, but your browser doesn't support geolocation.");
    },

    setPara: function (text) {
        var p = document.getElementById('location-info');
        if (!p) {
            p = document.createElement('p');
            p.id = 'location-info';
            document.body.appendChild(p);
        }

        p.innerHTML = text;
    }
};

if (navigator.geolocation) {
    navigator.geolocation.watchPosition(
        beerLocation.foundLocation, 
        beerLocation.noLocation,
        {
            enableHighAccuracy: true,
            maximumAge: 120000
        }
    );
} else {
    beerLocation.noGeoLocation();
}
</code></pre>

<p>Alternatively, take a look at it in situ and in action on my <a href="http://projects.thecodetrain.co.uk/geolocation/">geolocation test page</a>.  The location shown by this page will keep updating as you move around or your device&#8217;s accuracy improves.  For best results, try it out on an iPhone.</p>

<h2>And finally</h2>

<p>And that&#8217;s how easy it is to find a user&#8217;s location and keep updating it.  You just need to do something with the latitude and longitude once you&#8217;ve found it.</p>

<p>In <a href="http://beernear.me/">Beer Near Me</a> I do two things with the location once it&#8217;s been found &#8211; I <a href="http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-2-telling-the-user-where-they-are/">tell the user the street address of the location</a>, and I display it on a map.  And, of course, I&#8217;ll be writing about how I&#8217;m doing that in the next couple of posts.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/ERzzqZ9HSAU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/05/geolocation-and-beer-part-1-finding-the-user/</feedburner:origLink></item>
		<item>
		<title>Review: The Non-Designer’s Design Book</title>
		<link>http://feeds.thecodetrain.co.uk/~r/TheCodeTrain/~3/V0AtRIAMgE0/</link>
		<comments>http://thecodetrain.co.uk/2010/02/review-the-non-designers-design-book/#comments</comments>
		<pubDate>Sun, 21 Feb 2010 15:10:05 +0000</pubDate>
		<dc:creator>Neil Crosby</dc:creator>
				<category><![CDATA[Blog Posts]]></category>
		<category><![CDATA[book]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[review]]></category>

		<guid isPermaLink="false">http://thecodetrain.co.uk/?p=357</guid>
		<description><![CDATA[<p>I&#8217;m not a designer.  That much is obvious from looking at the websites I produce that haven&#8217;t been designed by someone else.  That doesn&#8217;t mean I don&#8217;t care though; which is why on Christmas day last year I ordered <a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.co.uk%2Fs%3Fie%3DUTF8%26redirect%3Dtrue%26search-type%3Dss%26index%3Dbooks-uk%26field-author%3DRobin%2520Williams&amp;tag=workingwmeuk-21&amp;linkCode=ur2&amp;camp=1634&amp;creative=19450">Robin Williams</a>&#8216; &#8220;<a href="http://www.amazon.co.uk/gp/product/0321534042?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321534042">The Non-Designer&#8217;s Design Book</a>&#8221; (as recommended to me by <a href="http://timhuegdon.com/">Tim Huegdon</a>).</p>

<iframe class="sidenote" src="http://rcm-uk.amazon.co.uk/e/cm?lt1=_top&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=workingwmeuk-21&#038;o=2&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=0M5A6TN3AXP2JHJBWT02&#038;asins=0321534042" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>

<p>I&#8217;ve only just got round to reading the book, but I&#8217;m glad I did.  As a learning book it&#8217;s deliberately quite lightweight and easy to get through in a short time, in the same way that <a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.co.uk%2Fs%3Fie%3DUTF8%26redirect%3Dtrue%26search-type%3Dss%26index%3Dbooks-uk%26field-author%3DSteve%2520Krug&amp;tag=workingwmeuk-21&amp;linkCode=ur2&amp;camp=1634&amp;creative=19450">Steve Krug</a>&#8216;s &#8220;<a href="http://www.amazon.co.uk/gp/product/0321344758?ie=UTF8&amp;tag=workingwmeuk-21&amp;linkCode=as2&amp;camp=1634&amp;creative=19450&amp;creativeASIN=0321344758">Don&#8217;t Make Me Think</a>&#8221; is designed to be absorbed in a couple of hours on a flight.  All told, I spent about three hours with this book, on my tube journeys to and from work this week.</p>

<p>Robin starts her journey by briefly explaining the concepts of Contrast, Repetition, Alignment and Proximity (anyone spot an acronym there?), before moving on to explore each concept in greater detail.  As Robin repeatedly tells us in her book, by being able to name the concepts you&#8217;re able to identify them and use them deliberately rather than accidentally.</p>

<p>The second half of the book deals with type; the different styles of typefaces, how to combine them and horrible errors of judgement to avoid.</p>

<p>Spread throughout the book are a bunch of mini quizzes and exercises designed to get you thinking about what you&#8217;ve just read.  The quizzes are a nice touch, and I&#8217;m sure I&#8217;ve retained more knowledge because of them than I would have done if I&#8217;d just read through from cover to cover.</p>

<p>The book covers design in general, rather than being focussed on the web, print or presentations.  Whilst Williams has written a book called &#8220;<a href="http://www.amazon.co.uk/gp/product/0321303377?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321303377">The Non-Designer&#8217;s Web Book</a>&#8221; I specifically chose to buy this one because both I wanted a general overview book and because the web book is from 2005 (a lifetime ago on the web) and has some mixed reviews.  &#8220;The Non-Designer&#8217;s Design Book&#8221; did not disappoint &#8211; it&#8217;s given me that overview that I was looking for, and I now feel a bit more comfortable that my designs will actually work.</p>

<p>&#8220;<a href="http://www.amazon.co.uk/gp/product/0321534042?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321534042">The Non-Designer&#8217;s Design Book</a>&#8221; costs £23.99 in all good bookshops, or currently £12.30 on Amazon.</p>
<div style="display:block"><small><em><a href="http://neilcrosby.com">Neil Crosby</a> also blogs at about t-shirts at <a href="http://iwearcotton.com">I Wear Cotton</a>, writes <a href="http://thetenwordreview.com/users/workingwithme">Ten Word Reviews</a>, and uploads <a href="http://www.flickr.com/photos/thevoicewithin/">photos</a> to flickr.  You can follow a combined feed of posts at <a href="http://neilcrosby.com/">NeilCrosby.com</a>.</em></small></div>]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m not a designer.  That much is obvious from looking at the websites I produce that haven&#8217;t been designed by someone else.  That doesn&#8217;t mean I don&#8217;t care though; which is why on Christmas day last year I ordered <a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.co.uk%2Fs%3Fie%3DUTF8%26redirect%3Dtrue%26search-type%3Dss%26index%3Dbooks-uk%26field-author%3DRobin%2520Williams&amp;tag=workingwmeuk-21&amp;linkCode=ur2&amp;camp=1634&amp;creative=19450">Robin Williams</a>&#8216; &#8220;<a href="http://www.amazon.co.uk/gp/product/0321534042?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321534042">The Non-Designer&#8217;s Design Book</a>&#8221; (as recommended to me by <a href="http://timhuegdon.com/">Tim Huegdon</a>).</p>

<iframe class="sidenote" src="http://rcm-uk.amazon.co.uk/e/cm?lt1=_top&#038;bc1=FFFFFF&#038;IS2=1&#038;bg1=FFFFFF&#038;fc1=000000&#038;lc1=0000FF&#038;t=workingwmeuk-21&#038;o=2&#038;p=8&#038;l=as1&#038;m=amazon&#038;f=ifr&#038;md=0M5A6TN3AXP2JHJBWT02&#038;asins=0321534042" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>

<p>I&#8217;ve only just got round to reading the book, but I&#8217;m glad I did.  As a learning book it&#8217;s deliberately quite lightweight and easy to get through in a short time, in the same way that <a href="http://www.amazon.co.uk/gp/redirect.html?ie=UTF8&amp;location=http%3A%2F%2Fwww.amazon.co.uk%2Fs%3Fie%3DUTF8%26redirect%3Dtrue%26search-type%3Dss%26index%3Dbooks-uk%26field-author%3DSteve%2520Krug&amp;tag=workingwmeuk-21&amp;linkCode=ur2&amp;camp=1634&amp;creative=19450">Steve Krug</a>&#8216;s &#8220;<a href="http://www.amazon.co.uk/gp/product/0321344758?ie=UTF8&amp;tag=workingwmeuk-21&amp;linkCode=as2&amp;camp=1634&amp;creative=19450&amp;creativeASIN=0321344758">Don&#8217;t Make Me Think</a>&#8221; is designed to be absorbed in a couple of hours on a flight.  All told, I spent about three hours with this book, on my tube journeys to and from work this week.</p>

<p>Robin starts her journey by briefly explaining the concepts of Contrast, Repetition, Alignment and Proximity (anyone spot an acronym there?), before moving on to explore each concept in greater detail.  As Robin repeatedly tells us in her book, by being able to name the concepts you&#8217;re able to identify them and use them deliberately rather than accidentally.</p>

<p>The second half of the book deals with type; the different styles of typefaces, how to combine them and horrible errors of judgement to avoid.</p>

<p>Spread throughout the book are a bunch of mini quizzes and exercises designed to get you thinking about what you&#8217;ve just read.  The quizzes are a nice touch, and I&#8217;m sure I&#8217;ve retained more knowledge because of them than I would have done if I&#8217;d just read through from cover to cover.</p>

<p>The book covers design in general, rather than being focussed on the web, print or presentations.  Whilst Williams has written a book called &#8220;<a href="http://www.amazon.co.uk/gp/product/0321303377?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321303377">The Non-Designer&#8217;s Web Book</a>&#8221; I specifically chose to buy this one because both I wanted a general overview book and because the web book is from 2005 (a lifetime ago on the web) and has some mixed reviews.  &#8220;The Non-Designer&#8217;s Design Book&#8221; did not disappoint &#8211; it&#8217;s given me that overview that I was looking for, and I now feel a bit more comfortable that my designs will actually work.</p>

<p>&#8220;<a href="http://www.amazon.co.uk/gp/product/0321534042?ie=UTF8tag=workingwmeuk-21linkCode=as2camp=1634creative=19450creativeASIN=0321534042">The Non-Designer&#8217;s Design Book</a>&#8221; costs £23.99 in all good bookshops, or currently £12.30 on Amazon.</p>
<img src="http://feeds.feedburner.com/~r/TheCodeTrain/~4/V0AtRIAMgE0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://thecodetrain.co.uk/2010/02/review-the-non-designers-design-book/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://thecodetrain.co.uk/2010/02/review-the-non-designers-design-book/</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.768 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-04-10 23:36:37 -->

