मोड्युल:Coordinates/प्रयोगस्थल
This is the module sandbox page for मोड्युल:Coordinates (diff). |
This Lua module is used on approximately १,१९०,००० pages, or roughly 1069% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
- निर्देशाङ्क स्थान (P625) (उपयोग हेर्नुहोस्)
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
यस ढाँचामा अप्ठ्यारा सुविधाहरू रहेका छन्। कृपया यस ढाँचालाई सम्पादन गर्नु पूर्व यस ढाँचाको व्यवस्थापन तथा पार्सर प्रणालीहरूसँग परिचित हुनुपर्छ र पनि यदि तपाईँले गरेको सम्पादनबाट कुनै जटिल तथा अप्रत्याशित समस्या आएमा तत्कालै आफ्नो सम्पादन फर्काई दिनुहोला, किनकि यो ढाँचा अत्यधिक सङ्ख्याका लेख/पृष्ठहरूमा प्रयोग भइरहेको छ। स्मरण रहोस्: तपाईँ प्रयोग आदि गर्न सक्नुहुन्छ तर यहाँ कुनै पनि प्रयोग गर्नुपूर्व सबै प्रयोग सामान्य ढाँचा प्रयोगस्थलमा अथवा आफ्नो प्रयोगकर्ता प्रयोगस्थलमा गर्नुहोस्। |
Note: The code which this module's main function (coord
) outputs is directly parsed and/or manipulated by Module:Location map and other functions of this module itself (coord2text
and coordinsert
). If the structure of the output changes (for example, to use the <mapframe>
and <maplink>
tags), please update the aforementioned scripts as well.
Using the module with coordinsert[सम्पादन गर्नुहोस्]
When using the {{Coord}} template inside another template, like an infobox, there may be parameters (like type:airport
) which should be added automatically. To do so, do something like this:
{{#if:{{{coordinates|}}}|{{#invoke:Coordinates|coordinsert|{{{coordinates|}}}|parameter1:value1|parameter2:value2|parameter3:value3…}}|
Do not add more vertical bars |
than necessary.
Using the module with coord2text to extract latitude or longitude[सम्पादन गर्नुहोस्]
Developers maintaining legacy code may need to extract latitude or longitude to use a parameters in other code, or a mathematical expression. The module's "coord2text" function can be used to extract data from the {{Coord}} template. To extract the latitude from a Coord template, use:
{{#invoke:coordinates|coord2text|{{Coord|57|18|22|N|4|27|32|E}}|lat}}
→ ५७.३०६११
To extract the longitude, use:
{{#invoke:coordinates|coord2text|{{Coord|57|18|22|N|4|27|32|E}}|long}}
→ ४.४५८८९
Modules using this module directly[सम्पादन गर्नुहोस्]
Tracking categories[सम्पादन गर्नुहोस्]
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