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