Module:Location map: Difference between revisions

Jump to navigation Jump to search
imported>Jackmcbarn
(handle skew and prevent transclusions of things that don't exist)
imported>Jackmcbarn
(add p.many for Template:Location map many)
Line 240: Line 240:
end
end
return p.top(frame, args, map) .. p.mark(frame, args, map) .. p.bottom(frame, args, map)
return p.top(frame, args, map) .. p.mark(frame, args, map) .. p.bottom(frame, args, map)
end
local function manyMakeArgs(fullArgs, n)
if n == 1 then
return {
fullArgs[1],
lat = fullArgs['lat' .. n] or fullArgs.lat,
long = fullArgs['long' .. n] or fullArgs.long,
lat_deg = fullArgs['lat' .. n .. '_deg'] or fullArgs.lat_deg,
lat_min = fullArgs['lat' .. n .. '_min'] or fullArgs.lat_min,
lat_sec = fullArgs['lat' .. n .. '_sec'] or fullArgs.lat_sec,
lat_dir = fullArgs['lat' .. n .. '_dir'] or fullArgs.lat_dir,
lon_deg = fullArgs['lon' .. n .. '_deg'] or fullArgs.lon_deg,
lon_min = fullArgs['lon' .. n .. '_min'] or fullArgs.lon_min,
lon_sec = fullArgs['lon' .. n .. '_sec'] or fullArgs.lon_sec,
lon_dir = fullArgs['lon' .. n .. '_dir'] or fullArgs.lon_dir,
mark = fullArgs['mark' .. n] or fullArgs.mark,
marksize = fullArgs['mark' .. n .. 'size'] or fullArgs.marksize,
link = fullArgs['link' .. n] or fullArgs.link,
label = fullArgs['label' .. n] or fullArgs.label,
label_size = fullArgs['label' .. n .. '_size'] or fullArgs.label_size,
position = fullArgs['position' .. n] or fullArgs['pos' .. n] or fullArgs.position or fullArgs.pos,
background = fullArgs['background' .. n] or fullArgs['bg' .. n] or fullArgs.background or fullArgs.bg
}
else
return {
fullArgs[1],
lat = fullArgs['lat' .. n],
long = fullArgs['long' .. n],
lat_deg = fullArgs['lat' .. n .. '_deg'],
lat_min = fullArgs['lat' .. n .. '_min'],
lat_sec = fullArgs['lat' .. n .. '_sec'],
lat_dir = fullArgs['lat' .. n .. '_dir'],
lon_deg = fullArgs['lon' .. n .. '_deg'],
lon_min = fullArgs['lon' .. n .. '_min'],
lon_sec = fullArgs['lon' .. n .. '_sec'],
lon_dir = fullArgs['lon' .. n .. '_dir'],
mark = fullArgs['mark' .. n],
marksize = fullArgs['mark' .. n .. 'size'],
link = fullArgs['link' .. n],
label = fullArgs['label' .. n],
label_size = fullArgs['label' .. n .. '_size'],
position = fullArgs['postition' .. n] or fullArgs['pos' .. n],
background = fullArgs['background' .. n] or fullArgs['bg' .. n]
}
end
end
function p.many(frame, args, map)
if not args then
args = getArgs(frame)
end
if not args[1] then
args[1] = 'World'
end
if not map then
map = getMapParams(args[1], frame)
end
local marks = { args.lat or args.lat_deg }
local markhigh = args.markhigh
for k, _ in pairs(args) do -- @todo change to uargs once we have that
if string.sub(k, -4) == '_deg' then
k = string.sub(k, 1, -5)
end
if string.sub(k, 1, 3) == 'lat' then
k = tonumber(string.sub(k, 4))
if k then
marks[k] = true
if args['mark' .. k .. 'high'] then
markhigh = true
end
end
end
end
local body = ''
for k, _ in ipairs(marks) do
-- don't try to consolidate this into the above loop. ordering of elements from pairs() is unspecified
body = body .. p.mark(frame, manyMakeArgs(args, k), map)
end
args.label = nil -- there is no global label
return p.top(frame, args, map) .. body .. p.bottom(frame, args, map) .. (markhigh and '[[Category:Location map many using markhigh parameter]]' or '')
end
end


return p
return p

Revision as of 02:27, 10 April 2014

Documentation for this module may be created at Module:Location map/doc

local p = {}

local getArgs = require('Module:Arguments').getArgs

local function round(n, digits)
	local mult = math.pow(10, digits)
	return math.floor(n * mult + 0.5) / mult
end

local function getMapParams(map, frame)
	if mw.title.new('Module:Location map/data/' .. map).exists then
		local mapData = mw.loadData('Module:Location map/data/' .. map)
		return function(name, params)
			if mapData[name] == nil then
				return ''
			elseif params then
				return mw.message.newRawMessage(mapData[name], unpack(params)):plain()
			else
				return mapData[name]
			end
		end
	else
		local cache = {}
		return function(name, params)
			if params then
				return frame:expandTemplate{title = 'Location map ' .. map, args = { name, unpack(params) }}
			else
				if cache[name] == nil then
					cache[name] = frame:expandTemplate{title = 'Location map ' .. map, args = { name }}
				end
				return cache[name]
			end
		end
	end
end

local function decdeg(degrees, minutes, seconds, hemisphere, digits, decimal)
	if degrees == nil then
		return tonumber(decimal)
	end
	decimal = (degrees or 0) + (minutes or 0)/60 + (seconds or 0)/3600
	if hemisphere == 'W' or hemisphere == 'w' or hemisphere == 'S' or  hemisphere == 's' then
		decimal = -decimal
	end
	if digits == nil then
		digits = 7
	end
	return round(decimal, digits)
end

function p.top(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	local width
	if args.width then
		width = mw.ustring.gsub(args.width, 'px', '')
	else
		width = round((args.default_width or 240) * (tonumber(map('defaultscale')) or 1), 0)
	end
	local retval = ''
	if args.float == 'center' then
		retval = retval .. '<div class="center">'
	end
	if args.caption then
		retval = retval .. '<div class="thumb '
		if args.float == '"left"' or args.float == 'left' then
			retval = retval .. 'tleft'
		elseif args.float == '"center"' or args.float == 'center' or args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'tnone'
		else
			retval = retval .. 'tright'
		end
		retval = retval .. '"><div class="thumbinner" style="width:' .. (width + 2) .. 'px; '
		if args.border == 'none' then
			retval = retval .. 'border: none;'
		elseif args.border then
			retval = retval .. 'border-color:' .. args.border .. ';'
		end
		retval = retval .. '"><div style="position: relative; '
		if args.border ~= 'none' then
			retval = retval .. 'border: 1px solid lightgray'
		end
		retval = retval .. '">'
	else
		retval = retval .. '<div style="width:' .. width .. 'px; '
		if args.float == '"left"' or args.float == 'left' then
			retval = retval .. 'float: left; clear: left'
		elseif args.float == '"center"' or args.float == 'center' then
			retval = retval .. 'float: none; clear: both; margin-left: auto; margin-right: auto'
		elseif args.float == '"none"' or args.float == 'none' then
			retval = retval .. 'float: none; clear: none'
		else
			retval = retval .. 'float: right; clear: right'
		end
		retval = retval .. '"><div style="width:' .. width .. 'px; padding:0"><div style="position: relative; ">'
	end
	local image
	if args.AlternativeMap then
		image = args.AlternativeMap
	elseif args.relief and map('image1') ~= '' then
		image = map('image1')
	else
		image = map('image')
	end
	retval = retval .. '[[File:' .. image .. '|' .. width .. 'px|' .. (args.alt or ((args.label or mw.title.getCurrentTitle().text) .. ' is located in ' .. map('name'))) .. ']]'
	if args.overlay_image then
		retval = retval .. '<div style="position:absolute; top: 0; left: 0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|link=File:' .. image .. ']]</div>'
	end
	return retval
end

function p.bottom(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	local retval = ''
	retval = retval .. '</div><div ' .. (args.caption and 'class="thumbcaption"' or 'style="font-size: 90%; padding-top:3px"') .. '>'
	local caption = frame.args.caption or frame:getParent().args.caption
	if caption and not args.caption_undefined then
		retval = retval .. mw.text.trim(caption)
	else
		retval = retval .. (args.label or mw.title.getCurrentTitle().text) .. ' (' .. map('name') .. ')'
	end
	retval = retval .. '</div></div></div>'
	if args.float == 'center' then
		retval = retval .. '</div>'
	end
	return retval
end

function p.container(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	return p.top(frame, args, map) .. (args.places or '') .. p.bottom(frame, args, map)
end

local function markOuterDiv(x, y, content)
	return '<div style="position:absolute;top:' .. y .. '%;left:' .. x .. '%;height:0;width:0;margin:0;padding:0">' .. content .. '</div>'
end

local function markImageDiv(mark, marksize, label, link, alt, title)
	local retval = '<div style="position:relative;text-align:center;left:-' .. round(marksize / 2, 0) .. 'px;top:-' .. round(marksize / 2, 0) .. 'px;width:' .. marksize .. 'px;font-size:' .. marksize .. 'px;line-height:0"'
	if title then
		retval = retval .. ' title="' .. title .. '"'
	end
	retval = retval .. '>'
	if marksize ~= 0 then
		retval = retval .. '[[File:' .. mark .. '|' .. marksize .. 'x' .. marksize .. 'px|' .. label .. '|link=' .. link
		if alt then
			retval = retval .. '|alt=' .. alt
		end
		retval = retval .. ']]'
	end
	return retval .. '</div>'
end

local function markLabelDiv(label, label_size, label_width, position, background, x)
	local retval = '<div style="font-size:' .. label_size .. '%;line-height:110%;position:relative;top:-1.5em;width:' .. label_width .. 'em;'
	if position == 'top' then -- specified top
		retval = retval .. 'top:-2.65em;left:-3em;text-align:center'
	elseif position == 'bottom' then -- specified bottom
		retval = retval .. 'top:-0.15em;left:-3em;text-align:center'
	elseif position == 'left' or (tonumber(x) > 70 and position ~= 'right') then -- specified left or autodetected to left
		retval = retval .. 'left:-6.5em;text-align:right'
	else -- specified right or autodetected to right
		retval = retval .. 'left:0.5em;text-align:left'
	end
	retval = retval .. '"><span style="padding:1px'
	if background then
		retval = retval .. ';background-color:' .. background
	end
	return retval .. '">' .. label .. '</span></div>'
end

local function getX(longitude, latitude, lon_dir, left, right, top, bottom, crosses180, skew, lon_shift)
	local crosses180_correction = crosses180 and lon_dir == 'W' and (-36000/(left - right)) or 0
	if skew then
		local lat_ratio = (top - latitude)/(top - bottom)
		local skew_factor = (1 - skew) * lat_ratio + skew
		longitude = (longitude - 0.5 * (right + left) + lon_shift) * skew_factor + 0.5 * (right + left) - lon_shift
	end
	return round(crosses180_correction + 100 * (longitude - left) / (right - left), 1)
end

local function getY(latitude, top, bottom)
	return round(100 * (top - latitude) / (top - bottom), 1)
end

function p.mark(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	local x, y, longitude, latitide
	longitude = decdeg(args.lon_deg, args.lon_min, args.lon_sec, args.lon_dir, nil, args.long)
	latitude = decdeg(args.lat_deg, args.lat_min, args.lat_sec, args.lat_dir, nil, args.lat)
	if map('x') ~= '' then
		x = frame:callParserFunction('#expr', map('x', { latitude, longitude }))
	else
		x = getX(longitude, latitude, args.lon_dir, map('left'), map('right'), map('top'), map('bottom'), map('crosses180') ~= '', tonumber(args.skew or map('skew')), args.lon_shift or 0)
	end
	if map('y') ~= '' then
		y = frame:callParserFunction('#expr', map('y', { latitude, longitude }))
	else
		y = getY(latitude, map('top'), map('bottom'))
	end
	local mark = args.mark or map('mark')
	if mark == '' then
		mark = 'Red pog.svg'
	end
	local divContent = markImageDiv(mark, tonumber(args.marksize) or tonumber(map('marksize')) or 8, args.label or mw.title.getCurrentTitle().text, args.link or '', args.alt, args[2])
	if args.label and args.position ~= 'none' then
		divContent = divContent .. markLabelDiv(args.label, args.label_size or 90, args.label_width or 6, args.position, args.background, x)
	end
	return markOuterDiv(x, y, divContent)
end

function p.main(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not args[1] then
		args[1] = 'World'
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	return p.top(frame, args, map) .. p.mark(frame, args, map) .. p.bottom(frame, args, map)
end

local function manyMakeArgs(fullArgs, n)
	if n == 1 then
		return {
			fullArgs[1],
			lat = fullArgs['lat' .. n] or fullArgs.lat,
			long = fullArgs['long' .. n] or fullArgs.long,
			lat_deg = fullArgs['lat' .. n .. '_deg'] or fullArgs.lat_deg,
			lat_min = fullArgs['lat' .. n .. '_min'] or fullArgs.lat_min,
			lat_sec = fullArgs['lat' .. n .. '_sec'] or fullArgs.lat_sec,
			lat_dir = fullArgs['lat' .. n .. '_dir'] or fullArgs.lat_dir,
			lon_deg = fullArgs['lon' .. n .. '_deg'] or fullArgs.lon_deg,
			lon_min = fullArgs['lon' .. n .. '_min'] or fullArgs.lon_min,
			lon_sec = fullArgs['lon' .. n .. '_sec'] or fullArgs.lon_sec,
			lon_dir = fullArgs['lon' .. n .. '_dir'] or fullArgs.lon_dir,
			mark = fullArgs['mark' .. n] or fullArgs.mark,
			marksize = fullArgs['mark' .. n .. 'size'] or fullArgs.marksize,
			link = fullArgs['link' .. n] or fullArgs.link,
			label = fullArgs['label' .. n] or fullArgs.label,
			label_size = fullArgs['label' .. n .. '_size'] or fullArgs.label_size,
			position = fullArgs['position' .. n] or fullArgs['pos' .. n] or fullArgs.position or fullArgs.pos,
			background = fullArgs['background' .. n] or fullArgs['bg' .. n] or fullArgs.background or fullArgs.bg
		}
	else
		return {
			fullArgs[1],
			lat = fullArgs['lat' .. n],
			long = fullArgs['long' .. n],
			lat_deg = fullArgs['lat' .. n .. '_deg'],
			lat_min = fullArgs['lat' .. n .. '_min'],
			lat_sec = fullArgs['lat' .. n .. '_sec'],
			lat_dir = fullArgs['lat' .. n .. '_dir'],
			lon_deg = fullArgs['lon' .. n .. '_deg'],
			lon_min = fullArgs['lon' .. n .. '_min'],
			lon_sec = fullArgs['lon' .. n .. '_sec'],
			lon_dir = fullArgs['lon' .. n .. '_dir'],
			mark = fullArgs['mark' .. n],
			marksize = fullArgs['mark' .. n .. 'size'],
			link = fullArgs['link' .. n],
			label = fullArgs['label' .. n],
			label_size = fullArgs['label' .. n .. '_size'],
			position = fullArgs['postition' .. n] or fullArgs['pos' .. n],
			background = fullArgs['background' .. n] or fullArgs['bg' .. n]
		}
	end
end

function p.many(frame, args, map)
	if not args then
		args = getArgs(frame)
	end
	if not args[1] then
		args[1] = 'World'
	end
	if not map then
		map = getMapParams(args[1], frame)
	end
	local marks = { args.lat or args.lat_deg }
	local markhigh = args.markhigh
	for k, _ in pairs(args) do -- @todo change to uargs once we have that
		if string.sub(k, -4) == '_deg' then
			k = string.sub(k, 1, -5)
		end
		if string.sub(k, 1, 3) == 'lat' then
			k = tonumber(string.sub(k, 4))
			if k then
				marks[k] = true
				if args['mark' .. k .. 'high'] then
					markhigh = true
				end
			end
		end
	end
	local body = ''
	for k, _ in ipairs(marks) do
		-- don't try to consolidate this into the above loop. ordering of elements from pairs() is unspecified
		body = body .. p.mark(frame, manyMakeArgs(args, k), map)
	end
	args.label = nil -- there is no global label
	return p.top(frame, args, map) .. body .. p.bottom(frame, args, map) .. (markhigh and '[[Category:Location map many using markhigh parameter]]' or '')
end

return p