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

Материал из Томская энциклопедии
м (1 версия импортирована)
 
 
Строка 8: Строка 8:
 
{{#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.
Строка 18: Строка 18:
  
 
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|ceres|vesta|'
 +
 +
local Dispay = '';
  
 
--[[ Helper function, replacement for {{coord/display/title}} ]]
 
--[[ Helper function, replacement for {{coord/display/title}} ]]
 
function displaytitle (s, notes, globalFrame)
 
function displaytitle (s, notes, globalFrame)
    return globalFrame:extensionTag{
+
return globalFrame:extensionTag{
    name = 'indicator',
+
name = 'indicator',
    content = s .. notes,
+
content = s .. notes,
    args = { name = '0-coord' }
+
args = { name = '0-coord' }
 
};
 
};
 
end
 
end
Строка 30: Строка 34:
 
--[[ 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
  
Строка 51: Строка 55:
 
]]
 
]]
 
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, " с. ш.", " ю. ш.", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
+
coordinateSpec["dms-lat"]  = convert_dec2dms( lat, " с. ш.", " ю. ш.", mode)  -- {{coord/dec2dms|{{{1}}}|N|S|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
    coordinateSpec["dms-long"] = convert_dec2dms( long, " в. д.", " з. д.", mode)  -- {{coord/dec2dms|{{{2}}}|E|W|{{coord/prec dec|{{{1}}}|{{{2}}}}}}}
+
coordinateSpec["dms-long"] = convert_dec2dms( long, " в. д.", " з. д.", 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
  
Строка 90: Строка 97:
 
]]
 
]]
 
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 = " ю. ш.";
+
lat_f = " ю. ш.";
            else
+
else
                lat_f = " с. ш.";
+
lat_f = " с. ш.";
            end
+
end
            if long_f:upper() == 'W' then
+
if long_f:upper() == 'W' then
                long_d = '-' .. long_d;
+
long_d = '-' .. long_d;
                long_f = " з. д.";
+
long_f = " з. д.";
            else
+
else
                long_f = " в. д.";
+
long_f = " в. д.";
            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 = " ю. ш.";
+
lat_f = " ю. ш.";
    else
+
else
        lat_f = " с. ш.";
+
lat_f = " с. ш.";
    end
+
end
  
    if long_f:upper() == 'E' then
+
if long_f:upper() == 'E' then
        long_f = " в. д.";
+
long_f = " в. д.";
    else
+
else
        long_f = " з. д.";
+
long_f = " з. д.";
    end
+
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,"′") .. optionalArg(lat_s,"″") .. lat_f
+
coordinateSpec["dms-lat"]  = lat_d.."°"..optionalArg(lat_m,"′", true) .. optionalArg(lat_s,"″") .. lat_f
    coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′") .. optionalArg(long_s,"″") .. long_f
+
coordinateSpec["dms-long"] = long_d.."°"..optionalArg(long_m,"′", true) .. optionalArg(long_s,"″") .. long_f
  
    if format ~= "" then
+
if format ~= "" then
        coordinateSpec.default = format
+
coordinateSpec.default = format
    else
+
else
        coordinateSpec.default = "dms"
+
coordinateSpec.default = "dms"
    end
+
end
  
    return coordinateSpec, errors
+
return coordinateSpec, errors
 
end
 
end
  
Строка 185: Строка 199:
 
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"])
 +
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 lat = tonumber( coordinateSpec["dec-lat"] ) or 0
+
local long = tonumber( coordinateSpec["dec-long"] ) or 0
    if lat < 0 then
+
if long < 0 then
        -- FIXME this breaks the pre-existing precision
+
-- FIXME does not handle unicode minus
        geodeclat = coordinateSpec["dec-lat"]:sub(2) .. "°&nbsp;ю.&nbsp;ш."
+
geodeclong = coordinateSpec["dec-long"]:sub(2):gsub('%.', ',') .. "°&nbsp;з.&nbsp;д."
    else
