Module:Road data
Appearance
![]() | This module is rated as ready for general use. It has reached a mature form and is thought to be relatively bug-free and ready for use wherever appropriate. It is ready to mention on help pages and other Wikipedia resources as an option for new users to learn. To reduce server load and bad output, it should be improved by sandbox testing rather than repeated trial-and-error editing. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | This Lua module is used on approximately 18,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
Usage
{{#invoke:Road data|function_name}}
Submodules
Submodules of Module:Road data are used to process and display the road data stored in the modules listed at #Data modules. There are also a few that contain additional data.
Data modules
String modules
String modules are used to store type data for use with several road templates, including {{jct}} and {{routelist row}}.
Banner modules
Banner modules store data about banners that should be shown. For example, Module:Road data/banners/USA defines the "TO" banner shown when {{jct}} is used with a |to=
parameter in the United States i.e. To I-82.
Mask modules
Mask modules store masks that can be hooked into by #String modules. See Module:Road data/strings#Hooks and Module:Road data/parser/hooks#mask.
local p = {}
-- Change to main module upon deployment
local parserModuleName = "Module:Road data/parser/sandbox"
local concat = table.concat
local insert = table.insert
local format = mw.ustring.format
local parserModule = require(parserModuleName)
local parser = parserModule.parser
-- Shields
local defaultShieldSize = 20
local function addContextBanner(route, name, suffix, bannerSpec)
local bannerModule = 'Module:Road data/banners/' .. string.upper(route.country)
local shieldfield = name .. 'shield'
local shield = parser(route, shieldfield)
if shield == nil then
-- This route type does not define shield.
-- Find shield in the default banner table.
shield = parser(route, 'shield', name, bannerModule)
if shield and shield ~= '' then
if suffix == nil then
suffix = parser(route, 'shield', 'suffix', bannerModule)
end
if suffix and suffix ~= '' then
shield = shield .. " " .. suffix
end
shield = shield .. ".svg"
end
end
if shield and shield ~= '' then
local shieldsizefield = name .. 'shieldsize'
local shieldSize = parser(route, shieldsizefield)
if shieldSize == nil then
-- This route type does not define shield size.
-- Find shield size in the default banner table.
shieldSize = parser(route, 'shieldsize', name, bannerModule)
end
if shieldSize == nil then shieldSize = "20" end
-- Add banner plate.
insert(bannerSpec, {shield, shieldSize})
end
end
local function bannerSpec(banner, bannerSize, bannerSuffix, route)
local banners = {}
if type(banner) == "table" then
local bannerSizeIsNotTable = type(bannerSize) ~= "table"
for i,filename in ipairs(banner) do
local bannersize = bannerSizeIsNotTable and bannerSize or bannerSize[i] or defaultShieldSize
insert(banners, {filename, bannersize})
end
elseif banner ~= '' then
insert(banners, {banner, bannerSize})
end
if route.dir then
addContextBanner(route, 'dir', bannerSuffix, banners)
end
if route.to then
addContextBanner(route, 'to', bannerSuffix, banners)
end
return banners
end
local function shieldSpec(route, mainShield)
local shieldSpec = {}
local shield
if mainShield then shield = parser(route, "shieldmain") end
if not shield then shield = parser(route, 'shield') or '' end
if shield == '' then return shieldSpec end
local shieldsize
if mainShield then shieldsize = parser(route, "shieldmainsize") end
if not shieldsize then shieldsize = parser(route, "shieldsize") or "" end
local shieldsizeIsNotTable = type(shieldsize) ~= "table"
local banner = parser(route, 'banner') or {}
local bannersize = parser(route, 'bannersize') or defaultShieldSize
local bannersuffix = parser(route, 'bannersuffix')
local bannerIsNotTable = type(banner) ~= "table"
local bannersizeIsNotTable = type(bannersize) ~= "table"
local bannersuffixIsNotTable = type(bannersuffix) ~= "table"
if type(shield) == "table" then
for i,filename in ipairs(shield) do
local size = shieldsizeIsNotTable and shieldsize or shieldsize[i]
if size == "" then size = nil end
-- banner.all describes banners that apply to all multiple shields.
local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or {})
-- Banner size is default if the corresponding entry
-- in bannerSize table is not set.
local shieldBannerSize =
bannersizeIsNotTable and bannersize
or (bannersize[i] or bannersize.all or defaultShieldSize)
local shieldBannerSuffix = bannersuffix and (bannersuffixIsNotTable and bannersuffix or bannersuffix[i])
insert(shieldSpec, {
shield = {filename, size},
banners = bannerSpec(shieldBanner, shieldBannerSize, shieldBannerSuffix, route)
})
end
elseif shield ~= '' then
if shieldsize == "" then shieldsize = nil end
insert(shieldSpec, {
shield = {shield, shieldsize},
banners = bannerSpec(banner, bannersize, bannersuffix, route)
})
end
return shieldSpec
end
local missingShields
local shieldExistsCache = {}
-- Return up to two booleans.
-- The first boolean is false if `shield` does not exist, and true otherwise.
-- If the first boolean is true, the second boolean is true if the shield is
-- landscape (width >= height), and false otherwise.
local function shieldExists(shield)
local result = shieldExistsCache[shield]
if result == nil then
local file = mw.title.new(shield, 'Media').file
-- Cache result.
local exists = file.exists
result = {exists}
if exists then result[2] = file.width >= file.height end
shieldExistsCache[shield] = result
end
if result[1] then return true, result[2] end
insert(missingShields, shield)
return false
end
local function render(shieldEntry, scale, showLink)
local shield = shieldEntry.shield
local banners = shieldEntry.banners
local exists, landscape = shieldExists(shield[1])
if not exists then return '' end
local size
if shield[2] then
local width, height = mw.ustring.match(shield[2], "(%d*)x?(%d*)")
width = tonumber(width)
height = tonumber(height)
local sizeparts = {}
if width then
insert(sizeparts, format("%d", width * scale))
end
if height then
insert(sizeparts, format("x%d", height * scale))
end
size = concat(sizeparts)
else
size = format("%s%d", landscape and "x" or "", defaultShieldSize * scale)
end
local link = showLink and "" or "|link="
local shieldCode = format("[[File:%s|%spx%s|%s]]", shield[1], size, link, shield[1])
if not banners[1] then return shieldCode end
for _,banner in ipairs(banners) do
if shieldExists(banner[1]) then
shieldCode = format("[[File:%s|%dpx%s|%s]]<br>%s",
banner[1],
banner[2] * scale,
link,
banner[1],
shieldCode)
end
end
return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">' .. shieldCode .. '</span>'
end
function p.shield(route, scale, showLink, mainShield)
missingShields = {}
if route.rdt then
local shieldSize = mw.ustring.match(route.rdt, '^(%d+)$') or 17
scale = shieldSize/defaultShieldSize
end
scale = scale or 1
local rendered = {}
for _,entry in ipairs(shieldSpec(route, mainShield)) do
insert(rendered, render(entry, scale, showLink))
end
return concat(rendered), missingShields
end
function p.link(route)
if route.type == '' then return route.route end
local abbr = parser(route, 'abbr')
if not abbr then
route.typeerror = true
return format("<span class=\"error\">Invalid type: %s</span>", route.type)
end
if route.nolink then return abbr, abbr end
local link = parser(route, 'link') or ''
if link == '' then return abbr, abbr end
return format("[[%s|%s]]", link, abbr), abbr
end
function p.locations(args, prependArticle)
-- Region, for disambiguation
local region = args.region
-- Independent city
local indepCityText
if args.indep_city_special then
indepCityText = args.indep_city_special -- Overrides `indep_city` argument.
elseif args.indep_city then
local indepCity = args.indep_city
local cityLink -- Wikilink for independent city
if args.indep_city_nodab or not region then
cityLink = format('[[%s]]', indepCity)
else
-- Specialize independent city to the region.
cityLink = format('[[%s, %s|%s]]', indepCity, region, indepCity)
end
indepCityText = "the [[Independent city|City]] of " .. cityLink
end
if indepCityText then
return {indep_city = indepCityText}
end
-- First-level subdivision, e.g., county
-- Name of the type of subdivision, e.g., "County" and "Parish"
local sub1name = args.sub1name
local sub1Text
if args.sub1_special then
sub1Text = args.sub1_special -- Overrides `sub1` argument.
elseif args.sub1 then
local sub1 = args.sub1
-- Add type (if specified) to wikilink for first-level subdivision.
sub1 = sub1name and format("%s %s", sub1, sub1name) or sub1
if args.sub1_nodab or not region then
sub1Text = format('[[%s]]', sub1)
else
-- Specialize first-level subdivision, with type added, to the region.
sub1Text = format('[[%s, %s|%s]]', sub1, region, sub1)
end
end
-- Second-level subdivision, e.g., city and town
local sub2Text
if args.sub2_special then
sub2Text = args.sub2_special -- Overrides `sub2` argument.
elseif args.sub2 then
local sub2 = args.sub2
if args.sub2_nodab then
sub2Text = format("[[%s]]", sub2)
else
local article
local sub2Link = {sub2}
local sub2Name = sub2
-- Type of area, e.g., city and village, as a form of disambiguation
local area = args.area
if area then
insert(sub2Link, format(' (%s)', area)) -- Add area to wikilink.
local areas = { -- table of different area types
village = "Village",
city = "City",
town = "Town",
community = "Community",
CDP = "Community",
hamlet = "Hamlet",
["unorganized territory"] = "Unorganized Territory"
}
-- Add area name to displayed wikitext, if defined.
local areaName = areas[area]
if areaName then
article = prependArticle and "the " or ""
sub2Name = format("%s of %s", areaName, sub2Name)
end
end
-- Some second-level subdivisions are not unique in a given region.
-- `sub1dab` is the first-level subdivision to be used for disambiguation.
local sub1dab = args.sub1dab
if sub1dab then
sub1dab = sub1name and format("%s %s", sub1dab, sub1name) or sub1dab
insert(sub2Link, format(", %s", sub1dab))
end
if region then
insert(sub2Link, format(", %s", region)) -- Add region to wikilink
end
sub2Text = format("%s[[%s|%s]]", article or "", concat(sub2Link), sub2Name)
end
end
return {sub1 = sub1Text, sub2 = sub2Text}
end
return p