Theme: iWiki Log in Register

Diff: Module:Location map

Comparing revision #1 (2023-12-31 15:35:14) with revision #2 (2024-03-04 22:45:29).

OldNew
require('strict')
require('strict')
local p = {}
local p = {}
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local function round(n, decimals)
local function round(n, decimals)
	local pow = 10^(decimals or 0)
	local pow = 10^(decimals or 0)
	return math.floor(n * pow + 0.5) / pow
	return math.floor(n * pow + 0.5) / pow
end
end
function p.getMapParams(map, frame)
function p.getMapParams(map, frame)
	if not map then
	if not map then
		error('The name of the location map definition to use must be specified', 2)
		error('The name of the location map definition to use must be specified', 2)
	end
	end
	local moduletitle = mw.title.new('Module:Location map/data/' .. map)
	local moduletitle = mw.title.new('Module:Location map/data/' .. map)
	if not moduletitle then
	if not moduletitle then
		error(string.format('%q is not a valid name for a location map definition', map), 2)
		error(string.format('%q is not a valid name for a location map definition', map), 2)
	elseif moduletitle.exists then
	elseif moduletitle.exists then
		local mapData = mw.loadData('Module:Location map/data/' .. map)
		local mapData = mw.loadData('Module:Location map/data/' .. map)
		return function(name, params)
		return function(name, params)
			if name == nil then
			if name == nil then
				return 'Module:Location map/data/' .. map
				return 'Module:Location map/data/' .. map
			elseif mapData[name] == nil then
			elseif mapData[name] == nil then
				return ''
				return ''
			elseif params then
			elseif params then
				return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain()
				return mw.message.newRawMessage(tostring(mapData[name]), unpack(params)):plain()
			else
			else
				return mapData[name]
				return mapData[name]
			end
			end
		end
		end
	else
	else
		error('Unable to find the specified location map definition: "Module:Location map/data/' .. map .. '" does not exist', 2)
		error('Unable to find the specified location map definition: "Module:Location map/data/' .. map .. '" does not exist', 2)
	end
	end
