Модуль:London DB

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Документация
Схема всех систем вместе (показан также Tramlink, не включённый в модуль)

Этот модуль строит списки станций по линиям для 4 транспортных систем Лондона.

Пояснения про реальность

[править код]

В Лондоне нет понятия «пересадочный узел», все соединённые переходами платформы считаются одной станцией, а нумерация платформ на ней сквозная, причём фактически нумеруются не платформы, а пути. Однако разные части станции могут соответствовать разным линиям (хотя разные линии, иногда даже разных систем, могут использовать одни и те же платформы и пути). Линией, в свою очередь, фактически называется маршрут либо группа маршрутов (особенно на наземных и мелких участках; на глубоких участках более однозначно соответствие между путями и линиями, не зависящее от маршрутов).

В Лондоне в некоторых местах пересадка требует выхода за турникеты и повторного входа, но дополнительная оплата при этом не взимается, если между выходом и входом прошло менее заданного числа минут (в разных местах разное). В модуле большинство таких случаев не будут показаны одной строкой, однако, возможно, удастся обозначить такие переходы при помощи примечаний.

Таблица

[править код]
Вывеска станции, на которой есть платформы всех систем (сверху вниз: DLR, Elizabeth line, Overground, Underground, National Rail)
Железнодорожный переезд через пути Overground
Поезд метро (красный) и поезд дальнего следования на одной станции

Большая таблица stations (около 600 строк) содержит полный список действующих станций четырёх систем, расположенных в основном на территории Лондона и управляемых TfL:

  • London Underground — метрополитен;
  • London Overground — сеть электричек;
  • DLR — автоматизированное лёгкое метро, в основном эстакадное;
  • Crossrail — скоростная сеть электричек, частично подземная (пока открыта одна линия — Elizabeth line).

Формально метрополитенами являются только первая и третья системы, но все они интегрированы с точки зрения входа-выхода и пересадок.

Существует пятая система, не управляемая TfL и не являющаяся городским транспортом, но интегрированная с первыми четырьмя в черте Лондона, — National Rail. Для станций, обслуживающих наряду с некоторыми из первых четырёх систем также National Rail, этот факт указывается, однако полный список станций National Rail не предусмотрен.

Функция и параметры

[править код]

Главная функция list строит таблицу со списком станций.

  • Если в первый параметр передано слово London, то в список включаются все станции, присутствующие в таблице stations, и в том порядке, как они там перечислены. После названия станции приведено написание этого названия в статьях Википедии на некоторых других языках (выбраны языки, передающие звучание, для сравнения с записью на русском).
  • Если в первый параметр передано обозначение линии либо системы (в соответствии в обозначениями в таблице lines либо systems соответственно), а остальные параметры не заданы, то в список включаются только станции соответствующей линии либо системы, а список сортируется по русскому алфавиту.
  • Если в первом параметре задана линия, то поддерживаются второй и третий параметры, в которые передаются номера первой и последней станций участка линии, который надо изобразить (если третий параметр равен второму, его можно оставить пустым). Номера соответствуют значениям поля num в большой таблице. Список сортируется по порядку расположения станций на участке.
  • В предыдущем случае если поменять местами значения во втором и третьем параметрах, то порядок станций будет обращён, а если добавить непустое значение в именованном параметре hor, то список будет расположен не по вертикали, а по горизонтали.
  • Если в качестве первой станции (второй параметр) передать 0, то будут выданы все станции линии по порядку нумерации с добавлением номеров и ссылок на категории Викисклада, а также с разделительными линиями там, где номера идут не подряд (это те места, где намеренно оставлены разрывы в нумерации, чтобы обозначить переход на другую ветку).

Основной способ обращения к функции — с линией в первом параметре и с номерами станций (остальные способы существуют только для отладки). Выдача этой функции при основном способе обращения представляет собой таблицу из 4 столбцов. Первый столбец — название станции. Второй столбец — фотография той части станции, которая соответствует заданной линии. Третий столбец — тарифная зона, географическое положение станции и год её пуска. Четвёртый столбец — линии, на которые возможна пересадка. Между вторым и третьим столбцами проходит цветная полоса с кружочком у каждой станции. Можно отменить полосу до первого кружочка (выше или левее его) или после последнего (ниже или правее), передав непустое значение в именованные параметры white1 и white2.

Другие функции

[править код]

