Aller au contenu

Module:CoordinatesFR

Cette page est semi-protégée.
Une page de Wikipédia, l'encyclopédie libre.

 Documentation[créer] [purger]
local math_mod = require( "Module:Math" )
local p = {}

--Chargement de la liste En/Au/Aux/A
local gdata
local success, resultat = pcall (mw.loadData, "Module:Drapeau/Data" )
if success then
	gdata = resultat
else
	-- Banque de données à minima en cas de bogue dans le Module:Langue/Data
	gdata={}
	gdata.data={};
	gdata.data[142]={qid="Q142", label="France", genre="fs"}
end

local i18n = {
	N = 'N',
	Nlong = 'Nord',
	W = 'O',
	Wlong = 'Ouest',
	E = 'E',
	Elong = 'Est',
	S = 'S',
	Slong = 'Sud',
	degrees = '° ',
	minutes = '′ ',
	seconds = '″ ',
	geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr',
	tooltip = 'Cartes, vues aériennes, etc.',
	errorcat = 'Page avec des balises de coordonnées mal formées',
	sameaswikidata = 'Page avec coordonnées similaires sur Wikidata',
	notaswikidata = 'Page avec coordonnées différentes sur Wikidata',
	nowikidata = 'Page sans coordonnées Wikidata',
	throughwikidata = 'Page géolocalisée par Wikidata',
	invalidFormat = 'format invalide',                                          -- 'invalid coordinate format',
	invalidNSEW = 'orientation invalide, devrait être "N", "S", "E" or "W"',    -- 'invalid direction should be "N", "S", "E" or "W"',
	invalidNS = 'orientation de latitude invalide, devrait être "N" ou "S"',    -- 'could not find latitude direction (should be N or S)',
	invalidEW = 'orientation de longitude invalide, devrait être "E" ou "W"',   -- 'could not find longitude direction (should be W or E) ',
	noCardinalDirection = 'orientation cardinale non trouvée',                  -- 'no cardinal direction found in coordinates',
	invalidDirection = 'direction invalide',                                    -- 'invalid direction',
	latitude90 = 'latitude > 90',
	longitude360 = 'longitude > 360',
	minSec60 = 'minutes ou secondes > 60',
	negativeCoode = 'en format dms les degrés doivent être positifs',           -- 'dms coordinates should be positive',
	dmIntergers = 'degrés et minutes doivent être des nombres entiers',         -- 'degrees and minutes should be integers',
	tooManyParam = 'trop de paramètres pour la latitude ou la longitude',       -- 'too many parameters for coordinates',
	coordMissing = 'latitude ou longitude absente',                             -- 'latitude or longitude missing',
	invalidGlobe = 'globe invalide : ',                                         -- 'invalid globe:',
}
local coordParse = {
	NORTH = 'N',
	NORD = 'N',
	EAST = 'E',
	EST = 'E',
	WEST = 'W',
	O = 'W',
	OUEST = 'W',
	SOUTH = 'S',
	SUD = 'S',
}

--Aide:Fonction_genre
local genre = {
	ms =    {le="le ",  du="du ",    de="du ",  au="au ",  en="au "},
	msa =   {le="l'",   du="de l'",  de="d'",   au="à l'", en="en "},
	msi =   {le="",     du="de ",    de="de ",  au="à ",   en="à "},
	msia =  {le="",     du="d'",     de="d'",   au="à ",   en="à "},
	msiae = {le="",     du="d'",     de="d'",   au="à ",   en="en "},
	fs =    {le="la ",  du="de la ", de="de ",  au="à la ",en="en "},
	fsa =   {le="l'",   du="de l'",  de="d'",   au="à l'", en="en "},
	fsi =   {le="",     du="de ",    de="de ",  au="à ",   en="à "},
	fsia =  {le="",     du="d'",     de="d'",   au="à ",   en="à "},
	mp =    {le="les ", du="des ",   de="des ", au="aux ", en="aux "},
	fp =    {le="les ", du="des ",   de="des ", au="aux ", en="aux "}
}

