Cette page est protégée.

Module:Coordonnées

Aller à la navigation Aller à la recherche
 Documentation[modifier] [purger]

Le module : Coordonnées implémente le modèle : Coord (fonction Coord, voir la documentation du modèle) et sert au modèle : Infobox CoordGéo (fonction CoordGeog).

Les deux modèles affichent à la fois (lorsque demandé) un texte qui expose les coordonnées du point donné, et une icône de titre (le module : Icône de titre est alors appelé).

À faire

  • Beaucoup de choses mais pas trop, pour ne pas s’éloigner trop du code de Wikipédia. Documenter un petit peu, peut-être.
  • Le code est importé de Wikipédia, et plein de fonctions ne sont ni utilisables ni souhaitables ici, il faudrait donc les enlever.
  • Les cartes devraient (un jour) utiliser l’extension Kartographer (qui est installée), mais cela n’est pas possible actuellement.

Source : cette page a été partiellement adaptée de la page Module:Coordinates de Wikipédia.
Source : cette page a été partiellement adaptée de la page Module:Math de Wikipédia.
local p = {}

--
-- fonctions d’aide
--

local function icone( url, texte )
	return require( 'Module:Icône de titre' ).iconeDeTitre( nil, -- fonction réservée aux modules
	                                                        'coords', -- id
	                                                        'Compass icon matte.svg',
	                                                        url,
	                                                        texte )
end

--
-- deux fonctions importées du module : Math de Wikipédia, voir les auteurs
-- https://fr.wikipedia.org/w/index.php?title=Module:Math&oldid=116008789
--

local function _precision( x )
	x = string.upper( x )

	local decimal = string.find( x, '.', 1, true )
	local exponent_pos = string.find( x, 'E', 1, true )
	local result = 0;

	if exponent_pos ~= nil then
		local exponent = string.sub( x, exponent_pos + 1 )
		x = string.sub( x, 1, exponent_pos - 1 )
		result = result - tonumber( exponent )
	end

	if decimal ~= nil then
		result = result + string.len( x ) - decimal
		return result
	end

	local pos = string.len( x );
	while x:byte(pos) == string.byte( '0' ) do
		pos = pos - 1
		result = result - 1
		if pos <= 0 then
			return 0
		end
	end

	return result
end

local function _round( value, precision )
	local rescale = math.pow( 10, precision )
	return math.floor( value * rescale + 0.5 ) / rescale
end

--
-- fonctions importées du module : Coordinates de Wikipédia, voir les auteurs
-- https://fr.wikipedia.org/w/index.php?title=Module:Coordinates&oldid=192947244
--

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 = 'https://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',
	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'},
	callisto  = {radius = 2410,   defaultdisplay = 'dec west'},
	ceres     = {radius = 470,    defaultdisplay = 'dec east'},
	charon    = {radius = 1214,   defaultdisplay = 'dec east'},
	deimos    = {radius = 7,      defaultdisplay = 'dec west'},
	dione     = {radius = 560,    defaultdisplay = 'dec west'},
	enceladus = {radius = 255,    defaultdisplay = 'dec west'},
	ganymede  = {radius = 2634,   defaultdisplay = 'dec west'},
	earth     = {radius = 6371,   defaultdisplay = 'dms'     },
	europa    = {radius = 1561,   defaultdisplay = 'dec east'},
	hyperion  = {radius = 140,    defaultdisplay = 'dec west'},
	iapetus   = {radius = 725,    defaultdisplay = 'dec west'},
	['io']    = {radius = 1322,   defaultdisplay = 'dec west'},
	jupiter   = {radius = 68911,  defaultdisplay = 'dec east'},
	mars      = {radius = 3389.5, defaultdisplay = 'dec east'},
	mercury   = {radius = 2439.7, defaultdisplay = 'dec west'},
	mimas     = {radius = 197,    defaultdisplay = 'dec west'},
	miranda   = {radius = 335,    defaultdisplay = 'dec east'},
	moon      = {radius = 1736,   defaultdisplay = 'dec'     },
	neptune   = {radius = 24553,  defaultdisplay = 'dec east'},
	oberon    = {radius = 761,    defaultdisplay = 'dec east'},
	phoebe    = {radius = 110,    defaultdisplay = 'dec west'},
	phobos    = {radius = 11,     defaultdisplay = 'dec west'},
	pluto     = {radius = 1185,   defaultdisplay = 'dec east'},
	rhea      = {radius = 765,    defaultdisplay = 'dec west'},
	saturn    = {radius = 58232,  defaultdisplay = 'dec east'},
	titan     = {radius = 2575.5, defaultdisplay = 'dec west'},
	tethys    = {radius = 530,    defaultdisplay = 'dec west'},
	titania   = {radius = 394,    defaultdisplay = 'dec east'},
	triton    = {radius = 1353,   defaultdisplay = 'dec east'},
	umbriel   = {radius = 584,    defaultdisplay = 'dec east'},
	uranus    = {radius = 25266,  defaultdisplay = 'dec east'},
	venus     = {radius = 6051.8, defaultdisplay = 'dec east'},
	vesta     = {radius = 260,    defaultdisplay = 'dec east'}
}
globedata[''] = globedata.earth

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

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