Разрабатываются возможности для рисования полос, соединяющих таблицы, выдаваемые главной функцией.

Возможности развития

[править код]

В модуле есть возможность добавить поддержку для других городов. Для этого надо добавить для каждого города:

  • строчки в таблицах cities, systems, lines, причём ключ в каждой из них должен остаться уникальным;
  • подстраницу по аналогии с Модуль:London DB/London.json (на месте слова London должно быть название города в том же написании, которое использовано в таблицах cities и systems).

Есть смысл добавлять города, в которых помимо собственно метрополитена имеется ещё как минимум одна рельсовая транспортная система, например со скоростными пригородными поездами, и много пересадочных станций между системами (Париж, Москва, Берлин).

Надо помнить, что у каждого города может быть своя специфика, например в Париже возможна ситуация, когда на станции есть платформы двух разных линий, но перехода с линии на линию (без выхода за турникеты) на этой станции нет.

Источники

[править код]
  • Источник по топологии станций и их взаимосвязей здесь.
  • Источники по произношению английских названий (увы, они вместе не покрывают всего): 1, 2, 3.
  • Источник по бесплатным пересадкам (которые пока не отражены в модуле) здесь.
  • Источник по операторам National Rail (которые пока не отражены в модуле) здесь или здесь.
  • Источник по датам открытия станций (которые пока не отражены в модуле; только Underground) здесь.
  • Источник по доступности станций для инвалидов (которая пока не отражена в модуле) здесь.
  • Источник по глубинам станций (которые пока не отражены в модуле; только Underground) здесь.

Отладка

[править код]

Черновики обращений к модулю находятся на его подстраницах.