local globedata = 	{
	--[[ 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 se seuil (en kilomètes), une catégorie de maintenance est ajoutée
local lang = mw.language.getContentLanguage()

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 : ' .. 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


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

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
	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)
	lat = lang:formatNum( latitude )
	long = lang:formatNum( longitude )
	
	if format == 'dec west' or  format == 'dec east' then
		local symbolNS, symbolEW = i18n.N, i18n.E
		if latitude < 0 then 
			symbolNS = i18n.S
			lat = lat:sub( 2 )
		end
		if format == 'dec west' then
			symbolEW = i18n.W
		end
		if longitude < 0 then 
			long = lang:formatNum( 360 + longitude )
		end
		
		return { lat .. i18n.degrees .. symbolNS,  long ..  i18n.degrees .. symbolEW }
		
	else 
		return { lat, long }
	end
end


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
			return dec
		elseif direction == 'W' or direction == 'S' then
			return '-' .. dec
		else
			if coordtype == 'latitude' then
				makeerror({message = i18n.invalidNS, sortkey = 'A'})
			else
				makeerror({message = i18n.invalidEW, sortkey = 'A'})
			end
			return nil
		end
	end

	if coordtype == 'latitude' and math.abs(numdec) > 90 then
		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
	args = frame.args
	local dec = args[1] 
	if not tonumber(dec) then
		makeerror({message = i18n.invalidFormat, sortkey = 'A'})
		return showerrors()
	end
	local precision = string.lower(args[4] or '')
	local displayformat, coordtype
	
	if args[2] == 'N' or args[2] == 'Nord' then
		coordtype = 'latitude'
	else 
		coordtype = 'longitude'
	end
	if args[2] == 'Nord' or args[2] == 'Est' or args[3] == 'Ouest' or args[3] == '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 = parsedmsstring(args[1])
		if dmsobject then
			return p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/N
		else
			return showerrors()
		end
	else 
		return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})
	end
end

-- Wikidata
local function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"
	if precision < 0.016 then
		return 'dms'
	elseif precision < 1 then
		return 'dm'
	else
		return 'd'
	end
end

local function wikidatacoords(query)
	query = query or {property = 'p625'}
	query.formatting = 'raw'
	local wd = require('Module:Wikidata')
	local claim = wd.getClaims(query)
	if claim and claim[1] then -- redundant but more robust in case of a change in the code of Module:Wikidata
		local coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values
		-- Wikidata does not handle correctly +West longitudes
		if globedata[ coords.globe ] and globedata[ coords.globe ].defaultdisplay == 'dec west' then
			coords.longitude = math.abs( coords.longitude )
		end
		return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001)
	end
	return nil
end


local function wikidatacat(globe)
	--catbase= Article géolocalisé sur Terre
	local entitycat = mw.wikibase.getEntityObject()
	
	local basecat = 'Article géolocalisé'
	local finalcat = {}
	--BADGES
	if entitycat then
		--BADGES
	   	for i, badgeId in ipairs( entitycat.sitelinks['frwiki'].badges ) do
			if badgeId == 'Q17437796'  then
				basecat=string.gsub(basecat, "Article géolocalisé", "Article de qualité géolocalisé")
			end
			if badgeId == 'Q17437798'  then
				basecat=string.gsub(basecat, "Article géolocalisé", "Bon article géolocalisé")
			end
		end
	end

	if globe == 'earth'  then
		if entitycat and entitycat.claims  then
			local country=entitycat.claims['P17']
			if not country then
				--pas pays à récupérer
				basecat=basecat .. ' sur Terre'
				table.insert(finalcat,basecat)
			else
				--parfois plusieurs pays
				for i, paysId in ipairs( country ) do
					--on fait confiance au label wikidata
					local gdataone,qid
					
					if paysId.mainsnak.snaktype == 'value' then
						qid=paysId.mainsnak.datavalue.value['numeric-id']
						gdataone=gdata.data[qid]
					else
						--Bir Tawil n'a pas de pays connu
						qid='?'
					end
					if gdataone ~= nil then
						local prep=genre[gdataone['genre']]['en'] or 'en '
						local thecat=basecat .. ' '..prep ..mw.wikibase.label( 'Q'.. qid)
						if mw.title.new('category:'..thecat).exists then
							table.insert(finalcat,thecat)
						else
							--Dommage!
							mw.log(thecat .. ' à créer')
						end
					else
						--pas d'id?
						mw.log(qid .. ' à paramétrer')
					end
				end
				if #finalcat == 0 then
					--pas pays à récupérer
					basecat=basecat .. ' sur Terre'
					table.insert(finalcat,basecat)
				end
			end
		else
			--pas wikidata
			basecat=basecat .. ' sur Terre'
			table.insert(finalcat,basecat)
		end
	elseif globedata[globe] then
		basecat=basecat .. ' ' .. globedata[globe].trackingcat
		table.insert(finalcat,basecat)
	else
		basecat=basecat .. ' extraterrestre'
		table.insert(finalcat,basecat)
	end
	return finalcat
