This article will accumulate content over time. It occurs to me that tidying up my toolbox would make life simpler. This is part of it. I don't think the maths in this is very hard; but pls don't ask me to explain it (which is very dull). This article is heavy on references, as its not original data; I am implementing the internationally agreed specs.

I use one of three map data systems, and three map rendering systems; depending on requirements. You may need an “address understander”, or a map display, or a route plotter. These are different objectives. GIS is mostly attention to detail, but historically it wasn't considered a noticeable business objective. As a consumer this annoys me.

Notes on map projections

This is probably something you have never needed to think about, so here is a comic, which is well researched, many thanks Randall Munroe. At some point, I think the UN will need to move away from the Mercator system, its designed by Europeans for Europeans.

Address lookup/ autocomplete

A.K.A. working out what users meant.
There are fairly simple API available from Bing, Google, Here maps and other companies.
As a European it is necessary to state country=UK (or FR etc) on each request, or it assumes you are looking for random villages in the US, where the locals reused the name of UK cities.

Distance between two points on a sphere

Technically this isn't precise, as its not compensating for Earths ellipsoid-ness. It is accurate enough for my purposes (i.e. maps on small screens)

// given lat1, lat2, and long1, long2  

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


Logarithmic map zoom levels to diameter

I don't think this zoom scale is actually Google IP, but a cartographer invention. The scale is shared between all map systems that I have used.

Use this to set the correct zoom to draw your data.

 *  compute_zoom_from_renderSize
 *  @param lat ~ must be in degrees ~ how far North
 *  @param pixels ~ how big your output device is.  The bigger the device, the less to need to compress the data 
 *  @returns int, the logarithmic zoom value
function compute_zoom_from_renderSize(lat, pixels) {
	var EARTH_RADIUS=6372798.2;
	var MYSTIC_OFFSET=8; // I don't know why 8

// source equation, progressively mapped to a more useful form
	//	thus S=C*cos(y)/2^(z+8)
	// var pixels = EARTH_CIRCUMFERENCE * Math.cos(lat)/math.pow( 2, (out +MYSTIC_OFFSET ))
	//	pixels * math.pow( 2, (out +8 )) =  EARTH_CIRCUMFERENCE * Math.cos(lat)
	//	math.pow( 2, (out +8 )) =  EARTH_CIRCUMFERENCE * Math.cos(lat) / pixels
	return Math.log2( EARTH_CIRCUMFERENCE * Math.cos(lat) / pixels ) -MYSTIC_OFFSET;


Range clipping co-ords

If your data is known to be inside a geographical area, a quick test during development can increase your development velocity e.g. UK data should never have co-ords less than 40deg North. As not all co-ord systems put the co-ords items in the same order, this check is really useful.

 * test_bounds ~ determines if your point is inside a box.
 * Doesn't do surface projection so don't use on boxes larger than around 10deg
 * NB: This does '>' not '>=', some business people may want you to change that.
 * @param long ~ float
 * @param lat ~ float
 * @param box ~ a struct of "upperRight" and "lowerleft", each with "long" and "lat" 
 * These are named items regardless of performance, as errors in your test data code invalidate the test
 * @return bool
function test_bounds(long, lat, box) {
	var state=0;
	if (long < box.upperRight.long) { state++; }
	if (long > box.lowerLeft.long) { state++; }
	if (lat < { state++; }
	if (lat > { state++; }
	return state==4;

 * rectify_inversion ~ if you think your data may be inverted, this will correct it
 * If your point is outside of a target box it will return null, otherwise the corrected data.
 * @param long
 * @param lat
 * @param box ~ a struct of "upperRight" and "lowerleft", each with "long" and "lat" 
 * These are named items regardless of performance, as errors in your test data code invalidate the test
 * @return data OR null
function rectify_inversion(long, lat, box) {
	if(test_bounds(long, lat, box)) {
		return {long:long, lat:lat};
	if(test_bounds(lat, long, box)) {
		return {long:lat, lat:long};
	return null;