Module:ConvertIB
Appearance
From Wikipedia, the free encyclopedia
Warning This Lua module is used on approximately 761,000 pages, or roughly 1% of all pages .
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them.
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them.
A Lua module that wraps {{convert }}, designed for infoboxes. It implements:
- {{convinfobox }}
- {{Infobox settlement/areadisp }}
- {{Infobox settlement/lengthdisp }}
- {{Infobox settlement/densdisp }}
Usage
{{#invoke:ConvertIB|convert}}
- Like {{convinfobox }}, accepts alternating series of pairs of [blank|value], unit . When a unit has a non-blank value, it will get converted to all other units that do have blank values
- Accepts all named parameters that {{convert }} does
- Accepts groups of multiple units (e.g., "5 ft 6 in") that {{convert }} does
{{#invoke:ConvertIB|area}}
- Implements {{Infobox settlement/areadisp }}, automatically converting area units, using the output order specified by MOS:UNIT
{{#invoke:ConvertIB|length}}
- Implements {{Infobox settlement/lengthdisp }}, automatically converting length units, using the output order specified by MOS:UNIT
{{#invoke:ConvertIB|density}}
- Implements {{Infobox settlement/densdisp }}, parsing population and area, producting density in inhabitants per square km and square mile.
The above documentation is transcluded from Module:ConvertIB/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Subpages of this module.
require('strict') localp={} localgetArgs=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 localfunctionparseValuesUnits(args) localvalues={} localunits={} localindx=1 localvalue=nil localanyValue=false -- loop through numeric arguments in pairs whileargs[indx]orargs[indx+1]do value=args[indx] anyValue=anyValueorvalue -- if there is a unit, save in output lists ifargs[indx+1]then table.insert(values,valueorfalse) table.insert(units,args[indx+1]) value=nil end indx=indx+2 end returnvalues,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 localfunctionparseMultiples(values,units) localnewValues={} localnewUnits={} locali=1 -- we will search for multiples with up to 4 entries (depending on length) localmaxMultiple=math.min(4,#units-1) localvalueFound=false-- flag to suppress second (and later) input values --- Hack for handling "stone": check if only value supplied is "lb" localonlyPounds=true fori=1,#unitsdo ifvalues[i]andunits[i]~='lb'then onlyPounds=false break end end localmultiple=mw.loadData('Module:ConvertIB/data').multiple -- sweep through units whilei<=#unitsdo -- determine index of last possible unit that could contain a multiple locallast_unit=math.min(i+maxMultiple-1,#units) localmultipleFound=false -- try from longest multiple down to double multiple (prefer longest ones) forj=last_unit,i+1,-1do localkey=table.concat({unpack(units,i,j)},'') ifmultiple[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 ifmw.ustring.sub(key,1,2)=='st'then table.insert(newValues,false) table.insert(newUnits,onlyPoundsandkeyor'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 localfirstValueFound=false fork=i,jdo firstValueFound=notvalueFoundand(firstValueFoundorvalues[k]) iffirstValueFoundthen table.insert(newValues,values[k]or0) table.insert(newUnits,units[k]) end end valueFound=valueFoundorfirstValueFound -- if no values in the span of the multiple, -- then the multiple is an output. Insert combined string as output unit ifnotfirstValueFoundthen 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 ifnotmultipleFoundthen ifvalueFoundthen 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 returnnewValues,newUnits end -- Call {{convert}} with args localfunctioncallConvert(args) localframe=mw.getCurrentFrame() returnframe:expandTemplate{title='Convert',args=args} end -- Implement {{convinfobox}} functionp._convert(args) -- find all values and units in numeric args (and the precision, if it exists) localvalues,units,precision,anyValue=parseValuesUnits(args) -- bail if no values at all ifnotanyValuethen returnnil end -- rewrite values and units if multiple units are found values,units=parseMultiples(values,units) -- sort input and outputs into different buckets localinput_values={} localinput_units={} localoutput_units={} fori=1,#unitsdo ifvalues[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==0or#output_units==0then returnnil end -- assemble argument list to {{convert}} localinnerArgs={} -- First, pass all input unit(s) fori,vinipairs(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,"+")) ifprecisionthen 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 fork,vinpairs(args)do ifnottonumber(k)then innerArgs[k]=v end end returncallConvert(innerArgs) end localfunctionimpUnitPref(pref,country) --- lower case all arguments pref=prefandmw.ustring.lower(pref) country=countryandmw.ustring.lower(country) --- determine imperial unit by going thru arguments in priority order ifprefandpref~='dunam'then localimpPref=mw.loadData('Module:ConvertIB/data').impPref returnimpPref[pref] elseifcountrythen returnmw.ustring.find(country,"united states",1,true) ormw.ustring.find(country,"united kingdom",1,true) end returnfalse end locallargeUnits={km2=true,mi2=true,sqmi=true,km=true,mi=true} -- Implement {{Infobox settlement/areadisp}} functionp._area(args) localpref=args['pref'] localcountry=args['name'] localimpus=impUnitPref(pref,country) localdunam=args['dunam']orargs['dunum'] locallink=args['link'] localinnerArgs={} innerArgs.abbr='on' innerArgs.order='out' for_,unitinipairs({'km2','mi2','sqmi','ha','acre'})do ifargs[unit]then table.insert(innerArgs,args[unit]) table.insert(innerArgs,unit) iflargeUnits[unit]then table.insert(innerArgs,impusand'sqmi km2'or'km2 sqmi') else table.insert(innerArgs,impusand'acre ha'or'ha acre') end returncallConvert(innerArgs) end end ifdunamthen table.insert(innerArgs,dunam) table.insert(innerArgs,'dunam') pref=prefandmw.ustring.lower(pref) localorder=pref=='dunam'and'dunam 'or'' dunam=mw.getContentLanguage():parseFormattedNumber(dunam) ifimpusthen order=order..(dunamanddunam<2589and'acre ha'or'sqmi km2') else order=order..(dunamanddunam<1000and'ha acre'or'km2 sqmi') end table.insert(innerArgs,order) localyesNo=require('Module:Yesno') ifyesNo(link,true)andlink~='none'then innerArgs.lk='in' end returncallConvert(innerArgs) end returnnil end -- Implement {{Infobox settlement/lengthdisp}} functionp._length(args) localpref=args['pref'] ifpref=='dunam'then-- ignore dunam pref for this function pref=nil end localcountry=args['name'] localimpus=impUnitPref(pref,country) localinnerArgs={} innerArgs.abbr='on' innerArgs.order='out' for_,unitinipairs({'km','mi','m','ft'})do ifargs[unit]then table.insert(innerArgs,args[unit]) table.insert(innerArgs,unit) iflargeUnits[unit]then table.insert(innerArgs,impusand'mi km'or'km mi') else table.insert(innerArgs,impusand'ft m'or'm ft') end returncallConvert(innerArgs) end end returnnil end locallang localfunctionparseNumeric(s) localnum_str=string.match(tostring(s),'^[+-]?%d[%d,]*%.?%d*e?[+-]?%d*') ifnotnum_strthen returnnil end lang=langormw.getContentLanguage() returnlang:parseFormattedNumber(num_str) end --Compute number of significant digits in a numeric string localfunctioncomputeSigFig(s) localnum_str=string.match(tostring(s),'^[+-]?%d[%d,]*%.?%d*') ifnotnum_strthen return0 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*','') ifnum_str==''then return0 end -- If there's a decimal point, all trailing zeros are significant. ifstring.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 functionp._density(args) localresult={} localpref=args['pref'] ifpref=='dunam'then-- ignore dunam pref for this function pref=nil end localcountry=args['name'] localper_km2=args['/km2'] localper_mi2=args['/mi2']orargs['/sqmi'] localimpus=impUnitPref(pref,country,per_km2,per_mi2) localper_km2_value=parseNumeric(per_km2) localper_mi2_value=parseNumeric(per_mi2) localinnerArgs={} innerArgs.abbr='on' ifper_km2_valueorper_mi2_valuethen innerArgs.order='out' ifper_km2_valuethen 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,impusand'/sqmi /km2'or'/km2 /sqmi') result.density=callConvert(innerArgs) returnresult end ifper_km2~='auto'andper_mi2~='auto'then -- automatic computation not requested, fail silently returnresult end ifnotargs['pop']then -- fail silently if no population given returnresult end localareaSigFig localareaValue localareaUnit for_,unitinipairs({'km2','mi2','sqmi','ha','acre','dunam','dunum'})do localvalue=parseNumeric(args[unit]) ifvaluethen ifvalue<=0then result.error=unit.." value not positive" returnresult end areaValue=value areaUnit=unit areaSigFig=computeSigFig(args[unit]) break elseifargs[unit]then result.error="Malformed "..unit.." value" returnresult end end ifnotareaSigFigthen -- fail silently if no area given returnresult end ifareaSigFig==0then result.error="Malformed area string" returnresult end localpopValue=parseNumeric(args['pop']) ifnotpopValuethen result.error="Malformed population value" returnresult end ifpopValue<0then result.error="Negative population value" returnresult end table.insert(innerArgs,popValue/areaValue) table.insert(innerArgs,'/'..areaUnit) localpopSigFig=computeSigFig(args['pop']) localsigFig=popSigFig<areaSigFigandpopSigFigorareaSigFig ifsigFig<2then sigFig=2 end innerArgs.sigfig=sigFig innerArgs.disp='out' table.insert(innerArgs,'/km2') localmetric=callConvert(innerArgs) innerArgs[3]='/sqmi' localimperial=callConvert(innerArgs) ifimpusthen result.density=string.format("%s (%s)",imperial,metric) else result.density=string.format("%s (%s)",metric,imperial) end returnresult end functionp.convert(frame) localargs=getArgs(frame) returnp._convert(args)or"" end functionp.area(frame) localargs=getArgs(frame) returnp._area(args)or"" end functionp.length(frame) localargs=getArgs(frame) returnp._length(args)or"" end functionp.density(frame) localargs=getArgs(frame) localresult=p._density(args) ifresult.densitythen returnresult.density end ifresult.errorthen localwarning=require('Module:If_preview')._warning localresult=warning({result.error}) ifmw.title.getCurrentTitle().namespace==0then result=result..'[[Category:Pages using infobox settlement with bad density arguments]]' end returnresult end return'' end returnp