+
else
        geodeclat = (coordinateSpec["dec-lat"] or 0) .. "°&nbsp;с.&nbsp;ш."
+
geodeclong = (coordinateSpec["dec-long"]:gsub('%.', ',') or 0) .. "°&nbsp;в.&nbsp;д."
    end
+
end
 +
 +
local geodmshtml = '<span class="geo-dms" title="Различные карты и схемы для этого места">'
 +
.. '<span class="latitude">' .. coordinateSpec["dms-lat"] .. '</span> '
 +
.. '<span class="longitude">' ..coordinateSpec["dms-long"] .. '</span>'
 +
.. '</span>'
  
    local long = tonumber( coordinateSpec["dec-long"] ) or 0
+
local geodechtml = '<span class="geo-dec" title="Различные карты и схемы для этого места">'
    if long < 0 then
+
.. geodeclat .. ' '
        -- FIXME does not handle unicode minus
+
.. geodeclong
        geodeclong = coordinateSpec["dec-long"]:sub(2) .. "°&nbsp;з.&nbsp;д."
+
.. '</span>'
    else
 
        geodeclong = (coordinateSpec["dec-long"] or 0) .. "°&nbsp;в.&nbsp;д."
 
    end
 
   
 
    local geodechtml = '<span class="geo-dec" title="Различные карты и схемы для этого места">'
 
            .. geodeclat .. ' '
 
            .. geodeclong
 
            .. '</span>'
 
  
    local geonumhtml = '<span class="geo">'
+
local geonumhtml = '<span class="geo">'
            .. coordinateSpec["dec-lat"] .. '; '
+
.. coordinateSpec["dec-lat"] .. '; '
            .. coordinateSpec["dec-long"]
+
.. coordinateSpec["dec-long"]
            .. '</span>'
+
.. '</span>'
  
    local inner;
+
local inner;
    inner = '<span class="geo-geo-' .. coordinateSpec.default .. '"><span class="geo-dms">' .. geodmshtml .. '</span>'
+
inner = '<span class="geo-geo-' .. coordinateSpec.default .. '"><span class="geo-dms">' .. geodmshtml .. '</span>'
                .. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
+
.. '<span class="geo-multi-punct">&#xfeff; / &#xfeff;</span>'
                .. '<span class="geo-dec">';
+
.. '<span class="geo-dec">';
  
    if args["name"] == "" or args["name"] == nil then
+
if args["name"] == "" or args["name"] == nil then
        inner = inner .. geodechtml
+
inner = inner .. geodechtml
                .. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span></span>'
+
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span></span></span>'
    else
+
else
        inner = inner .. '<span class="vcard">' .. geodechtml
+
inner = inner .. '<span class="vcard">' .. geodechtml
                .. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
+
.. '<span style="display:none">&#xfeff; / ' .. geonumhtml .. '</span>'
                .. '<span style="display:none">&#xfeff; (<span class="fn org">'
+
.. '<span style="display:none">&#xfeff; (<span class="fn org">'
                .. args["name"] .. '</span>)</span></span></span></span>'
+
.. args["name"] .. '</span>)</span></span></span></span>'
    end
+
end
  
local params = splitParam( coordinateSpec.param )
+
local params = splitParam( param )
 
local type = string.gsub( string.lower( params.type or '' ), '%(.+$', '' )
 
local type = string.gsub( string.lower( params.type or '' ), '%(.+$', '' )
  
Строка 295: Строка 317:
 
end
 
end
  
 +
if coordinateSpec.default == 'dec' then
 +
text = geodeclat .. ' ' .. geodeclong
 +
else
 +
text = coordinateSpec["dms-lat"] .. ' ' .. coordinateSpec["dms-long"]
 +
end
 +
 
