Module:Road data and Module:Road data/sandbox: Difference between pages
Appearance
(Difference between pages)
Content deleted Content added
m Changed protection settings for "Module:Road data": High-risk template or module ([Edit=Require template editor access] (indefinite)) |
BrandonXLF (talk | contribs) 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( |
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 |
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, |
local function shieldSpec(route, shieldType, size, ignoreUpright) |
||
local shieldSpec = {} |
local shieldSpec = {} |
||
local shield |
local shield |
||
if |
if route.to and shieldType == 'main' then shield = parser(route, 'shieldtomain') end |
||
if |
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 |
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 |
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, |
shield = {filename, singleShieldSize}, |
||
banners = bannerSpec(shieldBanner, |
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, |
shield = {shield, shieldSize}, |
||
banners = bannerSpec(banner, |
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 |
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 |
|||
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, |
local function render(shieldEntry, nonDecorative) |
||
local shield = shieldEntry.shield |
local shield = shieldEntry.shield |
||
local banners = shieldEntry.banners |
local banners = shieldEntry.banners |
||
local exists |
local exists = shieldExists(shield[1]) |
||
if not exists then return '' end |
if not exists then return '' end |
||
local |
local alt = '' |
||
if |
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 |
|||
"[[File:%s|%s|link=|alt=%s]]<br>%s", |
|||
banner[1], |
|||
banner[2], |
|||
shieldCode |
alt, |
||
shieldCode |
|||
) |
|||
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, |
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, |
for _, entry in ipairs(shieldSpec(route, shieldType, size, ignoreUpright)) do |
||
insert(rendered, render(entry, |
insert(rendered, render(entry, nonDecorative)) |
||
end |
end |
||
return concat(rendered), missingShields |
|||
return concat(rendered, ' '), missingShields |
|||
end |
end |
||
-- Links |
|||
function p.link(route) |
|||
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 = "​" -- Zero-width space |
|||
elseif sub2 == " " then |
|||
-- TODO transition |
|||
warnings.sub2 = " argument for sub2 parameter is deprecated" |
|||
sub2Text = "​" -- 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 |
||