end
end
function p.data(frame, args, map)
function p.data(frame, args, map)
	if not args then
	if not args then
		args = getArgs(frame, {frameOnly = true})
		args = getArgs(frame, {frameOnly = true})
	end
	end
	if not map then
	if not map then
		map = p.getMapParams(args[1], frame)
		map = p.getMapParams(args[1], frame)
	end
	end
	local params = {}
	local params = {}
	for k,v in ipairs(args) do
	for k,v in ipairs(args) do
		if k > 2 then
		if k > 2 then
			params[k-2] = v
			params[k-2] = v
		end
		end
	end
	end
	return map(args[2], #params ~= 0 and params)
	return map(args[2], #params ~= 0 and params)
end
end
local hemisphereMultipliers = {
local hemisphereMultipliers = {
	longitude = { W = -1, w = -1, E = 1, e = 1 },
	longitude = { W = -1, w = -1, E = 1, e = 1 },
	latitude = { S = -1, s = -1, N = 1, n = 1 }
	latitude = { S = -1, s = -1, N = 1, n = 1 }
}
}
local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction)
local function decdeg(degrees, minutes, seconds, hemisphere, decimal, direction)
	if decimal then
	if decimal then
		if degrees then
		if degrees then
			error('Decimal and DMS degrees cannot both be provided for ' .. direction, 2)
			error('Decimal and DMS degrees cannot both be provided for ' .. direction, 2)
		elseif minutes then
		elseif minutes then
			error('Minutes can only be provided with DMS degrees for ' .. direction, 2)
			error('Minutes can only be provided with DMS degrees for ' .. direction, 2)
		elseif seconds then
		elseif seconds then
			error('Seconds can only be provided with DMS degrees for ' .. direction, 2)
			error('Seconds can only be provided with DMS degrees for ' .. direction, 2)
		elseif hemisphere then
		elseif hemisphere then
			error('A hemisphere can only be provided with DMS degrees for ' .. direction, 2)
			error('A hemisphere can only be provided with DMS degrees for ' .. direction, 2)
		end
		end
		local retval = tonumber(decimal)
		local retval = tonumber(decimal)
		if retval then
		if retval then
			return retval
			return retval
		end
		end
		error('The value "' .. decimal .. '" provided for ' .. direction .. ' is not valid', 2)
		error('The value "' .. decimal .. '" provided for ' .. direction .. ' is not valid', 2)
	elseif seconds and not minutes then
	elseif seconds and not minutes then
		error('Seconds were provided for ' .. direction .. ' without minutes also being provided', 2)
		error('Seconds were provided for ' .. direction .. ' without minutes also being provided', 2)
	elseif not degrees then
	elseif not degrees then
		if minutes then
		if minutes then
			error('Minutes were provided for ' .. direction .. ' without degrees also being provided', 2)
			error('Minutes were provided for ' .. direction .. ' without degrees also being provided', 2)
		elseif hemisphere then
		elseif hemisphere then
			error('A hemisphere was provided for ' .. direction .. ' without degrees also being provided', 2)
			error('A hemisphere was provided for ' .. direction .. ' without degrees also being provided', 2)
		end
		end
		return nil
		return nil
	end
	end
	decimal = tonumber(degrees)
	decimal = tonumber(degrees)
	if not decimal then
	if not decimal then
		error('The degree value "' .. degrees .. '" provided for ' .. direction .. ' is not valid', 2)
		error('The degree value "' .. degrees .. '" provided for ' .. direction .. ' is not valid', 2)
	elseif minutes and not tonumber(minutes) then
	elseif minutes and not tonumber(minutes) then
		error('The minute value "' .. minutes .. '" provided for ' .. direction .. ' is not valid', 2)
		error('The minute value "' .. minutes .. '" provided for ' .. direction .. ' is not valid', 2)
	elseif seconds and not tonumber(seconds) then
	elseif seconds and not tonumber(seconds) then
		error('The second value "' .. seconds .. '" provided for ' .. direction .. ' is not valid', 2)
		error('The second value "' .. seconds .. '" provided for ' .. direction .. ' is not valid', 2)
	end
	end
	decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600
	decimal = decimal + (minutes or 0)/60 + (seconds or 0)/3600
	if hemisphere then
	if hemisphere then
		local multiplier = hemisphereMultipliers[direction][hemisphere]
		local multiplier = hemisphereMultipliers[direction][hemisphere]
		if not multiplier then
		if not multiplier then
			error('The hemisphere "' .. hemisphere .. '" provided for ' .. direction .. ' is not valid', 2)
			error('The hemisphere "' .. hemisphere .. '" provided for ' .. direction .. ' is not valid', 2)
		end
		end
		decimal = decimal * multiplier
		decimal = decimal * multiplier
	end
	end
	return decimal
	return decimal
end
end
-- Finds a parameter in a transclusion of {{Coord}}.
-- Finds a parameter in a transclusion of {{Coord}}.
local function coord2text(para,coord) -- this should be changed for languages which do not use Arabic numerals or the degree sign
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 class="p%-latitude latitude">([^<]+)</span><span class="p%-longitude longitude">([^<]+)</span>')
	local lat, long = mw.ustring.match(coord,'<span class="p%-latitude latitude">([^<]+)</span><span class="p%-longitude longitude">([^<]+)</span>')
	if lat then
	if lat then
		return tonumber(para == 'longitude' and long or lat)
		return tonumber(para == 'longitude' and long or lat)
	end
	end
	local result = mw.text.split(mw.ustring.match(coord,'%-?[%.%d]+°[NS] %-?[%.%d]+°[EW]') or '', '[ °]')
	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 para == 'longitude' then result = {result[3], result[4]} end
	if not tonumber(result[1]) or not result[2] then
	if not tonumber(result[1]) or not result[2] then
		mw.log('Malformed coordinates value')
		mw.log('Malformed coordinates value')
		mw.logObject(para, 'para')
		mw.logObject(para, 'para')
		mw.logObject(coord, 'coord')
		mw.logObject(coord, 'coord')
		return error('Malformed coordinates value', 2)
		return error('Malformed coordinates value', 2)
	end
	end
	return tonumber(result[1]) * hemisphereMultipliers[para][result[2]]
	return tonumber(result[1]) * hemisphereMultipliers[para][result[2]]
end
end
-- effectively make removeBlanks false for caption and maplink, and true for everything else
-- 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
-- if useWikidata is present but blank, convert it to false instead of nil
-- p.top, p.bottom, and their callers need to use this
-- p.top, p.bottom, and their callers need to use this
function p.valueFunc(key, value)
function p.valueFunc(key, value)
	if value then
	if value then
		value = mw.text.trim(value)
		value = mw.text.trim(value)
	end
	end
	if value ~= '' or key == 'caption' or key == 'maplink' then
	if value ~= '' or key == 'caption' or key == 'maplink' then
		return value
		return value
	elseif key == 'useWikidata' then
	elseif key == 'useWikidata' then
		return false
		return false
	end
	end
end
end
local function getContainerImage(args, map)
local function getContainerImage(args, map)
	if args.AlternativeMap then
	if args.AlternativeMap then
		return args.AlternativeMap
		return args.AlternativeMap
	elseif args.relief then
	elseif args.relief then
        local digits = mw.ustring.match(args.relief,'^[1-9][0-9]?$') or '1' -- image1 to image99
        local digits = mw.ustring.match(args.relief,'^[1-9][0-9]?$') or '1' -- image1 to image99
        if map('image' .. digits) ~= '' then
        if map('image' .. digits) ~= '' then
            return map('image' .. digits)
            return map('image' .. digits)
        end
        end
	end
	end
    return map('image')
    return map('image')
end
end
function p.top(frame, args, map)
function p.top(frame, args, map)
	if not args then
	if not args then
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
	end
	end
	if not map then
	if not map then
		map = p.getMapParams(args[1], frame)
		map = p.getMapParams(args[1], frame)
	end
	end
	local width
	local width
	local default_as_number = tonumber(mw.ustring.match(tostring(args.default_width),"%d*"))
	local default_as_number = tonumber(mw.ustring.match(tostring(args.default_width),"%d*"))
	if not args.width then
	if not args.width then
		width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1))
		width = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1))
	elseif mw.ustring.sub(args.width, -2) == 'px' then
	elseif mw.ustring.sub(args.width, -2) == 'px' then
		width = mw.ustring.sub(args.width, 1, -3)
		width = mw.ustring.sub(args.width, 1, -3)
	else
	else
		width = args.width
		width = args.width
	end
	end
	local width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
	local width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
    if width_as_number == 0 then
    if width_as_number == 0 then
    	-- check to see if width is junk. If it is, then use default calculation
    	-- 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 = round((default_as_number or 240) * (tonumber(map('defaultscale')) or 1))
    	width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
    	width_as_number = tonumber(mw.ustring.match(tostring(width),"%d*")) or 0;
    end	
    end	
    if args.max_width ~= "" and args.max_width ~= nil then
    if args.max_width ~= "" and args.max_width ~= nil then
        -- check to see if width bigger than max_width
        -- check to see if width bigger than max_width
        local max_as_number = tonumber(mw.ustring.match(args.max_width,"%d*")) or 0;
        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
        if width_as_number>max_as_number and max_as_number>0 then
            width = args.max_width;
            width = args.max_width;
        end
        end
    end
    end
	local retval = frame:extensionTag{name = 'templatestyles', args = {src = 'Module:Location map/styles.css'}}
	local retval = frame:extensionTag{name = 'templatestyles', args = {src = 'Module:Location map/styles.css'}}
	if args.float == 'center' then
	if args.float == 'center' then
		retval = retval .. '<div class="center">'
		retval = retval .. '<div class="center">'
	end
	end
	if args.caption and args.caption ~= '' and args.border ~= 'infobox' then
	if args.caption and args.caption ~= '' and args.border ~= 'infobox' then
		retval = retval .. '<div class="locmap noviewer noresize thumb '
		retval = retval .. '<div class="locmap noviewer noresize thumb '
		if args.float == '"left"' or args.float == 'left' then
		if args.float == '"left"' or args.float == 'left' then
			retval = retval .. 'tleft'
			retval = retval .. 'tleft'
		elseif args.float == '"center"' or args.float == 'center' or args.float == '"none"' or args.float == 'none' then
		elseif args.float == '"center"' or args.float == 'center' or args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'tnone'
			retval = retval .. 'tnone'
		else
		else
			retval = retval .. 'tright'
			retval = retval .. 'tright'
		end
		end
		retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px'
		retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px'
		if args.border == 'none' then
		if args.border == 'none' then
			retval = retval .. ';border:none'
			retval = retval .. ';border:none'
		elseif args.border then
		elseif args.border then
			retval = retval .. ';border-color:' .. args.border
			retval = retval .. ';border-color:' .. args.border
		end
		end
		retval = retval .. '"><div style="position:relative;width:' .. width .. 'px' .. (args.border ~= 'none' and ';border:1px solid lightgray">' or '">')
		retval = retval .. '"><div style="position:relative;width:' .. width .. 'px' .. (args.border ~= 'none' and ';border:1px solid lightgray">' or '">')
	else
	else
		retval = retval .. '<div class="locmap" style="width:' .. width .. 'px;'
		retval = retval .. '<div class="locmap" style="width:' .. width .. 'px;'
		if args.float == '"left"' or args.float == 'left' then
		if args.float == '"left"' or args.float == 'left' then
			retval = retval .. 'float:left;clear:left'
			retval = retval .. 'float:left;clear:left'
		elseif args.float == '"center"' or args.float == 'center' then
		elseif args.float == '"center"' or args.float == 'center' then
			retval = retval .. 'float:none;clear:both;margin-left:auto;margin-right:auto'
			retval = retval .. 'float:none;clear:both;margin-left:auto;margin-right:auto'
		elseif args.float == '"none"' or args.float == 'none' then
		elseif args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'float:none;clear:none'
			retval = retval .. 'float:none;clear:none'
		else
		else
			retval = retval .. 'float:right;clear:right'
			retval = retval .. 'float:right;clear:right'
		end
		end
		retval = retval .. '"><div style="width:' .. width .. 'px;padding:0"><div style="position:relative;width:' .. width .. 'px">'
		retval = retval .. '"><div style="width:' .. width .. 'px;padding:0"><div style="position:relative;width:' .. width .. 'px">'
	end
	end
	local image = getContainerImage(args, map)
	local image = getContainerImage(args, map)
	local currentTitle = mw.title.getCurrentTitle()
	local currentTitle = mw.title.getCurrentTitle()
	retval = string.format(
	retval = string.format(
		'%s[[File:%s|%spx|%s%s|class=notpageimage]]',
		'%s[[File:%s|%spx|%s%s|class=notpageimage]]',
		retval,
		retval,
		image,
		image,
		width,
		width,
		args.alt or ((args.label or currentTitle.text) .. ' is located in ' .. map('name')),
		args.alt or ((args.label or currentTitle.text) .. ' is located in ' .. map('name')),
		args.maplink and ('|link=' .. args.maplink) or ''
		args.maplink and ('|link=' .. args.maplink) or ''
	)
	)
	if args.caption and args.caption ~= '' then
	if args.caption and args.caption ~= '' then
		if (currentTitle.namespace == 0) and mw.ustring.find(args.caption, '##') then
		if (currentTitle.namespace == 0) and mw.ustring.find(args.caption, '##') then
			retval = retval .. '[[Category:Pages using location map with a double number sign in the caption]]'
			retval = retval .. '[[Category:Pages using location map with a double number sign in the caption]]'
		end
		end
	end
	end
	if args.overlay_image then
	if args.overlay_image then
		return retval .. '<div style="position:absolute;top:0;left:0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|class=notpageimage]]</div>'
		return retval .. '<div style="position:absolute;top:0;left:0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|class=notpageimage]]</div>'
	else
	else
		return retval
		return retval
	end
	end
