Jump to content

Module:Road data and Module:Road data/sandbox: Difference between pages

(Difference between pages)
Page 1
Page 2
Content deleted Content added
m Changed protection settings for "Module:Road data": High-risk template or module ([Edit=Require template editor access] (indefinite))
 
Fix ignoreUpright
 
Line 1: Line 1:
local p = {}
local p = {}

-- Change to "" upon deployment.
local moduleSuffix = ""

local parserModuleName = "Module:Road data/parser" .. moduleSuffix
local statenameModuleName = "Module:Jct/statename" .. moduleSuffix -- TODO transition


local concat = table.concat
local concat = table.concat
Line 12: Line 6:
local trim = mw.text.trim
local trim = mw.text.trim


local parserModule = require(parserModuleName)
local parserModule = require("Module:Road data/parser")
local parser = parserModule.parser
local parser = parserModule.parser
local util = require("Module:Road data/util")
local util = require("Module:Road data/util")
local sizeModule = require("Module:Road data/size").size({style = "jct"})
local sizeModuleName = 'Module:Road data/size/sandbox' -- REMOVE SANDBOX


-- Shields
-- Shields
local function addContextBanner(route, name, suffix, bannerSpec, size)
local defaultShieldSize = 24
local bannerModule = 'Module:Road data/banners/' .. string.upper(route.country)
local shieldField = name .. 'shield'
local shield = parser(route, shieldField)


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
if shield == nil then
-- This route type does not define shield.
-- This route type does not define shield.
Line 32: Line 25:
suffix = parser(route, 'shield', 'suffix', bannerModule)
suffix = parser(route, 'shield', 'suffix', bannerModule)
end
end

if suffix and suffix ~= '' then
if suffix and suffix ~= '' then
shield = shield .. " " .. suffix
shield = shield .. " " .. suffix
end
end

shield = shield .. ".svg"
shield = shield .. ".svg"
end
end
end
end

if shield and shield ~= '' then
if shield and shield ~= '' then
-- Add banner plate
local shieldSize = sizeModule
insert(bannerSpec, {shield, size})
-- Add banner plate.
insert(bannerSpec, {shield, shieldSize})
end
end
end
end


local function bannerSpec(banner, bannerSize, bannerSuffix, route)
local function bannerSpec(banner, bannerSize, bannerSuffix, route, size)
local banners = {}
local banners = {}

if type(banner) == "table" then
if type(banner) == "table" then
local bannerSizeIsNotTable = type(bannerSize) ~= "table"
local bannerSizeIsNotTable = type(bannerSize) ~= "table"

for i,filename in ipairs(banner) do
for i, filename in ipairs(banner) do
local bannersize = bannerSizeIsNotTable and bannerSize or bannerSize[i] or defaultShieldSize
local singleBannerSize = bannerSizeIsNotTable and bannerSize or bannerSize[i]
insert(banners, {filename, bannersize})
insert(banners, {filename, singleBannerSize})
end
end
elseif banner ~= '' then
elseif banner ~= '' then
Line 58: Line 55:


if route.dir then
if route.dir then
addContextBanner(route, 'dir', bannerSuffix, banners)
addContextBanner(route, 'dir', bannerSuffix, banners, size)
end
end

if route.to then
if route.to then
addContextBanner(route, 'to', bannerSuffix, banners)
addContextBanner(route, 'to', bannerSuffix, banners, size)
end
end


Line 67: Line 65:
end
end


local function shieldSpec(route, mainShield, shieldList)
local function shieldSpec(route, shieldType, size, ignoreUpright)
local shieldSpec = {}
local shieldSpec = {}


local shield
local shield
if mainShield then shield = parser(route, "shieldmain") end
if route.to and shieldType == 'main' then shield = parser(route, 'shieldtomain') end
if shieldList then shield = parser(route, "shieldlist") end
if not shield and route.to then shield = parser(route, 'shieldto') end
if not shield and shieldType == 'main' then shield = parser(route, 'shieldmain') end
if not shield and shieldType == 'list' then shield = parser(route, 'shieldlist') end
if not shield then shield = parser(route, 'shield') or '' end
if not shield then shield = parser(route, 'shield') or '' end
if shield == '' then return shieldSpec end
if shield == '' then return shieldSpec end

local orientation = parser(route, 'orientation')
local orientation = parser(route, 'orientation')
local shieldSize
local shieldSizeIsNotTable


if type(orientation) == "table" then
local function size(route)
shieldSize = {}
if orientation == "upright" then
shieldSizeIsNotTable = false
return sizeModule