local function twoDigit( value )
	if ( value < 10 ) then
		value = '0' .. lang:formatNum( value )
	else
		value = lang:formatNum( value )
	end
	return value
end

local function 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

--- decimal specific functions
local function 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

--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 = displaydec(decLat, decLong, displayformat)
	else
		displaycoords = {
			displaydmsdimension(dmsLat, displayformat),
			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
		titleText = icone( url, 'GeoHack' )
	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

-- dms specific functions

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

local function _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

-- 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, _precision(val))
	end
	return convertprecision(precision)
end

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

local function dec2dms_dm(dec)
	dec = _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 = _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

local function _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

local function _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( _precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?
	elseif dmsobject.minutes then
		precision = 3 + math.max( _precision(tostring(minutes), 0 ) )
	else
		precision = math.max( _precision(tostring(degrees), 0 ) )
	end

	local decimal = factor * (degrees+(minutes+seconds/60)/60)
	return _round(decimal, precision)
end

-- main function for displaying coordinates
local function _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 dmslatitude, dmslongitude -- table (when created)
	local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams)
	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 = false -- use maplink whenever it is possible

	-- II extract coordinates from Wikitext
	if (rawlat or rawlong) then
		if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude
			makeerror({message = i18n.coordMissing, sortkey = 'A'})
			return showerrors()
		end
		latitude = parsedec(rawlat, 'latitude', globe)

		if latitude then -- if latitude is decimal
			longitude = parsedec(rawlong, 'longitude', globe) -- so should be longitude
			precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros
			if not latitude or not longitude then
				if errorstring == '' then
					makeerror({message = i18n.invalidFormat, sortkey = 'A'})
				end
				return showerrors()
			end
			dmslatitude, dmslongitude = _dec2dms(latitude, 'latitude', precision), _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 = _parsedmsstring(args.latitude, 'latitude'), _parsedmsstring(args.longitude, 'longitude')
			if not dmslatitude or not dmslongitude then
				return showerrors()
			end
			latitude, longitude = _dms2dec(dmslatitude), _dms2dec(dmslongitude)
		end
	end

	-- III extract coordinate data from Wikidata and compare them to local data
	-- supprimé

	-- 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'
	elseif 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

	-- V geodata
	-- supprimé

	-- VI final output
	local mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
	return mainstring .. notes .. showerrors()
end

function p.Coord( frame ) -- parses the strange parameters of Template:Coord before sending them to p._coord
	local args = frame:getParent().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 _coord( args )
end

--
-- réécriture du [[modèle : CoordGéog]] pour le [[modèle : Infobox CoordGéo]]
--

local function lettreDirection( lettre )
	if not lettre or lettre == '' then
		return ''
	end
	lettre = mw.ustring.upper( lettre )
	if lettre == 'S' then
		return 'S'
	elseif lettre == 'N' then
		return 'N'
	elseif lettre == 'E' then
		return 'E'
	elseif lettre == 'W' or lettre == 'O' then
		return 'W'
	else
		error( 'mauvaise lettre en entrée de la fonction « lettreDirection »' )
	end
end