end
end
function p.bottom(frame, args, map)
function p.bottom(frame, args, map)
	if not args then
	if not args then
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
		args = getArgs(frame, {frameOnly = true, valueFunc = p.valueFunc})
	end
	end
	if not map then
	if not map then
		map = p.getMapParams(args[1], frame)
		map = p.getMapParams(args[1], frame)
	end
	end
	local retval = '</div>'
	local retval = '</div>'
	local currentTitle = mw.title.getCurrentTitle()
	local currentTitle = mw.title.getCurrentTitle()
	if not args.caption or args.border == 'infobox' then
	if not args.caption or args.border == 'infobox' then
		if args.border then
		if args.border then
			retval = retval .. '<div style="padding-top:0.2em">'
			retval = retval .. '<div style="padding-top:0.2em">'
		else
		else
			retval = retval .. '<div style="font-size:91%;padding-top:3px">'
			retval = retval .. '<div style="font-size:91%;padding-top:3px">'
		end
		end
		retval = retval
		retval = retval
		.. (args.caption or (args.label or currentTitle.text) .. ' (' .. map('name') .. ')')
		.. (args.caption or (args.label or currentTitle.text) .. ' (' .. map('name') .. ')')
		.. '</div>'
		.. '</div>'
	elseif args.caption ~= ''  then
	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
		-- 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 class="thumbcaption"><div class="magnify">[[:File:' .. getContainerImage(args, map) .. '|class=notpageimage| ]]</div>' .. args.caption .. '</div>'
		retval = retval .. '<div class="thumbcaption"><div class="magnify">[[:File:' .. getContainerImage(args, map) .. '|class=notpageimage| ]]</div>' .. args.caption .. '</div>'
	end
	end
	if args.switcherLabel then
	if args.switcherLabel then
		retval = retval .. '<span class="switcher-label" style="display:none">' .. args.switcherLabel .. '</span>'
		retval = retval .. '<span class="switcher-label" style="display:none">' .. args.switcherLabel .. '</span>'
	elseif args.autoSwitcherLabel then
	elseif args.autoSwitcherLabel then
		retval = retval .. '<span class="switcher-label" style="display:none">Show map of ' .. map('name') .. '</span>'
		retval = retval .. '<span class="switcher-label" style="display:none">Show map of ' .. map('name') .. '</span>'
	end
	end
	
	
	retval = retval .. '</div></div>'
	retval = retval .. '</div></div>'
	if args.caption_undefined then
	if args.caption_undefined then
		mw.log('Removed parameter caption_undefined used.')
		mw.log('Removed parameter caption_undefined used.')
		local parent = frame:getParent()
		local parent = frame:getParent()
		if parent then
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
			mw.log('Parent is ' .. parent:getTitle())
		end
		end
		mw.logObject(args, 'args')
		mw.logObject(args, 'args')
		if currentTitle.namespace == 0 then
		if currentTitle.namespace == 0 then
		    retval = retval .. '[[Category:Location maps with removed parameters|caption_undefined]]'
		    retval = retval .. '[[Category:Location maps with removed parameters|caption_undefined]]'
		end
		end
	end
	end
	if map('skew') ~= '' or map('lat_skew') ~= '' or map('crosses180') ~= '' or map('type') ~= '' then
	if map('skew') ~= '' or map('lat_skew') ~= '' or map('crosses180') ~= '' or map('type') ~= '' then
		mw.log('Removed parameter used in map definition ' .. map())
		mw.log('Removed parameter used in map definition ' .. map())
		if currentTitle.namespace == 0 then
		if currentTitle.namespace == 0 then
		    local key = (map('skew') ~= '' and 'skew' or '') ..
		    local key = (map('skew') ~= '' and 'skew' or '') ..
					(map('lat_skew') ~= '' and 'lat_skew' or '') ..
					(map('lat_skew') ~= '' and 'lat_skew' or '') ..
					(map('crosses180') ~= '' and 'crosses180' or '') ..
					(map('crosses180') ~= '' and 'crosses180' or '') ..
					(map('type') ~= '' and 'type' or '')
					(map('type') ~= '' and 'type' or '')
		    retval = retval .. '[[Category:Location maps with removed parameters|' .. key .. ' ]]'
		    retval = retval .. '[[Category:Location maps with removed parameters|' .. key .. ' ]]'
		end
		end
	end
	end
	if string.find(map('name'), '|', 1, true) then
	if string.find(map('name'), '|', 1, true) then
		mw.log('Pipe used in name of map definition ' .. map())
		mw.log('Pipe used in name of map definition ' .. map())
		if currentTitle.namespace == 0 then
		if currentTitle.namespace == 0 then
		   retval = retval .. '[[Category:Location maps with a name containing a pipe]]'
		   retval = retval .. '[[Category:Location maps with a name containing a pipe]]'
		end
		end
	end
	end
	if args.float == 'center' then
	if args.float == 'center' then
		retval = retval .. '</div>'
		retval = retval .. '</div>'
	end
	end
	return retval
	return retval
