Aller au contenu

« Module:Coordinates » : différence entre les versions

De Wreck
Modèle:Infobox>FDo64
Nouvelle version de Zolo
m 153 versions importées
 
(68 versions intermédiaires par 11 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
local math_mod = require( "Module:Math" )
local math_mod = require( "Module:Math" )
local p = {}
local p = {}
local NBSP = '\194\160' -- espace insécable (code UTF-8 sur deux octets)


local i18n = {
local i18n = {
N = ' N',
N = 'N',
Nlong = ' Nord',
Nlong = 'nord',
W = ' O',
W = 'O',
Wlong = ' Ouest',
Wlong = 'ouest',
E = ' E',
E = 'E',
Elong = ' Est',
Elong = 'est',
S = ' S',
S = 'S',
Slong = ' Sud',
Slong = 'sud',
degrees = '° ',
degrees = '°' .. NBSP,
minutes = '\′ ',
minutes = '′' .. NBSP,
seconds = '″ ',
seconds = '″' .. NBSP,
geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr',
geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr',
tooltip = 'Cartes, vues aériennes et autres données pour cet endroit',
tooltip = 'Cartes, vues aériennes, etc.',
errorcat = 'Pages avec des balises de coordonnées mal formées',  
errorcat = 'Page avec des balises de coordonnées mal formées',
sameaswikidata = 'Page avec coordonnées similaires sur Wikidata',
sameaswikidata = 'Page avec coordonnées similaires sur Wikidata',
notaswikidata = 'Page avec coordonnées différentes sur Wikidata',
notaswikidata = 'Page avec coordonnées différentes sur Wikidata',
nowikidata = 'Page sans coordonnées Wikidata',
throughwikidata = 'Page géolocalisée par 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' .. NBSP .. ': ',                            -- 'invalid globe:',
}
local coordParse = {
NORTH = 'N',
NORD = 'N',
EAST = 'E',
EST = 'E',
WEST = 'W',
O = 'W',
OUEST = 'W',
SOUTH = 'S',
SUD = 'S',
}
}


local globedata = {
local globedata = {
--[[ notes:
--[[ notes:
radius in kilometers
radius in kilometers (especially imprecise for non spheric bodies)
defaultdisplay is currently disabled, activate it ?
defaultdisplay is currently disabled, activate it ?
]]--
]]--
earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = 'Article géolocalisé sur Terre'},
ariel =  {radius = 580, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
jupiter =  {radius = 68911, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
callisto =  {radius = 2410, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
mars =  {radius = 3389.5, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé sur Mars' },
ceres =  {radius = 470, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
mercury =  {radius = 2439.7, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
charon =  {radius = 1214, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
moon =  {radius = 1736, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé sur la Lune'},
deimos =  {radius = 7, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
neptune =  {radius = 24553, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
dione =  {radius = 560, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
saturn =  {radius = 58232, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
enceladus =  {radius = 255, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
sun =  {radius = 696342, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
ganymede =  {radius = 2634, defaultdisplay = 'dec west', trackingcat = 'sur Ganymède'},
titan =  {radius = 2575.5, defaultdisplay = 'dec west', trackingcat = 'Article géolocalisé extraterrestre'},
earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = 'sur Terre'},
triton = {radius = 1353, defaultdisplay = 'dec west', trackingcat = 'Article géolocalisé extraterrestre'},
europa =  {radius = 1561, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'},
uranus =  {radius = 25266, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
hyperion =  {radius = 140, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'},
venus =  {radius = 6051.8, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé 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 ce seuil (en kilomètres), une catégorie de maintenance est ajoutée
local lang = mw.language.getContentLanguage()
local default_zoom = 13


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
local function makecat(cat, sortkey)
if type( sortkey ) == 'string' then
return '[[Category:' .. cat .. '|' .. sortkey .. ']]'
else
return '[[Category:' .. cat .. ']]'
end
end


----------------------------------------
----------------------------------------
Ligne 54 : Ligne 116 :
* B: invalid globe
* B: invalid globe
* C: something wrong with other parameters
* C: something wrong with other parameters
* D: more than one primary coord
]]--
]]--


errorstring = ''
local errorstring = ''


function makeerror(args)
local function makeerror(args)
local errormessage = ''
local errormessage = ''
if args.message then  
if args.message then
errormessage = '<strong class="error">Coordonnées: ' .. args.message .. '</strong>'  
errormessage = '<strong class="error"> Coordonnées' .. NBSP .. ': ' .. args.message .. '</strong>'
end
end
local errorcat = ''
local errorcat = ''
if mw.title.getCurrentTitle().namespace == 0 then  
if mw.title.getCurrentTitle().namespace == 0 then
errorcat = makecat(i18n.errorcat, args.sortkey)
errorcat = makecat(i18n.errorcat, args.sortkey)
end
end
Ligne 71 : Ligne 134 :
end
end


function showerrors()
local function showerrors()
return errorstring
return errorstring
end
end
-------------------------------------------
function makecat(cat, sortkey)
return '[[Category:' .. cat .. '|' .. (sortkey or '*') .. ']]'
end


----------------------------------------
 
 
-- Distance computation
-- Distance computation
function p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globe
function p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globe


globe = globe or 'earth'
globe = string.lower(globe or 'earth')
 
-- check arguments and converts degreees to radians
-- check arguments and converts degreees to radians
local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
if not latA or not latB or not longA or not longB then return
if (not latA) or (not latB) or (not longA) or (not longB) then return
error('coordinates missing, can\'t compute distance')
error('coordinates missing, can\'t compute distance')
end
end
Ligne 93 : Ligne 153 :
error('coordinates are not numeric, can\'t compute distance')
error('coordinates are not numeric, can\'t compute distance')
end
end
if not globe or not globedata[globe] then
-- distance angulaire en radians
return error('globe: ' .. globe .. 'is not supported')
end
 
-- calcul de la distance angulaire en radians
local convratio = math.pi / 180 -- convertit en radians
local convratio = math.pi / 180 -- convertit en radians
latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
local angle = math.acos(math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA))
local cosangle = math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA)
 
if cosangle >= 1 then -- may be above one because of rounding errors
-- convertit en km en fonction du globe
return 0
if not globe or not globedata[globe] then
return error('globe: ' .. globe .. 'is not supported')
end
end
 
local angle = math.acos(cosangle)
radius = globedata[globe].radius
-- calcul de la distance en km
local radius = globedata[globe].radius
return radius * angle
return radius * angle
end
end
Ligne 112 : Ligne 173 :
local args = frame.args
local args = frame.args
return p._distance(
return p._distance(
{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},  
{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},
{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
args.globe)
args.globe)
end
end


 
local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
------------------------
--HTML builder for a geohack link
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
local htmlBuilder = require("Module:HtmlBuilder")
    local root, text, url, noprint
extraparams = extraparams or ''
extraparams = extraparams or ''
    -- geohack url e parametri
local geohacklatitude, geohacklongitude
    local decimalcoords = p.displaydec(decLat, decLong, displayformat)
-- format latitude and longitude for the URL
    local geohacklatitude, geohacklongitude
if tonumber(decLat) < 0 then
 
geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
    -- format latitude and longitude for the URL
else
    if tonumber(decLat) < 0 then
geohacklatitude = decLat .. '_N'
    geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
    else  
    geohacklatitude = decLat .. '_N'
end
end
if tonumber(decLong) < 0 then
if tonumber(decLong) < 0 then
geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
    else  
elseif globedata[globe].defaultdisplay == 'dec west' then
    geohacklongitude = decLong .. '_E'
geohacklongitude = decLong .. '_W'
else
geohacklongitude = decLong .. '_E'
end
end
-- prepares the 'paramss=' parameter
-- prepares the 'paramss=' parameter
local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
-- concatenate parameteres for geohack
-- concatenate parameteres for geohack
local url = i18n.geohackurl ..  
return i18n.geohackurl ..
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
"&params=" .. geohackparams ..
"&params=" .. geohackparams ..
(objectname and ("&title=" .. objectname) or "")
(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
end


    root = htmlBuilder.create()
--HTML builder for a geohack link
    root
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
        .tag("span")
-- geohack url
            .addClass("plainlinks nourlexpansion")
local url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
            .wikitext("[" .. url)
            .tag("span")
                .addClass(string.sub(displayformat,1,3) == "dec" and "geo-nondefault" or "geo-default")
                .tag("span")
                    .addClass("geo-dms")
                    .attr("title", i18n.tooltip)
                    .tag("span")
                        .addClass("latitude")
                        .wikitext(p.displaydmsdimension(dmsLat, displayformat))
                        .done()
                    .wikitext(" ")
                    .tag("span")
                        .addClass("longitude")
                        .wikitext(p.displaydmsdimension(dmsLong, displayformat))
                        .done()
                    .done()
                .done()
            .tag("span")
                .addClass("geo-multi-punct")
                .wikitext("&#xfeff; / &#xfeff;")
                .done()
            .tag("span")
                .addClass(string.sub(displayformat,1,3) == 'dec' and "geo-default" or "geo-nondefault")
                .wikitext(objectname and "<span class=\"vcard\">" or "")
                .tag("span")
                    .addClass("geo-dec")
                    .attr("title", i18n.tooltip)
                    .wikitext(decimalcoords)
                    .done()
                .wikitext(objectname and ("<span style=\"display:none\"> (<span class=\"fn org\">" ..
                          objectname .. "</span>)</span></span>") or "")
                .done()
            .wikitext("]")
            .done()


    -- formatta il risultato a seconda di args["display"] (nil, "inline", "title", "inline,title")
-- displayed coordinates
    text = tostring(root)
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


    noprint = displayinline and "class=\"noprint\" " or ""
-- build coordinate in h-geo / h-card microformat
    htmlTitle = "<span style=\"font-size: small;\"><span " .. noprint .. "id=\"coordinates\">"
local globeNode
if globe and globe ~= 'earth' then
globeNode = mw.html.create('data')
:addClass('p-globe')
:attr{ value = globe }
:done()
end


    return (displayinline and text or "") ..
local coordNode = mw.html.create('')
          (displaytitle and (htmlTitle .. text .. "</span></span>") or "")
if objectname then
end
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()


-- Fonctions de manipualation/coonversion des coordonnées
-- 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


function p._dec2dms(dec, coordtype, precision) -- type: latitude or longitude
return inlineText .. titleText
local dec = tonumber(dec)
end
local dms = {}


-- precision
local function zoom( extraparams )
if precision and precision ~= '' and precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )
return makeerror({sortkey = 'C'})
if zoomParam then
return zoomParam
end
end
if not precision then
 
precision = math_mod._precision(dec)
local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
if precision <= 0 then
if scale then
precision = 'd'
return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25)
elseif precision <= 2 then
precision = 'dm';
else
precision = 'dms';
end
end  -- et fractions de secondes ?
if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
makeerror({message = 'invalid precision in function dec2dms', sortkey = 'C'})  
end
end
-- direction
 
if coordtype == 'latitude' then
local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
if dec < 0 then
if extraType then
direction = 'S'
local zoomType = {
else
country = 5,
direction = 'N'
state = 6,
end
adm1st = 7,
elseif coordtype == 'longitude' then
adm2nd = 8,
if dec < 0 then
city = 9,
direction = 'W'
isle = 10,
else
mountain = 10,
direction = 'E'
waterbody = 10,
end
airport = 12,
end
landmark = 13,
}
-- conversion
return zoomType[ extraType ]
dec = math.abs(dec) -- les coordonnées en dms sont toujours positives
if precision == 'dms' then
dec = math_mod._round( dec * 60 * 60, 0 )
seconds = dec % 60
dec = (dec - seconds) / 60 / 60
end
if precision == 'dm' or precision == 'dms' then
dec = math_mod._round( dec * 60, 0 )
minutes = dec % 60
dec = (dec - minutes) / 60
end
end
degrees = math_mod._round(dec, 0)
return builddmsdimension(degrees, minutes, seconds, direction)
end
end


function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
--HTML builder for a geohack link
args = frame.args
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )
local dec = args[1]
-- displayed coordinates
local precision = string.lower(args[4])
local displaycoords
local displayformat, coordtype
if string.sub(displayformat, 1, 3) == 'dec' then
displaycoords = p.displaydec(decLat, decLong, displayformat)
if args[2] == 'N' or 'Nord' then
else
coordtype = 'latitude'
displaycoords = {
else  
p.displaydmsdimension(dmsLat, displayformat),
coordtype = 'longitude'
p.displaydmsdimension(dmsLong, displayformat),
}
end
end
if args[2] == 'Nord' or args[2] == 'Est' or args[2] == 'Ouest' then
 
displayformat = 'dms long'
-- 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
end
local coordobject = p._dec2dms(dec, coordtype, precision)
-- ajout de geoshape via externaldata
return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )
end
if not geoshape and displaytitle and mw.wikibase.getEntity() then
 
geoshape = mw.wikibase.getEntity().id
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 = 'no cardinal direction found in coordinates', sortkey = 'A'})
return nil
else
makeerror({message = 'invalid direction', sortkey = 'A'})
return nil
end
end
if geoshape then
if dmsobject.seconds then -- vérifie la précision des données initiales
jsonParams = {
precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?
jsonParams,
elseif dmsobject.minutes then
{
precision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )
type = 'ExternalData',
else
service = 'geoshape',
precision = math.max( math_mod._precision(tostring(degrees), 0 ) )
ids = geoshape,
properties = {
['fill-opacity'] = 0.2
}
}
}
end
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 maplink = mw.getCurrentFrame():extensionTag{
local args = frame.args
name = 'maplink',
if tonumber(args[1]) then
content = mw.text.jsonEncode( jsonParams ),
return args[1] -- coordonnées déjà en décimal
args = {
elseif not args[2] then
text = displaycoords[1] .. ", " .. displaycoords[2],
return p._dms2dms(parsedmsstring(args[1])) -- coordonnées sous la fore 23/22/N
zoom = zoom( extraparams ) or default_zoom,
else
latitude = decLat,
return p._dms2dec({direction = args[1], degrees = args[2], minutes = args[3], seconds = args[4]})
longitude = decLong,
}
}
 
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
local inlineText = displayinline and maplink or ''
local titleText = ''
if displaytitle then
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:addClass( displayinline and 'noprint' or nil )
:wikitext( maplink )
local frame = mw.getCurrentFrame()
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
end
end
end


function parsedmsstring(str) -- prend une séquence et donne des noms aux paramètres
return inlineText .. titleText
-- output table: {latitude=, longitude = , direction =  }
args = mw.text.split(str, '/', true)
if #args > 4 then
makeerror({message = "too many parameters for coordinates", sortkey= 'A' })
end
local direction = args[#args]
table.remove(args)
local degrees, minutes, seconds = args[1], args[2], args[3]
return builddmsdimension(degrees, minutes, seconds, direction)
end
end


function builddmsdimension(degrees, minutes, seconds, direction)
-- dms specific funcions
-- no error checking, done in function validdms
local dimensionobject = {}


local function twoDigit( value )
-- direction and dimension (= latitude or longitude)
if ( value < 10 ) then
dimensionobject.direction = direction
value = '0' .. lang:formatNum( value )
if direction == 'N' or direction == 'S' then
dimensionobject.dimension = 'latitude'
else
else
dimensionobject.dimension = 'longitude'
value = lang:formatNum( value )
end
end
return value
-- degrees, minutes, seconds
dimensionobject.degrees = tonumber(degrees)
dimensionobject.minutes = tonumber(minutes)
dimensionobject.seconds = tonumber(seconds)
return dimensionobject
end
end


Ligne 353 : Ligne 405 :
direction = i18n[direction]
direction = i18n[direction]
end
end
degrees = valuetable.degrees .. i18n.degrees
degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees
 
if valuetable.minutes then
if valuetable.minutes then
minutes = valuetable.minutes .. i18n.minutes
minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
if (valuetable.minutes < 10) then
minutes = '0' .. minutes
end
end
end
if valuetable.seconds then
if valuetable.seconds then
seconds = valuetable.seconds .. i18n.seconds
seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
if (valuetable.seconds < 10) then
seconds = '0' .. seconds
end
end
end
return degrees .. minutes .. seconds .. direction
return degrees .. minutes .. seconds .. direction
end
end


function validdms(coordtable)
local function validdms(coordtable)
local direction = coordtable.direction
local direction = coordtable.direction
local degrees = coordtable.degrees or 0
local degrees = coordtable.degrees or 0
Ligne 375 : Ligne 422 :
local seconds = coordtable.seconds or 0
local seconds = coordtable.seconds or 0
local dimension = coordtable.dimension
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
if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
makeerror({message = 'coordinates should be numeric', sortkey = 'A'})
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
return false
return false
end
end
 
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
makeerror({message = 'latitude direction should be N or S is ' .. (direction or 'nil'), sortkey = 'A'})
makeerror({message = i18n.invalidNS, sortkey = 'A'})
return false
return false
end
end
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
makeerror({message = 'latitude direction should be W or E is ' .. (direction or 'nil'), sortkey = 'A'})
makeerror({message = i18n.invalidEW, sortkey = 'A'})
return false
return false
end
end
 
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
if dimension == 'latitude' and degrees > 90 then
makeerror({message = 'latitude direction should be W or E is ' .. (direction or 'nil'), sortkey = 'A'})
makeerror({message = i18n.latitude90, sortkey = 'A'})
return false
return false
end
end
 
if dimension == 'latitude' and degrees > 90 then
if dimension == 'longitude' and degrees > 360 then
makeerror({'latitude > 90', sortkey = 'A'})
makeerror({message = i18n.longitude360, sortkey = 'A'})
return false
return false
end
end
 
if dimension == 'longitude' and degrees > 180 then
if degrees < 0 or minutes < 0 or seconds < 0 then
makeerror({'longitude > 180', sortkey = 'A'})
makeerror({message = i18n.negativeCoode, sortkey = 'A'})
return false
return false
end
end
 
if degrees < 0 or minutes < 0 or seconds < 0 then
if minutes > 60 or seconds > 60 then
makeerror({message = 'dms coordinates should be positive', sortkey = 'A'})
makeerror({message = i18n.minSec60, sortkey = 'A'})
return false
return false
end
end
if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
if math.floor(degrees) ~= degrees or math.floor(minutes) ~= minutes then
makeerror({message = i18n.dmIntergers, sortkey = 'A'})
makeerror({message = 'degrees and minutes should be integers', sortkey = 'A'})
return false
return false
end
end
Ligne 417 : Ligne 473 :
end
end


function validlatitude(value) -- decimal latitude
local function builddmsdimension(degrees, minutes, seconds, direction, dimension)
if type(value) ~= 'number' or math.abs(value) > 90 then
-- no error checking, done in function validdms
makeerror({message = 'latitude should be number < 90, is ' .. (value or 'nil')})
local dimensionobject = {}
return false
 
-- direction and dimension (= latitude or longitude)
dimensionobject.direction = direction
if dimension then
dimensionobject.dimension = dimension
elseif direction == 'N' or direction == 'S' then
dimensionobject.dimension = 'latitude'
elseif direction == 'E' or direction == 'W' then
dimensionobject.dimension = 'longitude'
end
end
return true
 
-- degrees, minutes, seconds
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 validlongitude(value) -- decimal longitude
function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres
if type(value) ~= 'number' or math.abs(value) > 180 then
-- output table: { latitude=, longitude = , direction =  }
makeerror({message = 'longitude should be number < 180, is ' .. (value or 'nil')})
if type( str ) ~= 'string' then
return false
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
local 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
return true
end
end


--- decimal specific functions
function p.displaydec(latitude, longitude, format)
function p.displaydec(latitude, longitude, format)
if format == 'dec west' then
local lat = lang:formatNum( latitude )
local latsymbol = i18n.N
local long = lang:formatNum( longitude )
longitude = - longitude
 
if latitude < 0 then latsymbol = i18n.S end
if format == 'dec west' or  format == 'dec east' then
if longitude < 0 then
local symbolNS, symbolEW = i18n.N, i18n.E
long = 360 + longitude
if latitude < 0 then
symbolNS = i18n.S
lat = lang:formatNum( -latitude )
end
end
return latitude .. i18n.degrees .. latsymbol .. ', ' .. longitude ..  i18n.degrees .. i18n.W
if format == 'dec west' then
symbolEW = i18n.W
elseif format == 'dec west' then
local latsymbol = i18n.N
if latitude < 0 then latsymbol = i18n.S end
if longitude < 0 then
longitude = 360 + longitude
end
end
return latitude .. i18n.degrees .. latsymbol .. ', ' .. longitude ..  i18n.degrees .. i18n.E
if longitude < 0 then
long = lang:formatNum( 360 + longitude )
else  
end
return latitude .. ', ' .. longitude
 
return { lat .. i18n.degrees .. symbolNSlong ..  i18n.degrees .. symbolEW }
 
else
return { lat, long }
end
end
end
end


function wikidatacoords(query)
 
local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
dec = mw.text.trim(dec)
if not dec then
return nil
end
if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
makeerror({'invalid coord type', sortkey = "A"})
return nil
end
local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
if not numdec then -- tries the decimal + direction format
dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
if not dec or not tonumber(dec) then
return nil
end
if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
numdec = tonumber( dec )
elseif direction == 'W' or direction == 'S' then
dec = '-' .. dec
numdec = tonumber( 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
makeerror({message = i18n.latitude90 , sortkey = 'A'})
return nil
end
if coordtype == 'longitude' and math.abs(numdec) > 360 then
makeerror({message = i18n.longitude360 , sortkey = 'A'})
return nil
end
return dec
end
 
-- dms/dec conversion functions
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
if precision >= 3 then
return 'dms'
elseif precision >=1 then
return 'dm'
else
return 'd'
end
end
 
local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
local precision = 0
for d, val in ipairs(decs) do
precision = math.max(precision, math_mod._precision(val))
end
return convertprecision(precision)
end
 
local function dec2dms_d(dec)
local degrees = math_mod._round( dec, 0 )
return degrees
end
 
local function dec2dms_dm(dec)
dec = math_mod._round( dec * 60, 0 )
local minutes = dec % 60
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
return degrees, minutes
end
 
local function dec2dms_dms(dec)
dec = math_mod._round( dec * 60 * 60, 0 )
local seconds = dec % 60
dec = math.floor( (dec - seconds) / 60 )
local minutes = dec % 60
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
return degrees, minutes, seconds
end
 
function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
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
local 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
local coordType
if args[1]:match( '[NS]' ) then
coordType = 'latitude'
elseif args[1]:match( '[EWO]') then
coordType = 'longitude'
end
if coordType then
local result = parsedec( args[1],  coordType, args.globe or 'earth' )
if result then
return result
end
end
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 = query or {property = 'p625'}
query.formatting = 'raw'
local wd = require('Module:Wikidata')
local wd = require('Module:Wikidata')
local claim = wd.getClaims(query)
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
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
local coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values
return coords.latitude, coords.longitude, coords.globe
-- 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
end
return nil
return nil
end
end


--- fonctions principales
 
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 = mw.loadData("Module:Drapeau/Data").data[qid]
else
--Bir Tawil n'a pas de pays connu
qid = '?'
end
if gdataone ~= nil then
local genre = mw.loadData("Module:Drapeau/Domaine").genre
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)
function p._coord(args)
local latitude, longitude = tonumber(args.latitude), tonumber(args.longitude) -- must be in decimal format
 
if latitude and longitude and (not validlatitude(latitude) or not validlongitude(longitude)) then
-- I declare variable
return showerrors()
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 displayinline = string.find(displayplace, 'inline') and true or false
local displaytitle = string.find(displayplace, 'title') and true or false
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
local savegeodata = nil
if args.geodata ~= nil and args.geodata ~= '' then
savegeodata = require('Module:Yesno')(args.geodata)
end
if savegeodata == nil then -- args.geodata non renseigné ou valeur non reconnue
savegeodata = (displaytitle and mw.title.getCurrentTitle():inNamespaces(0, 14, 100))
end
end
local displayformat, inputformat = args.format, args.inputformat -- one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west'
 
local displayplace = string.lower(args.display or 'inline') -- on of 'inline', 'title' or 'inline,title'
-- II extract coordinates from Wikitext
local globe = args.globe -- planet see the globedata table for accepted values
if (rawlat or rawlong) then
local objectname = args.name -- name of the title displayed in geohack
if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude
local notes = (' ' and args.notes) or '' -- notes à afficher après les coordonnées
makeerror({message = i18n.coordMissing, sortkey = 'A'})
local wikidata = args.wikidata -- string set to true when
local dmslatitude, dmslongitude -- type: table when created
local extraparams = args.extraparams or '' -- chaîne destinée à geohack dernier paramètre avec argument numérique dans {{Coord}}
local wikidataquery = args.wikidataquery
local trackingstring = '' -- tracking cats except error cats (already in errorstring)
if not latitude and args.latitude and args.latitude ~= '' then -- latitude was not a number
dmslatitude, dmslongitude = parsedmsstring(args.latitude), parsedmsstring(args.longitude)
if (not validdms(dmslatitude)) or (not validdms(dmslongitude)) then
return showerrors()
return showerrors()
end
end
latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)
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
end
 
-- Wikidata
-- III extract coordinate data from Wikidata and compare them to local data
local wikidatalatitude, wikidatalongitude, wikidataglobe
local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
if wikidata == 'true' then
if wikidata == 'true' then
wikidatalatitude, wikidatalongitude, wikidataglobe = wikidatacoords(wikidataquery)
wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery)
end
 
if wikidatalatitude and latitude then
if wikidatalatitude and latitude and longitude then
local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) < maxdistance then
if p._distance({latitude = latitude, longitude = longitude}, {latitude = wikidatalatitude, longitude = wikidatalongitude}, wikidataglobe) < maxdistance then
trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
else
else
trackingstring = trackingstring .. makecat(i18n.notaswikidata)
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
end
end
if wikidatalatitude and not latitude then
 
latitude, longitude, globe = wikidatalatitude, wikidatalongitude, wikidataglobe
 
trackingstring = trackingstring .. makecat(i18n.throughwikidata)
-- exit if stil no latitude or no longitude
end
if not latitude and not longitude then
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
return nil -- ne rien ajouter ici pour que l'appel à cette fonction retourne bien nil en l'absence de données
end
end
if not latitude or not longitude then
 
makeerror({'latitude or lingitude missing', sortkey = 'A'})
-- IV best guesses for missing parameters
return showerrors()
 
end
-- best guesses for missing parameters
--- globe
--- globe
if not globe then -- cherche le globe dans l'extraparams destinée à geohack
if globe == '' then
globe = string.match(extraparams, 'globe\:%a+')
if globe then globe = string.sub(globe, 7) end
end
if not globe or globe == '' then
globe = 'earth'
globe = 'earth'
end
end
if not globedata[globe] then
if not globedata[globe] then
makeerror({message = 'invalid globe:' .. globe})
makeerror({message = i18n.invalidGlobe .. globe})
globe = 'earth'
globe = 'earth'
end
end
if globe ~= 'earth' then
extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
maplink = false
end
 
--- diplayformat
--- diplayformat
if not displayformat or displayformat == '' then
if not displayformat or displayformat == '' then
if dmslatitude then -- keeps the same format for output as for input, but dms by default when coordinates come from Wikidata
displayformat = globedata[globe].defaultdisplay
displayformat = 'dms'
else
displayformat = 'dec'
end
end
end
    local displayinline =  string.find(displayplace, 'inline')
 
    local displaytitle = string.find(displayplace, 'title')
-- displayinline/displaytitle
    if not displayinline and not displaytitle then
if not displayinline and not displaytitle then
    displayinline = true
displayinline = true
    makeerror({sortkey = 'C'}) -- not a major error, continue  
if displayplace ~= '' then
    end
makeerror({sortkey = 'C'}) --error if display not empty, but not a major error, continue
    if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
    trackingstring = trackingstring .. makecat(globedata[globe].trackingcat)
    end
-- standardize format for astronomical objects so that it can be used by geohack (back converted afterwards, pretty fast)
if inputformat == 'dec east' then
if longitude > 180 then
longitude = longitude - 360
end
end
longitude = - longitude
end
end
if inputformat == 'dec west' then
if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
if longitude > 180 then
--local cattoappend = globedata[globe].trackingcat
longitude = longitude - 360
--Récupération des badges
local cats = wikidatacat(globe)
for i, cat in ipairs( cats ) do
trackingstring = trackingstring .. makecat(cat)
end
end
end
end


-- finish up
-- V geodata
if not dmslatitude then
local geodata = ''
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude'), p._dec2dms(longitude, 'longitude')
if savegeodata and 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
end
extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
-- VI final output
-- final output
local mainstring = ''
local mainstring = ''
 
if maplink then
if latitude and longitude and dmslatitude and dmslongitude then  
mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
else
else
showerrors()
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
end
end
return mainstring .. notes .. trackingstring .. showerrors()
 
return mainstring .. notes .. trackingstring .. geodata .. showerrors()
end
end


function p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coord
function p.coord(frame) -- parses the strange parameters of Template:Coord before sending them to p.coord
local args = frame.args
local args = frame.args
local numericargs = {}
local numericargs = {}
for i, j in ipairs(args) do
for i, j in ipairs(args) do
args[i] = mw.text.trim(j)
args[i] = mw.text.trim(j)
if type(i) == 'number' and args[i] ~= '' then
if args[i] ~= '' then
table.insert(numericargs, args[i])
table.insert(numericargs, args[i])
end
end
end
end
if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
args.extraparams = numericargs[#numericargs]
args.extraparams = numericargs[#numericargs]
if #numericargs == 1 and tonumber(numericargs[1]) then
makeerror({message = i18n.coordMissing, sortkey = 'A'})
return showerrors()
end
table.remove(numericargs)
table.remove(numericargs)
end
end
Ligne 607 : Ligne 1 066 :
end
end
end
end
if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
args.latitude, args.longitude = args.longitude, args.latitude
args.latitude, args.longitude = args.longitude, args.latitude
end
end
if args.formatitle then -- legacy parameter
return p._coord(args)
args.display = 'inline'
local inline = p._coord(args)
args.display = 'title'
args.format = args.formatitle
local title = p._coord(args)
return (inline or '') .. (title or '')
else
return p._coord(args)
end
end
end


Ligne 626 : Ligne 1 077 :
end
end


function p.latitude(frame) -- helper function for infoboxes, à déprécier
function p.latitude(frame) -- helper function pour infobox, à déprécier
if frame.args[1] and frame.args[1] ~= '' then  
local args = frame.args
return frame.args[1]
local latitude  = frame.args[1]
else
if latitude and mw.text.trim(latitude) ~= '' then
return wikidatacoords(frame.args.query)
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
end
end


return p
return p

Dernière version du 22 février 2026 à 00:17

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

local math_mod = require( "Module:Math" )

local p = {}

local NBSP = '\194\160' -- espace insécable (code UTF-8 sur deux octets)

local i18n = {
	N = 'N',
	Nlong = 'nord',
	W = 'O',
	Wlong = 'ouest',
	E = 'E',
	Elong = 'est',
	S = 'S',
	Slong = 'sud',
	degrees = '°' .. NBSP,
	minutes = '′' .. NBSP,
	seconds = '″' .. NBSP,
	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' .. NBSP .. ': ',                            -- 'invalid globe:',
}
local coordParse = {
	NORTH = 'N',
	NORD = 'N',
	EAST = 'E',
	EST = 'E',
	WEST = 'W',
	O = 'W',
	OUEST = 'W',
	SOUTH = 'S',
	SUD = 'S',
}

local globedata = 	{
	--[[ notes:
		radius in kilometers (especially imprecise for non spheric bodies)
		defaultdisplay is currently disabled, activate it ?
	]]--
	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 ce seuil (en kilomètres), une catégorie de maintenance est ajoutée
local lang = mw.language.getContentLanguage()
local default_zoom = 13

local function makecat(cat, sortkey)
	if type( sortkey ) == 'string' then
		return '[[Category:' .. cat .. '|' .. sortkey .. ']]'
	else
		return '[[Category:' .. cat .. ']]'
	end
end

----------------------------------------
--Error handling
	--[[ 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 = ''

local function makeerror(args)
	local errormessage = ''
	if args.message then
		errormessage = '<strong class="error"> Coordonnées' .. NBSP .. ': ' .. args.message .. '</strong>'
	end
	local errorcat = ''
	if mw.title.getCurrentTitle().namespace == 0 then
		errorcat = makecat(i18n.errorcat, args.sortkey)
	end
	errorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messages
	return nil
end

local function showerrors()
	return errorstring
end



-- Distance computation
function p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globe

	globe = string.lower(globe or 'earth')

	-- check arguments and converts degreees to radians
	local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude
	if (not latA) or (not latB) or (not longA) or (not longB) then return
		error('coordinates missing, can\'t compute distance')
	end
	if type(latA) ~= 'number' or type(latB) ~= 'number' or type(longA) ~= 'number' or type(longB) ~= 'number' then
		error('coordinates are not numeric, can\'t compute distance')
	end
		if not globe or not globedata[globe] then
		return error('globe: ' .. globe .. 'is not supported')
	end

	-- calcul de la distance angulaire en radians
	local convratio = math.pi / 180 -- convertit en radians
	latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB
	local cosangle = math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA)
	if cosangle >= 1 then -- may be above one because of rounding errors
		return 0
	end
	local angle = math.acos(cosangle)
	-- calcul de la distance en km
	local radius = globedata[globe].radius
	return radius * angle
end

function p.distance(frame)
	local args = frame.args
	return p._distance(
		{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)},
		{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},
		args.globe)
end

local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
	extraparams = extraparams or ''
	local geohacklatitude, geohacklongitude
	-- format latitude and longitude for the URL
	if tonumber(decLat) < 0 then
		geohacklatitude = tostring(-tonumber(decLat)) .. '_S'
	else
		geohacklatitude = decLat .. '_N'
	end
	if tonumber(decLong) < 0  then
		geohacklongitude = tostring(-tonumber(decLong)) .. '_W'
	elseif globedata[globe].defaultdisplay == 'dec west' then
		geohacklongitude = decLong .. '_W'
	else
		geohacklongitude = decLong .. '_E'
	end
	-- prepares the 'paramss=' parameter
	local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
	-- concatenate parameteres for geohack
	return i18n.geohackurl ..
		"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
		"&params=" .. geohackparams ..
		(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
end

--HTML builder for a geohack link
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
	-- 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 )
	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
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, 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

	-- 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{
		name = 'maplink',
		content = mw.text.jsonEncode( jsonParams ),
		args = {
			text = displaycoords[1] .. ", " .. displaycoords[2],
			zoom = zoom( extraparams ) or default_zoom,
			latitude = decLat,
			longitude = decLong,
		}
	}

	-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
	local inlineText = displayinline and maplink or ''
	local titleText = ''
	if displaytitle then
		local htmlTitle = mw.html.create('span')
			:attr{ id = 'coordinates' }
			:addClass( displayinline and 'noprint' or nil )
			:wikitext( maplink )
		local frame = mw.getCurrentFrame()
		titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
	end

	return inlineText .. titleText
end

-- dms specific funcions

local function twoDigit( value )
	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
	local str = ''
	local direction = valuetable.direction
	local degrees, minutes, seconds = '', '', ''
	local dimension

	if format == 'dms long' then
		direction = i18n[direction .. 'long']
	else
		direction = i18n[direction]
	end
	degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees

	if valuetable.minutes then
		minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
	end
	if valuetable.seconds then
		seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
	end
	return degrees .. minutes .. seconds .. direction
end

local function validdms(coordtable)
	local direction = coordtable.direction
	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
		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)
	-- no error checking, done in function validdms
	local dimensionobject = {}

	-- direction and dimension (= latitude or longitude)
	dimensionobject.direction = direction
	if dimension then
		dimensionobject.dimension = dimension
	elseif direction == 'N' or direction == 'S' then
		dimensionobject.dimension = 'latitude'
	elseif direction == 'E' or direction == 'W' then
		dimensionobject.dimension = 'longitude'
	end

	-- degrees, minutes, seconds
	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

function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres
	-- output table: { latitude=, longitude = , direction =  }
	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
	local 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

--- decimal specific functions
function p.displaydec(latitude, longitude, format)
	local lat = lang:formatNum( latitude )
	local 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 = lang:formatNum( -latitude )
		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


local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
	dec = mw.text.trim(dec)
	if not dec then
		return nil
	end
	if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
		makeerror({'invalid coord type', sortkey = "A"})
		return nil
	end
	local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
	if not numdec then -- tries the decimal + direction format
		dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
		local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
		dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
		if not dec or not tonumber(dec) then
			return nil
		end
		if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
			numdec = tonumber( dec )
		elseif direction == 'W' or direction == 'S' then
			dec = '-' .. dec
			numdec = tonumber( 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
		makeerror({message = i18n.latitude90 , sortkey = 'A'})
		return nil
	end
	if coordtype == 'longitude' and math.abs(numdec) > 360 then
		makeerror({message = i18n.longitude360 , sortkey = 'A'})
		return nil
	end
	return dec
end

-- dms/dec conversion functions
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
	if precision >= 3 then
		return 'dms'
	elseif precision >=1 then
		return 'dm'
	else
		return 'd'
	end
end

local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
	local precision = 0
	for d, val in ipairs(decs) do
		precision = math.max(precision, math_mod._precision(val))
	end
	return convertprecision(precision)
end

local function dec2dms_d(dec)
	local degrees = math_mod._round( dec, 0 )
	return degrees
end

local function dec2dms_dm(dec)
	dec = math_mod._round( dec * 60, 0 )
	local minutes = dec % 60
	dec = math.floor( (dec - minutes) / 60 )
	local degrees = dec % 360
	return degrees, minutes
end

local function dec2dms_dms(dec)
	dec = math_mod._round( dec * 60 * 60, 0 )
	local seconds = dec % 60
	dec = math.floor( (dec - seconds) / 60 )
	local minutes = dec % 60
	dec = math.floor( (dec - minutes) / 60 )
	local degrees = dec % 360
	return degrees, minutes, seconds
end

function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
	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
	local 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
			local coordType
			if args[1]:match( '[NS]' ) then
				coordType = 'latitude'
			elseif args[1]:match( '[EWO]') then
				coordType = 'longitude'
			end
			if coordType then
				local result = parsedec( args[1],  coordType, args.globe or 'earth' )
				if result then
					return result
				end
			end
			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 = mw.loadData("Module:Drapeau/Data").data[qid]
					else
						--Bir Tawil n'a pas de pays connu
						qid = '?'
					end
					if gdataone ~= nil then
						local genre = mw.loadData("Module:Drapeau/Domaine").genre
						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 displayinline = string.find(displayplace, 'inline') and true or false
	local displaytitle = string.find(displayplace, 'title') and true or false
	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
	local savegeodata = nil
	if args.geodata ~= nil and args.geodata ~= '' then
		savegeodata = require('Module:Yesno')(args.geodata)
	end
	if savegeodata == nil then -- args.geodata non renseigné ou valeur non reconnue
		savegeodata = (displaytitle and mw.title.getCurrentTitle():inNamespaces(0, 14, 100))
	end

	-- 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
	if not displayinline and not displaytitle then
		displayinline = true
		if displayplace ~= '' then
			makeerror({sortkey = 'C'}) --error if display not empty, but 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 savegeodata and 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) -- parses 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 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