پودمان:Coordinates2
توضیحات این پودمان میتواند در پودمان:Coordinates2/توضیحات قرار گیرد.
--[[ * 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 -- Configurazione local cfg = mw.loadData("Module:Coord2/config") ------------------------------------------------------------------------------- -- 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 la stringa "0 + numero" quando il numero è di una sola cifra, altrimenti lo stesso numero 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) local function numberToString(num) -- la parentesi () extra serve per non ritornare anche il gsub.count return (string.format("%f", num):gsub("%.?0+$", "")) end -- Ritorna latitudine e longitudine della [[d:property:p625]] -- o nil nel caso non siano presenti local function getWikidataCoordinates() local entity, value, lat, long entity = mw.wikibase.getEntityObject() if entity and entity.claims and entity.claims.P625 and #entity.claims.P625 > 0 and entity.claims.P625[1].mainsnak.snaktype == "value" then value = entity.claims.P625[1].mainsnak.datavalue.value lat = round(value.latitude, 6) long = round(value.longitude, 6) end return lat, long end ------------------------------------------------------------------------------- -- Parsing parametri ------------------------------------------------------------------------------- -- Error handler per xpcall, formatta l'errore -- nel namespace principale aggiunge una categoria di warning local function errhandler(msg) local cat = "" 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() local function dumpError(...) local arg = {...} table.insert(errorTable, "* ") for _, val in ipairs(arg) do table.insert(errorTable, val) end table.insert(errorTable, "\n") end -- Parsifica il parametro "display" 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 -- Con le richieste "dm" e "dms" verifica se ci sono -- parametri lasciati vuoti in modo valido. -- Ritorna il tipo di richiesta dopo la eventuale semplificazione. local function paramsEmpty(reqFormat) if reqFormat == "dms" then -- {{coord|1|2||N|5|6||E}} valido if args[3] == "" and args[7] == "" then 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 if reqFormat == "dm" then -- {{coord|1||N|4||E}} valido 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 return reqFormat end -- Riconosce il tipo di richiesta ("dec", "d", "dm" o "dms") e parsifica i parametri posizionali -- Ritorna il tipo di richiesta. local function paramsParse() local reqFormat, globe, earth, prefix, num, str local param = {} -- riconoscimento tipo di richiesta if #args < 2 then error("* coordinate non specificate") elseif #args < 4 then reqFormat = "dec" elseif #args < 6 then reqFormat = "d" elseif #args < 8 then reqFormat = "dm" elseif #args < 10 then reqFormat = "dms" else error("* errato numero di parametri") end -- valida i parametri che possono essere lasciati vuoti reqFormat = paramsEmpty(reqFormat) -- validazione parametri posizionali currFormat = cfg.params[reqFormat] globe = args[#args]:match("globe:(%w+)") earth = not globe or globe == "earth" 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 if #errorTable > 0 then error(table.concat(errorTable)) end return reqFormat end ------------------------------------------------------------------------------- -- classi DecCoord e DmsCoord ------------------------------------------------------------------------------- -- Rappresenta una coordinata (lat o long) in gradi decimali local DecCoord = {} -- Rappresenta una coordinata (lat o long) in gradi/minuti/secondi local DmsCoord = {} -- Costruttore di DecCoord -- deg: gradi decimali, positivi o negativi, se negativi viene cambiato il segno e -- la direzione cardinale eventualmente invertita -- card: direzione cardinale (N|S|E|W) function DecCoord:new(deg, card) local self = {} setmetatable(self, { __index = DecCoord, __tostring = function(t) return self:__tostring() end, __concat = function(t, t2) return tostring(t) .. tostring(t2) end }) 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 numberToString(self.deg) .. "°" .. self.card end -- Ritorna i gradi con segno function DecCoord:getDeg() local deg = self.deg * ((self.card == "N" or self.card =="E") and 1 or -1) return numberToString(deg) end -- Ritorna un nuovo oggetto DmsCoord, convertendo in gradi/minuti/secondi 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 return DmsCoord:new(deg, min, sec, self.card) end -- Costruttore di DmsCoord -- deg: gradi -- min: minuti, può essere nil -- sec: secondi, può essere nil -- card: direzione cardinale (N|S|E|W) function DmsCoord:new(deg, min, sec, card) local self = {} setmetatable (self, { __index = DmsCoord, __tostring = function(t) return self:__tostring() end, __concat = function(t, t2) return tostring(t) .. tostring(t2) end }) self.deg = tonumber(deg) self.min = min and tonumber(min) self.sec = sec and tonumber(sec) self.card = card return self end -- Richiamata automaticamente ogni volta che è richiesto un tostring o un concatenamento function DmsCoord:__tostring() return self.deg .. "°" .. (self.min and (padleft0(self.min) .. "′") or "") .. (self.sec and (padleft0(self.sec) .. "″") or "") .. self.card end -- Ritorna un nuovo oggetto DecCoord, convertendo in gradi decimali function DmsCoord:toDec() local deg = round((self.deg + ((self.min or 0) + (self.sec or 0) / 60) / 60), 6) return DecCoord:new(deg, self.card) end ------------------------------------------------------------------------------- -- Coord ------------------------------------------------------------------------------- -- Nel namespace principale e con display.title legge la [[d:property:p625]] -- e la utilizza come latitudine e longitudine se non fornite dall'utente. -- Ritorna la categoria appropriata per [[Categoria:Wikidata]], altrimenti una stringa vuota local function checkWikidata(display) local wdLat, wdLong, cat if mw.title.getCurrentTitle().namespace == 0 and display.title then wdLat, wdLong = getWikidataCoordinates() if wdLat and wdLong then -- 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 table.insert(args, numberToString(wdLat)) table.insert(args, numberToString(wdLong)) if #args == 3 then table.insert(args, table.remove(args, 1)) end cat = "Coordinate assenti ma presenti su Wikidata" end else cat = "Coordinate non presenti su Wikidata" end end return cat and ("[[Categoria:" .. cat .. "]]") or "" end -- Crea l'HTML ritornato da Coord local function buildHTML(decLat, decLong, dmsLat, dmsLong, geohackParams, defaultFormat, display) local root, text, url, htmlTitle, noprint -- geohack url e parametri url = cfg.geohackUrl .. "&pagename=" .. mw.uri.encode(mw.title.getCurrentTitle().prefixedText, "WIKI") .. "¶ms=" .. geohackParams .. (args["name"] and ("&title=" .. mw.uri.encode(args["name"])) or "") root = mw.html.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(tostring(dmsLat)) :done() :wikitext(" ") :tag("span") :addClass("longitude") :wikitext(tostring(dmsLong)) :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 .. " " .. 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\">[[مختصات جغرافیایی|مختصات]]: " return (display.inline and text or "") .. (display.title and (htmlTitle .. text .. "</span></span>") or "") end -- Dato un input fino a 9 parametri posizionali e 4 con nome, ritorna un html -- contenente le coordinate in formato dec e dms come collegamento esterno a geohack.php. local function coord() local decLat, degLong, dmsLat, dmsLong local reqFormat, defaultFormat, geohackParams, display, wdCat, gdStr, gdRet, ret -- parsifica il parametro "display" display = getDisplay() -- legge [[d:property:p625]] ed eventualmente la utilizza wdCat = checkWikidata(display) -- parsifica i parametri posizionali ret, reqFormat = xpcall(paramsParse, errhandler) if not ret then return reqFormat end 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 -- 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 if args["format"] then defaultFormat = args["format"] elseif reqFormat == "dec" then defaultFormat = "dec" else defaultFormat = "dms" end -- crea la stringa param per geohack.php if reqFormat == "dec" then geohackParams = args[1] .. "_N_" .. args[2] .. "_E" .. (args[3] and ("_" .. args[3]) or "") else -- concatena solo i posizionali geohackParams = table.concat(args, "_") end -- utilizza l'estensione [[:mw:Extension:GeoData]] gdStr = string.format("{{#coordinates:%s|%s|name=%s}}", table.concat(args, "|"), (display.title and mw.title.getCurrentTitle().namespace == 0) and "primary" or "", args["name"] or "") gdRet = mw.getCurrentFrame():preprocess(gdStr) return display.debug and (decLat .. " " .. decLong .. " " .. dmsLat .. " " .. dmsLong) or wdCat .. buildHTML(decLat, decLong, dmsLat, dmsLong, geohackParams, defaultFormat, display) .. gdRet end -- Entry-point per eventuale {{dms2dec}} 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 -- Entry-point per eventuale {{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() end -- Entry-point per {{coord}} function p.coord(frame) -- copia i parametri ricevuti, eccetto quelli con nome valorizzati a stringa vuota 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 return coord() end return p