
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://zoophilia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AOrdnance_Survey_coordinates</id>
	<title>Module:Ordnance Survey coordinates - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://zoophilia.wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AOrdnance_Survey_coordinates"/>
	<link rel="alternate" type="text/html" href="https://zoophilia.wiki/index.php?title=Module:Ordnance_Survey_coordinates&amp;action=history"/>
	<updated>2026-05-10T16:41:30Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://zoophilia.wiki/index.php?title=Module:Ordnance_Survey_coordinates&amp;diff=134100&amp;oldid=prev</id>
		<title>SockyPaws: Import needed module from English Wikipedia</title>
		<link rel="alternate" type="text/html" href="https://zoophilia.wiki/index.php?title=Module:Ordnance_Survey_coordinates&amp;diff=134100&amp;oldid=prev"/>
		<updated>2026-01-01T00:23:34Z</updated>

		<summary type="html">&lt;p&gt;Import needed module from English Wikipedia&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--[[ Ordnance Survey to Latitude/Longitude functions in Lua&lt;br /&gt;
  Ported to Lua from PHP by User:Hike395, English Wikipedia, 18 Aug 2019&lt;br /&gt;
&lt;br /&gt;
  Found by RWH at http://www.megalithia.com/search/llfuncshighlight.php&lt;br /&gt;
&lt;br /&gt;
  With thanks to Andy, G4JNT for inspiration in GEOG, and to the OSGB&lt;br /&gt;
  for their white paper on coordinate transformation describing the&lt;br /&gt;
  iterative method used. Thanks to the Ordnance Survey of Ireland for&lt;br /&gt;
  details of the true and false origins of the Irish grid.&lt;br /&gt;
&lt;br /&gt;
  You may use and redistribute this code under the terms of the GPL, see&lt;br /&gt;
  &amp;lt;https://www.gnu.org/copyleft/gpl.html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  Written by Richard (&amp;lt;www.megalithia.com&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
  * v 0.something 27/2/2000&lt;br /&gt;
  * v 1.01        28/6/2004&lt;br /&gt;
  * v 1.02         6/8/2004 (line 89 add &amp;quot;0&amp;quot; to chars in ngr=stripcharsnotinbag Thx Andy)&lt;br /&gt;
  * v 1.03         9/3/2005 (Jan Klingon added conversion to WGS84 map datum and removed limitation of digits of the grid ref)&lt;br /&gt;
  * v 1.04        10/8/2005 (Richard correct error trapping (only manifest on malformed ngrs)&lt;br /&gt;
&lt;br /&gt;
  This code is predicated on the assumption that your are ONLY feeding&lt;br /&gt;
  it Irish or UK Grid references. It uses the single char prefix of Irish&lt;br /&gt;
  grid refs to tell the difference, UK grid refs have a two letter prefix.&lt;br /&gt;
  We would like an even number of digits for the rest of the grid ref.&lt;br /&gt;
  Anything in the NGR other than 0-9, A-Z, a-z is eliminated.&lt;br /&gt;
  WARNING: This assumes there are no decimal points in your NGR components.&lt;br /&gt;
&lt;br /&gt;
  The transformation from OSGB36/Ireland 1965 to WGS84 is more precise than&lt;br /&gt;
  5 m. The function is case-insensitive&lt;br /&gt;
&lt;br /&gt;
  Lat/Long to Ordnance Survey conversion is at bottom of file, see&lt;br /&gt;
  further authorship there.&lt;br /&gt;
]]&lt;br /&gt;
local oscoord = {}&lt;br /&gt;
local getArgs = require(&amp;quot;Module:Arguments&amp;quot;).getArgs&lt;br /&gt;
local yesno = require(&amp;quot;Module:Yesno&amp;quot;)&lt;br /&gt;
local namespace = mw.title.getCurrentTitle().namespace&lt;br /&gt;
&lt;br /&gt;
local pow = math.pow&lt;br /&gt;
local sqrt = math.sqrt&lt;br /&gt;
local abs = math.abs&lt;br /&gt;
local floor = math.floor&lt;br /&gt;
local ceil = math.ceil&lt;br /&gt;
local sin = math.sin&lt;br /&gt;
local cos = math.cos&lt;br /&gt;
local tan = math.tan&lt;br /&gt;
local atan = math.atan2&lt;br /&gt;
local deg2rad = math.rad&lt;br /&gt;
local rad2deg = math.deg&lt;br /&gt;
&lt;br /&gt;
--[[ DATUM SHIFT USING HELMERT TRANSFORMATION&lt;br /&gt;
 * Height above the ellipsoid is ignored&lt;br /&gt;
 * Latitude and longitude must be given in decimal degrees&lt;br /&gt;
 * No transformation if abs(latitude)&amp;gt;89 or abs(longitude)&amp;gt;89&lt;br /&gt;
 * (lifting this restriction requires some more lines of code)&lt;br /&gt;
&lt;br /&gt;
 * Written in 2009 by User:Telford at German Wikipedia.&lt;br /&gt;
 * Based on math described in &amp;quot;A Guide to Coordinate Systems in Great Britain&amp;quot;&lt;br /&gt;
 * by the UK Ordnance Survey.&lt;br /&gt;
 * URL: &amp;lt;https://www.ordnancesurvey.co.uk/documents/resources/guide-coordinate-systems-great-britain.pdf&amp;gt;&lt;br /&gt;
]]&lt;br /&gt;
-- Datum parameters&lt;br /&gt;
local data = mw.loadData(&amp;quot;Module:Ordnance Survey coordinates/data&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local OSGBglobe = data.OSGBglobe&lt;br /&gt;
local IEglobe = data.IEglobe&lt;br /&gt;
local WGSglobe = data.WGSglobe&lt;br /&gt;
local WGS2OSGBparam = data.WGS2OSGBparam&lt;br /&gt;
local OSGB2WGSparam = data.OSGB2WGSparam&lt;br /&gt;
local IE2WGSparam = data.IE2WGSparam&lt;br /&gt;
&lt;br /&gt;
local function HelmertDatumShift(latitude, longitude, param)&lt;br /&gt;
    -- Latitude and longitude are in degrees.&lt;br /&gt;
    param = param or WGS2OSGBparam&lt;br /&gt;
&lt;br /&gt;
    -- Parameters for ellipsoids; Annex A.1, Ordnance Survey document&lt;br /&gt;
    local a1 = param.semimajor_src&lt;br /&gt;
    local b1 = param.semiminor_src&lt;br /&gt;
    local e1 = param.ecc_src&lt;br /&gt;
&lt;br /&gt;
    local a2 = param.semimajor_dst&lt;br /&gt;
    local b2 = param.semiminor_dst&lt;br /&gt;
    local e2 = param.ecc_dst&lt;br /&gt;
&lt;br /&gt;
    -- Convert latitude and longitude to Cartesian coordinates;&lt;br /&gt;
    -- math in Annex B.1, Ordnance Survey document.&lt;br /&gt;
    local phi = deg2rad(latitude)&lt;br /&gt;
    local lambda = deg2rad(longitude)&lt;br /&gt;
    local cosphi = cos(phi)&lt;br /&gt;
    local sinphi = sin(phi)&lt;br /&gt;
    local coslambda = cos(lambda)&lt;br /&gt;
    local sinlambda = sin(lambda)&lt;br /&gt;
&lt;br /&gt;
    local ny = a1 / sqrt(1. - e1 * sinphi * sinphi)&lt;br /&gt;
&lt;br /&gt;
    local x1 = ny * cosphi * coslambda&lt;br /&gt;
    local y1 = ny * cosphi * sinlambda&lt;br /&gt;
    local z1 = ny * (1. - e1) * sinphi&lt;br /&gt;
    -- The helmert transformation proper,&lt;br /&gt;
    -- Equation 3, Section 6.2, Ordnance Survey document&lt;br /&gt;
    local x2 = x1 + param.tx + (param.s0 * x1 - param.rz * y1 + param.ry * z1)&lt;br /&gt;
    local y2 = y1 + param.ty + (param.rz * x1 + param.s0 * y1 - param.rx * z1)&lt;br /&gt;
    local z2 = z1 + param.tz + (-param.ry * x1 + param.rx * y1 + param.s0 * z1)&lt;br /&gt;
    -- Convert Cartesian coordinates to latitude and longitude;&lt;br /&gt;
    -- math in Annex B.2, of Ordnance Survey document.&lt;br /&gt;
    lambda = atan(y2, x2)&lt;br /&gt;
&lt;br /&gt;
    local p2 = sqrt(x2 * x2 + y2 * y2)&lt;br /&gt;
    phi = atan(z2, p2 * (1. - e2))&lt;br /&gt;
    local n0 = 101&lt;br /&gt;
&lt;br /&gt;
    local phi_alt&lt;br /&gt;
    repeat&lt;br /&gt;
        phi_alt = phi&lt;br /&gt;
        ny = a2 / sqrt(1. - e2 * sin(phi) * sin(phi))&lt;br /&gt;
        phi = atan(z2 + e2 * ny * sin(phi), p2)&lt;br /&gt;
        n0 = n0 - 1&lt;br /&gt;
    until abs(phi - phi_alt) &amp;lt;= 0.000000000001 or n0 &amp;lt;= 0&lt;br /&gt;
&lt;br /&gt;
    return {lat = rad2deg(phi), lon = rad2deg(lambda)}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- LAT/LONG TO OS GRID LIBRARY RESUMES HERE.&lt;br /&gt;
local function northeast(lett, num, shift)&lt;br /&gt;
    -- Split into northings and eastings.&lt;br /&gt;
    local le = mw.ustring.len(num)&lt;br /&gt;
    if le % 2 == 1 then&lt;br /&gt;
        return {err = &amp;quot;Malformed numerical part of NGR&amp;quot;}&lt;br /&gt;
    end&lt;br /&gt;
    local pr = le / 2&lt;br /&gt;
    local n = mw.ustring.sub(num, pr + 1)&lt;br /&gt;
    local e = mw.ustring.sub(num, 1, pr)&lt;br /&gt;
    -- Hack to move to center of square: append a 5 to northings and eastings.&lt;br /&gt;
    shift = yesno(shift)&lt;br /&gt;
    if shift then&lt;br /&gt;
        n = n .. &amp;quot;5&amp;quot;&lt;br /&gt;
        e = e .. &amp;quot;5&amp;quot;&lt;br /&gt;
        pr = pr + 1&lt;br /&gt;
    end&lt;br /&gt;
    -- End hack.&lt;br /&gt;
    n = n == &amp;quot;&amp;quot; and 0 or n&lt;br /&gt;
    e = e == &amp;quot;&amp;quot; and 0 or e&lt;br /&gt;
    pr = pow(10.0, (5.0 - pr))&lt;br /&gt;
    local T1 = mw.ustring.byte(mw.ustring.sub(lett, 1, 1)) - 65&lt;br /&gt;
    if T1 &amp;gt; 8 then&lt;br /&gt;
        T1 = T1 - 1&lt;br /&gt;
    end&lt;br /&gt;
    local T2 = nil&lt;br /&gt;
    if mw.ustring.len(lett) &amp;gt; 1 then&lt;br /&gt;
        T2 = mw.ustring.byte(mw.ustring.sub(lett, 2)) - 65&lt;br /&gt;
        if T2 &amp;gt; 8 then&lt;br /&gt;
            T2 = T2 - 1&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return {n = n, e = e, pr = pr, T1 = T1, T2 = T2}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function EN2LL(e, n, datum)&lt;br /&gt;
    local a = datum.semimajor * datum.scale&lt;br /&gt;
    local b = datum.semiminor * datum.scale&lt;br /&gt;
    local lat0rad = deg2rad(datum.lat0)&lt;br /&gt;
    local n1 = datum.n1&lt;br /&gt;
    local n12 = n1 * n1&lt;br /&gt;
    local n13 = n12 * n1&lt;br /&gt;
    local k = (n - datum.n0) / a + lat0rad&lt;br /&gt;
    local nextcounter = 0&lt;br /&gt;
    local j3, j4, j5, j6, m&lt;br /&gt;
    repeat&lt;br /&gt;
        nextcounter = nextcounter + 1&lt;br /&gt;
        local k3 = k - lat0rad&lt;br /&gt;
        local k4 = k + lat0rad&lt;br /&gt;
        j3 = (1.0 + n1 + 1.25 * n12 + 1.25 * n13) * k3&lt;br /&gt;
        j4 = (3.0 * n1 + 3.0 * n12 + 2.625 * n13) * sin(k3) * cos(k4)&lt;br /&gt;
        j5 = (1.875 * n12 + 1.875 * n13) * sin(2.0 * k3) * cos(2.0 * k4)&lt;br /&gt;
        j6 = 35.0 / 24.0 * n13 * sin(3.0 * k3) * cos(3.0 * k4)&lt;br /&gt;
        m = b * (j3 - j4 + j5 - j6)&lt;br /&gt;
        k = k + (n - datum.n0 - m) / a&lt;br /&gt;
    until abs(n - datum.n0 - m) &amp;lt;= 0.000000001 or nextcounter &amp;gt;= 100&lt;br /&gt;
    local x = 1.0 - datum.ecc * pow(sin(k), 2.0)&lt;br /&gt;
    local v = a / sqrt(x)&lt;br /&gt;
    local r = v * (1.0 - datum.ecc) / x&lt;br /&gt;
    local h2 = v / r - 1.0&lt;br /&gt;
    local y1 = e - datum.e0&lt;br /&gt;
    local tank = tan(k)&lt;br /&gt;
    local tank2 = tank * tank&lt;br /&gt;
    local tank4 = tank2 * tank2&lt;br /&gt;
    local tank6 = tank2 * tank4&lt;br /&gt;
    local cosk = cos(k)&lt;br /&gt;
    local yv = y1 / v -- dimensionless quantity in series expansion&lt;br /&gt;
    local yv2 = yv * yv&lt;br /&gt;
    local yv3 = yv * yv2&lt;br /&gt;
    local yv5 = yv3 * yv2&lt;br /&gt;
    local yv7 = yv5 * yv2&lt;br /&gt;
    j3 = tank / (2.0 * r)&lt;br /&gt;
    j4 = tank / (24.0 * r) * (5.0 + 3.0 * tank2 + h2 - 9.0 * tank2 * h2)&lt;br /&gt;
    j5 = tank / (720.0 * r) * (61.0 + 90.0 * tank2 + 45.0 * tank4)&lt;br /&gt;
    local k9 = k - y1 * (yv * j3 + yv3 * j4 - yv5 * j5)&lt;br /&gt;
    j6 = 1.0 / (cosk)&lt;br /&gt;
    local j7 = 1.0 / (cosk * 6.0) * (v / r + 2.0 * tank2)&lt;br /&gt;
    local j8 = 1.0 / (cosk * 120.0) * (5.0 + 28.0 * tank2 + 24.0 * tank4)&lt;br /&gt;
    local j9 = 1.0 / (cosk * 5040.0)&lt;br /&gt;
    j9 = j9 * (61.0 + 662.0 * tank2 + 1320.0 * tank4 + 720.0 * tank6)&lt;br /&gt;
    local long = datum.lon0 + rad2deg(yv * j6 - yv3 * j7 + yv5 * j8 - yv7 * j9)&lt;br /&gt;
    local lat = rad2deg(k9)&lt;br /&gt;
    return {lat = lat, lon = long}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function GBEN2LL(e, n)&lt;br /&gt;
    local latlong = EN2LL(e, n, OSGBglobe)&lt;br /&gt;
    local helmert = HelmertDatumShift(latlong.lat, latlong.lon, OSGB2WGSparam)&lt;br /&gt;
    return {region = &amp;quot;GB&amp;quot;, lat = helmert.lat, long = helmert.lon}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function GB2LL(lett, num)&lt;br /&gt;
    -- British OS to Lat+Long&lt;br /&gt;
    -- first caclulate e,n&lt;br /&gt;
    -- computing e and n exactly, to get SW corner of box&lt;br /&gt;
    local ne = northeast(lett, num)&lt;br /&gt;
    if ne.err then&lt;br /&gt;
        return {region = &amp;quot;GB&amp;quot;, err = ne.err}&lt;br /&gt;
    end&lt;br /&gt;
    -- use British definition of e and n&lt;br /&gt;
    local e = 500000.0 * (ne.T1 % 5) + 100000.0 * (ne.T2 % 5) - 1000000.0 + ne.e * ne.pr&lt;br /&gt;
    local n = 1900000.0 - 500000.0 * floor(ne.T1 / 5) - 100000.0 * floor(ne.T2 / 5) + ne.n * ne.pr&lt;br /&gt;
    local result = GBEN2LL(e, n)&lt;br /&gt;
    result.prec = 0.8165 * ne.pr&lt;br /&gt;
    return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function IrishEN2LL(e, n)&lt;br /&gt;
    local latlong = EN2LL(e, n, IEglobe)&lt;br /&gt;
    local helmert = HelmertDatumShift(latlong.lat, latlong.lon, IE2WGSparam)&lt;br /&gt;
    return {region = &amp;quot;IE&amp;quot;, lat = helmert.lat, long = helmert.lon}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function Irish2LL(lett, num)&lt;br /&gt;
    -- Irish OS to Lat+Long&lt;br /&gt;
    -- first caclulate e,n&lt;br /&gt;
    -- computing e and n exactly, to get SW corner of box&lt;br /&gt;
    local ne = northeast(lett, num)&lt;br /&gt;
    if ne.err then&lt;br /&gt;
        return {region = &amp;quot;IE&amp;quot;, err = ne.err}&lt;br /&gt;
    end&lt;br /&gt;
    -- use Irish definition of northing and easting&lt;br /&gt;
    local e = 100000.0 * (ne.T1 % 5.0) + ne.e * ne.pr&lt;br /&gt;
    local n = ne.n * ne.pr + 100000.0 * (4.0 - floor(ne.T1 / 5.0))&lt;br /&gt;
    local result = IrishEN2LL(e, n)&lt;br /&gt;
    result.prec = 0.8165 * ne.pr -- useful @ Commons&lt;br /&gt;
    return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function empty(s)&lt;br /&gt;
    return not s or s == &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function NGR2LL(ngr)&lt;br /&gt;
    local result = {}&lt;br /&gt;
    local _ = 0&lt;br /&gt;
    ngr, _ = mw.ustring.gsub(mw.ustring.upper(ngr), &amp;quot;[%s%p]&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    local first, last, lett, num = mw.ustring.find(ngr, &amp;quot;^([A-Z]+)(%d+)$&amp;quot;)&lt;br /&gt;
    if not first or empty(lett) or empty(num) or mw.ustring.len(lett) &amp;gt; 2 then&lt;br /&gt;
        return {err = &amp;quot;Malformed NGR&amp;quot;}&lt;br /&gt;
    end&lt;br /&gt;
    if mw.ustring.len(lett) == 1 then&lt;br /&gt;
        return Irish2LL(lett, num)&lt;br /&gt;
    end&lt;br /&gt;
    return GB2LL(lett, num)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function split(s, sep)&lt;br /&gt;
    -- split a string s into chunks, separated by sep&lt;br /&gt;
    sep = sep or &amp;quot;%s&amp;quot;&lt;br /&gt;
    local t = {}&lt;br /&gt;
    for chunk in mw.ustring.gmatch(s, &amp;quot;([^&amp;quot; .. sep .. &amp;quot;]+)&amp;quot;) do&lt;br /&gt;
        table.insert(t, chunk)&lt;br /&gt;
    end&lt;br /&gt;
    return t&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function trim(s)&lt;br /&gt;
    local _ = 0&lt;br /&gt;
    s, _ = mw.ustring.gsub(s, &amp;quot;^%s+&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    s, _ = mw.ustring.gsub(s, &amp;quot;%s+$&amp;quot;, &amp;quot;&amp;quot;)&lt;br /&gt;
    return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function alldigits(s)&lt;br /&gt;
    return not mw.ustring.find(s, &amp;quot;%D&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function warning(errmsg)&lt;br /&gt;
    local preview = require(&amp;quot;Module:If preview&amp;quot;)&lt;br /&gt;
    local msg = errmsg or &amp;quot;Empty OS grid ref&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    local html = preview._warning({msg})&lt;br /&gt;
&lt;br /&gt;
    if namespace == 0 and errmsg then&lt;br /&gt;
        html = html .. &amp;quot;[[Category:Pages with malformed OS coordinates]]&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    return html&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- generate URL of geohack map&lt;br /&gt;
local function geohack(main_args, other_args, LL)&lt;br /&gt;
    -- create geohack link. Example:&lt;br /&gt;
    -- https://geohack.toolforge.org/geohack.php?pagename=Mount_Whitney&amp;amp;params=36.578580925_N_118.29199495_W_type:mountain_region:US-CA_scale:100000_source:NGS&lt;br /&gt;
    local url = &amp;quot;https://geohack.toolforge.org/geohack.php?&amp;quot;&lt;br /&gt;
    local input = main_args[1]&lt;br /&gt;
    local namearg = main_args[&amp;quot;name&amp;quot;]&lt;br /&gt;
    local current_page = mw.title.getCurrentTitle()&lt;br /&gt;
    local pagename = mw.uri.encode(current_page.prefixedText, &amp;quot;WIKI&amp;quot;)&lt;br /&gt;
    if not empty(pagename) then&lt;br /&gt;
        url = url .. &amp;quot;pagename=&amp;quot; .. pagename .. &amp;quot;&amp;amp;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    url = url .. &amp;quot;params=&amp;quot; .. mw.ustring.format(&amp;quot;%.6f&amp;quot;, LL.lat) .. &amp;quot;_N_&amp;quot;&lt;br /&gt;
    if LL.long &amp;lt; 0 then&lt;br /&gt;
        url = url .. mw.ustring.format(&amp;quot;%.6f&amp;quot;, -LL.long) .. &amp;quot;_W&amp;quot;&lt;br /&gt;
    else&lt;br /&gt;
        url = url .. mw.ustring.format(&amp;quot;%.6f&amp;quot;, LL.long) .. &amp;quot;_E&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    for _, a in ipairs(other_args) do&lt;br /&gt;
        url = url .. &amp;quot;_&amp;quot; .. a&lt;br /&gt;
    end&lt;br /&gt;
    if not mw.ustring.find(input, &amp;quot;region&amp;quot;) and LL.region then&lt;br /&gt;
        url = url .. &amp;quot;_region:&amp;quot; .. LL.region&lt;br /&gt;
    end&lt;br /&gt;
    if&lt;br /&gt;
        not mw.ustring.find(input, &amp;quot;scale&amp;quot;) and not mw.ustring.find(input, &amp;quot;type&amp;quot;) and not mw.ustring.find(input, &amp;quot;dim&amp;quot;) and&lt;br /&gt;
            LL.prec&lt;br /&gt;
     then&lt;br /&gt;
        url = url .. &amp;quot;_dim:&amp;quot; .. floor(50 * LL.prec + 0.5) .. &amp;quot;m&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    if not empty(namearg) then&lt;br /&gt;
        url = url .. &amp;quot;&amp;amp;title=&amp;quot; .. mw.uri.encode(namearg)&lt;br /&gt;
    end&lt;br /&gt;
    return url&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- function to generate direct link to OS map&lt;br /&gt;
local function directLink(other_args, LL)&lt;br /&gt;
    -- create direct link to OS maps. The template is&lt;br /&gt;
    -- https://explore.osmaps.com/pin?lat={latdegdec}&amp;amp;lon={londegdec}&amp;amp;zoom={osmzoom}&amp;amp;overlays=&amp;amp;style=Standard&amp;amp;type=2d&lt;br /&gt;
    local args = {}&lt;br /&gt;
    for _, a in ipairs(other_args) do&lt;br /&gt;
        local splitOut = mw.text.split(a, &amp;quot;:&amp;quot;, true)&lt;br /&gt;
        args[splitOut[1]] = splitOut[2]&lt;br /&gt;
    end&lt;br /&gt;
    if not args.scale and not args.type and not args.dim then&lt;br /&gt;
        args.dim = LL.prec and tostring(floor(50 * LL.prec + 0.5)) .. &amp;quot;m&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    args.viewport_cm = 10&lt;br /&gt;
    local zoom = require(&amp;quot;Module:Infobox dim&amp;quot;)._zoom&lt;br /&gt;
    local lvl = zoom(args) or 12&lt;br /&gt;
    local url =&lt;br /&gt;
        mw.ustring.format(&lt;br /&gt;
        &amp;quot;https://explore.osmaps.com/pin?lat=%.6f&amp;amp;lon=%.6f&amp;amp;zoom=%d&amp;amp;overlays=&amp;amp;style=Standard&amp;amp;type=2d&amp;quot;,&lt;br /&gt;
        LL.lat,&lt;br /&gt;
        LL.long,&lt;br /&gt;
        lvl&lt;br /&gt;
    )&lt;br /&gt;
    return url&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord._main(main_args)&lt;br /&gt;
    local input = main_args[1]&lt;br /&gt;
    if empty(input) then&lt;br /&gt;
        return warning(nil)&lt;br /&gt;
    end&lt;br /&gt;
    local linktitle = main_args[2]&lt;br /&gt;
    local rawurl = yesno(main_args[&amp;quot;rawurl&amp;quot;])&lt;br /&gt;
    local direct = yesno(main_args[&amp;quot;direct&amp;quot;])&lt;br /&gt;
    local args = split(input, &amp;quot;_&amp;quot;)&lt;br /&gt;
    local LL&lt;br /&gt;
    local restargs = 1&lt;br /&gt;
    if #args &amp;gt;= 2 and alldigits(args[2]) then&lt;br /&gt;
        if mw.ustring.sub(args[1], 1, 1) == &amp;quot;i&amp;quot; then&lt;br /&gt;
            local firstArg = mw.ustring.sub(args[1], 2)&lt;br /&gt;
            if alldigits(firstArg) then&lt;br /&gt;
                LL = IrishEN2LL(firstArg, args[2])&lt;br /&gt;
                restargs = 3&lt;br /&gt;
                if empty(linktitle) then&lt;br /&gt;
                    linktitle = args[1] .. &amp;quot;_&amp;quot; .. args[2]&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
        elseif alldigits(args[1]) then&lt;br /&gt;
            LL = GBEN2LL(args[1], args[2])&lt;br /&gt;
            restargs = 3&lt;br /&gt;
            if empty(linktitle) then&lt;br /&gt;
                linktitle = args[1] .. &amp;quot;_&amp;quot; .. args[2]&lt;br /&gt;
            end&lt;br /&gt;
        end&lt;br /&gt;
    else&lt;br /&gt;
        LL = NGR2LL(args[1])&lt;br /&gt;
        restargs = 2&lt;br /&gt;
        if empty(linktitle) then&lt;br /&gt;
            linktitle = args[1]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    linktitle = trim(linktitle)&lt;br /&gt;
    if not empty(LL.err) then&lt;br /&gt;
        return linktitle .. warning(LL.err)&lt;br /&gt;
    end&lt;br /&gt;
    LL.lat = LL.lat or 0&lt;br /&gt;
    LL.long = LL.long or 0&lt;br /&gt;
    LL.lat = ceil(LL.lat * 1000000) / 1000000&lt;br /&gt;
    LL.long = ceil(LL.long * 1000000) / 1000000&lt;br /&gt;
    local other_args = {}&lt;br /&gt;
    for i = restargs, #args do&lt;br /&gt;
        table.insert(other_args, args[i])&lt;br /&gt;
    end&lt;br /&gt;
    local url&lt;br /&gt;
    if not direct then&lt;br /&gt;
        url = geohack(main_args, other_args, LL)&lt;br /&gt;
    else&lt;br /&gt;
        url = directLink(other_args, LL)&lt;br /&gt;
    end&lt;br /&gt;
    if not rawurl then&lt;br /&gt;
        url = &amp;quot;[&amp;quot; .. url .. &amp;quot; &amp;quot; .. linktitle .. &amp;quot;]&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    return url&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord._oscoord(args)&lt;br /&gt;
    local output =&lt;br /&gt;
        &amp;#039;&amp;lt;span class=&amp;quot;plainlinks nourlexpansion&amp;quot; style=&amp;quot;white-space: nowrap&amp;quot;&amp;gt;&amp;#039; .. oscoord._main(args) .. &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
    if namespace == 0 then&lt;br /&gt;
        output = output .. &amp;quot;[[Category:Articles with OS grid coordinates]]&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord.main(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
    return oscoord._main(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord.oscoord(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
    return oscoord._oscoord(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
 ******  LAT/LONG CONVERSION TO OS GRID REF FUNCTIONS *****&lt;br /&gt;
 *  Uses the WGS-84 ellipsoid and only works for GB grid ref&lt;br /&gt;
 *&lt;br /&gt;
 *  See also:&lt;br /&gt;
 *  http://www.posc.org/Epicentre.2_2/DataModel/ExamplesofUsage/eu_cs34h.html&lt;br /&gt;
 *  http://kanadier.gps-info.de/d-utm-gitter.htm&lt;br /&gt;
 *  http://www.gpsy.com/gpsinfo/geotoutm/&lt;br /&gt;
 *  http://www.colorado.edu/geography/gcraft/notes/gps/gps_f.html&lt;br /&gt;
 *  http://search.cpan.org/src/GRAHAMC/Geo-Coordinates-UTM-0.05/&lt;br /&gt;
 *  UK Ordnance Survey grid (OSBG36): http://www.gps.gov.uk/guidecontents.asp&lt;br /&gt;
 *  Swiss CH1903: http://www.gps.gov.uk/guidecontents.asp&lt;br /&gt;
 *&lt;br /&gt;
 *  ----------------------------------------------------------------------&lt;br /&gt;
 *&lt;br /&gt;
 *  Copyright 2005, Egil Kvaleberg &amp;lt;egil@kvaleberg.no&amp;gt;&lt;br /&gt;
 *&lt;br /&gt;
 *  This program is free software; you can redistribute it and/or modify&lt;br /&gt;
 *  it under the terms of the GNU General Public License as published by&lt;br /&gt;
 *  the Free Software Foundation; either version 2 of the License, or&lt;br /&gt;
 *  (at your option) any later version.&lt;br /&gt;
 *&lt;br /&gt;
 *  This program is distributed in the hope that it will be useful,&lt;br /&gt;
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;
 *  GNU General Public License for more details.&lt;br /&gt;
 *&lt;br /&gt;
 *  You should have received a copy of the GNU General Public License&lt;br /&gt;
 *  along with this program; if not, write to the Free Software&lt;br /&gt;
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA&lt;br /&gt;
 &lt;br /&gt;
 Converted to Lua by User:Hike395 on 2023-12-15&lt;br /&gt;
]]&lt;br /&gt;
local function find_M(lat_rad)&lt;br /&gt;
    local e = OSGBglobe.ecc&lt;br /&gt;
    local e2 = e * e&lt;br /&gt;
    local e3 = e * e * e&lt;br /&gt;
&lt;br /&gt;
    return OSGBglobe.semimajor *&lt;br /&gt;
        ((1 - e / 4 - 3 * e2 / 64 - 5 * e3 / 256) * lat_rad -&lt;br /&gt;
            (3 * e / 8 + 3 * e2 / 32 + 45 * e3 / 1024) * sin(2 * lat_rad) +&lt;br /&gt;
            (15 * e2 / 256 + 45 * e3 / 1024) * sin(4 * lat_rad) -&lt;br /&gt;
            (35 * e3 / 3072) * sin(6 * lat_rad))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
*  Convert latitude, longitude in decimal degrees to&lt;br /&gt;
*  Transverse Mercator Easting and Northing based on a GB origin&lt;br /&gt;
*&lt;br /&gt;
*  return nil if problems&lt;br /&gt;
]]&lt;br /&gt;
local function LatLonOrigin2TM(latitude, longitude)&lt;br /&gt;
    if longitude &amp;lt; -180 or longitude &amp;gt; 180 or latitude &amp;lt; -80 or latitude &amp;gt; 84 then&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local longitude2 = longitude - floor((longitude + 180) / 360) * 360&lt;br /&gt;
&lt;br /&gt;
    local lat_rad = deg2rad(latitude)&lt;br /&gt;
&lt;br /&gt;
    local e = OSGBglobe.ecc&lt;br /&gt;
    local e_prime_sq = e / (1 - e)&lt;br /&gt;
&lt;br /&gt;
    local v = OSGBglobe.semimajor / sqrt(1 - e * sin(lat_rad) * sin(lat_rad))&lt;br /&gt;
    local tank = tan(lat_rad)&lt;br /&gt;
    local T = tank * tank&lt;br /&gt;
    local T2 = T * T&lt;br /&gt;
    local C = e_prime_sq * pow(cos(lat_rad), 2)&lt;br /&gt;
    local A = deg2rad(longitude2 - OSGBglobe.lon0) * cos(lat_rad)&lt;br /&gt;
    local A2 = A * A&lt;br /&gt;
    local A3 = A2 * A&lt;br /&gt;
    local A4 = A2 * A2&lt;br /&gt;
    local A5 = A3 * A2&lt;br /&gt;
    local A6 = A3 * A3&lt;br /&gt;
    local M = find_M(lat_rad)&lt;br /&gt;
    local M0 = 0.0&lt;br /&gt;
    if latitude_origin ~= 0 then&lt;br /&gt;
        M0 = find_M(deg2rad(OSGBglobe.lat0))&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local northing =&lt;br /&gt;
        OSGBglobe.n0 +&lt;br /&gt;
        OSGBglobe.scale *&lt;br /&gt;
            ((M - M0) +&lt;br /&gt;
                v * tan(lat_rad) *&lt;br /&gt;
                    (A2 / 2 + (5 - T + 9 * C + 4 * C * C) * A4 / 24 +&lt;br /&gt;
                        (61 - 58 * T + T2 + 600 * C - 330 * e_prime_sq) * A6 / 720))&lt;br /&gt;
&lt;br /&gt;
    local easting =&lt;br /&gt;
        OSGBglobe.e0 +&lt;br /&gt;
        OSGBglobe.scale * v * (A + (1 - T + C) * A3 / 6 + (5 - 18 * T + T2 + 72 * C - 58 * e_prime_sq) * A5 / 120)&lt;br /&gt;
&lt;br /&gt;
    return {northing = northing, easting = easting}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
*  Convert latitude, longitude in decimal degrees to&lt;br /&gt;
*  OSBG36 Easting and Northing&lt;br /&gt;
]]&lt;br /&gt;
local function LatLon2OSGB36(latlon, prec)&lt;br /&gt;
    local tm = LatLonOrigin2TM(latlon.lat, latlon.lon)&lt;br /&gt;
    if not tm then&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    if not tonumber(prec) then&lt;br /&gt;
        prec = 5&lt;br /&gt;
    end&lt;br /&gt;
    prec = floor(prec + 0.5)&lt;br /&gt;
    if prec &amp;gt; 5 then&lt;br /&gt;
        prec = 5&lt;br /&gt;
    end&lt;br /&gt;
    if prec &amp;lt; 1 then&lt;br /&gt;
        prec = 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- fix by Roger W Haworth&lt;br /&gt;
    local grid_x = floor(tm.easting / 100000)&lt;br /&gt;
    local grid_y = floor(tm.northing / 100000)&lt;br /&gt;
    if grid_x &amp;lt; 0 or grid_x &amp;gt; 6 or grid_y &amp;lt; 0 or grid_y &amp;gt; 12 then&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    --               0000000001111111111222222&lt;br /&gt;
    --               1234567890123456789012345&lt;br /&gt;
    local letters = &amp;quot;ABCDEFGHJKLMNOPQRSTUVWXYZ&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    local indx1 = 18 - floor(grid_y / 5) * 5 + floor(grid_x / 5)&lt;br /&gt;
    local indx2 = 21 - (grid_y % 5) * 5 + grid_x % 5&lt;br /&gt;
    local c1 = mw.ustring.sub(letters, indx1, indx1)&lt;br /&gt;
    local c2 = mw.ustring.sub(letters, indx2, indx2)&lt;br /&gt;
&lt;br /&gt;
    local easting = tm.easting % 100000&lt;br /&gt;
    local northing = tm.northing % 100000&lt;br /&gt;
    local grid = pow(10.0, 5.0 - prec)&lt;br /&gt;
    easting = floor(easting / grid)&lt;br /&gt;
    northing = floor(northing / grid)&lt;br /&gt;
    local fmt = &amp;quot;%0&amp;quot; .. prec .. &amp;quot;d&amp;quot;&lt;br /&gt;
    local e = mw.ustring.format(fmt, easting)&lt;br /&gt;
    local n = mw.ustring.format(fmt, northing)&lt;br /&gt;
&lt;br /&gt;
    return c1 .. c2 .. e .. n&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord._WGS2OSGB(lat, lon, prec)&lt;br /&gt;
    return LatLon2OSGB36(HelmertDatumShift(lat, lon, WGS2OSGBparam), prec)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord.WGS2OSGB(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
    return args[1] and args[2] and oscoord._WGS2OSGB(args[1], args[2], args[3]) or &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function oscoord.LL2OS(frame)&lt;br /&gt;
    local args = getArgs(frame)&lt;br /&gt;
    if not args[1] or not args[2] then&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    local gridRef = oscoord._WGS2OSGB(args[1], args[2], args.prec)&lt;br /&gt;
    if not gridRef or #gridRef == 0 then&lt;br /&gt;
        return &amp;quot;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    if args[3] then&lt;br /&gt;
        gridRef = gridRef .. &amp;quot;_&amp;quot; .. args[3]&lt;br /&gt;
    end&lt;br /&gt;
    return oscoord._oscoord({gridRef, args.linktitle, name = args.name})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return oscoord&lt;/div&gt;</summary>
		<author><name>SockyPaws</name></author>
	</entry>
</feed>