end
end
local function markOuterDiv(x, y, imageDiv, labelDiv)
local function markOuterDiv(x, y, imageDiv, labelDiv)
	return mw.html.create('div')
	return mw.html.create('div')
		:addClass('od')
		:addClass('od')
		:addClass('notheme')  -- T236137
		:addClass('notheme')  -- T236137
		:cssText('top:' .. round(y, 3) .. '%;left:' .. round(x, 3) .. '%')
		:cssText('top:' .. round(y, 3) .. '%;left:' .. round(x, 3) .. '%')
		:node(imageDiv)
		:node(imageDiv)
		:node(labelDiv)
		:node(labelDiv)
end
end
local function markImageDiv(mark, marksize, label, link, alt, title)
local function markImageDiv(mark, marksize, label, link, alt, title)
	local builder = mw.html.create('div')
	local builder = mw.html.create('div')
		:addClass('id')
		:addClass('id')
		:cssText('left:-' .. round(marksize / 2) .. 'px;top:-' .. round(marksize / 2) .. 'px')
		:cssText('left:-' .. round(marksize / 2) .. 'px;top:-' .. round(marksize / 2) .. 'px')
		:attr('title', title)
		:attr('title', title)
	if marksize ~= 0 then
	if marksize ~= 0 then
		builder:wikitext(string.format(
		builder:wikitext(string.format(
			'[[File:%s|%dx%dpx|%s|link=%s%s|class=notpageimage]]',
			'[[File:%s|%dx%dpx|%s|link=%s%s|class=notpageimage]]',
			mark,
			mark,
			marksize,
			marksize,
			marksize,
			marksize,
			label,
			label,
			link,
			link,
			alt and ('|alt=' .. alt) or ''
			alt and ('|alt=' .. alt) or ''
		))
		))
	end
	end
	return builder
	return builder
