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;
```

### references

- read this guide, for more information.

## 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 EARTH_CIRCUMFERENCE=Math.PI * 2 * EARTH_RADIUS;
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;
}
```

### references

- Stokes theorem on some lecture notes hosted by MIT
- Open streetmaps zoom levels definition
- Google maps zoom level definition

## 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 < box.upperRight.lat) { state++; }
if (lat > box.lowerLeft.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;
}
```