local maplinkArgs = {
 
local maplinkArgs = {
 
['latitude'] = coordinateSpec['dec-lat'],
 
['latitude'] = coordinateSpec['dec-lat'],
 
['longitude'] = coordinateSpec['dec-long'],
 
['longitude'] = coordinateSpec['dec-long'],
 
['zoom'] = zoom,
 
['zoom'] = zoom,
['text'] = coordinateSpec["dms-lat"] .. ' ' .. coordinateSpec["dms-long"],
+
['text'] = text,
['title'] = mw.title.getCurrentTitle().text
+
['title'] = mw.title.getCurrentTitle().text,
 +
['lang'] = 'ru'
 
}
 
}
  
-- if args['name'] and args['name'] ~= '' then
+
if coordinateSpec['name'] and coordinateSpec['name'] ~= '' then
-- maplinkArgs['text'] = args['name']
+
maplinkArgs['title'] = coordinateSpec['name']
-- end
 
if args['title'] and args['title'] ~= '' then
 
maplinkArgs['title'] = args['title']
 
 
end
 
end
  
Строка 319: Строка 345:
 
country = 'city',
 
country = 'city',
 
edu = 'college',
 
edu = 'college',
forest = 'park-alt',
+
forest = 'park',
 
glacier = 'mountain',
 
glacier = 'mountain',
 
mountain = 'mountain',
 
mountain = 'mountain',
Строка 347: Строка 373:
 
"marker-color": "#3366cc"
 
"marker-color": "#3366cc"
 
}
 
}
    } ]];
+
} ]];
   
+
    local entityId = mw.wikibase.getEntityIdForCurrentPage()
+
local entityId = mw.wikibase.getEntityIdForCurrentPage()
    if entityId then
+
if entityId then
    maplinkContent = maplinkContent .. [[, {
+
maplinkContent = maplinkContent .. [[, {
 
"type": "ExternalData",
 
"type": "ExternalData",
 
"service": "geoline",
 
"service": "geoline",
Строка 358: Строка 384:
 
"stroke": "#FF9999"
 
"stroke": "#FF9999"
 
}
 
}
    }, {
+
}, {
 
"type": "ExternalData",
 
"type": "ExternalData",
 
"service": "geoshape",
 
"service": "geoshape",
Строка 367: Строка 393:
 
"stroke": "#FF9999"
 
"stroke": "#FF9999"
 
}
 
}
    } ]]
+
} ]]
    end
+
end
  
    local globe = string.lower( args.globe or params.globe or '' )
+
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( coordinateSpec.param ) .. '">'
+
local result = '<span class="coordinates plainlinks nourlexpansion" data-param="' .. mw.text.encode( param ) .. '">'
if globe == '' or globe == 'earth' then
 
    result = result .. '<span title="Показать карту">' .. globalFrame:extensionTag{
 
    name = 'maplink',
 
    content = '[' .. maplinkContent .. ']',
 
    args = maplinkArgs
 
    } .. '</span>'
 
else
 
-- FIXME [[phab:T151138]]
 
    result = result .. globalFrame:preprocess(
 
        '[//tools.wmflabs.org/geohack/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
 
        uriComponents .. ' ' .. inner .. ']' )
 
    end
 
  
 
-- external links
 
-- external links
Строка 391: Строка 406:
 
local noyandex = string.lower( args.noyandex or '' )
 
local noyandex = string.lower( args.noyandex or '' )
  
    if globe == '' then
+
if globe == 'earth' then
        if nogoogle == '' or noosm == '' or noyandex == '' then
+
result = result .. '<span title="Показать карту">' .. globalFrame:extensionTag{
        result = result .. '<sup class="geo-services noprint">'
+
name = 'maplink',
       
+
content = '[' .. maplinkContent .. ']',
        result = result .. globalFrame:preprocess(
+
args = maplinkArgs
        '<span class="geo-geohack" title="Карты и инструменты на GeoHack">' ..
+
} .. '</span>'
        '[//tools.wmflabs.org/geohack/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
+
if nogoogle == '' or noosm == '' or noyandex == '' then
        uriComponents .. ' ' .. '<span>H</span>]</span>' )
