Module:Coordinates

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

local math_mod = require( "Module:Math" )-- local JSON = require( 'Module:jf-JSON' ) -- chargé si maplink est utilisélocal p = {}--Chargement de la liste En/Au/Aux/Alocal gdatalocal success, resultat = pcall (mw.loadData, "Module:Drapeau/Data" )if success thengdata = resultatelse-- Banque de données à minima en cas de bogue dans le Module:Langue/Datagdata={}gdata.data={};gdata.data[142]={qid="Q142", label="France", genre="fs"}endlocal 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_genrelocal 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.earthlocal 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éelocal lang = mw.language.getContentLanguage()local function makecat(cat, sortkey)if type( sortkey ) == 'string' thenreturn '[[Category:' .. cat .. '|' .. sortkey .. ']]'elsereturn '[[Category:' .. cat .. ']]'endend------------------------------------------Error handling--[[ Notes:when errors occure a new error message is concatenated to errorstringan error message contains an error category with a sortkeyFor 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 textssortkeys:* 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>' endlocal errorcat = ''if mw.title.getCurrentTitle().namespace == 0 then errorcat = makecat(i18n.errorcat, args.sortkey)enderrorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messagesreturn nilendlocal function showerrors()return errorstringend-- Distance computationfunction p._distance(a, b, globe) -- calcule la [[distance orthodromique]] en kilomètres entre deux points du globeglobe = string.lower(globe or 'earth')-- check arguments and converts degreees to radianslocal latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitudeif (not latA) or (not latB) or (not longA) or (not longB) then returnerror('coordinates missing, can\'t compute distance')endif type(latA) ~= 'number' or type(latB) ~= 'number' or type(longA) ~= 'number' or type(longB) ~= 'number' thenerror('coordinates are not numeric, can\'t compute distance')endif not globe or not globedata[globe] thenreturn error('globe: ' .. globe .. 'is not supported')end-- calcul de la distance angulaire en radianslocal convratio = math.pi / 180 -- convertit en radianslatA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longBlocal 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 errorsreturn 0endlocal angle = math.acos(cosangle)-- calcul de la distance en kmlocal radius = globedata[globe].radiusreturn radius * angleendfunction p.distance(frame)local args = frame.argsreturn p._distance({latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)}, {latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)},args.globe)endlocal function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)extraparams = extraparams or ''local geohacklatitude, geohacklongitude-- format latitude and longitude for the URLif tonumber(decLat) < 0 thengeohacklatitude = tostring(-tonumber(decLat)) .. '_S'else geohacklatitude = decLat .. '_N'endif tonumber(decLong) < 0  thengeohacklongitude = tostring(-tonumber(decLong)) .. '_W'elseif globedata[globe].defaultdisplay == 'dec west' thengeohacklongitude = decLong .. '_W'elsegeohacklongitude = decLong .. '_E'end-- prepares the 'paramss=' parameterlocal geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams-- concatenate parameteres for geohackreturn 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 linklocal function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)-- geohack urllocal url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)-- displayed coordinateslocal displaycoordsif string.sub(displayformat,1,3) == 'dec' thendisplaycoords = p.displaydec(decLat, decLong, displayformat)elsedisplaycoords = {p.displaydmsdimension(dmsLat, displayformat),p.displaydmsdimension(dmsLong, displayformat),}end-- build coordinate in h-geo / h-card microformatlocal globeNodeif globe and globe ~= 'earth' thenglobeNode = mw.html.create('data'):addClass('p-globe'):attr{ value = globe }:done()endlocal coordNode = mw.html.create('')if objectname thencoordNode = mw.html.create('span'):addClass('h-card'):tag('data'):addClass('p-name'):attr{ value = objectname }:done()endcoordNode: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 linklocal 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 thenlocal 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' })endreturn inlineText .. titleTextendlocal function zoom( extraparams )local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )if zoomParam thenreturn zoomParamendlocal scale = extraparams:match( '%f[%w]scale: ?(%d+)' )if scale thenreturn math.log10( 1 / tonumber( scale ) ) * 3 + 25endlocal extraType = extraparams:match( '%f[%w]type: ?(%w+)' )if extraType thenlocal zoomType = {country = 5,state = 6,adm1st = 7,adm2nd = 8,city = 9,isle = 10,mountain = 10,waterbody = 10,airport = 12,landmark = 13,}return zoomType[ extraType ]endend--HTML builder for a geohack linklocal function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )local JSON = require( 'Module:jf-JSON' )-- displayed coordinateslocal displaycoordsif string.sub(displayformat,1,3) == 'dec' thendisplaycoords = p.displaydec(decLat, decLong, displayformat)elsedisplaycoords = {p.displaydmsdimension(dmsLat, displayformat),p.displaydmsdimension(dmsLong, displayformat),}end-- JSON for maplinklocal jsonParams = {type = 'Feature',geometry = { type ='Point',coordinates = { math_mod._round( decLong, 6 ), -- max precision in GeoJSON formatmath_mod._round( decLat, 6 )}},properties = {['marker-color'] = "228b22",}}if objectname thenjsonParams.properties.title = objectnameend-- ajout de geoshape via externaldatalocal geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )if not geoshape and displaytitle and mw.wikibase.getEntity() thengeoshape = mw.wikibase.getEntity().idendif geoshape thenjsonParams = {jsonParams,{type = 'ExternalData',service = 'geoshape',ids = geoshape,properties = {['fill-opacity'] = 0.2}}}endlocal maplink = mw.getCurrentFrame():extensionTag{name = 'maplink',content = JSON:encode( jsonParams ),args = { text = displaycoords[1] .. ", " .. displaycoords[2],zoom = zoom( extraparams ),latitude = decLat,longitude = decLong,}}-- format result depending on args["display"] (nil, "inline", "title", "inline,title")local inlineText = displayinline and maplink or ''local titleText = ''if displaytitle thenlocal htmlTitle = mw.html.create('span'):attr{ id = 'coordinates' }:addClass( displayinline and 'noprint' or nil ):wikitext( maplink )local frame = mw.getCurrentFrame()titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' })endreturn inlineText .. titleTextend-- dms specific funcionslocal function twoDigit( value )if ( value < 10 ) thenvalue = '0' .. lang:formatNum( value )elsevalue = lang:formatNum( value )endreturn valueendfunction p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dmslocal str = ''local direction = valuetable.directionlocal degrees, minutes, seconds = '', '', ''local dimensionif format == 'dms long' thendirection = i18n[direction .. 'long']elsedirection = i18n[direction]enddegrees = lang:formatNum( valuetable.degrees ) .. i18n.degreesif valuetable.minutes thenminutes = twoDigit( valuetable.minutes ) .. i18n.minutesendif valuetable.seconds thenseconds = twoDigit( valuetable.seconds ) .. i18n.secondsendreturn degrees .. minutes .. seconds .. directionendlocal function validdms(coordtable)local direction = coordtable.directionlocal degrees = coordtable.degrees or 0local minutes = coordtable.minutes or 0local seconds = coordtable.seconds or 0local dimension = coordtable.dimensionif not dimension thenif direction == 'N' or direction == 'S' thendimension = 'latitude'elseif direction == 'E' or direction == 'W' then dimension = 'longitude'elsemakeerror({message = i18n.invalidNSEW, sortkey = 'A'})return falseendendif type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' thenmakeerror({message = i18n.invalidFormat, sortkey = 'A'})return falseendif dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' thenmakeerror({message = i18n.invalidNS, sortkey = 'A'})return falseendif dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' thenmakeerror({message = i18n.invalidEW, sortkey = 'A'})return falseendif dimension == 'latitude' and degrees > 90 thenmakeerror({message = i18n.latitude90, sortkey = 'A'})return falseendif dimension == 'longitude' and degrees > 360 thenmakeerror({message = i18n.longitude360, sortkey = 'A'})return falseendif degrees < 0 or minutes < 0 or seconds < 0 thenmakeerror({message = i18n.negativeCoode, sortkey = 'A'})return falseendif minutes > 60 or seconds > 60 thenmakeerror({message = i18n.minSec60, sortkey = 'A'})return falseendif (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) thenmakeerror({message = i18n.dmIntergers, sortkey = 'A'})return falseendreturn trueendlocal function builddmsdimension(degrees, minutes, seconds, direction, dimension)-- no error checking, done in function validdmslocal dimensionobject = {}-- direction and dimension (= latitude or longitude)dimensionobject.direction = directionif dimension thendimensionobject.dimension = dimensionelseif direction == 'N' or direction == 'S' thendimensionobject.dimension = 'latitude'elseif direction == 'E' or direction == 'W' thendimensionobject.dimension = 'longitude'end-- degrees, minutes, secondsdimensionobject.degrees = tonumber(degrees)dimensionobject.minutes = tonumber(minutes)dimensionobject.seconds = tonumber(seconds)if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' endif minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' endif seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' endreturn dimensionobjectendlocal function parsedmsstring(str, dimension) -- prend une séquence et donne des noms aux paramètres -- output table: {latitude=, longitude = , direction =  }if type( str ) ~= 'string' thenreturn nilendstr = mw.ustring.gsub( mw.ustring.upper( str ), '%a+', coordParse )if not tonumber( str ) and not str:find( '/' ) and str:find( '°' ) thenlocal str2 = mw.ustring.gsub( str, '[°″′\"\'\194\160 ]+', '/' )-- avoid cases were there is degree ans seconds but no minutesif not mw.ustring.find( str, '[″"]' ) or mw.ustring.find( str, '%d[′\'][ \194\160%d]' ) thenstr = str2endendif not tonumber(str) and not string.find(str, '/') thenmakeerror({message = i18n.invalidFormat, sortkey= 'A'})return nilendargs = mw.text.split(str, '/', true)if #args > 4 thenmakeerror({message = i18n.tooManyParam, sortkey= 'A' })endlocal 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) thenreturn dimensionobjectelsereturn nilendend--- decimal specific functionsfunction p.displaydec(latitude, longitude, format)lat = lang:formatNum( latitude )long = lang:formatNum( longitude )if format == 'dec west' or  format == 'dec east' thenlocal symbolNS, symbolEW = i18n.N, i18n.Eif latitude < 0 then symbolNS = i18n.Slat = lat:sub( 2 )endif format == 'dec west' thensymbolEW = i18n.Wendif longitude < 0 then long = lang:formatNum( 360 + longitude )endreturn { lat .. i18n.degrees .. symbolNS,  long ..  i18n.degrees .. symbolEW }else return { lat, long }endendlocal function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitudedec = mw.text.trim(dec)if not dec thenreturn nilendif coordtype ~= 'latitude' and coordtype ~= 'longitude' thenmakeerror({'invalid coord type', sortkey = "A"})return nilendlocal numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zerosif not numdec then -- tries the decimal + direction formatdec = 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 endif not dec or not tonumber(dec) thenreturn nilendif direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' thenreturn decelseif direction == 'W' or direction == 'S' thenreturn '-' .. decelseif coordtype == 'latitude' thenmakeerror({message = i18n.invalidNS, sortkey = 'A'})elsemakeerror({message = i18n.invalidEW, sortkey = 'A'})endreturn nilendendif coordtype == 'latitude' and math.abs(numdec) > 90 thenmakeerror({message = i18n.latitude90 , sortkey = 'A'})return nilendif coordtype == 'longitude' and math.abs(numdec) > 360 thenmakeerror({message = i18n.longitude360 , sortkey = 'A'})return nilendreturn decend-- dms/dec conversion functionslocal function convertprecision(precision) -- converts a decimal precision like "2" into "dm"if precision >= 3 thenreturn 'dms'elseif precision >=1 thenreturn 'dm'elsereturn 'd'endendlocal function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs tablelocal precision = 0for d, val in ipairs(decs) doprecision = math.max(precision, math_mod._precision(val))endreturn convertprecision(precision)endlocal function dec2dms_d(dec)local degrees = math_mod._round( dec, 0 )return degreesendlocal function dec2dms_dm(dec)dec = math_mod._round( dec * 60, 0 )local minutes = dec % 60dec = math.floor( (dec - minutes) / 60 )local degrees = dec % 360return degrees, minutesend local function dec2dms_dms(dec)dec = math_mod._round( dec * 60 * 60, 0 )local seconds = dec % 60dec = math.floor( (dec - seconds) / 60 )local minutes = dec % 60dec = math.floor( (dec - minutes) / 60 )local degrees = dec % 360return degrees, minutes, secondsendfunction p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitudelocal degrees, minutes, seconds-- vérification du globeif not ( globe and globedata[ globe ] ) thenglobe = 'earth'end-- precisionif not precision or precision == '' thenprecision = determinedmsprec({dec})endif precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' thenreturn makeerror({sortkey = 'C'})endlocal dec = tonumber(dec)-- direction local directionif coordtype == 'latitude' then if dec < 0 thendirection = 'S'else direction = 'N'endelseif coordtype == 'longitude' thenif dec < 0 or globedata[globe].defaultdisplay == 'dec west' thendirection = 'W'else direction = 'E'endend-- conversiondec = math.abs(dec) -- les coordonnées en dms sont toujours positivesif precision == 'dms' then degrees, minutes, seconds = dec2dms_dms(dec)elseif precision == 'dm' thendegrees, minutes = dec2dms_dm(dec)elsedegrees = dec2dms_d(dec)endreturn builddmsdimension(degrees, minutes, seconds, direction)endfunction p.dec2dms(frame) -- legacy function somewhat cumbersome syntaxargs = frame.argslocal dec = args[1] if not tonumber(dec) thenmakeerror({message = i18n.invalidFormat, sortkey = 'A'})return showerrors()endlocal dirpositive = string.lower(args[2] or '')local dirnegative = string.lower(args[3] or '')local precision = string.lower(args[4] or '')local displayformat, coordtypeif dirpositive == 'n' or dirpositive == 'nord' thencoordtype = 'latitude'else coordtype = 'longitude'endif dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' thendisplayformat = 'dms long'endlocal coordobject = p._dec2dms(dec, coordtype, precision)if coordobject thenreturn p.displaydmsdimension(coordobject, displayformat) .. showerrors()elsereturn showerrors()endendfunction p._dms2dec(dmsobject) -- transforme une table degré minute secondes en nombre décimallocal direction, degrees, minutes, seconds = dmsobject.direction, dmsobject.degrees, dmsobject.minutes, dmsobject.secondslocal factor = 0local precision = 0if not minutes then minutes = 0 endif not seconds then seconds = 0 endif direction == "N" or direction == "E" thenfactor = 1elseif direction == "W" or direction == "S" thenfactor = -1elseif not direction then makeerror({message = i18n.noCardinalDirection, sortkey = 'A'})return nilelsemakeerror({message = i18n.invalidDirection, sortkey = 'A'})return nilendif dmsobject.seconds then -- vérifie la précision des données initialesprecision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?elseif dmsobject.minutes thenprecision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )elseprecision = math.max( math_mod._precision(tostring(degrees), 0 ) )endlocal decimal = factor * (degrees+(minutes+seconds/60)/60)return math_mod._round(decimal, precision)endfunction p.dms2dec(frame) -- legacy function, somewhat bizarre syntaxlocal args = frame.argsif tonumber(args[1]) then return args[1] -- coordonnées déjà en décimalelseif not args[2] thenlocal dmsobject = parsedmsstring(args[1])if dmsobject thenreturn p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/Nelsereturn showerrors()endelse return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})endend-- Wikidatalocal function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"if precision < 0.016 thenreturn 'dms'elseif precision < 1 thenreturn 'dm'elsereturn 'd'endendlocal 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:Wikidatalocal coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values-- Wikidata does not handle correctly +West longitudesif globedata[ coords.globe ] and globedata[ coords.globe ].defaultdisplay == 'dec west' thencoords.longitude = math.abs( coords.longitude )endreturn coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001)endreturn nilendlocal function wikidatacat(globe)--catbase= Atik ki jewolokalize sou latèlocal entitycat = mw.wikibase.getEntity()local basecat = 'Atik ki jewolokalize'local finalcat = {}--BADGESif entitycat then--BADGES   for i, badgeId in ipairs( entitycat.sitelinks['htwiki'].badges ) doif badgeId == 'Q17437796'  thenbasecat=string.gsub(basecat, "Atik ki jewolokalize", "Atik kalite ki jewolokalize")endif badgeId == 'Q17437798'  thenbasecat=string.gsub(basecat, "Atik ki jewolokalize", "Bon atik ki jewolokalize")endendendif globe == 'earth'  thenif entitycat and entitycat.claims  thenlocal country=entitycat.claims['P17']if not country then--pas pays à récupérerbasecat=basecat .. ' sou latè'table.insert(finalcat,basecat)else--parfois plusieurs paysfor i, paysId in ipairs( country ) do--on fait confiance au label wikidatalocal gdataone,qidif paysId.mainsnak.snaktype == 'value' thenqid=paysId.mainsnak.datavalue.value['numeric-id']gdataone=gdata.data[qid]else--Bir Tawil n'a pas de pays connuqid='?'endif gdataone ~= nil thenlocal prep=genre[gdataone['genre']]['en'] or 'en 'local thecat=basecat .. ' '..prep ..mw.wikibase.label( 'Q'.. qid)if mw.title.new('category:'..thecat).exists thentable.insert(finalcat,thecat)else--Dommage!mw.log(thecat .. ' pou kreye')endelse--pas d'id?mw.log(qid .. ' pou ajoute paramèt')endendif #finalcat == 0 then--pas pays à récupérerbasecat=basecat .. ' sou latè'table.insert(finalcat,basecat)endendelse--pas wikidatabasecat=basecat .. ' sou latè'table.insert(finalcat,basecat)endelseif globedata[globe] thenbasecat=basecat .. ' ' .. globedata[globe].trackingcattable.insert(finalcat,basecat)elsebasecat=basecat .. ' ekstrateres'table.insert(finalcat,basecat)endreturn finalcatend -- main function for displaying coordinatesfunction p._coord(args)-- I declare variablelocal 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 geohacklocal notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinateslocal wikidata = args.wikidata -- string: set to "true" if neededlocal wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoordslocal 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 valueslocal latitude, longitude, precision, dmslatitude, dmslongitude -- latitude and longitude in decimal / dmslatitude and dmslongitude: tables withdms coordslocal maplink = args.maplink and args.maplink ~= ''-- II extract coordinates from Wikitextif (rawlat or rawlong) thenif (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitudemakeerror({message = i18n.coordMissing, sortkey = 'A'})return showerrors()endlatitude = parsedec(rawlat, 'latitude', globe)if latitude then -- if latitude is decimallongitude = parsedec(rawlong, 'longitude', globe) -- so should be longitudeprecision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zerosif not latitude or not longitude thenif errorstring == '' thenmakeerror({message = i18n.invalidFormat, sortkey = 'A'})endreturn showerrors()enddmslatitude, 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 stringdmslatitude, dmslongitude = parsedmsstring(args.latitude, 'latitude'), parsedmsstring(args.longitude, 'longitude')if not dmslatitude or not dmslongitude thenreturn showerrors()endlatitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)endend-- III extract coordinate data from Wikidata and compare them to local datalocal wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecisionif wikidata == 'true' thenwikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery)if wikidatalatitude and latitude and longitude thenlocal maxdistance = tonumber(args.maxdistance) or wikidatathresholdif p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) <  maxdistance thentrackingstring = trackingstring .. makecat(i18n.sameaswikidata)elsetrackingstring = trackingstring .. makecat(i18n.notaswikidata)endendif wikidatalatitude and not latitude thenlatitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecisiondmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)trackingstring = trackingstring .. makecat(i18n.throughwikidata)endif latitude and not wikidatalatitude thenif mw.title.getCurrentTitle().namespace == 0 thentrackingstring = trackingstring .. makecat(i18n.nowikidata)endendend-- exit if stil no latitude or no longitudeif not latitude and not longitude thenreturn nil -- ne rien ajouter ici pour que l'appel à cette fonction retourne bien nil en l'absence de donnéesend-- IV best guesses for missing parameters--- globeif globe == '' thenglobe = 'earth'endif not globedata[globe] thenmakeerror({message = i18n.invalidGlobe .. globe})globe = 'earth'endif globe ~= 'earth' thenextraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en doublemaplink = falseend--- diplayformatif not displayformat or displayformat == '' thendisplayformat = globedata[globe].defaultdisplayend-- displayinline/displaytitlelocal displayinline =  string.find(displayplace, 'inline') local displaytitle = string.find(displayplace, 'title') if not displayinline and not displaytitle thendisplayinline = trueif displayplace ~= '' then makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continueendendif displaytitle and mw.title.getCurrentTitle().namespace == 0 then--local cattoappend=globedata[globe].trackingcat--Récupération des badgeslocal cats=wikidatacat(globe)for i, cat in ipairs( cats ) dotrackingstring = trackingstring .. makecat(cat)endend-- V geodatalocal geodata = ''if latitude and longitude thenlocal latstring, longstring = tostring(latitude), tostring(longitude)local primary = ''local frame = mw.getCurrentFrame()local geodataparams = {[1] = latstring, [2] = longstring, [3] = extraparams }if displaytitle thengeodataparams[4] = 'primary'endif objectname thengeodataparams.name = objectnameendgeodata = frame:callParserFunction('#coordinates', geodataparams )if string.find(geodata, 'error') then -- the only error that has not been caught yet is primary keygeodata = ''makeerror({sortkey='D'})endend-- VI final outputlocal mainstring = ''if args.formatitle thenif displaytitle thenmainstring = mainstring .. buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, args.formatitle, false, true, objectname,extraparams )endif displayinline thenmainstring = mainstring .. buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, true, false, objectname,extraparams )endelseif maplink thenmainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )elsemainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )endendreturn mainstring .. notes .. trackingstring .. geodata .. showerrors()endfunction p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coordlocal args = frame.argslocal numericargs = {}for i, j in ipairs(args) doargs[i] = mw.text.trim(j)if type(i) == 'number' and args[i] ~= '' thentable.insert(numericargs, args[i])endendif #numericargs %2 == 1 then -- if the number of args is odd, the last one provides formatting parametersargs.extraparams = numericargs[#numericargs]if #numericargs == 1 and tonumber(numericargs[1]) thenmakeerror({message = i18n.coordMissing, sortkey = 'A'})return showerrors()endtable.remove(numericargs)endfor i, j in ipairs(numericargs) doif i <= (#numericargs / 2) thenif not args.latitude thenargs.latitude = jelseargs.latitude =args.latitude .. '/' .. jendelseif not args.longitude thenargs.longitude = jelseargs.longitude = args.longitude .. '/' .. jendendendif string.find(args.latitude or '', 'E') or string.find(args.latitude or '', 'W') thenargs.latitude, args.longitude = args.longitude, args.latitudeendreturn p._coord(args)endfunction p.Coord(frame)return p.coord(frame)endfunction p.latitude(frame) -- helper function pour infobox, à déprécierlocal args = frame.argslocal latitude  = frame.args[1]if latitude and mw.text.trim(latitude) ~= '' thenreturn latitudeelseif frame.args['wikidata'] == 'true' thenlocal lat, long = wikidatacoords()return latendendfunction p.longitude(frame) -- helper function pour infobox, à déprécierlocal args = frame.argslocal longitude = frame.args[1]if longitude and mw.text.trim(longitude) ~= '' thenreturn longitudeelseif frame.args['wikidata'] == 'true' thenlocal lat, long = wikidatacoords()return longendendreturn p