end
end
local function markLabelDiv(label, label_size, label_width, position, background, x, marksize)
local function markLabelDiv(label, label_size, label_width, position, background, x, marksize)
	if tonumber(label_size) == 0 then
	if tonumber(label_size) == 0 then
		return mw.html.create('div'):addClass('l0'):wikitext(label)
		return mw.html.create('div'):addClass('l0'):wikitext(label)
	end
	end
	local builder = mw.html.create('div')
	local builder = mw.html.create('div')
		:cssText('font-size:' .. label_size .. '%;width:' .. label_width .. 'em')
		:cssText('font-size:' .. label_size .. '%;width:' .. label_width .. 'em')
	local distance = round(marksize / 2 + 1)
	local distance = round(marksize / 2 + 1)
	if position == 'top' then -- specified top
	if position == 'top' then -- specified top
		builder:addClass('pv'):cssText('bottom:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em')
		builder:addClass('pv'):cssText('bottom:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em')
	elseif position == 'bottom' then -- specified bottom
	elseif position == 'bottom' then -- specified bottom
		builder:addClass('pv'):cssText('top:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em')
		builder:addClass('pv'):cssText('top:' .. distance .. 'px;left:' .. (-label_width / 2) .. 'em')
	elseif position == 'left' or (tonumber(x) > 70 and position ~= 'right') then -- specified left or autodetected to left
	elseif position == 'left' or (tonumber(x) > 70 and position ~= 'right') then -- specified left or autodetected to left
		builder:addClass('pl'):cssText('right:' .. distance .. 'px')
		builder:addClass('pl'):cssText('right:' .. distance .. 'px')
	else -- specified right or autodetected to right
	else -- specified right or autodetected to right
		builder:addClass('pr'):cssText('left:' .. distance .. 'px')
		builder:addClass('pr'):cssText('left:' .. distance .. 'px')
	end
	end
	builder = builder:tag('div')
	builder = builder:tag('div')
		:wikitext(label)
		:wikitext(label)
	if background then
	if background then
		builder:cssText('background-color:' .. background)
		builder:cssText('background-color:' .. background)
	end
	end
	return builder:done()
	return builder:done()
end
end
local function getX(longitude, left, right)
local function getX(longitude, left, right)
	local width = (right - left) % 360
	local width = (right - left) % 360
	if width == 0 then
	if width == 0 then
		width = 360
		width = 360
	end
	end
	local distanceFromLeft = (longitude - left) % 360
	local distanceFromLeft = (longitude - left) % 360
	-- the distance needed past the map to the right equals distanceFromLeft - width. the distance needed past the map to the left equals 360 - distanceFromLeft. to minimize page stretching, go whichever way is shorter
	-- the distance needed past the map to the right equals distanceFromLeft - width. the distance needed past the map to the left equals 360 - distanceFromLeft. to minimize page stretching, go whichever way is shorter
	if distanceFromLeft - width / 2 >= 180 then
	if distanceFromLeft - width / 2 >= 180 then
		distanceFromLeft = distanceFromLeft - 360
		distanceFromLeft = distanceFromLeft - 360
	end
	end
	return 100 * distanceFromLeft / width
	return 100 * distanceFromLeft / width
end
end
local function getY(latitude, top, bottom)
local function getY(latitude, top, bottom)
	return 100 * (top - latitude) / (top - bottom)
	return 100 * (top - latitude) / (top - bottom)
