Módulo:Coordenadas
La documentación para este módulo puede ser creada en Módulo:Coordenadas/doc
--[[
Este módulo está diseñado para reemplazar la funcionalidad de {{coord}} y afines
plantillas. Proporciona varios métodos, incluye:
{{#Invoke:Coordenadas | coord }} : Función general de formato y visualización
valores de coordenadas.
{{#Invoke:Coordenadas | dec2dms }} : Función sencilla para la conversión el formato de grados con decimales (deg)
al formato DMS (grados minutos y segundos).
{{#Invoke:Coordenadas | dms2dec }} : Función sencilla para convertir el formato DMS (grados, minutos y segundos) al formato
de grados con decimales (deg).
]]
math_mod = require( "Module:Math" );
globalFrame = nil
coordinates = {};
--[[ Función auxiliar, sustituye a: {{coord/display/title}} ]]
function displaytitle (s, notes)
local htmlTitle = mw.html.create('span')
:attr{ id = 'coordinates' }
:css( 'font-size', 'small' )
:node( "[[Coordenadas geográficas|Coordenadas]]: " .. s .. notes )
local frame = mw.getCurrentFrame()
frame:extensionTag( 'indicator', tostring(htmlTitle), { name = 'coordinates' } )
return ''
end
--[[ Función auxiliar, sustituye a: {{coord/display/inline}} ]]
function displayinline (s, notes)
return s .. notes
end
--[[ Función auxiliar, que se utiliza en la detección de formato DMS]]
local dmsTest = function(first, second)
local concatenated = first:upper() .. second:upper();
if concatenated == "NE" or concatenated == "NW" or concatenated == "SE" or concatenated == "SW" or
concatenated == "EN" or concatenated == "WN" or concatenated == "ES" or concatenated == "WS" or
concatenated == "NO" or concatenated == "SO" or concatenated == "ON" or concatenated == "OS" then
return true;
end
return false;
end
--[[
parseDec
Transforma al formato decimal la latitud y la longitud en la estructura que utiliza
el visualizador de coordenadas
]]
function parseDec( lat, long, format )
local coordinateSpec = {}
local errors = {}
if long == "" or long == nil then
return nil, {{"parseDec", "falta la longitud"}}
end
errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
coordinateSpec["dec-lat"] = lat;
coordinateSpec["dec-long"] = long;
local mode = coordinates.determineMode( lat, long );
coordinateSpec["dms-lat"] = convert_dec2dms( lat, "N", "S", mode) -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
coordinateSpec["dms-long"] = convert_dec2dms( long, "E", "O", mode) -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
if format ~= "" then
coordinateSpec.default = format
else
coordinateSpec.default = "dec" -- por defecto el formato dado
end
return coordinateSpec, errors
end
--[[ Función auxiliar, manejar argumentos opcionales. ]]
function optionalArg(arg, suplement)
if arg ~= nil and arg ~= "" then
return arg .. suplement
end
return ""
end
--[[
parseDMS
Transforma el formato de grados, minutos y segundos de la latitud y longitud
en la estructura para ser utilizado en la visualización de coordenadas
]]
function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
local coordinateSpec = {}
local errors = {}
lat_f = lat_f:upper();
long_f = long_f:upper();
-- Compruebe si se ha especificado hacia atrás la posición E, O
if lat_f == 'E' or lat_f == 'W' or lat_f == "O" then
local t_d, t_m, t_s, t_f;
t_d = lat_d;
t_m = lat_m;
t_s = lat_s;
t_f = lat_f;
lat_d = long_d;
lat_m = long_m;
lat_s = long_s;
lat_f = long_f;
long_d = t_d;
long_m = t_m;
long_s = t_s;
long_f = t_f;
end
errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
if long_d == nil or long_d == "" then
table.insert(errors, {"parseDMS", "falta la longitud" })
end
if lat_m == nil and lat_s == nil and long_m == nil and long_s == nil and #errors == 0 then
if math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
if lat_f:upper() == 'S' then
lat_d = '-' .. lat_d;
end
if long_f:upper() == 'W' or long_f:upper() == 'O' then
long_d = '-' .. long_d;
end
return parseDec( lat_d, long_d, format );
end
end
if long_f == "W" then
long_f = "O" -- dirección a mostrar, excepto con coordinateSpec["param"]
end
coordinateSpec["dms-lat"] = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f
coordinateSpec["dec-lat"] = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}
if format ~= "" then
coordinateSpec.default = format
else
coordinateSpec.default = "dms"
end
return coordinateSpec, errors
end
--[[
specPrinter
Salida de formateador. Toma la estructura generada por cualquiera parseDec
o parseDMS y da el formato para su inclusión en Wikipedia.
]]
function specPrinter(args, coordinateSpec)
local uriComponents = coordinateSpec["param"]
if uriComponents == "" then
-- Devuelve error, nunca debe estar vacío o nulo
return "ERROR: El parémetro está vacio"
end
if coordinateSpec["name"] ~= "" and coordinateSpec["name"] ~= nil then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
end
local geodmshtml = '<span class="geo-dms" title="Mapas, fotos y otros datos de ' .. coordinateSpec["dms-lat"] .. ' ' .. coordinateSpec["dms-long"] .. '">'
.. '<span class="latitude">'.. coordinateSpec["dms-lat"] .. ' </span>'
.. '<span class="longitude">'..coordinateSpec["dms-long"] .. '</span>'
.. '</span>'
local geodechtml = '<span class="geo-dec" title="Mapas, fotos y otros datos de ' .. coordinateSpec["dec-lat"] .. ' ' .. coordinateSpec["dec-long"] .. '">'
.. '<span class="geo">'
.. '<span class="latitude">' .. coordinateSpec["dec-lat"] .. ', </span>'
.. '<span class="longitude">' .. coordinateSpec["dec-long"] .. '</span>'
.. '</span></span>'
local inner;
inner = '<span class="' .. displayDefault(coordinateSpec["default"], "dms" ) .. '">' .. geodmshtml .. '</span>'
.. '<span class="geo-multi-punct"> / </span>'
.. '<span class="' .. displayDefault(coordinateSpec["default"], "dec" ) .. '">';
if coordinateSpec["name"] == "" or coordinateSpec["name"] == nil then
inner = inner .. geodechtml .. '</span>'
else
inner = inner .. '<span class="vcard">' .. geodechtml
.. '<span style="display:none"> (<span class="fn org">'
.. coordinateSpec["name"] .. '</span>)</span></span></span>'
end
--return '<span class="plainlinksneverexpand">'
return '<span class="plainlinks nourlexpansion">'.. globalFrame:preprocess(
'[http://tools.wmflabs.org/geohack/geohack.php?language=es&pagename={{FULLPAGENAMEE}}¶ms=' ..
uriComponents .. ' ' .. inner .. ']') .. '</span>'
end
--http://tools.wmflabs.org/geohack/geohack.php?language=fr&pagename=Communaut%C3%A9_forale_de_Navarre¶ms=42.816666666667_N_1.65_W_type:city_globe:earth&title=
--[[
Formatos de los mensajes de error autogenerados
]]
function errorPrinter(errors)
local result = ""
for i,v in ipairs(errors) do
local errorHTML = '<small><strong class="error">Coordenadas: ' .. v[2] .. '</strong></small>'
result = result .. errorHTML .. "<br />"
end
return result
end
--[[
Determina la clase CSS requerida para mostrar las coordenadas
Por lo general, "geo-nodefault" está oculto por CSS, a menos que un usuario haya omitido esto
"default" es el modo que aparece por defecto, aunque el usuario puede especificarlo al llamar a la plantilla {{coord}}
modo es el modo de visualización (dec o DMS) que vamos a necesitar para determinar la clase css para
]]
function displayDefault(default, mode)
if default == "" then
default = "dec"
end
if default == mode then
return "geo-default"
else
return "geo-nondefault"
end
end
--[[
Comprueba los argumentos de entrada de coord para determinar el tipo de datos que se proporcionan
y luego hacer el proceso necesario.
]]
function formatTest(args)
local result, errors;
local primary = false;
if args[1] == "" then
-- Ninguna lat lógica
return errorPrinter( {{"formatTest", "Falta la latitud"}} )
elseif args[4] == "" and args[5] == "" and args[6] == "" then
-- dec lógico
result, errors = parseDec( formatPunt(args[1]), formatPunt(args[2]), args['format'] )
if result == nil then
return errorPrinter( errors );
end
result.param = table.concat( {formatPunt(args[1]), "_N_", formatPunt(args[2]), "_E_", args[3] } );
elseif dmsTest(args[4], args[8]) then
-- dms lógico
result, errors = parseDMS( args[1], args[2], formatPunt(args[3]), args[4],
args[5], args[6], formatPunt(args[7]), args[8], args['format'] )
result.param = table.concat( { args[1], args[2], formatPunt(args[3]), args[4], args[5],
args[6], formatPunt(args[7]), formatLongW(args[8]), args[9] } , '_' );
if args[10] ~= '' then
table.insert( errors, { 'formatTest', 'Sobran parámetros' } );
end
elseif dmsTest(args[3], args[6]) then
-- dm lógico
result, errors = parseDMS( args[1], formatPunt(args[2]), nil, args[3],
args[4], formatPunt(args[5]), nil, args[6], args['format'] )
result.param = table.concat( { args[1], formatPunt(args[2]), args[3], args[4], formatPunt(args[5]),
formatLongW(args[6]), args[7] } , '_' );
if args[8] ~= '' then
table.insert( errors, { 'formatTest', 'sobran paràmetros' } );
end
elseif dmsTest(args[2], args[4]) then
-- d lógico
result, errors = parseDMS( formatPunt(args[1]), nil, nil, args[2],
formatPunt(args[3]), nil, nil, args[4], args['format'] )
result.param = table.concat( { formatPunt(args[1]), args[2], formatPunt(args[3]), formatLongW(args[4]), args[5] } , '_' );
if args[6] ~= '' then
table.insert( errors, { 'formatTest', 'sobran parámetros' } );
end
else
-- Error
return errorPrinter( {{"formatTest", "Formato no reconocido"}} )
end
result.name = args["name"] or args["nom"]
local extra_param = {'dim', 'globe', 'scale', 'region', 'source', 'type'}
for _, v in ipairs( extra_param ) do
if (args[v] or '') ~= '' then
table.insert( errors, {'formatTest', 'Parámetro: "' .. v .. '=" debería de ser"' .. v .. ':"' } );
end
end
if #errors == 0 then
return specPrinter( args, result )
else
return specPrinter( args, result ) .. " " .. errorPrinter(errors) .. '[[Categoría:Wikipedia:Mantenimiento de la plantilla coord]]';
end
end
--[[ Función auxiliar para convertir la coma decimal en punto decimal ]]
function formatPunt(num)
return mw.ustring.gsub(num, ",", ".")
end
--[[ Función auxilar para convertir longitud O a W ]]
function formatLongW(dir)
if dir:upper() == "O" then
return "W"
end
return dir
end
--[[
Función auxiliar, convierte la latitud o longitud al formato decimal
grados, minutos y segundos en formato basado en la precisión especificada.
]]
function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
local coord = tonumber(coordinate) or 0
local postfix
if coord >= 0 then
postfix = firstPostfix
else
postfix = secondPostfix
end
precision = precision:lower();
if precision == "dms" then
return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
elseif precision == "dm" then
return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
elseif precision == "d" then
return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
end
end
--[[ Función auxiliar, convierte decimales a grados ]]
function convert_dec2dms_d(coordinate)
local d = math_mod._round( coordinate, 0 ) .. "°"
return d .. ""
end
--[[ Función auxiliar, convierte decimales a grados y minutos ]]
function convert_dec2dms_dm(coordinate)
coordinate = math_mod._round( coordinate * 60, 0 );
local m = coordinate % 60;
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m )
end
--[[ Función auxiliar, convierte decimales a grados, minutos y segundos ]]
function convert_dec2dms_dms(coordinate)
coordinate = math_mod._round( coordinate * 60 * 60, 0 );
local s = coordinate % 60
coordinate = math.floor( (coordinate - s) / 60 );
local m = coordinate % 60
coordinate = math.floor( (coordinate - m) / 60 );
local d = coordinate % 360 .."°"
return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end
--[[
Convierte el formato DMS con un N o E en coordenadas decimales
]]
function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
local degrees = tonumber(degrees_str) or 0
local minutes = tonumber(minutes_str) or 0
local seconds = tonumber(seconds_str) or 0
local factor
direction = mw.ustring.gsub(direction, '^[ ]*(.-)[ ]*$', '%1');
if direction == "N" or direction == "E" then
factor = 1
else
factor = -1
end
local precision = 0
if seconds_str ~= nil and seconds_str ~= '' then
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
elseif minutes_str ~= nil and minutes_str ~= '' then
precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
else
precision = math.max( math_mod._precision(degrees_str), 0 );
end
local decimal = factor * (degrees+(minutes+seconds/60)/60)
return string.format( "%." .. precision .. "f", decimal ) -- no en el número donde se basa la cadena.
end
--[[
Comprueba los valores de entrada y los errores fuera de rango.
]]
function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
local errors = {};
lat_d = tonumber( lat_d ) or 0;
lat_m = tonumber( lat_m ) or 0;
lat_s = tonumber( lat_s ) or 0;
long_d = tonumber( long_d ) or 0;
long_m = tonumber( long_m ) or 0;
long_s = tonumber( long_s ) or 0;
if strong then
if lat_d < 0 then
table.insert(errors, {source, "Latitud negativa y con dirección N"})
end
if long_d < 0 then
table.insert(errors, {source, "Longitud negativa y con dirección E"})
end
--[[
#coordinates es contradictoria sobre si esto es un error. Si "globe" es
especificado, no será error en esta condición, pero de lo contrario si lo será.
solo por no deshabilitar esta comprobación.
if long_d > 180 then
table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
end
]]
end
if lat_d > 90 then
table.insert(errors, {source, "grados de latitud > 90"})
end
if lat_d < -90 then
table.insert(errors, {source, "grados de latitud < -90"})
end
if lat_m >= 60 then
table.insert(errors, {source, "minutos de latitud >= 60"})
end
if lat_m < 0 then
table.insert(errors, {source, "minutos de latitud < 0"})
end
if lat_s >= 60 then
table.insert(errors, {source, "segundos de latitud >= 60"})
end
if lat_s < 0 then
table.insert(errors, {source, "segundos de latitud < 0"})
end
if long_d >= 360 then
table.insert(errors, {source, "grados de longitud >= 360"})
end
if long_d <= -360 then
table.insert(errors, {source, "grados de longitud <= -360"})
end
if long_m >= 60 then
table.insert(errors, {source, "minutos de longitud >= 60"})
end
if long_m < 0 then
table.insert(errors, {source, "minutos de longitud < 0"})
end
if long_s >= 60 then
table.insert(errors, {source, "segundos de longitud >= 60"})
end
if long_s < 0 then
table.insert(errors, {source, "segundos de longitud < 0"})
end
return errors;
end
--[[
dec2dms
Función para permitir que las plantillas llamen directamente a dec2dms.
Uso:
{{ #Invoke:Coordenadas | dec2dms | <coordenadas_decimales> | <sufijo_positivo> |
<sufijo_negativo> | <precision> }}
Las <coordenas_decimales> se convierte al formato DMS. Si es positivo, el <sufijo_positivo>
se añade (N o E), si es negativo, el <sufijo_negativo> se adjunta si la
<precision> especificada puede ser 'D', 'DM' o 'DMS' para especificar el nivel de detalle
para utilizar.
]]
function coordinates.dec2dms(frame)
globalFrame = frame
local coordinate = frame.args[1]
local firstPostfix = frame.args[2]
local secondPostfix = frame.args[3]
local precision = frame.args[4]
return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end
--[[
Función auxiliar para determinar si se debe utilizar el formato D, DM, o DMS
en la función "precision" de la entrada decimal
.
]]
function coordinates.determineMode( value1, value2 )
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
if precision <= 0 then
return 'd'
elseif precision <= 2 then
return 'dm';
else
return 'dms';
end
end
--[[
dms2dec
Wrapper para permitir que las plantillas llamen directamente a dms2dec.
Uso:
{{ #Invoke:Coordenadas | dms2dec | <indicador de dirección> | grados |
minutos | segundos }}
Convierte los valores de formato DMS especificados en grados, minutos y segundos en grados decimales.
El indicador de dirección puede ser N, S, E, O , y determina si la salida es
positiva (es decir, N o E) o negativa (es decir, S o O).
]]
function coordinates.dms2dec(frame)
globalFrame = frame
local direction = frame.args[1]
local degrees = frame.args[2]
local minutes = frame.args[3]
local seconds = frame.args[4]
return convert_dms2dec(direction, degrees, minutes, seconds)
end
--[[
coord
Principal punto de entrada para la función Lua para reemplazar {{coord}}
Uso:
{{ #Invoke:Coordenadas | coord }}
{{ #Invoke:Coordenadas | coord | lat | long }}
{{ #Invoke:Coordenadas | coord | lat | <lat_dirección> | <long> | <long_dirección> }}
...
Consulte la página de documentación {{coord}} para muchos parámetros adicionales y
opciones de configuración.
Nota: Esta función proporciona los elementos de presentación visual de {{coord}}.
Para poder cargar coordenadas en la base de datos, el parser function (analizador de función) {{#coordinates:}} también debe ser llamado, esto se realiza automáticamente en la versión de {{Coord}} en módulo Lua.
]]
function coordinates.coord(frame)
globalFrame = frame
local args = frame.args
if args[1] == nil then
local pFrame = frame:getParent();
args = pFrame.args;
for k,v in pairs( frame.args ) do
args[k] = v;
end
end
for i=1,10 do
if args[i] == nil then
args[i] = ""
else
args[i] = args[i]:match( '^%s*(.-)%s*$' ); --elimina espacios en blanco
end
end
args['format'] = args['formato'] or args['format'] or '';
local contents = formatTest(args)
local Notes = args.notes or ""
local Display = string.lower(args.display or "inline")
if Display == '' then
Display = 'inline';
end
local text = ''
if string.find( Display, 'inline' ) ~= nil or Display == 'i' or
Display == 'it' or Display == 'ti' then
text = displayinline(contents, Notes)
end
if string.find( Display, 'title' ) ~= nil or Display == 't' or
Display == 'it' or Display == 'ti' then
text = text .. displaytitle(contents, Notes)
end
return text
end
function coordinates.coord2(frame)
globalFrame = frame.args.gf
local origArgs = {}
if frame == mw.getCurrentFrame() then
origArgs = frame:getParent().args
else
origArgs = frame.args
end
args = {}
for k, v in pairs(origArgs) do
args[k] = v
end
for i=1,10 do
if args[i] == nil then
args[i] = ""
else
args[i] = args[i]:match( '^%s*(.-)%s*$' ); --elimina espacios en blanco
end
end
args['format'] = args['format'] or '';
local contents = formatTest(args)
local Notes = args.notes or ""
local Display = string.lower(args.display or "inline")
if Display == '' then
Display = 'inline';
end
local text = ''
if string.find( Display, 'inline' ) ~= nil or Display == 'i' or
Display == 'it' or Display == 'ti' then
text = displayinline(contents, Notes)
end
if string.find( Display, 'title' ) ~= nil or Display == 't' or
Display == 'it' or Display == 'ti' then
text = text .. displaytitle(contents, Notes)
end
return text
end
return coordinates