Maidenhead Locator System calculator
The GNU Octave program below calculates the Maidenhead Locator from latitude and longitude. It describes the calculation in detail for educational purposes.
See also MaidenheadLocator.
Maidenhead_locator.m:
clear; clc;
latitude = 50.70578714046577;
longitude = 4.392066457029492;
precision = 6;
verbose = true;
[locator, steps] = maidenhead_from_latlon(latitude, longitude, precision, verbose);
fprintf("\nRESULT\n");
fprintf(" Maidenhead locator (%d chars): %s\n", precision, locator);
maidenhead_from_latlon.m
function [locator, steps] = maidenhead_from_latlon(latitude, longitude, precision, verbose)
if (nargin < 3 || isempty(precision)), precision = 6; end
if (nargin < 4 || isempty(verbose)), verbose = true; end
steps = struct();
% 0) Basic validation
if (!isscalar(latitude) || !isscalar(longitude))
error("latitude and longitude must be scalar numeric values.");
end
if (precision != 2 && precision != 4 && precision != 6 && precision != 8)
error("precision must be one of: 2, 4, 6, 8");
end
steps.input.latitude = latitude;
steps.input.longitude = longitude;
steps.precision = precision;
% 1) Normalize / clamp
lon_norm = mod(longitude + 180, 360) - 180;
lat_clamp = latitude;
if (lat_clamp >= 90), lat_clamp = 90 - 1e-12; end
if (lat_clamp < -90), lat_clamp = -90; end
steps.normalized.latitude = lat_clamp;
steps.normalized.longitude = lon_norm;
if (verbose)
fprintf("INPUT\n");
fprintf(" latitude = %.12f deg\n", latitude);
fprintf(" longitude = %.12f deg\n", longitude);
fprintf("\nNORMALIZE / CLAMP\n");
fprintf(" longitude normalized to [-180, 180): %.12f deg\n", lon_norm);
fprintf(" latitude clamped to [-90, 90): %.12f deg\n", lat_clamp);
end
% 2) False easting/northing
lon_adj = lon_norm + 180; % [0, 360)
lat_adj = lat_clamp + 90; % [0, 180)
steps.adjusted.lon_adj = lon_adj;
steps.adjusted.lat_adj = lat_adj;
if (verbose)
fprintf("\nFALSE EASTING / NORTHING\n");
fprintf(" lon_adj = lon_norm + 180 = %.12f deg (range [0,360))\n", lon_adj);
fprintf(" lat_adj = lat_clamp + 90 = %.12f deg (range [0,180))\n", lat_adj);
end
letters18 = "ABCDEFGHIJKLMNOPQR"; % 18 letters (A..R)
letters24 = "ABCDEFGHIJKLMNOPQRSTUVWX"; % 24 letters (A..X)
locator = "";
% 3) Field (base 18)
field_lon = floor(lon_adj / 20); % 0..17
field_lat = floor(lat_adj / 10); % 0..17
steps.field.field_lon = field_lon;
steps.field.field_lat = field_lat;
locator(1) = letters18(field_lon + 1);
locator(2) = letters18(field_lat + 1);
lon_rem_field = lon_adj - field_lon * 20;
lat_rem_field = lat_adj - field_lat * 10;
steps.field.lon_rem_field = lon_rem_field;
steps.field.lat_rem_field = lat_rem_field;
if (verbose)
fprintf("\nFIELD (pair 1, letters A..R)\n");
fprintf(" field_lon = floor(lon_adj / 20) = %d -> '%c'\n", field_lon, locator(1));
fprintf(" field_lat = floor(lat_adj / 10) = %d -> '%c'\n", field_lat, locator(2));
fprintf(" lon remainder inside field: %.12f deg\n", lon_rem_field);
fprintf(" lat remainder inside field: %.12f deg\n", lat_rem_field);
end
if (precision == 2), return; end
% 4) Square (base 10 digits)
square_lon = floor(lon_rem_field / 2); % 0..9
square_lat = floor(lat_rem_field / 1); % 0..9
steps.square.square_lon = square_lon;
steps.square.square_lat = square_lat;
locator(3) = char('0' + square_lon);
locator(4) = char('0' + square_lat);
lon_rem_square = lon_rem_field - square_lon * 2;
lat_rem_square = lat_rem_field - square_lat * 1;
steps.square.lon_rem_square = lon_rem_square;
steps.square.lat_rem_square = lat_rem_square;
if (verbose)
fprintf("\nSQUARE (pair 2, digits 0..9)\n");
fprintf(" square_lon = floor(lon_rem_field / 2) = %d -> '%c'\n", square_lon, locator(3));
fprintf(" square_lat = floor(lat_rem_field / 1) = %d -> '%c'\n", square_lat, locator(4));
fprintf(" lon remainder inside square: %.12f deg\n", lon_rem_square);
fprintf(" lat remainder inside square: %.12f deg\n", lat_rem_square);
end
if (precision == 4), return; end
% 5) Subsquare (base 24 letters)
lon_sub_width = 2 / 24;
lat_sub_height = 1 / 24;
subs_lon = floor(lon_rem_square / lon_sub_width); % 0..23
subs_lat = floor(lat_rem_square / lat_sub_height); % 0..23
steps.subsquare.subs_lon = subs_lon;
steps.subsquare.subs_lat = subs_lat;
locator(5) = letters24(subs_lon + 1);
locator(6) = letters24(subs_lat + 1);
lon_rem_sub = lon_rem_square - subs_lon * lon_sub_width;
lat_rem_sub = lat_rem_square - subs_lat * lat_sub_height;
steps.subsquare.lon_sub_width_deg = lon_sub_width;
steps.subsquare.lat_sub_height_deg = lat_sub_height;
steps.subsquare.lon_rem_sub = lon_rem_sub;
steps.subsquare.lat_rem_sub = lat_rem_sub;
if (verbose)
fprintf("\nSUBSQUARE (pair 3, letters A..X)\n");
fprintf(" lon_sub_width = 2/24 = %.12f deg (= 5 arc-min)\n", lon_sub_width);
fprintf(" lat_sub_height = 1/24 = %.12f deg (= 2.5 arc-min)\n", lat_sub_height);
fprintf(" subs_lon = floor(lon_rem_square / lon_sub_width) = %d -> '%c'\n", subs_lon, locator(5));
fprintf(" subs_lat = floor(lat_rem_square / lat_sub_height) = %d -> '%c'\n", subs_lat, locator(6));
fprintf(" lon remainder inside subsquare: %.12f deg\n", lon_rem_sub);
fprintf(" lat remainder inside subsquare: %.12f deg\n", lat_rem_sub);
end
if (precision == 6), return; end
% 6) Extended square (base 10 digits)
lon_ext_width = lon_sub_width / 10;
lat_ext_height = lat_sub_height / 10;
ext_lon = floor(lon_rem_sub / lon_ext_width); % 0..9
ext_lat = floor(lat_rem_sub / lat_ext_height); % 0..9
steps.extended.ext_lon = ext_lon;
steps.extended.ext_lat = ext_lat;
locator(7) = char('0' + ext_lon);
locator(8) = char('0' + ext_lat);
lon_rem_ext = lon_rem_sub - ext_lon * lon_ext_width;
lat_rem_ext = lat_rem_sub - ext_lat * lat_ext_height;
steps.extended.lon_ext_width_deg = lon_ext_width;
steps.extended.lat_ext_height_deg = lat_ext_height;
steps.extended.lon_rem_ext = lon_rem_ext;
steps.extended.lat_rem_ext = lat_rem_ext;
if (verbose)
fprintf("\nEXTENDED SQUARE (pair 4, digits 0..9)\n");
fprintf(" lon_ext_width = (2/24)/10 = %.12f deg (= 30 arc-sec)\n", lon_ext_width);
fprintf(" lat_ext_height = (1/24)/10 = %.12f deg (= 15 arc-sec)\n", lat_ext_height);
fprintf(" ext_lon = floor(lon_rem_sub / lon_ext_width) = %d -> '%c'\n", ext_lon, locator(7));
fprintf(" ext_lat = floor(lat_rem_sub / lat_ext_height) = %d -> '%c'\n", ext_lat, locator(8));
fprintf(" lon remainder inside extended square: %.12f deg\n", lon_rem_ext);
fprintf(" lat remainder inside extended square: %.12f deg\n", lat_rem_ext);
end
end
output:
INPUT latitude = 50.705787140466 deg longitude = 4.392066457029 deg NORMALIZE / CLAMP longitude normalized to [-180, 180): 4.392066457030 deg latitude clamped to [-90, 90): 50.705787140466 deg FALSE EASTING / NORTHING lon_adj = lon_norm + 180 = 184.392066457030 deg (range [0,360)) lat_adj = lat_clamp + 90 = 140.705787140466 deg (range [0,180)) FIELD (pair 1, letters A..R) field_lon = floor(lon_adj / 20) = 9 -> 'J' field_lat = floor(lat_adj / 10) = 14 -> 'O' lon remainder inside field: 4.392066457030 deg lat remainder inside field: 0.705787140466 deg SQUARE (pair 2, digits 0..9) square_lon = floor(lon_rem_field / 2) = 2 -> '2' square_lat = floor(lat_rem_field / 1) = 0 -> '0' lon remainder inside square: 0.392066457030 deg lat remainder inside square: 0.705787140466 deg SUBSQUARE (pair 3, letters A..X) lon_sub_width = 2/24 = 0.083333333333 deg (= 5 arc-min) lat_sub_height = 1/24 = 0.041666666667 deg (= 2.5 arc-min) subs_lon = floor(lon_rem_square / lon_sub_width) = 4 -> 'E' subs_lat = floor(lat_rem_square / lat_sub_height) = 16 -> 'Q' lon remainder inside subsquare: 0.058733123696 deg lat remainder inside subsquare: 0.039120473799 deg RESULT Maidenhead locator (6 chars): JO20EQ