模組:RouteSequence
![]() | 此模組被引用於約15,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