跳转到内容

模組:Location map

被永久保护的模块
维基百科,自由的百科全书

这是本页的一个历史版本,由Liangent留言 | 贡献2015年11月14日 (六) 22:34编辑。这可能和当前版本存在着巨大的差异。

require('Module:No globals')

local p = {}

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

function p.getMapParams(map, frame)
	if not map then
		error('要使用的位置图坐标的名称必须指定', 2)
	end
	local moduletitle = mw.title.new('Module:Location map/data/' .. map)
	if not moduletitle then
		error('"' .. map .. '"不是有效的位置图坐标名称', 2)
	elseif moduletitle.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(tostring(mapData[name]), unpack(params)):plain()
			else
				return mapData[name]
			end
		end
	elseif mw.title.new('Template:Location map ' .. map).exists then
		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
	else
		error('找不到指定的位置图坐标,“Module:Location map/data/' .. map .. '”或者“Template:Location map ' .. map .. '”都不存在', 2)
	end
end

function p.data(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	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('为' .. direction .. '提供的数值“' .. degrees .. '”是无效的', 2)
	end
	if minutes and not tonumber(minutes) then
		error('为' .. direction .. '提供的数值“' .. minutes .. '”是无效的', 2)
	end
	if seconds and not tonumber(seconds) then
		error('为' .. direction .. '提供的数值“' .. seconds .. '”是无效的', 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('为' .. direction .. '提供的半球“' .. hemisphere .. '”是无效的', 2)
		end
		decimal = decimal * multiplier
	end
	return decimal
end

function p.top(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local width
	if not args.width then
		width = math.floor((args.default_width or 240) * (tonumber(map('defaultscale')) or 1) + 0.5)
	elseif mw.ustring.sub(args.width, -2) == 'px' then
		width = mw.ustring.sub(args.width, 1, -3)
	else
		width = args.width
	end
	local retval = args.float == 'center' and '<div class="center">' or ''
	if args.caption and 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;width:' .. width .. 'px' .. (args.border ~= 'none' and ';border:1px solid lightgray">' or '">')
	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;width:' .. width .. 'px">'
	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) .. '在' .. map('name'))) .. '的位置]]'
	if args.overlay_image then
		return retval .. '<div style="position:absolute;top:0;left:0">[[File:' .. args.overlay_image .. '|' .. width .. 'px|link=File:' .. image .. ']]</div>'
	else
		return retval
	end
end

-- effectively make removeBlanks false for caption, and true for everything else
-- p.bottom and its callers need to use this
local function 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

function p.bottom(frame, args, map)
	if not args then
		args = getArgs(frame, {frameOnly = true, valueFunc = valueFunc})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local retval = '</div><div ' .. (args.caption and args.caption ~= '' and 'class="thumbcaption">' or 'style="font-size:90%;padding-top:3px">')
		.. (args.caption or ((args.label or mw.title.getCurrentTitle().text) .. ' (' .. map('name') .. ')'))
		.. '</div></div></div>'
	if args.caption_undefined then
		mw.log('使用了已删除的参数caption_undefined。')
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		retval = retval .. '[[Category:可能存在错误的位置图|使用已删除参数的页面]]'
	end
	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, {wrappers = 'Template:Location map+', valueFunc = valueFunc})
	end
	if not map then
		map = p.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, imageDiv, labelDiv)
	return mw.html.create('div')
		:cssText('position:absolute;top:' .. y .. '%;left:' .. x .. '%;height:0;width:0;margin:0;padding:0')
		:node(imageDiv)
		:node(labelDiv)
end

local function markImageDiv(mark, marksize, label, link, alt, title)
	local builder = mw.html.create('div')
		:cssText('position:relative;text-align:center;left:-' .. math.floor(marksize / 2 + 0.5) .. 'px;top:-' .. math.floor(marksize / 2 + 0.5) .. 'px;width:' .. marksize .. 'px;font-size:' .. marksize .. 'px;line-height:0')
		:attr('title', title)
	if marksize ~= 0 then
		builder:wikitext(string.format(
			'[[File:%s|%dx%dpx|%s|link=%s%s]]',
			mark,
			marksize,
			marksize,
			label,
			link,
			alt and ('|alt=' .. alt) or ''
		))
	end
	return builder
end

