Модуль:Coordinates: различия между версиями

Материал из Янтарной ноосферы, свободной интернет-энциклопедии
(Атомный трамвай изменил модель содержимого страницы Модуль:Coordinates с «вики-текст» на «Scribunto»: сразу надо было это сделать, скотина)
Метка: изменение модели содержимого
(перевставил из рувики)
 
Строка 1: Строка 1:
--[[
--[[
This module is intended to replace the functionality of {{Coord}} and related
This module is intended to replace the functionality of {{Coord}} and related
templates. So. It provides several methods, including
templates. It provides several methods, including
 
{{#Invoke:Coordinates | coord }} : General function formatting and displaying
{{#Invoke:Coordinates | coord }} : General function formatting and displaying
coordinate values.
coordinate values.
 
{{#Invoke:Coordinates | dec2dms }} : Simple function for converting decimal
{{#Invoke:Coordinates | dec2dms }} : Simple function for converting decimal
degree values to DMS format.
degree values to DMS format.
g
{{#Invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
{{#Invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
to decimal degree format.
to decimal degree format.
 
]]
]]
 
math_mod = require( "Module:Math" );
math_mod = require( "Module:Math" );
globalFrame = nil
globalFrame = nil
 
coordinates = {};
coordinates = {};
 
local globe_list = '||earth|mercury|venus|moon|mars|phobos|deimos|ganymede|callisto|io|europa|mimas|enceladus|tethys|dione|rhea|titan|hyperion|iapetus|phoebe|miranda|ariel|umbriel|titania|oberon|triton|pluto|charon|ceres|vesta|'
 
local Dispay = '';
 
--[[ Helper function, replacement for {{coord/display/title}} ]]
--[[ Helper function, replacement for {{coord/display/title}} ]]
function displaytitle (s, notes)
function displaytitle (s, notes, globalFrame)
    local l = "[[Географические координаты|Координаты]]: " .. s
return globalFrame:extensionTag{
    local co = '<span id="coordinates">' .. l .. notes .. '</span>';
name = 'indicator',
    return '<span style="font-size: small;">' .. co .. '</span>';
content = s .. notes,
args = { name = '0-coord' }
};
end
end
 
--[[ Helper function, Replacement for {{coord/display/inline}} ]]
--[[ Helper function, Replacement for {{coord/display/inline}} ]]
function displayinline (s, notes)
function displayinline (s, notes)
    return s .. notes
return s .. notes
end
end
 
--[[ Helper function, used in detecting DMS formatting ]]
--[[ Helper function, used in detecting DMS formatting ]]
local dmsTest = function(first, second)
local dmsTest = function(first, second)
    local concatenated = first:upper() .. second:upper();
local concatenated = first:upper() .. second:upper();
    if concatenated == "NE" or concatenated == "NW" or concatenated == "SE" or concatenated == "SW" or
if concatenated == "NE" or concatenated == "NW" or concatenated == "SE" or concatenated == "SW" or
        concatenated == "EN" or concatenated == "WN" or concatenated == "ES" or concatenated == "WS" then
concatenated == "EN" or concatenated == "WN" or concatenated == "ES" or concatenated == "WS" then
        return true;
return true;
    end
end
    return false;
return false;
end
end
 
--[[
--[[
parseDec
parseDec
 
Transforms decimal format latitude and longitude into the a
Transforms decimal format latitude and longitude into the a
structure to be used in displaying coordinates
structure to be used in displaying coordinates
]]
]]
function parseDec( lat, long, format )
function parseDec( lat, long, format )
    local coordinateSpec = {}
local coordinateSpec = {}
    local errors = {}
local errors = {}
 
    if long == "" or long == nil then
errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
        return nil, {{"parseDec", "Пропущена долгота"}}
if #errors ~= 0 then
    end
return nil, errors
end
    errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
    coordinateSpec["dec-lat"]  = lat;
coordinateSpec["dec-lat"]  = lat;
    coordinateSpec["dec-long"] = long;
coordinateSpec["dec-long"] = long;
 
    local mode = coordinates.determineMode( lat, long );
local mode = coordinates.determineMode( lat, long );
    coordinateSpec["dms-lat"]  = convert_dec2dms( lat, "&nbsp;с.&nbsp;ш.", "&nbsp;ю.&nbsp;ш.", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
coordinateSpec["dms-lat"]  = convert_dec2dms( lat, "&nbsp;с.&nbsp;ш.", "&nbsp;ю.&nbsp;ш.", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
    coordinateSpec["dms-long"] = convert_dec2dms( long, "&nbsp;в.&nbsp;д.", "&nbsp;з.&nbsp;д.", mode)  -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
coordinateSpec["dms-long"] = convert_dec2dms( long, "&nbsp;в.&nbsp;д.", "&nbsp;з.&nbsp;д.", mode)  -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
    if format ~= "" then
if format ~= "" then
        coordinateSpec.default = format
coordinateSpec.default = format
    else
else
        coordinateSpec.default = "dec"
coordinateSpec.default = "dms"
    end
end
 
    return coordinateSpec, errors
return coordinateSpec, errors
end
end
 
--[[ Helper function, handle optional args. ]]
--[[ Helper function, handle optional args. ]]
function optionalArg(arg, suplement)
function optionalArg(arg, suplement, bool)
    if arg ~= nil and arg ~= "" then
if arg ~= nil and arg ~= "" then  
        return string.format( "%02d", tonumber( arg ) ) .. suplement
arg = (tonumber( arg ) % 1 == 0 or not bool) and
    end
string.format( "%02d", arg ) or
    return ""
string.format( "%02.2f", arg):gsub('%.', ',')
return arg  .. suplement
end
return ""
end
end
 
--[[
--[[
parseDMS
parseDMS
 
Transforms degrees, minutes, seconds format latitude and longitude
Transforms degrees, minutes, seconds format latitude and longitude
into the a structure to be used in displaying coordinates
into the a structure to be used in displaying coordinates
]]
]]
function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
function parseDMS( lat_d, lat_m, lat_s, lat_f, long_d, long_m, long_s, long_f, format )
    local coordinateSpec = {}
local coordinateSpec = {}
    local errors = {}
local errors = {}
    lat_f = lat_f:upper();
lat_f = lat_f:upper();
    long_f = long_f:upper();
long_f = long_f:upper();
    -- Check if specified backward
-- Check if specified backward
    if lat_f == 'E' or lat_f == 'W' then
if lat_f == 'E' or lat_f == 'W' then
        local t_d, t_m, t_s, t_f;
local t_d, t_m, t_s, t_f;
        t_d = lat_d;
t_d = lat_d;
        t_m = lat_m;
t_m = lat_m;
        t_s = lat_s;
t_s = lat_s;
        t_f = lat_f;
t_f = lat_f;
        lat_d = long_d;
lat_d = long_d;
        lat_m = long_m;
lat_m = long_m;
        lat_s = long_s;
lat_s = long_s;
        lat_f = long_f;
lat_f = long_f;
        long_d = t_d;
long_d = t_d;
        long_m = t_m;
long_m = t_m;
        long_s = t_s;
long_s = t_s;
        long_f = t_f;
long_f = t_f;
    end
end
    errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
errors = validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, 'parseDMS', true );
    if long_d == nil or long_d == "" then
if #errors ~= 0 then
        table.insert(errors, {"parseDMS", "Пропущена долгота" })
return nil, errors
    end
end
    coordinateSpec["dec-lat"]  = convert_dms2dec(lat_f, lat_d, lat_m, lat_s) -- {{coord/dms2dec|{{{4}}}|{{{1}}}|0{{{2}}}|0{{{3}}}}}
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}}}}}
coordinateSpec["dec-long"] = convert_dms2dec(long_f, long_d, long_m, long_s) -- {{coord/dms2dec|{{{8}}}|{{{5}}}|0{{{6}}}|0{{{7}}}}}
 
    if lat_m == nil and lat_s == nil and long_m == nil and long_s == nil and #errors == 0  
if lat_m == nil and lat_s == nil and long_m == nil and long_s == nil and #errors == 0  
        or math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
or math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
            if lat_f:upper() == 'S' then
if lat_f:upper() == 'S' then
                lat_d = '-' .. lat_d;
lat_d = '-' .. lat_d;
                lat_f = "&nbsp;ю.&nbsp;ш.";
lat_f = "&nbsp;ю.&nbsp;ш.";
            else
else
                lat_f = "&nbsp;с.&nbsp;ш.";
lat_f = "&nbsp;с.&nbsp;ш.";
            end
end
            if long_f:upper() == 'W' then
if long_f:upper() == 'W' then
                long_d = '-' .. long_d;
long_d = '-' .. long_d;
                long_f = "&nbsp;з.&nbsp;д.";
long_f = "&nbsp;з.&nbsp;д.";
            else
else
                long_f = "&nbsp;в.&nbsp;д.";
long_f = "&nbsp;в.&nbsp;д.";
            end
end
            return parseDec( lat_d, long_d, format );
return parseDec( lat_d, long_d, format );
    end
end
 
    if lat_f:upper() == 'S' then
if lat_f:upper() == 'S' then
        lat_f = "&nbsp;ю.&nbsp;ш.";
lat_f = "&nbsp;ю.&nbsp;ш.";
    else
else
        lat_f = "&nbsp;с.&nbsp;ш.";
lat_f = "&nbsp;с.&nbsp;ш.";
    end
end
 
    if long_f:upper() == 'E' then
if long_f:upper() == 'E' then
        long_f = "&nbsp;в.&nbsp;д.";
long_f = "&nbsp;в.&nbsp;д.";
    else
else
        long_f = "&nbsp;з.&nbsp;д.";
long_f = "&nbsp;з.&nbsp;д.";
    end
end
   
    coordinateSpec["dms-lat"]  = lat_d.."°"..optionalArg(lat_m,"′") .. optionalArg(lat_s,"″") .. lat_f
if lat_s == '0' and long_s == '0' then
    coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f
lat_s, long_s = nil, nil
if lat_m == '0' and long_m == '0' then
    if format ~= "" then
lat_m, long_m = nil, nil
        coordinateSpec.default = format
end
    else
end
        coordinateSpec.default = "dms"
 
    end
coordinateSpec["dms-lat"]  = lat_d.."°"..optionalArg(lat_m,"′", true) .. optionalArg(lat_s,"″") .. lat_f
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′", true) .. optionalArg(long_s,"″") .. long_f
    return coordinateSpec, errors
 
if format ~= "" then
coordinateSpec.default = format
else
coordinateSpec.default = "dms"
end
 
return coordinateSpec, errors
end
end
 
--[[
splitParam
 
Split the parameter string and convert it into an object.
]]
function splitParam( param )
local out = {}
for pair in mw.text.gsplit( param, '_', true ) do
local keyValue = mw.text.split( pair, ':', true )
if #keyValue == 2 then
out[keyValue[1]] = keyValue[2]
end
end
return out
end
 
--[[
--[[
specPrinter
specPrinter
 
Output formatter.  Takes the structure generated by either parseDec
Output formatter.  Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
or parseDMS and formats it for inclusion on Wikipedia.
]]
]]
function specPrinter(args, coordinateSpec)
function specPrinter(args)
    local uriComponents = coordinateSpec["param"]
local coordinateSpec, errors = formatTest(args)
    if uriComponents == "" then
        -- RETURN error, should never be empty or nil
if coordinateSpec == nil then
        return "Ошибка: не задан param"
return errors
    end
end
    if args["name"] ~= "" and args["name"] ~= nil then
        uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
local param = coordinateSpec["param"] .. '_' .. coordinateSpec["extra_param"]
    end
local uriComponents = param
if uriComponents == "" then
    local geodmshtml = '<span class="geo-dms" title="Различные карты и схемы для этого места">'
-- RETURN error, should never be empty or nil
            .. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
return "Ошибка: не задан param"
            .. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
end
            .. '</span>'
if args["name"] ~= "" and args["name"] ~= nil then
uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
    local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
end
    if lat < 0 then
local text = ''
        -- FIXME this breaks the pre-existing precision
        geodeclat = coordinateSpec["dec-lat"]:sub(2) .. "°&nbsp;ю.&nbsp;ш."
local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
    else
if lat < 0 then
        geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°&nbsp;с.&nbsp;ш."
-- FIXME this breaks the pre-existing precision
    end
geodeclat = coordinateSpec["dec-lat"]:sub(2):gsub('%.', ',') .. "°&nbsp;ю.&nbsp;ш."
else
    local long = tonumber( coordinateSpec["dec-long"] ) or 0
geodeclat = (coordinateSpec["dec-lat"]:gsub('%.', ',') or 0) .. "°&nbsp;с.&nbsp;ш."
    if long < 0 then
end
        -- FIXME does not handle unicode minus
 
        geodeclong = coordinateSpec["dec-long"]:sub(2) .. "°&nbsp;з.&nbsp;д."
local long = tonumber( coordinateSpec["dec-long"] ) or 0
    else
if long < 0 then
        geodeclong = (coordinateSpec["dec-long"] or 0) .. "°&nbsp;в.&nbsp;д."
-- FIXME does not handle unicode minus
    end
geodeclong = coordinateSpec["dec-long"]:sub(2):gsub('%.', ',') .. "°&nbsp;з.&nbsp;д."
else
    local geodechtml = '<span class="geo-dec" title="Различные карты и схемы для этого места">'
geodeclong = (coordinateSpec["dec-long"]:gsub('%.', ',') or 0) .. "°&nbsp;в.&nbsp;д."
            .. geodeclat .. ' '
end
            .. geodeclong
            .. '</span>'
local geodmshtml = '<span class="geo-dms" title="Различные карты и схемы для этого места">'
.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
    local geonumhtml = '<span class="geo">'
.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
            .. coordinateSpec["dec-lat"] .. '; '
.. '</span>'
            .. coordinateSpec["dec-long"]
 
            .. '</span>'
local geodechtml = '<span class="geo-dec" title="Различные карты и схемы для этого места">'
.. geodeclat .. ' '
    local inner;
.. geodeclong
    inner = '<span class="geo-geo-' .. coordinateSpec.default .. '"><span class="geo-dms">' .. geodmshtml .. '</span>'
.. '</span>'
                .. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
 
                .. '<span class="geo-dec">';
local geonumhtml = '<span class="geo">'
.. coordinateSpec["dec-lat"] .. '; '
    if args["name"] == "" or args["name"] == nil then
.. coordinateSpec["dec-long"]
        inner = inner .. geodechtml
.. '</span>'
                .. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span></span>'
 
    else
local inner;
        inner = inner .. '<span class="vcard">' .. geodechtml
inner = '<span class="geo-geo-' .. coordinateSpec.default .. '"><span class="geo-dms">' .. geodmshtml .. '</span>'
                .. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
.. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
                .. '<span style="display:none">&#xfeff; (<span class="fn org">'
.. '<span class="geo-dec">';
                .. args["name"] .. '</span>)</span></span></span></span>'
 
    end
if args["name"] == "" or args["name"] == nil then
inner = inner .. geodechtml
    local result = '<span class="plainlinks nourlexpansion">' .. globalFrame:preprocess(
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span></span>'
        '[//tools.wmflabs.org/geohack/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
else
        uriComponents .. ' ' .. inner .. ']')
inner = inner .. '<span class="vcard">' .. geodechtml
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
    local globe = string.lower(args.globe or '')
.. '<span style="display:none">&#xfeff; (<span class="fn org">'
    local scale = tonumber(args.scale or '30000')
.. args["name"] .. '</span>)</span></span></span></span>'
    local nogoogle = string.lower(args.nogoogle or '')
end
    local noosm = string.lower(args.noosm or '')
 
    local yandex = string.lower(args.yandex or '')
local params = splitParam( param )
local type = string.gsub( string.lower( params.type or '' ), '%(.+$', '' )
    if globe == '' then
 
        if nogoogle == '' then
local scale
            result = result .. '&nbsp;<sup class="geo-google noprint" title="Это место на Картах Google">[//maps.google.com/maps?'
if args.scale and args.scale ~= '' then
                .. 'll=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
scale = tonumber( args.scale )
                .. '&q=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
end
                .. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
if not scale then
                .. '&t=h&hl=ru (G)]'
local typeScale = {
                .. '</sup>'
adm1st = 1000000,
        end
adm2nd = 300000,
        if noosm == '' then
adm3rd = 100000,
            result = result .. '&nbsp;<sup class="geo-osm noprint" title="Это место на карте OpenStreetMap">[http://www.openstreetmap.org/?'
airport = 30000,
                .. 'mlat=' .. coordinateSpec["dec-lat"] .. '&mlon=' .. coordinateSpec["dec-long"]
city = 100000,
country = 10000000,
            if scale < 2000 then result = result .. '&zoom=18'
edu = 10000,
            elseif scale <   5000 then result = result .. '&zoom=17'
event = 50000,
            elseif scale <  10000 then result = result .. '&zoom=16'
forest = 50000,
            elseif scale <  20000 then result = result .. '&zoom=15'
glacier = 50000,
            elseif scale <  40000 then result = result .. '&zoom=14'
isle = 100000,
            elseif scale <  80000 then result = result .. '&zoom=13'
landmark = 10000,
            elseif scale < 160000 then result = result .. '&zoom=12'
mountain = 100000,
            elseif scale < 320000 then result = result .. '&zoom=11'
pass = 10000,
            elseif scale < 640000 then result = result .. '&zoom=10'
railwaystation = 10000,
            else result = result .. '&zoom=9'
river = 100000,
            end
satellite = 10000000,
waterbody = 100000,
            result = result .. ' (O)]</sup>'
camera = 10000
        end
}
if typeScale[type] then
        if yandex ~= '' then
scale = typeScale[type]
            result = result .. '&nbsp;<sup class="geo-yandex noprint" title="Это место на Яндекс.Картах">[//maps.yandex.ru/'
else
                .. '?ll=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
scale = 30000
                .. '&pt=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
end
                .. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
end
                .. '&l=' .. 'sat,skl'
 
                .. ' (Я)]</sup>'
if scale < 2000 then zoom = 18
        end
elseif scale <  5000 then zoom = 17
    end
elseif scale <  10000 then zoom = 16
elseif scale <  20000 then zoom = 15
    result = result .. '</span>'
elseif scale < 40000 then zoom = 14
elseif scale <  80000 then zoom = 13
    return result
elseif scale < 160000 then zoom = 12
elseif scale < 320000 then zoom = 11
elseif scale < 640000 then zoom = 10
elseif scale < 1280000 then zoom = 9
elseif scale < 2560000 then zoom = 8
elseif scale < 5120000 then zoom = 7
elseif scale < 10240000 then zoom = 6
elseif scale < 20480000 then zoom = 5
elseif scale < 40960000 then zoom = 4
else zoom = 3
end
 
if coordinateSpec.default == 'dec' then
text = geodeclat .. ' ' .. geodeclong
else
text = coordinateSpec["dms-lat"] .. ' ' .. coordinateSpec["dms-long"]
end
local maplinkArgs = {
['latitude'] = coordinateSpec['dec-lat'],
['longitude'] = coordinateSpec['dec-long'],
['zoom'] = zoom,
['text'] = text,
['title'] = mw.title.getCurrentTitle().text,
['lang'] = 'ru'
}
 
if coordinateSpec['name'] and coordinateSpec['name'] ~= '' then
maplinkArgs['title'] = coordinateSpec['name']
end
 
local maplinkMarkerSymbol = 'star'
local markerSymbols = {
adm1st = 'city',
adm2nd = 'city',
adm3rd = 'city',
airport = 'airport',
city = 'city',
country = 'city',
edu = 'college',
forest = 'park',
glacier = 'mountain',
mountain = 'mountain',
pass = 'mountain',
railwaystation = 'rail',
river = 'water',
satellite = 'rocket',
waterbody = 'water',
camera = 'attraction'
}
if markerSymbols[type] then
maplinkMarkerSymbol = markerSymbols[type]
end
 
local maplinkContent = [[ {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
]] .. coordinateSpec['dec-long'] .. [[,
]] .. coordinateSpec['dec-lat'] .. [[
]
},
"properties": {
"title": "]] .. mw.text.encode( maplinkArgs['title'] ) .. [[",
"marker-symbol": "]] .. maplinkMarkerSymbol .. [[",
"marker-color": "#3366cc"
}
} ]];
local entityId = mw.wikibase.getEntityIdForCurrentPage()
if entityId then
maplinkContent = maplinkContent .. [[, {
"type": "ExternalData",
"service": "geoline",
"ids": "]] .. mw.wikibase.getEntityIdForCurrentPage() .. [[",
"properties": {
"stroke": "#FF9999"
}
}, {
"type": "ExternalData",
"service": "geoshape",
"ids": "]] .. mw.wikibase.getEntityIdForCurrentPage() .. [[",
"properties": {
"fill": "#FF0000",
"fill-opacity": 0.1,
"stroke": "#FF9999"
}
} ]]
end
 
local globe = string.lower( args.globe or params.globe or '' )
if globe == '' then globe = 'earth' end
 
local result = '<span class="coordinates plainlinks nourlexpansion" data-param="' .. mw.text.encode( param ) .. '">'
 
-- external links
local nogoogle = string.lower( args.nogoogle or '' )
local noosm = string.lower( args.noosm or '' )
local noyandex = string.lower( args.noyandex or '' )
 
if globe == 'earth' then
result = result .. '<span title="Показать карту">' .. globalFrame:extensionTag{
name = 'maplink',
content = '[' .. maplinkContent .. ']',
args = maplinkArgs
} .. '</span>'
if nogoogle == '' or noosm == '' or noyandex == '' then
result = result .. '<sup class="geo-services noprint">'
result = result .. globalFrame:preprocess(
'<span class="geo-geohack" title="Карты и инструменты на GeoHack">' ..
'[https://geohack.toolforge.org/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
uriComponents .. ' ' .. '<span>H</span>]</span>' )
if nogoogle == '' then
result = result .. '<span class="geo-google" title="Это место на «Картах Google»">[//maps.google.com/maps?'
.. 'll=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
.. '&q=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
.. '&t=h&hl=ru '
.. '<span>G</span>]</span>'
end
if noyandex == '' then
result = result .. '<span class="geo-yandex" title="Это место на «Яндекс.Картах»">[//yandex.ru/maps/'
.. '?ll=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
.. '&pt=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
.. '&l=' .. 'sat,skl '
.. '<span>Я</span>]</span>'
end
if noosm == '' then
result = result .. '<span class="geo-osm" title="Это место на карте OpenStreetMap">[https://www.openstreetmap.org/?'
.. 'mlat=' .. coordinateSpec["dec-lat"] .. '&mlon=' .. coordinateSpec["dec-long"]
.. '&zoom=' .. zoom .. ' '
.. '<span>O</span>]</span>'
end
result = result .. '</sup>'
end
else
-- FIXME [[phab:T151138]]
result = result .. globalFrame:preprocess(
'[//geohack.toolforge.org/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
uriComponents .. ' ' .. inner .. ']' )
if globe == 'moon' or globe == 'mars' and nogoogle == '' then
result = result .. '<sup class="geo-services noprint"><span class="geo-google" title="Это место на «Картах Google»">[//www.google.com/' .. globe
.. '/#lat=' ..  coordinateSpec["dec-lat"] .. '&lon=' .. coordinateSpec["dec-long"]
.. '&zoom=7'
.. '&map=visible'
.. '&apollo= <span>G</span>]</span></sup>'
end
end
result = result .. '</span>'
local geodata = ''
if coordinateSpec["dec-lat"] and coordinateSpec["dec-long"] then
if globe ~= 'earth' and globe ~= 'moon' then
if tonumber(coordinateSpec["dec-long"]) < 0 then
coordinateSpec["dec-long"] = tostring(360 + tonumber(coordinateSpec["dec-long"]))
end
end
local frame = mw.getCurrentFrame()
local geodataparams = {[1] = coordinateSpec["dec-lat"], [2] = coordinateSpec["dec-long"], [3] = coordinateSpec["extra_param"], ['globe'] = globe }
if string.find( Display, 'title' ) ~= nil and mw.title.getCurrentTitle():inNamespace(0) then
geodataparams[4] = 'primary'
end
if coordinateSpec["name"] then
geodataparams.name = coordinateSpec["name"]
end
geodata = frame:callParserFunction('#coordinates', geodataparams )
result = result .. geodata
end
return errors and result .. errors or result
end
end
 
--[[
--[[
Formats any error messages generated for display
Formats any error messages generated for display
]]
]]
function errorPrinter(errors)
function errorPrinter(errors)
    local result = ""
local result = ""
    for i,v in ipairs(errors) do
for i,v in ipairs(errors) do
        local errorHTML = '<strong class="error">Координаты: ' .. v[2] .. '</strong>'
local errorHTML = '<strong class="error">Координаты: ' .. v[2] .. '</strong>'
        result = result .. errorHTML .. "<br />"
result = result .. errorHTML .. "<br />"
    end
end
    return result
if result ~= '' then
if mw.title.getCurrentTitle():inNamespace(0) then
return result .. '[[Категория:Страницы с некорректными тегами координат]]'
else
return result
end
end
end
end
 
--[[
--[[
Determine the required CSS class to display coordinates
Determine the required CSS class to display coordinates
 
Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
default is the mode as specificied by the user when calling the {{coord}} template
default is the mode as specificied by the user when calling the {{coord}} template
Строка 296: Строка 511:
]]
]]
function displayDefault(default, mode)
function displayDefault(default, mode)
    if default == "" then
if default == "" then
        default = "dec"
default = "dec"
    end
end
    if default == mode then
if default == mode then
        return "geo-default"
return "geo-default"
    else
else
        return "geo-nondefault"
return "geo-nondefault"
    end
end
end
end
 
--[[
--[[
Check the input arguments for coord to determine the kind of data being provided
Check the input arguments for coord to determine the kind of data being provided
Строка 312: Строка 527:
]]
]]
function formatTest(args)
function formatTest(args)
    local result, errors;
local result, errors;
    local primary = false;
local param, extra_param = {}, {}
    local param = {}
local globe = string.lower( args.globe or '' )
    if args[1] == "" then
if not globe_list:find('|' .. globe .. '|') then
        -- no lat logic
return nil, errorPrinter( {{"formatTest", "неизвестный глобус"}} )
        return errorPrinter( {{"formatTest", "Пропущена широта"}} )
end
    elseif args[4] == "" and args[5] == "" and args[6] == "" then
 
        -- dec logic
if args[4] == "" and args[5] == "" and args[6] == "" then
        result, errors = parseDec( args[1], args[2], args['format'] )
-- dec logic
        if result == nil then
result, errors = parseDec( args[1], args[2], args['format'] )
            return errorPrinter( errors );
param = { args[1], "N", args[2], "E", args[3] };
        end
elseif dmsTest(args[4], args[8]) then
        param = { args[1], "N", args[2], "E", args[3] };
-- dms logic
    elseif dmsTest(args[4], args[8]) then
result, errors = parseDMS( args[1], args[2], args[3], args[4],
        -- dms logic
args[5], args[6], args[7], args[8], args['format'] )
        result, errors = parseDMS( args[1], args[2], args[3], args[4],
param = { args[1], args[2], args[3], args[4], args[5],
            args[5], args[6], args[7], args[8], args['format'] )
args[6], args[7], args[8], args[9] };
        param = { args[1], args[2], args[3], args[4], args[5],
if args[10] ~= '' then
            args[6], args[7], args[8], args[9] };
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        if args[10] ~= '' then
end
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
elseif dmsTest(args[3], args[6]) then
        end
-- dm logic
    elseif dmsTest(args[3], args[6]) then
result, errors = parseDMS( args[1], args[2], nil, args[3],
        -- dm logic
args[4], args[5], nil, args[6], args['format'] )
        result, errors = parseDMS( args[1], args[2], nil, args[3],
param = { args[1], args[2], args[3], args[4], args[5], args[6], args[7] };
            args[4], args[5], nil, args[6], args['format'] )
if args[8] ~= '' then
        param = { args[1], args[2], args[3], args[4], args[5], args[6], args[7] };
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        if args[8] ~= '' then
end
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
elseif dmsTest(args[2], args[4]) then
        end
-- d logic
    elseif dmsTest(args[2], args[4]) then
result, errors = parseDMS( args[1], nil, nil, args[2],
        -- d logic
args[3], nil, nil, args[4], args['format'] )
        result, errors = parseDMS( args[1], nil, nil, args[2],
param = { args[1], args[2], args[3], args[4], args[5] };
            args[3], nil, nil, args[4], args['format'] )
if args[6] ~= '' then
        param = { args[1], args[2], args[3], args[4], args[5] };
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        if args[6] ~= '' then
end
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
else
        end
-- Error
    else
return nil, errorPrinter( {{"formatTest", "неизвестный формат аргумента"}} )
        -- Error
end
        return errorPrinter( {{"formatTest", "Неизвестный формат аргумента"}} )
    end
if not result then
    result.name     = args["name"]
return nil, errorPrinter( errors )
end
    local last = table.getn (param)
 
    if param[last] == '' then
result.name = args["name"]
        table.remove(param, last)
    end
local last = table.getn (param)
if param[last] == '' then
    local extra_param = { 'dim', 'globe', 'scale', 'region', 'source', 'type' }
table.remove(param, last)
    for _, v in ipairs( extra_param ) do
end
        if (args[v] or '') ~= '' then
 
            table.insert( param, v .. ':' .. args[v] );
local extra_params = { 'dim', 'globe', 'scale', 'region', 'source', 'type' }
        end
for _, v in ipairs( extra_params ) do
    end
if (args[v] or '') ~= '' then
table.insert( extra_param, v .. ':' .. args[v] );
    result.param = table.concat( param , '_' );
end
end
    if #errors == 0 then
 
        return specPrinter( args, result )
result.param = table.concat( param , '_' );
    else
result.extra_param = table.concat( extra_param , '_' );
        return specPrinter( args, result ) .. " " .. errorPrinter(errors) .. '[[Категория:Википедия:Статьи с ошибочными параметрами координат]]';
 
    end
return result, errorPrinter( errors )
end
end
 
--[[
--[[
Helper function, convert decimal latitude or longitude to
Helper function, convert decimal latitude or longitude to
Строка 384: Строка 599:
]]
]]
function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
function convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
    local coord = tonumber(coordinate) or 0
local coord = tonumber(coordinate) or 0
    local postfix
local postfix
    if coord >= 0 then
if coord >= 0 then
        postfix = firstPostfix
postfix = firstPostfix
    else
else
        postfix = secondPostfix
postfix = secondPostfix
    end
end
 
    precision = precision:lower();
precision = precision:lower();
    if precision == "dms" then
if precision == "dms" then
        return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
return convert_dec2dms_dms( math.abs( coord ) ) .. postfix;
    elseif precision == "dm" then
elseif precision == "dm" then
        return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
return convert_dec2dms_dm( math.abs( coord ) ) .. postfix;
    elseif precision == "d" then
elseif precision == "d" then
        return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
return convert_dec2dms_d( math.abs( coord ) ) .. postfix;
    end
end
end
end
 
--[[ Helper function, convert decimal to degrees ]]
--[[ Helper function, convert decimal to degrees ]]
function convert_dec2dms_d(coordinate)
function convert_dec2dms_d(coordinate)
    local d = math_mod._round( coordinate, 0 ) .. "°"
local d = math_mod._round( coordinate, 0 ) .. "°"
    return d .. ""
return d .. ""
end
end
 
--[[ Helper function, convert decimal to degrees and minutes ]]
--[[ Helper function, convert decimal to degrees and minutes ]]
function convert_dec2dms_dm(coordinate)
function convert_dec2dms_dm(coordinate)
    coordinate = math_mod._round( coordinate * 60, 0 );
coordinate = math_mod._round( coordinate * 60, 0 );
    local m = coordinate % 60;
local m = coordinate % 60;
    coordinate = math.floor( (coordinate - m) / 60 );
coordinate = math.floor( (coordinate - m) / 60 );
    local d = coordinate % 360 .."°"
local d = coordinate % 360 .."°"
    return d .. string.format( "%02d′", m )
return d .. string.format( "%02d′", m )
end
end
 
--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
function convert_dec2dms_dms(coordinate)
function convert_dec2dms_dms(coordinate)
    coordinate = math_mod._round( coordinate * 60 * 60, 0 );
coordinate = math_mod._round( coordinate * 60 * 60, 0 );
    local s = coordinate % 60
local s = coordinate % 60
    coordinate = math.floor( (coordinate - s) / 60 );
coordinate = math.floor( (coordinate - s) / 60 );
    local m = coordinate % 60
local m = coordinate % 60
    coordinate = math.floor( (coordinate - m) / 60 );
coordinate = math.floor( (coordinate - m) / 60 );
    local d = coordinate % 360 .."°"
local d = coordinate % 360 .."°"
 
    return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
return d .. string.format( "%02d′", m ) .. string.format( "%02d″", s )
end
end
 
--[[
--[[
Convert DMS format into a N or E decimal coordinate
Convert DMS format into a N or E decimal coordinate
]]
]]
function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
function convert_dms2dec(direction, degrees_str, minutes_str, seconds_str)
    local degrees = tonumber(degrees_str) or 0
local degrees = tonumber(degrees_str) or 0
    local minutes = tonumber(minutes_str) or 0
local minutes = tonumber(minutes_str) or 0
    local seconds = tonumber(seconds_str) or 0
local seconds = tonumber(seconds_str) or 0
    local factor
local factor
    direction = mw.ustring.gsub(direction, '^[ ]*(.-)[ ]*$', '%1');
direction = mw.ustring.gsub(direction, '^[ ]*(.-)[ ]*$', '%1');
    if direction == "S" or direction == "W" then
if direction == "S" or direction == "W" then
        factor = -1
factor = -1
    else
else
        factor = 1
factor = 1
    end
end
    local precision = 0
local precision = 0
    if seconds_str ~= nil and seconds_str ~= '' then
if seconds_str ~= nil and seconds_str ~= '' then
        precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
precision = 5 + math.max( math_mod._precision(seconds_str), 0 );
    elseif minutes_str ~= nil and minutes_str ~= '' then
elseif minutes_str ~= nil and minutes_str ~= '' then
        precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
precision = 3 + math.max( math_mod._precision(minutes_str), 0 );
    else
else
        precision = math.max( math_mod._precision(degrees_str), 0 );
precision = math.max( math_mod._precision(degrees_str), 0 );
    end
end
    local decimal = factor * (degrees+(minutes+seconds/60)/60)
local decimal = factor * (degrees+(minutes+seconds/60)/60)
    return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
return string.format( "%." .. precision .. "f", decimal ) -- not tonumber since this whole thing is string based.
end
end
 
--[[
--[[
Checks input values to for out of range errors.
Checks input values to for out of range errors.
]]
]]
function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
    local errors = {};
local errors = {};
    lat_d = tonumber( lat_d ) or 0;
    lat_m = tonumber( lat_m ) or 0;
if long_d == nil or long_d == '' then
    lat_s = tonumber( lat_s ) or 0;
table.insert(errors, {source, "пропущена долгота"})
    long_d = tonumber( long_d ) or 0;
end
    long_m = tonumber( long_m ) or 0;
if lat_d == nil or lat_d == '' then
    long_s = tonumber( long_s ) or 0;
table.insert(errors, {source, "пропущена широта"})
end
    if strong then
        if lat_d < 0 then
lat_d = tonumber( lat_d ) or 0;
            table.insert(errors, {source, "градусы широты  < 0"})
lat_m = tonumber( lat_m ) or 0;
        end
lat_s = tonumber( lat_s ) or 0;
        if long_d < 0 then
long_d = tonumber( long_d ) or 0;
            table.insert(errors, {source, "градусы долготы < 0"})
long_m = tonumber( long_m ) or 0;
        end
long_s = tonumber( long_s ) or 0;
        --[[
 
        #coordinates is inconsistent about whether this is an error.  If globe: is
if strong then
        specified, it won't error on this condition, but otherwise it will.
if lat_d < 0 then
table.insert(errors, {source, "градусы широты  < 0"})
        For not simply disable this check.
end
if long_d < 0 then
        if long_d > 180 then
table.insert(errors, {source, "градусы долготы < 0"})
            table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
end
        end
--[[
        ]]
#coordinates is inconsistent about whether this is an error.  If globe: is
    end
specified, it won't error on this condition, but otherwise it will.
    if lat_d > 90 then
For not simply disable this check.
        table.insert(errors, {source, "градусы широты > 90"})
    end
if long_d > 180 then
    if lat_d < -90 then
table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
        table.insert(errors, {source, "градусы широты < -90"})
end
    end
]]
    if lat_m >= 60 then
end
        table.insert(errors, {source, "минуты широты >= 60"})
    end
if lat_d > 90 then
    if lat_m < 0 then
table.insert(errors, {source, "градусы широты > 90"})
        table.insert(errors, {source, "минуты широты < 0"})
end
    end
if lat_d < -90 then
    if lat_s >= 60 then
table.insert(errors, {source, "градусы широты < -90"})
        table.insert(errors, {source, "секунды широты >= 60"})
end
    end
if lat_m >= 60 then
    if lat_s < 0 then
table.insert(errors, {source, "минуты широты >= 60"})
        table.insert(errors, {source, "секунды широты < 0"})
end
    end
if lat_m < 0 then
    if long_d >= 360 then
table.insert(errors, {source, "минуты широты < 0"})
        table.insert(errors, {source, "градусы долготы >= 360"})
end
    end
if lat_s >= 60 then
    if long_d <= -360 then
table.insert(errors, {source, "секунды широты >= 60"})
        table.insert(errors, {source, "градусы долготы <= -360"})
end
    end
if lat_s < 0 then
    if long_m >= 60 then
table.insert(errors, {source, "секунды широты < 0"})
        table.insert(errors, {source, "минуты долготы >= 60"})
end
    end
if long_d >= 360 then
    if long_m < 0 then
table.insert(errors, {source, "градусы долготы >= 360"})
        table.insert(errors, {source, "минуты долготы < 0"})
end
    end
if long_d <= -360 then
    if long_s >= 60 then
table.insert(errors, {source, "градусы долготы <= -360"})
        table.insert(errors, {source, "секунды долготы >= 60"})
end
    end
if long_m >= 60 then
    if long_s < 0 then
table.insert(errors, {source, "минуты долготы >= 60"})
        table.insert(errors, {source, "секунды долготы < 0"})
end
    end
if long_m < 0 then
table.insert(errors, {source, "минуты долготы < 0"})
    return errors;
end
if long_s >= 60 then
table.insert(errors, {source, "секунды долготы >= 60"})
end
if long_s < 0 then
table.insert(errors, {source, "секунды долготы < 0"})
end
return errors;
end
end
 
--[[
local function splitCoord(args, s)
dec2dms
if s and s~= nil then
local iterator = mw.ustring.gmatch(s, "[^/]+");
Wrapper to allow templates to call dec2dms directly.
local i = 1;
for w in iterator do
Usage:
args[i] = w;
    {{ Invoke:Coordinates | dec2dms | decimal_coordinate | positive_suffix |
i = i + 1;
        negative_suffix | precision }}
end
end
decimal_coordinate is converted to DMS format. If positive, the positive_suffix
is appended (typical N or E), if negative, the negative suffix is appended.  The
for i=1,10 do
specified precision is one of 'D', 'DM', or 'DMS' to specify the level of detail
if args[i] == nil then
to use.
args[i] = ""
]]
else
function coordinates.dec2dms(frame)
args[i] = args[i]:match( '^%s*(.-)%s*$' );  --remove whitespace
    globalFrame = frame
end
    local coordinate = frame.args[1]
end
    local firstPostfix = frame.args[2]
    local secondPostfix = frame.args[3]
return args
    local precision = frame.args[4]
    return convert_dec2dms(coordinate, firstPostfix, secondPostfix, precision)
end
end
 
--[[
--[[
Helper function to determine whether to use D, DM, or DMS
Helper function to determine whether to use D, DM, or DMS
Строка 559: Строка 779:
]]
]]
function coordinates.determineMode( value1, value2 )
function coordinates.determineMode( value1, value2 )
    local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
local precision = math.max( math_mod._precision( value1 ), math_mod._precision( value2 ) );
    if precision <= 0 then
if precision <= 0 then
        return 'd'
return 'd'
    elseif precision <= 2 then
elseif precision <= 2 then
        return 'dm';
return 'dm';
    else
else
        return 'dms';
return 'dms';
    end
end
end
--[[
dms2dec
Wrapper to allow templates to call dms2dec directly.
Usage:
    {{ Invoke:Coordinates | dms2dec | direction_flag | degrees |
        minutes | seconds }}
Converts DMS values specified as degrees, minutes, seconds too decimal format.
direction_flag is one of N, S, E, W, and determines whether the output is
positive (i.e. N and E) or negative (i.e. S and W).
]]
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
end
 
--[[
--[[
coord
coord
 
Main entry point for Lua function to replace {{coord}}
Main entry point for Lua function to replace {{coord}}
 
Usage:
Usage:
    {{ Invoke:Coordinates | coord }}
{{ Invoke:Coordinates | coord }}
    {{ Invoke:Coordinates | coord | lat | long }}
{{ Invoke:Coordinates | coord | lat | long }}
    {{ Invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
{{ Invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
    ...
...
    Refer to {{coord}} documentation page for many additional parameters and
Refer to {{coord}} documentation page for many additional parameters and
    configuration options.
configuration options.
Note: This function provides the visual display elements of {{coord}}.  In
Note: This function provides the visual display elements of {{coord}}.  In
order to load coordinates into the database, the {{#coordinates:}} parser
order to load coordinates into the database, the {{#coordinates:}} parser
Строка 612: Строка 809:
]]
]]
function coordinates.coord(frame)
function coordinates.coord(frame)
    globalFrame = frame
globalFrame = frame
    local args = frame.args
local args = frame.args
    if args[1] == nil then
if args[1] == nil then
        local pFrame = frame:getParent();
local pFrame = frame:getParent();
        args = pFrame.args;
args = pFrame.args;
        for k,v in pairs( frame.args ) do
for k,v in pairs( frame.args ) do
            args[k] = v;
args[k] = v;
        end
end
    end
end
    for i=1,10 do
local coord = args.coord or nil;
        if args[i] == nil then
args = splitCoord(args, coord)
            args[i] = ""
args['format'] = args['format'] or '';
        else
            args[i] = args[i]:match( '^%s*(.-)%s*$' );  --remove whitespace
Display = string.lower(args.display or "inline")
        end
local contents = specPrinter(args)
    end
local Notes = args.notes or ""
    args['format'] = args['format'] or '';
if Display == '' then
Display = 'inline';
    local contents = formatTest(args)
end
    local Notes = args.notes or ""
    local Display = string.lower(args.display or "inline")
local text = ''
    if Display == '' then
if string.find( Display, 'inline' ) ~= nil then
        Display = 'inline';
text = displayinline(contents, Notes)
    end
end
if string.find( Display, 'title' ) ~= nil then
    local text = ''
displaytitle_ = true
    if string.find( Display, 'inline' ) ~= nil or Display == 'i' or
text = text .. displaytitle(contents, Notes, frame)
            Display == 'it' or Display == 'ti' then
end
        text = displayinline(contents, Notes)
return text
    end
end
    if string.find( Display, 'title' ) ~= nil or Display == 't' or
 
            Display == 'it' or Display == 'ti' then
function coordinates.getLon(frame)
        text = text .. displaytitle(contents, Notes)
local args = frame.args
    end
    return text
args = splitCoord(args, args[1])
local out = formatTest(args)
return out['dec-long']
end
 
function coordinates.getLat(frame)
local args = frame.args
args = splitCoord(args, args[1])
local out = formatTest(args)
return out['dec-lat']
end
end
 
return coordinates
return coordinates

Текущая версия от 11:47, 13 марта 2024

Для документации этого модуля может быть создана страница Модуль:Coordinates/doc

--[[
This module is intended to replace the functionality of {{Coord}} and related
templates.  It provides several methods, including

{{#Invoke:Coordinates | coord }} : General function formatting and displaying
coordinate values.

{{#Invoke:Coordinates | dec2dms }} : Simple function for converting decimal
degree values to DMS format.
g
{{#Invoke:Coordinates | dms2dec }} : Simple function for converting DMS format
to decimal degree format.

]]

math_mod = require( "Module:Math" );
globalFrame = nil

coordinates = {};

local globe_list = '||earth|mercury|venus|moon|mars|phobos|deimos|ganymede|callisto|io|europa|mimas|enceladus|tethys|dione|rhea|titan|hyperion|iapetus|phoebe|miranda|ariel|umbriel|titania|oberon|triton|pluto|charon|ceres|vesta|'

local Dispay = '';

--[[ Helper function, replacement for {{coord/display/title}} ]]
function displaytitle (s, notes, globalFrame)
	return globalFrame:extensionTag{
		name = 'indicator',
		content = s .. notes,
		args = { name = '0-coord' }
	};
end

--[[ Helper function, Replacement for {{coord/display/inline}} ]]
function displayinline (s, notes)
	return s .. notes
end

--[[ Helper function, used in detecting DMS formatting ]]
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" then
		return true;
	end
	return false;
end

--[[
parseDec

Transforms decimal format latitude and longitude into the a
structure to be used in displaying coordinates
]]
function parseDec( lat, long, format )
	local coordinateSpec = {}
	local errors = {}

	errors = validate( lat, nil, nil, long, nil, nil, 'parseDec', false );
	if #errors ~= 0 then
		return nil, errors
	end
	
	coordinateSpec["dec-lat"]  = lat;
	coordinateSpec["dec-long"] = long;

	local mode = coordinates.determineMode( lat, long );
	coordinateSpec["dms-lat"]  = convert_dec2dms( lat, "&nbsp;с.&nbsp;ш.", "&nbsp;ю.&nbsp;ш.", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
	coordinateSpec["dms-long"] = convert_dec2dms( long, "&nbsp;в.&nbsp;д.", "&nbsp;з.&nbsp;д.", mode)  -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
	
	if format ~= "" then
		coordinateSpec.default = format
	else
		coordinateSpec.default = "dms"
	end

	return coordinateSpec, errors
end

--[[ Helper function, handle optional args. ]]
function optionalArg(arg, suplement, bool)
	if arg ~= nil and arg ~= "" then 
			arg = (tonumber( arg ) % 1 == 0 or not bool) and 
			string.format( "%02d", arg ) or 
			string.format( "%02.2f", arg):gsub('%.', ',')
		return arg  .. suplement
	end
	return ""
end

--[[
parseDMS

Transforms degrees, minutes, seconds format latitude and longitude
into the a structure to be used in displaying coordinates
]]
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();
	
	-- Check if specified backward
	if lat_f == 'E' or lat_f == 'W' 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 #errors ~= 0 then
		return nil, errors
	end
	
	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 lat_m == nil and lat_s == nil and long_m == nil and long_s == nil and #errors == 0 
		or math_mod._precision( lat_d ) > 0 or math_mod._precision( long_d ) > 0 then
			if lat_f:upper() == 'S' then
				lat_d = '-' .. lat_d;
				lat_f = "&nbsp;ю.&nbsp;ш.";
			else
				lat_f = "&nbsp;с.&nbsp;ш.";
			end
			if long_f:upper() == 'W' then
				long_d = '-' .. long_d;
				long_f = "&nbsp;з.&nbsp;д.";
			else
				long_f = "&nbsp;в.&nbsp;д.";
			end
			
			return parseDec( lat_d, long_d, format );
	end

	if lat_f:upper() == 'S' then
		lat_f = "&nbsp;ю.&nbsp;ш.";
	else
		lat_f = "&nbsp;с.&nbsp;ш.";
	end

	if long_f:upper() == 'E' then
		long_f = "&nbsp;в.&nbsp;д.";
	else
		long_f = "&nbsp;з.&nbsp;д.";
	end
	
	if lat_s == '0' and long_s == '0'  then
		lat_s, long_s = nil, nil
		if lat_m == '0' and long_m == '0' then
			lat_m, long_m = nil, nil
		end
	end

	coordinateSpec["dms-lat"]  = lat_d.."°"..optionalArg(lat_m,"′", true) .. optionalArg(lat_s,"″") .. lat_f
	coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′", true) .. optionalArg(long_s,"″") .. long_f

	if format ~= "" then
		coordinateSpec.default = format
	else
		coordinateSpec.default = "dms"
	end

	return coordinateSpec, errors
end

--[[
splitParam

Split the parameter string and convert it into an object.
]]
function splitParam( param )
	local out = {}
	for pair in mw.text.gsplit( param, '_', true ) do
		local keyValue = mw.text.split( pair, ':', true )
		if #keyValue == 2 then
			out[keyValue[1]] = keyValue[2]
		end
	end
	return out
end

--[[
specPrinter

Output formatter.  Takes the structure generated by either parseDec
or parseDMS and formats it for inclusion on Wikipedia.
]]
function specPrinter(args)
	local coordinateSpec, errors = formatTest(args)
	
	if coordinateSpec == nil then
		return errors
	end
	
	local param = coordinateSpec["param"] .. '_' .. coordinateSpec["extra_param"]
	local uriComponents = param
	if uriComponents == "" then
		-- RETURN error, should never be empty or nil
		return "Ошибка: не задан param"
	end
	if args["name"] ~= "" and args["name"] ~= nil then
		uriComponents = uriComponents .. "&title=" .. mw.uri.encode(coordinateSpec["name"])
	end
	local text = ''
	
	local lat = tonumber( coordinateSpec["dec-lat"] ) or 0
	if lat < 0 then
		-- FIXME this breaks the pre-existing precision
		geodeclat = coordinateSpec["dec-lat"]:sub(2):gsub('%.', ',') .. "°&nbsp;ю.&nbsp;ш."
	else
		geodeclat = (coordinateSpec["dec-lat"]:gsub('%.', ',') or 0) .. "°&nbsp;с.&nbsp;ш."
	end

	local long = tonumber( coordinateSpec["dec-long"] ) or 0
	if long < 0 then
		-- FIXME does not handle unicode minus
		geodeclong = coordinateSpec["dec-long"]:sub(2):gsub('%.', ',') .. "°&nbsp;з.&nbsp;д."
	else
		geodeclong = (coordinateSpec["dec-long"]:gsub('%.', ',') or 0) .. "°&nbsp;в.&nbsp;д."
	end
	
	local geodmshtml = '<span class="geo-dms" title="Различные карты и схемы для этого места">'
			 .. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
			 .. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
			 .. '</span>'

	local geodechtml = '<span class="geo-dec" title="Различные карты и схемы для этого места">'
			 .. geodeclat .. ' '
			 .. geodeclong
			 .. '</span>'

	local geonumhtml = '<span class="geo">'
			 .. coordinateSpec["dec-lat"] .. '; '
			 .. coordinateSpec["dec-long"]
			 .. '</span>'

	local inner;
	inner = '<span class="geo-geo-' .. coordinateSpec.default .. '"><span class="geo-dms">' .. geodmshtml .. '</span>'
				.. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
				.. '<span class="geo-dec">';

	if args["name"] == "" or args["name"] == nil then
		inner = inner .. geodechtml
				.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span></span>'
	else
		inner = inner .. '<span class="vcard">' .. geodechtml
				.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
				.. '<span style="display:none">&#xfeff; (<span class="fn org">'
				.. args["name"] .. '</span>)</span></span></span></span>'
	end

	local params = splitParam( param )
	local type = string.gsub( string.lower( params.type or '' ), '%(.+$', '' )

	local scale
	if args.scale and args.scale ~= '' then
		scale = tonumber( args.scale )
	end
	if not scale then
		local typeScale = {
			adm1st = 1000000,
			adm2nd = 300000,
			adm3rd = 100000,
			airport = 30000,
			city = 100000,
			country = 10000000,
			edu = 10000,
			event = 50000,
			forest = 50000,
			glacier = 50000,
			isle = 100000,
			landmark = 10000,
			mountain = 100000,
			pass = 10000,
			railwaystation = 10000,
			river = 100000,
			satellite = 10000000,
			waterbody = 100000,
			camera = 10000
		}
		if typeScale[type] then
			scale = typeScale[type]
		else
			scale = 30000
		end
	end

	if scale < 2000 then zoom = 18
	elseif scale <   5000 then zoom = 17
	elseif scale <  10000 then zoom = 16
	elseif scale <  20000 then zoom = 15
	elseif scale <  40000 then zoom = 14
	elseif scale <  80000 then zoom = 13
	elseif scale < 160000 then zoom = 12
	elseif scale < 320000 then zoom = 11
	elseif scale < 640000 then zoom = 10
	elseif scale < 1280000 then zoom = 9
	elseif scale < 2560000 then zoom = 8
	elseif scale < 5120000 then zoom = 7
	elseif scale < 10240000 then zoom = 6
	elseif scale < 20480000 then zoom = 5
	elseif scale < 40960000 then zoom = 4
	else zoom = 3
	end

	if coordinateSpec.default == 'dec' then
		text = geodeclat .. ' ' .. geodeclong
	else
		text = coordinateSpec["dms-lat"] .. ' ' .. coordinateSpec["dms-long"]
	end
	
	local maplinkArgs = {
		['latitude'] = coordinateSpec['dec-lat'],
		['longitude'] = coordinateSpec['dec-long'],
		['zoom'] = zoom,
		['text'] = text,
		['title'] = mw.title.getCurrentTitle().text,
		['lang'] = 'ru'
	}

	if coordinateSpec['name'] and coordinateSpec['name'] ~= '' then
		maplinkArgs['title'] = coordinateSpec['name']
	end

	local maplinkMarkerSymbol = 'star'
	local markerSymbols = {
		adm1st = 'city',
		adm2nd = 'city',
		adm3rd = 'city',
		airport = 'airport',
		city = 'city',
		country = 'city',
		edu = 'college',
		forest = 'park',
		glacier = 'mountain',
		mountain = 'mountain',
		pass = 'mountain',
		railwaystation = 'rail',
		river = 'water',
		satellite = 'rocket',
		waterbody = 'water',
		camera = 'attraction'
	}
	if markerSymbols[type] then
		maplinkMarkerSymbol = markerSymbols[type]
	end

	local maplinkContent = [[ {
		"type": "Feature",
		"geometry": {
			"type": "Point",
			"coordinates": [
				]] .. coordinateSpec['dec-long'] .. [[,
				]] .. coordinateSpec['dec-lat'] .. [[
			]
		},
		"properties": {
			"title": "]] .. mw.text.encode( maplinkArgs['title'] ) .. [[",
			"marker-symbol": "]] .. maplinkMarkerSymbol .. [[",
			"marker-color": "#3366cc"
		}
	} ]];
	
	local entityId = mw.wikibase.getEntityIdForCurrentPage()
	if entityId then
		maplinkContent = maplinkContent .. [[, {
			"type": "ExternalData",
			"service": "geoline",
			"ids": "]] .. mw.wikibase.getEntityIdForCurrentPage() .. [[",
			"properties": {
				"stroke": "#FF9999"
			}
		}, {
			"type": "ExternalData",
			"service": "geoshape",
			"ids": "]] .. mw.wikibase.getEntityIdForCurrentPage() .. [[",
			"properties": {
				"fill": "#FF0000",
				"fill-opacity": 0.1,
				"stroke": "#FF9999"
			}
		} ]]
	end

	local globe = string.lower( args.globe or params.globe or '' )
	if globe == '' then globe = 'earth' end

	local result = '<span class="coordinates plainlinks nourlexpansion" data-param="' .. mw.text.encode( param ) .. '">'

	-- external links
	local nogoogle = string.lower( args.nogoogle or '' )
	local noosm = string.lower( args.noosm or '' )
	local noyandex = string.lower( args.noyandex or '' )

	if globe == 'earth' then
		result = result .. '<span title="Показать карту">' .. globalFrame:extensionTag{
			name = 'maplink',
			content = '[' .. maplinkContent .. ']',
			args = maplinkArgs
		} .. '</span>'
		if nogoogle == '' or noosm == '' or noyandex == '' then
			result = result .. '<sup class="geo-services noprint">'
			
			result = result .. globalFrame:preprocess(
			'<span class="geo-geohack" title="Карты и инструменты на GeoHack">' ..
			'[https://geohack.toolforge.org/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
			uriComponents .. ' ' .. '<span>H</span>]</span>' )
			
			if nogoogle == '' then
				result = result .. '<span class="geo-google" title="Это место на «Картах Google»">[//maps.google.com/maps?'
					.. 'll=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
					.. '&q=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
					.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
					.. '&t=h&hl=ru '
					.. '<span>G</span>]</span>'
			end
			if noyandex == '' then
				result = result .. '<span class="geo-yandex" title="Это место на «Яндекс.Картах»">[//yandex.ru/maps/'
					.. '?ll=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
					.. '&pt=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
					.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
					.. '&l=' .. 'sat,skl '
					.. '<span>Я</span>]</span>'
			end
			if noosm == '' then
				result = result .. '<span class="geo-osm" title="Это место на карте OpenStreetMap">[https://www.openstreetmap.org/?'
					.. 'mlat=' .. coordinateSpec["dec-lat"] .. '&mlon=' .. coordinateSpec["dec-long"]
					.. '&zoom=' .. zoom .. ' '
					.. '<span>O</span>]</span>'
			end
			result = result .. '</sup>'
		end
	else
		-- FIXME [[phab:T151138]]
		result = result .. globalFrame:preprocess(
			'[//geohack.toolforge.org/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
			uriComponents .. ' ' .. inner .. ']' )
		if globe == 'moon' or globe == 'mars' and nogoogle == '' then
			result = result .. '<sup class="geo-services noprint"><span class="geo-google" title="Это место на «Картах Google»">[//www.google.com/' .. globe
			.. '/#lat=' ..  coordinateSpec["dec-lat"] .. '&lon=' .. coordinateSpec["dec-long"]
			.. '&zoom=7'
			.. '&map=visible'
			.. '&apollo= <span>G</span>]</span></sup>'
		end
	end
	
	result = result .. '</span>'
	
	local geodata = ''
	if coordinateSpec["dec-lat"] and coordinateSpec["dec-long"] then
		
		if globe ~= 'earth' and globe ~= 'moon' then
			if tonumber(coordinateSpec["dec-long"]) < 0 then
				coordinateSpec["dec-long"] = tostring(360 + tonumber(coordinateSpec["dec-long"]))
			end
		end
		local frame = mw.getCurrentFrame()
		local geodataparams = {[1] = coordinateSpec["dec-lat"], [2] = coordinateSpec["dec-long"], [3] = coordinateSpec["extra_param"], ['globe'] = globe }
		if string.find( Display, 'title' ) ~= nil and mw.title.getCurrentTitle():inNamespace(0) then
			geodataparams[4] = 'primary'
		end
		if coordinateSpec["name"] then
			geodataparams.name = coordinateSpec["name"]
		end
		
		geodata = frame:callParserFunction('#coordinates', geodataparams )
		result = result .. geodata
	end
	
	return errors and result .. errors or result
	
end

--[[
Formats any error messages generated for display
]]
function errorPrinter(errors)
	local result = ""
	for i,v in ipairs(errors) do
		local errorHTML = '<strong class="error">Координаты: ' .. v[2] .. '</strong>'
		result = result .. errorHTML .. "<br />"
	end
	if result ~= '' then
		if mw.title.getCurrentTitle():inNamespace(0) then
			return result .. '[[Категория:Страницы с некорректными тегами координат]]'
		else
			return result
		end
	end
end

--[[
Determine the required CSS class to display coordinates

Usually geo-nondefault is hidden by CSS, unless a user has overridden this for himself
default is the mode as specificied by the user when calling the {{coord}} template
mode is the display mode (dec or dms) that we will need to determine the css class for
]]
function displayDefault(default, mode)
	if default == "" then
		default = "dec"
	end
	
	if default == mode then
		return "geo-default"
	else
		return "geo-nondefault"
	end
end

--[[
Check the input arguments for coord to determine the kind of data being provided
and then make the necessary processing.
]]
function formatTest(args)
	local result, errors;
	
	local param, extra_param = {}, {}
	
	local globe = string.lower( args.globe or '' )
	if not globe_list:find('|' .. globe .. '|') then
		return nil, errorPrinter( {{"formatTest", "неизвестный глобус"}} )
	end

	if args[4] == "" and args[5] == "" and args[6] == "" then
		-- dec logic
		result, errors = parseDec( args[1], args[2], args['format'] )
		param = { args[1], "N", args[2], "E", args[3] };
	elseif dmsTest(args[4], args[8]) then
		-- dms logic
		result, errors = parseDMS( args[1], args[2], args[3], args[4],
			args[5], args[6], args[7], args[8], args['format'] )
		param = { args[1], args[2], args[3], args[4], args[5],
			args[6], args[7], args[8], args[9] };
		if args[10] ~= '' then
			table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
		end
	elseif dmsTest(args[3], args[6]) then
		-- dm logic
		result, errors = parseDMS( args[1], args[2], nil, args[3],
			args[4], args[5], nil, args[6], args['format'] )
		param = { args[1], args[2], args[3], args[4], args[5], args[6], args[7] };
		if args[8] ~= '' then
			table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
		end
	elseif dmsTest(args[2], args[4]) then
		-- d logic
		result, errors = parseDMS( args[1], nil, nil, args[2],
			args[3], nil, nil, args[4], args['format'] )
		param = { args[1], args[2], args[3], args[4], args[5] };
		if args[6] ~= '' then
			table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
		end
	else
		-- Error
		return nil, errorPrinter( {{"formatTest", "неизвестный формат аргумента"}} )
	end
	
	if not result then 
		return nil, errorPrinter( errors )
	end

	result.name = args["name"]
	
	local last = table.getn (param)
	if param[last] == '' then
		table.remove(param, last)
	end

	local extra_params = { 'dim', 'globe', 'scale', 'region', 'source', 'type' }
	for _, v in ipairs( extra_params ) do
		if (args[v] or '') ~= '' then
			table.insert( extra_param, v .. ':' .. args[v] );
		end
	end

	result.param = table.concat( param , '_' );
	result.extra_param = table.concat( extra_param , '_' );

	return result, errorPrinter( errors )
end

--[[
Helper function, convert decimal latitude or longitude to
degrees, minutes, and seconds format based on the specified precision.
]]
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

--[[ Helper function, convert decimal to degrees ]]
function convert_dec2dms_d(coordinate)
	local d = math_mod._round( coordinate, 0 ) .. "°"
	return d .. ""
end

--[[ Helper function, convert decimal to degrees and minutes ]]
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

--[[ Helper function, convert decimal to degrees, minutes, and seconds ]]
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

--[[
Convert DMS format into a N or E decimal coordinate
]]
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 == "S" or direction == "W" 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 ) -- not tonumber since this whole thing is string based.
end

--[[
Checks input values to for out of range errors.
]]
function validate( lat_d, lat_m, lat_s, long_d, long_m, long_s, source, strong )
	local errors = {};
	
	if long_d == nil or long_d == '' then
		table.insert(errors, {source, "пропущена долгота"})
	end
	if lat_d == nil or lat_d == '' then
		table.insert(errors, {source, "пропущена широта"})
	end
	
	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, "градусы широты  < 0"})
		end
		if long_d < 0 then
			table.insert(errors, {source, "градусы долготы < 0"})
		end
		--[[
		#coordinates is inconsistent about whether this is an error.  If globe: is
		specified, it won't error on this condition, but otherwise it will.
		
		For not simply disable this check.
		
		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, "градусы широты > 90"})
	end
	if lat_d < -90 then
		table.insert(errors, {source, "градусы широты < -90"})
	end
	if lat_m >= 60 then
		table.insert(errors, {source, "минуты широты >= 60"})
	end
	if lat_m < 0 then
		table.insert(errors, {source, "минуты широты < 0"})
	end
	if lat_s >= 60 then
		table.insert(errors, {source, "секунды широты >= 60"})
	end
	if lat_s < 0 then
		table.insert(errors, {source, "секунды широты < 0"})
	end
	if long_d >= 360 then
		table.insert(errors, {source, "градусы долготы >= 360"})
	end
	if long_d <= -360 then
		table.insert(errors, {source, "градусы долготы <= -360"})
	end
	if long_m >= 60 then
		table.insert(errors, {source, "минуты долготы >= 60"})
	end
	if long_m < 0 then
		table.insert(errors, {source, "минуты долготы < 0"})
	end
	if long_s >= 60 then
		table.insert(errors, {source, "секунды долготы >= 60"})
	end
	if long_s < 0 then
		table.insert(errors, {source, "секунды долготы < 0"})
	end
	
	return errors;
end

local function splitCoord(args, s)
	if s and s~= nil then
		local iterator = mw.ustring.gmatch(s, "[^/]+");
		local i = 1;
		for w in iterator do
			args[i] = w;
			i = i + 1;
		end	
	end
	
	for i=1,10 do
		if args[i] == nil then
			args[i] = ""
		else
			args[i] = args[i]:match( '^%s*(.-)%s*$' );  --remove whitespace
		end
	end
	
	return args
end

--[[
Helper function to determine whether to use D, DM, or DMS
format depending on the precision of the decimal input.
]]
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

--[[
coord

Main entry point for Lua function to replace {{coord}}

Usage:
	{{ Invoke:Coordinates | coord }}
	{{ Invoke:Coordinates | coord | lat | long }}
	{{ Invoke:Coordinates | coord | lat | lat_flag | long | long_flag }}
	...
	
	Refer to {{coord}} documentation page for many additional parameters and
	configuration options.
	
Note: This function provides the visual display elements of {{coord}}.  In
order to load coordinates into the database, the {{#coordinates:}} parser
function must also be called, this is done automatically in the Lua
version of {{coord}}.
]]
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
	
	local coord = args.coord or nil;
	args = splitCoord(args, coord)
	args['format'] = args['format'] or '';
	
	Display = string.lower(args.display or "inline")
	local contents = specPrinter(args)
	local Notes = args.notes or ""
	if Display == '' then
		Display = 'inline';
	end
	
	local text = ''
	if string.find( Display, 'inline' ) ~= nil then
		text = displayinline(contents, Notes)
	end
	if string.find( Display, 'title' ) ~= nil then
		displaytitle_ = true
		text = text .. displaytitle(contents, Notes, frame)
	end
	return text
end

function coordinates.getLon(frame)
	local args = frame.args
	
	args = splitCoord(args, args[1])
	
	local out = formatTest(args)
	return out['dec-long']
end

function coordinates.getLat(frame)
	local args = frame.args
	
	args = splitCoord(args, args[1])
	
	local out = formatTest(args)
	return out['dec-lat']
end

return coordinates