Moduldokumentasjon
--local math = require('Module:Math');

local labels = {
    ['en'] = { north = 'N', south = 'S', west = 'W', east = 'E' },
    ['nb'] = { north = 'N', south = 'S', west = 'V', east = 'Ø' }
}
local localLanguage = 'nb'

local titleTemplate = '<span style="font-size: small;">' ..
        ' <span id="coordinates">[[Geografiske koordinater|Koordinater]]: ' ..
        '  <span class="plainlinks nourlexpansion">' ..
        '   [//tools.wmflabs.org/geohack/geohack.php?language=no&pagename=%{pageName}s&params=%{geohackParams}s <span class="geo-dms" title="Kart, flyfoto og andre ressurser for dette stedet">' ..
        '    <span class="latitude">%{dmsLatitude}</span>' ..
        '    <span class="longitude">%{dmsLongitude}</span>' ..
        '   </span>]' ..
        '  </span>' ..
        ' </span>' ..
        '</span>';

-------------------------------------------------------------------------------
-- interp
--  Example: interp("${key}s is %{val}7.2f%", {key = "concentration", val = 56.2795} )
--  Source: http://lua-users.org/wiki/StringInterpolation
function interp(s, tab)
  return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))
end


-------------------------------------------------------------------------------
-- Coordinate class
--   Stores a geographic coordinate internally as DMS
-------------------------------------------------------------------------------
local Coordinate = {}

-------------------------------------------------------------------------------
-- Creates a new coordinate object.
-- @param o
--
function Coordinate:new(frame)
    local o = {}                -- create object
    meta = {__index = self}     -- 
    setmetatable(o, meta)       -- 

    o:init(frame)               -- do some initialization

    --[[
    o.fromWikidata = function(latitude, longitude, precision)
      return self:fromWikidata(latitude, longitude, precision)
    end
    o.formatForTitle = function(frame)
        return self:formatForTitle()
    end
    o.formatForInline = function(frame)
        return self:formatForInline()
    end
    ]]

    return o
end

-------------------------------------------------------------------------------
-- Initialize the coordinate object
--
function Coordinate:init( frame )

    -- store the frame
    self.frame = frame

    -- We use DMS for internal storage
    self.lat = { deg = nil, min = nil, sec = nil, sign = nil }
    self.long = { deg = nil, min = nil, sec = nil, sign = nil }


end

function Coordinate:parseDecimal( value, uncertainty )
    local o = {}
    o.sign = (value < 0 and -1 or 1)

    value = math.abs(value)
    value = math.floor(value/uncertainty + 1/2) * uncertainty

    o.deg = math.floor(value)     -- degrees
    value = (value - o.deg) * 60
    o.min = math.floor(value)     -- arcminutes
    o.sec = (value - o.min) * 60    -- arcseconds

    -- Round off arcseconds
    local factor = 1
    if (uncertainty < 1/3600.001) then
        factor = (1/3600) / uncertainty
    end
    o.factor = factor -- for debug
    o.sec = math.floor(o.sec * factor + 1/2) / factor
    if (o.sec >= 60) then
        o.sec = o.sec - 60
        o.min = o.min + 1
    end
    if (o.min == 60) then
        o.min = 0
        o.deg = o.deg + 1
    end

    -- if uncertainty < 1/60 => format = dms
    -- if uncertainty is between 1/3600 and 1/60 => format = dm
    -- if uncertainty >= 1, => format = d

    -- If uncertainty >= 1/60
    if (uncertainty > 1/60.001) then
        o.sec = nil
    end

    -- If uncertainty >= 1
    if (uncertainty > 1/1.001) then
        o.min = nil
    end

    return o
end

function Coordinate:fromDecimal( latitude, longitude, precision )

    if (precision == nil) then
        precision = 1/3600      -- set default precision to one arcsecond (1/3600 degree)
    end

    self.lat = Coordinate:parseDecimal(latitude, precision)
    self.long = Coordinate:parseDecimal(longitude, precision)

    --[[
    local antal_decimaler = 3
    if (prec >= 1e-8) then antal_decimaler = math.max(0,math.floor(-math.log(prec*3600)/math.log(10)+0.5)) end

    local format = coordinates.wdprec2dms(frame)
    if (format == 'd') then frame.args = {latd .. "", latNS, longd .. "", longEW, display=disp, 'region:'..reg..'_type:'..type..'_source:Wikidata'} end
    if (format == 'dm') then frame.args = {latd .. "", latm .. "", latNS, longd .. "", longm .. "", longEW, display=disp, 'region:'..reg..'_type:'..type..'_source:Wikidata'} end
    if (format == 'dms') then frame.args = {latd .. "", latm .. "", string.format('%2.'..antal_decimaler..'f',lats) .. "",latNS, longd .. "", longm .. "",string.format('%2.'..antal_decimaler..'f',longs) .. "",longEW, display=disp, 'region:'..reg..'_type:'..type..'_source:Wikidata'} end
    return coordinates.local_coord(frame)
    ]]
end

-------------------------------------------------------------------------------
-- Returns a single coordinate direction formatted as DMS
--
function Coordinate:formatAsDms(value, direction)
    return value.deg .. '°' ..
        (value.min and value.min .. '′' or '') ..
        (value.sec and value.sec .. '″' or '') ..
        (direction == 'latitude'
            and (value.sign < 0 and labels[localLanguage].south or labels[localLanguage].north)
            or (value.sign < 0 and labels[localLanguage].west or labels[localLanguage].east)
        )
end

-------------------------------------------------------------------------------
-- Returns the geohack URL params
--
function Coordinate:geohackParams(lat, long)
    return lat.deg ..
        (lat.min and '_' .. lat.min or '') ..
        (lat.sec and '_' .. lat.sec or '') ..
        '_' .. (lat.sign < 0 and labels.en.south or labels.en.north) .. 
        '_' .. long.deg ..
        (long.min and '_' .. long.min or '') ..
        (long.sec and '_' .. long.sec or '') ..
        '_' .. (long.sign < 0 and labels.en.west or labels.en.east)
end

-------------------------------------------------------------------------------
-- Returns the coordinate formatted for 'title' use
--
function Coordinate:formatForTitle()
    return interp(titleTemplate, {
        pageName = 'dummy',
        geohackParams = self:geohackParams(self.lat, self.long),
        dmsLatitude = self:formatAsDms(self.lat, 'latitude'),
        dmsLongitude = self:formatAsDms(self.long, 'longitude')
    })
end

return Coordinate