local function markLabelDiv(label, label_size, label_width, position, background, x)
	local builder = mw.html.create('div')
		:cssText('font-size:' .. label_size .. '%;line-height:110%;position:relative;top:-1.5em;width:' .. label_width .. 'em')
	if position == 'top' then -- specified top
		builder:cssText('top:-2.65em;left:-3em;text-align:center')
	elseif position == 'bottom' then -- specified bottom
		builder:cssText('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
		builder:cssText('left:-6.5em;text-align:right')
	else -- specified right or autodetected to right
		builder:cssText('left:0.5em;text-align:left')
	end
	builder = builder:tag('span')
		:cssText('padding:1px')
		:wikitext(label)
	if background then
		builder:cssText('background-color:' .. background)
	end
	return builder:done()
end

local function getX(longitude, left, right)
	local width = (right - left) % 360
	if width == 0 then
		width = 360
	end
	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
	if distanceFromLeft - width / 2 >= 180 then
		distanceFromLeft = distanceFromLeft - 360
	end
	return 100 * distanceFromLeft / width
end

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

function p.mark(frame, args, map)
	if not args then
		args = getArgs(frame, {wrappers = 'Template:Location map~'})
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local x, y, longitude, latitude
	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')
	if 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.
		local entity = mw.wikibase.getEntity()
		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
			longitude, latitude = value.longitude, value.latitude
		end
	end
	if not longitude then
		error('No value was provided for longitude')
	end
	if not latitude then
		error('No value was provided for latitude')
	end
	local builder = mw.html.create()
	if args.skew or args.lon_shift or args.markhigh then
		mw.log('Removed parameter used in invocation.')
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		builder:wikitext('[[Category:可能存在错误的位置图|使用已删除参数的页面]]')
	end
	if (map('skew') ~= '') or (map('lat_skew') ~= '') or (map('crosses180') ~= '') then
		mw.log('已删除参数用在了位置图里面,地图坐标名称是' .. args[1])
		builder:wikitext('[[Category:可能存在错误的位置图|使用已删除参数的地图]]')
	end
	if map('x') ~= '' then
		x = tonumber(mw.ext.ParserFunctions.expr(map('x', { latitude, longitude })))
	else
		x = tonumber(getX(longitude, map('left'), map('right')))
	end
	if map('y') ~= '' then
		y = tonumber(mw.ext.ParserFunctions.expr(map('y', { latitude, longitude })))
	else
		y = tonumber(getY(latitude, map('top'), map('bottom')))
	end
	if (x < 0 or x > 100 or y < 0 or y > 100) and not args.outside then
		mw.log('地图边界外面的标记没有设置外部标志。x = ' .. x .. ', y = ' .. y)
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		builder:wikitext('[[Category:可能存在错误的位置图|未设置与标记在地图以外的外部标志]]')
	end
	local mark = args.mark or map('mark')
	if mark == '' then
		mark = 'Red pog.svg'
	end
	local imageDiv = 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])
	local labelDiv
	if args.label and args.position ~= 'none' then
		labelDiv = markLabelDiv(args.label, args.label_size or 90, args.label_width or 6, args.position, args.background, x)
	end
	return builder:node(markOuterDiv(x, y, imageDiv, labelDiv))
end

function p.main(frame, args, map)
	if not args then
		args = getArgs(frame, {wrappers = 'Template:Location map', valueFunc = valueFunc})
	end
	if args.useWikidata == nil then
		args.useWikidata = true
	end
	if not map then
		if args[1] then
			map = {}
			for mapname in string.gmatch(args[1], '[^#]+') do
				map[#map + 1] = p.getMapParams(mapname, frame)
			end
			if #map == 1 then map = map[1] end
		else
			map = p.getMapParams('World', frame)
		end
	end
	return p.top(frame, args, map) .. tostring( 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.lat1 or fullArgs.lat,
			long = fullArgs.long1 or fullArgs.long,
			lat_deg = fullArgs.lat1_deg or fullArgs.lat_deg,
			lat_min = fullArgs.lat1_min or fullArgs.lat_min,
			lat_sec = fullArgs.lat1_sec or fullArgs.lat_sec,
			lat_dir = fullArgs.lat1_dir or fullArgs.lat_dir,
			lon_deg = fullArgs.lon1_deg or fullArgs.lon_deg,
			lon_min = fullArgs.lon1_min or fullArgs.lon_min,
			lon_sec = fullArgs.lon1_sec or fullArgs.lon_sec,
			lon_dir = fullArgs.lon1_dir or fullArgs.lon_dir,
			mark = fullArgs.mark1 or fullArgs.mark,
			marksize = fullArgs.mark1size or fullArgs.marksize,
			link = fullArgs.link1 or fullArgs.link,
			label = fullArgs.label1 or fullArgs.label,
			label_size = fullArgs.label1_size or fullArgs.label_size,
			position = fullArgs.position1 or fullArgs.pos1 or fullArgs.position or fullArgs.pos,
			background = fullArgs.background1 or fullArgs.bg1 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'],
			outside = fullArgs['outside' .. n],
			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['position' .. 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, {wrappers = 'Template:Location map many', valueFunc = valueFunc})
	end
	if not args[1] then
		args[1] = 'World'
	end
	if not map then
		map = p.getMapParams(args[1], frame)
	end
	local marks = {}
	local markhigh
	if args.markhigh then
		mw.log('使用已删除参数markhigh。')
		local parent = frame:getParent()
		if parent then
			mw.log('Parent is ' .. parent:getTitle())
		end
		mw.logObject(args, 'args')
		markhigh = true
	end
	for k, v in pairs(args) do -- @todo change to uargs once we have that
		if v then
			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
					table.insert(marks, k)
				end
			end
		end
	end
	table.sort(marks)
	if marks[1] ~= 1 and (args.lat or args.lat_deg) then
		table.insert(marks, 1, 1)
	end
	local body = ''
	for _, v in ipairs(marks) do
		-- don't try to consolidate this into the above loop. ordering of elements from pairs() is unspecified
		body = body .. tostring( p.mark(frame, manyMakeArgs(args, v), map) )
		if args['mark' .. v .. 'high'] then
			mw.log('使用已删除参数mark' .. v .. 'high。')
			local parent = frame:getParent()
			if parent then
				mw.log('Parent is ' .. parent:getTitle())
			end
			mw.logObject(args, 'args')
			markhigh = true
		end
	end
	args.label = nil -- there is no global label
	return p.top(frame, args, map) .. body .. p.bottom(frame, args, map) .. (markhigh and '[[Category:可能存在错误的位置图|使用已删除参数的页面]]' or '')
end

return p