else return "x" .. sizeModule
for i, shieldOrientation in ipairs(orientation) do
insert(shieldSize, (shieldOrientation ~= 'upright' or ignoreUpright) and 'x' .. size or size)
end
end
else
shieldSize = (orientation ~= 'upright' or ignoreUpright) and 'x' .. size or size
shieldSizeIsNotTable = true
end
end
local shieldsize = sizeModule
local banner = parser(route, 'banner') or {}
local bannerSize = size
local bannerSuffix = parser(route, 'bannersuffix')
local banner = parser(route, 'banner') or {}
local bannersize = sizeModule
local bannersuffix = parser(route, 'bannersuffix')

local bannerIsNotTable = type(banner) ~= "table"
local bannerIsNotTable = type(banner) ~= "table"
local bannersizeIsNotTable = type(bannersize) ~= "table"
local bannerSuffixIsNotTable = type(bannerSuffix) ~= "table"
local bannersuffixIsNotTable = type(bannersuffix) ~= "table"


if type(shield) == "table" then
if type(shield) == "table" then
for i,filename in ipairs(shield) do
for i, filename in ipairs(shield) do
-- Fallback to default size for the size style
local size = shieldsize or shieldsize[i]
local singleShieldSize = shieldSizeIsNotTable and shieldSize or shieldSize[i] or size
if size == "" then size = nil end

-- banner.all describes banners that apply to all multiple shields.
-- banner.all describes banners that apply to all multiple shields
local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or {})
local shieldBanner = bannerIsNotTable and banner or (banner[i] or banner.all or {})
local shieldBannerSuffix = bannerSuffix and (bannerSuffixIsNotTable and bannerSuffix or bannerSuffix[i])
-- 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, {
insert(shieldSpec, {
shield = {filename, size},
shield = {filename, singleShieldSize},
banners = bannerSpec(shieldBanner, shieldBannerSize, shieldBannerSuffix, route)
banners = bannerSpec(shieldBanner, bannerSize, shieldBannerSuffix, route, size),
route = route
})
})
end
end
elseif shield ~= '' then
elseif shield ~= '' then
if shieldsize == "" then shieldsize = nil end
insert(shieldSpec, {
insert(shieldSpec, {
shield = {shield, shieldsize},
shield = {shield, shieldSize},
banners = bannerSpec(banner, bannersize, bannersuffix, route)
banners = bannerSpec(banner, bannerSize, bannerSuffix, route, size),
route = route
})
})
end
end
Line 123: Line 126:


local missingShields
local missingShields

local shieldExistsCache = {}
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 function shieldExists(shield)
local result = shieldExistsCache[shield]
local exists = shieldExistsCache[shield]

if result == nil then
if exists == nil then
local file = mw.title.new(shield, 'Media').file
local file = mw.title.new(shield, 'Media').file
exists = file.exists
-- Cache result.
-- Cache result
local exists = file.exists
result = {exists}
shieldExistsCache[shield] = exists
if exists then result[2] = file.width >= file.height end
shieldExistsCache[shield] = result
end
end

if result[1] then return true, result[2] end
if exists then return true end

insert(missingShields, shield)
insert(missingShields, shield)
return false
return false
end
end


local function render(shieldEntry, scale, showLink)
local function render(shieldEntry, nonDecorative)
local shield = shieldEntry.shield
local shield = shieldEntry.shield
local banners = shieldEntry.banners
local banners = shieldEntry.banners
local exists, landscape = shieldExists(shield[1])
local exists = shieldExists(shield[1])
if not exists then return '' end
if not exists then return '' end

local size
local alt = ''
if shield[2] then
if nonDecorative then
alt = (parser(shieldEntry.route, 'abbr') or '') .. ' marker'
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 "", sizeModule)
end
end
local shieldCode = format("[[File:%s|%spx|link=|alt=]]", shield[1], size)
local shieldCode = format("[[File:%s|%s|link=|alt=%s]]", shield[1], shield[2], alt)
if not banners[1] then return shieldCode end
if not banners[1] then return shieldCode end


for _,banner in ipairs(banners) do
for _, banner in ipairs(banners) do
shieldCode = format(
if shieldExists(banner[1]) then
shieldCode = format("[[File:%s|%s|link=|alt=]]<br>%s",
"[[File:%s|%s|link=|alt=%s]]<br>%s",
banner[1],
banner[1],
banner[2],
banner[2],
shieldCode)
alt,
shieldCode
end
)
end
end

return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">' .. shieldCode .. '</span>'
return '<span style="display: inline-block; vertical-align: baseline; line-height: 0; text-align: center;">'
.. shieldCode
.. '</span>'
end
end