+
result = result .. '<sup class="geo-services noprint">'
       
+
        if nogoogle == '' then
+
result = result .. globalFrame:preprocess(
            result = result .. '<span class="geo-google" title="Это место на «Картах Google»">[//maps.google.com/maps?'
+
'<span class="geo-geohack" title="Карты и инструменты на GeoHack">' ..
                .. 'll=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
+
'[//geohack.toolforge.org/geohack.php?language=ru&pagename={{FULLPAGENAMEE}}&params=' ..
                .. '&q=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
+
uriComponents .. ' ' .. '<span>H</span>]</span>' )
                .. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
+
                .. '&t=h&hl=ru '
+
if nogoogle == '' then
                .. '<span>G</span>]</span>'
+
result = result .. '<span class="geo-google" title="Это место на «Картах Google»">[//maps.google.com/maps?'
        end
+
.. 'll=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
        if noyandex == '' then
+
.. '&q=' ..  coordinateSpec["dec-lat"] .. ',' .. coordinateSpec["dec-long"]
            result = result .. '<span class="geo-yandex" title="Это место на «Яндекс.Картах»">[//yandex.ru/maps/'
+
.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
                .. '?ll=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
+
.. '&t=h&hl=ru '
                .. '&pt=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
+
.. '<span>G</span>]</span>'
                .. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
+
end
                .. '&l=' .. 'sat,skl '
+
if noyandex == '' then
                .. '<span>Я</span>]</span>'
+
result = result .. '<span class="geo-yandex" title="Это место на «Яндекс.Картах»">[//yandex.ru/maps/'
        end
+
.. '?ll=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
        if noosm == '' then
+
.. '&pt=' .. coordinateSpec["dec-long"] .. ',' .. coordinateSpec["dec-lat"]
            result = result .. '<span class="geo-osm" title="Это место на карте OpenStreetMap">[https://www.openstreetmap.org/?'
+
.. '&spn=' .. (scale / 1000000) .. ',' .. (scale / 1000000)
                .. 'mlat=' .. coordinateSpec["dec-lat"] .. '&mlon=' .. coordinateSpec["dec-long"]
+
.. '&l=' .. 'sat,skl '
                .. '&zoom=' .. zoom .. ' '
+
.. '<span>Я</span>]</span>'
                .. '<span>O</span>]</span>'
+
end
        end
+
if noosm == '' then
        result = result .. '</sup>'
+
result = result .. '<span class="geo-osm" title="Это место на карте OpenStreetMap">[https://www.openstreetmap.org/?'
    end
+
.. 'mlat=' .. coordinateSpec["dec-lat"] .. '&mlon=' .. coordinateSpec["dec-long"]
    end
+
.. '&zoom=' .. zoom .. ' '
   
+
.. '<span>O</span>]</span>'
    result = result .. '</span>'
+
end
   
+
result = result .. '</sup>'
    return result
+
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
  
Строка 435: Строка 489:
 
]]
 
]]
 
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
  
Строка 451: Строка 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
  
Строка 467: Строка 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 not globe_list:find('|' .. globe .. '|') then
 +
return nil, errorPrinter( {{"formatTest", "неизвестный глобус"}} )
 +
end
  
    if args[1] == "" then
+
if args[4] == "" and args[5] == "" and args[6] == "" then
        -- no lat logic
+
-- dec logic
        return errorPrinter( {{"formatTest", "Пропущена широта"}} )
+
result, errors = parseDec( args[1], args[2], args['format'] )
    elseif args[4] == "" and args[5] == "" and args[6] == "" then
+
param = { args[1], "N", args[2], "E", args[3] };
        -- dec logic
+
elseif dmsTest(args[4], args[8]) then
        result, errors = parseDec( args[1], args[2], args['format'] )
+
-- dms logic
        if result == nil then
