Jump to content
Toggle menu
  • 8 articles
  • 75 files
  • 7 users
  • 37.8K edits
Neodyland Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Coordinates

From Neodyland Wiki
Revision as of 18:01, 11 August 2026 by CodexBot (talk | contribs) (Lightweight Wikidata-free implementation (GeoData-based coordinates))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Module documentation[ create · purge ]
This documentation is transcluded from Module:Coordinates/doc.
--[[
    Module:Coordinates (lightweight Neodyland version)
    Provides a minimal LocationTemplateCore used by {{Location}} and {{Object location}}.
    Wikidata/SDC-free: coordinates are taken from template parameters only.
    Uses GeoData's {{#coordinates:}} parser function when available.
]]

local p = {}
local i18n = require('Module:I18n/coordinates')

local CoorCat = {
    dms      = '[[Category:Media with coordinates in DMS format]]',
    erroneous = '[[Category:Location templates with invalid parameters]]',
}

local function dms2deg(v)
    -- parse "34° 05′ 32.36″ N" or "34|5|32.36|N" style input to decimal degrees
    local parts = {}
    for num in string.gmatch(v, "%d+%.?%d*") do
        table.insert(parts, tonumber(num))
    end
    if #parts < 2 then return nil end
    local deg = parts[1]
    local min = parts[2] or 0
    local sec = parts[3] or 0
    local val = deg + min / 60 + sec / 3600
    if v:find('[Ss]') or v:find('[Ww]') then val = -val end
    return val
end

local function parseLatLon(args)
    local lat, lon
    if args.lat and args.lon then
        lat, lon = args.lat, args.lon
    elseif args[4] then
        -- DMS with pipes: 34|5|32.36|N|116|9|24.55|W
        lat = dms2deg(args[1] .. ' ' .. args[2] .. "' " .. args[3] .. '" ' .. args[4])
        lon = dms2deg(args[5] .. ' ' .. args[6] .. "' " .. args[7] .. '" ' .. args[8])
    elseif args[2] then
        lat, lon = args[1], args[2]
    elseif args[1] then
        -- "34° 05′ N, 116° 09′ W"
        local s = mw.text.split(args[1], ',')
        if #s == 2 then
            lat, lon = s[1], s[2]
        end
    end
    if lat then
        local n = tonumber(lat)
        if not n then n = dms2deg(lat) end
        lat = n
    end
    if lon then
        local n = tonumber(lon)
        if not n then n = dms2deg(lon) end
        lon = n
    end
    return lat, lon
end

local function formatCoor(lat, lon, lang)
    if not lat or not lon then return '' end
    local ns, ew = 'N', 'E'
    if lat < 0 then lat = -lat; ns = 'S' end
    if lon < 0 then lon = -lon; ew = 'W' end
    local nsew = i18n.NSEW[lang] or i18n.NSEW.en
    local latStr = string.format("%.4f° %s", lat, nsew[ns])
    local lonStr = string.format("%.4f° %s", lon, nsew[ew])
    return latStr .. ' ' .. lonStr
end

function p._LocationTemplateCore(args)
    local lang = args.lang or mw.language.getContentLanguage().code
    local lat, lon = parseLatLon(args)
    local templateText, Categories, coorTag = '', '', ''

    if lat and lon then
        templateText = formatCoor(lat, lon, lang)
        -- Validate range
        if math.abs(lon) > 180 or math.abs(lat) > 90 then
            Categories = Categories .. '<span style="color:red;font-weight:bold">Error: Invalid parameters! (coordinates are outside allowed range)</span>\n' .. CoorCat.erroneous
            lat, lon = nil, nil
        end
    end

    if lat and lon then
        -- Add {{#coordinates:}} tag (GeoData) for File/Category namespaces
        local ns = mw.title.getCurrentTitle().nsText
        if ns == 'File' or ns == 'Category' or ns == 'Gallery' then
            local status = 'primary'
            local args3 = args.attributes or ''
            coorTag = mw.getCurrentFrame():callParserFunction('#coordinates', { status, lat, lon, args3 })
        end
    else
        Categories = Categories .. '<span style="color:red;font-weight:bold">Error: Invalid parameters! (coordinates are missing or not numeric)</span>\n' .. CoorCat.erroneous
    end

    return templateText, Categories, coorTag
end

function p.LocationTemplateCore(frame)
    local args = require('Module:Arguments').getArgs(frame)
    args.namespace = mw.title.getCurrentTitle().nsText
    local templateText, Categories, cat, coorTag
    local lat, lon = parseLatLon(args)
    -- validate numeric
    if lat and lon then
        local l1, l2 = tonumber(lat), tonumber(lon)
        if not l1 or not l2 then
            args.lat = dms2deg(tostring(lat) or '') or lat
            args.lon = dms2deg(tostring(lon) or '') or lon
            if args.namespace == 'File' or args.namespace == 'Category' then
                cat = CoorCat.dms
            end
        end
    end
    templateText, Categories, coorTag = p._LocationTemplateCore(args)
    return templateText .. Categories .. (cat or '') .. (coorTag or '')
end

return p
Cookies help us deliver our services. By using our services, you agree to our use of cookies.