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 41,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. |
Collection of utility functions for use within Module:Road data modules. See the code for function usages. Exported functions are: addAll
, arrayToString
, convertLengths
and err
.
local util = {}
local insert = table.insert
local concat = table.concat
local format = mw.ustring.format
---
-- Add all entries in `arr` into `target`.
-- An error is raised if `overwrite` is not true
-- and any key in `arr` is already in `target`.
function util.addAll(target, arr, overwrite)
if type(target) ~= "table" then
error("target is not a table")
end
for key,value in pairs(arr) do
if overwrite or target[key] == nil then
target[key] = value
else
error("Duplicate key: " .. tostring(key))
end
end
end
local function comp(e1, e2)
local t1 = type(e1)
local t2 = type(e2)
if t1 ~= t2 then return t1 < t2 end
if t1 == "function" then
error("Unexpected function type")
end
return e1 < e2
end
local arrayToStringAux
arrayToStringAux = function(arr, indent)
if type(arr) ~= "table" then
error("arr is not a table")
end
if type(indent) ~= "number" then
error("indent is not a number")
end
local result = {}
local keys = {}
for key in pairs(arr) do insert(keys, key) end
table.sort(keys, comp)
for _,key in ipairs(keys) do
local value = arr[key]
local keyPrint
if type(key) == "string" then
keyPrint = format("\"%s\"", key)
else
keyPrint = tostring(key)
end
local valuePrint
if type(value) == "table" then
valuePrint = format("{\n%s\n%s}",
arrayToStringAux(value, indent + 4),
string.rep(" ", indent))
elseif type(value) == "string" then
valuePrint = format("\"%s\"", value)
else
valuePrint = tostring(value)
end
insert(result, format("%s[%s] = %s",
string.rep(" ", indent),
keyPrint,
valuePrint))
end
return concat(result, ", \n")
end
--- Return a string representation of `arr`.
function util.arrayToString(arr, indent)
return arrayToStringAux(arr, indent or 0)
end
local function convert(distance, multiplier, desiredPrec)
if type(distance) ~= "string" then
error("distance is not a string")
end
if type(multiplier) ~= "number" then
error("multiplier is not a number")
end
-- Import math functions.
local math = require "Module:Math"
-- This function returns the precision of a given string representing a number.
local precision = math._precision
-- This function returns the order of magnitude of a given string representing a number.
local order = math._order
-- This function rounds a given number to the given number of digits.
local round = math._precision_format
local prec = desiredPrec or precision(distance)
if not desiredPrec then
local ord = order(distance)
-- Adjust precision based on multiplier, as done in {{convert}}.
prec = prec - order(multiplier / 0.2)
end
local converted = distance * multiplier
local magnitude = order(converted)
if prec <= -magnitude then
-- Ensure the result has at least two significant digits.
prec = -magnitude + 1
end
return round(converted, prec)
end
--[[-
Convert length specified in one unit (mi or km) to length in the other unit.
@param #map<#string, #string> lengths
a map from unit to distance (as a string) in that unit;
may contain entry `prec` indicating desired conversion precision
@param #string blank text to be used if length is unspecified
@return #table a table containing the conversion result:
orig = source unit;
comp = target unit;
mi = length in miles;
ft = converted length in feet;
km = length in kilometers;
m = converted length in meters;
error = error message, if any
]]
function util.convertLengths(lengths, blank)
-- Import math functions.
local math = require "Module:Math"
-- In Lua, storing functions locally results in more efficient execution.
-- This function rounds a given number to the given number of digits.
local round = math._precision_format
-- This function returns the precision of a given string representing a number.
local precision = math._precision
local kmPerMile = 1.609344
local ftPerMile = 5280
-- The length in kilometers as passed to the function.
local km = lengths.km
-- The length in miles as passed to the function.
local mi = lengths.mi
-- Precision for the converted length.
local prec = lengths.prec
local errMsg = {}
-- Sanitize inputs.
local km_ = tonumber(km)
if km and not km_ then
insert(errMsg, util.err("km is not a number"))
end
local mi_ = tonumber(mi)
if mi and not mi_ then
insert(errMsg, util.err("mi is not a number"))
end
local prec_ = tonumber(prec)
if prec and not prec_ then
insert(errMsg, util.err("prec is not a number"))
end
prec = prec_
local ft
local m
local orig = "mi"
local comp = "km"
if mi and km then
insert(errMsg, util.err("Both mi and km are specified"))
elseif mi then
-- Length in miles was passed.
if mi_ then
-- If `mi` is indeed a number, compute and round the length in kilometers.
km = convert(mi, kmPerMile, prec)
m = convert(mi, kmPerMile * 1000, prec)
-- format mi (insert separators as in 1,000)
mi = round(mi_, precision(mi))
else
-- `mi` is not a number.
km = blank
m = blank
end
elseif km then
-- Length in kilometers was passed.
-- Swap units.
orig, comp = comp, orig
if km_ then
-- If `km` is indeed a number, compute and round the length in miles.
mi = convert(km, 1 / kmPerMile, prec)
ft = convert(km, ftPerMile / kmPerMile, prec)
-- format km (insert separators as in 1,000)
km = round(km_, precision(km))
else
-- `km` is not a number.
mi = blank
ft = blank
end
else
mi = blank
ft = blank
km = blank
m = blank
end
local error = concat(errMsg)
if error == "" then error = nil end
return {mi = mi, ft = ft, km = km, m = m, orig = orig, comp = comp,
error = error}
end
--- Generates wikitext error messages.
function util.err(msg)
if msg == nil then
error("Unspecified error message")
end
return format('<strong class="error">Error: %s</strong>', msg)
end
return util