Aller au contenu

Module:Coordinates

De Wreck
Version datée du 18 février 2014 à 22:05 par Modèle:Infobox>Orlodrim (Remplacement par la version de Zolo à partir de Module:Coordinates/Test)

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

local math_mod = require( "Module:Math" )
local p = {}

local i18n = {
	N = ' N',
	Nlong = ' Nord',
	W = ' O',
	Wlong = ' Ouest',
	E = ' E',
	Elong = ' Est',
	S = ' S',
	Slong = ' Sud',
	degrees = '° ',
	minutes = '\′ ',
	seconds = '″ ',
	geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr',
	tooltip = 'Cartes, vues aériennes et autres données pour cet endroit',
	errorcat = 'Pages 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',
	throughwikidata = 'Page géolocalisée par Wikidata',
}

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 = 'Article géolocalisé extraterrestre'},
	callisto =  {radius = 2410, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	ceres =  {radius = 470, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	deimos =  {radius = 7, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	dione =  {radius = 560, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	enceladus =  {radius = 255, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	ganymede =  {radius = 1631, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = 'Article géolocalisé sur Terre'},
	europe =  {radius = 1561, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	hyperion =  {radius = 140, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	iapetus =  {radius = 725, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	['io'] =  {radius = 1322, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	jupiter =  {radius = 68911, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	mars =  {radius = 3389.5, defaultdisplay = 'dec east', trackingcat =  'Article géolocalisé sur Mars' },
	mercury =  {radius = 2439.7, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	mimas =  {radius = 197, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	miranda =  {radius = 335, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	moon =  {radius = 1736, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé sur la Lune'},
	neptune =  {radius = 24553, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	oberon =  {radius = 761, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	phoebe =  {radius = 110, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	phobos =  {radius = 11, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	rhea =  {radius = 765, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	saturn =  {radius = 58232, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	titan =  {radius = 2575.5, defaultdisplay = 'dec west', trackingcat = 'Article géolocalisé extraterrestre'},
	tethys =  {radius = 530, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	titania =  {radius = 394, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	triton = {radius = 1353, defaultdisplay = 'dec west', trackingcat = 'Article géolocalisé extraterrestre'},
	umbriel =  {radius = 584, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	uranus =  {radius = 25266, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	venus =  {radius = 6051.8, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'},
	vesta =  {radius = 260, defaultdisplay = 'dec east', trackingcat = 'Article géolocalisé extraterrestre'}
	
}

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

----------------------------------------
--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
	]]--

errorstring = ''

function makeerror(args)
	local errormessage = ''
	if args.message then 
		errormessage = '<strong class="error">Coordonnées: ' .. 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

function showerrors()
	return errorstring
end
-------------------------------------------
function makecat(cat, sortkey)
	return '[[Category:' .. cat .. '|' .. (sortkey or '*') .. ']]'
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
	
	-- 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 angle = math.acos(math.sin(latA) * math.sin(latB) + math.cos(latA) * math.cos(latB) * math.cos(longB - longA))

	-- convertit en km en fonction du globe
	if not globe or not globedata[globe] then
		return error('globe: ' .. globe .. 'is not supported')
	end

	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


------------------------
--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 ''
    -- geohack url and parameters
    local decimalcoords = p.displaydec(decLat, decLong, displayformat)
    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'
    else 
    	geohacklongitude = decLong .. '_E'
	end
	-- prepares the 'paramss=' parameter
	local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams
	-- concatenate parameteres for geohack
	local url = i18n.geohackurl .. 
		"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
		"&params=" .. geohackparams ..
		(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")

    root = htmlBuilder.create()
    root
        .tag("span")
            .addClass("plainlinks nourlexpansion")
            .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")
    text = tostring(root)

    noprint = displayinline and "class=\"noprint\" " or ""
    htmlTitle = "<span style=\"font-size: small;\"><span " .. noprint .. "id=\"coordinates\">" 

    return (displayinline and text or "") ..
           (displaytitle and (htmlTitle .. text .. "</span></span>") or "")
end

-- Fonctions de manipualation/coonversion des coordonnées

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

	-- precision
	if precision and precision ~= '' and precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
		return makeerror({sortkey = 'C'})
	end
	if not precision then
		precision = math_mod._precision(dec)
		if precision <= 0 then
			precision = 'd'
		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
	-- direction 
	if coordtype == 'latitude' then 
		if dec < 0 then
			direction = 'S'
		else 
			direction = 'N'
		end
	elseif coordtype == 'longitude' then
		if dec < 0 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
		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
	degrees = math_mod._round(dec, 0)
	return builddmsdimension(degrees, minutes, seconds, direction)
end

function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
	args = frame.args
	local dec = args[1] 
	local precision = string.lower(args[4])
	local displayformat, coordtype
	
	if args[2] == 'N' or 'Nord' then
		coordtype = 'latitude'
	else 
		coordtype = 'longitude'
	end
	if args[2] == 'Nord' or args[2] == 'Est' or args[2] == 'Ouest' then
		displayformat = 'dms long'
	end
	local coordobject = p._dec2dms(dec, coordtype, precision)
	return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
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 = 'no cardinal direction found in coordinates', sortkey = 'A'})
		return nil
	else
		makeerror({message = 'invalid direction', 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
		return p._dms2dec(parsedmsstring(args[1])) -- coordonnées sous la fore 23/22/N
	else 
		return p._dms2dec({direction = args[1], degrees = args[2], minutes = args[3], seconds = args[4]})
	end
end

function parsedmsstring(str) -- prend une séquence et donne des noms aux paramètres 
	-- output table: {latitude=, longitude = , direction =  }
	if not tonumber(str) and not string.find(str, '/') then
		makeerror({message ='invalid coordinate format', sortkey= 'A'})
		return nil
	end
	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]
	local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction)
	if validdms(dimensionobject) then
		return dimensionobject
	else
		return nil
	end
	
end

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

	
	-- direction and dimension (= latitude or longitude)
	dimensionobject.direction = direction
	if direction == 'N' or direction == 'S' then
		dimensionobject.dimension = 'latitude'
	else
		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.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 = valuetable.degrees .. i18n.degrees
	if valuetable.minutes then
		minutes = valuetable.minutes .. i18n.minutes
		if (valuetable.minutes < 10) then
			minutes = '0' .. minutes
		end
	end
	if valuetable.seconds then
		seconds = valuetable.seconds .. i18n.seconds
		if (valuetable.seconds < 10) then
			seconds = '0' .. seconds
		end
	end
	return degrees .. minutes .. seconds .. direction
end

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 type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
		makeerror({message = 'coordinates should be numeric', sortkey = 'A'})
		return false
	end
	
	if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
		makeerror({message = 'could not find latitude direction (should be N or S)', sortkey = 'A'})
		return false
	end
	if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
		makeerror({message = 'could not find longitude direction (should be W or E) ', sortkey = 'A'})
		return false
	end
	
	if dimension == 'latitude' and degrees > 90 then
		makeerror({'latitude > 90', sortkey = 'A'})
		return false
	end
	
	if dimension == 'longitude' and degrees > 180 then
		makeerror({'longitude > 180', sortkey = 'A'})
		return false
	end
	
	if degrees < 0 or minutes < 0 or seconds < 0 then
		makeerror({message = 'dms coordinates should be positive', sortkey = 'A'})
		return false
	end
	
	if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
		makeerror({message = 'degrees and minutes should be integers', sortkey = 'A'})
		return false
	end
	return true
end

function validlatitude(value) -- decimal latitude
	if type(value) ~= 'number' or math.abs(value) > 90 then
		makeerror({message = 'latitude should be number < 90, is ' .. (value or 'nil')})
		return false
	end
	return true
end

function validlongitude(value) -- decimal longitude
	if type(value) ~= 'number' or math.abs(value) > 180 then
		makeerror({message = 'longitude should be number < 180, is ' .. (value or 'nil')})
		return false
	end
	return true
end

function p.displaydec(latitude, longitude, format)
	if format == 'dec west' then
		local latsymbol = i18n.N
		longitude = - longitude
		if latitude < 0 then latsymbol = i18n.S end
		if longitude < 0 then 
			long = 360 + longitude
		end
	return latitude .. i18n.degrees .. latsymbol .. ', ' .. longitude ..  i18n.degrees .. 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
		return latitude .. i18n.degrees .. latsymbol .. ', ' .. longitude  ..  i18n.degrees .. i18n.E
		
	else 
		return latitude .. ', ' .. longitude 
	end
end

function wikidatacoords(query)
	query = query or {property = 'p625'}
	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
		return coords.latitude, coords.longitude, coords.globe
	end
	return nil
end

--- fonctions principales
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
		return showerrors()
	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' 
	local globe = string.lower(args.globe or '') -- planet see the globedata table for accepted values
	local objectname = args.name -- name of the title displayed in geohack
	local notes = (' ' and args.notes) or '' -- notes à afficher après les coordonnées
	local wikidata = args.wikidata -- string set to true when
	local dmslatitude, dmslongitude -- type: table when created	
	local extraparams = string.lower(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)
 	
 	
 	-- parsage des donnés en format non-décimal
	if (not latitude and args.latitude and args.latitude ~= '') then -- latitude was not a number
		dmslatitude = parsedmsstring(args.latitude)
		if not dmslatitude then return showerrors() end
		latitude = p._dms2dec(dmslatitude)
	end
	if (not longitude and args.longitude and args.longitude ~= '') then -- latitude was not a number
		dmslongitude = parsedmsstring(args.longitude)
		if not dmslongitude then return showerrors() end
		longitude = p._dms2dec(dmslongitude)
	end
	
	
	-- récuparation des coordonnées Wikidata
	local wikidatalatitude, wikidatalongitude, wikidataglobe
	if wikidata == 'true' then
		wikidatalatitude, wikidatalongitude, wikidataglobe = wikidatacoords(wikidataquery)
	end
	if wikidatalatitude and latitude 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 = wikidatalatitude, wikidatalongitude, wikidataglobe
		trackingstring = trackingstring .. makecat(i18n.throughwikidata)
	end
	-- exit if stil no latitude or not 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
	if not latitude or not longitude then 
		makeerror({message = 'latitude or longitude missing', sortkey = 'A'})
		return showerrors()
	end
	
	-- best guesses for missing parameters
	
	--- globe
	if globe == '' then -- cherche le globe dans l'extraparams destinée à geohack
		local globe2 = string.match(extraparams, 'globe\:%a+')
		if globe2 then globe = string.sub(globe2, 7) end
	end
	if globe == '' then
		globe = 'earth'
	end
	if not globedata[globe] then
		makeerror({message = 'invalid globe:' .. globe})
		globe = 'earth'
	end
	
	--- diplayformat
	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 = 'dms'
		else
			displayformat = 'dec'
		end
	end
	
	-- displayinline/displaytitle
    local displayinline =  string.find(displayplace, 'inline') 
    local displaytitle = string.find(displayplace, 'title') 
    if not displayinline and not displaytitle then
    	displayinline = true
    	if displayplace ~= '' then 
    		makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue
    	end
    end
    if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
    	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
		longitude = - longitude
	end
	if inputformat == 'dec west' then
		if longitude > 180 then
			longitude = longitude - 360
		end
	end

	-- finish up
	if not dmslatitude then
			dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude'), p._dec2dms(longitude, 'longitude')
	end
	extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
	
	-- final output
	local mainstring = ''

	mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
	return mainstring .. notes .. trackingstring .. showerrors()
end

function p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coord
	local args = frame.args
	local numericargs = {}
	for i, j in ipairs(args) do
		args[i] = mw.text.trim(j)
		if type(i) == 'number' and args[i] ~= '' then
			table.insert(numericargs, args[i])
		end
	end


	if #numericargs == 1 then
		makeerror({message = 'invalid coordinates format', sortkey = 'A'})
		return showerrors()
	elseif #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
		args.extraparams = numericargs[#numericargs]
		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
	if args.formatitle then -- legacy parameter
		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

function p.Coord(frame)
	return p.coord(frame)
end

function p.latitude(frame) -- helper function for infoboxes, à déprécier
	if frame.args[1] and frame.args[1] ~= '' then 
		return frame.args[1]
	else
		return wikidatacoords(frame.args.query)
	end
end

return p