local p = {}
--local getArgs=require('Module:Arguments').getArgs
local filter
local filtertype
local color=''
local color0
local prevx
local s
local name
local transfers
local transfers_coll
local zone
local subdiv
local comment
local year
local sorted
local from
local to
local dbg
local list0
local lastnum
local hor
local doub
local city
local subdivlabel
local subdivlabel_local
local gallery
local fra
local stations
local P='padding:0px;border-width:0px;'
local W2='|}\r\n'
local T='{| class="standard" cellspacing=0 cellpadding=0 style="table-layout:fixed;margin:0px;border-width:0px;"\r\n'
local a=10
local r=10
local q=120
local cities = {
	['London']={placeholder='Cable Car roundel (no text).svg',subdivlabel='боро'},
}
local systems = {
	['Underground']={logo='Underground no-text.svg',city='London'},
	['Overground']={logo='Overground roundel (no text).svg',city='London'},
	['DLR']={logo='DLR no-text roundel.svg',city='London'},
	['Crossrail']={logo='Elizabeth line roundel (no text).svg',city='London'},
	['NR']={logo='National Rail logo.svg',city='London',placeholder='National Rail logo.svg'},
}
local lines = {
	['BLOO']={sys='Underground',name='Бейкерлоо',color='#B36305'},
	['CEN']={sys='Underground',name='Центральная',color='#E32017'},
	['CIR']={sys='Underground',name='Кольцевая',color='#FFD300'},
	['DIS']={sys='Underground',name='Дистрикт',color='#00782A'},
	['HC']={sys='Underground',name='Хаммерсмит-<br>энд-Сити',color='#F3A9BB'},
	['JUB']={sys='Underground',name='Юбилейная',color='#A0A5A9'},
	['MET']={sys='Underground',name='Метрополитен',color='#9B0056'},
	['NOR']={sys='Underground',name='Северная',color='#000000'},
	['PIC']={sys='Underground',name='Пикадилли',color='#003688'},
	['VIC']={sys='Underground',name='Виктория',color='#0098D4'},
	['WC']={sys='Underground',name='Ватерлоо-<br>энд-Сити',color='#95CDBA'},
	['LO-WEA']={sys='Overground',name='Уивер',color='#9a2c62',color0='white'}, --Ли-Вэлли
	['LO-WR']={sys='Overground',name='Уиндраш',color='#ff4e5a',color0='white'}, --Ист-Лондон
	['LO-LIB']={sys='Overground',name='Либерти',color='#5e6867',color0='white'}, --Эмерсон
	['LO-SUF']={sys='Overground',name='Сафраджет',color='#49c07d',color0='white'}, --Баркинг
	['LO-MM']={sys='Overground',name='Майлдмей',color='#3784c7',color0='white'}, --Норт-Лондон
	['LO-LIO']={sys='Overground',name='Лайонесс',color='#feb231',color0='white'}, --Уотфорд
	['DLR']={sys='DLR',name='DLR',color='#00A4A7',color0='white'},
	['ELIZ']={sys='Crossrail',name='Элизабет',color='#6950a1',color0='white'},
	['NR']={sys='NR',name='Нашенал Рейл'},
}
function p.list(frame)
	init(frame)
	fra=frame
	filter=frame.args[1] --getArgs(frame)[1]
	from=frame.args[2]
	if from then
		from=tonumber(from)
		sorted=true
	end
	to=frame.args[3]
	if to==nil or to=='' then
		to=from
	else
		to=tonumber(to)
	end
	local obj=lines[filter]
	if obj then
		filtertype='line'
		city=systems[obj.sys].city
		color=obj.color
		color0=obj.color0
	else
		obj=systems[filter]
		if obj then
			filtertype='system'
			city=obj.city
		else
			obj=cities[filter]
			if obj then
				filtertype='none'
				city=filter
			else
				return '<b>Ошибка в параметрах модуля London DB.</b>'
			end
		end
	end
	subdivlabel=cities[city].subdivlabel
	dbg=from==0
	hor=frame.args['hor']~=nil
	doub=frame.args['doub']
	stations=mw.loadJsonData('Модуль:London DB/'..city..'.json').stations
	list0={}
	s=''
	local cap=''
	if filtertype=='line' then
		local obj=lines[filter]
		cap=bullet(obj.sys)..'&nbsp;'..obj.name
	elseif filtertype=='system' then
		cap=bullet(filter)
	end
	if dbg then
		s=s..cap..'\r\n'
	end
	s=s..T
	if not sorted then
		if filtertype=='none' then
			s=s..'|-\r\n!Станция\r\n!Линии\r\n'
		else
			s=s..'|-\r\n!Станция\r\n!Фото по линиям\r\n!География и история\r\n!Пересадки\r\n'
		end
	end
	prevx=nil
	-- loop from here
	for n,x in pairs(stations) do
		addone(x)
		prevx=x
	end
	-- loop till here
	s=s..onerow(prevx)
	if filtertype=='none' then
		return s..'|}'
	end
	if dbg or not sorted or from<to then
		table.sort(list0, function(a,b) return a.num<b.num end)
	else
		table.sort(list0, function(a,b) return a.num>b.num end)
	end
	local prevx0
	local s_saved=s
	s=''
	if hor then
		if dbg then
			prevx0=nil
			s=s..'|-\r\n'
			for n0,x0 in pairs(list0) do
				if prevx0~=nil and x0.num~=nil and prevx0.num+1~=x0.num then
					s=s..'|rowspan=5 bgcolor=0|\r\n'
				end
				s=s..'|'..x0.num..'\r\n'
				prevx0=x0
			end
		end
		s=s..H(1)
		for n0,x0 in pairs(list0) do
			s=s..WW(1)..W1(1,x0.name,x0.comment)..'\r\n'
			if dbg then
				local com=commons(x0.en)
				s=s..'\r\n'..WW(0)..'\r\n*[[:en:'..x0.en..'|'..x0.en..']]\r\n*[[:commons:'..com..'|'..string.sub(com,10)..']]\r\n*'
			end
			s=s..W2
		end
		s=s..H(2)
		for n0,x0 in pairs(list0) do
			s=s..WW(2)..W1(2)..x0.photo..'\r\n'..W2
		end
		s=s..'|-\r\n'
		for n0,x0 in pairs(list0) do
			s=s..WW(0)..'\r\n'..inner(n0==1,n0==#list0,1)
		end
		if doub then
			s=s..'|-\r\n'
			for n0,x0 in pairs(list0) do
				s=s..WW(0)..'\r\n'..inner(n0==1,n0==#list0,1)
			end
		end
		s=s..H(3)
		for n0,x0 in pairs(list0) do
			s=s..WW(3)..W1(3,x0.zone,x0.subdiv,x0.year,x0.subdivlabel)..'\r\n'..W2
		end
		s=s..H(4)
		for n0,x0 in pairs(list0) do
			if x0.transfers then
				s=s..WW(4)..W1(4,x0.transfers)..'\r\n'..W2
			else
				s=s..WW(4)..'&nbsp;\r\n'
			end
		end
	else
		prevx0=nil
		for n0,x0 in pairs(list0) do
			if dbg and prevx0~=nil and x0.num~=nil and prevx0.num+1~=x0.num then
				s=s..'|-\r\n|colspan=6 bgcolor=0|\r\n'
			end
			s=s..H(0)..WW(1)
			if dbg then
				local com=commons(x0.en)
				s=s..W1(1)..x0.num..'\r\n'..W2..'| style="border-width:0px;" |\r\n'..W1(0)..'*[[:en:'..x0.en..'|'..x0.en..']]\r\n*[[:commons:'..com..'|'..string.sub(com,10)..']]\r\n*'..x0.name
			else
				s=s..W1(1,x0.name,x0.comment)
			end
			s=s..'\r\n'..W2..WW(2)
			if filtertype=='system' then
				s=s..x0.photo..'\r\n'
			else
				s=s..W1(2)..x0.photo..'\r\n'..W2
				s=s..inner(n0==1,n0==#list0,1)
				if doub then
					s=s..inner(n0==1,n0==#list0,2)
				end
			end
			s=s..WW(3)..W1(3,x0.zone,x0.subdiv,x0.year,x0.subdivlabel)..'\r\n'..W2..WW(4)..W1(4,x0.transfers)..'\r\n'..W2
			prevx0=x0
		end
	end
	return s_saved..s..'|}'
end
function qq(num,calc)
	if calc then
		if doub=='middle' then
			if num==1 then
				return q-r
			elseif num==3 or num==4 then
				return q-r/2
			end
		elseif doub=='back' then
			if num==1 then
				return q-2*r
			end
		elseif doub=='forward' then
			if num==3 or num==4 then
				return q-r
			end
		end
	end
	return q
end
function W1(num,c0,c1,c2,c3)
	local s='\r\n{|'
	local qw=qq(num,not hor)
	local qh=qq(num,hor)
	if num==2 or num==0 then
		s=s..'cellspacing=0 cellpadding=0 style="width:100%;'
	else
		s=s..'style="width:'..qw..'px;'
	end
	s=s..'height:'..qh..'px;'
	if filtertype~='system' then
		s=s..'outline:1px solid '..color..';outline-offset:-1px;'
		if num==1 then
			s=s..'border-top-left-radius:20px;'
			if hor then
				s=s..'border-top-right-radius:20px;'
			else
				s=s..'border-bottom-left-radius:20px;'
			end
		elseif num==4 then
			s=s..'border-bottom-right-radius:20px;'
			if hor then
				s=s..'border-bottom-left-radius:20px;'
			else
				s=s..'border-top-right-radius:20px;'
			end
		end
	end
	if num==1 or num==3 or num==4 then
		s=s..'"\r\n|- valign=top\r\n|\r\n'
	else
		s=s..'"\r\n|-\r\n|\r\n'
	end
	if c0~=nil and c0>'' then
		if num==4 and filtertype~='system' then
			s=s..'<div style="height:0px;width:0px;">\r\n'
			s=s..'{| cellspacing=0 cellpadding=0 style="height:'..qh..'px;width:'..qw..'px;color:gray;">\r\n|- valign=top\r\n|<small>'
			s=s..'пересадки:'
			s=s..'</small>\r\n|}\r\n</div>\r\n'
		end
		if num==1 and c1~=nil and filtertype~='system' then
			s=s..'<div style="height:0px;width:0px;">\r\n'
			s=s..'{| cellspacing=0 cellpadding=0 style="height:'..qh..'px;width:'..qw..'px;color:gray;">\r\n|- valign=bottom\r\n|<small>'
			s=s..c1..'<br><br>'
			s=s..'</small>\r\n|}\r\n</div>\r\n'
		end
		s=s..'<div style="height:0px;width:0px;">\r\n'
		if num==1 or num==3 then
			s=s..'{| cellspacing=0 cellpadding=0 style="height:'..qh..'px;width:'..qw..'px;">\r\n|- valign=top\r\n|'
			if filtertype~='system' and num~=3 then
				s=s..'<br>'
			end
		else
			s=s..'{| cellspacing=0 cellpadding=0 style="height:'..qh..'px;width:'..qw..'px;">\r\n|-\r\n|'
		end
		if num==3 then
			s=s..'<span style="color:gray;"><small>тарифная зона:</small></span><br>'
			local t=c0
			if c0=='-' then
				t='особый тариф'
			end
			t='<b>'..t..'</b>'
			if c0=='-' then
				if q==120 then
					s=s..t
				else
					s=s..'<small>'..t..'</small>'
				end
			else
				if q==120 then
					s=s..'<big>'..t..'</big>'
				else
					s=s..t
				end
			end
			s=s..'<br><span style="color:gray;"><small>'
			if c3 then
				s=s..c3
			else
				s=s..subdivlabel
			end
			s=s..':</small></span><br>'..c1
			if c2>'' then
				s=s..'<br><span style="color:gray;"><small>год открытия:</small></span><br><b>'..c2..'</b>'
			end
		elseif q==120 then
			s=s..c0
		else
			s=s..'<small>'..c0..'</small>'
		end
		s=s..'\r\n|}\r\n</div>\r\n'
	end
	return s
end
function wh(size)
	if color0==nil then
		return ''
	elseif hor then
		return '<div style="width:'..size..';height:4px;background-color:'..color0..';"></div>'
	else
		return '<div style="width:4px;height:'..size..';background-color:'..color0..';margin:auto;"></div>'
	end
end
function p.dummy(frame)
	init(frame)
	color=lines[frame.args[1]].color
	color0=lines[frame.args[1]].color0
	hor=frame.args['hor']~=nil
	local s=T
	local thi=frame.args['thickness']
	if thi==nil then
		thi=q
	end
	local Y=thi..'px;'
	local q1=q
	local q2=q
	doub=frame.args['doub']
	if doub=='middle' then
		q1=q-r
		q2=q-r
	elseif doub=='back' then
		q1=q-2*r
	elseif doub=='forward' then
		q2=q-2*r
	end
	if hor then
		s=s..'|- style="height:'..(r-a/2+q1+q)..'px;line-height:1;"\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
		s=s..'|- style="height:'..a..'px;line-height:1;"\r\n'
		s=s..'| style="background-color:'..color..';width:'..Y..P..'" |'..wh(Y)..'\r\n'
		if doub then
			s=s..'|- style="height:'..(2*r-a)..'px;line-height:1;"\r\n'
			s=s..'| style="width:'..Y..P..'" |\r\n'
			s=s..'|- style="height:'..a..'px;line-height:1;"\r\n'
			s=s..'| style="background-color:'..color..';width:'..Y..P..'" |'..wh(Y)..'\r\n'
		end
		s=s..'|- style="height:'..(r-a/2+q2+q)..'px;line-height:1;"\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
	else
		s=s..'|- style="height:'..Y..'line-height:1;"\r\n'
		local XX
		if frame.args['arm1'] then
			XX=(r-a/2+frame.args['arm1'])..'px;'
		else
			XX=(r-a/2+q1*2)..'px;'
		end
		s=s..'| style="width:'..XX..P..'" |\r\n'
		s=s..'| style="background-color:'..color..';width:'..a..'px;'..P..'" |'..wh(Y)..'\r\n'
		if doub then
			s=s..'| style="width:'..(2*r-a)..'px;'..P..'" |\r\n'
			s=s..'| style="background-color:'..color..';width:'..a..'px;'..P..'" |'..wh(Y)..'\r\n'
		end
		if frame.args['arm2'] then
			XX=(r-a/2+frame.args['arm2'])..'px;'
		else
			XX=(r-a/2+q2*2)..'px;'
		end
		s=s..'| style="width:'..XX..P..'" |\r\n'
	end
	s=s..'|}\r\n'
	return s
end
function inner(first,last,call)
	local s=''
	local X=(r-a/2)..'px;'
	local Y=(q/2-r)..'px;'
	local O='background:'
	if color0 then
		O=O..'radial-gradient(7px 7px at '..r..'px '..r..'px,'..color0..' 100%,transparent 100%),'
	end
	O=O..'radial-gradient('..r..'px '..r..'px at '..r..'px '..r..'px,'..color..' 100%,transparent 100%);'
	local white1=fra.args['white1']~=nil or (doub~=nil and fra.args['white1_'..call]~=nil)
	local white2=fra.args['white2']~=nil or (doub~=nil and fra.args['white2_'..call]~=nil)
	if hor then
		s=s..T
		s=s..'|- style="height:'..X..'line-height:1;"\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
		s=s..'| rowspan=3 style="width:'..(2*r)..'px;'..P..O..'" |\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
		s=s..'|- style="height:'..a..'px;line-height:1;"\r\n'
		if white1 and first then
			s=s..'| style="width:'..Y..P..';" |\r\n'
		else
			s=s..'| style="width:'..Y..P..'background-color:'..color..';" |'..wh(Y)..'\r\n'
		end
		if white2 and last then
			s=s..'| style="width:'..Y..P..';" |\r\n'
		else
			s=s..'| style="width:'..Y..P..'background-color:'..color..';" |'..wh(Y)..'\r\n'
		end
		s=s..'|- style="height:'..X..'line-height:1;"\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
		s=s..'| style="width:'..Y..P..'" |\r\n'
	else
		s=s..'|style="min-width:'..(2*r)..'px;'..P..'"|\r\n'
		s=s..T
		s=s..'|- style="height:'..Y..'line-height:1;"\r\n'
		s=s..'| style="width:'..X..P..'" |\r\n'
		if white1 and first then
			s=s..'| style="width:'..a..'px;'..P..';" |\r\n'
		else
			s=s..'| style="width:'..a..'px;'..P..'background-color:'..color..';" |'..wh(Y)..'\r\n'
		end
		s=s..'| style="width:'..X..P..'" |\r\n'
		s=s..'|- style="height:'..(2*r)..'px;line-height:1;"\r\n'
		s=s..'| colspan=3 style="width:'..(2*r)..'px;'..P..O..'" |\r\n'
		s=s..'|- style="height:'..Y..'line-height:1;"\r\n'
		s=s..'| style="width:'..X..P..'" |\r\n'
		if white2 and last then
			s=s..'| style="width:'..a..'px;'..P..';" |\r\n'
		else
			s=s..'| style="width:'..a..'px;'..P..'background-color:'..color..';" |'..wh(Y)..'\r\n'
		end
		s=s..'| style="width:'..X..P..'" |\r\n'
	end
	s=s..'|}\r\n'
	return s
end
function bullet(sys)
	return '[[File:'..systems[sys].logo..'|'..(q/10)..'px|link=]]'
end
function lineformatted(obj)
	local s=' '
	if obj.color then
		s='<span style="color:'..obj.color..'">►</span>'
	end
	return '<span class="nowrap">'..bullet(obj.sys)..s..obj.name..'</span>'
end
function addone(x)
	local first=prevx==nil or x.en~=prevx.en
	local obj=lines[x.lin]
	local linetext=lineformatted(obj)
	local belongs=false
	if first and prevx then
		s=s..onerow(prevx)
	end
	if filtertype=='none' then
		linetext=linetext..' ('..x.year..')'
	else
		if first then
			name=''
			transfers=''
			transfers_coll={}
			if filtertype=='line' then
				transfers_coll[filter]=true
			end
			zone=''
			subdiv=''
			comment=''
			year=''
			subdivlabel_local=''
		end
		if filtertype=='line' then
			belongs=filter==x.lin
		elseif filtertype=='system' then
			belongs=filter==obj.sys
		end
		if sorted and belongs and not dbg then
			if from<to then
				belongs=from<=x.num and x.num<=to
			else
				belongs=to<=x.num and x.num<=from
			end
		end
		if belongs then
			if name=='' then
				name=staformatted(x)
			end
			zone=x.zone
			subdiv=x.subdiv
			comment=x.comment
			lastnum=x.num
			year=x.year
			subdivlabel_local=x.subdivlabel
		else
			if transfers~='' then
				transfers=transfers..'<br>'
			end
			transfers=transfers..linetext
			transfers_coll[x.lin]=true
		end
	end
	if first then
		gallery={}
	end
	if filtertype=='none' or belongs then
		local file=x.file
		if file==nil then
			file=systems[obj.sys].placeholder
		end
		if file==nil then
			file=cities[city].placeholder
		end
		if filtertype=='line' then
			linetext=''
		end
		if gallery[file] then
			gallery[file]={file=file,text=gallery[file].text..'<br>'..linetext}
		else
			gallery[file]={file=file,text=linetext}
		end
	end
end
function staformatted(x)
	local art=x.ru
	if art=='' then
		return ''
	end
	if (filtertype=='system' or filtertype=='none') and x.complex~=nil then
		art=x.complex
	end
	local sta=art
	if not dbg then
		local pos=string.find(sta,') (',1,true)
		if pos then
			sta=string.sub(sta,1,pos)
		else
			pos=string.find(sta,' (',1,true)
			if pos then
				sta=string.sub(sta,1,pos-1)
			end
		end
	end
	local ex=mw.title.new(art).exists
	if not ex then
		local art1=ru_iw(x.en)
		if art1 then
			art=art1
			ex=true
		end
	end
	local link='<b>[['..art..'|'..sta..']]</b>'
	if not (dbg or ex) then
		link=link..'<sup>[[:en:'..x.en..'|[англ.]]]</sup>'
	end
	return link
end
function onerow(x)
	local s0=''
	if filtertype=='line' then
		for nn,xx in pairs(gallery) do
			s0=s0..'[[File:'..xx.file..'|'..q..'px]]\r\n'
		end
	else
		for nn,xx in pairs(gallery) do
			s0=s0..xx.file..'|'..xx.text..'\r\n'
		end
		s0=fra:preprocess('<gallery mode=nolines>\r\n'..s0..'\r\n</gallery>\r\n')
	end
	if filtertype=='none' then
		return "|-\r\n"..WW(0)..'\r\n* [[:en:'..x.en..']]\r\n* [['..x.ru..']]\r\n'..iw(x.en)..'| nowrap |'..s0
	end
	if name=='' then
		return ''
	end
	if x.other then
		local s=''
		for nn,xx in pairs(stations) do
			if xx.en==x.other then
				local obj=lines[xx.lin]
				local linetext=lineformatted(obj)
				if transfers_coll[xx.lin]==nil then
					--if s=='' then
					--	s=s..'<i>на станции '..staformatted(xx)..':</i><br>'
					--end
					s=s..linetext..'<br>'
					transfers_coll[xx.lin]=true
				end
			end
		end
		if transfers~='' then
			transfers=transfers..'<br>'
		end
		transfers=transfers..s
	end
	if sorted then
		table.insert(list0, {num=lastnum,en=x.en,photo=s0,name=name,transfers=transfers,zone=zone,subdiv=subdiv,comment=comment,year=year,subdivlabel=subdivlabel_local})
	else
		table.insert(list0, {num=x.ru,photo=s0,name=name,transfers=transfers,zone=zone,subdiv=subdiv,comment=comment,year=year,subdivlabel=subdivlabel_local})
	end
	return ''
end
function commons(param)
	local qid = mw.wikibase.getEntityIdForTitle(param, "enwiki")
	if qid==nil then
		return ''
	end
	local c=mw.wikibase.getSitelink(qid, 'commonswiki')
	if c then
		return c
	end
	local statements = mw.wikibase.getBestStatements(qid, 'P373') 
	return 'Category:'..statements[1].mainsnak.datavalue.value
end
function iw(param)
	local qid = mw.wikibase.getEntityIdForTitle(param, "enwiki")
	if qid==nil then
		return ''
	end
	local rtn=''
	for nn,xx in pairs({'uk','yi'}) do
		local s=mw.wikibase.getSitelink(qid, xx..'wiki')
		if s then
			rtn=rtn..'* [[:'..xx..':'..s..']]\r\n'
		else
			rtn=rtn..'* '..xx..':\r\n'
		end
	end
	return rtn
end
function ru_iw(en,ru)
	local qid = mw.wikibase.getEntityIdForTitle(en, "enwiki")
	if qid then
		return mw.wikibase.getSitelink(qid, 'ruwiki')
	else
		return nil
	end
end
function p.draw(frame)
	init(frame)
	local s=''
	local c
	local k=0
	local count=0
	for n,x in pairs(frame.args) do
		if tonumber(n) then
			count=count+1
		end
	end
	for n,x in pairs(frame.args) do
		if tonumber(n) then
			if n==1 then
				local obj=lines[x]
				if obj then
					color=obj.color
					color0=obj.color0
				else
					color=''
					color0=nil
				end
			else
				hor=(n-1)*2~=count or x=='h'
				if x=='w' or x=='v' then
					c='background-color:transparent;'
				elseif x=='b' or x=='h' then
					c='background-color:'..color..';'
				elseif x=='1' then
					c='background:'
					if color0 then
						c=c..'radial-gradient(3px 3px at '..a..'px '..a..'px,'..color..' 100%,transparent 100%),'
						c=c..'radial-gradient(7px 7px at '..a..'px '..a..'px,'..color0..' 100%,transparent 100%),'
					end
					c=c..'radial-gradient('..a..'px '..a..'px at '..a..'px '..a..'px,'..color..' 100%,transparent 100%);'
				elseif x=='2' then
					c='background:'
					if color0 then
						c=c..'radial-gradient(3px 3px at 0px '..a..'px,'..color..' 100%,transparent 100%),'
						c=c..'radial-gradient(7px 7px at 0px '..a..'px,'..color0..' 100%,transparent 100%),'
					end
					c=c..'radial-gradient('..a..'px '..a..'px at 0px '..a..'px,'..color..' 100%,transparent 100%);'
				elseif x=='3' then
					c='background:'
					if color0 then
						c=c..'radial-gradient(3px 3px at '..a..'px 0px,'..color..' 100%,transparent 100%),'
						c=c..'radial-gradient(7px 7px at '..a..'px 0px,'..color0..' 100%,transparent 100%),'
					end
					c=c..'radial-gradient('..a..'px '..a..'px at '..a..'px 0px,'..color..' 100%,transparent 100%);'
				elseif x=='4' then
					c='background:'
					if color0 then
						c=c..'radial-gradient(3px 3px at 0px 0px,'..color..' 100%,transparent 100%),'
						c=c..'radial-gradient(7px 7px at 0px 0px,'..color0..' 100%,transparent 100%),'
					end
					c=c..'radial-gradient('..a..'px '..a..'px at 0px 0px,'..color..' 100%,transparent 100%);'
				elseif x=='5' then
					c='background:radial-gradient('..a..'px '..a..'px at '..a..'px '..a..'px,transparent 100%,'..color..' 100%);'
				elseif x=='6' then
					c='background:radial-gradient('..a..'px '..a..'px at 0px '..a..'px,transparent 100%,'..color..' 100%);'
				elseif x=='7' then
					c='background:radial-gradient('..a..'px '..a..'px at '..a..'px 0px,transparent 100%,'..color..' 100%);'
				elseif x=='8' then
					c='background:radial-gradient('..a..'px '..a..'px at 0px 0px,transparent 100%,'..color..' 100%);'
				else
					c=''
				end
				if x=='v' then
					s=s..'| style="width:'..(r-a/2)..'px;'..P..c..'" |\r\n'
				elseif x=='b' or x=='h' then
					s=s..'| style="width:'..a..'px;'..P..c..'" |'..wh(a..'px')..'\r\n'
				else
					s=s..'| style="width:'..a..'px;'..P..c..'" |\r\n'
				end
				k=k+a
			end
		end
	end
	local arm1=2*qq(0,not hor)+r-k/2
	local arm2=2*qq(0,not hor)+r-k/2
	if frame.args['arm1'] then
		arm1=frame.args['arm1']+r-k/2
	end
	if frame.args['arm2'] then
		arm2=frame.args['arm2']+r-k/2
	end
	hor=true
	if arm1>0 then
		s='\r\n'..s
		if frame.args['text1'] then
			s=frame.args['text1']..s
		end
		if frame.args['color1'] then
			s='! style="color:white;background-color:'..color..';width:'..arm1..'px;'..P..'" |'..wh('100%')..s
		else
			s='! style="color:'..color..';background-color:transparent;width:'..arm1..'px;'..P..'" |'..s
		end
	end
	if arm2>0 then
		if frame.args['color2'] then
			s=s..'! style="color:white;background-color:'..color..';width:'..arm2..'px;'..P..'" |'..wh('100%')
		else
			s=s..'! style="color:'..color..';background-color:transparent;width:'..arm2..'px;'..P..'" |'
		end
		if frame.args['text2'] then
			s=s..frame.args['text2']
		end
		s=s..'\r\n'
	end
	return T..'|- style="height:'..a..'px;line-height:0;"\r\n'..s..'|}\r\n'
end
function init(frame)
	if frame.args['compact'] then
		q=100
	end
end
function H(num)
	return '|- style="height:'..qq(num,hor)..'px;line-height:1;"\r\n'
end
function WW(num)
	if filtertype=='system' or filtertype=='none' then
		if num==2 then
			return '| nowrap |'
		else
			return '|'
		end
	else
		return '|style="max-width:'..qq(num,not hor)..'px;min-width:'..qq(num,not hor)..'px;'..P..'"|'
	end
end
return p
-- =p.list{args={'DLR','1','2'}}