local function nomDirection( lettre )
	if not lettre or lettre == '' then
		error( 'pas de lettre en entrée de la fonction « nomDirection »' )
	end
	lettre = mw.ustring.upper( lettre )
	if lettre == 'S' then
		return 'Sud'
	elseif lettre == 'N' then
		return 'Nord'
	elseif lettre == 'E' then
		return 'Est'
	elseif lettre == 'W' or lettre == 'O' then
		return 'Ouest'
	else
		error( 'mauvaise lettre en entrée de la fonction « nomDirection »' )
	end
end

local function axeDirection( lettre )
	if not lettre or lettre == '' then
		error( 'pas de lettre en entrée de la fonction « axeDirection »' )
	end
	lettre = mw.ustring.upper( lettre )
	if lettre == 'S' or lettre == 'N' then
		return 'latitude'
	elseif lettre == 'E' or lettre == 'W' or lettre == 'O' then
		return 'longitude'
	else
		error( 'mauvaise lettre en entrée de la fonction « axeDirection »' )
	end
end

local function coordGeog( coord, format )
	local split = mw.text.split( coord, '/', true )
	local rendu = format == '°'
	local retour
	if split[2] and split[2] ~= '' then -- coordonnées fournies au format D/M/S/X ou D/M//X
		retour = split[1]
		if rendu then
			retour = retour .. "°&nbsp;"
		else
			retour = retour .. "_"
		end
		retour = retour .. split[2]
		if rendu then
			retour = retour .. "′"
		end
		if split[3] and split[3] ~= '' then
			if rendu then
				retour = retour .. "&nbsp;"
				                .. mw.ustring.gsub( split[3], '%.', ',' )
				                .. "″"
			else
				retour = retour .. "_"
				                .. mw.ustring.gsub( split[3], ',', '.' )
			end
		end
		if rendu then
			retour = retour .. "&nbsp;"
			                .. nomDirection( split[4] )
		else
			retour = retour .. "_"
			                .. lettreDirection( split[4] )
		end

	else -- coordonnées fournies au format D///X
		if rendu then
			local coords = _dec2dms( mw.ustring.gsub( split[1], ',', '.' ),
			                         axeDirection( split[4] ))
			retour = coords.degrees .. "°"
			if coords.minutes ~= nil then
				retour = retour .. "&nbsp;"
				                .. coords.minutes
				                .. "′"
				if coords.seconds ~= nil then
					retour = retour .. "&nbsp;"
					                .. mw.ustring.gsub( coords.seconds, '%.', ',' )
					                .. "″"
				end
			end
			retour = retour .. "&nbsp;" .. nomDirection( coords.direction )
		else
			retour = mw.ustring.gsub( split[1], ',', '.' )
			      .. '_'
			      .. lettreDirection( split[4] )
		end
	end
	return retour
end

-- pour le [[modèle : Infobox CoordGéo]]
function p.CoordGeog( frame )
	local args = frame:getParent().args
	local latitude = args['latitude']
	local longitude = args['longitude']
	local retour = coordGeog( latitude  .. "///N", "°" )
	            .. "<br />"
	            .. coordGeog( longitude .. "///E", "°" )

	local altitude = args['altitude']
	if altitude and altitude ~= '' then
		retour = retour .. "<br />[[altitude]] : "
		                .. altitude
		                .. "&nbsp;m" -- TODO abréviation
	end

	local url = "https://tools.wmflabs.org/geohack/geohack.php?language=fr"
	         .. "&pagename="
	         .. mw.uri.encode( mw.title.getCurrentTitle().prefixedText, 'PATH' )
	         .. "&params="
	         .. coordGeog( latitude  .. "///N", "_" )
	         .. "_"
	         .. coordGeog( longitude .. "///E", "_" )
	         .. "_type:" .. args['type']

	local tmp = args['échelle']
	if tmp and tmp ~= '' then
		url = url .. "_scale:"
		          .. tmp
	end
	tmp = args['dimension']
	if tmp and tmp ~= '' then
		url = url .. "_dim:"
		          .. tmp
	end
	tmp = args['région']
	if tmp and tmp ~= '' then
		url = url .. "_region:"
		          .. tmp
	end
	tmp = args['nom du lieu']
	if tmp and tmp ~= '' then
		url = url .. "&title="
		          .. mw.uri.encode( tmp, 'PATH' )
	end

	return retour .. icone( url, "Cartes, vues aériennes et satellitaires" )
end

return p