文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

本模块为方便填写{{鐵道路線}}模板中的线路图而设计。

基本用法

站名1 ~ 站名2 ~ 站名3前缀#站名3#站名3后缀 ~ 站名4条目名!站名4

例如,代码

{{鐵道路線|lua=1
|模板名稱=昆明地铁6号线
|名稱=昆明地铁6号线
|代表色=#2c8195
|铁道系统 = 昆明地铁
|Logo=
|車站列表= (预留延伸)#东部汽车站~#大板桥##(未开通)~机场前~机场中心站!长水国际机场
}}

将显示为

站名的转换规则如下:

  1. 如果给出“铁道系统”参数,将依照“Module:Adjacent stations/{{{铁道系统}}}”中“stationNames”表的链接标题和条目名称转换站名,此为最高优先级。如果未定义数据模块,请勿使用该参数;
  2. 如果已经给出条目名,模板优先按照条目名进行显示,而不进行其他的判断;
  3. 如果给出的站名末尾为不包含“站”字,模板将自动追加这个“站”字;
  4. 如果给出的站名末尾为“站”,在最终显示的模板中,这个“站”字将被省略。因而,如果站名中本身末尾为“站”字,那么需要在末尾写连续两个“站”。例如,如果需要显示“城站”,那么提供给模板的站名应该是完整的条目名“城站站”;
  5. 如果给出的站名为消歧义,例如“客运中心站 (杭州地铁)”,将创建形如“[[客运中心站 (杭州地铁)|客运中心]]”的链接。

用于标识前缀/后缀的“#”如果单用,表示前/后缀与站名链接间不加空格。如果连用,即“##”,表示前/后缀与站名链接间加入空格,即“ ”。

站名样式

现支持两种自定义站名样式的方法。

采用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 = '&#32;&ndash;&#32;',
    DEFAULT_STATION_SUFFIX = '站',
    BLOCK_ALIGN_PATTERN = '##BLOCK_ALIGN##',
    BLOCK_START = '&#32;&lt;&#32;<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>&#32;&gt;&#32;',
    BLOCK_END_PURE = '</div>',
    GRAY_COLOR = '#717171'
}

p.sep_translate = {
    ['%%dash%%'] = '&ndash;',
    ['%%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., `&#32;`
            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 = '&#32;'
            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 = '&#32;' .. separator .. '&#32;'
		            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