Modulo:Coord: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
aggiunte funzioni getWikidataCoordinates, checkWikidata e getDisplay
non generare indicatore quando non va mostrato, fix per Special:Permalink/140491317#Anomalia_coordinate
 
(35 versioni intermedie di 7 utenti non mostrate)
Riga 1:
--[[
* Modulo perche implementareimplementa leil funzionalità ditemplate 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
]]
 
require('strict')
-- 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
 
local mWikidata = require('Modulo:Wikidata')
-- Import
local htmlBuildercfg = requiremw.loadData("'Modulo:HtmlBuilder"Coord/Configurazione')
local errorCategory = '[[Categoria:Errori di compilazione del template Coord]]'
 
-- =============================================================================
-- Configurazione
-- Funzioni di utilità
local cfg = mw.loadData("Modulo:Coord/Configurazione")
-- =============================================================================
 
-- Error handler per xpcall, formatta l'errore.
-------------------------------------------------------------------------------
--
-- Funzioni di utilità
-- @param {string} msg
-------------------------------------------------------------------------------
-- @return {string}
local function errhandler(msg)
local cat = mw.title.getCurrentTitle().namespace == 0 and errorCategory or ''
return string.format('<div style="color:red">Il template {{Coord}} ha riscontrato degli errori ' ..
'([[Template:Coord|istruzioni]]):\n%s</div>%s', msg, cat)
end
 
-- RitornaRestituisce il numero arrotondato al numero di cifre decimali richiesto.
-- http://lua-users.org/wiki/SimpleRound
--
-- @param {number} num
-- @param {number} idp
-- @return {number}
local function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
 
return math.floor(num * mult + 0.5) / mult
end
 
-- RitornaRestituisce la stringa "0 + numero" quando il numero è di una sola cifra, altrimenti lo stesso numero.
--
-- @param {number} num
-- @return {string}
local function padleft0(num)
return (num < 10 and "'0"' or ""'') .. num
end
 
-- Converte un numero in stringa senza usare la notazione scientifica, esempio tostring(0.00001).
--
-- @param {number} num
-- @return {string}
local function numberToString(num)
-- la parentesi () extra serve per non ritornarerestituire anche il gsub.count
return (string.format("'%f"', num):gsub("'%.?0+$"', ""''))
end
 
-- Parsifica il parametro display e restituisce una table con chiavi inline, title e debug.
-- Ritorna latitudine e longitudine della [[d:property:p625]]
--
-- o nil nel caso non siano presenti
-- @param {table} args
local function getWikidataCoordinates()
-- @return {table}
local entity, value, lat, long
local function getDisplay(args)
return {
inline = not args.display or args.display == 'inline' or args.display == 'inline,title',
title = args.display == 'title' or args.display == 'inline,title',
debug = args.display == 'debug'
}
end
 
local function getZoom( extraparams )
entity = mw.wikibase.getEntity()
local scale = extraparams:match( '%f[%w]scale: ?(%d+)' )
if entity and entity.claims and entity.claims.p625 and
if scale then
entity.claims.p625[0].mainsnak.snaktype == "value" then
return math.floor(math.log10( 5 / tonumber( scale ) ) * 3 + 25)
value = entity.claims.p625[0].mainsnak.datavalue.value
end
lat = round(value.latitude, 6)
long = round(value.longitude, 6)
end
 
local extraType = extraparams:match( '%f[%w]type: ?(%w+)' )
return lat, long
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
return 13
end
 
-- Legge i parametri passati al modulo.
-------------------------------------------------------------------------------
--
-- Parsing parametri
-- @param {table} frame
-------------------------------------------------------------------------------
-- @return {table}
local function getArgs(frame)
local args = {}
 
-- copia i parametri ricevuti, eccetto quelli con nome valorizzati a stringa vuota
-- Error handler per xpcall, formatta l'errore
for k, v in pairs(frame:getParent().args) do
-- nel namespace principale aggiunge una categoria di warning
if v ~= '' or tonumber(k) then
local function errhandler(msg)
args[k] = string.gsub(v, '^%s*(.-)%s*$', '%1')
local cat = ""
end
end
-- retrocompatibilità con una funzionalità nascosta del precedente template:
-- ignorava qualunque parametro posizionale vuoto dopo longitudine e parametri geohack
for i = #args, 1, -1 do
if args[i] == '' then
table.remove(args, i)
else
break
end
end
-- rimuove i parametri posizionali vuoti front to back fermandosi al primo non vuoto
while args[1] == '' do
table.remove(args, 1)
end
-- se l'utente non ha fornito lat e long con i posizionali ma con latdec e longdec
if (#args == 0 or (#args == 1 and not tonumber(args[1]))) and
tonumber(args.latdec) and tonumber(args.longdec) then
table.insert(args, 1, numberToString(args.latdec))
table.insert(args, 2, numberToString(args.longdec))
end
 
return args
if mw.title.getCurrentTitle().namespace == 0 then
cat = "[[Categoria:" .. cfg.categorie["warning"] .. "]]\n"
end
 
return string.format("%s<span style=\"color:red;\">Il template {{Coord}} ha riscontrato degli errori " ..
"([[Template:Coord|istruzioni]]):\n%s</span>\n", cat, msg:match(".+%d:%s(.+)$"))
end
 
-- =============================================================================
-- Raccoglie più messaggi di errore in un'unica table prima di usare error()
-- Classe DecCoord
local function dumpError(...)
-- =============================================================================
local arg = {...}
 
-- La classe DecCoord rappresenta una coordinata (latitudine o longitudine) in gradi decimali.
table.insert(errorTable, "* ")
for _, val in ipairs(arg) do
table.insert(errorTable, val)
end
table.insert(errorTable, "\n")
end
 
local DecCoord = {}
-- Parsifica il parametro "display"
local DmsCoord = {} -- dichiarata qui per le conversioni
local function getDisplay()
local display = {}
display.inline = not args["display"] or args["display"] == "inline" or args["display"] == "inline,title"
display.title = args["display"] == "title" or args["display"] == "inline,title"
display.debug = args["display"] == "debug"
return display
end
 
-- Costruttore della classe DecCoord.
-- Con le richieste "dm" e "dms" verifica se ci sono
--
-- parametri lasciati vuoti in modo valido.
-- @param {string} deg - i gradi decimali, positivi o negativi, se negativi viene
-- Ritorna il tipo di richiesta dopo la eventuale semplificazione.
-- cambiato il segno e la direzione cardinale eventualmente invertita
local function paramsEmpty(reqFormat)
-- @param {string} card - la direzione cardinale (N|S|E|W)
if reqFormat == "dms" then
-- @return {table} un nuovo oggetto DecCoord
-- {{coord|1|2||N|5|6||E}} valido
function DecCoord:new(deg, card)
if args[3] == "" and args[7] == "" then
local self = {}
table.remove(args, 7)
table.remove(args, 3)
reqFormat = "dm"
-- {{coord|1|2|3|N|5|6||E}} non valido
elseif args[3] == "" or args[7] == "" then
error("* lat e long hanno diversa precisione")
-- {{coord|1||3|N|5||7|E}} valido
elseif args[2] == "" and args[6] == "" then
args[2], args[6] = 0, 0
-- {{coord|1|2|3|N|5||7|E}} non valido
elseif args[2] == "" or args[6] == "" then
error("* lat e long hanno diversa precisione")
end
end
 
setmetatable(self, { __index = DecCoord,
if reqFormat == "dm" then
__tostring = function(t) return self:__tostring() end,
-- {{coord|1||N|4||E}} valido
__concat = function(t, t2) return tostring(t) .. tostring(t2) end })
if args[2] == "" and args[5] == "" then
table.remove(args, 5)
table.remove(args, 2)
reqFormat = "d"
-- {{coord|1|2|N|4||E}} non valido
elseif args[2] == "" or args[5] == "" then
error("* lat e long hanno diversa precisione")
end
end
 
self.deg = tonumber(deg)
return reqFormat
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
 
return self
end
 
-- Richiamata automaticamente ogni volta che è richiesto un tostring o un concatenamento.
-- Riconosce il tipo di richiesta ("dec", "d", "dm" o "dms") e parsifica i parametri posizionali
--
-- Ritorna il tipo di richiesta.
-- @return {string}
local function paramsParse()
function DecCoord:__tostring()
local reqFormat, globe, earth, prefix, num, str
return numberToString(self.deg) .. '°' .. self.card
local param = {}
end
 
-- Restituisce i gradi con segno.
-- riconoscimento tipo di richiesta
--
if #args < 2 then
-- @return {string}
error("* coordinate non specificate")
function DecCoord:getDeg()
elseif #args < 4 then
local deg = self.deg * ((self.card == 'N' or self.card =='E') and 1 or -1)
reqFormat = "dec"
return numberToString(deg)
elseif #args < 6 then
end
reqFormat = "d"
elseif #args < 8 then
reqFormat = "dm"
elseif #args < 10 then
reqFormat = "dms"
else
error("* errato numero di parametri")
end
 
-- Restituisce un nuovo oggetto DmsCoord, convertendo in gradi/minuti/secondi.
-- valida i parametri che possono essere lasciati vuoti
--
reqFormat = paramsEmpty(reqFormat)
-- @return {table} un nuovo oggetto DmsCoord
function DecCoord:toDms()
local deg, min, sec
 
deg = round(self.deg * 3600, 2)
-- validazione parametri posizionali
sec = round(math.floor(deg) % 60 + deg - math.floor(deg), 2)
currFormat = cfg.params[reqFormat]
deg = math.floor((deg - sec) / 60)
globe = args[#args]:match("globe:(%w+)")
min = deg % 60
earth = not globe or globe == "earth"
deg = math.floor((deg - min) / 60) % 360
for k, v in ipairs(args) do
if currFormat[k] then
param.type = currFormat[k][1]
param.name = currFormat[k][2]
param.min = currFormat[k][3]
param.max = currFormat[k][4]
prefix = reqFormat .. " format: " .. param.name
-- valida un parametro di tipo numero
if param.type == "number" then
num = tonumber(v)
if num then
if earth and num < param.min then
dumpError(prefix, " < ", param.min)
elseif earth and math.floor(num) > param.max then
dumpError(prefix, " > ", param.max)
end
else
dumpError(prefix, " non è un numero")
end
-- valida un parametro di tipo stringa
elseif param.type == "string" then
if v ~= param.min and v ~= param.max then
dumpError(prefix, " diverso da ", param.min, " e da ", param.max)
end
end
end
end
 
return DmsCoord:new(deg, min, sec, self.card)
if #errorTable > 0 then
error(table.concat(errorTable))
end
return reqFormat
end
 
-- =============================================================================
-------------------------------------------------------------------------------
-- classi DecCoord e Classe DmsCoord
-- =============================================================================
-------------------------------------------------------------------------------
 
-- RappresentaLa classe DmsCoord rappresenta una coordinata (latlatitudine o longlongitudine) in gradi decimali /minuti/secondi.
local DecCoord = {}
 
-- Costruttore della classe DmsCoord.
-- Rappresenta una coordinata (lat o long) in gradi/minuti/secondi
--
local DmsCoord = {}
-- @param {string} deg - i gradi
-- @param {string} min - i minuti, può essere nil
-- @param {string} sec - i secondi, può essere nil
-- @param {string} card - la direzione cardinale (N|S|E|W)
-- @return {table} un nuovo oggetto DmsCoord
function DmsCoord:new(deg, min, sec, card)
local self = {}
 
setmetatable (self, { __index = DmsCoord,
-- Costruttore di DecCoord
__tostring = function(t) return self:__tostring() end,
-- deg: gradi decimali, positivi o negativi, se negativi viene cambiato il segno e
__concat = function(t, t2) return tostring(t) .. tostring(t2) end })
-- la direzione cardinale eventualmente invertita
-- card: direzione cardinale (N|S|E|W)
function DecCoord:new(deg, card)
local self = {}
 
self.deg = tonumber(deg)
setmetatable(self, { __index = DecCoord,
self.min = min and tonumber(min)
__tostring = function(t) return self:__tostring() end,
self.sec = sec and tonumber(sec)
__concat = function(t, t2) return tostring(t) .. tostring(t2) end })
self.card = card
 
return self
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
 
return self
end
 
-- Richiamata automaticamente ogni volta che è richiesto un tostring o un concatenamento.
--
function DecCoord:__tostring()
-- @return {string}
return numberToString(self.deg) .. "°" .. self.card
function DmsCoord:__tostring()
return self.deg .. '°' ..
(self.min and (padleft0(self.min) .. '′') or '') ..
(self.sec and (padleft0(self.sec) .. '″') or '') ..
self.card
end
 
-- Restituisce un nuovo oggetto DecCoord, convertendo in gradi decimali.
-- Ritorna i gradi con segno
--
function DecCoord:getDeg()
-- @return {table} un nuovo oggetto DecCoord
local deg = self.deg * ((self.card == "N" or self.card =="E") and 1 or -1)
function DmsCoord:toDec()
local deg = round((self.deg + ((self.min or 0) + (self.sec or 0) / 60) / 60), 6)
return numberToString(deg)
return DecCoord:new(deg, self.card)
end
 
-- =============================================================================
-- Ritorna un nuovo oggetto DmsCoord, convertendo in gradi/minuti/secondi
-- Classe Coord
function DecCoord:toDms()
-- =============================================================================
local deg, min, sec
deg = round(self.deg * 3600, 2)
sec = round(math.floor(deg) % 60 + deg - math.floor(deg), 2)
deg = math.floor((deg - sec) / 60)
min = deg % 60
deg = math.floor((deg - min) / 60) % 360
 
-- La classe Coord è la classe principale del modulo.
return DmsCoord:new(deg, min, sec, self.card)
-- Al suo interno ha un riferimento alla latitudine e longitudine in ogni formato.
 
local Coord = {}
 
-- Costruttore della classe Coord.
--
-- @param {table} args
-- @return {table} un nuovo oggetto Coord
function Coord:new(args)
local decLat, decLong, dmsLat, dmsLong
local display = getDisplay(args)
local self = { args = args }
 
setmetatable(self, { __index = Coord })
 
if args.from and display.title then
error('il parametro "from" è valido solo con display=inline', 3)
end
-- con display=title o con i parametri "prop" o "from"
-- legge le coordinate da P625 per utilizzarle o per confrontarle con quelle inserite
if getDisplay(self.args).title or self.args.prop or args.from then
self:_checkWikidata()
-- con "from", senza coordinate utente e su Wikidata non esegue i controlli successivi
if self.args.from and #self.args < 2 and not tonumber(args[1]) then
return self
end
end
 
-- identifica il tipo di chiamata
self:_checkRequestFormat()
 
-- in base al tipo di chiamata crea gli oggetti DecCoord o DmsCoord
if self.reqFormat == 'dec' then
-- {{coord|1.111|2.222}}
decLat = DecCoord:new(args[1], 'N')
decLong = DecCoord:new(args[2], 'E')
elseif self.reqFormat == 'd' then
-- {{coord|1.111|N|3.333|W}}
decLat = DecCoord:new(args[1], args[2])
decLong = DecCoord:new(args[3], args[4])
elseif self.reqFormat == 'dm' then
-- {{coord|1|2|N|4|5|W}}
dmsLat = DmsCoord:new(args[1], args[2], nil, args[3])
dmsLong = DmsCoord:new(args[4], args[5], nil, args[6])
elseif self.reqFormat == 'dms' then
-- {{coord|1|2|3|N|5|6|7|W}}
dmsLat = DmsCoord:new(args[1], args[2], args[3], args[4])
dmsLong = DmsCoord:new(args[5], args[6], args[7], args[8])
end
 
-- effettua le conversioni dec <=> dms
if self.reqFormat == 'dec' or self.reqFormat == 'd' then
dmsLat = decLat:toDms()
dmsLong = decLong:toDms()
-- rimuove secondi e minuti se zero e presenti in lat e long
if dmsLat.sec == 0 and dmsLong.sec == 0 then
dmsLat.sec, dmsLong.sec = nil, nil
if dmsLat.min == 0 and dmsLong.min == 0 then
dmsLat.min, dmsLong.min = nil, nil
end
end
elseif self.reqFormat == 'dm' or self.reqFormat == 'dms' then
decLat = dmsLat:toDec()
decLong = dmsLong:toDec()
end
-- se presente args.catuguali e non è stato usato Wikidata verifica se uguali
if args.catuguali and self.wdLat and self.wdLong and
self.wdCat == nil and
self.wdLat == round(decLat:getDeg(), 6) and
self.wdLong == round(decLong:getDeg(), 6) then
self.wdCat = '[[Categoria:P625 uguale su Wikidata]]'
end
 
self.decLat = decLat
self.decLong = decLong
self.dmsLat = dmsLat
self.dmsLong = dmsLong
 
return self
end
 
-- Legge la P625 e la utilizza come latitudine e longitudine se non fornite dall'utente.
-- Costruttore di DmsCoord
function Coord:_checkWikidata()
-- deg: gradi
self.wdEntityId = self.args.from or mw.wikibase.getEntityIdForCurrentPage();
-- min: minuti, può essere nil
if self.args.prop then
-- sec: secondi, può essere nil
self.wdLat = mWikidata._getQualifier({ self.args.prop, 'P625', coord = 'latitude', n = 1, nq = 1, from = self.wdEntityId })
-- card: direzione cardinale (N|S|E|W)
self.wdLong = mWikidata._getQualifier({ self.args.prop, 'P625', coord = 'longitude', n = 1, nq = 1, from = self.wdEntityId })
function DmsCoord:new(deg, min, sec, card)
else
local self = {}
self.wdLat = mWikidata._getProperty({ 'P625', coord = 'latitude', n = 1, from = self.wdEntityId })
self.wdLong = mWikidata._getProperty({ 'P625', coord = 'longitude', n = 1, from = self.wdEntityId })
end
if self.wdLat and self.wdLong then
self.wdLat = round(self.wdLat, 6)
self.wdLong = round(self.wdLong, 6)
-- se l'utente non ha fornito lat e long usa quelli di Wikidata
if #self.args == 0 or (#self.args == 1 and not tonumber(self.args[1])) then
table.insert(self.args, 1, numberToString(self.wdLat))
table.insert(self.args, 2, numberToString(self.wdLong))
self.wdCat = '[[Categoria:P625 letta da Wikidata]]'
end
else
self.wdCat = '[[Categoria:P625 assente su Wikidata]]'
end
end
 
-- Riconosce il tipo di richiesta: dec, d, dm o dms.
setmetatable (self, { __index = DmsCoord,
function Coord:_checkRequestFormat()
__tostring = function(t) return self:__tostring() end,
local errorTable = {}
__concat = function(t, t2) return tostring(t) .. tostring(t2) end })
 
-- riconoscimento tipo di richiesta
self.deg = tonumber(deg)
if #self.min =args min< and2 tonumber(min)then
error('* coordinate non specificate', 4)
self.sec = sec and tonumber(sec)
elseif #self.cardargs < =4 cardthen
self.reqFormat = 'dec'
elseif #self.args < 6 then
self.reqFormat = 'd'
elseif #self.args < 8 then
self.reqFormat = 'dm'
elseif #self.args < 10 then
self.reqFormat = 'dms'
else
error('* errato numero di parametri', 4)
end
 
-- con le richieste dm e dms verifica se ci sono parametri lasciati vuoti in modo valido.
return self
if self.reqFormat == 'dms' then
-- {{coord|1|2||N|5|6||E}} valido
if self.args[3] == '' and self.args[7] == '' then
table.remove(self.args, 7)
table.remove(self.args, 3)
self.reqFormat = 'dm'
-- {{coord|1|2|3|N|5|6||E}} non valido
elseif self.args[3] == '' or self.args[7] == '' then
error('* lat e long hanno diversa precisione', 4)
-- {{coord|1||3|N|5||7|E}} valido
elseif self.args[2] == '' and self.args[6] == '' then
self.args[2], self.args[6] = 0, 0
-- {{coord|1|2|3|N|5||7|E}} non valido
elseif self.args[2] == '' or self.args[6] == '' then
error('* lat e long hanno diversa precisione', 4)
end
end
if self.reqFormat == 'dm' then
-- {{coord|1||N|4||E}} valido
if self.args[2] == '' and self.args[5] == '' then
table.remove(self.args, 5)
table.remove(self.args, 2)
self.reqFormat = 'd'
-- {{coord|1|2|N|4||E}} non valido
elseif self.args[2] == '' or self.args[5] == '' then
error('* lat e long hanno diversa precisione', 4)
end
end
 
-- validazione parametri posizionali
local currFormat = cfg.params[self.reqFormat]
local globe = self.args[#self.args]:match('globe:(%w+)')
self.isEarth = not globe or globe == 'earth'
for k, v in ipairs(self.args) do
if currFormat[k] then
local err
local parType = currFormat[k][1]
local parName = currFormat[k][2]
local parMin = currFormat[k][3]
local parMax = currFormat[k][4]
-- valida un parametro di tipo numero
if parType == 'number' then
local num = tonumber(v)
if num then
if self.isEarth and num < parMin then
err = string.format('* %s format: %s < %s', self.reqFormat, parName, parMin)
elseif self.isEarth and math.floor(num) > parMax then
err = string.format('* %s format: %s > %s', self.reqFormat, parName, parMax)
end
else
err = string.format('* %s format: %s non è un numero', self.reqFormat, parName)
end
-- valida un parametro di tipo stringa
elseif parType == 'string' then
if v ~= parMin and v ~= parMax then
err = string.format('* %s format: %s diverso da %s e da %s',
self.reqFormat, parName, parMin, parMax)
end
end
if err then
table.insert(errorTable, err)
end
end
end
 
if #errorTable > 0 then
error(table.concat(errorTable, '\n'), 4)
end
end
 
-- Utilizza l'estensione [[mw:Extension:GeoData]].
-- Richiamata automaticamente ogni volta che è richiesto un tostring o un concatenamento
--
function DmsCoord:__tostring()
-- @param {table} display
return self.deg .. "°" ..
-- @return {string}
(self.min and (padleft0(self.min) .. "′") or "") ..
function Coord:_setGeoData(display)
(self.sec and (padleft0(self.sec) .. "″") or "") ..
local gdStr = string.format('{{#coordinates:%s|%s|name=%s}}',
self.card
table.concat(self.args, '|'),
(display.title and mw.title.getCurrentTitle().namespace == 0) and 'primary' or '',
self.args.name or '')
return mw.getCurrentFrame():preprocess(gdStr)
end
 
-- Funzione di debug, restituisce latitudine e longitudine in entrambi i formati.
-- Ritorna un nuovo oggetto DecCoord, convertendo in gradi decimali
--
function DmsCoord:toDec()
-- @return {string}
local deg = round((self.deg + ((self.min or 0) + (self.sec or 0) / 60) / 60), 6)
function Coord:getDebugCoords()
-- con args.from restitusce una stringa vuota se non c'è nessun dato
return DecCoord:new(deg, self.card)
if self.args.from and #self.args < 2 and not tonumber(self.args[1]) then
return ''
end
return self.decLat .. ' ' .. self.decLong .. ' ' .. self.dmsLat .. ' ' .. self.dmsLong
end
 
-- Restituisce l'HTML di un elemento <maplink> di Kartographer per le coordinate
-------------------------------------------------------------------------------
--
-- Coord
-- @return {string}
-------------------------------------------------------------------------------
function Coord:_buildMaplinkHTML()
local defaultFormat
if self.args.format then
defaultFormat = self.args.format
elseif self.reqFormat == 'dec' then
defaultFormat = 'dec'
else
defaultFormat = 'dms'
end
 
local linkText;
if defaultFormat == 'dec' then
linkText = self.decLat .. ' ' .. self.decLong
else
linkText = tostring(self.dmsLat) .. ' ' .. tostring(self.dmsLong)
end
 
local jsonParams = {
-- Nel namespace principale e con display.title legge la [[d:property:p625]]
type = 'Feature',
-- e la utilizza come latitudine e longitudine se non fornite dall'utente.
geometry = {
-- Ritorna la categoria appropriata per [[Categoria:Wikidata]], altrimenti una stringa vuota
type ='Point',
local function checkWikidata(display)
coordinates = {
local wdLat, wdLong, cat
round( self.decLong:getDeg(), 6 ), -- max precision in GeoJSON format
round( self.decLat:getDeg(), 6 )
}
},
properties = {
['marker-color'] = "228b22",
}
}
 
if self.wdEntityId then
if mw.title.getCurrentTitle().namespace == 0 and display.title then
-- geoshape da Wikidata
wdLat, wdLong = getWikidataCoordinates()
jsonParams = {
if wdLat and wdLong then
jsonParams,
-- se l'utente non ha fornito lat e long usa quelli di Wikidata
{
if #args == 0 or (#args == 1 and not tonumber(args[1])) then
type = 'ExternalData',
table.insert(args, numberToString(wdLat))
service = 'geoshape',
table.insert(args, numberToString(wdLong))
ids = self.wdEntityId,
if #args == 3 then
properties = {
table.insert(args, table.remove(args, 1))
['fill-opacity'] = 0.2
end
}
cat = "Coordinate assenti ma presenti su Wikidata"
}
end
}
else
end
cat = "Coordinate non presenti su Wikidata"
end
end
 
return mw.getCurrentFrame():extensionTag{
return cat and ("[[Categoria:" .. cat .. "]]\n") or ""
name = 'maplink',
content = mw.text.jsonEncode( jsonParams ),
args = {
text = linkText,
zoom = getZoom( self.args[#self.args] or '' ),
latitude = self.decLat:getDeg(),
longitude = self.decLong:getDeg(),
}
}
end
 
-- Crea l'HTML ritornato da Coord
local function buildHTML(decLat, decLong, dmsLat, dmsLong, geohackParams, defaultFormat, display)
local root, text, url, htmlTitle, noprint
 
-- Restituisce l'HTML di un link a geohack con le coordinate (usato solo quando
-- geohack url e parametri
-- non è possibile usare maplink).
url = cfg.geohackUrl ..
--
"&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") ..
-- @return {string}
"&params=" .. geohackParams ..
function Coord:_buildGeohackLink()
(args["name"] and ("&title=" .. mw.uri.encode(args["name"])) or "")
-- crea la stringa per il parametro params di geohack.php
local geohackParams
 
if self.reqFormat == 'dec' then
root = htmlBuilder.create()
geohackParams = string.format('%s_N_%s_E', self.args[1], self.args[2])
root
if self.args[3] then
.tag("span")
geohackParams = geohackParams .. '_' .. self.args[3]
.addClass("plainlinks nourlexpansion")
end
.wikitext("[" .. url)
else
.tag("span")
-- concatena solo i posizionali
.addClass(defaultFormat == "dec" and "geo-nondefault" or "geo-default")
geohackParams = table.concat(self.args, '_')
.tag("span")
end
.addClass("geo-dms")
.attr("title", "Mappe, foto aeree e altri dati per questa posizione")
-- geohack url e parametri
.tag("span")
local url = string.format('%s&pagename=%s&params=%s', cfg.geohackUrl,
.addClass("latitude")
mw.uri.encode(mw.title.getCurrentTitle().prefixedText, 'WIKI'), geohackParams)
.wikitext(tostring(dmsLat))
if self.args.name then
.done()
url = url .. '&title=' .. mw.uri.encode(self.args.name)
.wikitext(" ")
end
.tag("span")
.addClass("longitude")
local linkNode = mw.html.create('span')
.wikitext(tostring(dmsLong))
:addClass('plainlinks nourlexpansion')
.done()
:wikitext('[' .. url .. ' ' .. tostring(self.dmsLat) .. ' ' .. tostring(self.dmsLong) .. ']')
.done()
. :done()
return tostring(linkNode)
.tag("span")
end
.addClass("geo-multi-punct")
.wikitext("&#xfeff; / &#xfeff;")
.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 .. " " .. decLong)
.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 "")
-- se inline e title, in stampa deve apparire solo il primo
noprint = display.inline and "class=\"noprint\" " or ""
htmlTitle = "<span style=\"font-size: small;\"><span " .. noprint .. "id=\"coordinates\">[[Coordinate geografiche|Coordinate]]: "
 
-- Restituisce l'HTML per il microformat Geo.
return (display.inline and text or "") ..
-- FIXME Serve ancora nel 2024? La documentazione in proposito è scarsissima, e tutti
(display.title and (htmlTitle .. text .. "</span></span>") or "")
-- i link a strumenti che lo usano non vanno più. Inoltre, qui usiamo un elemento nascosto;
-- va bene lo stesso? Come verificarlo? Le specifiche non ne parlano.
--
-- @return {string}
function Coord:_buildGeoMarkup()
return mw.html.create('')
:wikitext(self.args.name and '<span class="vcard">' or '')
:tag('span')
:attr('style', 'display:none')
:addClass('geo')
:tag('span')
:addClass('latitude')
:wikitext(tostring(self.dmsLat))
:done()
:wikitext( ', ' )
:tag('span')
:addClass('longitude')
:wikitext(tostring(self.dmsLong))
:done()
:wikitext(self.args.name and ('<span style="display:none"> (<span class="fn org">' ..
self.args.name .. '</span>)</span></span>') or '')
:done()
end
 
-- Restituisce l'HTML contenente le coordinate con link che utilizza <maplink> (Kartographer)
-- Dato un input fino a 9 parametri posizionali e 4 con nome, ritorna un html
-- se possibile, e geohack altrimenti.
-- contenente le coordinate in formato dec e dms come collegamento esterno a geohack.php.
--
local function coord()
-- @return {string}
local decLat, degLong, dmsLat, dmsLong
function Coord:getHTML()
local reqFormat, defaultFormat, geohackParams, display, wdCat, ret
-- con args.from restitusce una stringa vuota se non c'è nessun dato
if self.args.from and #self.args < 2 and not tonumber(self.args[1]) then
-- parsifica il parametro "display"
return ''
display = getDisplay()
elseif self.args.display == 'debug' then
return self:getDebugCoords()
end
 
local display = getDisplay(self.args)
-- legge [[d:property:p625]] ed eventualmente la utilizza
wdCat = checkWikidata(display)
 
local coordLink
-- parsifica i parametri posizionali
if self.isEarth then
ret, reqFormat = xpcall(paramsParse, errhandler)
-- Usa maplink se possibile. A maggio 2024 supporta solo coordinate terrestri,
if not ret then
-- vedi T151138.
return reqFormat
coordLink = self:_buildMaplinkHTML()
end
else
coordLink = self:_buildGeohackLink()
end
 
local geo = self:_buildGeoMarkup();
if reqFormat == "dec" then
-- {{coord|1.111|2.222}}
decLat = DecCoord:new(args[1], "N")
decLong = DecCoord:new(args[2], "E")
elseif reqFormat == "d" then
-- {{coord|1.111|N|3.333|W}}
decLat = DecCoord:new(args[1], args[2])
decLong = DecCoord:new(args[3], args[4])
elseif reqFormat == "dm" then
-- {{coord|1|2|N|4|5|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|1|2|3|N|5|6|7|W}}
dmsLat = DmsCoord:new(args[1], args[2], args[3], args[4])
dmsLong = DmsCoord:new(args[5], args[6], args[7], args[8])
end
 
local html = coordLink .. tostring(geo) .. (self.args.notes or '')
-- conversioni dec <=> dms
if reqFormat == "dec" or reqFormat == "d" then
dmsLat = decLat:toDms()
dmsLong = decLong:toDms()
-- rimuove secondi e minuti se zero e presenti in lat e long
if dmsLat.sec == 0 and dmsLong.sec == 0 then
dmsLat.sec, dmsLong.sec = nil, nil
if dmsLat.min == 0 and dmsLong.min == 0 then
dmsLat.min, dmsLong.min = nil, nil
end
end
elseif reqFormat == "dm" or reqFormat == "dms" then
decLat = dmsLat:toDec()
decLong = dmsLong:toDec()
end
 
local frame = mw.getCurrentFrame()
if args["format"] then
defaultFormat = args["format"]
local ret = frame:extensionTag('templatestyles', '', {src = 'Modulo:Coord/styles.css'}) ..
elseif reqFormat == "dec" then
(display.inline and html or '');
defaultFormat = "dec"
else
defaultFormat = "dms"
end
 
if display.title then
-- crea la stringa param per geohack.php
-- formatta il risultato a seconda di args.display (nil, 'inline', 'title', 'inline,title')
if reqFormat == "dec" then
-- se inline e title, in stampa visualizza solo il primo
geohackParams = args[1] .. "_N_" .. args[2] .. "_E" .. (args[3] and ("_" .. args[3]) or "")
local htmlTitle = string.format(
else
'<div style="font-size: small"><span %s id="coordinates">[[Coordinate geografiche|Coordinate]]: %s</span></div>',
-- concatena solo i posizionali
display.inline and 'class="noprint"' or '',
geohackParams = table.concat(args, "_")
html
end
)
ret = ret .. frame:extensionTag( 'indicator', htmlTitle, { name = 'coordinates' } )
end
 
return ret ..
return display.debug and (decLat .. " " .. decLong .. " " .. dmsLat .. " " .. dmsLong) or
self:_setGeoData(display) ..
wdCat .. buildHTML(decLat, decLong, dmsLat, dmsLong, geohackParams, defaultFormat, display)
(mw.title.getCurrentTitle().namespace == 0 and self.wdCat or '')
end
 
-- =============================================================================
-- Entry-point per eventuale {{dms2dec}}
-- Funzioni esportate
function p.dms2dec(frame)
-- =============================================================================
local args = frame.args
 
local p = {}
-- {{dms2dec|N|2|3|4}}
 
return DmsCoord:new(args[2], args[3], args[4], args[1]):toDec():getDeg()
-- Funzione importata da https://en.wikipedia.org/w/index.php?title=Module:Coordinates&oldid=789126031
-- per estrarre lat, long, type, scale, dim, region, globe, source, dal link a geohack.php generato da coord.
function p.coord2text(frame)
if frame.args[1] == '' or frame.args[2] == '' or not frame.args[2] then return nil end
frame.args[2] = mw.text.trim(frame.args[2])
if frame.args[2] == 'lat' or frame.args[2] == 'long' then
local result, negative = mw.text.split((mw.ustring.match(frame.args[1],'[%.%d]+°[NS] [%.%d]+°[EW]') or ''), ' ')
if frame.args[2] == 'lat' then
result, negative = result[1], 'S'
else
result, negative = result[2], 'W'
end
result = mw.text.split(result, '°')
if result[2] == negative then result[1] = '-'..result[1] end
return result[1]
else
return mw.ustring.match(frame.args[1], 'params=.-_'..frame.args[2]..':(.-)[ _]')
end
end
 
-- Entry-pointFunzione per eventuale template {{dec2dmsdms2dec}}.
function p.dms2dec(frame)
local args = frame.args
-- {{dms2dec|N|2|3|4}}
return DmsCoord:new(args[2], args[3], args[4], args[1]):toDec():getDeg()
end
 
-- Funzione per eventuale template {{dec2dms}}.
function p.dec2dms(frame)
local args = frame.args
-- {{dec2dms|1.111|N|S}}
return DecCoord:new(args[1], tonumber(args[1]) >= 0 and args[2] or args[3]):toDms()
-- {{dec2dms|1.111|N|S}}
return DecCoord:new(args[1], tonumber(args[1]) >= 0 and args[2] or args[3]):toDms()
end
 
-- Funzione per l'utilizzo da un altro modulo.
-- Entry-point per {{coord}}
function p.coord_main(frameargs)
return Coord:new(args):getHTML()
-- copia i parametri ricevuti, eccetto quelli con nome valorizzati a stringa vuota
end
for k, v in pairs(frame:getParent().args) do
if v ~= "" or tonumber(k) then
args[k] = string.gsub(v, "^%s*(.-)%s*$", "%1")
end
end
-- retrocompatibilità con una funzionalità nascosta del precedente template:
-- ignorava qualunque parametro posizionale vuoto dopo longitudine e parametri geohack
for i = #args, 1, -1 do
if args[i] == "" then
table.remove(args, i)
else
break
end
end
-- rimuove i parametri posizionali vuoti front to back fermandosi al primo non vuoto
while args[1] == "" do
table.remove(args, 1)
end
 
-- Funzione per il template {{Coord}}.
return coord()
function p.main(frame)
return select(2, xpcall(function()
return p._main(getArgs(frame))
end, errhandler))
end