انتقل إلى المحتوى

وحدة:Size

من ويكي مصدر، المكتبة الحرة
--[[
  __  __           _       _        ____  _         
 |  \/  | ___   __| |_   _| | ___ _/ ___|(_)_______ 
 | |\/| |/ _ \ / _` | | | | |/ _ (_)___ \| |_  / _ \
 | |  | | (_) | (_| | |_| | |  __/_ ___) | |/ /  __/
 |_|  |_|\___/ \__,_|\__,_|_|\___(_)____/|_/___\___|
                                                    
Authors and maintainers:
* User:Zolo   - original draft
* User:Jarekt - original version
]]
require('strict')
local core      = require('Module:Core')
local formatnum = require "Module:Formatnum".formatNum
local createTag = require('Module:TagQS').createTag

-- ==================================================
-- === global parameters  ===========================
-- ==================================================

--  arrays for unit conversion   3     4     5     6     7       8       9        10     11  12    13   14       15
local unitMult   = {1e-9, 1e-6, 1e-3, 1e-2,  1  , 1e3 , 0.0254, 0.3048, 0.9144, 1609.344, 1, 1e3, 1e6, 28.3495, 453.592, 1}  -- conversion to meters
local unitList   = {'nm', 'um', 'mm', "cm", "m" , "km", "in"  , "ft"  , "yd"  , "mi",    "g", "kg", "t", "oz", "lb", "ct" }   -- units handled by this module
local unitType   = {'m' , 'm' , 'm' , 'm' , 'm' , 'm' , 'i'   , 'i'   , 'i'   , 'i' ,    'm', 'm' , 'm', 'i' , 'i', ''    }   -- m for metric and i for imperial
local unitItem   = {nm='Q178674', um='Q175821', mm='Q174789', cm='Q174728', m='Q11573', km='Q828224',     -- used for unit abbreviation translation
                    ['in']='Q218593', ft='Q3710', yd='Q482798', mi='Q253276', kg='Q11570', g='Q41803', t='Q191118', oz='Q48013', lb="Q100995", ct="Q261247" }
-- properties used for harvesting the wikidata 	and item IDs are used for translating dimension's name									
local dimProp = { length='P2043' , height='P2048'  , width='P2049' , depthH='P5524',    depthV='P4511'  , thickness='P2610'   , diameter='P2386' , radius='P2120'  , perimeter='P2547' , weight='P2067'}
local dimName = { length='Q36253', height='Q208826', width='Q35059', depthH='Q3250078', depthV='Q930412', thickness='Q3589038', diameter='Q37221', radius='Q173817', perimeter='Q28474', weight='Q11423'} 

local	aliases    = { -- aliases for units used to unify
	Q178674="nm", nm="nm", nanometer="nm",	nanometers= "nm", nanometre="nm", nanometres="nm",
	Q175821="um",	um="um", ["µm"]="um", micrometer="um", micrometers="um", micrometre="um", micrometres="um",
	Q200323="dm", dm="dm", decimeter="dm", decimeters="dm", decimetre="dm", decimetres="dm",
	Q174789="mm", mm="mm", millimeter="mm", millimeters="mm", millimetre="mm", millimetres="mm",
	Q174728="cm", cm="cm", centimeter="cm", centimeters="cm", centimetre="cm", centimetres="cm",
	Q11573="m", m="m", meter="m", meters="m", metre="m", metres="m", 
	Q828224="km", km="km", kilometer="km", kilometers="km", kilometre="km", kilometres="km",
	Q218593="in", ["in"]="in", inch="in", inches="in", Q61771670="in",
	Q3710="ft", ft="ft", foot="ft", feet="ft", 
	Q482798="yd", yd="yd", yard="yd", yards="yd", Q61756607="yd",
	Q253276="mi", mi="mi", mile="mi", miles="mi",
	Q93318="nmi", nmi="nmi", ["nautic mile"]="nmi", ["nautic miles"]="nmi",
	Q11570="kg", kilogram="kg", kilograms="kg", kg="kg",
	Q41803="g", gram="g", grams="g", g="g",
	Q191118="t", tonne="t", tonnes="t", ton="t", tons="t", ["metric ton"]="t", t="t", 
	Q48013="oz", ounce="oz", oz="oz", 
	Q100995="lb", pound="lb", pounds="lb", lb="lb",
	Q261247="ct", carat="ct", ct="ct"
}

-- ==================================================
-- === Internal functions ===========================
-- ==================================================

-------------------------------------------------------------------------------
local function getBareLabel(id1, id2, userLang) 
-- code equivalent to require("Module:Wikidata label")._getLabel with Wikidata=- option
	local label, link
	-- build language fallback list
	local langList = mw.language.getFallbacksFor(userLang)
	table.insert(langList, 1, userLang)
	for _, lang in ipairs(langList) do  -- loop over language fallback list looking for label in the specific language
		label = mw.wikibase.getLabelByLang(id1, lang)
		if not label and id2 then 
			label = mw.wikibase.getLabelByLang(id2, lang)
		end
		if label then break end  -- label found and we are done
	end	
	return label or id
end

-------------------------------------------------------------------------------
local function translateProperty(item, prop, lang)
	local n, title = 0, {}
	for _, statement in pairs( mw.wikibase.getBestStatements( item, prop )) do 
		if (statement.mainsnak.snaktype == "value") then 
			local val = statement.mainsnak.datavalue.value
			title[val.language] = val.text -- look for multiple values each with a language code
			n = n+1;
		end
	end
	if n>0 then
		return core.langSwitch(title, lang)
	end
end

-------------------------------------------------------------------------------
local function findInArray(str, list)
	for k, v in ipairs(list) do
		if v==str then                -- match units with the list
			return k;
		end
	end	
	return nil
end

-------------------------------------------------------------------------------
local function formatNum( value, lang, precision )
	assert(value, "Input value is nil")
	assert(precision, "Input precision is nil")
	local str = formatnum( value, lang, precision )
	str = mw.ustring.gsub(str, "[,%.]0+$", "") -- remove trailing zeros
	return str
end

-------------------------------------------------------------------------------
--[[
INPUTS:
* v - size in meters
* unitMult - array used to convert meters to other units
* iMin, iMax - min and max index of unitMult array to use
]]
local function niceNumber(v, iMin, iMax)
	local s = 10; -- scaling parameter. Means that "nice" numbers are in units that give the smallest number bigger than s
	if v<s*unitMult[iMin] then
			return iMin  -- will show as fractions of the smallest unit
	end
	for k = iMin,iMax-1 do
		if v>=s*unitMult[k] and v<s*unitMult[k+1] then
			return k
		end
	end
	return iMax -- will use the largest unit
end

-------------------------------------------------------------------------------
local function pickUnit(val, unit, lang)
	local k1, k2, unit1, unit2
	unit1 = aliases[unit]                                         -- convert unit item ID to standard units
	assert(unit1, "Unit name is not recognized: " .. unit)
	k1    = findInArray(unit1, unitList)	
	unit1 = translateProperty(unitItem[unit1], 'P5061', lang) -- gets the abbreviated form of the name of the unit
	
	local valInM = val*unitMult[k1]     -- find value in metres
	if (k1<=6) then                     -- input units are metric length
		k2 = niceNumber(valInM, 7, 10)  -- find best imperial units
	elseif (k1<=10) then                -- input units are imperial length
		k2 = niceNumber(valInM, 1, 6)   -- find best metric units
	elseif (k1<=13) then                -- input units are metric weight
		k2 = niceNumber(valInM, 14, 15) -- find best imperial units
	else                                -- input units are imperial weight
		k2 = niceNumber(valInM, 11, 13) -- find best metric units
	end
	local factor = unitMult[k1]/unitMult[k2]
	unit2 = translateProperty(unitItem[unitList[k2]], 'P5061', lang) -- gets the abbreviated form of the name of the unit
	return unit1 or unit, unit2 or unitItem[unitList[k2]], factor, unitType[k1]
end

-------------------------------------------------------------------------------
local function unit_conversion(val, unit, prec, lang, wordsep)
	local factor, unit1, unit2, numStr1, numStr2, system, str

	unit1, unit2, factor, system = pickUnit(val, unit, lang) -- based on val magnitude and unit, translate unit and provide coversion factor to convert to other type of units

	numStr1 = formatNum( val, lang, prec)  .. wordsep .. unit1
	if (lang~='en' and lang~='en-US' and system=='m') or (system=='') then -- if input is in metric units and output language is not English then show only metric output
		return numStr1 --  just show metric values
	end
	
	-- final string in imperial and metric units
	numStr2 = formatNum( val*factor, lang, prec)
	str = mw.ustring.format("%s%s(%s%s%s)", numStr1, wordsep, numStr2, wordsep, unit2)
	str = mw.ustring.gsub(str, '(%d)%s(%p)', '%1%2')
	return str
end

-------------------------------------------------------------------------------
local function disambiguate_dimensions(args)
	-- compare painting dimensions to image dimensions
	if args[1] and args[2] and not args[3] then
		local title = mw.title.getCurrentTitle()
		if title.namespace==6 then  -- this is a file
			local width, height, ratio, R, dr1, dr2, dr
			width  = title.file.width
			height = title.file.height
			ratio  = 1.0*height/width           -- file size ratio
			R      = 1.0*args[1]/args[2]        -- painting size ratio
			dr1    = math.abs(  R-ratio)/ratio  -- compare ratios
			dr2    = math.abs(1/R-ratio)/ratio
			dr     = math.min(dr1, dr2)
			args.debug = string.format('width=%f; height=%f; ratio=%f; R=%f; dr=%f', width, height, ratio, R, dr)
			if dr<0.1 and (ratio>1.15 or ratio<0.85) then -- ratios are within 10% from each other and image is not square
				if dr1<dr2 then
					args.height, args.width = args[1], args[2]
				else
					args.height, args.width = args[2], args[1]
				end
				args[1], args[2] = nil, nil
			end
		end
	end
	return args
end

-------------------------------------------------------------------------------
local function create_QScode(args, unitIDs)
	-- create non-visible encoding with untranslated dimensions
	local meta_str = ''
	local fields = { 'length', 'height', 'width', 'depthH', 'depthV', 'thickness', 'diameter'}
	local meta = {}
	for _, field in ipairs( fields ) do
		if args[field] then
			local uStr = unitIDs[field] -- get item ID of the unit
			uStr = "U" .. string.sub(uStr, 2, -1) -- replace Q with U on the beginning of the string
			table.insert(meta, createTag('dimensions', dimProp[field], args[field] .. uStr) )
		end
	end
	if #meta>0 then
		meta_str = table.concat(meta, '')
	end
	return meta_str
end

-------------------------------------------------------------------------------
local function harvest_wikidata(args, lang)
	-- each property stores a single dimension. Notice that P4511 is for vertical depth only, while Size template parameter "depth" was mostly used for horizontal depth
	local entity, units, wdIcon = nil, {}, {}
	if args.wikidata then
		entity = mw.wikibase.getEntity(args.wikidata)
	elseif args.entity then
		entity = args.entity
	end
	if entity then
		for field, prop in pairs(dimProp) do
			if entity.claims and entity.claims[prop] then -- if we have wikidata item and item has the property
				for _, statement in pairs( entity:getBestStatements( prop )) do
					if (statement.mainsnak.snaktype == "value") then 
						local v = statement.mainsnak.datavalue.value
						if v.unit and #v.unit>20 then
							args  [field] = v.amount
							units [field] = string.gsub(v.unit, "http:%/%/www%.wikidata%.org%/entity%/", "") -- strip URL and keep the item ID
							wdIcon[field] = core.editAtWikidata(entity.id, prop, lang)
						end
					end
				end
			end
		end
	end
	return args, units, wdIcon
end

-- ==================================================
-- === External functions ===========================
-- ==================================================
local p = {}

-- ===========================================================================
-- === Version of the function to be called from other LUA codes
-- ===========================================================================

function p._size_old(args, unit, prec, lang)
--This function mimics the functionality of the original {{Size|unit|dim1|dim2|dim3}} template

	if not prec then
		prec = 1;
		if unit == 'mm' then prec=2; end
	end
	
	-- process values
	local val, mean = {}, 0
	for i = 1,3 do
		local v = args[i]
		if v then
			v = string.gsub(v, ',', '.')
			v = tonumber(v)
			if type(v)=='number' and v>0 then
				table.insert(val, v)
				mean = mean + v
			end
		end
	end
	mean = mean / #val -- find mean of 3 dimensions
	assert(#val>0, "No numeric dimensions found.")
	--if n==0 then return '' end
	
	-- pick metric and imperial units
	local factor, unit1, unit2, system 
	unit1, unit2, factor, system = pickUnit(mean, unit, lang)

	-- convert numbers to localized strings
	local numStr1, numStr2 = {}, {}
	for _, v in ipairs(val) do
		table.insert(numStr1, formatNum( v       , lang, prec))
		table.insert(numStr2, formatNum( v*factor, lang, prec))
	end
	
	-- final string in the same units as input
	local wordsep = mw.message.new( "Word-separator" ):inLanguage(lang):plain()
	local x = wordsep .. '×'.. wordsep
	numStr1 = table.concat( numStr1, x) .. wordsep .. unit1
	if (lang~='en' and lang~='en-US' and system=='m') or (system=='') then -- if input is in metric units and output language is not English then show only metric output
		return numStr1 --  just show metric values
	end

	-- final string in imperial and metric units
	numStr2 = table.concat( numStr2, x) .. wordsep .. unit2
	return mw.ustring.format("%s%s(%s)", numStr1, wordsep, numStr2)
end

-- ==================================================
function p._size(args, unit, prec, lang)
--This function mimics the functionality of the latter {{Size|unit|width=...|height=...|...}} template
	local unit1 = aliases[unit] -- disambiguate units
	assert(unit1 or args.wikidata or args.entity, "Unit name is not recognized")

	if not prec then
		prec = 1;
		if unit == 'mm' then prec=2; end
	end
	args.depthH = args.depth  -- assume that "depth" defined by {{Size}} meant "horizontal dimension away from the observer"
	
	-- harvest wikidata
	local unitIDs, wdIcon
	args, unitIDs, wdIcon = harvest_wikidata(args, lang)
	if core.yesno(args.noicon, false) then
		wdIcon = {}
	end 
	
	-- create the final string
	local colon     = mw.message.new( "Colon-separator" ):inLanguage(lang):plain()
	local semicolon = mw.message.new( "Semicolon-separator" ):inLanguage(lang):plain()
	local wordsep   = mw.message.new( "Word-separator" ):inLanguage(lang):plain()
	local dimOrder = { 'length', 'height', 'width', 'depthH', 'depthV', 'thickness', 'diameter', 'radius', 'perimeter', 'weight'} -- array with order of fields to display
	local results = {}
	for _, field in ipairs(dimOrder) do -- values with named dimensions like "depth: 2 cm"
		local val = args[field]
		if val then
			unitIDs[field] = unitIDs[field] or unitItem[unit] 
			val = string.gsub(val, ',', '.')
			val = tonumber(val)
			if type(val)=='number' then 
				local dimStr = getBareLabel(dimName[field], dimProp[field], lang) 
				local valStr = unit_conversion(val, unitIDs[field], prec, lang, wordsep)
				table.insert(results, dimStr .. colon .. valStr .. (wdIcon[field] or '')) 
			end
			
		end
	end
	
	local qs = ''
	if core.yesno(args.tagqs, true) then
		qs = create_QScode(args, unitIDs)
	end

	return table.concat(results, semicolon) .. qs
end
 
-- ===========================================================================
-- === Versions of the function to be called from template namespace
-- ===========================================================================

-- ==================================================
function p.size(frame)
	local args = core.getArgs(frame)
	
	local unit = args[1] or args.unit or args.units
	table.remove(args,1)
	unit = aliases[unit]
	if not unit and not args.wikidata then
		return ''
	end
	
	-- see if we can deduce which dimension is which
	local cat = ''
	if args[1] and args[2] then
		args = disambiguate_dimensions(args)
		-- if not args[2] then
		--	cat = '\n[[تصنيف:Size templates with unnamed dimensions]]'
		-- end
		--cat = cat .. args.debug
	end
	
	-- call either a function for named and for unnamed dimensions
	if args[1] or args[2] or args[3] then
--		args = table.remove(args,1)
		return p._size_old(args, unit, args.prec, args.lang) .. cat -- old style of display for unnamed dimensions
	else
		return p._size(args, unit, args.prec, args.lang) .. cat -- dimensions are named
	end
end

return p
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy