« Module:Coordinates » : différence entre les versions
Apparence
Modèle:Infobox>Orlodrim "latitude" n'utilise pas wikidata par défaut |
m 153 versions importées |
||
| (62 versions intermédiaires par 11 utilisateurs non affichées) | |||
| Ligne 1 : | Ligne 1 : | ||
local math_mod = require( "Module:Math" ) | local math_mod = require( "Module:Math" ) | ||
local p = {} | local p = {} | ||
local NBSP = '\194\160' -- espace insécable (code UTF-8 sur deux octets) | |||
local i18n = { | local i18n = { | ||
N = ' N', | N = 'N', | ||
Nlong = ' | Nlong = 'nord', | ||
W = ' O', | W = 'O', | ||
Wlong = ' | Wlong = 'ouest', | ||
E = ' E', | E = 'E', | ||
Elong = ' | Elong = 'est', | ||
S = ' S', | S = 'S', | ||
Slong = ' | Slong = 'sud', | ||
degrees = '° ', | degrees = '°' .. NBSP, | ||
minutes = ' | minutes = '′' .. NBSP, | ||
seconds = '″ ', | seconds = '″' .. NBSP, | ||
geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr', | geohackurl = 'http://tools.wmflabs.org/geohack/geohack.php?language=fr', | ||
tooltip = 'Cartes, vues aériennes | tooltip = 'Cartes, vues aériennes, etc.', | ||
errorcat = ' | errorcat = 'Page avec des balises de coordonnées mal formées', | ||
sameaswikidata = 'Page avec coordonnées similaires sur Wikidata', | sameaswikidata = 'Page avec coordonnées similaires sur Wikidata', | ||
notaswikidata = 'Page avec coordonnées différentes sur Wikidata', | notaswikidata = 'Page avec coordonnées différentes sur Wikidata', | ||
nowikidata = 'Page sans coordonnées Wikidata', | |||
throughwikidata = 'Page géolocalisée par 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' .. NBSP .. ': ', -- 'invalid globe:', | |||
} | |||
local coordParse = { | |||
NORTH = 'N', | |||
NORD = 'N', | |||
EAST = 'E', | |||
EST = 'E', | |||
WEST = 'W', | |||
O = 'W', | |||
OUEST = 'W', | |||
SOUTH = 'S', | |||
SUD = 'S', | |||
} | } | ||
| Ligne 26 : | Ligne 55 : | ||
radius in kilometers (especially imprecise for non spheric bodies) | radius in kilometers (especially imprecise for non spheric bodies) | ||
defaultdisplay is currently disabled, activate it ? | defaultdisplay is currently disabled, activate it ? | ||
]]-- | ]]-- | ||
ariel = {radius = 580, defaultdisplay = 'dec east', trackingcat = ' | ariel = {radius = 580, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
callisto = {radius = 2410, defaultdisplay = 'dec | callisto = {radius = 2410, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
ceres = {radius = 470, defaultdisplay = 'dec east', trackingcat = ' | ceres = {radius = 470, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
deimos = {radius = 7, defaultdisplay = 'dec | charon = {radius = 1214, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
dione = {radius = 560, defaultdisplay = 'dec | deimos = {radius = 7, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
enceladus = {radius = 255, defaultdisplay = 'dec | dione = {radius = 560, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
ganymede = {radius = | enceladus = {radius = 255, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = ' | ganymede = {radius = 2634, defaultdisplay = 'dec west', trackingcat = 'sur Ganymède'}, | ||
europa = {radius = 1561, defaultdisplay = 'dec east', trackingcat = ' | earth = {radius = 6371, defaultdisplay = 'dms', trackingcat = 'sur Terre'}, | ||
hyperion = {radius = 140, defaultdisplay = 'dec | europa = {radius = 1561, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
iapetus = {radius = 725, defaultdisplay = 'dec | hyperion = {radius = 140, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
['io'] = {radius = 1322, defaultdisplay = 'dec | iapetus = {radius = 725, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
jupiter = {radius = 68911, defaultdisplay = 'dec east', trackingcat = ' | ['io'] = {radius = 1322, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
mars = {radius = 3389.5, defaultdisplay = 'dec east', trackingcat = | jupiter = {radius = 68911, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
mercury = {radius = 2439.7, defaultdisplay = 'dec | mars = {radius = 3389.5, defaultdisplay = 'dec east', trackingcat = 'sur Mars'}, | ||
mimas = {radius = 197, defaultdisplay = 'dec | mercury = {radius = 2439.7, defaultdisplay = 'dec west', trackingcat = 'sur Mercure'}, | ||
miranda = {radius = 335, defaultdisplay = 'dec east', trackingcat = ' | mimas = {radius = 197, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
moon = {radius = 1736, defaultdisplay = 'dec | miranda = {radius = 335, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
neptune = {radius = 24553, defaultdisplay = 'dec east', trackingcat = ' | moon = {radius = 1736, defaultdisplay = 'dec', trackingcat = 'sur la Lune'}, | ||
oberon = {radius = 761, defaultdisplay = 'dec east', trackingcat = ' | neptune = {radius = 24553, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
phoebe = {radius = 110, defaultdisplay = 'dec | oberon = {radius = 761, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
phobos = {radius = 11, defaultdisplay = 'dec east', trackingcat = ' | phoebe = {radius = 110, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
rhea = {radius = 765, defaultdisplay = 'dec | phobos = {radius = 11, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
saturn = {radius = 58232, defaultdisplay = 'dec east', trackingcat = ' | pluto = {radius = 1185, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
titan = {radius = 2575.5, defaultdisplay = 'dec west', trackingcat = ' | rhea = {radius = 765, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
tethys = {radius = 530, defaultdisplay = 'dec | saturn = {radius = 58232, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
titania = {radius = 394, defaultdisplay = 'dec east', trackingcat = ' | titan = {radius = 2575.5, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
triton = {radius = 1353, defaultdisplay = 'dec | tethys = {radius = 530, defaultdisplay = 'dec west', trackingcat = 'extraterrestre'}, | ||
umbriel = {radius = 584, defaultdisplay = 'dec east', trackingcat = ' | titania = {radius = 394, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
uranus = {radius = 25266, defaultdisplay = 'dec east', trackingcat = ' | triton = {radius = 1353, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
venus = {radius = 6051.8, defaultdisplay = 'dec east', trackingcat = ' | umbriel = {radius = 584, defaultdisplay = 'dec east', trackingcat = 'extraterrestre'}, | ||
vesta = {radius = 260, defaultdisplay = 'dec east', trackingcat = ' | 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 | local wikidatathreshold = 10 -- si la distance entre coordonnées Wikipédia et Wikidata dépasse ce seuil (en kilomètres), une catégorie de maintenance est ajoutée | ||
local lang = mw.language.getContentLanguage() | |||
local default_zoom = 13 | |||
local function makecat(cat, sortkey) | local function makecat(cat, sortkey) | ||
return '[[Category:' .. cat .. '|' .. | if type( sortkey ) == 'string' then | ||
return '[[Category:' .. cat .. '|' .. sortkey .. ']]' | |||
else | |||
return '[[Category:' .. cat .. ']]' | |||
end | |||
end | end | ||
| Ligne 79 : | Ligne 116 : | ||
* B: invalid globe | * B: invalid globe | ||
* C: something wrong with other parameters | * C: something wrong with other parameters | ||
* D: more than one primary coord | |||
]]-- | ]]-- | ||
errorstring = '' | local errorstring = '' | ||
local function makeerror(args) | local function makeerror(args) | ||
local errormessage = '' | local errormessage = '' | ||
if args.message then | if args.message then | ||
errormessage = '<strong class="error">Coordonnées: ' .. args.message .. '</strong>' | errormessage = '<strong class="error"> Coordonnées' .. NBSP .. ': ' .. args.message .. '</strong>' | ||
end | end | ||
local errorcat = '' | local errorcat = '' | ||
if mw.title.getCurrentTitle().namespace == 0 then | if mw.title.getCurrentTitle().namespace == 0 then | ||
errorcat = makecat(i18n.errorcat, args.sortkey) | errorcat = makecat(i18n.errorcat, args.sortkey) | ||
end | end | ||
| Ligne 106 : | Ligne 144 : | ||
globe = string.lower(globe or 'earth') | globe = string.lower(globe or 'earth') | ||
-- check arguments and converts degreees to radians | -- check arguments and converts degreees to radians | ||
local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude | local latA, latB, longA, longB = a.latitude, b.latitude, a.longitude, b.longitude | ||
| Ligne 115 : | Ligne 153 : | ||
error('coordinates are not numeric, can\'t compute distance') | error('coordinates are not numeric, can\'t compute distance') | ||
end | end | ||
if not globe or not globedata[globe] then | |||
-- distance angulaire en radians | return error('globe: ' .. globe .. 'is not supported') | ||
end | |||
-- calcul de la distance angulaire en radians | |||
local convratio = math.pi / 180 -- convertit en radians | local convratio = math.pi / 180 -- convertit en radians | ||
latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB | latA, latB, longA, longB = convratio * latA, convratio * latB, convratio * longA, convratio * longB | ||
local | 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 | ||
return | |||
end | end | ||
local angle = math.acos(cosangle) | |||
radius = globedata[globe].radius | -- calcul de la distance en km | ||
local radius = globedata[globe].radius | |||
return radius * angle | return radius * angle | ||
end | end | ||
| Ligne 134 : | Ligne 173 : | ||
local args = frame.args | local args = frame.args | ||
return p._distance( | return p._distance( | ||
{latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)}, | {latitude = tonumber(args.latitude1), longitude = tonumber(args.longitude1)}, | ||
{latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)}, | {latitude = tonumber(args.latitude2), longitude = tonumber(args.longitude2)}, | ||
args.globe) | args.globe) | ||
end | end | ||
local function geoHackUrl(decLat, decLong, globe, displayformat, objectname, extraparams) | |||
local function | |||
extraparams = extraparams or '' | 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 | end | ||
if tonumber(decLong) < 0 then | if tonumber(decLong) < 0 then | ||
geohacklongitude = tostring(-tonumber(decLong)) .. '_W' | geohacklongitude = tostring(-tonumber(decLong)) .. '_W' | ||
elseif globedata[globe].defaultdisplay == 'dec west' then | |||
geohacklongitude = decLong .. '_W' | |||
else | |||
geohacklongitude = decLong .. '_E' | |||
end | end | ||
-- prepares the 'paramss=' parameter | -- prepares the 'paramss=' parameter | ||
local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams | local geohackparams = geohacklatitude .. '_' .. geohacklongitude .. '_' ..extraparams | ||
-- concatenate parameteres for geohack | -- concatenate parameteres for geohack | ||
return i18n.geohackurl .. | |||
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") .. | "&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") .. | ||
"¶ms=" .. geohackparams .. | "¶ms=" .. geohackparams .. | ||
(objectname and ("&title=" .. mw.uri.encode(objectname)) or "") | (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() | :done() | ||
end | |||
coordNode | |||
:wikitext( | :tag('span') | ||
:addClass('h-geo') | |||
:addClass('geo-' .. string.sub(displayformat, 1, 3)) | |||
:tag('data') | |||
:addClass('p-latitude') | |||
:attr{ value = decLat } | |||
:wikitext( displaycoords[1] ) | |||
:done() | :done() | ||
: | :wikitext(", ") | ||
:tag('data') | |||
:addClass('p-longitude') | |||
:attr{ value = decLong } | |||
:wikitext( displaycoords[2] ) | |||
:wikitext( | |||
:done() | :done() | ||
: | :node( globeNode ) | ||
:done() | :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 | 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 | -- 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 | function p.displaydmsdimension(valuetable, format) -- formate en latitude ou une longitude dms | ||
| Ligne 231 : | Ligne 405 : | ||
direction = i18n[direction] | direction = i18n[direction] | ||
end | end | ||
degrees = valuetable.degrees .. i18n.degrees | degrees = lang:formatNum( valuetable.degrees ) .. i18n.degrees | ||
if valuetable.minutes then | if valuetable.minutes then | ||
minutes = valuetable.minutes .. i18n.minutes | minutes = twoDigit( valuetable.minutes ) .. i18n.minutes | ||
end | end | ||
if valuetable.seconds then | if valuetable.seconds then | ||
seconds = valuetable.seconds .. i18n.seconds | seconds = twoDigit( valuetable.seconds ) .. i18n.seconds | ||
end | end | ||
return degrees .. minutes .. seconds .. direction | return degrees .. minutes .. seconds .. direction | ||
| Ligne 257 : | Ligne 425 : | ||
if direction == 'N' or direction == 'S' then | if direction == 'N' or direction == 'S' then | ||
dimension = 'latitude' | dimension = 'latitude' | ||
elseif direction == 'E' or direction == 'W' then | elseif direction == 'E' or direction == 'W' then | ||
dimension = 'longitude' | dimension = 'longitude' | ||
else | else | ||
makeerror({message = | makeerror({message = i18n.invalidNSEW, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
end | end | ||
if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then | if type(degrees) ~= 'number' or type(minutes) ~= 'number' or type(seconds) ~= 'number' then | ||
makeerror({message = | makeerror({message = i18n.invalidFormat, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then | if dimension == 'latitude' and direction ~= 'N' and direction ~= 'S' then | ||
makeerror({message = | makeerror({message = i18n.invalidNS, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then | if dimension == 'longitude' and direction ~= 'W' and direction ~= 'E' then | ||
makeerror({message = | makeerror({message = i18n.invalidEW, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if dimension == 'latitude' and degrees > 90 then | if dimension == 'latitude' and degrees > 90 then | ||
makeerror({message = | makeerror({message = i18n.latitude90, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if dimension == 'longitude' and degrees > 360 then | if dimension == 'longitude' and degrees > 360 then | ||
makeerror({message = | makeerror({message = i18n.longitude360, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if degrees < 0 or minutes < 0 or seconds < 0 then | if degrees < 0 or minutes < 0 or seconds < 0 then | ||
makeerror({message = | makeerror({message = i18n.negativeCoode, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if minutes > 60 or seconds > 60 then | if minutes > 60 or seconds > 60 then | ||
makeerror({message = | makeerror({message = i18n.minSec60, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then | if (math.floor(degrees) ~= degrees and minutes ~= 0) or (math.floor(minutes) ~= minutes and seconds ~= 0) then | ||
makeerror({message = | makeerror({message = i18n.dmIntergers, sortkey = 'A'}) | ||
return false | return false | ||
end | end | ||
| Ligne 305 : | Ligne 473 : | ||
end | end | ||
local function builddmsdimension(degrees, minutes, seconds, direction) | local function builddmsdimension(degrees, minutes, seconds, direction, dimension) | ||
-- no error checking, done in function validdms | -- no error checking, done in function validdms | ||
local dimensionobject = {} | local dimensionobject = {} | ||
-- direction and dimension (= latitude or longitude) | -- direction and dimension (= latitude or longitude) | ||
dimensionobject.direction = direction | dimensionobject.direction = direction | ||
if direction == 'N' or direction == 'S' then | if dimension then | ||
dimensionobject.dimension = dimension | |||
elseif direction == 'N' or direction == 'S' then | |||
dimensionobject.dimension = 'latitude' | dimensionobject.dimension = 'latitude' | ||
elseif direction == 'E' or direction == 'W' then | |||
dimensionobject.dimension = 'longitude' | dimensionobject.dimension = 'longitude' | ||
end | end | ||
-- degrees, minutes, seconds | -- degrees, minutes, seconds | ||
dimensionobject.degrees = tonumber(degrees) | dimensionobject.degrees = tonumber(degrees) | ||
| Ligne 328 : | Ligne 497 : | ||
end | end | ||
function p._parsedmsstring( str, dimension ) -- prend une séquence et donne des noms aux paramètres | |||
-- output table: {latitude=, longitude = , direction = } | -- output table: { latitude=, longitude = , direction = } | ||
if | if type( str ) ~= 'string' then | ||
return nil | 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 | end | ||
if not tonumber(str) and not string.find(str, '/') then | if not tonumber(str) and not string.find(str, '/') then | ||
makeerror({message = | makeerror({message = i18n.invalidFormat, sortkey = 'A'}) | ||
return nil | return nil | ||
end | end | ||
args = mw.text.split(str, '/', true) | local args = mw.text.split(str, '/', true) | ||
if #args > 4 then | if #args > 4 then | ||
makeerror({message = | makeerror({message = i18n.tooManyParam, sortkey = 'A'}) | ||
end | end | ||
local direction = mw.text.trim(args[#args]) | local direction = mw.text.trim(args[#args]) | ||
table.remove(args) | table.remove(args) | ||
local degrees, minutes, seconds = args[1], args[2], args[3] | local degrees, minutes, seconds = args[1], args[2], args[3] | ||
local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction) | local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction, dimension) | ||
if validdms(dimensionobject) then | if validdms(dimensionobject) then | ||
return dimensionobject | return dimensionobject | ||
| Ligne 354 : | Ligne 531 : | ||
--- decimal specific functions | --- decimal specific functions | ||
function p.displaydec(latitude, longitude, format) | function p.displaydec(latitude, longitude, format) | ||
if format == 'dec west' then | local lat = lang:formatNum( latitude ) | ||
local | local long = lang:formatNum( longitude ) | ||
if latitude < 0 then | if format == 'dec west' or format == 'dec east' then | ||
if | local symbolNS, symbolEW = i18n.N, i18n.E | ||
if latitude < 0 then | |||
symbolNS = i18n.S | |||
lat = lang:formatNum( -latitude ) | |||
end | |||
if format == 'dec west' then | |||
symbolEW = i18n.W | |||
end | end | ||
if longitude < 0 then | |||
long = lang:formatNum( 360 + longitude ) | |||
if longitude < 0 then | |||
end | end | ||
return | |||
return { lat .. i18n.degrees .. symbolNS, long .. i18n.degrees .. symbolEW } | |||
else | |||
return | else | ||
return { lat, long } | |||
end | end | ||
end | end | ||
local function parsedec(dec, coordtype) -- coordtype = latitude or longitude | local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude | ||
dec = mw.text.trim(dec) | dec = mw.text.trim(dec) | ||
if not dec then | |||
return nil | |||
end | |||
if coordtype ~= 'latitude' and coordtype ~= 'longitude' then | if coordtype ~= 'latitude' and coordtype ~= 'longitude' then | ||
makeerror({'invalid coord type', sortkey = "A"}) | makeerror({'invalid coord type', sortkey = "A"}) | ||
return nil | return nil | ||
end | end | ||
local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros | local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros | ||
if not numdec then -- tries the decimal + direction format | if not numdec then -- tries the decimal + direction format | ||
direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec)) | 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 | 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 | if not dec or not tonumber(dec) then | ||
return nil | return nil | ||
end | end | ||
if direction == 'N' or direction == 'E' then | 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 | elseif direction == 'W' or direction == 'S' then | ||
dec = '-' .. dec | |||
numdec = tonumber( dec ) | |||
else | else | ||
makeerror({message = ' | if coordtype == 'latitude' then | ||
makeerror({message = i18n.invalidNS, sortkey = 'A'}) | |||
else | |||
makeerror({message = i18n.invalidEW, sortkey = 'A'}) | |||
end | |||
return nil | return nil | ||
end | end | ||
| Ligne 404 : | Ligne 588 : | ||
if coordtype == 'latitude' and math.abs(numdec) > 90 then | if coordtype == 'latitude' and math.abs(numdec) > 90 then | ||
makeerror({message = | makeerror({message = i18n.latitude90 , sortkey = 'A'}) | ||
return nil | return nil | ||
end | end | ||
if coordtype == 'longitude' and math.abs(numdec) > 360 then | if coordtype == 'longitude' and math.abs(numdec) > 360 then | ||
makeerror({message = | makeerror({message = i18n.longitude360 , sortkey = 'A'}) | ||
return nil | return nil | ||
end | end | ||
| Ligne 444 : | Ligne 628 : | ||
local degrees = dec % 360 | local degrees = dec % 360 | ||
return degrees, minutes | return degrees, minutes | ||
end | end | ||
local function dec2dms_dms(dec) | local function dec2dms_dms(dec) | ||
| Ligne 456 : | Ligne 640 : | ||
end | end | ||
function p._dec2dms(dec, coordtype, precision) -- | 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 | -- precision | ||
if not precision or precision == '' then | if not precision or precision == '' then | ||
| Ligne 468 : | Ligne 656 : | ||
end | end | ||
local dec = tonumber(dec) | local dec = tonumber(dec) | ||
-- direction | |||
if coordtype == 'latitude' then | -- direction | ||
local direction | |||
if coordtype == 'latitude' then | |||
if dec < 0 then | if dec < 0 then | ||
direction = 'S' | direction = 'S' | ||
else | else | ||
direction = 'N' | direction = 'N' | ||
end | end | ||
elseif coordtype == 'longitude' then | elseif coordtype == 'longitude' then | ||
if dec < 0 then | if dec < 0 or globedata[globe].defaultdisplay == 'dec west' then | ||
direction = 'W' | direction = 'W' | ||
else | else | ||
direction = 'E' | direction = 'E' | ||
end | end | ||
end | end | ||
-- conversion | -- conversion | ||
dec = math.abs(dec) -- les coordonnées en dms sont toujours positives | dec = math.abs(dec) -- les coordonnées en dms sont toujours positives | ||
if precision == 'dms' then | if precision == 'dms' then | ||
degrees, minutes, seconds = dec2dms_dms(dec) | degrees, minutes, seconds = dec2dms_dms(dec) | ||
elseif precision == 'dm' then | elseif precision == 'dm' then | ||
| Ligne 496 : | Ligne 686 : | ||
function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax | function p.dec2dms(frame) -- legacy function somewhat cumbersome syntax | ||
args = frame.args | local args = frame.args | ||
local dec = args[1] | 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 precision = string.lower(args[4] or '') | ||
local displayformat, coordtype | local displayformat, coordtype | ||
if | if dirpositive == 'n' or dirpositive == 'nord' then | ||
coordtype = 'latitude' | coordtype = 'latitude' | ||
else | else | ||
coordtype = 'longitude' | coordtype = 'longitude' | ||
end | end | ||
if | if dirpositive == 'nord' or dirpositive == 'est' or dirnegative == 'ouest' or dirnegative == 'sud' then | ||
displayformat = 'dms long' | displayformat = 'dms long' | ||
end | end | ||
local coordobject = p._dec2dms(dec, coordtype, precision) | local coordobject = p._dec2dms(dec, coordtype, precision) | ||
return p.displaydmsdimension(coordobject, displayformat) .. showerrors() | if coordobject then | ||
return p.displaydmsdimension(coordobject, displayformat) .. showerrors() | |||
else | |||
return showerrors() | |||
end | |||
end | end | ||
| Ligne 520 : | Ligne 720 : | ||
if not minutes then minutes = 0 end | if not minutes then minutes = 0 end | ||
if not seconds then seconds = 0 end | if not seconds then seconds = 0 end | ||
if direction == "N" or direction == "E" then | if direction == "N" or direction == "E" then | ||
factor = 1 | factor = 1 | ||
elseif direction == "W" or direction == "S" then | elseif direction == "W" or direction == "S" then | ||
factor = -1 | factor = -1 | ||
elseif not direction then | elseif not direction then | ||
makeerror({message = | makeerror({message = i18n.noCardinalDirection, sortkey = 'A'}) | ||
return nil | return nil | ||
else | else | ||
makeerror({message = | makeerror({message = i18n.invalidDirection, sortkey = 'A'}) | ||
return nil | return nil | ||
end | end | ||
if dmsobject.seconds then -- vérifie la précision des données initiales | 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é ? | precision = 5 + math.max( math_mod._precision(tostring(seconds), 0 ) ) -- passage par des strings assez tarabiscoté ? | ||
| Ligne 540 : | Ligne 740 : | ||
precision = math.max( math_mod._precision(tostring(degrees), 0 ) ) | precision = math.max( math_mod._precision(tostring(degrees), 0 ) ) | ||
end | end | ||
local decimal = factor * (degrees+(minutes+seconds/60)/60) | local decimal = factor * (degrees+(minutes+seconds/60)/60) | ||
return math_mod._round(decimal, precision) | return math_mod._round(decimal, precision) | ||
| Ligne 547 : | Ligne 747 : | ||
function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax | function p.dms2dec(frame) -- legacy function, somewhat bizarre syntax | ||
local args = frame.args | local args = frame.args | ||
if tonumber(args[1]) then | if tonumber(args[1]) then | ||
return args[1] -- coordonnées déjà en décimal | return args[1] -- coordonnées déjà en décimal | ||
elseif not args[2] then | elseif not args[2] then | ||
local dmsobject = p._parsedmsstring(args[1]) | |||
else | if dmsobject then | ||
return p._dms2dec({direction = args[1], degrees = args[2], minutes = args[3], seconds = args[4]}) | 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 | ||
end | end | ||
| Ligne 574 : | Ligne 791 : | ||
if claim and claim[1] then -- redundant but more robust in case of a change in the code of Module:Wikidata | 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 | 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) | return coords.latitude, coords.longitude, coords.globe or 'earth', convertwikidataprecision(coords.precision or .001) | ||
end | end | ||
| Ligne 579 : | Ligne 800 : | ||
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 = mw.loadData("Module:Drapeau/Data").data[qid] | |||
else | |||
--Bir Tawil n'a pas de pays connu | |||
qid = '?' | |||
end | |||
if gdataone ~= nil then | |||
local genre = mw.loadData("Module:Drapeau/Domaine").genre | |||
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 | -- main function for displaying coordinates | ||
function p._coord(args) | function p._coord(args) | ||
-- I declare variable | -- I declare variable | ||
local displayformat = args.format -- string: one of: 'dms', 'dms long', 'dec', 'dec east' and 'dec west' | 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 displayplace = string.lower(args.display or 'inline') --string: one of 'inline', 'title' or 'inline,title' | ||
local objectname = args.name -- string: name of the title displayed in geohack | local displayinline = string.find(displayplace, 'inline') and true or false | ||
local displaytitle = string.find(displayplace, 'title') and true or false | |||
local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack | |||
local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates | local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates | ||
local wikidata = args.wikidata -- string: set to "true" if needed | local wikidata = args.wikidata -- string: set to "true" if needed | ||
local wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoords | local wikidataquery = args.wikidataquery -- table: see [[Module:Wikidata]] see function wikidatacoords | ||
local dmslatitude, dmslongitude -- table (when created) | local dmslatitude, dmslongitude -- table (when created) | ||
local extraparams = | local extraparams = args.extraparams or '' -- string (legacy, corresponds to geohackparams) | ||
local trackingstring = '' -- tracking cats except error cats (already in errorstring) | local trackingstring = '' -- tracking cats except error cats (already in errorstring) | ||
local rawlat, rawlong = args.latitude, args.longitude | local rawlat, rawlong = args.latitude, args.longitude | ||
if | if rawlat == '' then rawlat = nil end | ||
if rawlong == '' then rawlong = nil end | if rawlong == '' then rawlong = nil end | ||
local globe = string.lower(args.globe or '') -- string: see the globedata table for accepted values | 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 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 | |||
local savegeodata = nil | |||
if args.geodata ~= nil and args.geodata ~= '' then | |||
savegeodata = require('Module:Yesno')(args.geodata) | |||
end | |||
if savegeodata == nil then -- args.geodata non renseigné ou valeur non reconnue | |||
savegeodata = (displaytitle and mw.title.getCurrentTitle():inNamespaces(0, 14, 100)) | |||
end | |||
-- II extract coordinates from Wikitext | -- II extract coordinates from Wikitext | ||
if (rawlat or rawlong) then | if (rawlat or rawlong) then | ||
if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude | if (not rawlat) or (not rawlong) then -- if latitude is provided so should be longitude | ||
makeerror({message = | makeerror({message = i18n.coordMissing, sortkey = 'A'}) | ||
return showerrors() | return showerrors() | ||
end | end | ||
latitude = parsedec(rawlat, 'latitude') | latitude = parsedec(rawlat, 'latitude', globe) | ||
if latitude then -- if latitude is decimal | if latitude then -- if latitude is decimal | ||
longitude = parsedec(rawlong, 'longitude') -- so should be longitude | longitude = parsedec(rawlong, 'longitude', globe) -- so should be longitude | ||
precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros | precision = determinedmsprec({latitude, longitude}) -- before conversion from string to number for trailing zeros | ||
if not latitude or not longitude then | if not latitude or not longitude then | ||
if errorstring == '' then | if errorstring == '' then | ||
makeerror({message = | makeerror({message = i18n.invalidFormat, sortkey = 'A'}) | ||
end | end | ||
return showerrors() | return showerrors() | ||
end | end | ||
dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision) | dmslatitude, dmslongitude = p._dec2dms(latitude, 'latitude', precision), p._dec2dms(longitude, 'longitude', precision, globe) | ||
latitude, longitude = tonumber(latitude), tonumber(longitude) | latitude, longitude = tonumber(latitude), tonumber(longitude) | ||
else -- if latitude is not decimal try to parse it as a dms string | else -- if latitude is not decimal try to parse it as a dms string | ||
dmslatitude, dmslongitude = | dmslatitude, dmslongitude = p._parsedmsstring(args.latitude, 'latitude'), p._parsedmsstring(args.longitude, 'longitude') | ||
if not dmslatitude or not dmslongitude then | if not dmslatitude or not dmslongitude then | ||
return showerrors() | return showerrors() | ||
| Ligne 628 : | Ligne 934 : | ||
-- III extract coordinate data from Wikidata and compare them to local data | -- III extract coordinate data from Wikidata and compare them to local data | ||
local wikidatalatitude, wikidatalongitude, wikidataglobe | local wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision | ||
if wikidata == 'true' then | if wikidata == 'true' then | ||
wikidatalatitude, wikidatalongitude, wikidataglobe, wikidataprecision = wikidatacoords(wikidataquery) | 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. | trackingstring = trackingstring .. makecat(i18n.sameaswikidata) | ||
else | |||
trackingstring = trackingstring .. makecat(i18n. | 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 | ||
end | end | ||
-- exit if stil no latitude or no longitude | -- exit if stil no latitude or no longitude | ||
if not latitude and not longitude then | if not latitude and not longitude then | ||
| Ligne 651 : | Ligne 966 : | ||
-- IV best guesses for missing parameters | -- IV best guesses for missing parameters | ||
--- globe | --- globe | ||
if globe == '' then | if globe == '' then | ||
globe = 'earth' | |||
end | end | ||
if not globedata[globe] then | if not globedata[globe] then | ||
makeerror({message = | makeerror({message = i18n.invalidGlobe .. globe}) | ||
globe = 'earth' | 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 | |||
--- diplayformat | --- diplayformat | ||
if not displayformat or displayformat == '' then | if not displayformat or displayformat == '' then | ||
displayformat = globedata[globe].defaultdisplay | displayformat = globedata[globe].defaultdisplay | ||
end | end | ||
-- displayinline/displaytitle | -- displayinline/displaytitle | ||
if not displayinline and not displaytitle then | |||
displayinline = true | |||
if displayplace ~= '' then | |||
makeerror({sortkey = 'C'}) --error if display not empty, but not a major error, continue | |||
end | |||
end | |||
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 | |||
local | -- V geodata | ||
if | local geodata = '' | ||
if savegeodata and 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 | if displaytitle then | ||
geodataparams[4] = 'primary' | |||
end | end | ||
if | if objectname then | ||
geodataparams.name = objectname | |||
end | 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 | else | ||
mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname,extraparams ) | mainstring = buildHTML(latitude, longitude, dmslatitude, dmslongitude, globe, displayformat, displayinline, displaytitle, objectname, extraparams) | ||
end | end | ||
return mainstring .. notes .. trackingstring .. showerrors() | |||
return mainstring .. notes .. trackingstring .. geodata .. showerrors() | |||
end | end | ||
function p.coord(frame) -- | function p.coord(frame) -- parses the strange parameters of Template:Coord before sending them to p.coord | ||
local args = frame.args | local args = frame.args | ||
local numericargs = {} | local numericargs = {} | ||
for i, j in ipairs(args) do | for i, j in ipairs(args) do | ||
args[i] = mw.text.trim(j) | args[i] = mw.text.trim(j) | ||
if | if args[i] ~= '' then | ||
table.insert(numericargs, args[i]) | table.insert(numericargs, args[i]) | ||
end | end | ||
| Ligne 713 : | Ligne 1 046 : | ||
args.extraparams = numericargs[#numericargs] | args.extraparams = numericargs[#numericargs] | ||
if #numericargs == 1 and tonumber(numericargs[1]) then | if #numericargs == 1 and tonumber(numericargs[1]) then | ||
makeerror({message = | makeerror({message = i18n.coordMissing, sortkey = 'A'}) | ||
return showerrors() | return showerrors() | ||
end | end | ||
table.remove(numericargs) | table.remove(numericargs) | ||
end | end | ||
for i, j in ipairs(numericargs) do | for i, j in ipairs(numericargs) do | ||
| Ligne 747 : | Ligne 1 076 : | ||
return p.coord(frame) | return p.coord(frame) | ||
end | end | ||
function p.latitude(frame) -- helper function pour infobox, à déprécier | function p.latitude(frame) -- helper function pour infobox, à déprécier | ||
Dernière version du 22 février 2026 à 00:17
La documentation pour ce module peut être créée à Module:Coordinates/doc
local math_mod = require( "Module:Math" )
local p = {}
local NBSP = '\194\160' -- espace insécable (code UTF-8 sur deux octets)
local i18n = {
N = 'N',
Nlong = 'nord',
W = 'O',
Wlong = 'ouest',
E = 'E',
Elong = 'est',
S = 'S',
Slong = 'sud',
degrees = '°' .. NBSP,
minutes = '′' .. NBSP,
seconds = '″' .. NBSP,
geohackurl = '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' .. NBSP .. ': ', -- 'invalid globe:',
}
local coordParse = {
NORTH = 'N',
NORD = 'N',
EAST = 'E',
EST = 'E',
WEST = 'W',
O = 'W',
OUEST = 'W',
SOUTH = 'S',
SUD = 'S',
}
local globedata = {
--[[ notes:
radius in kilometers (especially imprecise for non spheric bodies)
defaultdisplay is currently disabled, activate it ?
]]--
ariel = {radius = 580, defaultdisplay = 'dec east', 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 ce seuil (en kilomètres), 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' .. NBSP .. ': ' .. args.message .. '</strong>'
end
local errorcat = ''
if mw.title.getCurrentTitle().namespace == 0 then
errorcat = makecat(i18n.errorcat, args.sortkey)
end
errorstring = errormessage .. errorcat -- reinitializes the string to avoid absurdly long messages
return nil
end
local function showerrors()
return errorstring
end
-- 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
local args = mw.text.split(str, '/', true)
if #args > 4 then
makeerror({message = i18n.tooManyParam, sortkey = 'A'})
end
local direction = mw.text.trim(args[#args])
table.remove(args)
local degrees, minutes, seconds = args[1], args[2], args[3]
local dimensionobject = builddmsdimension(degrees, minutes, seconds, direction, dimension)
if validdms(dimensionobject) then
return dimensionobject
else
return nil
end
end
--- decimal specific functions
function p.displaydec(latitude, longitude, format)
local lat = lang:formatNum( latitude )
local long = lang:formatNum( longitude )
if format == 'dec west' or format == 'dec east' then
local symbolNS, symbolEW = i18n.N, i18n.E
if latitude < 0 then
symbolNS = i18n.S
lat = lang:formatNum( -latitude )
end
if format == 'dec west' then
symbolEW = i18n.W
end
if longitude < 0 then
long = lang:formatNum( 360 + longitude )
end
return { lat .. i18n.degrees .. symbolNS, long .. i18n.degrees .. symbolEW }
else
return { lat, long }
end
end
local function parsedec(dec, coordtype, globe) -- coordtype = latitude or longitude
dec = mw.text.trim(dec)
if not dec then
return nil
end
if coordtype ~= 'latitude' and coordtype ~= 'longitude' then
makeerror({'invalid coord type', sortkey = "A"})
return nil
end
local numdec = tonumber(dec) -- numeric value, kept separated as it looses significant zeros
if not numdec then -- tries the decimal + direction format
dec = mw.ustring.gsub( mw.ustring.upper( dec ), '%a+', coordParse )
local direction = mw.ustring.sub(dec, mw.ustring.len(dec), mw.ustring.len(dec))
dec = mw.ustring.sub(dec, 1, mw.ustring.len(dec)-2) -- removes the /N at the end
if not dec or not tonumber(dec) then
return nil
end
if direction == 'N' or direction == 'E' or direction == 'W' and globedata[globe].defaultdisplay == 'dec west' then
numdec = tonumber( dec )
elseif direction == 'W' or direction == 'S' then
dec = '-' .. dec
numdec = tonumber( dec )
else
if coordtype == 'latitude' then
makeerror({message = i18n.invalidNS, sortkey = 'A'})
else
makeerror({message = i18n.invalidEW, sortkey = 'A'})
end
return nil
end
end
if coordtype == 'latitude' and math.abs(numdec) > 90 then
makeerror({message = i18n.latitude90 , sortkey = 'A'})
return nil
end
if coordtype == 'longitude' and math.abs(numdec) > 360 then
makeerror({message = i18n.longitude360 , sortkey = 'A'})
return nil
end
return dec
end
-- 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
local 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 = mw.loadData("Module:Drapeau/Data").data[qid]
else
--Bir Tawil n'a pas de pays connu
qid = '?'
end
if gdataone ~= nil then
local genre = mw.loadData("Module:Drapeau/Domaine").genre
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 displayinline = string.find(displayplace, 'inline') and true or false
local displaytitle = string.find(displayplace, 'title') and true or false
local objectname = (args.name ~= '') and args.name -- string: name of the title displayed in geohack
local notes = (' ' and args.notes) or '' -- string: notes to de displayed after coordinates
local 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
local savegeodata = nil
if args.geodata ~= nil and args.geodata ~= '' then
savegeodata = require('Module:Yesno')(args.geodata)
end
if savegeodata == nil then -- args.geodata non renseigné ou valeur non reconnue
savegeodata = (displaytitle and mw.title.getCurrentTitle():inNamespaces(0, 14, 100))
end
-- 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
if not displayinline and not displaytitle then
displayinline = true
if displayplace ~= '' then
makeerror({sortkey = 'C'}) --error if display not empty, but not a major error, continue
end
end
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 savegeodata and 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) -- parses 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 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