Skip to content

Instantly share code, notes, and snippets.

@DrPaulBrewer
Last active December 12, 2020 17:18
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save DrPaulBrewer/4279e9d234a1bd6dd3c0 to your computer and use it in GitHub Desktop.
Save DrPaulBrewer/4279e9d234a1bd6dd3c0 to your computer and use it in GitHub Desktop.
find Maidenhead grid square from latitude and longitude
// HamGridSquare.js
// Copyright 2014 Paul Brewer KI6CQ
// License: MIT License http://opensource.org/licenses/MIT
//
// Javascript routines to convert from lat-lon to Maidenhead Grid Squares
// typically used in Ham Radio Satellite operations and VHF Contests
//
// Inspired in part by K6WRU Walter Underwood's python answer
// http://ham.stackexchange.com/a/244
// to this stack overflow question:
// How Can One Convert From Lat/Long to Grid Square
// http://ham.stackexchange.com/questions/221/how-can-one-convert-from-lat-long-to-grid-square
//
latLonToGridSquare = function(param1,param2){
var lat=-100.0;
var lon=0.0;
var adjLat,adjLon,GLat,GLon,nLat,nLon,gLat,gLon,rLat,rLon;
var U = 'ABCDEFGHIJKLMNOPQRSTUVWX'
var L = U.toLowerCase();
// support Chris Veness 2002-2012 LatLon library and
// other objects with lat/lon properties
// properties could be numbers, or strings
function toNum(x){
if (typeof(x) === 'number') return x;
if (typeof(x) === 'string') return parseFloat(x);
// dont call a function property here because of binding issue
throw "HamGridSquare -- toNum -- can not convert input: "+x;
}
if (typeof(param1)==='object'){
if (param1.length === 2){
lat = toNum(param1[0]);
lon = toNum(param1[1]);
} else if (('lat' in param1) && ('lon' in param1)){
lat = (typeof(param1.lat)==='function')? toNum(param1.lat()): toNum(param1.lat);
lon = (typeof(param1.lon)==='function')? toNum(param1.lon()): toNum(param1.lon);
} else if (('latitude' in param1) && ('longitude' in param1)){
lat = (typeof(param1.latitude)==='function')? toNum(param1.latitude()): toNum(param1.latitude);
lon = (typeof(param1.longitude)==='function')? toNum(param1.longitude()): toNum(param1.longitude);
} else {
throw "HamGridSquare -- can not convert object -- "+param1;
}
} else {
lat = toNum(param1);
lon = toNum(param2);
}
if (isNaN(lat)) throw "lat is NaN";
if (isNaN(lon)) throw "lon is NaN";
if (Math.abs(lat) === 90.0) throw "grid squares invalid at N/S poles";
if (Math.abs(lat) > 90) throw "invalid latitude: "+lat;
if (Math.abs(lon) > 180) throw "invalid longitude: "+lon;
adjLat = lat + 90;
adjLon = lon + 180;
GLat = U[Math.trunc(adjLat/10)];
GLon = U[Math.trunc(adjLon/20)];
nLat = ''+Math.trunc(adjLat % 10);
nLon = ''+Math.trunc((adjLon/2) % 10);
rLat = (adjLat - Math.trunc(adjLat)) * 60;
rLon = (adjLon - 2*Math.trunc(adjLon/2)) *60;
gLat = L[Math.trunc(rLat/2.5)];
gLon = L[Math.trunc(rLon/5)];
return GLon+GLat+nLon+nLat+gLon+gLat;
}
gridSquareToLatLon = function(grid, obj){
var returnLatLonConstructor = (typeof(LatLon)==='function');
var returnObj = (typeof(obj)==='object');
var lat=0.0,lon=0.0,aNum="a".charCodeAt(0),numA="A".charCodeAt(0);
function lat4(g){
return 10*(g.charCodeAt(1)-numA)+parseInt(g.charAt(3))-90;
}
function lon4(g){
return 20*(g.charCodeAt(0)-numA)+2*parseInt(g.charAt(2))-180;
}
if ((grid.length!=4) && (grid.length!=6)) throw "gridSquareToLatLon: grid must be 4 or 6 chars: "+grid;
if (/^[A-X][A-X][0-9][0-9]$/.test(grid)){
lat = lat4(grid)+0.5;
lon = lon4(grid)+1;
} else if (/^[A-X][A-X][0-9][0-9][a-x][a-x]$/.test(grid)){
lat = lat4(grid)+(1.0/60.0)*2.5*(grid.charCodeAt(5)-aNum+0.5);
lon = lon4(grid)+(1.0/60.0)*5*(grid.charCodeAt(4)-aNum+0.5);
} else throw "gridSquareToLatLon: invalid grid: "+grid;
if (returnLatLonConstructor) return new LatLon(lat,lon);
if (returnObj){
obj.lat = lat;
obj.lon = lon;
return obj;
}
return [lat,lon];
};
testGridSquare = function(){
// First four test examples are from "Conversion Between Geodetic and Grid Locator Systems",
// by Edmund T. Tyson N5JTY QST January 1989
// original test data in Python / citations by Walter Underwood K6WRU
// last test and coding into Javascript from Python by Paul Brewer KI6CQ
var testData = [
['Munich', [48.14666,11.60833], 'JN58td'],
['Montevideo', [[-34.91,-56.21166]], 'GF15vc'],
['Washington, DC', [{lat:38.92,lon:-77.065}], 'FM18lw'],
['Wellington', [{latitude:-41.28333,longitude:174.745}], 'RE78ir'],
['Newington, CT (W1AW)', [41.714775,-72.727260], 'FN31pr'],
['Palo Alto (K6WRU)', [[37.413708,-122.1073236]], 'CM87wj'],
['Chattanooga (KI6CQ/4)', [{lat:function(){ return "35.0542"; },
lon: function(){ return "-85.1142"}}], "EM75kb"]
];
var i=0,l=testData.length,result='',result2,result3,thisPassed=0,totalPassed=0;
for(i=0;i<l;++i){
result = latLonToGridSquare.apply({}, testData[i][1]);
result2 = gridSquareToLatLon(result);
result3 = latLonToGridSquare(result2);
thisPassed = (result===testData[i][2]) && (result3===testData[i][2]);
console.log("test "+i+": "+testData[i][0]+" "+JSON.stringify(testData[i][1])+
" result = "+result+" result2 = "+result2+" result3 = "+result3+" expected= "+testData[i][2]+
" passed = "+thisPassed);
totalPassed += thisPassed;
}
console.log(totalPassed+" of "+l+" test passed");
return totalPassed===l;
};
HamGridSquare = {
toLatLon: gridSquareToLatLon,
fromLatLon: latLonToGridSquare,
test: testGridSquare
};
@odpad
Copy link

odpad commented Aug 14, 2015

In function latLonToGridSquare I suggest simpler expression for gLat and gLon (without rLat, rLon):

current code:

rLat = (adjLat - Math.trunc(adjLat)) * 60;
rLon = (adjLon - 2*Math.trunc(adjLon/2)) *60;
gLat = L[Math.trunc(rLat/2.5)];
gLon = L[Math.trunc(rLon/5)];

new code (same functionality):

gLat = L[Math.trunc((adjLat % 1) * 24)];
gLon = L[Math.trunc((adjLon % 2) * 12)];

@kkaiser1952
Copy link

Can I talk you into expanding the output gridsquare by two more levels? i.e. EM29qe78PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment