require('strict')  
local p = {}  
local getArgs = require('Module:Arguments').getArgs  
local function round(n, decimals)
local pow = 10^(decimals or 0)
return math.floor(n * pow + 0.5) / pow
end  
function p.getMapParams(map, )
if not map then
error('The name of the location map definition to use must be specified', 2)
end
local moduletitle = mw.title.new('Module:Location map/data/' .. map)
if not moduletitle then
error(string.format('%q is not a valid name for a location map definition', map), 2)
elseif moduletitle.exists then
local mapData = mw.loadData('Module:Location map/data/' .. map)
return function(name, params)
if name == nil then
return 'Module:Location map/data/' .. map
elseif mapData[name] == nil then
return ''
elseif params then
return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain
else
return mapData[name]
end
end
else
error('Unable to find the specified location map definition: "Module:Location map/data/' .. map .. '" does not exist', 2)
end
end  
function p.data(, args, map)
if not args then
args = getArgs(, {frameOnly = true})
end
if not map then
map = p.getMapParams(args[1], )
end
local params = {}
for k,v in ipairs(args) do
if k > 2 then
params[k-2] = v
end
end
return map(args[2], #params ~= 0 and params)
end  
local hemisphereMultipliers = {
longitude = { W = -1, w = -1, E = 1, e = 1 },
latitude = { S = -1, s = -1, N = 1, n = 1 }
}  
local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction)
if decimal then
if degrees then
error('Decimal and DMS degrees cannot both be provided for ' .. direction, 2)
elseif minutes then
error('Minutes can only be provided with DMS degrees for ' .. direction, 2)
elseif seconds then
error('Seconds can only be provided with DMS degrees for ' .. direction, 2)
elseif hemisphere then
error('A hemisphere can only be provided with DMS degrees for ' .. direction, 2)
end
local retval = tonumber(decimal)
if retval then
return retval
end
error('The value "' .. decimal .. '" provided for ' .. direction .. ' is not valid', 2)
elseif seconds and not minutes then
error('Seconds were provided for ' .. direction .. ' without minutes also being provided', 2)
elseif not degrees then
if minutes then
error('Minutes were provided for ' .. direction .. ' without degrees also being provided', 2)
elseif hemisphere then
error('A hemisphere was provided for ' .. direction .. ' without degrees also being provided', 2)
end
return nil
end
decimal = tonumber(degrees)
if not decimal then
error('The degree value "' .. degrees .. '" provided for ' .. direction .. ' is not valid', 2)
elseif minutes and not tonumber(minutes) then
error('The minute value "' .. minutes .. '" provided for ' .. direction .. ' is not valid', 2)
elseif seconds and not tonumber(seconds) then
error('The second value "' .. seconds .. '" provided for ' .. direction .. ' is not valid', 2)
end
decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600
if hemisphere then
local multiplier = hemisphereMultipliers[direction][hemisphere]
if not multiplier then
error('The hemisphere "' .. hemisphere .. '" provided for ' .. direction .. ' is not valid', 2)
end
decimal = decimal * multiplier
end
return decimal
end  
-- Finds a parameter in a transclusion of .
local function coord2text(para,coord) -- this should be changed for languages which do not use Arabic numerals or the degree sign
local lat, long = mw.ustring.match(coord,'<span>([^<]+)</span><span>([^<]+)</span>')
if lat then
return tonumber(para == 'longitude' and long or lat)
end
local result = mw.text.split(mw.ustring.match(coord,'%-?[%.%d]+°[NS] %-?[%.%d]+°[EW]') or '', '[ °]')
if para == 'longitude' then result = {result[3], result[4]} end
if not tonumber(result[1]) or not result[2] then
mw.log('Malformed coordinates value')
mw.logObject(para, 'para')
mw.logObject(coord, 'coord')
return error('Malformed coordinates value', 2)
end
return tonumber(result[1]) * hemisphereMultipliers[para][result[2]]
end  
-- effectively make removeBlanks false for caption and maplink, and true for everything else
-- if useWikidata is present but blank, convert it to false instead of nil
-- p.top, p.bottom, and their callers need to use this
function p.valueFunc(key, value)
if value then
value = mw.text.trim(value)
end
if value ~= '' or key == 'caption' or key == 'maplink' then
return value
elseif key == 'useWikidata' then
return false
end
end  
local function getContainerImage(args, map)
if args.AlternativeMap then
return args.AlternativeMap
elseif args.relief then
local digits = mw.ustring.match(args.relief,'^[1-9][0-9]?$') or '1' -- image1 to image99
if map('image' .. digits) ~= '' then
return map('image' .. digits)
end
end
return map('image')
end  
function p.top(, args, map)
if not args then
args = getArgs(, {frameOnly = true, valueFunc = p.valueFunc})
end
if not map then
map = p.getMapParams(args[1], )
end
local width
local default_as_number = tonumber(mw.ustring.match(tostring(args.default_width),"%d*"))
if not args.width then
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1))
elseif mw.ustring.sub(args.width, -2) == 'px' then
width = mw.ustring.sub(args.width, 1, -3)
else
width = args.width
end
local width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
if width_as_number == 0 then
-- check to see if width is junk. If it is, then use default calculation
width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1))
width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
end
if args.max_width ~= "" and args.max_width ~= nil then
-- check to see if width bigger than max_width
local max_as_number = tonumber(mw.ustring.match(args.max_width,"%d*")) or 0;
if width_as_number>max_as_number and max_as_number>0 then
width = args.max_width;
end
end
local retval = :extensionTag{name = 'templatestyles', args = {src = 'Module:Location map/styles.css'}}
if args.float == '' then
retval = retval .. '<div>'
end
if args.caption and args.caption ~= '' and args. ~= 'infobox' then
retval = retval .. '<div"' or args.float == '' then
retval = retval .. 'tleft'
elseif args.float == '""' or args.float == * or args.float == '""' or args.float == * then
retval = retval .. 'tnone'
else
retval = retval .. 'tright'
end
retval = retval .. '"><div><div>' or '">')
else
retval = retval .. '<div"' or args.float == '' then
retval = retval .. 'float:;clear:'
elseif args.float == '""' or args.float == '' then
retval = retval .. 'float:;clear:both;margin-:auto;margin-:auto'
elseif args.float == '""' or args.float == '' then
retval = retval .. 'float:;clear:'
else
retval = retval .. 'float:;clear:'
end
retval = retval .. '"><div><div>'
end
local image = getContainerImage(args, map)
local currentTitle = mw.title.getCurrentTitle
retval = string.format(
'%s',
retval,
image,
width,
args.alt or ((args.label or currentTitle.text) .. ' is located in ' .. map('name')),
args.maplink and ('|link=' .. args.maplink) or ''
if args.caption and args.caption ~= '' then
if (currentTitle.namespace == 0) and mw.ustring.find(args.caption, '##') then
retval = retval .. ''
end
end
if args.overlay_image then
return retval .. '<div></div>'
else
return retval
end
end  
function p.bottom(, args, map)
if not args then
args = getArgs(, {frameOnly = true, valueFunc = p.valueFunc})
end
if not map then
map = p.getMapParams(args[1], )
end
local retval = '</div>'
local currentTitle = mw.title.getCurrentTitle
if not args.caption or args. == 'infobox' then
if args. then
retval = retval .. '<div>'
else
retval = retval .. '<div>'
end
retval = retval
.. (args.caption or (args.label or currentTitle.text) .. ' (' .. map('name') .. ')')
.. '</div>'
elseif args.caption ~= '' then
-- This is not the pipe trick. We're creating a link with no text on purpose, so that CSS can give us a nice image
retval = retval .. '<div><div></div>' .. args.caption .. '</div>'
end  
if args.switcherLabel then
retval = retval .. '<span>' .. args.switcherLabel .. '</span>'