+
result, errors = parseDMS( args[1], args[2], args[3], args[4],
            return errorPrinter( errors );
+
args[5], args[6], args[7], args[8], args['format'] )
        end
+
param = { args[1], args[2], args[3], args[4], args[5],
        param = { args[1], "N", args[2], "E", args[3] };
+
args[6], args[7], args[8], args[9] };
    elseif dmsTest(args[4], args[8]) then
+
if args[10] ~= '' then
        -- dms logic
+
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        result, errors = parseDMS( args[1], args[2], args[3], args[4],
+
end
            args[5], args[6], args[7], args[8], args['format'] )
+
elseif dmsTest(args[3], args[6]) then
        param = { args[1], args[2], args[3], args[4], args[5],
+
-- dm logic
            args[6], args[7], args[8], args[9] };
+
result, errors = parseDMS( args[1], args[2], nil, args[3],
        if args[10] ~= '' then
+
args[4], args[5], nil, args[6], args['format'] )
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
+
param = { args[1], args[2], args[3], args[4], args[5], args[6], args[7] };
        end
+
if args[8] ~= '' then
    elseif dmsTest(args[3], args[6]) then
+
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        -- dm logic
+
end
        result, errors = parseDMS( args[1], args[2], nil, args[3],
+
elseif dmsTest(args[2], args[4]) then
            args[4], args[5], nil, args[6], args['format'] )
+
-- d logic
        param = { args[1], args[2], args[3], args[4], args[5], args[6], args[7] };
+
result, errors = parseDMS( args[1], nil, nil, args[2],
        if args[8] ~= '' then
+
args[3], nil, nil, args[4], args['format'] )
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
+
param = { args[1], args[2], args[3], args[4], args[5] };
        end
+
if args[6] ~= '' then
    elseif dmsTest(args[2], args[4]) then
+
table.insert( errors, { 'formatTest', 'неожиданные дополнительные параметры' } );
        -- d logic
+
end
        result, errors = parseDMS( args[1], nil, nil, args[2],
+
else
            args[3], nil, nil, args[4], args['format'] )
+
-- Error
        param = { args[1], args[2], args[3], args[4], args[5] };
+
return nil, errorPrinter( {{"formatTest", "неизвестный формат аргумента"}} )
        if args[6] ~= '' then
+
end
            table.insert( errors, { 'formatTest', 'Неожиданные дополнительные параметры' } );
+
        end
+
if not result then
    else
+
return nil, errorPrinter( errors )
        -- Error
+
end
        return errorPrinter( {{"formatTest", "Неизвестный формат аргумента"}} )
 
    end
 
    result.name    = args["name"]
 
  
    local last = table.getn (param)
+
result.name = args["name"]
    if param[last] == '' then
+
        table.remove(param, last)
+
local last = table.getn (param)
    end
+
if param[last] == '' then
 +
table.remove(param, last)
 +
end
  
    local extra_param = { 'dim', 'globe', 'scale', 'region', 'source', 'type' }
+
local extra_params = { 'dim', 'globe', 'scale', 'region', 'source', 'type' }
    for _, v in ipairs( extra_param ) do
+
for _, v in ipairs( extra_params ) do
        if (args[v] or '') ~= '' then
+
if (args[v] or '') ~= '' then
            table.insert( param, v .. ':' .. args[v] );
+
table.insert( extra_param, v .. ':' .. args[v] );
        end
+
end
    end
+
end
  
    result.param = table.concat( param , '_' );
+
result.param = table.concat( param , '_' );
 +
result.extra_param = table.concat( extra_param , '_' );
  
    if #errors == 0 then
+
return result, errorPrinter( errors )
        return specPrinter( args, result )
 
    else
 
        return specPrinter( args, result ) .. " " .. errorPrinter(errors) .. '[[Категория:Википедия:Статьи с ошибочными параметрами координат]]';
 
    end
 
 
end
 
end
  
Строка 539: Строка 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
  
