Différences entre les pages « Module:Coordinates » et « Module:Math »

De Les Mots de l'agronomie
(Différence entre les pages)
imported>Jacques Ducloy
m (1 révision importée)
 
imported>Jacques Ducloy
m (1 révision importée)
 
Ligne 1 : Ligne 1 :
local math_mod = require( "Module:Math" )
+
--[[
  
local p = {}
+
This module provides a number of basic mathematical operations.
  
--Chargement de la liste En/Au/Aux/A
+
]]
local gdata
+
local z = {}
local success, resultat = pcall (mw.loadData, "Module:Drapeau/Data" )
 
if success then
 
gdata = resultat
 
else
 
-- Banque de données à minima en cas de bogue dans le Module:Langue/Data
 
gdata={}
 
gdata.data={};
 
gdata.data[142]={qid="Q142", label="France", genre="fs"}
 
end
 
 
 
local i18n = {
 
N = 'N',
 
Nlong = 'nord',
 
W = 'O',
 
Wlong = 'ouest',
 
E = 'E',
 
Elong = 'est',
 
S = 'S',
 
Slong = 'sud',
 
degrees = '° ',
 
minutes = '′ ',
 
seconds = '″ ',
 
geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr',
 
tooltip = 'Cartes, vues aériennes, etc.',
 
errorcat = 'Page avec des balises de coordonnées mal formées',
 
sameaswikidata = 'Page avec coordonnées similaires sur Wikidata',
 
notaswikidata = 'Page avec coordonnées différentes sur Wikidata',
 
nowikidata = 'Page sans coordonnées Wikidata',
 
throughwikidata = 'Page géolocalisée par Wikidata',
 
invalidFormat = 'format invalide',                                          -- 'invalid coordinate format',
 
invalidNSEW = 'orientation invalide, devrait être "N", "S", "E" or "W"',    -- 'invalid direction should be "N", "S", "E" or "W"',
 
invalidNS = 'orientation de latitude invalide, devrait être "N" ou "S"',    -- 'could not find latitude direction (should be N or S)',
 
invalidEW = 'orientation de longitude invalide, devrait être "E" ou "W"',  -- 'could not find longitude direction (should be W or E) ',
 
noCardinalDirection = 'orientation cardinale non trouvée',                  -- 'no cardinal direction found in coordinates',
 
invalidDirection = 'direction invalide',                                    -- 'invalid direction',
 
latitude90 = 'latitude > 90',
 
longitude360 = 'longitude > 360',
 
minSec60 = 'minutes ou secondes > 60',
 
negativeCoode = 'en format dms les degrés doivent être positifs',          -- 'dms coordinates should be positive',
 
dmIntergers = 'degrés et minutes doivent être des nombres entiers',        -- 'degrees and minutes should be integers',
 
tooManyParam = 'trop de paramètres pour la latitude ou la longitude',      -- 'too many parameters for coordinates',
 
coordMissing = 'latitude ou longitude absente',                            -- 'latitude or longitude missing',
 
invalidGlobe = 'globe invalide : ',                                        -- 'invalid globe:',
 
}
 
local coordParse = {
 
NORTH = 'N',
 
NORD = 'N',
 
EAST = 'E',
 
EST = 'E',
 
WEST = 'W',
 
O = 'W',
 
OUEST = 'W',
 
SOUTH = 'S',
 
SUD = 'S',
 
}
 
 
 
--Aide:Fonction_genre
 