end
end
function p.mark(frame, args, map)
function p.mark(frame, args, map)
	if not args then
	if not args then
		args = getArgs(frame, {wrappers = 'Template:Location map~'})
		args = getArgs(frame, {wrappers = 'Template:Location map~'})
	end
	end
	local mapnames = {}
	local mapnames = {}
	if not map then
	if not map then
		if args[1] then
		if args[1] then
			map = {}
			map = {}
			for mapname in mw.text.gsplit(args[1], '#', true) do
			for mapname in mw.text.gsplit(args[1], '#', true) do
				map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame)
				map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame)
				mapnames[#mapnames + 1] = mapname
				mapnames[#mapnames + 1] = mapname
			end
			end
			if #map == 1 then map = map[1] end
			if #map == 1 then map = map[1] end
		else
		else
			map = p.getMapParams('World', frame)
			map = p.getMapParams('World', frame)
			args[1] = 'World'
			args[1] = 'World'
		end
		end
	end
	end
	if type(map) == 'table' then
	if type(map) == 'table' then
		local outputs = {}
		local outputs = {}
		local oldargs = args[1]
		local oldargs = args[1]
		for k,v in ipairs(map) do
		for k,v in ipairs(map) do
			args[1] = mapnames[k]
			args[1] = mapnames[k]
			outputs[k] = tostring(p.mark(frame, args, v))
			outputs[k] = tostring(p.mark(frame, args, v))
		end
		end
		args[1] = oldargs
		args[1] = oldargs
		return table.concat(outputs, '#PlaceList#') .. '#PlaceList#'
		return table.concat(outputs, '#PlaceList#') .. '#PlaceList#'
	end
	end
	local x, y, longitude, latitude
	local x, y, longitude, latitude
	longitude = decdeg(args.lon_deg, args.lon_min, args.lon_sec, args.lon_dir, args.long, 'longitude')
	longitude = decdeg(args.lon_deg, args.lon_min, args.lon_sec, args.lon_dir, args.long, 'longitude')
	latitude = decdeg(args.lat_deg, args.lat_min, args.lat_sec, args.lat_dir, args.lat, 'latitude')
	latitude = decdeg(args.lat_deg, args.lat_min, args.lat_sec, args.lat_dir, args.lat, 'latitude')
	if args.excludefrom then
	if args.excludefrom then
		-- If this mark is to be excluded from certain maps entirely (useful in the context of multiple maps)
		-- If this mark is to be excluded from certain maps entirely (useful in the context of multiple maps)
		for exclusionmap in mw.text.gsplit(args.excludefrom, '#', true) do
		for exclusionmap in mw.text.gsplit(args.excludefrom, '#', true) do
			-- Check if this map is excluded. If so, return an empty string.
			-- Check if this map is excluded. If so, return an empty string.
			if args[1] == exclusionmap then
			if args[1] == exclusionmap then
				return ''
				return ''
			end
			end
		end
		end
			
			
	end
	end
	local builder = mw.html.create()
	local builder = mw.html.create()
	local currentTitle = mw.title.getCurrentTitle()
	local currentTitle = mw.title.getCurrentTitle()
	if args.coordinates then
	if args.coordinates then
--		Temporarily removed to facilitate infobox conversion. See [[Wikipedia:Coordinates in infoboxes]]
--		Temporarily removed to facilitate infobox conversion. See [[Wikipedia:Coordinates in infoboxes]]
--		if longitude or latitude then
--		if longitude or latitude then
--			error('Coordinates from [[Module:Coordinates]] and individual coordinates cannot both be provided')
--			error('Coordinates from [[Module:Coordinates]] and individual coordinates cannot both be provided')
--		end
--		end
		longitude = coord2text('longitude', args.coordinates)
		longitude = coord2text('longitude', args.coordinates)
		latitude = coord2text('latitude', args.coordinates)
		latitude = coord2text('latitude', args.coordinates)
	elseif not longitude and not latitude and args.useWikidata then
	elseif not longitude and not latitude and args.useWikidata then
		-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't.
		-- If they didn't provide either coordinate, try Wikidata. If they provided one but not the other, don't.
		local entity = mw.wikibase.getEntity()
		local entity = mw.wikibase.getEntity()
		if entity and entity.claims and entity.claims.P625 and entity.claims.P625[1].mainsnak.snaktype == 'value' then
		if entity and entity.claims and entity.claims.P625 and entity.claims.P625[1].mainsnak.snaktype == 'value' then
			local value = entity.claims.P625[1].mainsnak.datavalue.value
			local value = entity.claims.P625[1].mainsnak.datavalue.value
			longitude, latitude = value.longitude, value.latitude
			longitude, latitude = value.longitude, value.latitude
		end
		end
		if args.link and (currentTitle.namespace == 0) then
		if args.link and (currentTitle.namespace == 0) then
			builder:wikitext('[[Category:Location maps with linked markers with coordinates from Wikidata]]')	
			builder:wikitext('[[Category:Location maps with linked markers with coordinates from Wikidata]]')	
		end
		end
	end
	end
	if not longitude then
	if not longitude then
		error('No value was provided for longitude')
		error('No value was provided for longitude')
	elseif not latitude then
	elseif not latitude then
		error('No value was provided for latitude')
		error('No value was provided for latitude')
	end
	end
	if currentTitle.namespace > 0 then
	if currentTitle.namespace > 0 then
		if (not args.lon_deg) ~= (not args.lat_deg) then
		if (not args.lon_deg) ~= (not args.lat_deg) then
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Degrees]]')
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Degrees]]')
		elseif (not args.lon_min) ~= (not args.lat_min) then
		elseif (not args.lon_min) ~= (not args.lat_min) then
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Minutes]]')
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Minutes]]')
		elseif (not args.lon_sec) ~= (not args.lat_sec) then
		elseif (not args.lon_sec) ~= (not args.lat_sec) then
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Seconds]]')
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Seconds]]')
		elseif (not args.lon_dir) ~= (not args.lat_dir) then
		elseif (not args.lon_dir) ~= (not args.lat_dir) then
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Hemisphere]]')
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Hemisphere]]')
		elseif (not args.long) ~= (not args.lat) then
		elseif (not args.long) ~= (not args.lat) then
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Decimal]]')
			builder:wikitext('[[Category:Location maps with different longitude and latitude precisions|Decimal]]')
		end
		end
	end
	end
	if ((tonumber(args.lat_deg) or 0) < 0) and ((tonumber(args.lat_min) or 0) ~= 0 or (tonumber(args.lat_sec) or 0) ~= 0 or (args.lat_dir and args.lat_dir ~='')) then
	if ((tonumber(args.lat_deg) or 0) < 0) and ((tonumber(args.lat_min) or 0) ~= 0 or (tonumber(args.lat_sec) or 0) ~= 0 or (args.lat_dir and args.lat_dir ~='')) then
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
	end
	end
	if ((tonumber(args.lon_deg) or 0) < 0) and ((tonumber(args.lon_min) or 0) ~= 0 or (tonumber(args.lon_sec) or 0) ~= 0 or (args.lon_dir and args.lon_dir ~= '')) then
	if ((tonumber(args.lon_deg) or 0) < 0) and ((tonumber(args.lon_min) or 0) ~= 0 or (tonumber(args.lon_sec) or 0) ~= 0 or (args.lon_dir and args.lon_dir ~= '')) then
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
	end
	end
	if (((tonumber(args.lat_min) or 0) < 0) or ((tonumber(args.lat_sec) or 0) < 0)) then
	if (((tonumber(args.lat_min) or 0) < 0) or ((tonumber(args.lat_sec) or 0) < 0)) then
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
	end
	end
	if (((tonumber(args.lon_min) or 0) < 0) or ((tonumber(args.lon_sec) or 0) < 0)) then
	if (((tonumber(args.lon_min) or 0) < 0) or ((tonumber(args.lon_sec) or 0) < 0)) then
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
		builder:wikitext('[[Category:Location maps with negative degrees and minutes or seconds]]')
	end
	end
	if args.skew or args.lon_shift or args.markhigh then
	if args.skew or args.lon_shift or args.markhigh then
		mw.log('Removed parameter used in invocation.')
		mw.log('Removed parameter used in invocation.')
		local parent = frame:getParent()
		local parent = frame:getParent()
		if parent then
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
			mw.log('Parent is ' .. parent:getTitle())
		end
		end
		mw.logObject(args, 'args')
		mw.logObject(args, 'args')
		if currentTitle.namespace == 0 then
		if currentTitle.namespace == 0 then
			local key = (args.skew and 'skew' or '') ..
			local key = (args.skew and 'skew' or '') ..
						(args.lon_shift and 'lon_shift' or '') ..
						(args.lon_shift and 'lon_shift' or '') ..
						(args.markhigh and 'markhigh' or '')
						(args.markhigh and 'markhigh' or '')
			builder:wikitext('[[Category:Location maps with removed parameters|' .. key ..' ]]')
			builder:wikitext('[[Category:Location maps with removed parameters|' .. key ..' ]]')
		end
		end
	end
	end
	if map('x') ~= '' then
	if map('x') ~= '' then
		x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude })))
		x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude })))
	else
	else
		x = tonumber(getX(longitude, map('left'), map('right')))
		x = tonumber(getX(longitude, map('left'), map('right')))
	end
	end
	if map('y') ~= '' then
	if map('y') ~= '' then
		y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude })))
		y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude })))
	else
	else
		y = tonumber(getY(latitude, map('top'), map('bottom')))
		y = tonumber(getY(latitude, map('top'), map('bottom')))
	end
	end
	if (x < 0 or x > 100 or y < 0 or y > 100) and not args.outside then
	if (x < 0 or x > 100 or y < 0 or y > 100) and not args.outside then
		mw.log('Mark placed outside map boundaries without outside flag set. x = ' .. x .. ', y = ' .. y)
		mw.log('Mark placed outside map boundaries without outside flag set. x = ' .. x .. ', y = ' .. y)
		local parent = frame:getParent()
		local parent = frame:getParent()
		if parent then
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
			mw.log('Parent is ' .. parent:getTitle())
		end
		end
		mw.logObject(args, 'args')
		mw.logObject(args, 'args')
		if currentTitle.namespace == 0 then
		if currentTitle.namespace == 0 then
			local key = currentTitle.prefixedText
			local key = currentTitle.prefixedText
			builder:wikitext('[[Category:Location maps with marks outside map and outside parameter not set|' .. key .. ' ]]')
			builder:wikitext('[[Category:Location maps with marks outside map and outside parameter not set|' .. key .. ' ]]')
		end
		end
	end
	end
	local mark = args.mark or map('mark')
	local mark = args.mark or map('mark')
	if mark == '' then
	if mark == '' then
		mark = 'Red pog.svg'
		mark = 'Red pog.svg'
	end
	end
	local marksize = tonumber(args.marksize) or tonumber(map('marksize')) or 8
	local marksize = tonumber(args.marksize) or tonumber(map('marksize')) or 8
	local imageDiv = markImageDiv(mark, marksize, args.label or mw.title.getCurrentTitle().text, args.link or '', args.alt, args[2])
	local imageDiv = markImageDiv(mark, marksize, args.label or mw.title.getCurrentTitle().text, args.link or '', args.alt, args[2])
	local labelDiv
	local labelDiv
	if args.label and args.position ~= 'none' then
	if args.label and args.position ~= 'none' then
		labelDiv = markLabelDiv(args.label, args.label_size or 91, args.label_width or 6, args.position, args.background, x, marksize)
		labelDiv = markLabelDiv(args.label, args.label_size or 91, args.label_width or 6, args.position, args.background, x, marksize)
	end
	end
	return builder:node(markOuterDiv(x, y, imageDiv, labelDiv))
	return builder:node(markOuterDiv(x, y, imageDiv, labelDiv))