function p.shield(route, scale, showLink, mainShield, shieldList)
function p.shield(route, shieldType, sizeOrStyle, nonDecorative)
missingShields = {}
missingShields = {}
if route.rdt then
local size
local shieldSize = mw.ustring.match(route.rdt, '^(%d+)$') or 17
local ignoreUpright
scale = shieldSize/defaultShieldSize
if sizeOrStyle and sizeOrStyle:match('^%d+px$') then
size = sizeOrStyle
ignoreUpright = false
else
local sizeModule = require(sizeModuleName) -- REMOVE SANDBOX
size = sizeModule._size({ style = sizeOrStyle })
ignoreUpright = sizeModule._ignoreUpright(sizeOrStyle)
end
end
scale = scale or 1

local rendered = {}
local rendered = {}
for _,entry in ipairs(shieldSpec(route, mainShield, shieldList)) do
for _, entry in ipairs(shieldSpec(route, shieldType, size, ignoreUpright)) do
insert(rendered, render(entry, scale, showLink))
insert(rendered, render(entry, nonDecorative))
end
end
return concat(rendered), missingShields
return concat(rendered, '&thinsp;'), missingShields
end
end


-- Links
function p.link(route)
local abbr, errMsg = parser(route, 'abbr')
function p.link(route, useName)
local abbr, errMsg = parser(route, useName and 'name' or 'abbr')

if not abbr then
if not abbr then
route.typeerror = true
route.typeerror = true
return util.err(errMsg or format("Invalid type: %s", route.type or "(nil)"))
return util.err(errMsg or format("Invalid type: %s", route.type or "(nil)"))
end
end

if route.nolink then return abbr, abbr end
if route.nolink then return abbr, abbr end


Line 208: Line 211:


return format("[[%s|%s]]", link, abbr), abbr
return format("[[%s|%s]]", link, abbr), abbr
end

local function stateName(args)
-- TODO transition
local data = mw.loadData(statenameModuleName)
local abbr = args.state or args.province
local countryData = data[args.country]
return countryData and countryData[abbr]
end

function p.locations(args, module, group)
module = module or ""
local modulearticle = module .. "article"
local moduleprefix = module .. "prefix"
local modulenameprefix = module .. "nameprefix"
local modulenamesuffix = module .. "namesuffix"

local warnings = {}

-- Region, for disambiguation
local region = parserModule.parser(args, "region", " common ")
if not region then
-- TODO transition
if args.region then
warnings.region = "region parameter is deprecated"
region = args.region
elseif args.country and (args.state or args.province) then
warnings.region = "Inferring region from country and state/province"
region = stateName(args)
end
end
local regionName
local regionText
if type(region) == "table" then
regionName = region.name
regionText = format("[[%s|%s]]", region.link, regionName)
elseif region then
regionName = region
regionText = format("[[%s]]", regionName)
end
args.region = regionName

local locations = parserModule.parser(args, "locations", " common ") or {}

-- Primary topic requires no specialization to supplied locations.
local primaryTopic = not locations and module == "jctint" and args.primary_topic ~= 'no'
if args.primary_topic then
-- TODO transition
warnings.primary_topic = "primary_topic parameter is deprecated"
end

-- 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 spec = locations.indep_city
if spec then
local link = format("%s%s%s",
spec.linkprefix or "", indepCity, spec.linksuffix or "")
local name = format("%s%s%s",
spec[modulenameprefix] or spec.nameprefix or "",
indepCity,
spec[modulenamesuffix] or spec.namesuffix or "")
indepCityText = format("%s%s[[%s|%s]]",
spec[modulearticle] or spec.article or "",
spec[moduleprefix] or spec.prefix or "",
link, name)
else
-- TODO transition
warnings.indep_city = "Spec for indep_city parameter undefined in road data module"
local cityLink -- Wikilink for independent city
if primaryTopic then
cityLink = format('[[%s]]', indepCity)
else
-- Specialize independent city to the region.
cityLink = format('[[%s, %s|%s]]', indepCity, region, indepCity)
end
indepCityText = "[[Independent city|City]] of " .. cityLink
end
end
if indepCityText then
return {region = regionText, indep_city = indepCityText, warnings = warnings}
end

