Modulo:Coord

Questo è un modulo scritto in Lua. Le istruzioni che seguono sono contenute nella sottopagina Modulo:Coord/man (modifica · cronologia)
Sandbox: Modulo:Coord/sandbox (modifica · cronologia) · Sottopagine: lista · Test: Modulo:Coord/test (modifica · cronologia · esegui)
Modulo Lua che implementa le funzionalità del Template:Coord.
Ha una sottopagina di configurazione Modulo:Coord/Configurazione e una sottopagina CSS Modulo:Coord/styles.css.
Utilizzo da un altro modulo
Il modulo può essere usato anche da un altro modulo tramite "require". È sufficiente inserire nel modulo:
local mCoord = require('Modulo:Coord')
La funzione esportata è _main, con gli stessi parametri del template.
- Esempio
local mCoord = require('Modulo:Coord')
local p = {}
function p.main(frame)
local sydney, wd
sydney = mCoord._main( { '-33.86', '151.211111', format = 'dms' } )
wd = mCoord._main( { display = 'inline,title', format = 'dec' } )
return string.format('Le coordinate dms di Sydney sono: %s. ' ..
'Le coordinate dec dell\'elemento Wikidata collegato: %s.',
sydney, wd)
end
return p
--[[
* Modulo per implementare le funzionalità di Coord
*
* Traduce in lua:
* Template:Coord
* Template:Coord/input/dec
* Template:Coord/input/d
* Template:Coord/input/dm
* Template:Coord/input/dms
* Template:Coord/input/ERROR
* Template:Coord/input/error2
* Template:Coord/link
* Template:Coord/prec dec
* Template:Coord/dms2dec
* Template:Coord/dec2dms
* Template:Coord/dec2dms/d
* Template:Coord/dec2dms/d1
* Template:Coord/dec2dms/dm
* Template:Coord/dec2dms/dm1
* Template:Coord/dec2dms/dms
* Template:Coord/dec2dms/dms1
* Template:Precision1
]]
-- Variabili globali
local p = {} -- per l'esportazione delle funzioni del modulo
local args = {} -- argomenti passati al template
local errorTable = {} -- table per contenere gli errori da ritornare
-- Import
local htmlBuilder = require("Modulo:HtmlBuilder")
-- Configurazione
local cfg = mw.loadData("Modulo:Coord/Configurazione")
-------------------------------------------------------------------------------
-- Funzioni di utilità
-------------------------------------------------------------------------------
-- Ritorna il numero arrotondato al numero di cifre decimali richiesto
local function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
-- Ritorna il numero di cifre decimali di un numero
local function getDecimalPlaces(num)
local dec = tostring(num):match("%d+%.(%d+)")
return dec and dec:len() or 0
end
-- Ritorna la stringa "0 + numero" quando il numero è di una sola cifra, altrimenti lo stesso numero
local function padleft0(num)
return num < 10 and ("0" .. num) or num
end
-------------------------------------------------------------------------------
-- Validazione parametri
-------------------------------------------------------------------------------
-- Aggiunge un messaggio di errore alla risposta come elenco puntato
local function dumpError(...)
local arg = {...}
table.insert(errorTable, "* ")
for _, val in ipairs(arg) do
table.insert(errorTable, val)
end
table.insert(errorTable, "\n")
end
-- Ritorna una stringa contenente la lista degli errori,
-- nel namespace principale aggiunge una categoria di warning
local function getErrors()
local text = ""
if mw.title.getCurrentTitle().namespace == 0 then
text = "[[Categoria:" .. cfg.categorie["warning"] .. "]]\n"
end
text = text .. "<span style=\"color:red;\">Il template {{Coord}} ha riscontrato degli errori ([[Template:Coord|istruzioni]]):\n" ..
table.concat(errorTable) .. "</span>"
return text
end
-- Riconosce il tipo di richiesta ("dec", "d", "dm" o "dms") e valida i parametri posizionali
-- Ritorna il tipo di richiesta o nil in caso di errore.
local function paramsParse()
local paramType, paramName, paramMin, paramMax, reqFormat, prefix, num, str
local posArgs = 0
-- riconoscimento tipo di richiesta
for k, v in ipairs(args) do
posArgs = posArgs + 1
end
if posArgs == 2 or posArgs == 3 then
reqFormat = "dec"
elseif posArgs == 4 or posArgs == 5 then
reqFormat = "d"
elseif posArgs == 6 or posArgs == 7 then
reqFormat = "dm"
elseif posArgs == 8 or posArgs == 9 then
reqFormat = "dms"
else
dumpError("Errato numero di parametri")
return nil
end
-- validazione parametri posizionali
currFormat = cfg.params[reqFormat]
for k, v in ipairs(args) do
if currFormat[k] then
paramType = currFormat[k][1]
paramName = currFormat[k][2]
paramMin = currFormat[k][3]
paramMax = currFormat[k][4]
prefix = reqFormat .. " format: " .. paramName
-- valida un parametro di tipo numero
if paramType == "number" then
num = tonumber(v)
if num then
if num < paramMin then
dumpError(prefix, " < ", paramMin)
elseif num > paramMax then
dumpError(prefix, " > ", paramMax)
end
else
dumpError(prefix, " non è un numero")
end
-- valida un parametro di tipo stringa
elseif paramType == "string" then
if v ~= paramMin and v ~= paramMax then
dumpError(prefix, " diverso da ", paramMin, " e da ", paramMax)
end
end
end
end
return #errorTable == 0 and reqFormat or nil
end
-------------------------------------------------------------------------------
-- classi DecCoord e DmsCoord
-------------------------------------------------------------------------------
-- Rappresenta una coordinata (lat o long) in gradi decimali
local DecCoord = {
deg = nil, card = nil, display = nil
}
-- Rappresenta una coordinata (lat o long) in gradi/minuti/secondi
local DmsCoord = {
deg = nil, min = nil, sec = nil, card = nil, display = nil
}
-- Costruttore di DecCoord
-- Se deg è negativo viene cambiato di segno e la direzione cardinale eventualmente invertita
function DecCoord:new(deg, card)
local self = {}
setmetatable(self, { __index = DecCoord })
self.deg = tonumber(deg)
if self.deg < 0 then
self.card = card == "N" and "S" or (card == "E" and "W" or card)
self.deg = -self.deg
else
self.card = card
end
self.display = self.deg .. "°" .. self.card
return self
end
-- Ritorna un nuovo oggetto DmsCoord, convertendo in gradi/minuti/secondi
function DecCoord:toDms(dmsFormat)
local deg, min, sec
if dmsFormat == "d" then
deg = round(self.deg, 0)
elseif dmsFormat == "dm" then
deg = round(self.deg * 60, 0)
min = deg % 60
deg = math.floor((deg - min) / 60)
elseif dmsFormat == "dms" then
deg = round(self.deg * 3600, 0)
sec = deg % 60
deg = math.floor((deg - sec) / 60)
min = deg % 60
deg = math.floor((deg - min) / 60) % 360
end
return DmsCoord:new(deg, min, sec, self.card)
end
-- Ritorna i gradi con segno
function DecCoord:getDeg()
return self.deg * ((self.card == "N" or self.card =="E") and 1 or -1)
end
-- Costruttore di DmsCoord
function DmsCoord:new(deg, min, sec, card)
local self = {}
setmetatable (self, { __index = DmsCoord })
self.deg = tonumber(deg)
self.min = min and tonumber(min)
self.sec = sec and tonumber(sec)
self.card = card
self.display = self.deg .. "°" ..
(self.min and (padleft0(self.min) .. "′") or "") ..
(self.sec and (padleft0(self.sec) .. "″") or "") ..
(self.card and self.card or "")
return self
end
-- Ritorna un nuovo oggetto DecCoord, convertendo in gradi decimali
function DmsCoord:toDec()
local roundval, deg
roundval = (self.sec and 5 or (self.min and 3 or 0)) + getDecimalPlaces(self.sec or self.min or self.deg)
deg = round((self.deg + ((self.min or 0) + (self.sec or 0) / 60) / 60), roundval)
return DecCoord:new(deg, self.card)
end
-------------------------------------------------------------------------------
-- Coord
-------------------------------------------------------------------------------
-- Usato nelle conversioni da gradi decimali a gradi/minuti/secondi,
-- dato il numero di decimali maggiore tra latdec e longdec ritorna il formato adatto:
-- "d" per 0 decimali
-- "dm" per 1-2 decimali
-- "dms" per > 3 decimali
local function getDmsFormat(latdec, longdec)
local d1, d2, max
d1 = getDecimalPlaces(latdec)
d2 = getDecimalPlaces(longdec)
max = d1 > d2 and d1 or d2
return max == 0 and "d" or
((max == 1 or max == 2) and "dm" or "dms")
end
-- Crea l'HTML ritornato da Coord
local function buildHTML(decLat, decLong, dmsLat, dmsLong, param, defaultFormat)
local root, text, url, displayInline, displayTitle, htmlTitle
-- geohack url e parametri
url = cfg.geohackUrl ..
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
"¶ms=" .. param ..
(args["name"] and ("&title=" .. mw.uri.encode(args["name"])) or "")
root = htmlBuilder.create()
root
.tag("span")
.addClass("plainlinks nourlexpansion")
.wikitext("[" .. url)
.tag("span")
.addClass(defaultFormat == "dec" and "geo-nondefault" or "geo-default")
.tag("span")
.addClass("geo-dms")
.attr("title", "Mappe, foto aeree e altri dati per questa posizione")
.tag("span")
.addClass("latitude")
.wikitext(dmsLat.display)
.done()
.wikitext(" ")
.tag("span")
.addClass("longitude")
.wikitext(dmsLong.display)
.done()
.done()
.done()
.tag("span")
.addClass("geo-multi-punct")
.wikitext(" / ")
.done()
.tag("span")
.addClass(defaultFormat == "dec" and "geo-default" or "geo-nondefault")
.wikitext(args["name"] and "<span class=\"vcard\">" or "")
.tag("span")
.addClass("geo-dec")
.attr("title", "Mappe, foto aeree e altri dati per questa posizione")
.wikitext(decLat.display .. " " .. decLong.display)
.done()
.tag("span")
.attr("style", "display:none")
.tag("span")
.addClass("geo")
.wikitext(decLat:getDeg() .. "; " .. decLong:getDeg())
.done()
.done()
.wikitext(args["name"] and ("<span style=\"display:none\"> (<span class=\"fn org\">" ..
args["name"] .. "</span>)</span></span>") or "")
.done()
.wikitext("]")
.done()
-- formatta il risultato a seconda di args["display"] (nil, "inline", "title", "inline,title")
text = tostring(root) .. (args["notes"] or "")
displayInline = not args["display"] or args["display"] == "inline" or args["display"] == "inline,title"
displayTitle = args["display"] == "title" or args["display"] == "inline,title"
htmlTitle = "<span style=\"font-size: small;\"><span id=\"coordinates\">[[Coordinate geografiche|Coordinate]]: "
return (displayInline and text or "") ..
(displayTitle and (htmlTitle .. text .. "</span></span>") or "")
end
-- Dato un input fino a 9 parametri posizionali e 3 con nome, ritorna un html
-- contenente le coordinate in formato dec e dms come collegamento esterno a geohack.php.
local function coord()
local param, defaultFormat, dmsFormat, reqFormat
reqFormat = paramsParse()
if not reqFormat then
return getErrors()
end
if reqFormat == "dec" then
-- {{coord|43.6500|-79.3800}}
decLat = DecCoord:new(args[1], "N")
decLong = DecCoord:new(args[2], "E")
elseif reqFormat == "d" then
-- {{coord|43.651234|N|79.383333|W}}
decLat = DecCoord:new(args[1], args[2])
decLong = DecCoord:new(args[3], args[4])
elseif reqFormat == "dm" then
-- {{coord|43|29|N|79|23|W}}
dmsLat = DmsCoord:new(args[1], args[2], nil, args[3])
dmsLong = DmsCoord:new(args[4], args[5], nil, args[6])
elseif reqFormat == "dms" then
-- {{coord|43|29|4|N|79|23|0|W}}
dmsLat = DmsCoord:new(args[1], args[2], args[3], args[4])
dmsLong = DmsCoord:new(args[5], args[6], args[7], args[8])
end
-- conversioni dec <=> dms
if reqFormat == "dec" or reqFormat == "d" then
dmsFormat = getDmsFormat(decLat.deg, decLong.deg)
dmsLat = decLat:toDms(dmsFormat)
dmsLong = decLong:toDms(dmsFormat)
elseif reqFormat == "dm" or reqFormat == "dms" then
decLat = dmsLat:toDec()
decLong = dmsLong:toDec()
end
if args["format"] then
defaultFormat = args["format"]
elseif reqFormat == "dec" then
defaultFormat = "dec"
elseif reqFormat == "d" then
defaultFormat = (dmsFormat == "d" and "dms" or "dec")
else
defaultFormat = "dms"
end
-- crea la stringa param per geohack.php
if reqFormat == "dec" then
param = args[1] .. "_N_" .. args[2] .. "_E" .. (args[3] and ("_" .. args[3]) or "")
else
-- concatena solo i posizionali
param = table.concat(args, "_")
end
return args["display"] == "debug" and
(decLat.display .. " " .. decLong.display .. " " .. dmsLat.display .. " " .. dmsLong.display) or
buildHTML(decLat, decLong, dmsLat, dmsLong, param, defaultFormat)
end
-- Entry-point per eventuale {{dms2dec}}
function p.dms2dec(frame)
local args = frame.args
-- {{dms2dec|N|10|20|35}}
return DmsCoord:new(tonumber(args[2]), tonumber(args[3]), tonumber(args[4]), args[1]):toDec():getDeg()
end
-- Entry-point per eventuale {{dec2dms}}
function p.dec2dms(frame)
local args = frame.args
-- {{dec2dms|10.391944|N|S|d}}
return DecCoord:new(tonumber(args[1]), tonumber(args[1]) >= 0 and args["2"] or args["3"]):toDms(args["4"]).display
end
-- Entry-point per {{coord}}
function p.coord(frame)
for k, v in pairs(frame:getParent().args) do
if v ~= "" then
args[k] = v
end
end
return coord()
end
return p