end

 -- main function for displaying coordinates
function p._coord(args)

	-- I declare variable	
	local displayformat = args.format -- string: one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west'
	local displayplace = string.lower(args.display or 'inline') --string: one of 'inline', 'title' or 'inline,title' 
	local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack
	local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates
	local wikidata = args.wikidata -- string: set to "true" if needed
	local wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoords
	local dmslatitude, dmslongitude -- table (when created)	
	local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams)
 	local trackingstring = '' -- tracking cats except error cats (already in errorstring)
 	local rawlat, rawlong = args.latitude, args.longitude
 	if rawlat == '' then rawlat = nil end
 	if rawlong == '' then rawlong = nil end
 	local globe = string.lower( args.globe or extraparams:match('globe:(%a+)') or '' ) -- string: see the globedata table for accepted values
	local latitude, longitude, precision, dmslatitude, dmslongitude -- latitude and longitude in decimal / dmslatitude and dmslongitude: tables withdms coords
	
	-- 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 = parsedmsstring(args.latitude, 'latitude'), 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
	end
	
	--- diplayformat
	if not displayformat or displayformat == '' then
		displayformat = globedata[globe].defaultdisplay
	end
	
	-- displayinline/displaytitle
	local displayinline =  string.find(displayplace, 'inline') 
	local displaytitle = string.find(displayplace, 'title') 
	if not displayinline and not displaytitle then
		displayinline = true
		if displayplace ~= '' then 
			makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue
		end
	end
	if displaytitle and mw.title.getCurrentTitle().namespace == 0 then
		--local cattoappend=globedata[globe].trackingcat
		--Récupération des badges
		local cats=wikidatacat(globe)
		for i, cat in ipairs( cats ) do
			trackingstring = trackingstring .. makecat(cat)
		end
	
	end
	
-- V geodata
	local geodata = ''
	if latitude and longitude then
		local latstring, longstring = tostring(latitude), tostring(longitude)
		local primary = ''

		local frame = mw.getCurrentFrame()
		local geodataparams = {[1] = latstring, [2] = longstring, [3] = extraparams }
		if displaytitle then
			geodataparams[4] = 'primary'
		end
		if objectname then
			geodataparams.name = objectname
		end
		geodata = frame:callParserFunction('#coordinates', geodataparams )
		if string.find(geodata, 'error') then -- the only error that has not been caught yet is primary key
			geodata = ''
			makeerror({sortkey='D'})
		end
	end
-- VI final output
	local mainstring = ''
	if args.formatitle then
		if displaytitle then
			mainstring = mainstring .. buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, args.formatitle, false, true, objectname,extraparams )
		end
		if displayinline then
			mainstring = mainstring .. buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, true, false, objectname,extraparams )
		end
	else
		mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
	end
	
	return mainstring .. notes .. trackingstring .. geodata .. showerrors()
end

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

	if #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parameters
		args.extraparams = numericargs[#numericargs]
		if #numericargs == 1 and tonumber(numericargs[1]) then
			makeerror({message = i18n.coordMissing, sortkey = 'A'})
			return showerrors()
		end
		table.remove(numericargs)
	end
	for i, j in ipairs(numericargs) do
		if i <= (#numericargs / 2) then
			if not args.latitude then
				args.latitude = j
			else
				args.latitude =	args.latitude .. '/' .. j
			end
		else
			if not args.longitude then
				args.longitude = j
			else
				args.longitude = args.longitude .. '/' .. j
			end
		end
	end

	if string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') then
		args.latitude, args.longitude = args.longitude, args.latitude
	end
	return p._coord(args)
end

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


function p.latitude(frame) -- helper function pour infobox, à déprécier
	local args = frame.args
	local latitude  = frame.args[1]
	if latitude and mw.text.trim(latitude) ~= '' then
		return latitude
	elseif frame.args['wikidata'] == 'true' then
		local lat, long = wikidatacoords()
		return lat
	end
end
function p.longitude(frame) -- helper function pour infobox, à déprécier
	local args = frame.args
	local longitude = frame.args[1]
	if longitude and mw.text.trim(longitude) ~= '' then
		return longitude
	elseif frame.args['wikidata'] == 'true' then
		local lat, long = wikidatacoords()
		return long
	end
end


return p
pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy