<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Passing Curiosity: Posts tagged geographical</title>
    <link href="https://passingcuriosity.com/tags/geographical/geographical.xml" rel="self" />
    <link href="https://passingcuriosity.com" />
    <id>https://passingcuriosity.com/tags/geographical/geographical.xml</id>
    <author>
        <name>Thomas Sutton</name>
        
        <email>me@thomas-sutton.id.au</email>
        
    </author>
    <updated>2009-08-05T00:00:00Z</updated>
    <entry>
    <title>Adding a distance function to MySQL</title>
    <link href="https://passingcuriosity.com/2009/adding-a-distance-function-to-mysql/" />
    <id>https://passingcuriosity.com/2009/adding-a-distance-function-to-mysql/</id>
    <published>2009-08-05T00:00:00Z</published>
    <updated>2009-08-05T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>I’ve been working on a project which involves a little bit of geographical
information lately (using <a href="http://djangoproject.com/">Django</a>, if that’s
important to you) and one of the problems I’ve encountered is MySQL’s
incomplete implementation of the various functions that you’d expect of a
geographically-capable database system. One particular lack is a function to
calculate the distance between two points (or, even, other geometric forms).
Thankfully, it’s possible to define it yourself (though convincing
<a href="http://geodjango.org/">GeoDjango</a> to run it is another matter). There are
copies floating around in the ’tubes, but <a href="http://forge.mysql.com/tools/tool.php?id=41">the one I found at MySQL
forge</a> (and also <a href="http://pabloj.blogspot.com/2006/01/distance-function-for-mysql.html">on the author’s
blog</a>) is
a bit broken.</p>
<p>Below, I’ll mention <em>why</em> I think it’s broken and the small change required to
“fix” it.</p>
<p>Alas, it doesn’t work very well on current versions of MySQL (and judging by a
comment on the page, it didn’t work meaningfully back in 2006): it defines a
new function <code>distance</code> from two <code>POINT</code>s to a <code>DOUBLE</code>. For some bizarre
reason, though, it <code>round</code>s it’s result to 0 decimal places. This makes it
pretty useless for local <code>POINT</code>s in a coarse metric like degrees (where 1° of
latitude is around 111km) when your <code>POINT</code>s are local: you’ll often get a
whole lots of <code>0</code>s in your results:</p>
<p>Thankfully, it’s easy to fix: just remove the call to <code>round</code> (or, I suppose,
give it an accuracy rather than let it default to 0 places).</p>
<p>To <a href="http://dev.mysql.com/doc/refman/5.0/en/create-procedure.html">create the
function</a> simply
run the following command somehow – I prefer the <code>mysql</code> command-line, others
[phpMyAdmin][], and still more will put it in an XML file to be interpreted by
a rule engine which is called as part of a semi-automated deployment process
(these are the people that enjoy using Java):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode sql"><code class="sourceCode sql"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- Switch delimiter so the ; will work in the function body</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>DELIMITER $$</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- Create the function</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">FUNCTION</span> `distance`(a POINT, b POINT) RETURNS <span class="dt">double</span> </span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    DETERMINISTIC </span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">COMMENT</span> <span class="st">'Calculate the distance between two points'</span> </span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">BEGIN</span> </span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="kw">RETURN</span> glength(linestringfromwkb(linestring(asbinary(a), asbinary(b)))); </span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    END$$</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">-- Switch the delimiter back to ;</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>DELIMITER ;</span></code></pre></div>
<p>You’ll notice that this is calling both <code>asbinary</code> (to convert WKB values into
internal MySQL values) and then <code>linestringfromwkb</code> (to convert WKB into
internal MySQL values). Exactly <em>why</em> <code>linestring</code> takes MySQL values and
returns a WKB value, I’m not sure, but it does. If you need to support WKT
inputs, then you’ll wind up calling conversion functions four or five times
per query.</p>
<p>Convincing Django to call this new function is another matter all together.
After a day and a half of trying to understand the GeoDjango back-ends, I gave
up (see <a href="http://groups.google.com/group/geodjango/browse_thread/thread/8f3e66b03c126a32">this thread on the GeoDjango mailing
list</a>
for a little more information) and just used the <code>extra()</code> method to add a
call to the function as a new column and <code>order by</code> it:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>pc <span class="op">=</span> Postcode.objects.get(postcode<span class="op">=</span><span class="st">'0200'</span>)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>sel <span class="op">=</span> SortedDict([(<span class="st">'distance'</span>, <span class="st">'distance(location, geomfromtext(</span><span class="sc">%s</span><span class="st">))'</span>)])</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>sel_p <span class="op">=</span> (pc.location.wkt,)</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>locations <span class="op">=</span> Locations.objects.extra(select<span class="op">=</span>sel, select_params<span class="op">=</span>sel_p, </span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    order_by<span class="op">=</span>[<span class="st">'distance'</span>])</span></code></pre></div>]]></summary>
</entry>

</feed>