Строка 589: Строка 649:
 
]]
 
]]
 
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
  
Строка 618: Строка 678:
 
]]
 
]]
 
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
 +
 +
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 strong then
        if lat_d < 0 then
+
if lat_d < 0 then
            table.insert(errors, {source, "градусы широты  < 0"})
+
table.insert(errors, {source, "градусы широты  < 0"})
        end
+
end
        if long_d < 0 then
+
if long_d < 0 then
            table.insert(errors, {source, "градусы долготы < 0"})
+
table.insert(errors, {source, "градусы долготы < 0"})
        end
+
end
        --[[
+
--[[
        #coordinates is inconsistent about whether this is an error.  If globe: is
+
#coordinates is inconsistent about whether this is an error.  If globe: is
        specified, it won't error on this condition, but otherwise it will.
+
specified, it won't error on this condition, but otherwise it will.
       
+
        For not simply disable this check.
+
For not simply disable this check.
       
+
        if long_d > 180 then
+
if long_d > 180 then
            table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
+
table.insert(errors, {source, "longitude degrees > 180 with hemisphere flag"})
        end
+
end
        ]]
+
]]
    end
+
end
       
+
    if lat_d > 90 then
+
if lat_d > 90 then
        table.insert(errors, {source, "градусы широты > 90"})
+
table.insert(errors, {source, "градусы широты > 90"})
    end
+
end
    if lat_d < -90 then
+
if lat_d < -90 then
        table.insert(errors, {source, "градусы широты < -90"})
+
table.insert(errors, {source, "градусы широты < -90"})
    end
+
end
    if lat_m >= 60 then
+
if lat_m >= 60 then
        table.insert(errors, {source, "минуты широты >= 60"})
+
table.insert(errors, {source, "минуты широты >= 60"})
    end
+
end
    if lat_m < 0 then
+
if lat_m < 0 then
        table.insert(errors, {source, "минуты широты < 0"})
+
table.insert(errors, {source, "минуты широты < 0"})
    end
+
end
    if lat_s >= 60 then
+
if lat_s >= 60 then
        table.insert(errors, {source, "секунды широты >= 60"})
+
table.insert(errors, {source, "секунды широты >= 60"})
    end
+
end
    if lat_s < 0 then
+
if lat_s < 0 then
        table.insert(errors, {source, "секунды широты < 0"})
+
table.insert(errors, {source, "секунды широты < 0"})
    end
+
end
    if long_d >= 360 then
+
if long_d >= 360 then
        table.insert(errors, {source, "градусы долготы >= 360"})
+
table.insert(errors, {source, "градусы долготы >= 360"})
    end
+
end
    if long_d <= -360 then
+
if long_d <= -360 then
        table.insert(errors, {source, "градусы долготы <= -360"})
+
table.insert(errors, {source, "градусы долготы <= -360"})
    end
+
end
    if long_m >= 60 then
+
if long_m >= 60 then
        table.insert(errors, {source, "минуты долготы >= 60"})
+
table.insert(errors, {source, "минуты долготы >= 60"})
    end
+
end
    if long_m < 0 then
+
if long_m < 0 then
        table.insert(errors, {source, "минуты долготы < 0"})
+
table.insert(errors, {source, "минуты долготы < 0"})
    end
+
end
    if long_s >= 60 then
+
if long_s >= 60 then
        table.insert(errors, {source, "секунды долготы >= 60"})
+
table.insert(errors, {source, "секунды долготы >= 60"})
    end
+
end
    if long_s < 0 then
+
if long_s < 0 then
        table.insert(errors, {source, "секунды долготы < 0"})
+
table.insert(errors, {source, "секунды долготы < 0"})
    end
+
end
   
+
    return errors;
+
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
  
Строка 714: Строка 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
  
Строка 753: Строка 795:
  
 
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
Строка 767: Строка 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, frame)
+
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

Текущая версия на 07:34, 14 июня 2022




--[[
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|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">' ..
			'[//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