Home
Random
Log in
Settings
About The Republic Wiki
Disclaimers
The Republic Wiki
Search
Editing
Module:ConvertIB
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
require('strict') local p = {} local getArgs = require('Module:Arguments').getArgs -- Function to pull out values and units from numeric args -- Returns: -- values: list of numeric values, or "false" if no numeric argument is given -- units: list of units (str) -- value: if there is a last numeric value unpaired with a unit, it becomes the precision -- anyValue: whether there is a non-false value in the values list local function parseValuesUnits(args) local values = {} local units = {} local indx = 1 local value = nil local anyValue = false -- loop through numeric arguments in pairs while args[indx] or args[indx+1] do value = args[indx] anyValue = anyValue or value -- if there is a unit, save in output lists if args[indx+1] then table.insert(values, value or false) table.insert(units, args[indx+1]) value = nil end indx = indx+2 end return values, units, value, anyValue end -- Function to identify multiple units and rewrite them as new input or output groups -- Args: -- values, units: numeric values and units, as lists with same length -- Returns: -- newValues, newUnits: same lists rewritten local function parseMultiples(values, units) local newValues = {} local newUnits = {} local i = 1 -- we will search for multiples with up to 4 entries (depending on length) local maxMultiple = math.min(4,#units-1) local valueFound = false -- flag to suppress second (and later) input values --- Hack for handling "stone": check if only value supplied is "lb" local onlyPounds = true for i = 1, #units do if values[i] and units[i] ~= 'lb' then onlyPounds = false break end end local multiple = mw.loadData('Module:ConvertIB/data').multiple -- sweep through units while i <= #units do -- determine index of last possible unit that could contain a multiple local last_unit = math.min(i+maxMultiple-1,#units) local multipleFound = false -- try from longest multiple down to double multiple (prefer longest ones) for j = last_unit, i+1, -1 do local key = table.concat({unpack(units,i,j)}, '') if multiple[key] then -- we found a multiple unit multipleFound = true -- Hack for "stone": add either 'lb' or multiple unit string to output units -- depending on whether 'lb' was the only unit string with a value if mw.ustring.sub(key,1,2) == 'st' then table.insert(newValues, false) table.insert(newUnits, onlyPounds and key or 'lb') end -- if there are any value in the span of the multiple, -- then the multiple is an input -- assume all missing values after the first are zero local firstValueFound = false for k = i, j do firstValueFound = not valueFound and (firstValueFound or values[k]) if firstValueFound then table.insert(newValues, values[k] or 0) table.insert(newUnits, units[k]) end end valueFound = valueFound or firstValueFound -- if no values in the span of the multiple, -- then the multiple is an output. Insert combined string as output unit if not firstValueFound then table.insert(newValues, false) table.insert(newUnits, key) end i = j+1 break end end --- If no multiple unit was found, insert value[i] and unit[i] into rewritten lists if not multipleFound then if valueFound then table.insert(newValues, false) -- skip writing value if it is a duplicate else table.insert(newValues,values[i]) valueFound = values[i] end table.insert(newUnits, units[i]) i = i+1 end end return newValues, newUnits end -- Call {{convert}} with args local function callConvert(args) local frame = mw.getCurrentFrame() return frame:expandTemplate{title='Convert', args=args} end -- Implement {{convinfobox}} function p._convert(args) -- find all values and units in numeric args (and the precision, if it exists) local values, units, precision, anyValue = parseValuesUnits(args) -- bail if no values at all if not anyValue then return nil end -- rewrite values and units if multiple units are found values, units = parseMultiples(values, units) -- sort input and outputs into different buckets local input_values = {} local input_units = {} local output_units = {} for i = 1, #units do if values[i] then table.insert(input_values, values[i]) table.insert(input_units, units[i]) else table.insert(output_units, units[i]) end end -- bail if nothing to convert if #input_values == 0 or #output_units == 0 then return nil end -- assemble argument list to {{convert}} local innerArgs = {} -- First, pass all input unit(s) for i, v in ipairs(input_values) do table.insert(innerArgs,v) table.insert(innerArgs,input_units[i]) end -- Then the output unit(s) [concatenated as single argument] table.insert(innerArgs,table.concat(output_units,"+")) if precision then table.insert(innerArgs,precision) -- last non-nil value contains precision end -- now handle all non-numeric arguments, passing to {{convert}} innerArgs.abbr = 'on' -- abbr=on by default for k, v in pairs(args) do if not tonumber(k) then innerArgs[k] = v end end return callConvert(innerArgs) end local function impUnitPref(pref,country) --- lower case all arguments pref = pref and mw.ustring.lower(pref) country = country and mw.ustring.lower(country) --- determine imperial unit by going thru arguments in priority order if pref and pref ~= 'dunam' then local impPref = mw.loadData('Module:ConvertIB/data').impPref return impPref[pref] elseif country then return mw.ustring.find(country,"united states",1,true) or mw.ustring.find(country,"united kingdom",1,true) end return false end -- Implement {{Infobox settlement/areadisp}} function p._area(args) local pref = args['pref'] local country = args['name'] local impus = impUnitPref(pref, country) local km2 = args['km2'] local mi2 = args['mi2'] or args['sqmi'] local ha = args['ha'] local acre = args['acre'] local dunam = args['dunam'] or args['dunum'] local link = args['link'] local innerArgs = {} innerArgs.abbr = 'on' innerArgs.order = 'out' if km2 then table.insert(innerArgs,km2) table.insert(innerArgs,'km2') elseif mi2 then table.insert(innerArgs,mi2) table.insert(innerArgs,'sqmi') end if km2 or mi2 then table.insert(innerArgs,impus and 'sqmi km2' or 'km2 sqmi') return callConvert(innerArgs) end if ha then table.insert(innerArgs,ha) table.insert(innerArgs,'ha') elseif acre then table.insert(innerArgs,acre) table.insert(innerArgs,'acre') end if ha or acre then table.insert(innerArgs,impus and 'acre ha' or 'ha acre') return callConvert(innerArgs) end if dunam then table.insert(innerArgs,dunam) table.insert(innerArgs,'dunam') pref = pref and mw.ustring.lower(pref) local order = pref == 'dunam' and 'dunam ' or '' dunam = mw.getContentLanguage():parseFormattedNumber(dunam) if impus then order = order..(dunam and dunam < 2589 and 'acre ha' or 'sqmi km2') else order = order..(dunam and dunam < 1000 and 'ha acre' or 'km2 sqmi') end table.insert(innerArgs,order) local yesNo = require('Module:Yesno') if yesNo(link,true) and link ~= 'none' then innerArgs.lk = 'in' end return callConvert(innerArgs) end return nil end -- Implement {{Infobox settlement/lengthdisp}} function p._length(args) local pref = args['pref'] if pref == 'dunam' then -- ignore dunam pref for this function pref = nil end local country = args['name'] local impus = impUnitPref(pref, country) local km = args['km'] local mi = args['mi'] local m = args['m'] local ft = args['ft'] local innerArgs = {} innerArgs.abbr = 'on' innerArgs.order = 'out' if km then table.insert(innerArgs,km) table.insert(innerArgs,'km') elseif mi then table.insert(innerArgs,mi) table.insert(innerArgs,'mi') end if km or mi then table.insert(innerArgs,impus and 'mi km' or 'km mi') return callConvert(innerArgs) end if m then table.insert(innerArgs,m) table.insert(innerArgs,'m') elseif ft then table.insert(innerArgs,ft) table.insert(innerArgs,'ft') end if m or ft then table.insert(innerArgs,impus and 'ft m' or 'm ft') return callConvert(innerArgs) end return nil end --Compute number of significant digits in a numeric string local function computeSigFig(s) local num_str = string.match(tostring(s), '^[+-]?[%d%.,]+%d*') if not num_str then return 0 end -- Strip leading signs num_str = string.gsub(num_str, '^[+-]', '') -- Strip commas num_str = string.gsub(num_str, ',', '') -- Strip leading zeros num_str = string.gsub(num_str, '^0*', '') if num_str == '' then return 0 end -- If there's a decimal point, all trailing zeros are significant. if string.find(num_str, '%.') then return #string.gsub(num_str, '%.', '') -- Count all digits after removing decimal end -- If no decimal point, trailing zeros are not significant. -- Count all digits up to the last non-zero one. num_str = string.gsub(num_str, '0+$', '') return #num_str end -- Implement {{Infobox settlement/densdisp}} -- Returns table: -- density = computed value if no error -- error = error string if error. -- These are only errors detected in this code, {{convert}} does its own error handling function p._density(args) local result = {} local lang = mw.getContentLanguage() local pref = args['pref'] if pref == 'dunam' then -- ignore dunam pref for this function pref = nil end local country = args['name'] local per_km2 = args['/km2'] local per_mi2 = args['/mi2'] or args['/sqmi'] local impus = impUnitPref(pref, country, per_km2, per_mi2) local per_km2_value = lang:parseFormattedNumber(per_km2) local per_mi2_value = lang:parseFormattedNumber(per_mi2) local innerArgs = {} innerArgs.abbr = 'on' if per_km2_value or per_mi2_value then innerArgs.order = 'out' if per_km2_value then table.insert(innerArgs,per_km2_value) table.insert(innerArgs,'/km2') else table.insert(innerArgs,per_mi2_value) table.insert(innerArgs,'/sqmi') end table.insert(innerArgs,impus and '/sqmi /km2' or '/km2 /sqmi') result.density = callConvert(innerArgs) return result end if per_km2 ~= 'auto' and per_mi2 ~= 'auto' then -- automatic computation not requested, fail silently return result end if not args['pop'] then -- fail silently if no population given return result end local areaSigFig local areaValue local areaUnit for _, unit in ipairs({'km2','mi2','sqmi','ha','acre','dunam','dunum'}) do local value = lang:parseFormattedNumber(args[unit]) if value then if value <= 0 then result.error = unit.." value not positive" return result end areaValue = value areaUnit = unit areaSigFig = computeSigFig(args[unit]) break elseif args[unit] then result.error = "Malformed "..unit.." value" return result end end if not areaSigFig then -- fail silently if no area given return result end if areaSigFig == 0 then result.error = "Malformed area string" return result end local popValue = lang:parseFormattedNumber(args['pop']) if not popValue then result.error = "Malformed population value" return result end if popValue < 0 then result.error = "Negative population value" return result end table.insert(innerArgs,popValue/areaValue) table.insert(innerArgs,'/'..areaUnit) local popSigFig = computeSigFig(args['pop']) local sigFig = popSigFig < areaSigFig and popSigFig or areaSigFig if sigFig < 2 then sigFig = 2 end innerArgs.sigfig = sigFig innerArgs.disp = 'out' table.insert(innerArgs,'/km2') local metric = callConvert(innerArgs) innerArgs[3] = '/sqmi' local imperial = callConvert(innerArgs) if impus then result.density = string.format("%s (%s)",imperial,metric) else result.density = string.format("%s (%s)",metric,imperial) end return result end function p.convert(frame) local args = getArgs(frame) return p._convert(args) or "" end function p.area(frame) local args = getArgs(frame) return p._area(args) or "" end function p.length(frame) local args = getArgs(frame) return p._length(args) or "" end function p.density(frame) local args = getArgs(frame) local result = p._density(args) if result.density then return result.density end if result.error then local warning = require('Module:If_preview')._warning local result = warning({result.error}) if mw.title.getCurrentTitle().namespace == 0 then result = result..'[[Category:Pages using infobox settlement with bad density arguments]]' end return result end return '' end return p
Summary:
Please note that all contributions to The Republic Wiki are considered to be released under the Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) (see
The Republic Wiki:Copyrights
for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:ConvertIB/doc
(
edit
)