RailSystems模块可用于处理、访问轨道交通线路系统,并允许进行面向对象使用,支持管理线路连接、标志色、车站的页面名称和显示名称等数据。许多其他模块和模板都使用了本模块。
本模块共提供3个可用于面向对象的类:系统(System)、线路(Line)和车站(Station)。
目前,原先从维基百科的zhwiki:Module:RailSystems导入至求闻百科的模块已被本模块取代,旧的模块参见Module:RailSystems/old。(此模块在维基百科已经被删除并完全被zhwiki:Module:Adjacent stations取代,而在求闻百科,此模块已经被重构并兼容Module:Adjacent stations胡功能。
请参考本文档中的“数据格式”以修改相应数据内容。本模块对旧版格式保持着一定程度的兼容性,但这种向下兼容不是永久的。与列车时间有关的功能未来将从本模块中完全移除。数据存储
本模块的各个子页面存储各轨道交通系统的数据,详见Special:前缀索引/Module:RailSystems/。例如,Module:RailSystems/SHMetro存储上海轨道交通系统的数据,访问与上海轨道交通有关的数据时就会使用此页面的数据,Module:RailSystems/SuzhouRT则是存储的苏州轨道交通的数据。
子页面的数据都是通过mw.loadData
加载的,因此有一些限制,可以指定override
参数表示另一个模块的名称,将加载该模块以调整数据的行为。
为确保兼容性,本模块会尝试加载Module:Adjacent stations的子页面,并尽可能地转换为RailSystems的格式。请注意这种转换是有限的。建议有能力的用户直接创建对应的RailSystems子页面的版本,Adjacent stations的子模块将被逐渐弃用。
数据格式
本模块的子页面均为记录各个轨道交通线网的表,遵从#System的数据格式。这些表均通过mw.loadData
加载,因此不能存有任何的函数。下列列表列举出了表中可以拥有的各个字段及其说明,加粗内容为字段名称(也就是键)。
System的数据格式
- name:可选字符串。该线网系统的名称,如“苏州轨道交通”。
- icon:字符串或表。若为字符串,则为图标文件名称。
- icon:字符串或表。若为表,表示轨道交通系统的图标。
- file:轨道交通系统的图标文件名称、
- size:图标的默认大小。
- link:图标的默认链接,可以是空字符串。
- alt:图标的替换文字。
- line_index:表。该线网系统的线路ID的索引。
- 准则名称:由线路ID组成的数组。准则名称可以是all、open等,也可以是自定义的准则名称,如tram(表示列举所有的有轨电车线路)。
之所以使用line_index,是因为线路在lines字段中是以表的形式存在的,且迭代顺序不确定。此外,还存在一个线路数据对应多个键的情况。
- 准则名称:由线路ID组成的数组。准则名称可以是all、open等,也可以是自定义的准则名称,如tram(表示列举所有的有轨电车线路)。
- lines:表。该线网系统的所有线路。
- 线路ID:键是线路的ID,值为一个Line对象的数据,参见#Line的数据格式。
- stationNames:表。指定一些特殊的车站的显示名称和页面名称。将影响该轨道交通系统中的所有车站。该字段的值的各个字段都有两种写法:
- 车站ID:字符串。键为车站ID,值的格式为“页面名称|线路名称”。如果其值含有链接的话,那么获取该车站的ID时,直接返回这个整个字符串。
- 车站ID:表。键为车站ID,值的格式如下:
- page:字符串。该车站对应的页面名称。可能为nil,表示该车站没有对应页面,生成链接时将产生纯文本。
- name:字符串。该车站的显示名称。
- link:可选字符串,一般不建议使用,表示该车站的链接。
- override:可选字符串。指定一个模块名称,加载该系统时,将导入(require)此模块,并根据该模块的内容覆盖默认的行为,通常不需要使用。
- stationFormat:可选字符串。表示各站对应页面名称的默认格式。默认为“
%s站
”。 plain_link_template:已经弃用,不再有用。
Line的数据格式
- short_name:字符串。线路的短名称,如“1号线”。
- full_name:字符串,可选。线路的长名称,如“上海轨道交通1号线”。
- name:字符串,可选。线路的通用名称,一般和短名称一致。
- page:字符串,可选。该线路的页面名称。若不存在且不等于false,则取线路的完整名称,若仍不存在,则取线路的通用名称。若为false,则表示指定该线路没有页面名称,不产生链接。
- link:字符串,可选且不建议。若存在,则获取线路链接(line:getLink())时,强制使用此值,忽略nameType和isStyled参数,因此不建议使用。
title:字符串。已弃用。一个形如“页面名称|线路名称”的字符串。- color:字符串。线路的十六进制颜色(不带井号)。
- textColor:字符串。当文字显示在以线路颜色为底色的环境下时,文字应当显示的颜色。默认为白色,但对于部分线路应当设为黑色。
- icon:字符串或表。与#System的数据格式中的icon相同。
- stations:数组。该线路的车站列表。可选,但如果不存在,部分功能将受到限制。
- 一个Station格式的值,参见#Station的数据格式。
- terminus:数组。格式和用法同stations,用来表示终点站。调用Line:getTerminalStation时,如果该字段存在则根据该字段获取终点站,否则根据stations的值来推断终点站。
- 一个Station格式的值,参见#Station的数据格式。
- notOpen:布尔值,表示线路是否未开通。我们约定这里的开通是指的施工状态的开通,通常,受疫情、天气等因素临时影响运营的,不在此字段考虑范围内。若为true,表示线路未开通,此时该线路的所有车站(包括与已开通车站的换乘站)都是未开通的。
- planning:布尔值,表示线路是否仍在规划中。若为true,则该线路的所有车站都是规划中的。
terminals:表。已弃用,表示线路的终点站。left:表。right:表。
- isLoop:布尔值,表示是否为环线。如果为环线,则首尾两站也可以视为邻站。
trainTime:表。已弃用。left、right字符串。已弃用。列车时间,格式为hh:mm:ss
的格式。
openDates:表。已弃用。
Station的数据格式
在数据中,可以直接使用字符串表示一个简单车站,不为该车站指定特殊属性,其车站ID即为该字符串的值。尝试生成车站链接时,会尝试根据车站的ID从系统数据的stationNames参数中查找命名规则。如果表示一个带有特殊情况的车站(比如一条已开通的线路的多个车站中,有一个没有开通),或者一个抽象车站(将一条子线,或者线路的主线支线部分抽象为一个车站),则使用表,这种情况下表的格式如下:
- id:字符串。车站的id。通常与车站名称一致。有时候不同的车站会有相同的名称(例如上海轨道交通4号线和6号线的浦电路是不同的车站),此时车站的id应当不同。
- type:字符串,可选。表示车站的类型。
"branches"
表示该车站是由线路的主线和支线抽象而成的车站。"adjacent_branches"
表示某站在主线和支线上的两个相邻车站构成的一个抽象车站(通常在轨道交通系统数据中不会使用)。"line"
表示将一个子线路,将该线路抽象为车站,当有一段连续的车站未开通时,可以将这些车站放在一个子线路中,并将这个子线路的notOpen设为true。 - notOpen:表示该车站是否暂未开通。若车站属于一个未开通的线路,则认为该线路中的所有车站是未开通的,不再需要为各车站设置notOpen的值。
- planning:表示该车站是否还在规划中。若车站属于一个规划中的线路,则认为该线路中的所有车站都在规划中,不再需要为各车站设置planning的值。
- icon:字符串或表。与#System的数据格式中的icon相同。
- main:表,可选。若type为branches或adjacent_branches,则该值为一条线路数据,参见#Line的数据格式。
- branch:表,可选。若type为branches或adjacent_branches,则该值为一条线路数据,参见#Line的数据格式。
- line:表,可选。若type为line,则该值为一条线路数据,参见#Line的数据格式。
System
System类表示一个轨道交通系统。每个轨道交通系统都有其data字段,其值为一个表(可以是mw.loadData
返回的只读表)。
下面的所有例子中,system
均表示一个System对象(注意大小写)。
System.of
System.of(data, override)
根据轨道交通系统的数据,生成一个System对象。通常只是用于内部使用,一般情况下请使用System.load。
- data
- 该轨道交通系统的数据,是一个表,可以是
mw.loadData
返回的只读表。 - override
- 值为函数组成的表,用来覆盖其自身及其返回的线路、车站对象的默认行为。一般不需要使用。
System.load
System.load(name)
从本模块的子页面(Module:RailSystems/name)中加载数据,根据加载的数据来返回一个System对象。例如,System.load 'SHMetro'
就会加载Module:RailSystems/SHMetro的数据,并根据这些数据,返回一个表示上海轨道交通的线网系统对象。
如果Module:RailSystems/name不存在,则会尝试加载Module:Adjacent stations/name,并将其转换为本模块格式的数据。这种转换是有限的,这样做只是为了与Module:Adjacent stations保持兼容,并不建议这么做。
如果对应的数据不存在,则会抛出错误。如果name为nil,则返回nil,不抛出错误。
system:getIcon
system:getIcon(params)
返回该系统的图标。参数params是一个表,可以为nil。如果params是一个表,则可以拥有如下字段:
- size:图标的大小,如果未指定,则取线路数据中的icon字段定义的图标大小。
- link:图标的链接,如果未指定,则取线路数据中的icon字段定义的图标链接。可以是空字符串,表示没有链接。
注意:图标文件是由线路数据中的icon字段指定的,无法通过params参数指定。
system:getLines
system:getLines(criterion, condition)
获取该线网中的所有线路,返回由线路对象组成的数组。
- criterion
- 一个指定线网列表的准则。通常可用的值由数据中的line_index字段决定。以上海轨道交通为例,Module:RailSystems/SHMetro中的line_index有all、tram、metro等字段,其中metro字段的值为
{'1', '2', '3', ..., '26', '磁', '浦', '崇'}
,因此System.load 'SHMetro':getLines 'metro'
将会返回由1号线至26号线以及磁浮线、浦江线、崇明线组成的数组。 - 如果线路数据没有line_index字段,则该函数会产生错误。
- 该参数的默认值为'all'。
- condition
- 该线路需要符合的条件。默认为nil,即不需要满足任何条件。参见#line:meets。
system:getLine
system:getLine(key)
返回该线网中的线路对象。key的值为线网数据中的lines字段的键。如果线路不存在,则返回nil。
调用此函数不需要线路数据中有line_index字段。
system:getLineOrThrow
system:getLineOrThrow(key)
返回该线网中的线路对象。如果线路不存在,则抛出异常。
system:getLineOrAnonymous
system:getLineOrAnonymous(key)
返回该线网中的线路对象。如果线路不存在,则返回仅使用该id的线路。
system:getStation
system:getStation(id)
根据指定的车站id,返回一个Station对象。
由于这种方法并未指定车站所属的线路,因此车站对象不会受到任何线路数据的影响,但是,该Station对象仍有可能受到系统数据中的stationNames字段影响。
该方法并不要求系统中确实存在这个id的车站,如果是一个不存在的车站,仍能正常返回结果。
Line
Line表示一条轨道交通线路。system:getLine(key)可用从线网系统中获得一个线路对象。
下面所有的例子中,line
均表示一个Line对象。
Line.of
Line.of(data, system)
根据指定的线路数据和所属的系统,返回一个线路对象。通常用于内部使用。
line:getIcon
line:getIcon(params)
根据线路数据中的icon字段,返回线路的图标。如果该线路的图标不存在(大多数情况都是如此),则直接使用系统的图标。
line:getName
line:getName(nameType, isStyled)
返回线路的名称。nameType
参数决定你是要获取该线路的完整名称、长名称、短名称还是简写名称。例如,
local line4 = System.load 'SHMetro':getLine '4'
print (line4:getName 'short') -- '4号线'
print (line4:getName 'full') -- '上海轨道交通4号线'
print (line4:getName 'blink') -- '4'
根据线路数据,线路名称会依次尝试根据data.<nameType>_name
、data.name
、data.short_name
和data.title
决定。
isStyled
视为布尔值,如果为true,则如果该线路未开通,就会显示为灰色,如果线路仍在规划中,则会显示为斜体(这两种情况可能会叠加)。
line:getStyledName
line:getStyledName(nameType)
相当于line:getName(nameType, true)
。
line:getColor
line:getColor(prefixed)
返回该线路的标志色。prefixed
参数会在线路标志色前面加个井号,这样可以直接用于CSS。
local line = System.load 'SHMetro':getLine '4'
print (line:getColor()) -- '4F1F91'
print (line:getColor(true)) -- '#4F1F91'
如果线路没有指定标志色,则会返回nil。
line:getPage
line:getPage()
返回该线路对应的页面的名称。可能为nil。
会依次尝试根据线路数据的data.page
、data.title
、data.full_name
和data.name
决定。其中,若由data.title
决定,则取管道符之前的部分。
line:getLink
line:getLink(nameType, rich, isStyled)
返回一个该线路的维基链接。nameType
和isStyled
参数请见#line:getName。
local line4 = System.load 'SHMetro':getLine '4'
print (line:getLink()) -- '[[上海轨道交通4号线|4号线]]'
print (line:getLink(nil, true)) -- '[[上海轨道交通4号线|<span style="color:#4F1F91">■</span> 4号线]]''
如果nameType
为"blink",那么会根据data.textColor
决定文字颜色。
line:getRichLink
line:getRichLink(nameType, isStyled)
相当于line:getLink(nameType, true, isStyled)
。
line:getAdjacentStations
line:getAdjacentStations(station_id, condition)
返回该线路车站的指定条件的相邻车站。如果该线路数据没有stations字段,该函数会抛出错误。
如果该线路找到了id为station_id的车站,则会返回像{left = 左侧邻站对象, right = 右侧邻站对象}
这样的表。如果没有符合指定条件的邻站(比如该车站是线路的终点站),则该侧相邻车站对象为nil。如果线路位于支线交汇处导致某个方向的邻站不止一个(例如龙溪路站),则这种情况下,相邻车站对象可以将多个车站看成一个抽象车站对象。
如果该线路没有找到id为station_id的车站,则返回nil(不是空表)。
condition
的值请参考#station:meets。
line:getStation
line:getStation(id, condition)
返回该线路中id为指定的id的车站。如果该线路不存在该车站,则返回nil;如果存在车站但不符合指定的条件(condition),也会返回nil。
如果该线路数据的车站列表(stations字段)中存在由线路组成的抽象车站(type为"line"或"branches"),则会尝试在该抽象车站中包含的车站中查找。
如果线路数据没有stations字段,则直接返回nil。
line:getSubline
line:getSubline(line)
该方法主要用于模块内部。line是另一个车站对象。
line:listStations
line:listStations(condition)
返回由该线路中的所有车站组成的数组。condition参数参见#station:meets。
如果线路数据中没有stations字段,则无法列举出所有车站,将抛出错误。
line:getTerminalStation
line:getTerminalStation(direction, type, condition)
返回该线路指定方向的终点站的Station对象。direction可以是'left'或'right',如果为'both',则会返回像{left = 左侧终点站对象, right = 右侧终点站对象}
这样的表。
type参数主要用于主线与支线。其值可以为'left'或'right',若为'both',则会返回像{main = 主线终点站对象, branch = 支线终点站对象}
这样的表。
该方法会根据线路数据中的terminus字段或stations字段推断终点站。如果上述字段均不存在,则该方法返回nil。
line:renderStationList
line:renderStationList {isStyled = isStyled, showInterchange = showInterchange, condition = condition, interchangeCondition = interchangeCondition, stationCondition = stationCondition}
返回该线路所有车站链接字符串组成的列表。所有参数均可选。其中interchangeCondition和stationCondition参数若未指定,则使用condition参数。请参见#line:meets和#station:meets。
如果线路数据的stations字段不存在,则抛出错误。如果stations字段中有类型为"branches"的抽象车站,则这个抽象车站只会显示为“主线/支线”。
line:meets
line:meets(condition)
返回该线路是否满足指定的条件。condition可以是字符串、表或函数。
- 若condition为字符串,则其值可以是'not_open'、'open'或'planning'。
- 若condition为表,则其数据必须与该表匹配。
- 若condition为函数,则返回
condition(line)
的值。
Station
Station对象通常是通过对线路对象调用getStation或listStations方法获得的。
下面的例子中,station
均表示一个Station对象。
Station.of
Station.of(data, line, system)
根据指定的数据、线路和系统,构造一个Station对象。
参数line表示该车站是位于指定线路的车站。参数system表示该车站是位于指定系统的车站,若line参数存在,则默认为line.system。
line参数意义在于,如果该线路未开通(线路数据指定了未开通,但是车站自身数据未指定是否未开通),则一定认为该车站是未开通的。
system参数意义在于,获取名称和页面时受到该系统数据stationNames影响。
该函数主要由模块内部使用。
station:getIcon
station:getIcon(params)
返回该车站的图标,由该车站数据的icon字段决定。如果不存在,则尝试获取线路或系统的图标,如果扔不存在,则返回nil。
station:meets
station:meets(condition)
返回该线路是否满足指定的条件。condition可以是字符串、表或函数。
- 若condition为字符串,则其值可以是'not_open'、'open'或'planning'。注意:车站是否未开通或处于规划中,由该车站对象自身的数据和车站对象对应的线路对象(若有)的数据共同决定。例如,对于指定了线路的车站对象,如果该线路未开通,则认为车站一定未开通(包括与已开通线路的换乘站)。
- 若condition为表,则其数据必须与该表匹配。
- 若condition为函数,则返回
condition(line)
的值。
station:getName
station:getName(isStyled, notShort)
返回车站名称。
参数说明:
- isStyled
- 布尔值。若为true,则未开通的车站会显示为灰色,规划中的车站显示为斜体,与已开通线路的换乘站显示为粗体。
- notShort
- 布尔值。若为true,则车站名称结尾加上“站”字。这种情况下会根据页面名称推断站名,一般不建议这样做。若为false,则一般与车站id相同。
station:getStyledName
station:getStyledName(notShort)
等同于station:getName(true, notShort)
。
station:getPage
station:getPage()
返回该车站的页面名称。页面名称通常由该对象所属的System对象的数据中的stationNames对象指定。
local SHMetro = System.load 'SHMetro'
print(SHMetro:getStation '人民广场':getPage()) -- '人民广场站 (上海市)'
print(SHMetro:getStation '浦电路4':getPage()) -- '浦电路站 (4号线)'
若该车站为抽象车站,则会抛出错误。
station:getLink
station:getLink(isStyled, notShort)
返回该车站的维基链接。参见#station:getName和#station:getPage。同样也会受到系统数据(若有)中的stationNames字段影响。
station:getLines
station:getLines(criterion, condition, avoid)
获取该车站在线网中所属的线路组成的数组。例如,在上海轨道交通中,真如站是11号线与14号线的换乘站:
local SHMetro = System.load 'SHMetro' local Zhenru = SHMetro:getStation '真如' mw.logObject( Zhenru:getLines() -- 其值大致相当于 {SHMetro:getLine '11', SHMetro:getLine '14'} )
criterion
和condition
参数与#system:getLines中的参数类似。
avoid
参数是字符串,用于在结果中回避该车站对象自身所属的线路。前提是该车站对象必须指定了所属的线路。例如:
local SHMetro = System.load 'SHMetro' local L11Zhenru SHMetro:getLine '11':getStation '真如' mw.logObject( Zhenru:getLines() -- 其值大致相当于 {SHMetro:getLine '11', SHMetro:getLine '14'} Zhenru:getLines(nil, nil, true) -- 其值大致相当于 {SHMetro:getLine '14'}
通过invoke调用
本模块除了可以由Lua使用之外,也可以直接通过维基文本来使用。
stationLink
返回车站链接。其中name参数与station_id参数等价。
模板链接:{{#invoke:RailSystems|stationLink|name=真如|system=SHMetro}}
- 效果:真如
模板链接:{{#invoke:RailSystems|stationLink|name=盘蠡路|system=SuzhouRT}}
- 效果:盘蠡路
模板链接:{{#invoke:RailSystems|stationLink|name=浦电路4|system=SHMetro}}
- 效果:浦电路
模板链接:{{#invoke:RailSystems|stationLink|name=浦电路4|short=false|system=SHMetro}}
- 效果:浦电路站
参数styled
可用于将未开通或者计划中的车站添加特殊的样式。
lineColor
返回线路颜色。
模板链接:{{#invoke:RailSystems|lineColor|line=1|system=SHMetro}}
- 效果:EA0437
模板链接:{{#invoke:RailSystems|lineColor|line=1|prefixed=1|system=SHMetro}}
- 效果:
- EA0437
模板链接:{{#invoke:RailSystems|lineColor|default=ffffff|line=114514|system=SHMetro}}
- 效果:ffffff
lineLink
返回线路链接。如果optional
为true,那么当线路id不存在时,返回空白字符串,否则抛出错误(下同)。
模板链接:{{#invoke:RailSystems|lineLink|line=1|system=SHMetro}}
- 效果:1号线
模板链接:{{#invoke:RailSystems|lineLink|line=114514|optional=true|system=SHMetro}}
- 效果:
模板链接:{{#invoke:RailSystems|lineLink|line=1|rich=1|system=SHMetro}}
模板链接:{{#invoke:RailSystems|lineLink|line=1|style=full|system=SHMetro}}
- 效果:上海轨道交通1号线
参数styled
可用于将未开通或者计划中的线路添加特殊的样式。
lineName
返回线路名称,name_type参数与style参数等价。
linePage
返回线路对应的页面名称。
lineRichLink
与lineLink类似,但rich始终为true。参数styled
可用于将未开通或者计划中的线路添加特殊的样式。
lineStationList
显示车站列表,其中name参数与line_id参数等价。
- 显示完整:
模板链接:{{#invoke:RailSystems|lineStationList|system=SHMetro|line=11}}
- 仅显示已开通:
模板链接:{{#invoke:RailSystems|lineStationList|system=SHMetro|line=11|condition=open}}
- 可以仅显示支线:
模板链接:{{#invoke:RailSystems|lineStationList|system=SHMetro|line=11branch}}
- 显示完整:
模板链接:{{#invoke:RailSystems|lineStationList|system=SHMetro|line=18}}
- 不显示换乘:
模板链接:{{#invoke:RailSystems|lineStationList|system=SHMetro|line=14|showInterchange=false}}
参数styled
可用于将未开通或者计划中的车站添加特殊的样式。
lineTerminalLink
返回该车站的终点站链接。
模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=2|system=SHMetro}}
模板链接:{{#invoke:RailSystems|lineTerminalLink|condition=open|direction=left|line=2|system=SHMetro}}
模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=11|system=SHMetro}}
模板链接:{{#invoke:RailSystems|lineTerminalLink|direction=left|line=10|system=SHMetro}}
参数styled
可用于将未开通或者计划中的车站添加特殊的样式。
lineTerminalName
返回该车站的终点站名称(一般不带链接)。
模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=2|system=SHMetro}}
- 徐泾东
模板链接:{{#invoke:RailSystems|lineTerminalName|condition=open|direction=left|line=2|system=SHMetro}}
- 徐泾东
模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=11|system=SHMetro}}
- 嘉定北/花桥
模板链接:{{#invoke:RailSystems|lineTerminalName|direction=left|line=10|system=SHMetro}}
- 虹桥火车站/航中路
测试
- 测试成功,结果:[[宁波站|宁波站]]
- 测试成功,结果:[[宁波站|宁波站]]
- 测试成功,结果:[[高桥西站(宁波)|高桥西]]
- 测试成功,结果:[[高桥西站(宁波)|高桥西站]]
- 测试成功,结果:[[泽民站|泽民]]
- 结果
[[内嵌模板站|内嵌{{!}}模板]]
不符合预期[[内嵌模板站|内嵌|模板]]
- 测试成功,结果:3180b7
- 测试成功,结果:#3180b7
- 测试成功,结果:[[宁波轨道交通1号线|1号线]]
- 测试成功,结果:[[西铁线|西铁线]]
- 测试成功,结果:[[宁波东站|宁波东站]]
- 测试成功,结果:高桥西
- 测试成功,结果:宁波站
- 测试成功,结果:宁波东站
- 测试成功,结果:余姚
- 测试成功,结果:马渚
- 测试成功,结果:杭州湾
- 测试成功,结果:22:06
- 测试成功,结果:06:08
- 测试成功,结果:往[[高桥西站(宁波)|高桥西]]方向:22:06
- 测试成功,结果:往[[霞浦站(宁波)|霞浦]]方向:06:08
- 测试成功,结果:06:07-22:08
- 测试成功,结果:06:07-22:10
- 测试成功,结果:在2016年的3月的19日,月份2016年3月,日期2016年3月19日开通
- 结果
[[Category:需要去除时间判断模板的页面]]
不符合预期2016年
- 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
- 结果
[[Category:需要去除时间判断模板的页面]]
不符合预期2016年3月
- 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
- 测试成功,结果:2016年3月19日
- 测试成功,结果:[[Category:需要去除时间判断模板的页面]]
- 测试成功,结果:2019年12月28日
- 测试成功,结果:二期规划中
- 测试成功,结果:规划中
- 函数调用出错:attempt to call a nil value,预期为
1号线从[[高桥西站(宁波)|高桥西]] |到[[霞浦站(宁波)|霞浦]]
- 函数调用出错:attempt to call a nil value,预期为
1号线从[[高桥西站(宁波)|高桥西站]] |到[[霞浦站(宁波)|霞浦站]]
测试完成,共成功30次,失败5次。
local System = {}
local Line = {}
local Station = {}
local p = {System=System, Line=Line, Station=Station}
local deprecated = {
DEFAULT_DATE_ELAPSED_TEXT = '[[Category:需要去除时间判断模板的页面]]',
DEFAULT_LINE_DATE_ABSENT_TEXT = '规划中',
DEFAULT_STATION_TRAIN_TIME_TEMPLATE = '{start_time|H:i|次日}-{end_time|H:i|次日}'
}
local allowDeprecated = true
if not allowDeprecated then
setmetatable(deprecated, {
__index = function () error "弃用的内容已停用!" end,
__newindex = function () end
})
end
require 'Module:No globals'
--[=[
根据page和name,创建一个[[page|name]]这样的内链。
且如果page为nil,则直接返回name。
page必须是nil或字符串,name必须是字符串。
]=]
local function createLink(page, name)
return page and '[[' .. page .. '|' .. name .. ']]' or name
end
--[[
== 元表定义和构造函数部分 ==
]]
--- 类似于 RailSystems 模块中的 _loadSystemData,不过会将其用元表包装,返回一个System对象。
-- @string name 铁路系统名称,也就是Module:RailSystems/后的子页面的部分。
function System.load(name)
if not name then return nil end
local successes, data = pcall(mw.loadData, "Module:RailSystems/" .. name)
if not successes then
-- 导入失败的情况
mw.log ("[RailSystems] 无法加载[[Module:RailSystems/" .. name .. "]]的数据,原因:" .. data)
local successes2, data2 = pcall(p.importAdjacentStations, name)
if successes2 then
mw.addWarning (("'''来自[[Module:RailSystems]]的警告:'''[[Module:RailSystems/%s]]的数据不存在(或者加载错误),已导入[[Module:Adjacent stations/%s]]的数据。这可能会降低性能,未来将不再支持。请有能力的用户尽快创建RailSystems中的数据表。"):format(name, name))
mw.log ("[RailSystems] 加载转换自[[Module:Adjacent stations/" .. name .. "]]的数据。")
data = data2
else
mw.log ("[RailSystems] 无法加载[[Module:Adjacent stations/" .. name .. "]]中的数据,原因:" .. data2)
error ("无法加载铁路系统数据,原因:" .. data)
end
end
local override = nil
if data.override then
-- override 字段用于如何修改 System、Line、Station类。
-- 这样可以使得部分系统能够拥有自己的行为。
override = require (data.override)
end
if data.plain_link_template then
mw.addWarning('“' .. name .. '”的plain_link_template字段已被弃用,且不再被使用,请将其移除。')
end
local system = System.of(data, override)
system.modulePageName = name
return system
end
local metatable1 = {
__index = System,
__name = 'System',
__tostring = function (self) return 'System(name=' .. tostring(self.data.name or self.modulePageName) .. ')' end
}
p.metatable1 = metatable1
--- 这部分代码用于处理铁路系统的override。
local package_processed_override = {}
local function add_super_as_metatable(t, super)
local metatable = getmetatable(t)
if not metatable then
metatable = {}
setmetatable(t, metatable)
end
for k, v in pairs(t) do
if type(v) == 'table' then
add_super_as_metatable(v, super[k])
end
end
metatable.__index = super
end
local function process_override(override)
if package_processed_override[override] then
-- 已经处理过了,直接返回处理过的。
return override
end
-- override.super 可用于在override的部分中调用被override的部分。
override.super = p
for k, v in pairs(override) do
if type(v) == 'table' and type(override.super[k]) == 'table' then
add_super_as_metatable(v, override.super[k])
end
end
package_processed_override[override] = true
return override
end
--- 根据整个系统的数据(可以是mw.loadData导出的数据)生成一个可以作为面向对象调用的System对象。
-- @table[opt] data 铁路系统的数据。需要注意,这个data可能是通过mw.loadData导出的只读的表,因此应该是不可变的。
-- @table[opt] override 在线路数据中,指定override字段,其值作为模块名称,加载此模块。该模块应该“继承”此模块的p。
-- @return System对象,或者nil。
function System.of(data, override)
if data == nil then return nil
elseif type(data) == 'string' then
error(string.format("System.of中的参数data应当是一个表。你是不是想调用System.load(%q)?", data))
elseif type(data) ~= 'table' then
error(string.format("System.of中的参数data应当是一个表,而非%s。", type(data)))
elseif data == System then
error "System.of第一个参数错误,你是否误调用了System:of()?"
end
if override then
override = process_override(override)
end
return setmetatable({data=data, override=override}, override and override.metatable1 or metatable1)
end
local metatable2 = {
__index = Line,
__name = 'Line',
__eq = function (self,other) return self.data == other.data end,
__tostring = function (self)
return 'Line(' .. tostring(self:getName() or 'unnamed') .. ', system=' .. tostring(self.system and (self.system.name or self.system.modulePageName) or nil) .. ')'
end
}
p.metatable2 = metatable2
--- 根据一条线路的数据(可以是mw.loadData导出的数据),生成一个新的线路对象。
-- @table[opt]|str data 线路的数据。需要注意,这个data可能是通过mw.loadData导出的只读的表,因此应该是不可变的。
-- @table[opt] system 该线路所属的轨道系统。
-- @return Line对象,或者nil。
function Line.of(data, system)
if data == nil then return nil
elseif data == Line then
error "Line.of第一个参数错误,你是否误调用了Line:of()?"
elseif type(data) == 'string' then
data = {stations = {data}}
end
return setmetatable(
{data=data, system=system},
(system and system.override) and system.override.metatable2 or metatable2
)
end
local metatable3 = {
__index = Station,
__name = 'Station',
__tostring = function (self)
if self.type == 'line' then
return string.format('AbstractStation:Line(%s)', tostring(self.line and (self.line:getName() or 'unnamed') or nil))
elseif self.type == 'branches' then
return string.format('AbstractStation:Branches(main=%s, branch=%s)', tostring(self.main and (self.main:getName() or 'unnamed') or nil), tostring(self.branch and (self.branch:getName() or 'unnamed') or nil))
elseif self.type == 'adjacent_branches' then
return string.format('AbstractStation:AdjacentBranches(main=%s, branch=%s)', tostring(self.main and (self.main:getName() or 'unnamed') or nilo), tostring(self.branch and (self.branch:getName() or 'unnamed') or nil))
end
local paras = {}
if self.data and self.data.id then
table.insert(paras, 'id=' .. tostring(self.data.id))
end
if self.line then
table.insert(paras, 'line=' .. tostring(self.line:getName()))
end
if self.line and self.line.system~=self.system then
table.insert(paras, 'system=' .. tostring(self.system and (self.system.name or self.system.modulePageName) or nil))
end
return 'Station(' .. table.concat(paras, ', ') .. ')'
end
}
p.metatable3 = metatable3
--- 根据车站数据(可以是表或者字符串),以及可选的线路和系统,生成一个新的车站对象。
-- @table[opt] data 车站的数据。可能是mw.loadData导出的只读的表,因此不应该更改。
-- @string[opt] data 相当于 {id=data}。
-- @table[opt] line 该车站所属的线路。应当是一个Line对象。
-- @table[opt=line.system]
-- system 该车站所属的系统。应当是一个System对象。
-- @return Station对象,或者nil。
function Station.of(data, line, system)
system = system or (line and line.system)
if data == nil then
return nil
elseif data == Station then
error "Station.of第一个参数错误,你是否误调用了Station:of()?"
elseif type(data)=='string' then
data = {id = data}
end
local metatable3 = system and system.override and system.override.metatable3 or metatable3
-- @string[opt] data.type 可能的取值:branches、adjacent_branches、line或nil。
if data.type == 'branches' or data.type == 'adjacent_branches' then
-- branches 其本质为由主线和支线组成的抽象车站。将这两段分支(不包括分支合并的车站)看做是一个车站。
-- adjacent_branches 其本质为由支线交汇处的两个临站组成的抽象车站。将同一侧不同分支的两个车站看做是一个车站。
return setmetatable({
type = data.type,
main = Line.of(data.main, system),
branch = Line.of(data.branch, system),
line = line,
system = system
}, metatable3)
elseif data.type == 'line' then
-- 其本质为线路的抽象车站。将一段线路看做是一个车站。
return setmetatable({
type = data.type,
line = Line.of(data.line,system),
system=system,
}, metatable3)
else
return setmetatable({data=data,line=line,system=system}, metatable3)
end
end
--[[
== 日志管理方法部分 ==
]]
--- 向控制台输出一条警告消息,并在预览时显示该警告消息。消息会显示自身对象名称。
-- @return 始终为nil,因此可用在短路运算中。
function System:warn(msg)
msg = '[' .. tostring(self) .. '] ' .. msg
mw.log(msg)
mw.addWarning(msg)
end
Line.warn = System.warn
Station.warn = System.warn
--- 类似于System:warn,但是会格式化字符串。
function System:warnf(msg, ...)
return self:warn(string.format(msg, ...))
end
Line.warnf = System.warnf
Station.warnf = System.warnf
--- 抛出错误,错误内容中会显示自身对象名称。
function System:error(msg)
error('[' .. tostring(self) .. '] ' .. msg)
end
Line.error = System.error
Station.error = System.error
--- 抛出错误,且可以格式化字符串。
function System:errorf(msg, ...)
return self:error(string.format(msg, ...))
end
Line.errorf = System.errorf
Station.errorf = System.errorf
--[[
== 实例方法部分 ==
]]
--- 获取该轨道交通系统的图标。
function System:getIcon(params)
params = params or {}
local size = params.size
local iconLink = params.link
local iconFile
local iconData = self.data.icon
local alt = params.alt
if not iconData then
return nil
elseif type(iconData) == 'string' then
iconFile = iconData
size = size or '20px'
iconLink = iconLink or self.data.link
elseif type(iconData) == 'table' then
iconFile = iconData.file
size = size or iconData.size or '20px'
iconLink = iconLink or iconData.link or self.data.link
alt = alt or iconData.alt
end
if not iconFile or iconFile == '' then
return nil
end
local builder = {'[[', iconFile}
builder[#builder + 1] = size and ('|' .. tostring(size)) or nil
builder[#builder + 1] = iconLink and ('|link=' .. tostring(iconLink)) or nil
builder[#builder + 1] = alt and ('|alt=' .. tostring(alt) or nil)
builder[#builder + 1] = ']]'
return table.concat(builder)
end
function Line:getIcon(params)
return System.getIcon(line, params) or (self.system and self.system:getIcon(params) or nil)
end
function Station:getIcon(params)
return System.getIcon(station, params) or (self.line and self.line:getIcon(params) or nil)
end
--- 根据指定的准则(criterion),返回该系统内符合该准则的所有线路对象组成的数组。
-- 会迭代line_index[criterion],然后访问lines字段,但不会迭代lines字段。
-- 返回的结果中按照line_index中的顺序排序。
-- @return {Line}
function System:getLines(criterion, condition)
local lines = {}
criterion = criterion or 'all'
local line_index = self.data.line_index
if line_index and line_index[criterion] then
for i, index in ipairs(line_index[criterion]) do
local line = self:getLine(index)
-- 插入lines的内容,使用原始方式以提升性能。
lines[#lines + 1] = (line:meets(condition) and line or nil)
end
elseif line_index then -- and not line_index[criterion]
self:errorf('调用System:getLines(criterion, condition)时出现错误,未知的准则名称:%s', tostring(criterion))
else -- if not line_index
self:error('无法调用System:getLines,因为该系统数据的line_index字段不存在')
end
return lines
end
--- 根据lines字段中的键,获取线路对象。如果不存在该键,则返回nil。
function System:getLine(key)
local lines = self.data.lines
return lines and lines[key] and Line.of(lines[key], self) or nil
end
--- 根据lines字段中的键,获取线路对象。如果不存在该键,则抛出错误。
function System:getLineOrThrow(key)
return self:getLine(key) or self:error("未知的线路id:" .. tostring(key))
end
--- 根据lines字段中的键,获取线路对象。如果不存在该键,则创建虚拟的。
function System:getLineOrAnonymous(key)
return self:getLine(key) or Line.of({name = key, page = false}, self) or nil
end
--- 根据指定的车站名称,获取该车站对应的车站对象。
-- 需要注意:此情况下获取的车站可能根本不存在,但仍会正常返回,这与Line:getStation中的不同。
-- @return Station对象。
function System:getStation(id)
return Station.of(id, nil, self)
end
-- @deprecated
-- 指定车站的链接,例如从“朝阳门”转换至“[[朝阳门站(北京)|朝阳门]]”。
function System:getStationLink(id, frame)
local stationNames = self.data.stationNames
local station = self:getStation(id)
return station:getLink()
end
--- 获得指定类型的线路名,不带链接。
-- @string[opt='short']
-- nameType 线路名称的类型,可以是 short full blink 等值,默认为short。
-- @bool isStyled 是否对线路应用加粗、灰色等格式。
-- @see Station:getName中的isStyled参数。
-- @see Line:getStyledName
-- @return 会依次获取data.<nameType_>name、data.short_name、data.name和data.title。
function Line:getName(nameType, isStyled)
if isStyled then
return self:getStyledName(nameType)
end
return self.data[nameType and nameType .. '_name' or 'name']
or (self.data.name
or self.data.short_name)
-- 读取title字段,这是考虑到向前兼容。在之前的版本中,title的值是像“苏州轨道交通1号线|1号线”这样的。
or (self.data.title
-- 通过tabel.remove来返回最后一个值,
-- 如果仍然不存在,则返回nil。
and mw.text.split(self.data.title, '|')[2] or self.data.title)
end
--- 获取指定类型的线路名,且进行格式化:若线路未开通,则显示为灰色;若线路还在计划中,则显示为斜体。
function Line:getStyledName(nameType)
local name = self:getName(nameType, false)
if self:meets 'not_open' then
name = '<span class=graylink>' .. name .. '</span>'
end
if self:meets 'planning' then
name = '<i>' .. name .. '</i>'
end
return name
end
--- 线路颜色。
-- @bool prefixed 是否给返回的颜色加上井号前缀。
-- @return string[opt] 线路颜色。可能是nil。
function Line:getColor(prefixed)
return self.data.color and ((prefixed and '#' or '') .. self.data.color) or 'transparent'
end
-- @return 该线路对应的页面名称。
function Line:getPage()
if self.data.page == false then
return false
end
return self.data.page
-- 读取title字段,这是考虑到向前兼容。在之前的版本中,title的值是像“苏州轨道交通1号线|1号线”这样的。
or (self.data.title
and mw.text.split(self.data.title, '|')[1])
or self.data.full_name
or self.data.name
-- 如果仍然不存在,则返回nil。
end
--- 返回线路链接。
---@string style 可以是short full blink等。@see Line:getName中的style参数。
---@bool rich 是否展示其标志色。
---@bool isStyled 是否对线路应用加粗、灰色等格式。
function Line:getLink(nameType, rich, isStyled)
local page = self:getPage()
local name
if rich then
local color = self:getColor(true) -- 以#开头的颜色
if nameType == 'blink' then
name = self:getName(nameType, false)
local notOpen = isStyled and self:meets 'not_open'
-- 这里或许最好还是应当可配置比较好。
name = string.format([[<span class="mw-no-invert RailSystems-blink" style="display: inline-block; height: 1.25em; min-width: 1.25em; line-height: 1.25em; text-align: center; color: %s; border-radius: 2px; background-color: %s; white-space: nowrap">%s</span>]], '#' .. (notOpen and 'ffffff' or self.data.textColor or 'ffffff'), notOpen and '#cccccc' or color, name)
else
name = self:getName(nameType, isStyled)
name = (color and name) and '<span style="background-color:' .. color .. '; display: inline-block; height:1em; width:1em; line-height:1em; margin-inline:0.2em; user-select:none" class="mw-no-invert RailSystems-color-block" aria-hidden="true"><br></span>' .. name or name
end
else
if self.data.link then
return self.data.link
end
name = self:getName(nameType, isStyled)
end
return name and createLink(page, name) or nil
end
--- 相当于getLink(style, rich = true)
function Line:getRichLink(style, isStyled)
return self:getLink(style, true, isStyled)
end
--- 在index处,以step的步长,寻找符合条件condition的邻站。
-- @int/string index 在line中的所在位置。若为字符串'end',则表示从头或尾开始寻找。
-- @int step 步长,表示需要寻找的方向,一般为1或-1。
-- @func|str[opt]
-- condition 寻找的车站需要符合的条件。@see Station:meets
function Line:seekAdjacentStation(index, step, condition)
local stations_data = self.data.stations
-- stations_data的长度。
local hash = 0
if not stations_data then
self:error "无法调用seekAdjacentStations,因为其线路数据中没有stations字段"
end
-- 计算stations_data的长度。由于可能是通过mw.loadData导出的数据,因此“#”可能不起作用。
for k, v in ipairs(stations_data) do
hash = hash + 1
end
if not index then return nil end
if hash < 1 then
self:error "无法调用seekAdjacentStations,因为其线路数据的stations字段是空的。"
elseif not(index=='end' or index <= hash) then
self:error "调用seekAdjacentStation(index, step, condition)的参数index无效。index不能超过data.stations的长度。"
end
if index=='end' then index = step < 0 and hash + 1 or 0 end
local limit = 0
local i = index
repeat
i = i + step
if self.data.isLoop then -- 处理环线的情况。
if i < 1 then i = hash end
if i > hash then i = 1 end
if i == index then return nil end
end
local station_data = stations_data[i]
if station_data and station_data.type == 'branches' then
local ret = Station.of(
-- data =
{ type = 'adjacent_branches' },
-- (Line) line =
self
)
ret.main = Line.of(station_data.main, self.system):seekAdjacentStation('end', step, condition)
ret.branch = Line.of(station_data.branch, self.system):seekAdjacentStation('end', step, condition)
return ret
elseif station_data and station_data.type == 'line' then
local line = Line.of(station_data.line, self.system)
if line and line:meets(condition) then
local ret = line:seekAdjacentStation('end', step, condition)
if ret then return ret end
end
else
local station = Station.of(station_data, self)
if station and station:meets(condition) then return station end
end
limit = limit + 1
until limit > hash
return nil
end
--- 根据指定车站,获取其临站。注意可能会有支线的情况。
-- @treturn {left = 左侧临站对象, right = 右侧临站对象},但如果是线路位于终点站等原因,则可能为nil。
-- 如果线路根本找不到station_id的车站,则直接返回nil。
-- 如果某站位于支线交汇处,则该站的其中一个临站会是一个抽象的AdjacentBranches对象,大多情况下可以作为Station对象看待。
-- @string station_id
-- @func @str[opt] condition
function Line:getAdjacentStations(station_id, condition)
local stations = self.data.stations
if not stations then
self:error "无法调用getAdjacentStations,因为其数据中没有stations字段。"
end
local index
local left, right
for k, v in ipairs(stations) do
local id = type(v)=='string' and v or v.id
if v.type == 'line' then
local line = Line.of(v.line, self.system)
local result = line:getAdjacentStations(station_id, condition)
if result then
left = left or result.left
right = right or result.right
index = k
break
end
elseif v.type == 'branches' then
local mainLine = Line.of(v.main, self.system)
local branchLine = Line.of(v.branch, self.system)
local result = mainLine:getAdjacentStations(station_id, condition) or branchLine:getAdjacentStations(station_id,condition)
if result then
left = left or result.left
right = right or result.right
index = k
break
end
elseif v.type == 'adjacent_branches' then -- 一般不会存在这种情况,但是仍然写在代码中。
self:error "类型为adjacent_branches的抽象车站数据暂无法用于Line:getAdjacentStations方法"
elseif id==station_id then
index = k
break
end
end
-- 如果该线路找得到车站,则index不会为nil,返回表格结果(尽管结果可能为空)。
-- 如果该线路不存在该车站,则返回nil。
return index and {
left = left or self:seekAdjacentStation(index, -1, condition),
right = right or self:seekAdjacentStation(index, 1, condition)
} or nil
end
--- 根据id返回该线路的车站对象。如果不存在此车站则会返回nil。
-- @str id 车站id。
-- @func @str[opt]
-- condition 返回的车站必须符合此条件。如果发现符合id的车站但不符合条件,则仍会返回nil。
function Line:getStation(id, condition)
if not self.data.stations then return nil end
if condition then
local ret = self:getStation(id, nil) -- 注意避免无限循环调用。
return ret and ret:meets(condition) and ret or nil
end -- 以下内容只有在不存在condition时执行。
for i, station_id in ipairs(self.data.stations) do
-- 这个station_id可能是车站id,也有可能是带有特殊属性的车站对象,这是需要留意的。
if type(station_id) == 'string' and station_id == id then
-- 此时 station_id是字符串,在构造函数中自动转化为 {id = station_id}。
return Station.of(station_id, self)
elseif type(station_id) == 'table' then
if station_id.type == 'line' then
local ret = Line.of(station_id.line, self.system):getStation(id)
if ret then return ret end
elseif station_id.type == 'branches' then
local ret = Line.of(station_id.main, self.system):getStation(id)
or Line.of(station_id.branch, self.system):getStation(id)
if ret then return ret end
else -- 默认认为 station_id.type == 'station' then
if station_id.id == id then
return Station.of(station_id, self)
end
end
end
end
return nil
end
--- 若该线路车站存在:
-- 以line为线路的抽象线路车站,或
-- 以line为主线或支线的抽象线路车站,
-- 则将其返回,否则返回nil。
-- 例如,苏州地铁4号线:getLine(苏州地铁4号线支线),将会得到苏州地铁4号线支线。
-- 苏州地铁3号线:getLine(苏州地铁4号线支线),将会得到nil。
-- 苏州地铁4号线:getLine(苏州地铁3号线),将会得到nil。
-- @return Line对象或nil。
function Line:getSubline(line)
if not self.data.stations then return nil end
if not line then return nil end
for i, station_id in ipairs(self.data.stations) do
if type(station_id)~='table' then
-- 如果station数据不是table,则直接continue。
elseif station_id.type == 'line' then
if line.data == station_id.line then
return Line.of(line.data, self.system)
else
-- 尝试获取子线路的子线路,看看其是否存在数据相符的子线路。
local ret = Line.of(station_id.line, self.system):getSubline(line)
if ret then return ret end
end
elseif station_id.type == 'branches' then
if line.data == station_id.main or line.data == station_id.branch then
return Line.of(line.data, self.system)
else
local ret = Line.of(station_id.main, self.system):getSubline(line)
or Line.of(station_id.branch, self.system):getSubline(line)
if ret then return ret end
end
end
end
return nil
end
--- 返回该线路中指定条件的车站的数组。
-- @func @str[opt] condition
-- @return {Line}
function Line:listStations(condition)
local list = {}
for i, station_id in ipairs(self.data.stations or self:error '由于线路数据中没有stations字段,无法调用listStations') do
local station = Station.of(station_id, self)
-- 使用最原始的方式以加快速度。
list[#list + 1] = (station:meets(condition) and station or nil)
end
return list
end
--- 获取该线路指定方向的终点站。
-- @str[opt='both'] direction 方向。可以是'left'、'right'、'both'。
-- @str[opt='both'] t 终点站类型。
-- 可以是'main'(获取主线终点站)、'branch'(获取支线终点站)、'both'(若存在分支,则两个都获取。
-- @return Station[opt]
-- @treturn {main=Station, branch=Station}
function Line:getTerminalStation(direction, t, condition)
if direction == nil or direction == 'both' then
return {
left = self:getTerminalStation('left', t, condition),
right = self:getTerminalStation('right', t, condition)
}
end
if condition == nil and self:meets 'open' then -- 需要考虑condition为false的情况
condition = 'open'
end
local isLeft = direction == 'left'
and true or direction ~= 'right'
and self:errorf("getTerminalStation(direction, t, condition) 的参数direction错误,可以是'left'、'right'、nil或者'both',但得到了%s", tostring(direction))
or false
local stations = self.data.stations or self.data.terminus
local hash = 0 -- 相当于#stations,但是stations是loadData加载出来的只读表,“#”不起作用,故迭代。
-- 参照旧版模块,按照旧版数据和规则来返回车站。
-- @deprecated
if not stations then
if true and self.data.terminals then
local function get_condition(cond, data)
if type(data) == 'table' then
local c = cond[data['#field']]
return get_condition(cond, data[c] or data['#default'])
else
return data
end
end
local side_data = self.data.terminals[direction]
-- local conds = { ['type'] = t, ['branch'] = branch }
local conds = type(t)=='table' and t or {type = t}
if type(side_data)=='table' then
self:warnf('检测到仍在使用过时的方式获取线路终点站:%s', debug.traceback())
end
return self.system:getStation(get_condition(conds, side_data))
else
return nil
end
end
-- 旧版模块语法部分结束。
for k, v in ipairs(stations) do hash = hash + 1 end
for i = (isLeft and 1 or hash), (isLeft and hash or 1), (isLeft and 1 or -1) do
local station = Station.of(stations[i], self)
if station.type == 'branches' then
-- 如果该车站为代表分支的抽象车站,则精细划分。
local mainLine = station.main
local branchLine = station.branch
if t == 'main' then
return mainLine:getTerminalStation(direction, t, condition)
elseif t == 'branch' then
return branchLine:getTerminalStation(direction, t, condition)
elseif t == nil or t == 'both' then
local ret = Station.of({
type = 'adjacent_branches'
}, self)
ret.main = mainLine:getTerminalStation(direction, t, condition)
ret.branch = branchLine:getTerminalStation(direction, t, condition)
return ret
else
self:errorf("调用Line:getTerminalStation时参数错误:应为'main'、'branch'、'both'或nil,而不是%s", tostring(t))
end
elseif station.type == 'line' then
local ret = station.line:getTerminalStation(direction, t, condition)
if ret and ret:meets(condition) then
return ret
end
else -- if station.type == 'station' or nil then
if station:meets(condition) then
return station
end
end
end
return nil -- 获取不到终点站的情况。
end
--- 将线路转化为由链接组成的列表。
-- @treturn {@str}
function Line:renderStationList(params)
-- @bool params.isStyled:是否对链接进行修饰。该值将会用于Station:getLink或Station:getName方法。
-- 默认为true,若为false则为false。
local isStyled = params.isStyled==nil and true or params.isStyled
-- @bool params.noLink:是否不生成链接,即若为true,则会调用getName而非getLink。
-- @bool params.showInterchange:是否显示换乘站。
-- 默认为true,若为false则为false、
local showInterchange = params.showInterchange==nil and true or params.showInterchange
-- @func @str[opt] params.condition:显示符合该条件的车站和换乘站。
-- 必须是同时适用于线路和车站的条件。
-- 默认为'',若为false则为false。
local condition = params.condition==nil and '' or params.condition
-- @func @str[opt] params.interchangeCondition:显示换乘站时,换乘站需要符合的条件。
-- 默认为condition,若为false则为false。
local interchangeCondition = params.interchangeCondition==nil and condition or params.interchangeCondition
-- @func @str[opt] params.stationCondition:显示的车站需要符合的条件。
-- 默认为condition,若为false则为false。
local stationCondition = params.stationCondition==nil and condition or params.stationCondition
local stations = self.data.stations
if not stations then
self:error "无法调用renderStationList,因为线路数据的stations字段不存在。"
end
local list = {}
for k,v in ipairs(stations) do
local station = Station.of(v, self)
if station.type == 'line' then
for k1, v1 in ipairs(station.line:renderStationList(params)) do
list[#list + 1] = v1
end
elseif station.type == 'branches' then
list[#list + 1] = '主线/支线'
elseif station.type == 'adjacent_branches' then
local elem = (params.noLink and station.getName or station.getLink)(station, isStyled)
list[#list + 1] = elem
elseif station:meets(stationCondition) then -- and station.type == others
local elem = (params.noLink and station.getName or station.getLink)(station, isStyled)
if showInterchange then
-- 加载换乘站列表。
local interchanges = {} -- 由Line对象的链接组成的数组。
local array1, array2 = station:getLines('all', interchangeCondition, true)
for k2,v2 in ipairs(array1) do -- 以后会改成condition
local v3 = array2[k2] -- Station对象。
function v2:meets(condition) return v3:meets(condition) end
if v2~=station.line then
interchanges[#interchanges + 1] = (v2:getRichLink('blink', isStyled))
end
end
if #interchanges>0 then
elem = elem .. '(' .. table.concat(interchanges, '') .. ')'
end
end
list[#list + 1] = elem
end
end
return list
end
-- @deprecated
function deprecated._parseTime(time_str)
local base_parts = mw.text.split(time_str, ':')
local parts_size = #base_parts
local hour = 0
local minute = 0
local sec = 0
if parts_size == 1 then
minute = tonumber(mw.text.trim(base_parts[1]))
elseif parts_size == 2 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
elseif parts_size == 3 then
hour = tonumber(mw.text.trim(base_parts[1]))
minute = tonumber(mw.text.trim(base_parts[2]))
sec = tonumber(mw.text.trim(base_parts[3]))
else
error('非法的时间输入')
end
return hour, minute, sec
end
-- @deprecated
function deprecated._extractSeconds(totalsec)
local hour = math.floor(totalsec / 3600)
local minute = math.floor(totalsec % 3600 / 60)
local sec = totalsec % 60
return hour, minute, sec
end
-- @deprecated
function deprecated._formatTemplateTime(frame, sparts, hour, minute, second)
local tomorrow_prefix = ''
if #sparts >= 3 then
if hour >= 24 then
tomorrow_prefix = mw.text.trim(sparts[3])
if tomorrow_prefix == '' then tomorrow_prefix = '次日' end
hour = hour - 24
end
end
local time_str = string.format('%d:%d:%d', hour, minute, second)
return tomorrow_prefix .. frame:callParserFunction{ name = '#time', args = { sparts[2], time_str } }
end
--- 获取列车首班车或末班车的运营时间。
-- @deprecated
-- @para dir 方向。
-- @str type 可以是 'first' 或者 'last'。
-- @return 3个数字,分别表示时分秒。
function Line:getTrainTime(dir, type, delta)
local dir_info = assert(assert(self.data.trainTime, '该线路数据无trainTime字段,无法获取列车时间。')[dir], '没有' .. tostring(dir) .. '方向的线路数据')
local bhour, bmin, bsec = deprecated._parseTime(dir_info[type])
local dhour, dmin, dsec = deprecated._parseTime(delta)
local totalsec = (bhour * 60 + bmin) * 60 + bsec + (dhour * 60 + dmin) * 60 + dsec
return deprecated._extractSeconds(totalsec)
end
if not allowDeprecated then Line.getTrainTime = nil end
-- @deprecated
function Line:getDateMessage(t, options)
local system = assert(self.system, "没有指定system的Line对象无法调用getDateMessage")
local reprs = options
local lowest_level = 0 -- 0: everything is ok, 1: year, 2: month, 3: day
local frame = mw.getCurrentFrame()
local cur_time = frame:callParserFunction{ name = '#time', args = { 'U' } }
local open_date, year, month, day, new_year, new_month
local ret_list = {}
for i, repr in ipairs(reprs) do
repr = mw.text.trim(repr)
if lowest_level < 1 and (repr == 'year') then
lowest_level = 1
elseif lowest_level < 2 and (repr == 'month' or repr == 'ym') then
lowest_level = 2
elseif lowest_level < 3 and (repr == 'day' or repr == 'date' or repr == 'ymd') then
lowest_level = 3
end
end
options = options or {}
options.auto_hide = options.auto_hide -- 暂时免除convBool
options.auto_defer = options.auto_defer -- 暂时免除convBool
if options.cur_time then -- for testing purpose
cur_time = frame:callParserFunction{ name = '#time', args = { 'U', options.cur_time } }
end
open_date = self.data.openDates or {}
if type(open_date) == 'table' then
if open_date[t] then
open_date = open_date[t]
elseif open_date['#default'] then
open_date = open_date['#default']
elseif lowest_level == 0 then
for i, repr in ipairs(reprs) do ret_list[#ret_list + 1] = repr end
return table.concat(ret_list)
else
return deprecated.DEFAULT_LINE_DATE_ABSENT_TEXT
end
end
year = tonumber(frame:callParserFunction{ name = '#time', args = { 'Y', open_date } })
month = tonumber(frame:callParserFunction{ name = '#time', args = { 'm', open_date } })
day = tonumber(frame:callParserFunction{ name = '#time', args = { 'd', open_date } })
if options.auto_defer then
if lowest_level == 1 then
open_date = string.format('%04d-01-01', year + 1)
elseif lowest_level == 2 then
new_year, new_month = year, month
if month == 12 then
new_year = new_year + 1
new_month = 1
else
new_month = new_month + 1
end
open_date = string.format('%04d-%02d-01', new_year, new_month)
end
end
open_date = frame:callParserFunction{ name = '#time', args = { 'U', open_date } }
if options.auto_hide and cur_time >= open_date then
return deprecated.DEFAULT_DATE_ELAPSED_TEXT
end
for i, repr in ipairs(reprs) do
if repr == 'year' then
ret_list[#ret_list + 1] = year .. '年'
elseif repr == 'month' then
ret_list[#ret_list + 1] = month .. '月'
elseif repr == 'day' then
ret_list[#ret_list + 1] = day .. '日'
elseif repr == 'ym' then
ret_list[#ret_list + 1] = string.format('%s年%s月', year, month)
elseif repr == 'date' or repr == 'ymd' then
ret_list[#ret_list + 1] = string.format('%s年%s月%s日', year, month, day)
else
ret_list[#ret_list + 1] = repr
end
end
return table.concat(ret_list)
end
if not allowDeprecated then Line.getDateMessage = nil end
--- 判断该线路是否符合指定的条件。若condition为nil,则认为肯定符合。
-- @func @str[opt] condition
function Line:meets(condition)
local add = true
if condition == '' or condition == 'all' or not condition then
return true
elseif type(condition) == 'function' then
add = add and condition(self)
elseif type(condition) == 'table' then
for k,v in pairs(condition) do
add = add and (self.data[k] or false) == v
end
elseif condition == 'not_open' then
-- 该车站是否暂未开通。如果该车站所在的线路未开通,则该车站一定未开通。
-- 注意:以实际工程状态为准。
-- 因2019冠状病毒病疫情、洪涝灾害、气象灾害、节日限流等原因关闭的,
-- 不在此范围内。
add = add and self.data.notOpen or (self.line and self.line.data.notOpen)
elseif condition == 'open' then
-- 与not_open相反。
add = add and not self.data.notOpen and not (self.line and self.line.data.notOpen)
elseif condition == 'planning' then
add = add and self.data.planning or (self.line and self.line.data.planning)
end
return add
end
--- 判断该车站是否符合指定的条件。若condition为nil,则认为肯定符合。
-- @func @str[opt] condition
function Station:meets(condition)
local add = true
if condition == '' or condition == 'all' or not condition then
return true
elseif type(condition) == 'function' then
add = add and condition(self)
elseif type(condition) == 'table' then
for k,v in pairs(condition) do
add = add and (self.data[k] or false) == v
end
elseif condition == 'not_open' then
-- 该车站是否暂未开通。如果该车站所在的线路未开通,则该车站一定未开通。
-- 注意:以实际工程状态为准。
-- 因2019冠状病毒病疫情、洪涝灾害、气象灾害、节日限流等原因关闭的,
-- 不在此范围内。
add = add and self.data.notOpen or (self.line and self.line.data.notOpen)
elseif condition == 'open' then
-- 与not_open相反。
add = add and not self.data.notOpen and not (self.line and self.line.data.notOpen)
elseif condition == 'planning' then
add = add and self.data.planning or (self.line and self.line.data.planning)
end
return add
end
--- 获取该车站的名称,如:“浦电路”“世纪大道”“虹桥火车站”。
-- @bool isStyled 是否对结果进行加粗、灰色等处理,如果为true,则跳到Station:getStyledName(),该函数会调用Station:getName(style=false)。
-- @bool notShort 是否在结尾加上“站”字。此情况将不使用车站数据中的名称,而是根据页面名称来推断车站名称。
-- @return string
function Station:getName(isStyled, notShort)
if isStyled then return self:getStyledName() end
if self.data and self.data.name then return self.data.name end
if self.type == 'branches' then
return (self.main and self.main:getName 'short' or 'nil') .. '/' .. (self.branch and self.branch:getName 'short' or 'nil')
elseif self.type == 'line' then
error "无法对抽象线路车站对象调用getName"
elseif self.type == 'adjacent_branches' then
return (self.main and self.main:getName() or 'nil') .. '/' .. (self.branch and self.branch:getName() or 'nil')
end
if self.system == nil then return self.data.id end
local stationNames = self.system and self.system.data.stationNames
if notShort then
-- 直接根据页面名称来推断站名。
-- 例如,如果页面名称为“长沙火车站(地铁)”,则返回的值为“长沙火车站”。
-- 如果页面名称为“平阳站(湖南省)”,则返回的值为“平阳站”。
local page = self:getPage()
return page and page:match '^(.-)%s*[(%(].-[%))]$' or page
elseif stationNames and stationNames[self.data.id] then
local specifiedName = stationNames[self.data.id]
if type(specifiedName) == 'string' then
if specifiedName:match '%[%[.-%]%]' then
return specifiedName
end
return mw.text.split(specifiedName, '|')[2] or specifiedName
elseif type(specifiedName) == 'table' then
return specifiedName.name or self.data.id
else
error(("指定的线路名称的值(%s)的格式不正确,应为string或table,实际上为%s")
:format(tostring(specifiedName), type(specifiedName)))
end
else
return self.data.id
end
end
--- 获取车站名称,但是会进行格式化:
-- * 若车站未开通,则显示为灰色。
-- * 若车站还在规划中,则显示为斜体。
-- * 若车站为换乘站,则显示为粗体。
-- @see Station:getName
function Station:getStyledName(notShort)
local result = self:getName(false, notShort)
if self.data.notOpen or (self.line and self.line.data.notOpen) then
result = '<span class="graylink">' .. result .. '</span>'
end
if self.data.planning or (self.line and self.line.data.planning) then
result = '<i>' .. result .. '</i>'
end
if #self:getLines('all', 'open', true) > 0 then
result = '<b>' .. result .. '</b>'
end
return result
end
--- 获取该车站对应的页面,如:浦电路站 (4号线)、金湖站 (苏州)。
-- 如果该车站数据有page字段,则直接将其返回。
function Station:getPage()
if self.data and self.data.page then return self.data.page end
if self.type == 'branches' or self.type == 'adjacent_branches' or self.type == 'line' then
self:error "无法获取抽象车站对象对应的页面名称。"
end
if self.system == nil then return self.data.id end
local stationNames = self.system.data.stationNames
if stationNames and stationNames[self.data.id] then
local specifiedName = stationNames[self.data.id]
if type(specifiedName) == 'string' then
if specifiedName:match '%[%[.-%]%]' then
return nil
end
return mw.text.split(specifiedName, '|')[1] or specifiedName
elseif type(specifiedName) == 'table' then
return specifiedName.page or nil -- 注意可能为nil
else
self:errorf ("线路指定的名称(stationNames[%s])字段的类型不正确,应为string或table,实际为%s。", tostring(self.data.id), type(specifiedName))
end
else
return self.data.id and (self.system and self.system.data.stationFormat or '%s站'):format(self.data.id) or nil
end
end
--- 获取该车站对应的链接,如[[浦电路站(4号线)|浦电路]]、[[虹桥火车站站|虹桥火车站]]。
-- 若该车站数据中有link字段,则直接将其返回,该值将覆盖stationNames中的行为。
-- @bool isStyled 是否对样式进行加粗、灰色等操作。
function Station:getLink(isStyled, notShort)
if self.data and self.data.link then return self.data.link end
if self.type == 'branches' then
return (self.main and self.main:getLink 'short' or 'nil') .. '/' .. (self.branch and self.branch:getLink 'short' or 'nil')
elseif self.type == 'line' then
self:error "无法将抽象线路车站对象转化为链接"
elseif self.type == 'adjacent_branches' then
return (self.main and self.main:getLink(isStyled) or 'nil') .. '/' .. (self.branch and self.branch:getLink(isStyled) or 'nil')
end
local stationNames = self.system and self.system.data.stationNames or nil
local specifiedName = stationNames and stationNames[self.data.id]
if specifiedName then
if type(specifiedName) == 'table' then
return specifiedName.link or
specifiedName.page and '[[' .. specifiedName.page .. '|' .. (specifiedName.name or self.data.id) .. ']]'
or (specifiedName.name or self.data.id)
elseif type(specifiedName) == 'string' and specifiedName:find '%[%[.-%]%]' then
-- 如果指定的名称中有链接,则无论是否有isStyled和notShort,直接强制返回这个链接文本
return specifiedName
end
end
return createLink(self:getPage(), self:getName(isStyled, notShort))
end
--- 返回该车站所在系统内,拥有该车站的所有线路。如果该车站对象没有指定所在系统,则会抛出错误。
-- @return 由线路对象组成的数组。可能为空,但不会是nil。
-- @return 由这些线路中,该车站对象组成的数组。
-- @func @str[opt] condition 返回的线路中该车站必须符合此条件。@see Station:meets
-- 注意是车站要符合的条件,而不是线路要符合的条件。
-- @table avoid 是否在结果中规避该车站对象所属的线路(self.line)。
-- 例如,3号线虹桥路站的getLines(avoid = true)只会返回4号线和10号线。
-- 若为true,则规避station对象自身所在线路。
-- 若为线路对象,则规避该线路对象。这是为了避免支线或子线路上的车站没有规避自身所在完整线路,或其他类似情况。
function Station:getLines(criterion, condition, avoid)
local system = self.system
if not system then
-- 如果没有对应的系统对象,不跑错,直接返回空。
return {}, {}
end
local result = {}
local result2 = {}
for i, line in ipairs(system:getLines(criterion)) do
local lineToAvoid = avoid==true and self.line or avoid or self.line
if line and not (avoid and line==lineToAvoid or line:getSubline(lineToAvoid)) then
local stationInThisLine = line:getStation(self.data.id, condition)
if stationInThisLine then
-- 使用原始的赋值方式而非table.insert,以提高性能。
result[#result + 1] = line
result2[#result2 + 1] = stationInThisLine
end
end
end
return result, result2
end
-- 以下部分为模块调用专用。
function p.stationLink(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local yesno = require 'Module:Yesno'
local station_id = args.name or args.station or args.station_id -- 车站名。注意实质上是车站id,因此并不是station:getName()得到的名字。
local system_name = args.system
local isStyled = yesno (args.styled or args.isStyled, false)
local short = yesno (args.short)
if short==nil then short=true end
if not station_id then
error "请提供车站id(参数station)"
end
return System.load(system_name):getStation(station_id):getLink(isStyled, not short)
end
function p.lineColor(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id -- 线路名。注意是在lines字段中的键,而不是line:getName()得到的线路名。
if not line_id then
error "请提供线路id(参数line)"
end
local system_name = args.system
local prefixed = require 'Module:Yesno' (args.prefixed or args.prefix)
local optional = require 'Module:Yesno' (args.optional)
local line = System.load(system_name):getLine(line_id)
return line and line:getColor(prefixed) or args.default
or (optional and '' or error('找不到id为“' .. tostring(line_id) .. '”的线路'))
end
function p.lineTitle(frame)
mw.addWarning("已弃用的函数lineTitle,请使用lineLink。")
return p.lineLink(frame)
end
function p.lineLink(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id
if not line_id then
error "请提供线路id(参数line)"
end
local system_name = args.system
local style = args.nameType or args.name_type or args.style
local rich = require 'Module:Yesno' (args.rich)
local optional = require 'Module:Yesno' (args.optional)
local system = System.load(system_name)
local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
local styled = require 'Module:Yesno' (args.styled)
return line and line:getLink(style, rich, styled) or ''
end
function p.lineName(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id
if not line_id then
error "请提供线路id(参数line)"
end
local system_name = args.system
local nameType = args.nameType or args.name_type or args.style
local rich = require 'Module:Yesno' (args.rich)
local system = System.load(system_name)
local optional = require 'Module:Yesno' (args.optional)
local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
return line and line:getName(nameType, rich) or ''
end
function p.linePage(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id
if not line_id then
error "请提供线路id(参数line)"
end
local system_name = args.system
local optional = require 'Module:Yesno' (args.optional)
local system = System.load(system_name)
local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
return line and line:getPage() or ''
end
function p.lineRichTitle(frame)
mw.addWarning("已弃用的函数lineRichTitle,请使用lineRichLink。")
return p.lineRichLink(frame)
end
function p.lineRichLink(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
if not line_id then
error "请提供线路id(参数line)"
end
local optional = require 'Module:Yesno' (args.optional)
local system_name = args.system or error "缺少参数:system"
local style = args.nameType or args.name_type or args.style
local styled = require 'Module:Yesno' (args.styled or args.isStyled)
local system = System.load(system_name)
local line = not optional and system:getLineOrThrow(line_id) or system:getLine(line_id)
return line and line:getLink(style, true, styled) or ''
end
function p.lineStationList(frame)
local yesno = require 'Module:Yesno'
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
local system_name = args.system or error "缺少参数:system"
local system = System.load(system_name)
local separator = args.separator or ' – '
local params = {}
params.showInterchange = yesno(args.showInterchange, true)
params.isStyled = yesno(args.styled or args.isStyled, true)
params.noLink = yesno(args.noLink)
params.condition = args.condition
params.interchangeCondition = args.interchangeCondition
params.stationCondition = args.stationCondition
return table.concat(system:getLine(line_id):renderStationList(params), separator)
end
function p.lineTerminalLink(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
local system_name = args.system or error "缺少参数:system"
local system = System.load(system_name)
local line = system:getLine(line_id)
local terminal = line:getTerminalStation(args.side or args.direction, args.branch and {type = args.type, branch = args.branch} or args.type, args.condition)
if terminal and terminal.getLink then
return terminal and terminal:getLink() or ''
else return '' end
end
function p.lineTerminal(frame)
mw.addWarning '已弃用的函数lineTerminal,请使用lineTerminalLink'
return p.lineTerminalLink(frame)
end
function p.lineTerminalName(frame)
local args = require 'Module:Arguments'.getArgs(frame)
local line_id = args.name or args.line or args.line_id or error "缺少参数:line"
local system_name = args.system or error "缺少参数:system"
local system = System.load(system_name)
local line = system:getLine(line_id)
local terminal = line:getTerminalStation(args.side or args.direction, args.branch and {type = args.type, branch = args.branch} or args.type, args.condition)
return terminal and terminal:getName() or ''
end
-- @deprecatd
function p.trainTime(frame)
mw.addWarning "trainTime函数已经弃用,请考虑不再使用此函数。"
local args = require 'Module:Arguments'.getArgs(frame)
local t = args.type
if t == 'f' or t == 'F' then t = 'first' end
if t == 'l' or t == 'L' then t = 'last' end
local system = System.load(args.system or error "缺少参数:system")
local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
local h,m,s = line:getTrainTime(args.dir, t, args.delta)
return string.format('%02d:%02d', h, m)
end
-- @deprecated
function p.trainDirectionTime(frame)
(allowDeprecated and mw.addWarning or error) "trainDirectionTime函数已经弃用,请考虑不再使用此函数。"
local args = require 'Module:Arguments'.getArgs(frame)
local t = args.type
if t == 'f' or t == 'F' then t = 'first' end
if t == 'l' or t == 'L' then t = 'last' end
local system = System.load(args.system or error "缺少参数:system")
local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
local h,m,s = line:getTrainTime(args.dir, t, args.delta)
local endService = (line.data.trainTime[args.dir].endService)
local terminalStation = line:getStation(endService) or system:getStation(endService)
--[[
if sparts[1] == 'station_name' then
str = plain_replace(str, '{' .. c .. '}', terminal)
elseif sparts[1] == 'station_link' then
str = plain_replace(str, '{' .. c .. '}', p._internalStationLink(terminal, '', true, a.system, frame))
elseif sparts[1] == 'train_time' then
str = plain_replace(str, '{' .. c .. '}', p._formatTemplateTime(frame, sparts, hour, minute, sec))
end
]]
local template = '往{station_link}方向:{train_time|H:i|次日}'
return template:gsub('%{(.-)%}', function (content)
local sparts = mw.text.split(content, '|')
if content == 'station_name' then return terminalStation
elseif sparts[1] == 'station_link' then
return terminalStation:getLink()
elseif sparts[1] == 'train_time' then
return deprecated._formatTemplateTime(frame, sparts, h, m, s)
end
end)
end
-- @deprecated
function p.stationTrainTime(frame)
(allowDeprecated and mw.addWarning or error) "stationTrainTime函数已经弃用,请考虑不再使用此函数。"
local args = require 'Module:Arguments'.getArgs(frame)
local system = System.load(args.system or error "缺少参数:system")
local dir_reprs = {}
local min_time_data = 999999
local max_time_data = -999999
local line_names = mw.text.split(args.name, ',')
for k, v in pairs(args) do
if type(k) == 'number' then
dir_reprs[k] = v
end
end
for k, line_name in ipairs(line_names) do
for i = 1, #(dir_reprs), 2 do
local dir = dir_reprs[i]
local diff = dir_reprs[i + 1]
local dir_times = system.data.lines[line_name].trainTime[dir]
if dir_times then
local dh, dm, ds = deprecated._parseTime(diff)
for _, dir_time in ipairs({dir_times.first, dir_times.last}) do
if dir_time ~= nil then
local h, m, s = deprecated._parseTime(dir_time)
local t = (h * 60 + m) * 60 + s + (dh * 60 + dm) * 60 + ds
min_time_data = math.min(min_time_data, t)
max_time_data = math.max(max_time_data, t)
end
end
end
end
end
local min_h, min_m, min_s = deprecated._extractSeconds(min_time_data)
local max_h, max_m, max_s = deprecated._extractSeconds(max_time_data)
local str = frame.template
str = str or deprecated.DEFAULT_STATION_TRAIN_TIME_TEMPLATE
local l = 0
return str:gsub('%{(.-)%}', function (content)
local sparts = mw.text.split(content, '|')
if sparts[1] == 'start_time' then
return deprecated._formatTemplateTime(frame, sparts, min_h, min_m, min_s)
elseif sparts[1] == 'end_time' then
return deprecated._formatTemplateTime(frame, sparts, max_h, max_m, max_s)
end
end)
end
-- @deprecated
function p.lineDateMessage(frame)
(allowDeprecated and mw.addWarning or error) "lineDataMessage函数已经弃用,请考虑不再使用此函数。"
local args = require 'Module:Arguments'.getArgs(frame)
local system = System.load(args.system or error "缺少参数:system")
local line = system:getLine(args.name or args.line or args.line_id or error "缺少参数:line")
local reprs, options = {}, {}
local preserved = { name = true, ['type'] = true, system = true }
return line:getDateMessage(args.type, args)
end
local testCases = {
-- {function, args, expected_result}
{p.stationLink, {name='宁波站', system='UseCase'}, '[[宁波站|宁波站]]'},
{p.stationLink, {name='寧波站', system='UseCase'}, '[[宁波站|宁波站]]'},
{p.stationLink, {name='高桥西', system='UseCase'}, '[[高桥西站(宁波)|高桥西]]'},
{p.stationLink, {name='高桥西', short='0', system='UseCase'}, '[[高桥西站(宁波)|高桥西站]]'},
{p.stationLink, {name='泽民', system='UseCase'}, '[[泽民站|泽民]]'},
{p.stationLink, {name='内嵌模板', system='UseCase'}, '[[内嵌模板站|内嵌|模板]]'},
{p.lineColor, {name='1', system='UseCase'}, '3180b7'},
{p.lineColor, {name='1', prefix='1', system='UseCase'}, '#3180b7'},
-- {p.lineColor, {name='1', system='TestComp'}, 'cc0000'},
{p.lineLink, {name='1', system='UseCase'}, "[[宁波轨道交通1号线|1号线]]"},
{p.lineLink, {name='WRL', system='UseCase'}, "[[西鐵綫|西鐵綫]]"},
-- {p.lineLink, {name='WRL', link='0', system='UseCase'}, "西鐵綫"},
-- {p.lineTitle, {name='1', system='TestComp'}, '[[哈尔滨地铁1号线|1号线]]'},
{p.lineTerminal, {name='S1', side='left', type='F', system='UseCase'}, '[[宁波东站|宁波东站]]'},
-- {p.lineTerminal, {name='1', side='left', type='s1', branch='b1', system='TestComp'}, '[[S1B1]]'},
{p.lineTerminalName, { name='1', side='left', system='UseCase'}, '高桥西'},
{p.lineTerminalName, { name='S1', side='left', system='UseCase'}, '宁波站'},
{p.lineTerminalName, { name='S1', side='left', type='F', system='UseCase'}, '宁波东站'},
{p.lineTerminalName, { name='S1', side='right', system='UseCase'}, '余姚'},
{p.lineTerminalName, { name='S1', side='right', type='F', system='UseCase'}, '马渚'},
{p.lineTerminalName, { name='S1', side='right', type='F', branch='HZW', system='UseCase'}, '杭州湾'},
-- {p.lineTerminalName, { name='1', side='left', ['type']='s1', branch='b1', system='TestComp'}, 'S1B1'},
-- {p.lineTerminalName, { name='1', side='left', ['type']='s1', system='TestComp'}, 'S1DEFAULT'},
-- {p.lineTerminalName, { name='1', side='left', ['type']='s2', system='TestComp'}, 'DEFAULT'},
{p.trainTime, {name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}, '22:06'},
{p.trainTime, {name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}, '06:08'},
{p.trainDirectionTime, {name='1', dir='霞高', ['type']='L', delta='0:3', system='UseCase'}, "往[[高桥西站(宁波)|高桥西]]方向:22:06"},
{p.trainDirectionTime, {name='1', dir='高霞', ['type']='F', delta='0:4', system='UseCase'}, "往[[霞浦站(宁波)|霞浦]]方向:06:08"},
{p.stationTrainTime, {'高霞', '0:3', name='1', system='UseCase'}, "06:07-22:08"},
{p.stationTrainTime, {'高霞', '0:3', '栎清', '0:1', name='1,2', system='UseCase'}, "06:07-22:10"},
{p.lineDateMessage, {'在', 'year', '的', 'month', '的', 'day', ',月份', 'ym', ',日期', 'date', '开通', name='1', ['type']='2', cur_time = '2016-1-1', auto_hide=true, system='UseCase'}, '在2016年的3月的19日,月份2016年3月,日期2016年3月19日开通'},
{p.lineDateMessage, {'year', name='1', ['type']='2', cur_time = '2016-5-19', auto_hide=true, system='UseCase'}, '2016年' },
{p.lineDateMessage, {'year', name='1', ['type']='2', cur_time = '2017-3-19', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_DATE_ELAPSED_TEXT},
{p.lineDateMessage, {'ym', name='1', ['type']='2', cur_time = '2016-3-30', auto_hide=true, system='UseCase'}, '2016年3月'},
{p.lineDateMessage, {'ym', name='1', ['type']='2', cur_time = '2016-4-19', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_DATE_ELAPSED_TEXT},
{p.lineDateMessage, {'ymd', name='1', ['type']='2', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}, '2016年3月19日'},
{p.lineDateMessage, {'ymd', name='1', ['type']='2', cur_time = '2016-3-19', auto_hide=true, system='UseCase'},deprecated.DEFAULT_DATE_ELAPSED_TEXT},
{p.lineDateMessage, {'ymd', name='S1', cur_time = '2016-3-18', auto_hide=true, system='UseCase'}, '2019年12月28日'},
{p.lineDateMessage, {'二期规划中', name='2', auto_hide=true, system='UseCase'},'二期规划中'},
{p.lineDateMessage, {'ymd', name='2', auto_hide=true, system='UseCase'}, deprecated.DEFAULT_LINE_DATE_ABSENT_TEXT},
{p.renderStationLinks, {system='UseCase', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}, '1号线从[[高桥西站(宁波)|高桥西]]\n|到[[霞浦站(宁波)|霞浦]]'},
{p.renderStationLinks, {system='UseCase', short='0', '1号线从$$高桥西$$\n', '到$$霞浦$$\n'}, '1号线从[[高桥西站(宁波)|高桥西站]]\n|到[[霞浦站(宁波)|霞浦站]]'},
}
function p.testCase(frame)
frame = frame or mw.getCurrentFrame()
local fail = 0
local success = 0
local root = mw.html.create()
local ol = root:tag 'ol'
for k1, v1 in ipairs(testCases) do
local func, args, expected = (unpack or table.unpack)(v1)
local state, result = pcall(func, frame:newChild{args=args})
if not state then
fail = fail + 1
ol :newline()
:tag 'li'
:wikitext(string.format('函数调用出错:<tt class="error">%s</tt>,预期为<code>%s</code>', result, mw.text.nowiki(expected)))
elseif result ~= expected then
fail = fail + 1
ol :newline()
:tag 'li'
:wikitext(string.format('结果<code>%s</code>不符合预期<code>%s</code>', mw.text.nowiki(tostring(result)), mw.text.nowiki(expected)))
else
success = success + 1
ol :newline()
:tag 'li'
:css('color','gray')
:wikitext(string.format('测试成功,结果:<tt>%s</tt>', mw.text.nowiki(tostring(result))))
end
end
return root:newline():wikitext(string.format("测试完成,共成功%s次,失败%s次。",success,fail))
end
--- 从[[Module:Adjacent stations]]的子页面的数据中导入,并返回转换后的数据。
-- 未来将被弃用!
function p.importAdjacentStations(name)
local data = mw.loadData ('Module:Adjacent stations/' .. name)
local result = {}
for k, v in pairs(data) do
if type(v)~='table' and k ~= 'system title' and k ~= 'system icon'
and k ~= 'station format' and k ~= 'lines' and k ~= 'aliases' then
result[k] = v
end
end
result.title = data['system title']
do
local icon = data['system icon']
if icon then
icon = icon:match '%[%[(.-)%]%]' or icon
result.icon = {}
for piece in mw.text.gsplit(icon, "|") do
local matched
if (function ()
matched = piece:match '^alt=(.*)' return matched end)() then
result.icon.alt = matched
elseif (function ()
matched = piece:match '^link=(.*)' return matched end)() then
result.icon.link = matched
elseif piece:match '^%d+px$' or piece:match '^%d+x%d+px$' then
result.icon.size = piece
else
result.icon.file = result.icon.file or piece
end
end
end
end
result.stationNames = {}
for k, v in pairs(data['station format'] or {}) do
if type(v) == 'table' then
v = v[1]
end
v = tostring(v):gsub('%%1', k)
local stripped = v:match '^%[%[([^%[%]]+)%]%]$'
if stripped then
v = stripped
elseif not v:find '%|' then
v = v .. '|' .. k
end
result.stationNames[k] = v
end
result.lines = {}
for k, v in pairs(data.lines or {}) do
if type(k) == 'string' then
local line = {
link = v.title,
terminus = {
},
color = v.color,
isLoop = v.circular
}
for _, key in ipairs{'left terminus', 'right terminus'} do
local v1 = v[key]
line.terminus[#line.terminus + 1] = type(v1) == 'table' and {main = v1[1], branch = v1[2]} or tostring(v1)
end
if v.title then
local name = v.title:match '^%[%[(.-)%]%]$'
if name then
local split = mw.text.split(name, '%|')
line.page = split[1]
line.name = split[2] or split[1]
else
line.name = name
end
end
if not (line.name or line.short_name) then
-- 确保线路拥有名字
line.name = k
line.page = line.page or line.name
end
for k1, v1 in pairs(v) do
if type(v) ~= 'table' and k1 ~= 'title' and k1 ~= 'left terminus'
and k1 ~= 'right terminus' and k1 ~= 'types' and k1 ~= 'circular' and k1 ~= 'color' then
line[k1] = v1
end
end
if v.types and v.types['在建'] then
table.insert(line.terminus, 1, {notOpen = true, id = v.types['在建']['left terminus']})
table.insert(line.terminus, {notOpen = true, id = v.types['在建']['right terminus']})
end
result.lines[k] = line
end
end
for k, v in pairs(data.aliases or {}) do
result.lines[k] = result.lines[v]
end
result._imported = true
return result
end
-- 仅出于兼容性考虑存在
-- 参见 {{RenderStations}}
function p._internalStationLink(name, style, short, system_data, frame)
local station_parts, station_data
local station_link, station_name
local repr = ''
if short == nil then short = true end
local function marshalStation(station_link, station_name, style)
if not short then
local link_pos, _ = string.find(station_link, ' %(')
if link_pos ~= nil then
station_name = string.sub(station_link, 1, link_pos - 1)
else
station_name = station_link
end
end
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 type(system_data) == 'string' then
system_data = _loadSystemData(system_data, false)
end
if system_data == nil then
return nil
end
if system_data.stationNames[name] ~= nil then
station_data = system_data.stationNames[name]
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 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 .. marshalStation(station_link, station_name, style)
end
end
return repr
else
station_link = name .. '站'
return marshalStation(station_link, name, style)
end
end
local function _loadSystemData(system, raises)
local system_data = nil
local state
if raises == nil then
raises = true
end
if system ~= nil then
state, system_data = pcall(mw.loadData, "Module:RailSystems/" .. system)
if not state then
if raises then
error(string.format('铁道系统“%s”的数据模块不存在', system))
else
system_data = nil
end
end
end
return system_data
end
local function _convBool(val, default)
if val == nil then
return default
elseif type(val) == 'boolean' then
return val
elseif mw.text.trim(val) == '' then
return default
end
return (mw.text.trim(val) == '1')
end
function p.renderStationLinks(frame)
local a = frame:getParent().args
local short = _convBool(a.short, true)
local spos, epos, station_name = 1, 1, nil
local pos = 1
local system_data = _loadSystemData(a.system)
local sys_args = { system=true, short=true }
local text = ''
for k, v in frame:getParent():argumentPairs() do
if type(k) == 'number' then
if string.len(text) > 0 then
text = text .. '|' .. v
else
text = v
end
else
if sys_args[k] == nil then
if string.len(text) > 0 then
text = text .. '|' .. k .. '=' .. v
else
text = k .. '=' .. v
end
end
end
end
text = mw.text.trim(text)
local out_text = ''
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._internalStationLink(station_name, '', short, system_data, frame)
pos = epos + 1
else
out_text = out_text .. string.sub(text, pos, string.len(text))
end
end
return out_text
end
return p