Module:Coordinates : Différence entre versions
(Recopie du code de https://en.wikipedia.org/w/index.php?title=Module:Coordinates&action=edit) |
(Copier-coller de wikipedia) |
||
Ligne 1 : | Ligne 1 : | ||
− | + | 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 default_zoom = 13 | ||
− | local | + | 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 | + | |
− | local | + | 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 | end | ||
− | + | local function showerrors() | |
− | local function | + | return errorstring |
− | return | ||
end | 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 | 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 | 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 | end | ||
− | + | local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams) | |
− | + | extraparams = extraparams or '' | |
− | + | local geohacklatitude, geohacklongitude | |
− | local function | + | -- format latitude and longitude for the URL |
− | local | + | if tonumber(decLat) < 0 then |
− | for | + | 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 | end | ||
− | return | + | -- 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") .. | ||
+ | "¶ms=" .. geohackparams .. | ||
+ | (objectname and ("&title=" .. mw.uri.encode(objectname)) or "") | ||
end | 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 | |
− | local | + | if string.sub(displayformat,1,3) == 'dec' then |
− | if | + | 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 | 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 | end | ||
+ | |||
+ | return inlineText .. titleText | ||
end | end | ||
− | + | local function zoom( extraparams ) | |
− | + | local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' ) | |
− | + | if zoomParam then | |
− | + | return zoomParam | |
− | + | end | |
− | + | ||
− | local function | + | local scale = extraparams:match( '%f[%w]scale: ?(%d+)' ) |
− | local | + | if scale then |
− | if | + | return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25) |
− | |||
− | return | ||
end | 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 | ||
+ | end | ||
− | + | --HTML builder for a geohack link | |
− | + | local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams ) | |
− | + | -- displayed coordinates | |
− | + | local displaycoords | |
− | + | if string.sub(displayformat,1,3) == 'dec' then | |
− | local | + | displaycoords = p.displaydec(decLat, decLong, displayformat) |
− | |||
− | if | ||
− | |||
− | |||
else | else | ||
− | + | displaycoords = { | |
+ | p.displaydmsdimension(dmsLat, displayformat), | ||
+ | p.displaydmsdimension(dmsLong, displayformat), | ||
+ | } | ||
+ | end | ||
+ | |||
+ | -- JSON for maplink | ||
+ | local jsonParams = { | ||
+ | type = 'Feature', | ||
+ | geometry = { | ||
+ | type ='Point', | ||
+ | coordinates = { | ||
+ | math_mod._round( decLong, 6 ), -- max precision in GeoJSON format | ||
+ | math_mod._round( decLat, 6 ) | ||
+ | } | ||
+ | }, | ||
+ | properties = { | ||
+ | ['marker-color'] = "228b22", | ||
+ | } | ||
+ | } | ||
+ | if objectname then | ||
+ | jsonParams.properties.title = objectname | ||
+ | end | ||
+ | -- ajout de geoshape via externaldata | ||
+ | local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' ) | ||
+ | if not geoshape and displaytitle and mw.wikibase.getEntity() then | ||
+ | geoshape = mw.wikibase.getEntity().id | ||
+ | end | ||
+ | if geoshape then | ||
+ | jsonParams = { | ||
+ | jsonParams, | ||
+ | { | ||
+ | type = 'ExternalData', | ||
+ | service = 'geoshape', | ||
+ | ids = geoshape, | ||
+ | properties = { | ||
+ | ['fill-opacity'] = 0.2 | ||
+ | } | ||
+ | } | ||
+ | } | ||
end | end | ||
− | local | + | local maplink = mw.getCurrentFrame():extensionTag{ |
− | local | + | name = 'maplink', |
− | if | + | content = mw.text.jsonEncode( jsonParams ), |
− | + | args = { | |
− | + | text = displaycoords[1] .. ", " .. displaycoords[2], | |
− | + | zoom = zoom( extraparams ) or default_zoom, | |
− | + | latitude = decLat, | |
+ | longitude = decLong, | ||
+ | } | ||
+ | } | ||
+ | |||
+ | -- format result depending on args["display"] (nil, "inline", "title", "inline,title") | ||
+ | local inlineText = displayinline and maplink or '' | ||
+ | local titleText = '' | ||
+ | if displaytitle then | ||
+ | local htmlTitle = mw.html.create('span') | ||
+ | :attr{ id = 'coordinates' } | ||
+ | :addClass( displayinline and 'noprint' or nil ) | ||
+ | :wikitext( maplink ) | ||
+ | local frame = mw.getCurrentFrame() | ||
+ | titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } ) | ||
end | end | ||
+ | |||
+ | return inlineText .. titleText | ||
+ | end | ||
− | + | -- dms specific funcions | |
− | |||
− | |||
− | |||
− | + | local function twoDigit( value ) | |
− | + | if ( value < 10 ) then | |
− | + | value = '0' .. lang:formatNum( value ) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
else | else | ||
− | + | value = lang:formatNum( value ) | |
− | |||
− | |||
− | |||
end | end | ||
− | + | return value | |
− | return | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | function p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dms | |
− | + | local str = '' | |
− | + | local direction = valuetable.direction | |
− | local | + | local degrees, minutes, seconds = '', '', '' |
− | + | local dimension | |
− | local | ||
− | + | if format == 'dms long' then | |
− | + | direction = i18n[direction .. 'long'] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
else | else | ||
− | + | direction = i18n[direction] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees | |
− | + | ||
− | + | if valuetable.minutes then | |
− | + | minutes = twoDigit( valuetable.minutes ) .. i18n.minutes | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | |||
end | end | ||
− | + | if valuetable.seconds then | |
− | + | seconds = twoDigit( valuetable.seconds ) .. i18n.seconds | |
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | + | return degrees .. minutes .. seconds .. direction | |
− | |||
− | |||
end | end | ||
− | + | local function validdms(coordtable) | |
− | + | local direction = coordtable.direction | |
− | + | local degrees = coordtable.degrees or 0 | |
− | local function | + | local minutes = coordtable.minutes or 0 |
− | local | + | 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 | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | if | + | if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then |
− | + | makeerror({message = i18n.invalidFormat, sortkey = 'A'}) | |
+ | return false | ||
end | end | ||
− | if | + | |
− | + | if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then | |
+ | makeerror({message = i18n.invalidNS, sortkey = 'A'}) | ||
+ | return false | ||
end | end | ||
− | if | + | if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then |
− | + | makeerror({message = i18n.invalidEW, sortkey = 'A'}) | |
+ | return false | ||
end | end | ||
− | if | + | |
− | + | if dimension == 'latitude' and degrees > 90 then | |
+ | makeerror({message = i18n.latitude90, sortkey = 'A'}) | ||
+ | return false | ||
end | end | ||
− | if | + | |
− | + | if dimension == 'longitude' and degrees > 360 then | |
+ | makeerror({message = i18n.longitude360, sortkey = 'A'}) | ||
+ | return false | ||
end | end | ||
− | if | + | |
− | + | if degrees < 0 or minutes < 0 or seconds < 0 then | |
+ | makeerror({message = i18n.negativeCoode, sortkey = 'A'}) | ||
+ | return false | ||
end | end | ||
− | if | + | |
− | + | 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 | end | ||
− | if | + | 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 | end | ||
− | if | + | |
− | + | -- degrees, minutes, seconds | |
+ | dimensionobject.degrees = tonumber(degrees) | ||
+ | dimensionobject.minutes = tonumber(minutes) | ||
+ | dimensionobject.seconds = tonumber(seconds) | ||
+ | if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' end | ||
+ | if minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' end | ||
+ | if seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' end | ||
+ | return dimensionobject | ||
+ | end | ||
+ | |||
+ | function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres | ||
+ | -- output table: { latitude=, longitude = , direction = } | ||
+ | if type( str ) ~= 'string' then | ||
+ | return nil | ||
end | end | ||
− | if | + | 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 | end | ||
− | if | + | if not tonumber(str) and not string.find(str, '/') then |
− | + | makeerror({message = i18n.invalidFormat, sortkey= 'A'}) | |
+ | return nil | ||
end | end | ||
− | if | + | args = mw.text.split(str, '/', true) |
− | table. | + | 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 | ||
+ | end | ||
− | return | + | --- 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 | end | ||
− | |||
− | |||
− | + | local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude | |
− | + | dec = mw.text.trim(dec) | |
− | + | if not dec then | |
− | local | + | 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 | + | 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 | 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 | else | ||
− | + | return 'd' | |
end | end | ||
+ | end | ||
− | return | + | 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 | 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 | + | 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 |
− | if | + | 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 | end | ||
− | + | if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then | |
− | + | return makeerror({sortkey = 'C'}) | |
− | |||
− | return | ||
− | |||
− | |||
end | end | ||
− | + | local dec = tonumber(dec) | |
− | if | + | |
− | if | + | -- 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 | ||
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 | else | ||
− | + | degrees = dec2dms_d(dec) | |
end | end | ||
− | + | return builddmsdimension(degrees, minutes, seconds, direction) | |
− | return | ||
end | end | ||
− | --[ | + | function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax |
− | + | args = frame.args | |
− | + | local dec = args[1] | |
− | ] | + | if not tonumber(dec) then |
− | local | + | makeerror({message = i18n.invalidFormat, sortkey = 'A'}) |
− | local | + | return showerrors() |
− | local | + | end |
− | + | local dirpositive = string.lower(args[2] or '') | |
− | + | local dirnegative = string.lower(args[3] or '') | |
− | + | local precision = string.lower(args[4] or '') | |
− | + | local displayformat, coordtype | |
− | + | ||
− | + | if dirpositive == 'n' or dirpositive == 'nord' then | |
− | + | coordtype = 'latitude' | |
+ | else | ||
+ | coordtype = 'longitude' | ||
+ | end | ||
+ | if dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' then | ||
+ | displayformat = 'dms long' | ||
end | end | ||
− | + | local coordobject = p._dec2dms(dec, coordtype, precision) | |
− | + | if coordobject then | |
− | + | return p.displaydmsdimension(coordobject, displayformat) .. showerrors() | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
else | else | ||
− | + | return showerrors() | |
− | return | ||
end | end | ||
− | + | end | |
− | local | + | |
− | + | 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 | 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 | end | ||
− | return | + | |
+ | local decimal = factor * (degrees+(minutes+seconds/60)/60) | ||
+ | return math_mod._round(decimal, precision) | ||
end | 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 | |
− | local | + | elseif not args[2] then |
− | if | + | local dmsobject = p._parsedmsstring(args[1]) |
− | + | if dmsobject then | |
− | + | return p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/N | |
− | + | else | |
− | + | local coordType | |
− | + | if args[1]:match( '[NS]' ) then | |
− | + | coordType = 'latitude' | |
− | + | elseif args[1]:match( '[EWO]') then | |
− | + | coordType = 'longitude' | |
− | elseif | ||
− | |||
end | end | ||
− | + | if coordType then | |
− | + | local result = parsedec( args[1], coordType, args.globe or 'earth' ) | |
− | + | if result then | |
+ | return result | ||
+ | end | ||
+ | end | ||
+ | return showerrors() | ||
end | end | ||
+ | else | ||
+ | return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])}) | ||
end | end | ||
− | if | + | end |
− | return | + | |
+ | -- 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 | else | ||
− | return '' | + | return 'd' |
end | end | ||
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 ) | |
− | return | + | end |
+ | return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001) | ||
+ | end | ||
+ | return nil | ||
end | end | ||
− | |||
− | |||
− | + | local function wikidatacat(globe) | |
+ | --catbase= Article géolocalisé sur Terre | ||
+ | local entitycat = mw.wikibase.getEntity() | ||
+ | |||
+ | local basecat = 'Article géolocalisé' | ||
+ | local finalcat = {} | ||
+ | --BADGES | ||
+ | if entitycat then | ||
+ | --BADGES | ||
+ | for i, badgeId in ipairs( entitycat.sitelinks['frwiki'].badges ) do | ||
+ | if badgeId == 'Q17437796' then | ||
+ | basecat=string.gsub(basecat, "Article géolocalisé", "Article de qualité géolocalisé") | ||
+ | end | ||
+ | if badgeId == 'Q17437798' then | ||
+ | basecat=string.gsub(basecat, "Article géolocalisé", "Bon article géolocalisé") | ||
+ | end | ||
+ | end | ||
+ | end | ||
− | + | if globe == 'earth' then | |
− | + | if entitycat and entitycat.claims then | |
− | + | local country=entitycat.claims['P17'] | |
− | + | if not country then | |
− | + | --pas pays à récupérer | |
− | + | basecat=basecat .. ' sur Terre' | |
− | + | table.insert(finalcat,basecat) | |
− | + | else | |
− | + | --parfois plusieurs pays | |
− | + | for i, paysId in ipairs( country ) do | |
− | + | --on fait confiance au label wikidata | |
− | + | local gdataone,qid | |
− | + | ||
− | + | if paysId.mainsnak.snaktype == 'value' then | |
− | + | qid=paysId.mainsnak.datavalue.value['numeric-id'] | |
− | + | gdataone=gdata.data[qid] | |
− | + | else | |
− | end | + | --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! | |
− | elseif | + | 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 | else | ||
− | + | basecat=basecat .. ' extraterrestre' | |
+ | table.insert(finalcat,basecat) | ||
end | end | ||
+ | return finalcat | ||
end | 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 | ||
+ | local maplink = true -- 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 = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe) | ||
+ | latitude, longitude = tonumber(latitude), tonumber(longitude) | ||
+ | else -- if latitude is not decimal try to parse it as a dms string | ||
+ | dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude') | ||
+ | if not dmslatitude or not dmslongitude then | ||
+ | return showerrors() | ||
+ | end | ||
+ | latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude) | ||
+ | end | ||
+ | end | ||
− | + | -- III extract coordinate data from Wikidata and compare them to local data | |
− | + | local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision | |
− | + | if wikidata == 'true' then | |
− | + | wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery) | |
− | + | ||
− | + | if wikidatalatitude and latitude and longitude then | |
− | + | local maxdistance = tonumber(args.maxdistance) or wikidatathreshold | |
− | + | if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) < maxdistance then | |
− | + | trackingstring = trackingstring .. makecat(i18n.sameaswikidata) | |
− | + | else | |
− | + | trackingstring = trackingstring .. makecat(i18n.notaswikidata) | |
− | + | end | |
− | + | end | |
− | + | if wikidatalatitude and not latitude then | |
− | + | latitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision | |
− | + | dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe) | |
− | + | trackingstring = trackingstring .. makecat(i18n.throughwikidata) | |
− | + | end | |
− | + | ||
− | + | if latitude and not wikidatalatitude then | |
− | + | if mw.title.getCurrentTitle().namespace == 0 then | |
− | + | trackingstring = trackingstring .. makecat(i18n.nowikidata) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
end | end | ||
end | end | ||
− | |||
− | |||
− | |||
− | + | -- exit if stil no latitude or no longitude | |
− | + | if not latitude and not longitude then | |
− | return | + | 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 | end | ||
− | + | if not globedata[globe] then | |
− | + | makeerror({message = i18n.invalidGlobe .. globe}) | |
− | + | globe = 'earth' | |
end | end | ||
− | + | if globe ~= 'earth' then | |
− | + | extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double | |
− | -- | + | maplink = false |
− | |||
end | end | ||
− | + | ||
− | + | --- diplayformat | |
− | + | if not displayformat or displayformat == '' then | |
− | + | displayformat = globedata[globe].defaultdisplay | |
end | end | ||
− | + | ||
− | + | -- displayinline/displaytitle | |
− | + | local displayinline = string.find(displayplace, 'inline') | |
− | + | local displaytitle = string.find(displayplace, 'title') | |
+ | if not displayinline and not displaytitle then | ||
+ | displayinline = true | ||
+ | if displayplace ~= '' then | ||
+ | makeerror({sortkey = 'C'}) --error if display not empty, but not not a major error, continue | ||
+ | end | ||
end | end | ||
− | if | + | 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 | ||
− | + | ||
− | |||
− | |||
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'}) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | if | ||
− | |||
− | |||
− | |||
end | end | ||
− | + | end | |
− | + | -- VI final output | |
− | + | local mainstring = '' | |
+ | if maplink then | ||
+ | mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams ) | ||
else | else | ||
− | + | mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams ) | |
end | end | ||
+ | |||
+ | return mainstring .. notes .. trackingstring .. geodata .. showerrors() | ||
end | end | ||
− | --[[ | + | function p.coord(frame) -- parrses the strange parameters of Template:Coord before sending them to p.coord |
− | + | 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 | |
− | function | + | 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 | ||
− | + | end | |
− | + | function p.longitude(frame) -- helper function pour infobox, à déprécier | |
− | + | local args = frame.args | |
− | + | local longitude = frame.args[1] | |
− | + | if longitude and mw.text.trim(longitude) ~= '' then | |
− | + | return longitude | |
− | + | elseif frame.args['wikidata'] == 'true' then | |
− | + | local lat, long = wikidatacoords() | |
+ | return long | ||
end | end | ||
− | |||
end | end | ||
− | return | + | |
+ | return p |
Version actuelle datée du 7 août 2020 à 18:21
Sommaire
Utilisation
Fonctions exportables :
coordinates.coord(frame)
– Fonction qui permet insérer des coordonnées géographiques dans un article ; il insère en même temps un lien vers l’outil « GeoHack », qui donne accès à de nombreux outils de vues satellites, cartes, etc. comme Google Maps par exemple.p.dec2dms(frame)
– Fonction permettant de convertir des coordonnées exprimées dans le système décimal dans le système sexagésimalp.dms2dec(frame)
– Fonction permettant de convertir des coordonnées dans l'autre sens (du système sexagésimal dans le système décimal)p.latitude(frame)
– récupère la latitude de coordonnées (locale ou stockées sur Wikidata). Surtout utile pour les infoboîtesp.longitude(frame)
– récupère la longitude de coordonnées (locale ou stockées sur Wikidata). Surtout utile pour les infoboîtesp.distance(frame)
– calcule la distance entre deux points dont on précise les latitudes et longitudes.
p._coord(args)
– similaire à p.coord, mais à utiliser depuis un autre module Luap._dms2dec(dmsobject)
– similaire à p.dms2dec, mais à utiliser depuis un autre module Luap._dec2dms(coordtype, precision)
– similaire à p.dec2dms, mais à utiliser depuis un autre module Luap._distance(a, b, globe)
– calcule la distance entre deux points dont on précise les latitudes et longitudesp._parsedmsstring(str, dimension)
- créé une table dms à partir d'une chaîne de type "48/22/16/W".
fonctions internes
makeerror
- gestion des erreursmakeerror
- buildHTML (construit le HTML pour la fonction p.coord()displaydmsdimension
- transforme une table dms contenant degrees, minutes, seconds, directions et coordtype (=latitude ou longitude) en une chaîne du type 48° 29' 32 " Nvaliddms
- vérifie qu'une table dms est valide (direction valides et coordtype valide, valeurs autorisées pour les dégrés, minutes et secondes)builddmsdimension
- crée une table dmsdisplaydec
- transforme une latitude et une longitude décimal en chaîne du type "34.294, 12.321"parsedec
- analyse et valide des coordonnées décimalesconvertprcision
- transforme la précision trouvée par module:Math.precision en "d", "dm" ou "dms"convertwikidataprecision
- transforme la précision indiquée par les données Wikidata en "d", "dm" ou "dms"determinedmsprec
- calcule le degré de précision le plus adaptées pour des coordonnées décimales donnéesdec2dms_d
- convertit une coordonnées décimale en coordonnées dms de niveau de précision degrédec2dms_dm
- convertit une coordonnées décimale en coordonnées dms de niveau de précision minutedec2dms_dms
- convertit une coordonnées décimale en coordonnées dms de niveau de précision secondewikidatacoords
- récupère des coordonnées sur Wikidata
variables internes
- globedata : table des planètes prises en charge par geohack, en indiquant le rayon (pour calculer les distances), la catégorie de maintenance, et le format d'affichage des coordonnées par défaut
- wikidatathreshold : distance maximum par défaut entre les coordonnées données sur Wikipédia et celles de Wikidata. Au-delà du seuil, la page est catégorisée dans Catégorie:Page avec coordonnées différentes sur Wikidata
Modules externes et autres éléments dont ce module a besoin pour fonctionner :
Module:Math
– Pour gérer les arrondis et la précision.Module:Wikidata
– Pour récupérer les coordonnées sur Wikidata
Exemples basiques
Il existe deux moyens pour renseigner dans un article des coordonnées
- À partir des coordonnées décimales
{{#Invoke:Coordinates | coord |43.651234|-79.383333}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found- la latitude(43.651234) et la longitude (-79.383333) de Toronto sont renseignés sous formes décimale
- À partir des coordonnées sexagésimales
{{#Invoke:Coordinates | coord |43|29|4|N|79|23|0|W}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found- la latitude(43/29/4/N) et la longitude (79/23/0/W) de Toronto sont renseignés sous forme d'un quadruplet Heure/Minute/Seconde/Sens représentant le calcul sous forme de minutes d'arc.
{{#Invoke:Coordinates | coord |43/29/4/N|79/23/0/W}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found- la latitude(43/29/4/N) et la longitude (79/23/0/W) de Toronto sont renseignés directement
- À partir des coordonnées géographiques issu de Wikidata seulement pour les coordonnées principale d'un article
{{#Invoke:Coordinates | coord }}
Le format de sortie est par défaut le format d'entrée mais on peut le changer en passant un argument supplémentaire sous la forme |format=xxx
- dms pour un affichage degrée/minute/seconde
{{#Invoke:Coordinates | coord |43.651234|-79.383333|format=dms}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found
- dms long pour un affichage degrée/minute/seconde avec l'orientation en toute lettres
{{#Invoke:Coordinates | coord |43.651234|-79.383333|format=dms long}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found
- dec pour un affichage degrée/minute/seconde
{{#Invoke:Coordinates | coord |43|29|4|N|79|23|0|W|format=dec}}
: Erreur Lua : callParserFunction: function "#coordinates" was not found
Options de la fonction p.coord
Option de la fonction p.coord (utilisable depuis Lua)
- latitude =
- longitude =
- globe = (voir la table globedata pour la liste
- format = format d'affichage : dms, dec, dms long (affiche la direction en entier), dec east (calcule la latitude dans la direction Est et l'affiche, dms west calcule la longitude dans la direction Ouest et l'affiche
- displaytitle = mettre sur "true" si l'on veut afficher les coordonnées dans le titre
- formattitle = si l'on souhaite un format particulier pour les coordonnées de titre
- wikidata = mettre sur "true" lorsque l'on souhaite récupérer les coordonnées Wikidata
- wikidataquery = pour effectuer une requête Wikidata personnalisée (voir Module:Wikidata)
Exemples détaillés
Entrée en écriture décimale
Code | Résultat | Résultat |format=dec |
Résultat |format=dms |
Résultat |format=dms long |
Notes |
---|---|---|---|---|---|
{{#invoke:Coordinates|coord|43.651234|-79.383333}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, entrée en écriture décimale |
{{#invoke:Coordinates|coord|-33.856111|151.1925}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Sydney, entrée en écriture décimale |
{{#invoke:Coordinates|coord|43.65|-79.38}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, avec moins de précision |
{{#invoke:Coordinates|coord|43.6500|-79.3800}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto,avec des zéros supplémentaires |
{{#invoke:Coordinates|coord|43.651234|N|79.383333|W}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, écriture N/W plutôt que +/- |
Entrée en écriture sexagésimale
Code | Résultat | Résultat |format=dec |
Résultat |format=dms |
Résultat |format=dms long |
Notes |
---|---|---|---|---|---|
{{#invoke:Coordinates|coord|43|29|N|79|23|W}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, avec degrés et minutes |
{{#invoke:Coordinates|coord|43|29|4|N|79|23|0|W}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, avec degrés, minutes et secondes |
{{#invoke:Coordinates|coord|43|29|4.5|N|79|23|0.5|W}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, avec degrés, minutes, secondes et fractions de secondes |
{{#invoke:Coordinates|coord|43/29/N|79/23/W}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto avec les coordonnées en un seul champ séparé par le symbole / |
Paramètres de format pour {{GeoTemplate}}
Ici, c'est le lien qui est modifié : il faut cliquer dessus pour voir les différences
Code | Résultat | Notes |
---|---|---|
{{#invoke:Coordinates|coord|43.65|-79.38}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, appel basique |
{{#invoke:Coordinates|coord|43.65|-79.38|scale:3000000}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, avec une échelle au pays scale:N avec N un nombre sans espace (par défaut 300000) |
{{#invoke:Coordinates|coord|40.6892|-74.0445|dim:100}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Statue de la Liberté avec un diamètre de 100 m autre moyen pour afficher une échelle adaptée. |
{{#invoke:Coordinates|coord|43.65|-79.38|type:city}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, en mentionnant comme type de structure une ville de grande taille scale:N avec N un nombre sans espace (par défaut 300000) |
{{#invoke:Coordinates|coord|43.65|-79.38|region:CA_type:city}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, en mentionnant deux paramètres. |
{{#invoke:Coordinates|coord|43.65|-79.38|region:CA}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, en mentionnant le Canada comme pays utile pour sélectionner des fonds cartographiques appropriés à la région. |
{{#invoke:Coordinates|coord|9.7|-20.0|globe:moon}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | cratère Copernic sur la Lune utile pour sélectionner des fonds cartographiques appropriés à la région. |
{{#invoke:Coordinates|coord|43.65|-79.38|name=Toronto}} | Erreur Lua : callParserFunction: function "#coordinates" was not found | Toronto, en présisant non pas le titre de la page mais le nom de l'article utile pour des pages avec homonymies. |
Affichage dans la barre de titre
Il est possible de renseigner les coordonnées en haut de la page en précisant l'argument |display=
- {{#invoke:Coordinates|coord|43.65|-79.38|display=inline}} : Affiche uniquement les coordonnées dans le corps du texte
- C'est le comportement par défaut
- {{#invoke:Coordinates|coord|43.65|-79.38|display=title}} : Affiche uniquement les coordonnées dans la barre de titre
- {{#invoke:Coordinates|coord|43.65|-79.38|display=inline,title}} : Affiche les coordonnées dans le corps du texte et dans la barre de titre
Ce modèle « catégorise » la page qui l’emploie dans la catégorie article géolocalisé.
Pour un affichage différent entre les coordonnées dans le texte et celui dans le titre, on peut utiliser l'argument |formatitle
- {{#invoke:Coordinates|coord|43.65|-79.38|display=inline,title|format=dec|formatitle=dms}} : Affiche les coordonnées dans le corps du texte en décimal et dans la barre de titre au format sexagésimal
Gestion des erreurs
Si les paramètres sont mal renseignés ou incorrects, une balise est affiché à coté des coordonnées précisant le type d'erreur.
- Exemple d'appel erronés
- {{#invoke:Coordinates|coord|2843.65|-79.38}} : Coordonnées : orientation de longitude invalide, devrait être "E" ou "W"
Ce modèle « catégorise » la page qui l’emploie dans la catégorie Page avec des balises de coordonnées mal formées.
Utilisation des sous-fonctions
Conversion décimal vers sexagésimal
Permet de convertir une des deux coordonnées (latitude ou longitude) d'un système vers l'autre
{{#Invoke:Coordinates | dec2dms | donnée | direction positive | direction négative | précision}}
- donnée : le nombre décimal
- direction positive : la direction (N pour la latitude / E pour la longitude)
- direction négative : la direction (S pour la latitude / O pour la longitude)
- précision : D , DM ou DMS
- Exemple
{{#invoke:Coordinates|dec2dms|43.651234|N|S|DMS}}
: 43° 39′ 04″ N{{#invoke:Coordinates|dec2dms|43.651234|Nord|Sud|DM}}
: 43° 39′ N
Conversion sexagésimal vers décimal
Permet de convertir une des deux coordonnées (latitude ou longitude) d'un système vers l'autre
{{#Invoke:Coordinates | dms2dec | direction | degrés | minutes | secondes}}
- direction : la direction (N/S/E/O)
- degrés ,minutes ,secondes : la donnée exprimés sous forme de mesure d'arc
- Exemple
{{#invoke:Coordinates|dms2dec|N|43|29|4}}
: 43.48444{{#invoke:Coordinates|dms2dec|N|43|29}}
: 43.483
{{#Invoke:Coordinates | dms2dec | direction | degrés | minutes | secondes}}
- Exemple
{{#invoke:Coordinates|dms2dec|43/29/4/N}}
: 43.48444{{#invoke:Coordinates|dms2dec|43/29/N}}
: 43.483
Voir aussi
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 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 : ' .. 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") ..
"¶ms=" .. geohackparams ..
(objectname and ("&title=" .. mw.uri.encode(objectname)) or "")
end
--HTML builder for a geohack link
local function buildHTML(decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams)
-- geohack url
local url = geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams)
-- displayed coordinates
local displaycoords
if string.sub(displayformat,1,3) == 'dec' then
displaycoords = p.displaydec(decLat, decLong, displayformat)
else
displaycoords = {
p.displaydmsdimension(dmsLat, displayformat),
p.displaydmsdimension(dmsLong, displayformat),
}
end
-- build coordinate in h-geo / h-card microformat
local globeNode
if globe and globe ~= 'earth' then
globeNode = mw.html.create('data')
:addClass('p-globe')
:attr{ value = globe }
:done()
end
local coordNode = mw.html.create('')
if objectname then
coordNode = mw.html.create('span')
:addClass('h-card')
:tag('data')
:addClass('p-name')
:attr{ value = objectname }
:done()
end
coordNode
:tag('span')
:addClass('h-geo')
:addClass('geo-' .. string.sub(displayformat,1,3))
:tag('data')
:addClass('p-latitude')
:attr{ value = decLat }
:wikitext( displaycoords[1] )
:done()
:wikitext(", ")
:tag('data')
:addClass('p-longitude')
:attr{ value = decLong }
:wikitext( displaycoords[2] )
:done()
:node( globeNode )
:done()
-- buid GeoHack link
local root = mw.html.create('span')
:addClass('plainlinks nourlexpansion')
:attr('title', i18n.tooltip)
:wikitext('[' .. url )
:node(coordNode)
:wikitext("]")
:done()
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
local inlineText = displayinline and tostring(root) or ''
local titleText = ''
if displaytitle then
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:addClass( displayinline and 'noprint' or nil )
:node( root )
local frame = mw.getCurrentFrame()
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
end
return inlineText .. titleText
end
local function zoom( extraparams )
local zoomParam = extraparams:match( '%f[%w]zoom: ?(%d+)' )
if zoomParam then
return zoomParam
end
local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
if scale then
return math.floor(math.log10( 1 / tonumber( scale ) ) * 3 + 25)
end
local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
if extraType then
local zoomType = {
country = 5,
state = 6,
adm1st = 7,
adm2nd = 8,
city = 9,
isle = 10,
mountain = 10,
waterbody = 10,
airport = 12,
landmark = 13,
}
return zoomType[ extraType ]
end
end
--HTML builder for a geohack link
local function buildMaplinkHTML( decLat, decLong, dmsLat, dmsLong, globe, displayformat, displayinline, displaytitle, objectname, extraparams )
-- displayed coordinates
local displaycoords
if string.sub(displayformat,1,3) == 'dec' then
displaycoords = p.displaydec(decLat, decLong, displayformat)
else
displaycoords = {
p.displaydmsdimension(dmsLat, displayformat),
p.displaydmsdimension(dmsLong, displayformat),
}
end
-- JSON for maplink
local jsonParams = {
type = 'Feature',
geometry = {
type ='Point',
coordinates = {
math_mod._round( decLong, 6 ), -- max precision in GeoJSON format
math_mod._round( decLat, 6 )
}
},
properties = {
['marker-color'] = "228b22",
}
}
if objectname then
jsonParams.properties.title = objectname
end
-- ajout de geoshape via externaldata
local geoshape = extraparams:match( '%f[%w]geoshape: ?(Q%d+)' )
if not geoshape and displaytitle and mw.wikibase.getEntity() then
geoshape = mw.wikibase.getEntity().id
end
if geoshape then
jsonParams = {
jsonParams,
{
type = 'ExternalData',
service = 'geoshape',
ids = geoshape,
properties = {
['fill-opacity'] = 0.2
}
}
}
end
local maplink = mw.getCurrentFrame():extensionTag{
name = 'maplink',
content = mw.text.jsonEncode( jsonParams ),
args = {
text = displaycoords[1] .. ", " .. displaycoords[2],
zoom = zoom( extraparams ) or default_zoom,
latitude = decLat,
longitude = decLong,
}
}
-- format result depending on args["display"] (nil, "inline", "title", "inline,title")
local inlineText = displayinline and maplink or ''
local titleText = ''
if displaytitle then
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:addClass( displayinline and 'noprint' or nil )
:wikitext( maplink )
local frame = mw.getCurrentFrame()
titleText = frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
end
return inlineText .. titleText
end
-- dms specific funcions
local function twoDigit( value )
if ( value < 10 ) then
value = '0' .. lang:formatNum( value )
else
value = lang:formatNum( value )
end
return value
end
function p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dms
local str = ''
local direction = valuetable.direction
local degrees, minutes, seconds = '', '', ''
local dimension
if format == 'dms long' then
direction = i18n[direction .. 'long']
else
direction = i18n[direction]
end
degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees
if valuetable.minutes then
minutes = twoDigit( valuetable.minutes ) .. i18n.minutes
end
if valuetable.seconds then
seconds = twoDigit( valuetable.seconds ) .. i18n.seconds
end
return degrees .. minutes .. seconds .. direction
end
local function validdms(coordtable)
local direction = coordtable.direction
local degrees = coordtable.degrees or 0
local minutes = coordtable.minutes or 0
local seconds = coordtable.seconds or 0
local dimension = coordtable.dimension
if not dimension then
if direction == 'N' or direction == 'S' then
dimension = 'latitude'
elseif direction == 'E' or direction == 'W' then
dimension = 'longitude'
else
makeerror({message = i18n.invalidNSEW, sortkey = 'A'})
return false
end
end
if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
return false
end
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then
makeerror({message = i18n.invalidNS, sortkey = 'A'})
return false
end
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then
makeerror({message = i18n.invalidEW, sortkey = 'A'})
return false
end
if dimension == 'latitude' and degrees > 90 then
makeerror({message = i18n.latitude90, sortkey = 'A'})
return false
end
if dimension == 'longitude' and degrees > 360 then
makeerror({message = i18n.longitude360, sortkey = 'A'})
return false
end
if degrees < 0 or minutes < 0 or seconds < 0 then
makeerror({message = i18n.negativeCoode, sortkey = 'A'})
return false
end
if minutes > 60 or seconds > 60 then
makeerror({message = i18n.minSec60, sortkey = 'A'})
return false
end
if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then
makeerror({message = i18n.dmIntergers, sortkey = 'A'})
return false
end
return true
end
local function builddmsdimension(degrees, minutes, seconds, direction, dimension)
-- no error checking, done in function validdms
local dimensionobject = {}
-- direction and dimension (= latitude or longitude)
dimensionobject.direction = direction
if dimension then
dimensionobject.dimension = dimension
elseif direction == 'N' or direction == 'S' then
dimensionobject.dimension = 'latitude'
elseif direction == 'E' or direction == 'W' then
dimensionobject.dimension = 'longitude'
end
-- degrees, minutes, seconds
dimensionobject.degrees = tonumber(degrees)
dimensionobject.minutes = tonumber(minutes)
dimensionobject.seconds = tonumber(seconds)
if degrees and not dimensionobject.degrees then dimensionobject.degrees = 'error' end
if minutes and not dimensionobject.minutes then dimensionobject.minutes = 'error' end
if seconds and not dimensionobject.seconds then dimensionobject.seconds = 'error' end
return dimensionobject
end
function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres
-- output table: { latitude=, longitude = , direction = }
if type( str ) ~= 'string' then
return nil
end
str = mw.ustring.gsub( mw.ustring.upper( str ), '%a+', coordParse )
if not tonumber( str ) and not str:find( '/' ) and str:find( '°' ) then
local str2 = mw.ustring.gsub( str, '[°″′\"\'\194\160 ]+', '/' )
-- avoid cases were there is degree ans seconds but no minutes
if not mw.ustring.find( str, '[″"]' ) or mw.ustring.find( str, '%d[′\'][ \194\160%d]' ) then
str = str2
end
end
if not tonumber(str) and not string.find(str, '/') then
makeerror({message = i18n.invalidFormat, sortkey= 'A'})
return nil
end
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
numdec = tonumber( dec )
elseif direction == 'W' or direction == 'S' then
dec = '-' .. dec
numdec = tonumber( dec )
else
if coordtype == 'latitude' then
makeerror({message = i18n.invalidNS, sortkey = 'A'})
else
makeerror({message = i18n.invalidEW, sortkey = 'A'})
end
return nil
end
end
if coordtype == 'latitude' and math.abs(numdec) > 90 then
makeerror({message = i18n.latitude90 , sortkey = 'A'})
return nil
end
if coordtype == 'longitude' and math.abs(numdec) > 360 then
makeerror({message = i18n.longitude360 , sortkey = 'A'})
return nil
end
return dec
end
-- dms/dec conversion functions
local function convertprecision(precision) -- converts a decimal precision like "2" into "dm"
if precision >= 3 then
return 'dms'
elseif precision >=1 then
return 'dm'
else
return 'd'
end
end
local function determinedmsprec(decs) -- returns the most precision for a dec2dms conversion, depending on the most precise value in the decs table
local precision = 0
for d, val in ipairs(decs) do
precision = math.max(precision, math_mod._precision(val))
end
return convertprecision(precision)
end
local function dec2dms_d(dec)
local degrees = math_mod._round( dec, 0 )
return degrees
end
local function dec2dms_dm(dec)
dec = math_mod._round( dec * 60, 0 )
local minutes = dec % 60
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
return degrees, minutes
end
local function dec2dms_dms(dec)
dec = math_mod._round( dec * 60 * 60, 0 )
local seconds = dec % 60
dec = math.floor( (dec - seconds) / 60 )
local minutes = dec % 60
dec = math.floor( (dec - minutes) / 60 )
local degrees = dec % 360
return degrees, minutes, seconds
end
function p._dec2dms(dec, coordtype, precision, globe) -- coordtype: latitude or longitude
local degrees, minutes, seconds
-- vérification du globe
if not ( globe and globedata[ globe ] ) then
globe = 'earth'
end
-- precision
if not precision or precision == '' then
precision = determinedmsprec({dec})
end
if precision ~= 'd' and precision ~= 'dm' and precision ~= 'dms' then
return makeerror({sortkey = 'C'})
end
local dec = tonumber(dec)
-- direction
local direction
if coordtype == 'latitude' then
if dec < 0 then
direction = 'S'
else
direction = 'N'
end
elseif coordtype == 'longitude' then
if dec < 0 or globedata[globe].defaultdisplay == 'dec west' then
direction = 'W'
else
direction = 'E'
end
end
-- conversion
dec = math.abs(dec) -- les coordonnées en dms sont toujours positives
if precision == 'dms' then
degrees, minutes, seconds = dec2dms_dms(dec)
elseif precision == 'dm' then
degrees, minutes = dec2dms_dm(dec)
else
degrees = dec2dms_d(dec)
end
return builddmsdimension(degrees, minutes, seconds, direction)
end
function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax
args = frame.args
local dec = args[1]
if not tonumber(dec) then
makeerror({message = i18n.invalidFormat, sortkey = 'A'})
return showerrors()
end
local dirpositive = string.lower(args[2] or '')
local dirnegative = string.lower(args[3] or '')
local precision = string.lower(args[4] or '')
local displayformat, coordtype
if dirpositive == 'n' or dirpositive == 'nord' then
coordtype = 'latitude'
else
coordtype = 'longitude'
end
if dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' then
displayformat = 'dms long'
end
local coordobject = p._dec2dms(dec, coordtype, precision)
if coordobject then
return p.displaydmsdimension(coordobject, displayformat) .. showerrors()
else
return showerrors()
end
end
function p._dms2dec(dmsobject) -- transforme une table degré minute secondes en nombre décimal
local direction, degrees, minutes, seconds = dmsobject.direction, dmsobject.degrees, dmsobject.minutes, dmsobject.seconds
local factor = 0
local precision = 0
if not minutes then minutes = 0 end
if not seconds then seconds = 0 end
if direction == "N" or direction == "E" then
factor = 1
elseif direction == "W" or direction == "S" then
factor = -1
elseif not direction then
makeerror({message = i18n.noCardinalDirection, sortkey = 'A'})
return nil
else
makeerror({message = i18n.invalidDirection, sortkey = 'A'})
return nil
end
if dmsobject.seconds then -- vérifie la précision des données initiales
precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ?
elseif dmsobject.minutes then
precision = 3 + math.max( math_mod._precision(tostring(minutes), 0 ) )
else
precision = math.max( math_mod._precision(tostring(degrees), 0 ) )
end
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return math_mod._round(decimal, precision)
end
function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax
local args = frame.args
if tonumber(args[1]) then
return args[1] -- coordonnées déjà en décimal
elseif not args[2] then
local dmsobject = p._parsedmsstring(args[1])
if dmsobject then
return p._dms2dec(dmsobject) -- coordonnées sous la fore 23/22/N
else
local coordType
if args[1]:match( '[NS]' ) then
coordType = 'latitude'
elseif args[1]:match( '[EWO]') then
coordType = 'longitude'
end
if coordType then
local result = parsedec( args[1], coordType, args.globe or 'earth' )
if result then
return result
end
end
return showerrors()
end
else
return p._dms2dec({direction = args[1], degrees = tonumber(args[2]), minutes = tonumber(args[3]), seconds = tonumber(args[4])})
end
end
-- Wikidata
local function convertwikidataprecision(precision) -- converts a decima like "0.1" into "dm"
if precision < 0.016 then
return 'dms'
elseif precision < 1 then
return 'dm'
else
return 'd'
end
end
local function wikidatacoords(query)
query = query or {property = 'p625'}
query.formatting = 'raw'
local wd = require('Module:Wikidata')
local claim = wd.getClaims(query)
if claim and claim[1] then -- redundant but more robust in case of a change in the code of Module:Wikidata
local coords = wd.formatSnak(claim[1].mainsnak) -- todo: check for special values
-- Wikidata does not handle correctly +West longitudes
if globedata[ coords.globe ] and globedata[ coords.globe ].defaultdisplay == 'dec west' then
coords.longitude = math.abs( coords.longitude )
end
return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001)
end
return nil
end
local function wikidatacat(globe)
--catbase= Article géolocalisé sur Terre
local entitycat = mw.wikibase.getEntity()
local basecat = 'Article géolocalisé'
local finalcat = {}
--BADGES
if entitycat then
--BADGES
for i, badgeId in ipairs( entitycat.sitelinks['frwiki'].badges ) do
if badgeId == 'Q17437796' then
basecat=string.gsub(basecat, "Article géolocalisé", "Article de qualité géolocalisé")
end
if badgeId == 'Q17437798' then
basecat=string.gsub(basecat, "Article géolocalisé", "Bon article géolocalisé")
end
end
end
if globe == 'earth' then
if entitycat and entitycat.claims then
local country=entitycat.claims['P17']
if not country then
--pas pays à récupérer
basecat=basecat .. ' sur Terre'
table.insert(finalcat,basecat)
else
--parfois plusieurs pays
for i, paysId in ipairs( country ) do
--on fait confiance au label wikidata
local gdataone,qid
if paysId.mainsnak.snaktype == 'value' then
qid=paysId.mainsnak.datavalue.value['numeric-id']
gdataone=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
local maplink = true -- 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 = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
latitude, longitude = tonumber(latitude), tonumber(longitude)
else -- if latitude is not decimal try to parse it as a dms string
dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude')
if not dmslatitude or not dmslongitude then
return showerrors()
end
latitude, longitude = p._dms2dec(dmslatitude), p._dms2dec(dmslongitude)
end
end
-- III extract coordinate data from Wikidata and compare them to local data
local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
if wikidata == 'true' then
wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery)
if wikidatalatitude and latitude and longitude then
local maxdistance = tonumber(args.maxdistance) or wikidatathreshold
if p._distance({latitude = latitude, longitude= longitude}, {latitude = wikidatalatitude, longitude= wikidatalongitude}, wikidataglobe) < maxdistance then
trackingstring = trackingstring .. makecat(i18n.sameaswikidata)
else
trackingstring = trackingstring .. makecat(i18n.notaswikidata)
end
end
if wikidatalatitude and not latitude then
latitude, longitude, globe, precision = wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe)
trackingstring = trackingstring .. makecat(i18n.throughwikidata)
end
if latitude and not wikidatalatitude then
if mw.title.getCurrentTitle().namespace == 0 then
trackingstring = trackingstring .. makecat(i18n.nowikidata)
end
end
end
-- exit if stil no latitude or no longitude
if not latitude and not longitude then
return nil -- ne rien ajouter ici pour que l'appel à cette fonction retourne bien nil en l'absence de données
end
-- IV best guesses for missing parameters
--- globe
if globe == '' then
globe = 'earth'
end
if not globedata[globe] then
makeerror({message = i18n.invalidGlobe .. globe})
globe = 'earth'
end
if globe ~= 'earth' then
extraparams = extraparams .. '_globe:' .. globe -- pas de problème si le globe est en double
maplink = false
end
--- diplayformat
if not displayformat or displayformat == '' then
displayformat = globedata[globe].defaultdisplay
end
-- displayinline/displaytitle
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 maplink then
mainstring = buildMaplinkHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
else
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams )
end
return mainstring .. notes .. trackingstring .. geodata .. showerrors()
end
function p.coord(frame) -- 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