end
end
local function switcherSeparate(s)
local function switcherSeparate(s)
	if s == nil then return {} end
	if s == nil then return {} end
	local retval = {}
	local retval = {}
	for i in string.gmatch(s .. '#', '([^#]*)#') do
	for i in string.gmatch(s .. '#', '([^#]*)#') do
		i = mw.text.trim(i)
		i = mw.text.trim(i)
		retval[#retval + 1] = (i ~= '' and i)
		retval[#retval + 1] = (i ~= '' and i)
	end
	end
	return retval
	return retval
end
end
function p.main(frame, args, map)
function p.main(frame, args, map)
	local caption_list = {}
	local caption_list = {}
	if not args then
	if not args then
		args = getArgs(frame, {wrappers = 'Template:Location map', valueFunc = p.valueFunc})
		args = getArgs(frame, {wrappers = 'Template:Location map', valueFunc = p.valueFunc})
	end
	end
	if args.useWikidata == nil then
	if args.useWikidata == nil then
		args.useWikidata = true
		args.useWikidata = true
	end
	end
	if not map then
	if not map then
		if args[1] then
		if args[1] then
			map = {}
			map = {}
			for mapname in string.gmatch(args[1], '[^#]+') do
			for mapname in string.gmatch(args[1], '[^#]+') do
				map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame)
				map[#map + 1] = p.getMapParams(mw.ustring.gsub(mapname, '^%s*(.-)%s*$', '%1'), frame)
			end
			end
			if args['caption'] then
			if args['caption'] then
				if args['caption'] == "" then
				if args['caption'] == "" then
					while #caption_list < #map do
					while #caption_list < #map do
						caption_list[#caption_list + 1] = args['caption']
						caption_list[#caption_list + 1] = args['caption']
					end
					end
				else
				else
					for caption in mw.text.gsplit(args['caption'], '##', true) do
					for caption in mw.text.gsplit(args['caption'], '##', true) do
						caption_list[#caption_list + 1] = caption
						caption_list[#caption_list + 1] = caption
					end
					end
				end
				end
			end
			end
			if #map == 1 then map = map[1] end
			if #map == 1 then map = map[1] end
		else
		else
			map = p.getMapParams('World', frame)
			map = p.getMapParams('World', frame)
		end
		end
	end
	end
	if type(map) == 'table' then
	if type(map) == 'table' then
		local altmaps = switcherSeparate(args.AlternativeMap)
		local altmaps = switcherSeparate(args.AlternativeMap)
		if #altmaps > #map then
		if #altmaps > #map then
			error(string.format('%d AlternativeMaps were provided, but only %d maps were provided', #altmaps, #map))
			error(string.format('%d AlternativeMaps were provided, but only %d maps were provided', #altmaps, #map))
		end
		end
		local overlays = switcherSeparate(args.overlay_image)
		local overlays = switcherSeparate(args.overlay_image)
		if #overlays > #map then
		if #overlays > #map then
			error(string.format('%d overlay_images were provided, but only %d maps were provided', #overlays, #map))
			error(string.format('%d overlay_images were provided, but only %d maps were provided', #overlays, #map))
		end
		end
		if #caption_list > #map then
		if #caption_list > #map then
			error(string.format('%d captions were provided, but only %d maps were provided', #caption_list, #map))
			error(string.format('%d captions were provided, but only %d maps were provided', #caption_list, #map))
		end
		end
		local outputs = {}
		local outputs = {}
		args.autoSwitcherLabel = true
		args.autoSwitcherLabel = true
		for k,v in ipairs(map) do
		for k,v in ipairs(map) do
			args.AlternativeMap = altmaps[k]
			args.AlternativeMap = altmaps[k]
			args.overlay_image = overlays[k]
			args.overlay_image = overlays[k]
			args.caption = caption_list[k]
			args.caption = caption_list[k]
			outputs[k] = p.main(frame, args, v)
			outputs[k] = p.main(frame, args, v)
		end
		end
		return '<div class="switcher-container">' .. table.concat(outputs) .. '</div>'
		return '<div class="switcher-container">' .. table.concat(outputs) .. '</div>'
	else
	else
		return p.top(frame, args, map) .. tostring( p.mark(frame, args, map) ) .. p.bottom(frame, args, map)
		return p.top(frame, args, map) .. tostring( p.mark(frame, args, map) ) .. p.bottom(frame, args, map)
	end
	end
end
end
return p
return p