-- First-level subdivision, e.g., county
-- Name of the type of subdivision, e.g., "County" and "Parish"
local sub1name = args.sub1name -- TODO transition
local sub1Text
if args.sub1_special then
sub1Text = args.sub1_special -- Overrides `sub1` argument.
elseif args.sub1 then
local sub1 = args.sub1
local article
local link = sub1
local name = sub1
-- Type of first-level subdivision area, as a form of disambiguation
local sub1area = args.sub1area
if sub1area then
local sub1areaSpec = locations.sub1areas and locations.sub1areas[sub1area]
if sub1areaSpec then
article = sub1areaSpec[modulearticle] or sub1areaSpec.article or ""
link = format("%s%s%s",
sub1areaSpec.linkprefix or "", link, sub1areaSpec.linksuffix or "")
name = format("%s%s%s",
group and "" or sub1areaSpec[modulenameprefix] or sub1areaSpec.nameprefix or "",
name,
group and "" or sub1areaSpec[modulenamesuffix] or sub1areaSpec.namesuffix or "")
else
-- TODO report error
local errMsg = util.err(format("Undefined sub1area: %s", sub1area))
name = format("%s%s", name, errMsg)
end
end
if locations.sub1 then
local spec = locations.sub1
-- Prepend and append text from spec.
link = format("%s%s%s",
spec.linkprefix or "", link, spec.linksuffix or "")
name = format("%s%s%s",
spec[modulenameprefix] or spec.nameprefix or "",
name,
spec[modulenamesuffix] or spec.namesuffix or "")
sub1Text = format("%s[[%s|%s]]", article or "", link, name)
else
-- TODO transition
warnings.sub1 = "Spec for sub1 parameter undefined in road data module"
-- Add type (if specified) to wikilink for first-level subdivision.
local sub1Link = sub1name and trim(format("%s %s", sub1, sub1name)) or sub1
local sub1Name = module == "jcttop" and sub1Link or sub1
if primaryTopic then
sub1Text = format('[[%s|%s]]', sub1Link, sub1Name)
else
-- Specialize first-level subdivision, with type added, to the region.
sub1Text = format('[[%s, %s|%s]]', sub1Link, region, sub1Name)
end
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 sub2 == "none" then
sub2Text = "&#8203;" -- Zero-width space
elseif sub2 == "&nbsp;" then
-- TODO transition
warnings.sub2 = "&nbsp; argument for sub2 parameter is deprecated"
sub2Text = "&#8203;" -- Zero-width space
elseif primaryTopic then
-- TODO transition
sub2Text = format("[[%s]]", sub2)
else
local article
local link = sub2
local name = sub2
-- Type of area, e.g., city and village, as a form of disambiguation
local sub2area = args.sub2area --[[TODO transition]] or args.area
if sub2area then
local sub2areaSpec = locations.sub2areas and locations.sub2areas[sub2area]
if not sub2areaSpec then
-- TODO transition
warnings.sub2 =
format("Spec for area parameter '%s' undefined in road data module", sub2area)
local sub2areas = { -- table of different area types
city = {
linksuffix = " (city)",
jcttoparticle = "the ",
nameprefix = "City of "
},
town = {
linksuffix = " (town)",
jcttoparticle = "the ",
nameprefix = "Town of "
},
village = {
linksuffix = " (village)",
jcttoparticle = "the ",
nameprefix = "Village of "
},
community = {
linksuffix = " (community)",
jcttoparticle = "the ",
nameprefix = "Community of "
},
CDP = {
linksuffix = " (CDP)",
jcttoparticle = "the ",
nameprefix = "Community of "
},
hamlet = {
linksuffix = " (hamlet)",
jcttoparticle = "the ",
nameprefix = "Hamlet of "
},
["unorganized territory"] = {
linksuffix = " (unorganized territory)",
jcttoparticle = "the ",
nameprefix = "Unorganized Territory of "
},
township = {
linksuffix = " Township",
namesuffix = " Township",
}
}
sub2areaSpec = sub2areas[sub2area]
end
if sub2areaSpec then
article = sub2areaSpec[modulearticle] or sub2areaSpec.article or ""
link = format("%s%s%s",
sub2areaSpec.linkprefix or "", link, sub2areaSpec.linksuffix or "")
name = format("%s%s%s",
group and "" or sub2areaSpec[modulenameprefix] or sub2areaSpec.nameprefix or "",
name,
group and "" or sub2areaSpec[modulenamesuffix] or sub2areaSpec.namesuffix or "")
else
-- TODO report error
local errMsg = util.err(format("Undefined sub2area: %s", sub2area))
name = format("%s%s", name, errMsg)
end
end
if locations.sub2 then
local spec = locations.sub2
-- Prepend and append text from spec.
link = format("%s%s%s",
spec.linkprefix or "", link, spec.linksuffix or "")
name = format("%s%s%s",
spec[modulenameprefix] or spec.nameprefix or "",
name,
spec[modulenamesuffix] or spec.namesuffix or "")
else
-- TODO transition
warnings.sub2 = "Spec for sub2 parameter undefined in road data module"
-- 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 trim(format("%s %s", sub1dab, sub1name)) or sub1dab
link = format("%s, %s", link, sub1dab)
end
link = format("%s, %s", link, region) -- Add region to wikilink
end
sub2Text = format("%s[[%s|%s]]", article or "", link, name)
end
end
return {region = regionText, sub1 = sub1Text, sub2 = sub2Text, warnings = warnings}
end
end