模块:RouteSequence
此模块被引用于约14,000个页面。 为了避免造成大规模的影响,所有对此模块的编辑应先于沙盒或测试样例上测试。 测试后无误的版本可以一次性地加入此模块中,但是修改前请务必于讨论页发起讨论。 模板引用数量会自动更新。 |
本模块为方便填写{{铁道路线}}模板中的线路图而设计。
基本用法
站名1 ~ 站名2 ~ 站名3前缀#站名3#站名3后缀 ~ 站名4条目名!站名4
例如,代码
{{铁道路线|lua=1 |模板名称=昆明地铁6号线 |名称=昆明地铁6号线 |代表色=#2c8195 |铁道系统 = 昆明地铁 |Logo= |车站列表= (预留延伸)#东部汽车站~#大板桥##(未开通)~机场前~机场中心站!长水国际机场 }}
将显示为
站名的转换规则如下:
- 如果给出“铁道系统”参数,将依照“Module:Adjacent stations/{{{铁道系统}}}”中“stationNames”表的链接标题和条目名称转换站名,此为最高优先级。如果未定义数据模块,请勿使用该参数;
- 如果已经给出条目名,模板优先按照条目名进行显示,而不进行其他的判断;
- 如果给出的站名末尾为不包含“站”字,模板将自动追加这个“站”字;
- 如果给出的站名末尾为“站”,在最终显示的模板中,这个“站”字将被省略。因而,如果站名中本身末尾为“站”字,那么需要在末尾写连续两个“站”。例如,如果需要显示“城站”,那么提供给模板的站名应该是完整的条目名“城站站”;
- 如果给出的站名为消歧义,例如“客运中心站 (杭州地铁)”,将创建形如“[[客运中心站 (杭州地铁)|客运中心]]”的链接。
用于标识前缀/后缀的“#”如果单用,表示前/后缀与站名链接间不加空格。如果连用,即“##”,表示前/后缀与站名链接间加入空格,即“ ”。
站名样式
现支持两种自定义站名样式的方法。
采用style标记
Style标记给出一个CSS样式,此后的元素将加入这一样式。如果样式留空,则恢复默认样式。例如:
盛莫路 ~ 东环南路 ~ style=color:gray ~ 邱隘东站 ~ 五乡 ~ 宝幢站 (地铁) ~ style= ~ 邬隘站 (地铁)
将会输出
对于未开通站点,常用的方法为将站名变灰。为此,模块提供了简易的方式进行书写,例如上面的例子可以改写为
盛莫路 ~ 东环南路 ~ gstart ~ 邱隘站 (地铁) ~ 五乡 ~ 宝幢站 (地铁) ~ gend ~ 邬隘站 (地铁)
另一种方法为将站名变斜体,类似的建议写法如下,即将“gstart/gend”替换为“istart/iend”:
盛莫路 ~ 东环南路 ~ istart ~ 邱隘站 (地铁) ~ 五乡 ~ 宝幢站 (地铁) ~ iend ~ 邬隘站 (地铁)
单站标注
现支持标注单个车站的字体为斜体、粗体,增加删除线以及依照原始格式输出,例子见下方。
翔宇路北 ~ '''翔宇路南''' ~ %(支线:[[禄口机场站|禄口机场]])% ~ ''铜山站 (南京)'' ~ '''s石湫s''' ~ (g明觉g)
将会输出
除“%”外,多个标注可重复使用。
自定义分隔符
在线路定义中加入“sep=分隔符”可替换短横线为自定义分隔符,使用“sep=”可消除该设置。例如,
''北新桥'' ~ 东直门 ~ 三元桥 ~ sep=→ ~ 3号航站楼 ~ 2号航站楼 ~ 返回#三元桥
将输出
为了简化常用分隔符的输入,下列箭头已被预先定义,输入标识符即可使用。
标识符 | 符号 | 说明 |
---|---|---|
%rarrow% | → | 右箭头 |
%larrow% | ← | 左箭头 |
%darrow% | ↔ | 双向箭头 |
%rdarrow% | ⇒ | 双线右箭头 |
%ldarrow% | ⇐ | 双线左箭头 |
%ddarrow% | ⇔ | 双线双向箭头 |
因而,上述线路也可改写为
''北新桥'' ~ 东直门 ~ 三元桥 ~ sep=%rarrow% ~ 3号航站楼 ~ 2号航站楼 ~ 返回#三元桥
条件输出
在某些情况下,我们可能会希望模板在不同条件下显示不同车站,例如需要控制模板显示已运营车站还是全线所有车站。模块支持引入控制条件以显示不同的内容。
条件控制采用con标记,语法为“con=条件1#-条件2”,表示当条件1满足,或者条件2不满足时,显示con标记后的内容。下面的例子给出了使用该条件的一个模板:
{{铁道路线|lua=1 | 模板名称 = 上海轨道交通13号线车站 | 名称 = [[上海轨道交通13号线]] | 代表色 = {{上海地铁标志色|13|s}} | Logo = [[File:Shanghai Metro logo.svg|20px|上海地铁标志]] | 显示条件 = {{{2|}}} | 车站列表 = con=-SB ~ 金运路 ~ 金沙江西路 ~ 丰庄 ~ 祁连山南路 ~ 真北路 ~ 大渡河路 ~ 金沙江路 ~ 隆德路 ~ 武宁路 ~ 长寿路站 (上海) ~ con=F ~ style=color:#888 ~ 江宁路 ~ 汉中路 ~ 自然博物馆 ~ 南京西路 ~ 淮海中路 ~ 一大会址·新天地 ~ con=F#SB ~ 马当路 ~ 世博会博物馆 ~ 世博大道 ~ con=F ~ 长清路 ~ 成山路 ~ 东明路站 (上海) ~ 华鹏路 ~ 下南路 ~ 北蔡 ~ 陈春路 ~ 莲溪路 ~ 华夏中路 ~ 中科路 ~ 学林路 ~ 张江路 }}
如果需要显示上海轨道交通13号线世博专线的内容,只需要将参数2填写为SB,此时模板将隐藏金运路站至新天地站以及长清路至张江路之间的车站:
如果需要显示2014年年底运营的线路,只需要保留参数2为空,此时江宁路至张江路的车站将不会显示:
如果需要显示整条线路,只需要将参数2置为F:
支线
线路中,“blstart”、“blline”、“blend”三个标记表示支线开始、下一支线以及支线结束,可方便地书写较短的支线线路。如
blstart ~ ([[杭州至绍兴城际铁路|杭绍城际]]←)#中国轻纺城 ~ 瓜渚湖 ~ 镜水湖 ~ blline ~ 会展中心站 (绍兴市)!会展中心 ~ 绍兴北站 (地铁)!绍兴北站 ~ 大庆寺 ~ 高教园区 ~ 群贤路 ~ blend ~ 站前大道 ~ 绿云路 ~ 奥体中心站 (绍兴市)
将会输出
模块会根据 blstart 左侧是否有其他车站决定支线文字的对齐方向和分隔符。相关情形列举如下
情形 | 对齐 | 分隔符变更 |
---|---|---|
左侧无站 | 右对齐 | 右侧车站前分隔符改为“>” |
右侧无站 | 左对齐 | 左侧车站前分隔符改为“<” |
两侧均有站 | 左对齐 | 左、右侧车站前分隔符分别改为“<”和“>” |
在两侧均有站的情况下,可能需要使站名居中以使得显示更为美观。此时,可通过“blstart=center”手动设置居中。如
乔司站 (铁路) ~ 笕桥 ~ blstart=center ~ 杭州东 ~ 盈宁 ~ 杭州南 ~ blline ~ 艮山门 ~ 杭州 ~ 南星桥站 (中国铁路)!南星桥 ~ 钱塘江 ~ 萧山西 ~ blend ~ 萧山 ~ 湄池 ~ 诸暨东
将会输出
换行
使用 line 标记可以换行。例如,
沪昆线:#杭州东 ~ 盈宁 ~ 杭州南 ~ line ~ 绕行线:#艮山门 ~ 杭州 ~ 南星桥站 (中国铁路)!南星桥 ~ 钱塘江 ~ 萧山西
将输出
对于支线,使用 line 标记时,模块将结束支线模式,同时不会输出“>”符号。
测试用例
本模块使用Module:UnitTests提供集成测试,测试代码见Module:RouteSequence/testcases。
local p = {
STATION_SEPARATOR = ' – ',
DEFAULT_STATION_SUFFIX = '站',
BLOCK_ALIGN_PATTERN = '##BLOCK_ALIGN##',
BLOCK_START = ' < <div style="display:inline-block; text-align: ##BLOCK_ALIGN##; vertical-align:middle;">',
BLOCK_START_PURE = '<div style="display:inline-block; text-align: ##BLOCK_ALIGN##; vertical-align:middle;">',
BLOCK_LINE_SEPARATOR = '<br />',
BLOCK_END = '</div> > ',
BLOCK_END_PURE = '</div>',
GRAY_COLOR = '#717171'
}
p.sep_translate = {
['%%dash%%'] = '–',
['%%rarrow%%'] = '→',
['%%larrow%%'] = '←',
['%%darrow%%'] = '↔',
['%%ldarrow%%'] = '⇐',
['%%rdarrow%%'] = '⇒',
['%%ddarrow%%'] = '⇔'
}
p.station_suffix = p.DEFAULT_STATION_SUFFIX
function p.truncateEnds(s, l)
if l == nil then l = 1 end
return string.sub(s, l + 1, string.len(s) - l)
end
function p.appendStyle(s, a)
s = mw.text.trim(s)
if s == '' then
return a
elseif string.find(s, ';$') ~= nil then
return s .. ' ' .. a
else
return s .. '; ' .. a
end
end
function p.loadSystemStationData(system, station_link)
local status
local system_data
local adj_result
if string.sub(system, 1, 3) == 'AS:' then
-- legacy AS-coersion code
system = string.sub(system, 4)
end
adj_result = require('Module:Adjacent stations')._station({
system = system,
station = station_link,
line = nil,
type = nil,
}, {})
return string.sub(adj_result, 3, string.len(adj_result) - 2)
end
function p.marshalStation(station_link, style, system, frame)
local station_name
local station_data = nil
function marshal(station_link, station_name, style)
if style == nil or mw.text.trim(style) == '' then
return '[[' .. station_link .. '|' .. station_name .. ']]'
else
return '[[' .. station_link .. '|<span style="' .. style .. '">' .. station_name .. '</span>]]'
end
end
if system ~= nil and mw.text.trim(system) ~= '' and not string.find(station_link, "!") then
station_data = p.loadSystemStationData(system, station_link)
end
if station_data ~= nil then
local i, datum
local repr = ''
local station_parts
if type(station_data) ~= 'table' then
station_data = {station_data, }
end
for i, datum in ipairs(station_data) do
if string.find(datum, '|', 1, true) == nil or string.find(datum, '[[', 1, true) ~= nil then
repr = repr .. datum
else
station_parts = mw.text.split(datum, '|')
station_link = station_parts[1]
station_name = table.concat(station_parts, '|', 2)
if frame ~= nil and string.find(station_name, '{', 1, true) ~= nil then
station_name = frame:preprocess(table.concat(station_parts, '|', 2))
end
repr = repr .. marshal(station_link, station_name, style)
end
end
return repr
else
local link_name_split = mw.text.split(station_link, '!')
if table.getn(link_name_split) == 1 then
station_name, match = string.gsub(station_link, p.station_suffix .. ' +%(.+%)$', '')
if match == 0 then
station_name, match = string.gsub(station_link, p.station_suffix .. '$', '')
end
if match == 0 then
station_name = station_link
station_link = station_link .. p.station_suffix
end
else
station_link = link_name_split[1]
station_name = link_name_split[2]
end
return marshal(station_link, station_name, style)
end
end
function p.parseLink(station_link, style, system, frame)
local station_name, match
local prefix, suffix = '', ''
local link_name_split
if string.find(station_link, "^%%.+%%$") ~= nil then
return p.renderPlain(p.truncateEnds(station_link), style, system, frame)
end
local has_decorator = true
while has_decorator do
local prefix_patterns = {
["^s.+s$"] = { prefix='<s>', suffix='</s>', len=1 },
["^'.+'$"] = { prefix="'", suffix="'", len=1 },
["^%(.+%)$"] = { prefix='(', suffix=')', len=1 },
["^(.+)$"] = { prefix='(', suffix=')', len=1 },
}
has_decorator = false
if string.find(station_link, "^g.+g$") ~= nil then
station_link = p.truncateEnds(station_link)
style = p.appendStyle(style, 'color:' .. p.GRAY_COLOR)
has_decorator = true
end
for patt, ps in pairs(prefix_patterns) do
if string.find(station_link, patt) ~= nil then
station_link = p.truncateEnds(station_link, ps.len)
prefix = ps.prefix .. prefix
suffix = suffix .. ps.suffix
has_decorator = true
end
end
end
return prefix .. p.marshalStation(station_link, style, system, frame) .. suffix
end
function p.renderPlain(text, style, system, frame)
local spos, epos, station_name = 1, 1, nil
local out_text = ''
local pos = 1
while spos ~= nil do
spos, epos, station_name = string.find(text, '%$([^%$]+)%$', pos)
if spos ~= nil then
out_text = out_text .. string.sub(text, pos, spos - 1) .. p.parseLink(station_name, style, system, frame)
pos = epos + 1
else
out_text = out_text .. string.sub(text, pos, string.len(text))
end
end
return out_text
end
function p.splitStationExpr(station)
local new_split = {}
local separators = {}
local last_element, cur_element
local cur_separator = ''
local station_split = mw.text.split(station, '#')
for i, spart in ipairs(station_split) do
if i > 1 and (string.find(last_element, '&$') ~= nil -- process html escapes, i.e., ` `
or string.find(last_element, ': *$') ~= nil -- process CSS color, i.e., `color: #cccccc`
or string.find(last_element, '= *$') ~= nil -- process old-fashioned property without quotes, i.e., `<font color=#cccccc>`
or string.find(last_element, '= *\" *$') ~= nil -- process old-fashioned property with quotes, i.e., `<font color="#cccccc">`
) then
table.remove(new_split)
table.remove(separators)
cur_element = last_element .. '#' .. spart
elseif i > 1 and spart == '' then
table.remove(new_split)
table.remove(separators)
cur_separator = ' '
cur_element = last_element
else
cur_element = spart
end
table.insert(new_split, cur_element)
table.insert(separators, cur_separator)
cur_separator = ''
last_element = cur_element
end
return new_split, separators
end
function p.parseStation(station, style, system, frame)
local station_str, station_link
local station_prefix, station_suffix
local station_split, station_seps = p.splitStationExpr(station, '#')
if table.getn(station_split) == 1 then
station_prefix = ''
station_expr = mw.text.trim(station_split[1])
station_suffix = ''
elseif table.getn(station_split) == 2 then
station_prefix = mw.text.trim(station_split[1]) .. station_seps[1]
station_expr = mw.text.trim(station_split[2])
station_suffix = ''
elseif table.getn(station_split) == 3 then
if mw.text.trim(station_split[1]) ~= '' then
station_prefix = mw.text.trim(station_split[1]) .. station_seps[1]
else
station_prefix = ''
end
station_expr = mw.text.trim(station_split[2])
station_suffix = station_seps[2] .. mw.text.trim(station_split[3])
end
if station_prefix ~= '' then
station_prefix = p.renderPlain(station_prefix, style, system, frame)
end
if station_suffix ~= '' then
station_suffix = p.renderPlain(station_suffix, style, system, frame)
end
station_link = p.parseLink(mw.text.trim(station_expr), style, system, frame)
station_str = station_prefix .. station_link .. station_suffix
if station_prefix ~= '' or station_suffix ~= '' then
station_str = '<span class="nowrap">' .. station_str .. '</span>'
end
return station_str
end
function p.processRoute(route_str, gstyle, condition, system, frame)
if gstyle == nil then
gstyle = ''
end
if condition == nil then
condition = ''
end
local style = gstyle
local out_style = gstyle
local stations = mw.text.split(route_str, '~')
local output_strs = {}
local condition = mw.text.trim(condition)
local cond_expr, conds, cond, raw_cond
local cond_met = true
local gray_state, italic_state = false, false
local block_level = 0
local block_align
local station_index = 0
local bl_mark
local separator
local next_separator = p.STATION_SEPARATOR
local user_separator = p.STATION_SEPARATOR
function endRouteLine()
local i
-- change all BLOCK_END at ends with BLOCK_END_PURE
i = table.getn(output_strs)
while i > 0 and output_strs[i] == p.BLOCK_END do
output_strs[i] = p.BLOCK_END_PURE
i = i - 1
end
-- make sure all blocks ended
while block_level > 0 do
block_level = block_level - 1
table.insert(output_strs, p.BLOCK_END_PURE)
end
end
for i, station in ipairs(stations) do
station = mw.text.trim(station)
if string.find(station, '^con *=') ~= nil then
cond_expr = mw.text.trim(mw.text.split(station, '=')[2])
if cond_expr == '' then
cond_met = true
else
conds = mw.text.split(cond_expr, '#')
cond_met = false
for i, cond in ipairs(conds) do
raw_cond = mw.text.trim(cond)
if string.find(raw_cond, '^- *') ~= nil then
raw_cond = string.gsub(raw_cond, '^- *', '')
if mw.text.trim(raw_cond) ~= condition then
cond_met = true
break
end
else
if mw.text.trim(raw_cond) == condition then
cond_met = true
break
end
end
end
end
elseif cond_met then
if string.find(station, '^style *=') ~= nil then
style = mw.text.trim(mw.text.split(station, '=')[2])
if style == '' then
style = gstyle
end
elseif string.find(station, '^sep *=') ~= nil then
separator = mw.text.trim(mw.text.split(station, '=')[2])
if separator == '' then
separator = p.STATION_SEPARATOR
else
separator = ' ' .. separator .. ' '
for spatt, sep in pairs(p.sep_translate) do
separator = string.gsub(separator, spatt, sep)
end
end
next_separator = separator
user_separator = separator
elseif station == 'blstart' then
block_level = block_level + 1
if station_index > 0 then
bl_mark = string.gsub(p.BLOCK_START, p.BLOCK_ALIGN_PATTERN, 'left')
else
bl_mark = string.gsub(p.BLOCK_START_PURE, p.BLOCK_ALIGN_PATTERN, 'right')
end
table.insert(output_strs, bl_mark)
next_separator = ''
elseif string.find(station, '^blstart *=') ~= nil then
block_level = block_level + 1
block_align = mw.text.trim(mw.text.split(station, '=')[2])
if station_index > 0 then
bl_mark = string.gsub(p.BLOCK_START, p.BLOCK_ALIGN_PATTERN, block_align)
else
bl_mark = string.gsub(p.BLOCK_START_PURE, p.BLOCK_ALIGN_PATTERN, block_align)
end
table.insert(output_strs, bl_mark)
next_separator = ''
elseif station == 'blline' then
next_separator = p.BLOCK_LINE_SEPARATOR
elseif station == 'blend' then
block_level = block_level - 1
table.insert(output_strs, p.BLOCK_END)
next_separator = ''
elseif station == 'gstart' then
gray_state = true
elseif station == 'gend' then
gray_state = false
elseif station == 'istart' then
italic_state = true
elseif station == 'iend' then
italic_state = false
elseif station == 'line' then
endRouteLine()
next_separator = '<br />'
else
station_index = station_index + 1
if station_index > 1 or block_level > 0 then
table.insert(output_strs, next_separator)
end
out_style = style
if gray_state then
out_style = p.appendStyle(style, 'color:' .. p.GRAY_COLOR)
end
if italic_state then
out_style = p.appendStyle(style, 'font-style:italic')
end
table.insert(output_strs, p.parseStation(station, out_style, system, frame))
next_separator = user_separator
end
end
end
endRouteLine()
return table.concat(output_strs)
end
function p.route(frame)
local gstyle = frame.args['style']
local route_str = frame.args['stations']
local condition = frame.args['condition']
local system = frame.args['system']
p.station_suffix = mw.text.trim(frame.args['station_suffix'] or p.DEFAULT_STATION_SUFFIX)
return p.processRoute(string.gsub(route_str, '\n', ''), gstyle, condition, system, frame)
end
return p