模組:Location map
外观
![]() | 此模块被引用於約156,000個頁面。 為了避免造成大規模的影響,所有對此模块的編輯應先於沙盒或測試樣例上測試。 測試後無誤的版本可以一次性地加入此模块中,但是修改前請務必於討論頁發起討論。 模板引用數量會自動更新。 |
用途
這個模塊結合了{{Location map}}和{{Location map~}}等模板。用法請參見模板頁。
追蹤分類
參見
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 not degrees then
if not decimal then
error('没有为' .. direction .. '提供任何值', 2)
end
local retval = tonumber(decimal)
if retval then
return retval
end
error('为' .. direction .. '提供的数值“' .. decimal .. '”是无效的', 2)
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' then
return value
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')
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('Mark placed outside map boundaries without outside flag set. 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 not args[1] then
args[1] = 'World'
end
if not map then
map = p.getMapParams(args[1], frame)
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('Removed parameter markhigh used.')
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('Removed parameter mark' .. v .. 'high used.')
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