Tag Archives: tips

Distance? Math? What?

A few days ago, I became painfully aware that the distance equation we all learn in geometry class, while it works perfectly well on flat, local projections,  is woefully inadequate when we’re talking about global coordinate systems.

In the midst of implementing Google Maps in our up-coming Sajara sample application, I realized that the International Date Line was causing problems. Large problems. Whenever the IDL was in play, I never retrieved the correct results from our database. One large problem was how we defined our bounding box inside Sajara itself. Once that was fixed, however, I realized that something was still wrong. The Nearest-to-Farthest sort was not behaving quite right: it was really doing a Nearest-on-this-side-of-the-IDL-first sort! The IDL was acting like a wall for our distance sort.

distance

Example of linear distance with IDL in play. X = center of map

Here’s what we used before (in sql-speak):

RETURN sqrt(power(@FromX - @ToX,2) + power(@FromY - @ToY,2))

Your standard square-root of difference-squared plus difference-squared. This formula is fast and worked perfectly well so long as I operated under these two criteria:

1. a FLAT projection
2. POSITIVE numbers only

Once either of those things aren’t true anymore, I can’t use the basic distance formula. In my case, both things stopped being true at the same time! The solution? Something fairly math-magical to my mind, but accepted as THE formula to get at the distance between any two global coordinates: the Haversine Formula. Delving into descriptions of the formula brings up concepts such as spherical triangles, the planet’s elipticity, great circles and other things very important to navigation. It’s accurate down to about a meter, which is plenty for anything I’ll be doing with it.

Haversine Formula in Javascript:

var R = 6371; // radius of the earth
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
        Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
        Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distance = R * c;

So here is the resulting SQL function that gives me the right distance between any two global coordinates.. basically a direct translation of the above javascript into T-SQL:

CREATE FUNCTION [dbo].[distance] (@FromX float, @FromY float, @ToX float, @ToY float)
RETURNS float AS BEGIN
DECLARE @R AS FLOAT;
SET @R = 6371;
DECLARE @DLAT AS FLOAT;
DECLARE @DLON AS FLOAT;
DECLARE @A AS FLOAT;
DECLARE @C AS FLOAT;
DECLARE @D AS FLOAT;
SET @DLAT = RADIANS(@ToY - @FromY);
SET @DLON = RADIANS(@ToX - @FromX);
SET @A =
SIN(@DLAT/2) * SIN(@DLAT/2) +
COS(RADIANS(@FromY)) * COS(RADIANS(@ToY)) * SIN(@DLON/2) * SIN(@DLON/2);
SET @C = 2 * ATN2(SQRT(@A), SQRT(1-@A));
SET @D = @R * @C;
RETURN @D
END


Tricks & Woes w/ WMS GetFeatureInfo

Here’s a trick that could be useful if you’re writing a simple map application in JavaScript that interacts with a WMS map service that you control, and your application needs information about a feature that the user has clicked on.   Did you know that you can cajole your WMS server to directly produce JSON (which is a very convenient way to receive data in a JS application) with feature information as a response to a WMS request?  I found this trick useful in a recent application as a simple approach, if one doesn’t mind wrangling with server configuration.  Unfortunately, it involves using an under-specified part of the WMS standard, so this recipe is also a good illustration of where the OGC WMS standard could be improved.

In the WMS specification, OGC defined an “optional” GetFeatureInfo request that returns feature information based on a pixel from a map.  It’s both useful and limiting that the request is based on the pixel — it’s useful in that you can just pass along the pixel that the user clicked on, but you don’t have a lot of control (without other trickery I won’t go into here).  But here’s the catch: the output format isn’t defined in the standard.  But here the problem becomes the opportunity — most WMS servers allow you can define your own output format, which includes JSON.

But again the problem returns to the fact that there isn’t a standard in place here:  different mapservers handle the output format definition differently.  MapServer has a very straightforward method — it will respond to a “text/html” request with three templates you can define: you can define a header template, a content template (that is repeated for each feature), and a footer template.  It will replace any attribute name surrounded with brackets with the value of the attribute.  So if you create a header.html file with a single left bracket (‘[') and a footer.html with a single right bracket (']‘) and then a template.html file that is something like:

{
id: “[id]“,
attribute: “[attribute]“
}

you’ll get responses to your GetFeatureInfo request in proper JSON, e.g.:

“[ { id: "123", attribute: "foo" }, { id: "456", attribute: "bar" } ]“

GeoServer has a different structure for its GetFeatureInfo templates, which is described here but the same basic approach works, with the major difference being that you create a single template with header and footer sections.  I’m not sure how it’s done with ArcGIS Server, but with ArcIMS there are XSL files for each output format, and you could create one that generates JSON without too much trouble if you are familiar with XSL.  If someone figures out how to do this with ArcGIS Server, I’ve love to know.

Of course, what would really be fantastic would be if the OGC standard was extended to include a standard output format — I won’t even dare to dream for a standard way to define the output format.