local genre = {
 
ms =    {le="le ",  du="du ",    de="du ",  au="au ",  en="au "},
 
msa =  {le="l'",  du="de l'",  de="d'",  au="à l'", en="en "},
 
msi =  {le="",    du="de ",    de="de ",  au="à ",  en="à "},
 
msia =  {le="",    du="d'",    de="d'",  au="à ",  en="à "},
 
msiae = {le="",    du="d'",    de="d'",  au="à ",  en="en "},
 
fs =    {le="la ",  du="de la ", de="de ",  au="à la ",en="en "},
 
fsa =  {le="l'",  du="de l'",  de="d'",  au="à l'", en="en "},
 
fsi =  {le="",    du="de ",    de="de ",  au="à ",  en="à "},
 
fsia =  {le="",    du="d'",    de="d'",  au="à ",  en="à "},
 
mp =    {le="les ", du="des ",  de="des ", au="aux ", en="aux "},
 
fp =    {le="les ", du="des ",  de="des ", au="aux ", en="aux "}
 
}
 
  
local globedata = {
+
-- Generate random number
--[[ notes:
+
function z.random( frame )
radius in kilometers (especially imprecise for non spheric bodies)
+
    local first = tonumber(frame.args[1]) -- if it doesn't exist it's NaN, if not a number it's nil
defaultdisplay is currently disabled, activate it ?
+
    local second = tonumber(frame.args[2])
]]--  
 
ariel = {radius = 580, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
callisto = {radius = 2410, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
ceres =  {radius = 470, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
charon =  {radius = 1214, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
deimos =  {radius = 7, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
dione =  {radius = 560, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
enceladus =  {radius = 255, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
ganymede =  {radius = 2634, defaultdisplay = 'dec west', trackingcat = 'sur Ganymède'},
 
earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = 'sur Terre'},
 
europa =  {radius = 1561, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
hyperion =  {radius = 140, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
iapetus =  {radius = 725, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
['io'] =  {radius = 1322, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
jupiter =  {radius = 68911, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
mars =  {radius = 3389.5, defaultdisplay = 'dec east', trackingcat =  'sur Mars' },
 
mercury =  {radius = 2439.7, defaultdisplay = 'dec west', trackingcat = 'sur Mercure'},
 
mimas =  {radius = 197, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
miranda =  {radius = 335, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
moon =  {radius = 1736, defaultdisplay = 'dec', trackingcat = 'sur la Lune'},
 
neptune =  {radius = 24553, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
oberon =  {radius = 761, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
phoebe =  {radius = 110, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
phobos =  {radius = 11, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
pluto =  {radius = 1185, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
rhea =  {radius = 765, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
saturn =  {radius = 58232, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
titan =  {radius = 2575.5, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
tethys =  {radius = 530, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
 
titania =  {radius = 394, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
triton = {radius = 1353, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
umbriel =  {radius = 584, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
uranus =  {radius = 25266, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
 
venus =  {radius = 6051.8, defaultdisplay = 'dec east', trackingcat = 'sur Vénus'},
 
vesta =  {radius = 260, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}
 
}
 
globedata[''] = globedata.earth
 
  
local wikidatathreshold = 10 -- si la distance entre coordonnées Wikipédia et Wikidata dépasse se seuil (en kilomètes), une catégorie de maintenance est ajoutée
+
    if first then -- if NaN or nil, will skip down to final return
local lang = mw.language.getContentLanguage()
+
        if first <= second then -- could match if both nil, but already checked that first is a number in last line
local default_zoom = 13
+
            return math.random(first, second)
 
+
        end
local function makecat(cat, sortkey)
+
        return math.random(first)
if type( sortkey ) == 'string' then
+
    end 
return '[[Category:' .. cat .. '|' .. sortkey .. ']]'
+
    return math.random()
else
 
return '[[Category:' .. cat .. ']]'
 
end
 
 
end
 
end
  
----------------------------------------
+
--[[
--Error handling
+
order
--[[ Notes:
 
when errors occure a new error message is concatenated to errorstring
 
an error message contains an error category with a sortkey
 
For major errors, it can also display an error message (the error message will the usually be returned and the function terminated)
 
More minor errors do only add a category, so that readers are not bothered with error texts
 
sortkeys:
 
* A: invalid latitude, longitude or direction
 
* B: invalid globe
 
* C: something wrong with other parameters
 
* D: more than one primary coord
 
]]--
 
  
local errorstring = ''
+
Determine order of magnitude of a number
  
local function makeerror(args)
+
Usage:
local errormessage = ''
+
    {{#invoke: Math | order | value }}
if args.message then  
+
]]
errormessage = '<strong class="error"> Coordonnées : ' .. args.message .. '</strong>'  
+
function z.order(frame)
end
+
    local input_string = (frame.args[1] or frame.args.x or '0');
local errorcat = ''
+
    local input_number;
if mw.title.getCurrentTitle().namespace == 0 then
+
   
errorcat = makecat(i18n.errorcat, args.sortkey)
+
    input_number = z._cleanNumber( frame, input_string );
end
+
    if input_number == nil then
errorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messages
+
        return '<strong class="error">Formatting error: Order of magnitude input appears non-numeric</strong>'
return nil
+
    else
 +
        return z._order( input_number )
 +
    end  
 
end
 
end
 
+
function z._order(x)
local function showerrors()
+
    if x == 0 then return 0 end
return errorstring
+
    return math.floor(math.log10(math.abs(x)))
 
end
 
end
  
 +
--[[
 +
precision
  
 +
Detemines the precision of a number using the string representation
  
-- Distance computation
+
Usage:
function p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globe
+
    {{ #invoke: Math | precision | value }}
 
+
]]
globe = string.lower(globe or 'earth')
+
function z.precision( frame )
+
    local input_string = (frame.args[1] or frame.args.x or '0');
-- check arguments and converts degreees to radians
+
    local trap_fraction = frame.args.check_fraction or false;
local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
+
    local input_number;
if (not latA) or (not latB) or (not longA) or (not longB) then return
+
   
error('coordinates missing, can\'t compute distance')
+
    if type( trap_fraction ) == 'string' then
end
+
        trap_fraction = trap_fraction:lower();
if type(latA) ~= 'number' or type(latB) ~= 'number' or type(longA) ~= 'number' or type(longB) ~= 'number' then
+
        if trap_fraction == 'false' or trap_fraction == '0' or
error('coordinates are not numeric, can\'t compute distance')
+
                trap_fraction == 'no' or trap_fraction == '' then
end
+
            trap_fraction = false;
if not globe or not globedata[globe] then
+
        else
return error('globe: ' .. globe .. 'is not supported')
+
            trap_fraction = true;
end
+
        end
+
    end
-- calcul de la distance angulaire en radians
+
   
local convratio = math.pi / 180 -- convertit en radians
+
    if trap_fraction then
latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
+
        local pos = string.find( input_string, '/', 1, true );
local cosangle = math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA)
+
        if pos ~= nil then
if cosangle >= 1 then -- may be above one because of rounding errors
+
            if string.find( input_string, '/', pos + 1, true ) == nil then
return 0
+
                local denominator = string.sub( input_string, pos+1, -1 );
end
+
                local denom_value = tonumber( denominator );
local angle = math.acos(cosangle)
+
                if denom_value ~= nil then
-- calcul de la distance en km
+
                    return math.log10(denom_value);
local radius = globedata[globe].radius
+
                end
return radius * angle
+
            end                       
end
+
        end
 
+
    end  
function p.distance(frame)
+
   
local args = frame.args
+
    input_number, input_string = z._cleanNumber( frame, input_string );
return p._distance(
+
    if input_string == nil then
{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},
+
        return '<strong class="error">Formatting error: Precision input appears non-numeric</strong>'
{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
+
    else
args.globe)
+
        return z._precision( input_string )
 +
    end   
 
end
 
end
 +
function z._precision( x )   
 +
    x = string.upper( x )
  
local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
+
    local decimal = string.find( x, '.', 1, true )
extraparams = extraparams or ''
+
    local exponent_pos = string.find( x, 'E', 1, true )
local geohacklatitude, geohacklongitude
+
    local result = 0;
-- format latitude and longitude for the URL
+
   
if tonumber(decLat) < 0 then
+
    if exponent_pos ~= nil then
geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
+
        local exponent = string.sub( x, exponent_pos + 1 )
else
+
        x = string.sub( x, 1, exponent_pos - 1 )
geohacklatitude = decLat .. '_N'
+
        result = result - tonumber( exponent )
end
+
    end   
if tonumber(decLong) < 0  then
+
   
geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
+
    if decimal ~= nil then
elseif globedata[globe].defaultdisplay == 'dec west' then
+
        result = result + string.len( x ) - decimal
geohacklongitude = decLong .. '_W'
+
        return result
else
+
    end
geohacklongitude = decLong .. '_E'
+
       
end
+
    local pos = string.len( x );
-- prepares the 'paramss=' parameter
+
    while x:byte(pos) == string.byte('0') do
local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
+
        pos = pos - 1
-- concatenate parameteres for geohack
+
        result = result - 1
return i18n.geohackurl ..
+
        if pos <= 0 then
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
+
            return 0
"&params=" .. geohackparams ..
+
        end
(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
+
    end
 +
   
 +
    return result
 
end
 
end
  
--HTML builder for a geohack link
+
--[[
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
+
max
-- geohack url
 
local url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
 
 
-- displayed coordinates
 
local displaycoords
 
if string.sub(displayformat,1,3) == 'dec' then
 
displaycoords = p.displaydec(decLat, decLong, displayformat)
 
else
 
displaycoords = {
 
p.displaydmsdimension(dmsLat, displayformat),
 
p.displaydmsdimension(dmsLong, displayformat),
 
}
 
end
 
 
-- build coordinate in h-geo / h-card microformat
 
local globeNode
 
if globe and globe ~= 'earth' then
 
globeNode = mw.html.create('data')
 
:addClass('p-globe')
 
:attr{ value = globe }
 
:done()
 
end
 
 
local coordNode = mw.html.create('')
 
if objectname then
 
coordNode = mw.html.create('span')
 
:addClass('h-card')
 
:tag('data')
 
:addClass('p-name')
 
:attr{ value = objectname }
 
:done()
 
end
 
coordNode
 
:tag('span')
 
:addClass('h-geo')
 
:addClass('geo-' .. string.sub(displayformat,1,3))
 
:tag('data')
 
:addClass('p-latitude')
 
:attr{ value = decLat }
 
:wikitext( displaycoords[1] )
 
:done()
 
:wikitext(", ")
 
:tag('data')
 
:addClass('p-longitude')
 
:attr{ value = decLong }
 
:wikitext( displaycoords[2] )
 
:done()
 
:node( globeNode )
 
:done()
 
 
-- buid GeoHack link
 
local root = mw.html.create('span')
 
:addClass('plainlinks nourlexpansion')
 
:attr('title', i18n.tooltip)
 
:wikitext('[' .. url )
 
:node(coordNode)
 
:wikitext("]")
 
:done()
 
 
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
 
local inlineText = displayinline and tostring(root) or ''
 
local titleText = ''
 
if displaytitle then
 
local htmlTitle = mw.html.create('span')
 
:attr{ id = 'coordinates' }
 
:addClass( displayinline and 'noprint' or nil )
 
:node( root )
 
local frame = mw.getCurrentFrame()
 
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
 
end
 
 
return inlineText .. titleText
 
end
 
  
local function zoom( extraparams )
+
Finds the maximum argument
local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )
 
if zoomParam then
 
return zoomParam
 
end
 
 
local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
 
if scale then
 
return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25)
 
end
 
 
local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
 
if extraType then
 
local zoomType = {
 
country = 5,
 
state = 6,
 
adm1st = 7,
 
adm2nd = 8,
 
city = 9,
 
isle = 10,
 
mountain = 10,
 
waterbody = 10,
 
airport = 12,
 
landmark = 13,
 
}
 
return zoomType[ extraType ]
 
end
 
end
 
  
--HTML builder for a geohack link
+
Usage:
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )
+
    {{#invoke:Math| max | value1 | value2 | ... }}
-- displayed coordinates
+
OR
local displaycoords
+
    {{#invoke:Math| max }}
if string.sub(displayformat,1,3) == 'dec' then
 
displaycoords = p.displaydec(decLat, decLong, displayformat)
 
else
 
displaycoords = {
 
p.displaydmsdimension(dmsLat, displayformat),
 
p.displaydmsdimension(dmsLong, displayformat),
 
}
 
end
 
 
-- JSON for maplink
 
local jsonParams = {
 
type = 'Feature',
 
geometry = {
 
type ='Point',
 
coordinates = {
 
math_mod._round( decLong, 6 ), -- max precision in GeoJSON format
 
math_mod._round( decLat, 6 )
 
}
 
},
 
properties = {
 
['marker-color'] = "228b22",
 
}
 
}
 
if objectname then
 
jsonParams.properties.title = objectname
 
end
 
-- ajout de geoshape via externaldata
 
local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )
 
if not geoshape and displaytitle and mw.wikibase.getEntity() then
 
geoshape = mw.wikibase.getEntity().id
 
end
 
if geoshape then
 
jsonParams = {
 
jsonParams,
 
{
 
type = 'ExternalData',
 
service = 'geoshape',
 
ids = geoshape,
 
properties = {
 
['fill-opacity'] = 0.2
 
}
 
}
 
}
 
end
 
  
local maplink = mw.getCurrentFrame():extensionTag{
+
When used with no arguments, it takes its input from the parent
name = 'maplink',
+
frame.  Note, any values that do not evaluate to numbers are ignored.
content = mw.text.jsonEncode( jsonParams ),
+
]]
args = {
+
function z.max( frame )
text = displaycoords[1] .. ", " .. displaycoords[2],
+
    local args = frame.args;
zoom = zoom( extraparams ) or default_zoom,
+
   
latitude = decLat,
+
    if args[1] == nil then
longitude = decLong,
+
        local parent = frame:getParent();
}
+
        args = parent.args;
}
+
    end
+
    local max_value = nil;
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
+
   
local inlineText = displayinline and maplink or ''
+
    local i = 1;
local titleText = ''
+
    while args[i] ~= nil do
if displaytitle then
+
        local val = z._cleanNumber( frame, args[i] );
local htmlTitle = mw.html.create('span')
+
        if val ~= nil then
:attr{ id = 'coordinates' }
+
            if max_value == nil or val > max_value then
:addClass( displayinline and 'noprint' or nil )
+
                max_value = val;
:wikitext( maplink )
+
            end
local frame = mw.getCurrentFrame()
+
        end       
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
+
        i = i + 1;
end
+
    end
+
 
return inlineText .. titleText
+
    return max_value
 
end
 
end
  
-- dms specific funcions
+
--[[
 +
min
  
local function twoDigit( value )
+
Finds the minimum argument
if ( value < 10 ) then
 
value = '0' .. lang:formatNum( value )
 
else
 
value = lang:formatNum( value )
 
end
 
return value
 
end
 
  
function p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dms
+
Usage:
local str = ''
+
    {{#invoke:Math| min | value1 | value2 | ... }}
local direction = valuetable.direction
+
OR
local degrees, minutes, seconds = '', '', ''
+
    {{#invoke:Math| min }}
local dimension
 
  
if format == 'dms long' then
+
When used with no arguments, it takes its input from the parent
direction = i18n[direction .. 'long']
+
frame.  Note, any values that do not evaluate to numbers are ignored.
else
+
]]
direction = i18n[direction]
+
function z.min( frame )
end
+
    local args = frame.args;
degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees
+
   
+
    if args[1] == nil then
if valuetable.minutes then
+
        local parent = frame:getParent();
minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
+
        args = parent.args;
end
+
    end
if valuetable.seconds then
+
    local min_value = nil;
seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
+
   
end
+
    local i = 1;
return degrees .. minutes .. seconds .. direction
+
    while args[i] ~= nil do
 +
        local val = z._cleanNumber( frame, args[i] );
 +
        if val ~= nil then
 +
            if min_value == nil or val < min_value then
 +
                min_value = val;
 +
            end
 +
        end       
 +
        i = i + 1;
 +
    end
 +
 
 +
    return min_value
 
end
 
end
  
local function validdms(coordtable)
+
--[[
local direction = coordtable.direction
+
round
local degrees = coordtable.degrees or 0
 
local minutes = coordtable.minutes or 0
 
local seconds = coordtable.seconds or 0
 
local dimension = coordtable.dimension
 
if not dimension then
 
if direction == 'N' or direction == 'S' then
 
dimension = 'latitude'
 
elseif direction == 'E' or direction == 'W' then
 
dimension = 'longitude'
 
else
 
makeerror({message = i18n.invalidNSEW, sortkey = 'A'})
 
return false
 
end
 
end
 
  
if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
+
Rounds a number to specified precision
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
 
return false
 
end
 
 
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
 
makeerror({message = i18n.invalidNS, sortkey = 'A'})
 
return false
 
end
 
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
 
makeerror({message = i18n.invalidEW, sortkey = 'A'})
 
return false
 
end
 
 
if dimension == 'latitude' and degrees > 90 then
 
makeerror({message = i18n.latitude90, sortkey = 'A'})
 
return false
 
end
 
 
if dimension == 'longitude' and degrees > 360 then
 
makeerror({message = i18n.longitude360, sortkey = 'A'})
 
return false
 
end
 
 
if degrees < 0 or minutes < 0 or seconds < 0 then
 
makeerror({message = i18n.negativeCoode, sortkey = 'A'})
 
return false
 
end
 
 
if minutes > 60 or seconds > 60 then
 
makeerror({message = i18n.minSec60, sortkey = 'A'})
 
return false
 
end
 
if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
 
makeerror({message = i18n.dmIntergers, sortkey = 'A'})
 
return false
 
end
 
return true
 
end
 
  
local function builddmsdimension(degrees, minutes, seconds, direction, dimension)
+
Usage:
-- no error checking, done in function validdms
+
    {{#invoke:Math | round | value | precision }}
local dimensionobject = {}
+
   
+
--]]
-- direction and dimension (= latitude or longitude)
+
function z.round(frame)
dimensionobject.direction = direction
+
    local value, precision;
if dimension then
+
   
dimensionobject.dimension = dimension
+
    value = z._cleanNumber( frame, frame.args[1] or frame.args.value or 0 );
elseif direction == 'N' or direction == 'S' then
+
    precision = z._cleanNumber( frame, frame.args[2] or frame.args.precision or 0 );
dimensionobject.dimension = 'latitude'
+
   
elseif direction == 'E' or direction == 'W' then
+
    if value == nil or precision == nil then
dimensionobject.dimension = 'longitude'
+
        return '<strong class="error">Formatting error: Round input appears non-numeric</strong>'
end
+
    else
+
        return z._round( value, precision );
-- degrees, minutes, seconds
+
    end  
dimensionobject.degrees = tonumber(degrees)
 
dimensionobject.minutes = tonumber(minutes)
 
dimensionobject.seconds = tonumber(seconds)
 
if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' end
 
if minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' end
 
if seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' end
 
return dimensionobject
 
 
end
 
end
 
+
function z._round( value, precision )
function p._parsedmsstring(str, dimension) -- prend une séquence et donne des noms aux paramètres
+
    local rescale = math.pow( 10, precision );
-- output table: {latitude=, longitude = , direction =  }
+
    return math.floor( value * rescale + 0.5 ) / rescale;
if type( str ) ~= 'string' then
 
return nil
 
end
 
str = mw.ustring.gsub( mw.ustring.upper( str ), '%a+', coordParse )
 
if not tonumber( str ) and not str:find( '/' ) and str:find( '°' ) then
 
local str2 = mw.ustring.gsub( str, '[°″′\"\'\194\160 ]+', '/' )
 
-- avoid cases were there is degree ans seconds but no minutes
 
if not mw.ustring.find( str, '[″"]' ) or mw.ustring.find( str, '%d[′\'][ \194\160%d]' ) then
 
str = str2
 
end
 
end
 
if not tonumber(str) and not string.find(str, '/') then
 
makeerror({message = i18n.invalidFormat, sortkey= 'A'})
 
return nil
 
end
 
args = mw.text.split(str, '/', true)
 
if #args > 4 then
 
makeerror({message = i18n.tooManyParam, sortkey= 'A' })
 
end
 
local direction = mw.text.trim(args[#args])
 
table.remove(args)
 
local degrees, minutes, seconds = args[1], args[2], args[3]
 
local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction, dimension)
 
if validdms(dimensionobject) then
 
return dimensionobject
 
else
 
return nil
 
end
 
 
end
 
end
  
--- decimal specific functions
+
--[[
function p.displaydec(latitude, longitude, format)
+
precision_format
lat = lang:formatNum( latitude )
 
long = lang:formatNum( longitude )
 
 
if format == 'dec west' or  format == 'dec east' then
 
local symbolNS, symbolEW = i18n.N, i18n.E
 
if latitude < 0 then
 
symbolNS = i18n.S
 
lat = lat:sub( 2 )
 
end
 
if format == 'dec west' then
 
symbolEW = i18n.W
 
end
 
if longitude < 0 then
 
long = lang:formatNum( 360 + longitude )
 
end
 
 
return { lat .. i18n.degrees .. symbolNS,  long ..  i18n.degrees .. symbolEW }
 
 
else
 
return { lat, long }
 
end
 
end
 
  
 +
Rounds a number to the specified precision and formats according to rules
 +
originally used for {{template:Rnd}}.  Output is a string.
  
local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
+
Usage:
dec = mw.text.trim(dec)
+
    {{#invoke: Math | precision_format | number | precision }}
if not dec then
+
]]
return nil
+
function z.precision_format( frame )
end
+
    -- For access to Mediawiki built-in formatter.
if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
+
    local lang = mw.getContentLanguage();
makeerror({'invalid coord type', sortkey = "A"})
+
   
return nil
+
    local value_string, value, precision;
end
+
    value, value_string = z._cleanNumber( frame, frame.args[1] or 0 );
local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
+
    precision = z._cleanNumber( frame, frame.args[2] or 0 );
if not numdec then -- tries the decimal + direction format
+
   
dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
+
    -- Check for non-numeric input
local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
+
    if value == nil or precision == nil then
dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
+
        return '<strong class="error">Formatting error: invalid input when rounding</strong>'
if not dec or not tonumber(dec) then
+
    end
return nil
+
   
end
+
    local current_precision = z._precision( value );
if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
 
return dec
 
elseif direction == 'W' or direction == 'S' then
 
return '-' .. dec
 
else
 
if coordtype == 'latitude' then
 
makeerror({message = i18n.invalidNS, sortkey = 'A'})
 
else
 
makeerror({message = i18n.invalidEW, sortkey = 'A'})
 
end
 
return nil
 
end
 
end
 
  
if coordtype == 'latitude' and math.abs(numdec) > 90 then
+
    local order = z._order( value );
makeerror({message = i18n.latitude90 , sortkey = 'A'})
+
   
return nil
+
    -- Due to round-off effects it is neccesary to limit the returned precision under
end
+
    -- some circumstances because the terminal digits will be inaccurately reported.
if coordtype == 'longitude' and math.abs(numdec) > 360 then
+
    if order + precision >= 14 then
makeerror({message = i18n.longitude360 , sortkey = 'A'})
+
        local orig_precision = z._precision( value_string );
return nil
+
        if order + orig_precision >= 14 then
end
+
            precision = 13 - order;       
return dec
+
        end      
end
+
    end
  
-- dms/dec conversion functions
+
    -- If rounding off, truncate extra digits
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
+
    if precision < current_precision then
if precision >= 3 then
+
        value = z._round( value, precision );
return 'dms'
+
        current_precision = z._precision( value );
elseif precision >=1 then
+
    end   
return 'dm'
+
   
else
+
    local formatted_num = lang:formatNum( math.abs(value) );
return 'd'
+
    local sign;
end
+
   
end
+
    -- Use proper unary minus sign rather than ASCII default
 +
    if value < 0 then
 +
        sign = '−';
 +
    else
 +
        sign = '';
 +
    end   
 +
       
 +
    -- Handle cases requiring scientific notation
 +
    if string.find( formatted_num, 'E', 1, true ) ~= nil or math.abs(order) >= 9 then
 +
        value = value * math.pow( 10, -order );
 +
        current_precision = current_precision + order;
 +
        precision = precision + order;
 +
        formatted_num = lang:formatNum( math.abs(value) );
 +
    else
 +
        order = 0;       
 +
    end
 +
    formatted_num = sign .. formatted_num;
 +
   
 +
    -- Pad with zeros, if needed   
 +
    if current_precision < precision then
 +
        local padding;
 +
        if current_precision <= 0 then
 +
            if precision > 0 then
 +
                local zero_sep = lang:formatNum( 1.1 );
 +
                formatted_num = formatted_num .. zero_sep:sub(2,2);
  
local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
+
                padding = precision;
local precision = 0
+
                if padding > 20 then
for d, val in ipairs(decs) do
+
                    padding = 20;
precision = math.max(precision, math_mod._precision(val))
+
                end
end
+
               
return convertprecision(precision)
+
                formatted_num = formatted_num .. string.rep( '0', padding );
end
+
            end           
 +
        else                 
 +
            padding = precision - current_precision
 +
            if padding > 20 then
 +
                padding = 20;
 +
            end
 +
            formatted_num = formatted_num .. string.rep( '0', padding );
 +
        end
 +
    end
  
local function dec2dms_d(dec)
+
    -- Add exponential notation, if necessary.
local degrees = math_mod._round( dec, 0 )
+
    if order ~= 0 then
return degrees
+
        -- Use proper unary minus sign rather than ASCII default
 +
        if order < 0 then
 +
            order = '−' .. lang:formatNum( math.abs(order) );
 +
        else
 +
            order = lang:formatNum( order );
 +
        end   
 +
       
 +
        formatted_num = formatted_num .. '<span style="margin:0 .15em 0 .25em">×</span>10<sup>' .. order .. '</sup>'
 +
    end
 +
   
 +
    return formatted_num;
 
end
 
end
  
local function dec2dms_dm(dec)
+
--[[
dec = math_mod._round( dec * 60, 0 )
+
Helper function that interprets the input numerically.  If the
local minutes = dec % 60
+
input does not appear to be a number, attempts evaluating it as
dec = math.floor( (dec - minutes) / 60 )
+
a parser functions expression.
local degrees = dec % 360
+
]]
return degrees, minutes
 
end
 
  
local function dec2dms_dms(dec)
+
function z._cleanNumber( frame, number_string )
dec = math_mod._round( dec * 60 * 60, 0 )
+
    if number_string == nil or number_string:len() == 0 then
local seconds = dec % 60
+
        return nil, nil;
dec = math.floor( (dec - seconds) / 60 )
+
    end   
local minutes = dec % 60
+
   
dec = math.floor( (dec - minutes) / 60 )
+
    -- Attempt basic conversion
local degrees = dec % 360
+
    local number = tonumber( number_string )
return degrees, minutes, seconds
+
   
 +
    -- If failed, attempt to evaluate input as an expression
 +
    if number == nil then       
 +
        local attempt = frame:preprocess( '{{#expr: ' .. number_string .. '}}' );
 +
        attempt = tonumber( attempt );
 +
        if attempt ~= nil then
 +
            number = attempt;
 +
            number_string = tostring( number );
 +
        else
 +
            number = nil;
 +
            number_string = nil;
 +
        end
 +
    else
 +
    -- String is valid but may contain padding, clean it.
 +
        number_string = number_string:match( "^%s*(.-)%s*$" );
 +
    end
 +
   
 +
    return number, number_string;
 
end
 
end
  
function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
+
return z
local degrees, minutes, seconds
 
 
-- vérification du globe
 
if not ( globe and globedata[ globe ] ) then
 
globe = 'earth'
 
end
 
 
-- precision
 
if not precision or precision == '' then
 
precision = determinedmsprec({dec})
 
end
 
if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
 
return makeerror({sortkey = 'C'})
 
end
 
local dec = tonumber(dec)
 
 
-- direction
 
local direction
 
if coordtype == 'latitude' then
 
if dec < 0 then
 
direction = 'S'
 
else
 
direction = 'N'
 
end
 
elseif coordtype == 'longitude' then
 
if dec < 0 or globedata[globe].defaultdisplay == 'dec west' then
 
direction = 'W'
 
else
 
direction = 'E'
 
end
 
end
 
 
-- conversion
 
dec = math.abs(dec) -- les coordonnées en dms sont toujours positives
 
if precision == 'dms' then
 
degrees, minutes, seconds = dec2dms_dms(dec)
 
elseif precision == 'dm' then
 
degrees, minutes = dec2dms_dm(dec)
 
else
 
degrees = dec2dms_d(dec)
 
end
 
return builddmsdimension(degrees, minutes, seconds, direction)
 
end
 
 
 
function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
 
args = frame.args
 
local dec = args[1]
 
if not tonumber(dec) then
 
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
 
return showerrors()
 
end
 
local dirpositive = string.lower(args[2] or '')
 
local dirnegative = string.lower(args[3] or '')
 
local precision = string.lower(args[4] or '')
 
local displayformat, coordtype
 
 
if dirpositive == 'n' or dirpositive == 'nord' then
 
coordtype = 'latitude'
 
else
 
coordtype = 'longitude'
 
end
 
if dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' then
 
displayformat = 'dms long'
 
end
 
local coordobject = p._dec2dms(dec, coordtype, precision)
 
if coordobject then
 
return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
 
else
 
return showerrors()
 
end
 
end
 
 
 
 
 
function p._dms2dec(dmsobject) -- transforme une table degré minute secondes en nombre décimal
 
local direction, degrees, minutes, seconds = dmsobject.direction, dmsobject.degrees, dmsobject.minutes, dmsobject.seconds
 
local factor = 0
 
local precision = 0
 
if not minutes then minutes = 0 end
 
if not seconds then seconds = 0 end
 
 
if direction == "N" or direction == "E" then
 
factor = 1
 
elseif direction == "W" or direction == "S" then
 
factor = -1
 
elseif not direction then
 
makeerror({message = i18n.noCardinalDirection, sortkey = 'A'})
 
return nil
 
else
 
makeerror({message = i18n.invalidDirection, sortkey = 'A'})
 
return nil
 
end
 
 
if dmsobject.seconds then -- vérifie la précision des données initiales
 
precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?
 
elseif dmsobject.minutes then
 
precision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )
 
else
 
precision = math.max( math_mod._precision(tostring(degrees), 0 ) )
 
end
 
 
local decimal = factor * (degrees+(minutes+seconds/60)/60)
 
return math_mod._round(decimal, precision)
 
end
 
 
 
function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax
 
local args = frame.args
 
if tonumber(args[1]) then
 
return args[1] -- coordonnées déjà en décimal
 
elseif not args[2] then
 
local dmsobject = p._parsedmsstring(args[1])
 
if dmsobject then
 
return p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/N
 
else
 
return showerrors()
 
end
 
else
 
return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})
 
end
 
end
 
 
 
-- Wikidata
 
local function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"
 
if precision < 0.016 then
 
return 'dms'
 
elseif precision < 1 then
 
return 'dm'
 
else
 
return 'd'
 
end
 
end
 
 
 
local function wikidatacoords(query)
 
query = query or {property = 'p625'}
 
query.formatting = 'raw'
 
local wd = require('Module:Wikidata')
 
local claim = wd.getClaims(query)
 
if claim and claim[1] then -- redundant but more robust in case of a change in the code of Module:Wikidata
 
local coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values
 
-- Wikidata does not handle correctly +West longitudes
 
if globedata[ coords.globe ] and globedata[ coords.globe ].defaultdisplay == 'dec west' then
 
coords.longitude = math.abs( coords.longitude )
 
end
 
return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001)
 
end
 
return nil
 
end
 
 
 
 
 
local function wikidatacat(globe)
 
--catbase= Article géolocalisé sur Terre
 
local entitycat = mw.wikibase.getEntity()
 
 
local basecat = 'Article géolocalisé'
 
local finalcat = {}
 
--BADGES
 
if entitycat then
 
--BADGES
 
  for i, badgeId in ipairs( entitycat.sitelinks['frwiki'].badges ) do
 
if badgeId == 'Q17437796'  then
 
basecat=string.gsub(basecat, "Article géolocalisé", "Article de qualité géolocalisé")
 
end
 
if badgeId == 'Q17437798'  then
 
basecat=string.gsub(basecat, "Article géolocalisé", "Bon article géolocalisé")
 
end
 
end
 
end
 
 
 
if globe == 'earth'  then
 
if entitycat and entitycat.claims  then
 
local country=entitycat.claims['P17']
 
if not country then
 
--pas pays à récupérer
 
basecat=basecat .. ' sur Terre'
 
table.insert(finalcat,basecat)
 
else
 
--parfois plusieurs pays
 
for i, paysId in ipairs( country ) do
 
--on fait confiance au label wikidata
 
local gdataone,qid
 
 
if paysId.mainsnak.snaktype == 'value' then
 
qid=paysId.mainsnak.datavalue.value['numeric-id']
 
gdataone=gdata.data[qid]
 
else
 
--Bir Tawil n'a pas de pays connu
 
qid='?'
 
end
 
if gdataone ~= nil then
 
local prep=genre[gdataone['genre']]['en'] or 'en '
 
local thecat=basecat .. ' '..prep ..mw.wikibase.label( 'Q'.. qid)
 
if mw.title.new('category:'..thecat).exists then
 
table.insert(finalcat,thecat)
 
else
 
--Dommage!
 
mw.log(thecat .. ' à créer')
 
end
 
else
 
--pas d'id?
 
mw.log(qid .. ' à paramétrer')
 
end
 
end
 
if #finalcat == 0 then
 
--pas pays à récupérer
 
basecat=basecat .. ' sur Terre'
 
table.insert(finalcat,basecat)
 
end
 
end
 
else
 
--pas wikidata
 
basecat=basecat .. ' sur Terre'
 
table.insert(finalcat,basecat)
 
end
 
elseif globedata[globe] then
 
basecat=basecat .. ' ' .. globedata[globe].trackingcat
 
table.insert(finalcat,basecat)
 
else
 
basecat=basecat .. ' extraterrestre'
 
table.insert(finalcat,basecat)
 
end
 
return finalcat
 
end
 
 
 
-- main function for displaying coordinates
 
function p._coord(args)
 
 
 
-- I declare variable
 
local displayformat = args.format -- string: one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west'
 
local displayplace = string.lower(args.display or 'inline') --string: one of 'inline', 'title' or 'inline,title'
 
local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack
 
local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates
 
local wikidata = args.wikidata -- string: set to "true" if needed
 
local wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoords
 
local dmslatitude, dmslongitude -- table (when created)
 
local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams)
 
local trackingstring = '' -- tracking cats except error cats (already in errorstring)
 
local rawlat, rawlong = args.latitude, args.longitude
 
if rawlat == '' then rawlat = nil end
 
if rawlong == '' then rawlong = nil end
 
local globe = string.lower( args.globe or extraparams:match('globe:(%a+)') or '' ) -- string: see the globedata table for accepted values
 
local latitude, longitude, precision, dmslatitude, dmslongitude -- latitude and longitude in decimal / dmslatitude and dmslongitude: tables withdms coords
 
local maplink = true -- use maplink whenever it is possible
 
 
-- II extract coordinates from Wikitext
 
if (rawlat or rawlong) then
 
if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude
 
makeerror({message = i18n.coordMissing, sortkey = 'A'})
 
return showerrors()
 
end
 
latitude = parsedec(rawlat, 'latitude', globe)
 
 
 
if latitude then -- if latitude is decimal
 
longitude = parsedec(rawlong, 'longitude', globe) -- so should be longitude
 
precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros
 
if not latitude or not longitude then
 
if errorstring == '' then
 
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
 
end
 
return showerrors()
 
end
 
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
 
latitude, longitude = tonumber(latitude), tonumber(longitude)
 
else -- if latitude is not decimal try to parse it as a dms string
 
dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude')
 
if not dmslatitude or not dmslongitude then
 
return showerrors()
 
end
 
latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)
 
end
 
end
 
 
 
-- III extract coordinate data from Wikidata and compare them to local data
 
local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
 
if wikidata == 'true' then
 
wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery)
 
 
if wikidatalatitude and latitude and longitude then
 
local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
 
if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) <  maxdistance then
 
trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
 
else
 
trackingstring = trackingstring .. makecat(i18n.notaswikidata)
 
end
 
end
 
if wikidatalatitude and not latitude then
 
latitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
 
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
 
trackingstring = trackingstring .. makecat(i18n.throughwikidata)
 
end
 
 
if latitude and not wikidatalatitude then
 
if mw.title.getCurrentTitle().namespace == 0 then
 
trackingstring = trackingstring .. makecat(i18n.nowikidata)
 
end
 
end
 
end
 
 
 
 
 
-- exit if stil no latitude or no longitude
 
if not latitude and not longitude then
 
return nil -- ne rien ajouter ici pour que l'appel à cette fonction retourne bien nil en l'absence de données
 
end
 
 
 
-- IV best guesses for missing parameters
 
 
--- globe
 
if globe == '' then
 
globe = 'earth'
 
end
 
if not globedata[globe] then
 
makeerror({message = i18n.invalidGlobe .. globe})
 
globe = 'earth'
 
end
 
if globe ~= 'earth' then
 
extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
 
maplink = false
 
end
 
 
--- diplayformat
 
if not displayformat or displayformat == '' then
 
displayformat = globedata[globe].defaultdisplay
 
end
 
 
-- displayinline/displaytitle
 
local displayinline =  string.find(displayplace, 'inline')
 
local displaytitle = string.find(displayplace, 'title')
 
if not displayinline and not displaytitle then
 
displayinline = true
 
if displayplace ~= '' then
 
makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue
 
end
 
end
 
if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
 
--local cattoappend=globedata[globe].trackingcat
 
--Récupération des badges
 
local cats=wikidatacat(globe)
 
for i, cat in ipairs( cats ) do
 
trackingstring = trackingstring .. makecat(cat)
 
end
 
 
end
 
 
-- V geodata
 
local geodata = ''
 
if latitude and longitude then
 
local latstring, longstring = tostring(latitude), tostring(longitude)
 
local primary = ''
 
 
 
local frame = mw.getCurrentFrame()
 
local geodataparams = {[1] = latstring, [2] = longstring, [3] = extraparams }
 
if displaytitle then
 
geodataparams[4] = 'primary'
 
end
 
if objectname then
 
geodataparams.name = objectname
 
end
 
geodata = frame:callParserFunction('#coordinates', geodataparams )
 
if string.find(geodata, 'error') then -- the only error that has not been caught yet is primary key
 
geodata = ''
 
makeerror({sortkey='D'})
 
end
 
end
 
-- VI final output
 
local mainstring = ''
 
if maplink then
 
mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
 
else
 
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
 
end
 
 
return mainstring .. notes .. trackingstring .. geodata .. showerrors()
 
end
 
 
 
function p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coord
 
local args = frame.args
 
local numericargs = {}
 
for i, j in ipairs(args) do
 
args[i] = mw.text.trim(j)
 
if type(i) == 'number' and args[i] ~= '' then
 
table.insert(numericargs, args[i])
 
end
 
end
 
 
 
if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
 
args.extraparams = numericargs[#numericargs]
 
if #numericargs == 1 and tonumber(numericargs[1]) then
 
makeerror({message = i18n.coordMissing, sortkey = 'A'})
 
return showerrors()
 
end
 
table.remove(numericargs)
 
end
 
for i, j in ipairs(numericargs) do
 
if i <= (#numericargs / 2) then
 
if not args.latitude then
 
args.latitude = j
 
else
 
args.latitude = args.latitude .. '/' .. j
 
end
 
else
 
if not args.longitude then
 
args.longitude = j
 
else
 
args.longitude = args.longitude .. '/' .. j
 
end
 
end
 
end
 
 
 
if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
 
args.latitude, args.longitude = args.longitude, args.latitude
 
end
 
return p._coord(args)
 
end
 
 
 
function p.Coord(frame)
 
return p.coord(frame)
 
end
 
 
 
function p.latitude(frame) -- helper function pour infobox, à déprécier
 
local args = frame.args
 
local latitude  = frame.args[1]
 
if latitude and mw.text.trim(latitude) ~= '' then
 
return latitude
 
elseif frame.args['wikidata'] == 'true' then
 
local lat, long = wikidatacoords()
 
return lat
 
end
 
end
 
function p.longitude(frame) -- helper function pour infobox, à déprécier
 
local args = frame.args
 
local longitude = frame.args[1]
 
if longitude and mw.text.trim(longitude) ~= '' then
 
return longitude
 
elseif frame.args['wikidata'] == 'true' then
 
local lat, long = wikidatacoords()
 
return long
 
end
 
end
 
 
 
 
 
return p
 

Version actuelle datée du 28 décembre 2020 à 11:45

La documentation pour ce module peut être créée à Module:Math/doc

--[[

This module provides a number of basic mathematical operations.

]]
local z = {}

-- Generate random number
function z.random( frame )
    local first = tonumber(frame.args[1]) -- if it doesn't exist it's NaN, if not a number it's nil
    local second = tonumber(frame.args[2])

    if first then -- if NaN or nil, will skip down to final return
        if first <= second then -- could match if both nil, but already checked that first is a number in last line
            return math.random(first, second)
        end
        return math.random(first)
    end   
    return math.random()
end

--[[
order

Determine order of magnitude of a number

Usage:
    {{#invoke: Math | order | value }}
]]
function z.order(frame)
    local input_string = (frame.args[1] or frame.args.x or '0');
    local input_number;
    
    input_number = z._cleanNumber( frame, input_string );
    if input_number == nil then
        return '<strong class="error">Formatting error: Order of magnitude input appears non-numeric</strong>'
    else
        return z._order( input_number )
    end    
end
function z._order(x)
    if x == 0 then return 0 end
    return math.floor(math.log10(math.abs(x)))
end

--[[
precision

Detemines the precision of a number using the string representation

Usage:
    {{ #invoke: Math | precision | value }}
]]
function z.precision( frame )
    local input_string = (frame.args[1] or frame.args.x or '0');
    local trap_fraction = frame.args.check_fraction or false;
    local input_number;
    
    if type( trap_fraction ) == 'string' then
        trap_fraction = trap_fraction:lower();
        if trap_fraction == 'false' or trap_fraction == '0' or
                trap_fraction == 'no' or trap_fraction == '' then
            trap_fraction = false;
        else
            trap_fraction = true;
        end
    end
    
    if trap_fraction then
        local pos = string.find( input_string, '/', 1, true );
        if pos ~= nil then
            if string.find( input_string, '/', pos + 1, true ) == nil then
                local denominator = string.sub( input_string, pos+1, -1 );
                local denom_value = tonumber( denominator );
                if denom_value ~= nil then
                    return math.log10(denom_value);
                end
            end                        
        end
    end    
    
    input_number, input_string = z._cleanNumber( frame, input_string );
    if input_string == nil then
        return '<strong class="error">Formatting error: Precision input appears non-numeric</strong>'
    else
        return z._precision( input_string )
    end    
end
function z._precision( x )    
    x = string.upper( x )

    local decimal = string.find( x, '.', 1, true )
    local exponent_pos = string.find( x, 'E', 1, true )
    local result = 0;
    
    if exponent_pos ~= nil then
        local exponent = string.sub( x, exponent_pos + 1 )
        x = string.sub( x, 1, exponent_pos - 1 )
        result = result - tonumber( exponent )
    end    
    
    if decimal ~= nil then
        result = result + string.len( x ) - decimal
        return result
    end
        
    local pos = string.len( x );
    while x:byte(pos) == string.byte('0') do
        pos = pos - 1
        result = result - 1
        if pos <= 0 then
            return 0
        end
    end
    
    return result
end

--[[
max

Finds the maximum argument

Usage:
    {{#invoke:Math| max | value1 | value2 | ... }}
OR
    {{#invoke:Math| max }}

When used with no arguments, it takes its input from the parent
frame.  Note, any values that do not evaluate to numbers are ignored.
]]
function z.max( frame )
    local args = frame.args;
    
    if args[1] == nil then
        local parent = frame:getParent();
        args = parent.args;
    end
    local max_value = nil;
    
    local i = 1;
    while args[i] ~= nil do
        local val = z._cleanNumber( frame, args[i] );
        if val ~= nil then
            if max_value == nil or val > max_value then
                max_value = val;
            end
        end        
        i = i + 1;
    end
  
    return max_value
end

--[[
min 

Finds the minimum argument

Usage:
    {{#invoke:Math| min | value1 | value2 | ... }}
OR
    {{#invoke:Math| min }}

When used with no arguments, it takes its input from the parent
frame.  Note, any values that do not evaluate to numbers are ignored.
]]
function z.min( frame )
    local args = frame.args;
    
    if args[1] == nil then
        local parent = frame:getParent();
        args = parent.args;
    end
    local min_value = nil;
    
    local i = 1;
    while args[i] ~= nil do
        local val = z._cleanNumber( frame, args[i] );
        if val ~= nil then
            if min_value == nil or val < min_value then
                min_value = val;
            end
        end        
        i = i + 1;
    end
  
    return min_value
end

--[[
round

Rounds a number to specified precision

Usage:
    {{#invoke:Math | round | value | precision }}
    
--]]
function z.round(frame)
    local value, precision;
    
    value = z._cleanNumber( frame, frame.args[1] or frame.args.value or 0 );
    precision = z._cleanNumber( frame, frame.args[2] or frame.args.precision or 0 );
    
    if value == nil or precision == nil then
        return '<strong class="error">Formatting error: Round input appears non-numeric</strong>'
    else
        return z._round( value, precision );
    end    
end
function z._round( value, precision )
    local rescale = math.pow( 10, precision );
    return math.floor( value * rescale + 0.5 ) / rescale;
end

--[[
precision_format

Rounds a number to the specified precision and formats according to rules 
originally used for {{template:Rnd}}.  Output is a string.

Usage:
    {{#invoke: Math | precision_format | number | precision }}
]]
function z.precision_format( frame )
    -- For access to Mediawiki built-in formatter.
    local lang = mw.getContentLanguage();
    
    local value_string, value, precision;
    value, value_string = z._cleanNumber( frame, frame.args[1] or 0 );
    precision = z._cleanNumber( frame, frame.args[2] or 0 );
    
    -- Check for non-numeric input
    if value == nil or precision == nil then
        return '<strong class="error">Formatting error: invalid input when rounding</strong>'
    end
    
    local current_precision = z._precision( value );

    local order = z._order( value );
    
    -- Due to round-off effects it is neccesary to limit the returned precision under
    -- some circumstances because the terminal digits will be inaccurately reported.
    if order + precision >= 14 then
        local orig_precision = z._precision( value_string );
        if order + orig_precision >= 14 then
            precision = 13 - order;        
        end        
    end

    -- If rounding off, truncate extra digits
    if precision < current_precision then
        value = z._round( value, precision );
        current_precision = z._precision( value );
    end    
    
    local formatted_num = lang:formatNum( math.abs(value) );
    local sign;
    
    -- Use proper unary minus sign rather than ASCII default
    if value < 0 then
        sign = '−';
    else
        sign = '';
    end    
        
    -- Handle cases requiring scientific notation
    if string.find( formatted_num, 'E', 1, true ) ~= nil or math.abs(order) >= 9 then
        value = value * math.pow( 10, -order );
        current_precision = current_precision + order;
        precision = precision + order;
        formatted_num = lang:formatNum( math.abs(value) );
    else
        order = 0;        
    end
    formatted_num = sign .. formatted_num;
    
    -- Pad with zeros, if needed    
    if current_precision < precision then
        local padding;
        if current_precision <= 0 then
            if precision > 0 then
                local zero_sep = lang:formatNum( 1.1 );
                formatted_num = formatted_num .. zero_sep:sub(2,2);

                padding = precision;
                if padding > 20 then
                    padding = 20;
                end
                
                formatted_num = formatted_num .. string.rep( '0', padding );
            end            
        else                   
            padding = precision - current_precision
            if padding > 20 then
                padding = 20;
            end
            formatted_num = formatted_num .. string.rep( '0', padding );
        end
    end

    -- Add exponential notation, if necessary.
    if order ~= 0 then
        -- Use proper unary minus sign rather than ASCII default
        if order < 0 then
            order = '−' .. lang:formatNum( math.abs(order) );
        else
            order = lang:formatNum( order );
        end    
        
        formatted_num = formatted_num .. '<span style="margin:0 .15em 0 .25em">×</span>10<sup>' .. order .. '</sup>'
    end
    
    return formatted_num;
end

--[[
Helper function that interprets the input numerically.  If the 
input does not appear to be a number, attempts evaluating it as
a parser functions expression.
]]

function z._cleanNumber( frame, number_string )
    if number_string == nil or number_string:len() == 0 then
        return nil, nil;
    end    
    
    -- Attempt basic conversion
    local number = tonumber( number_string )
    
    -- If failed, attempt to evaluate input as an expression
    if number == nil then        
        local attempt = frame:preprocess( '{{#expr: ' .. number_string .. '}}' );
        attempt = tonumber( attempt );
        if attempt ~= nil then
            number = attempt;
            number_string = tostring( number );
        else
            number = nil;
            number_string = nil;
        end
    else
    -- String is valid but may contain padding, clean it.
        number_string = number_string:match( "^%s*(.-)%s*$" );
    end
    
    return number, number_string;
end

return z