Notice: Template failed integrity check: /home/jvzmxxx/wiki/extensions/MobileFrontend/includes/skins/minerva.mustache in /home/jvzmxxx/wiki/includes/TemplateParser.php on line 121
Moduł:Koordynaty: Różnice pomiędzy wersjami – Czarnobyl Wiki
Otwórz menu główne

Czarnobyl Wiki β

Moduł:Koordynaty: Różnice pomiędzy wersjami

(nowy link do geohacka)
 
m (1 wersja)
 
(Nie pokazano 2 wersji utworzonych przez 2 użytkowników)
Linia 2: Linia 2:
  
 
local geoformatdata = {
 
local geoformatdata = {
    supportedFormats = {
+
supportedFormats = {
        { prec = "10st", precision = 10.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
+
{ prec = "10st", precision = 10.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
        { prec = "st",  precision =  1.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
+
{ prec = "st",  precision =  1.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
        { prec = "1",    precision =  0.10000000000000000000, dms = false, secondsFormat = nil,      format = "%0.1f%s" },
+
{ prec = "1",    precision =  0.10000000000000000000, dms = false, secondsFormat = nil,      format = "%0.1f%s" },
        { prec = "min",  precision =  0.01666666666666670000, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s" },
+
{ prec = "min",  precision =  0.01666666666666670000, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s" },
        { prec = "2",    precision =  0.01000000000000000000, dms = false, secondsFormat = nil,      format = "%0.2f%s" },
+
{ prec = "2",    precision =  0.01000000000000000000, dms = false, secondsFormat = nil,      format = "%0.2f%s" },
        { prec = "3",    precision =  0.00100000000000000000, dms = false, secondsFormat = nil,      format = "%0.3f%s" },
+
{ prec = "3",    precision =  0.00100000000000000000, dms = false, secondsFormat = nil,      format = "%0.3f%s" },
        { prec = "sek",  precision =  0.00027777777777777800, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s%02.0f%s" },
+
{ prec = "sek",  precision =  0.00027777777777777800, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s%02.0f%s" },
        { prec = "4",    precision =  0.00010000000000000000, dms = false, secondsFormat = nil,      format = "%0.4f%s" },
+
{ prec = "4",    precision =  0.00010000000000000000, dms = false, secondsFormat = nil,      format = "%0.4f%s" },
        { prec = "sek+", precision =  0.00002777777777777780, dms = true,  secondsFormat = "%04.1f", format = "%0.0f%s%02.0f%s%04.1f%s" },
+
{ prec = "sek+", precision =  0.00002777777777777780, dms = true,  secondsFormat = "%04.1f", format = "%0.0f%s%02.0f%s%04.1f%s" },
        { prec = "5",    precision =  0.00001000000000000000, dms = false, secondsFormat = nil,      format = "%0.5f%s" },
+
{ prec = "5",    precision =  0.00001000000000000000, dms = false, secondsFormat = nil,      format = "%0.5f%s" },
        { prec = "sek2", precision =  0.00000277777777777778, dms = true,  secondsFormat = "%05.2f", format = "%0.0f%s%02.0f%s%05.2f%s" },
+
{ prec = "sek2", precision =  0.00000277777777777778, dms = true,  secondsFormat = "%05.2f", format = "%0.0f%s%02.0f%s%05.2f%s" },
        { prec = "6",    precision =  0.00000100000000000000, dms = false, secondsFormat = nil,      format = "%0.6f%s" },
+
{ prec = "6",    precision =  0.00000100000000000000, dms = false, secondsFormat = nil,      format = "%0.6f%s" },
        { prec = "sek3", precision =  0.00000027777777777778, dms = true,  secondsFormat = "%06.3f", format = "%0.0f%s%02.0f%s%06.3f%s" },
+
{ prec = "sek3", precision =  0.00000027777777777778, dms = true,  secondsFormat = "%06.3f", format = "%0.0f%s%02.0f%s%06.3f%s" },
        { prec = "7",    precision =  0.00000010000000000000, dms = false, secondsFormat = nil,      format = "%0.7f%s" },
+
{ prec = "7",    precision =  0.00000010000000000000, dms = false, secondsFormat = nil,      format = "%0.7f%s" },
        { prec = "sek4", precision =  0.00000002777777777778, dms = true,  secondsFormat = "%07.4f", format = "%0.0f%s%02.0f%s%07.4f%s" },
+
{ prec = "sek4", precision =  0.00000002777777777778, dms = true,  secondsFormat = "%07.4f", format = "%0.0f%s%02.0f%s%07.4f%s" },
    },
+
},
  
    displayGlobes = {
+
displayGlobes = {
        earth    = "EW",
+
earth    = "EW",
        moon      = "EW",
+
moon      = "EW",
        mercury  = "W",
+
mercury  = "W",
        mars      = "W",
+
mars      = "W",
        phobos    = "W",
+
phobos    = "W",
        deimos    = "W",
+
deimos    = "W",
        ganymede  = "W",
+
ganymede  = "W",
        callisto  = "W",
+
callisto  = "W",
        io        = "W",
+
io        = "W",
        europa    = "W",
+
europa    = "W",
        mimas    = "W",
+
mimas    = "W",
        enceladus = "W",
+
enceladus = "W",
        tethys    = "W",
+
tethys    = "W",
        dione    = "W",
+
dione    = "W",
        rhea      = "W",
+
rhea      = "W",
        titan    = "W",
+
titan    = "W",
        lapetus  = "W",
+
lapetus  = "W",
        phoebe    = "W",
+
phoebe    = "W",
        venus    = "E",
+
venus    = "E",
        ceres    = "E",
+
ceres    = "E",
        vesta    = "E",
+
vesta    = "E",
        miranda  = "E",
+
miranda  = "E",
        ariel    = "E",
+
ariel    = "E",
        umbriel  = "E",
+
umbriel  = "E",
        titania  = "E",
+
titania  = "E",
        oberon    = "E",
+
oberon    = "E",
        triton    = "E",
+
triton    = "E",
        pluto    = "E",
+
pluto    = "E",
    },
+
},
  
    latitudeLinkMarkers  = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
+
latitudeLinkMarkers  = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
    longitudeLinkMarkers  = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },
+
longitudeLinkMarkers  = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },
    latitudeGlobeMarkers  = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
+
latitudeGlobeMarkers  = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
    longitudeGlobeMarkers = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },
+
longitudeGlobeMarkers = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },
  
    displayDecimalSeparator = ",",
+
displayDecimalSeparator = ",",
    coordinatesSeparator = "\194\160",
+
coordinatesSeparator = "\194\160",
    topPrefix = "Na mapach: ",
+
topPrefix = "Na mapach: ",
    documentationSubpage = "opis",
+
documentationSubpage = "opis",
  
    geohack_link = "//tools.wmflabs.org/geohack/geohack.php?language=pl&pagename=%s&params=%s",
+
geohack_link = "//tools.wmflabs.org/geohack/geohack.php?language=pl&pagename=%s&params=%s",
    geohack_hint = "Mapy, zdjęcia satelitarne i inne informacje dotyczące miejsca o współrzędnych geograficznych %s %s",
+
geohack_hint = "Mapy, zdjęcia satelitarne i inne informacje dotyczące miejsca o współrzędnych geograficznych %s %s",
  
    -- template API data
+
-- template API data
    apiTemplateName = "szablon",
+
apiTemplateName = "szablon",
    apiMicroName = "mikro",
+
apiMicroName = "mikro",
    apiAutoName = "auto",
+
apiAutoName = "auto",
    apiLatitude = "szerokość",
+
apiLatitude = "szerokość",
    apiLongitude = "długość",
+
apiLongitude = "długość",
   
+
apiMapPoint = "punkt",
    argLocation = "umieść",
 
    valLocationTop = "na górze",
 
    valLocationInline = "w tekście",
 
    valLocationTopAndInline = "w tekście i na górze",
 
   
 
    argPrecision = "dokładność",
 
    valPrecisionDecimal = { "1", "2", "3", "4", "5", "6", "7", },
 
    valPrecisionDMS = { "st", "min", "sek", "sek+", },
 
    valPrecisionAutoDecimal = "dziesiętnie",
 
    valPrecisionAutoDMS = "kątowo",
 
   
 
    argLink = "linkuj",
 
    valLinkYes = "tak",
 
    valLinkNo = "nie",
 
   
 
    argName = "nazwa",
 
  
    -- categories
+
argLocation = "umieść",
    errorCategory = "[[Kategoria:Strony z błędami w parametrach szablonów współrzędnych geograficznych]]",
+
valLocationTop = "na górze",
 +
valLocationInline = "w tekście",
 +
valLocationTopAndInline = "w tekście i na górze",
  
    -- error messages
+
argPrecision = "dokładność",
    errorTooManyPositionalArguments = "Za dużo parametrów",  
+
valPrecisionAutoDecimal = "dziesiętnie",
    errorExpectedIntegerDegree = "Oczekiwana liczba stopni bez kropki dziesiętnej jeśli podawane są minuty (%s°%s')",
+
valPrecisionAutoDMS = "kątowo",
    errorInvalidMinutes = "Wartość minut jest nieprawidłowa (%s°%s')",
+
 
    errorExpectedIntegerMinutes = "Oczekiwana liczba minut bez kropki dziesiętnej jeśli podawane są sekundy (%s°%s'%s″)",
+
argLink = "linkuj",
    errorInvalidSeconds = "Wartość sekund jest nieprawidłowa (%s°%s'%s″)",
+
valLinkYes = "tak",
    errorInvalidPositionalArguments = "Nieprawidłowe parametry",
+
valLinkNo = "nie",
    errorExpectedNonNegativeLatitude = "Oczekiwana nieujemna wartość szerokości geograficznej: %f",
+
 
    errorLatitudeOutOfRange = "Przekroczony zakres szerokości geograficznej (%f)",
+
argName = "nazwa",
    errorExpectedNonNegativeLongitude = "Oczekiwana nieujemna wartość długości geograficznej: %f",
+
    errorLongitudeOutOfRange = "Przekroczony zakres długości geograficznej (%f)",
+
argGlobe = "glob", -- for 'auto'
    errorUnrecognizedLinkOption = "Niedozwolona wartość parametru ''link'': %s",
+
 
    errorUnrecognizedLocationOption = "Niedozwolona wartość parametru ''umieść'': %s",
+
-- apiMapPoint
 +
argLatitude = "szerokość",
 +
argLongitude = "długość",
 +
argMark = "znak",
 +
argMarkSize = "rozmiar znaku",
 +
argDescription = "opis",
 +
argGeohack = "opcje geohack",
 +
argDescriptionPosition = "pozycja",
 +
 +
defArgMark = "Red pog.svg",
 +
defArgMarkSize = 6,
 +
defArgGeohack = "type:city",
 +
 
 +
mapPointMapping = {
 +
["Mars"] = "globe:Mars",
 +
["Księżyc"] = "globe:Moon",
 +
["Wenus"] = "globe:Venus",
 +
["Merkury"] = "globe:Mercury",
 +
},
 +
 
 +
-- categories
 +
errorCategory = "[[Kategoria:Strony z błędami w parametrach szablonów współrzędnych geograficznych]]",
 +
 
 +
-- error messages
 +
errorTooManyPositionalArguments = "Za dużo parametrów",  
 +
errorExpectedIntegerDegree = "Oczekiwana liczba stopni bez kropki dziesiętnej jeśli podawane są minuty (%s°%s')",
 +
errorInvalidMinutes = "Wartość minut jest nieprawidłowa (%s°%s')",
 +
errorExpectedIntegerMinutes = "Oczekiwana liczba minut bez kropki dziesiętnej jeśli podawane są sekundy (%s°%s'%s”)",
 +
errorInvalidSeconds = "Wartość sekund jest nieprawidłowa (%s°%s'%s”)",
 +
errorInvalidPositionalArguments = "Nieprawidłowe parametry",
 +
errorExpectedNonNegativeLatitude = "Oczekiwana nieujemna wartość szerokości geograficznej: %f",
 +
errorLatitudeOutOfRange = "Przekroczony zakres szerokości geograficznej (%f)",
 +
errorExpectedNonNegativeLongitude = "Oczekiwana nieujemna wartość długości geograficznej: %f",
 +
errorLongitudeOutOfRange = "Przekroczony zakres długości geograficznej (%f)",
 +
errorUnrecognizedLinkOption = "Niedozwolona wartość parametru ''link'': %s",
 +
errorUnrecognizedLocationOption = "Niedozwolona wartość parametru ''umieść'': %s",
 
}
 
}
  
 
local function create()
 
local function create()
   
 
    -- initialize default data
 
    local result = {
 
        latitude  = 0,
 
        longitude = 0,
 
        precision = 1,
 
        params    = nil,
 
        inline    = false,
 
        top      = false,
 
        link      = true,
 
    }
 
  
    function result:parseCoordinates(args)
+
-- initialize default data
 +
local result = {
 +
latitude  = 0,
 +
longitude = 0,
 +
precision = 1,
 +
params    = nil,
 +
inline    = false,
 +
top      = false,
 +
link      = true,
 +
}
 +
 
 +
function result:parseCoordinates(args)
  
        local function isInt(s)
+
local function isInt(s)
            -- up to 3 digits is enough for coordinates
+
-- up to 3 digits is enough for coordinates
            return s:match"^-?%d%d?%d?$"
+
return s:match"^-?%d%d?%d?$"
        end
+
end
  
        local lang = mw.getContentLanguage()
+
local lang = mw.getContentLanguage()
  
        local function parseTypes()
+
local function parseTypes()
            local types = {}
+
local types = {}
            for i = 1, 9 do
+
for i = 1, 9 do
                local arg = mw.text.trim(args[i] or "")
+
local arg = mw.text.trim(args[i] or "")
                if #arg==0 then
+
if #arg==0 then
                    table.insert(types, "_")
+
table.insert(types, "_")
                elseif arg == "N" or arg=="E" or arg=="S" or arg=="W" then
+
elseif arg == "N" or arg=="E" or arg=="S" or arg=="W" then
                    table.insert(types, arg)
+
table.insert(types, arg)
                elseif lang:parseFormattedNumber(arg) then
+
elseif lang:parseFormattedNumber(arg) then
                    local scientific = arg:match"[eE]"
+
local scientific = arg:match"[eE]"
                    table.insert(types, scientific and "X" or "#")
+
table.insert(types, scientific and "X" or "#")
                else
+
else
                    table.insert(types, "X")
+
table.insert(types, "X")
                end
+
end
            end
+
end
 
   
 
   
            return table.concat(types, "")
+
return table.concat(types, "")
        end
+
end
 
   
 
   
        local function calculateDecimalPrecision(s)
+
local function calculateDecimalPrecision(s)
            local s1 = string.gsub(s,"%d","0")
+
local s1 = string.gsub(s,"%d","0")
            local s2 = string.gsub(s1,"^-","0")
+
local s2 = string.gsub(s1,"^-","0")
            local s3 = string.gsub(s2,"0$","1")
+
local s3 = string.gsub(s2,"0$","1")
            local result = lang:parseFormattedNumber(s3)
+
local result = lang:parseFormattedNumber(s3)
            return result > 0 and result or 1.0
+
return result > 0 and result or 1.0
        end
+
end
  
        local function selectAutoPrecision(p1, p2)
+
local function selectAutoPrecision(p1, p2)
            local dms = nil
+
local dms = nil
            if (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDecimal)  then
+
if (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDecimal)  then
                dms = false
+
dms = false
            elseif not args[geoformatdata.argPrecision] or (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDMS) then
+
elseif not args[geoformatdata.argPrecision] or (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDMS) then
                dms = true
+
dms = true
            else
+
else
            -- precision is selected explicit in the parameter
+
-- precision is selected explicit in the parameter
                return
+
return
            end
+
end
  
            -- select automatic precision
+
-- select automatic precision
            local precision = p1 < p2 and p1 or p2
+
local precision = p1 < p2 and p1 or p2
 
   
 
   
            -- find best DMS or decimal precision
+
-- find best DMS or decimal precision
            if precision < 1 then
+
if precision < 1 then
                local eps = precision / 1024
+
local eps = precision / 1024
                for i,v in ipairs(geoformatdata.supportedFormats) do
+
for i,v in ipairs(geoformatdata.supportedFormats) do
                    if (v.dms == dms) and ((v.precision - precision) < eps) then
+
if (v.dms == dms) and ((v.precision - precision) < eps) then
                        precision = v.precision
+
precision = v.precision
                        break
+
break
                    end
+
end
                end
+
end
            end
+
end
  
            self.precision = precision
+
self.precision = precision
        end
+
end
       
+
        local function parseAngle(index, extra)
+
local function parseAngle(index, extra)
 
   
 
   
            local degree = mw.text.trim(args[index])
+
local degree = mw.text.trim(args[index])
            local result = lang:parseFormattedNumber(degree)
+
local result = lang:parseFormattedNumber(degree)
            if extra == 0 then
+
if extra == 0 then
                return true, result, calculateDecimalPrecision(degree)
+
return true, result, calculateDecimalPrecision(degree)
            end
+
end
 
   
 
   
            local minutes = mw.text.trim(args[index+1])
+
local minutes = mw.text.trim(args[index+1])
            if not isInt(degree) then
+
if not isInt(degree) then
                return false, string.format(geoformatdata.errorExpectedIntegerDegree, degree, minutes)
+
return false, string.format(geoformatdata.errorExpectedIntegerDegree, degree, minutes)
            end
+
end
 
   
 
   
            local precision = isInt(minutes) and 0.01666666666666670000 or 0.00027777777777777800
+
local precision = isInt(minutes) and 0.01666666666666670000 or 0.00027777777777777800
 
   
 
   
            local value = lang:parseFormattedNumber(minutes)
+
local value = lang:parseFormattedNumber(minutes)
            if value < 0 or value >= 60 then
+
if value < 0 or value >= 60 then
                return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
+
return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
            end
+
end
 
   
 
   
            if result < 0 then
+
if result < 0 then
                result = result * 60 - value
+
result = result * 60 - value
            else
+
else
                result = result * 60 + value
+
result = result * 60 + value
            end
+
end
 
   
 
   
            if extra == 1 then
+
if extra == 1 then
                return true, result / 60, precision
+
return true, result / 60, precision
            end
+
end
 
   
 
   
            local seconds = mw.text.trim(args[index+2])
+
local seconds = mw.text.trim(args[index+2])
            if not isInt(minutes) then
+
if not isInt(minutes) then
                return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
+
return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
            end
+
end
 
   
 
   
            precision = 0.00027777777777777800 * calculateDecimalPrecision(seconds)
+
precision = 0.00027777777777777800 * calculateDecimalPrecision(seconds)
            local value = lang:parseFormattedNumber(seconds)
+
local value = lang:parseFormattedNumber(seconds)
            if value < 0 or value >= 60 then
+
if value < 0 or value >= 60 then
                return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
+
return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
            end
+
end
 
   
 
   
            if result < 0 then
+
if result < 0 then
                result = result * 60 - value
+
result = result * 60 - value
            else
+
else
                result = result * 60 + value
+
result = result * 60 + value
            end
+
end
 
   
 
   
            return true, result / 3600, precision
+
return true, result / 3600, precision
        end
+
end
       
+
        local function analyzeAngle(degree, minutes, seconds)
+
local function analyzeAngle(degree, minutes, seconds)
            local result = lang:parseFormattedNumber(degree)
+
local result = lang:parseFormattedNumber(degree)
            if not result then
+
if not result then
                return false, geoformatdata.errorInvalidPositionalArguments
+
return false, geoformatdata.errorInvalidPositionalArguments
            end
+
end
 
   
 
   
            if not string.match(degree, "^%d+$") then
+
if not string.match(degree, "^%d+$") then
                if (#minutes > 0) or (#seconds > 0) then
+
if (#minutes > 0) or (#seconds > 0) then
                    -- expected empty minutes and empty seconds if float degree is given
+
-- expected empty minutes and empty seconds if float degree is given
                    return false, geoformatdata.errorInvalidPositionalArguments
+
return false, geoformatdata.errorInvalidPositionalArguments
                end
+
end
 
   
 
   
                return true, result, calculateDecimalPrecision(degree)
+
return true, result, calculateDecimalPrecision(degree)
            end
+
end
 
   
 
   
            if #minutes == 0 then
+
if #minutes == 0 then
                if #seconds > 0 then
+
if #seconds > 0 then
                    -- expected empty seconds if minute is not given
+
-- expected empty seconds if minute is not given
                    return false, geoformatdata.errorInvalidPositionalArguments
+
return false, geoformatdata.errorInvalidPositionalArguments
                end
+
end
           
+
                return true, result, calculateDecimalPrecision(degree)
+
return true, result, calculateDecimalPrecision(degree)
            end
+
end
 
   
 
   
            local minute = lang:parseFormattedNumber(minutes)
+
local minute = lang:parseFormattedNumber(minutes)
            if not minute or (minute >= 60) then
+
if not minute or (minute >= 60) then
                return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
+
return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
            end
+
end
 
   
 
   
            result = result * 60 + minute
+
result = result * 60 + minute
            if not string.match(minutes, "^%d+$") then
+
if not string.match(minutes, "^%d+$") then
                if #seconds > 0 then
+
if #seconds > 0 then
                    return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
+
return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
                end
+
end
  
                return true, result/60, 0.00027777777777777800
+
return true, result/60, 0.00027777777777777800
            end
+
end
 
   
 
   
            if #seconds == 0 then
+
if #seconds == 0 then
                return true, result/60, 0.01666666666666670000
+
return true, result/60, 0.01666666666666670000
            end
+
end
 
   
 
   
            local second = lang:parseFormattedNumber(seconds)
+
local second = lang:parseFormattedNumber(seconds)
            if not second or (second >= 60) then
+
if not second or (second >= 60) then
                return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
+
return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
            end
+
end
  
            result = result*60 + second
+
result = result*60 + second
            return true, result/3600, calculateDecimalPrecision(seconds)*0.00027777777777777800
+
return true, result/3600, calculateDecimalPrecision(seconds)*0.00027777777777777800
        end
+
end
 
   
 
   
        assert(args, "Missing template arguments")
+
assert(args, "Missing template arguments")
        if not args[1] then
+
if not args[1] then
            -- display nothing if no positional arguments are provided
+
-- display nothing if no positional arguments are provided
            return false, nil
+
return false, nil
        end
+
end
 
   
 
   
        if args[10] then
+
if args[10] then
            return false, geoformatdata.errorTooManyPositionalArguments
+
return false, geoformatdata.errorTooManyPositionalArguments
        end
+
end
 +
 
 +
local types = parseTypes()
 +
 
 +
local function parseSimpleText()
 +
local arg = mw.text.trim(args[1])
 +
if types == "XX_______" then
 +
self.params = mw.text.trim(args[2])
 +
end
 +
 
 +
local d1, m1, s1, h1, d2, m2, s2, h2 = mw.ustring.match(arg, "^([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([NSEW])[,;]?%s+([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([EWNS])$")
 +
if d1 then
 +
if (((h1 == "N") or (h1 == "S")) and ((h2 == "N") or (h2 == "S"))) or (((h1 == "E") or (h1 == "W")) and ((h2 == "E") or (h2 == "W"))) then
 +
return geoformatdata.errorInvalidPositionalArguments
 +
end
 +
 
 +
local status1, v1, p1 = analyzeAngle(d1, m1, s1)
 +
if not status1 then
 +
return v1
 +
end
 +
 
 +
local status2, v2, p2 = analyzeAngle(d2, m2, s2)
 +
if not status2 then
 +
return v2
 +
end
 +
 
 +
if (h1 == "S") or (h1 == "W") then
 +
v1 = -v1;
 +
end
 +
if (h2 == "S") or (h2 == "W") then
 +
v2 = -v2;
 +
end
  
        local types = parseTypes()
+
self.latitude  = ((h1 == "N") or (h1 == "S")) and v1 or v2
 +
self.longitude = ((h1 == "E") or (h1 == "W")) and v1 or v2
 +
selectAutoPrecision(p1, p2)
 +
return nil
 +
end
  
        local function parseSimpleText()
+
local lat, lon = string.match(arg, "^(-?[0-9%.,]+)%s+(-?[0-9%.,]+)$")
            local arg = mw.text.trim(args[1])
+
if lat then
            if types == "XX_______" then
+
local latitude = lang:parseFormattedNumber(lat)
                self.params = mw.text.trim(args[2])
+
local longitude = lang:parseFormattedNumber(lon)
            end
+
if latitude and longitude then
 +
self.latitude = latitude
 +
self.longitude = longitude
 +
selectAutoPrecision(calculateDecimalPrecision(lat), calculateDecimalPrecision(lon))
 +
return nil
 +
end
 +
end
  
            local d1, m1, s1, h1, d2, m2, s2, h2 = mw.ustring.match(arg, "^([0-9,.]+)[°_]?%s*([0-9,.]*)['′_]?%s*([0-9,.]*)[\"″_]?%s*([NSEW])[,;]?%s+([0-9,.]+)[°_]?%s*([0-9,.]*)['′_]?%s*([0-9,.]*)[\"″_]?%s*([EWNS])$")
+
return geoformatdata.errorInvalidPositionalArguments
            if d1 then
+
end
                if (((h1 == "N") or (h1 == "S")) and ((h2 == "N") or (h2 == "S"))) or (((h1 == "E") or (h1 == "W")) and ((h2 == "E") or (h2 == "W"))) then
 
                    return geoformatdata.errorInvalidPositionalArguments
 
                end
 
  
                local status1, v1, p1 = analyzeAngle(d1, m1, s1)
+
if (types == "X________") or (types == "XX_______") then
                if not status1 then
+
local errorMessage = parseSimpleText()
                    return v1
+
if errorMessage then
                end
+
return false, errorMessage
 +
end
 +
else
 +
local mapping = mw.loadData("Module:Koordynaty/parserData")[types]
 +
if not mapping then
 +
return false, geoformatdata.errorInvalidPositionalArguments
 +
end
  
                local status2, v2, p2 = analyzeAngle(d2, m2, s2)
+
if mapping[7] ~= 0 then
                if not status2 then
+
self.params = mw.text.trim(args[mapping[7]])
                    return v2
+
end
                end
 
  
                if (h1 == "S") or (h1 == "W") then
+
local status1, latitude, latPrecision = parseAngle(mapping[1], mapping[2])
                    v1 = -v1;
+
if not status1 then
                end
+
return false, latitude
                if (h2 == "S") or (h2 == "W") then
+
end
                    v2 = -v2;
 
                end
 
  
                self.latitude  = ((h1 == "N") or (h1 == "S")) and v1 or v2
+
if mapping[3] ~= 0 then
                self.longitude = ((h1 == "E") or (h1 == "W")) and v1 or v2
+
assert(mapping[3] == 1 or mapping[3] == -1, "Invalid adjust mode: " .. mapping[3]);
                selectAutoPrecision(p1, p2)
+
if latitude < 0 then
                return nil
+
return false, string.format(geoformatdata.errorExpectedNonNegativeLatitude, latitude)
            end
+
end
 +
latitude = mapping[3] * latitude
 +
end
  
            local lat, lon = string.match(arg, "^(-?[0-9.,]+)%s+(-?[0-9.,]+)$")
+
local status2, longitude, lonPrecision = parseAngle(mapping[4], mapping[5])
            if lat then
+
if not status2 then
                local latitude = lang:parseFormattedNumber(lat)
+
return false, longitude
                local longitude = lang:parseFormattedNumber(lon)
+
end
                if latitude and longitude then
 
                    self.latitude = latitude
 
                    self.longitude = longitude
 
                    selectAutoPrecision(calculateDecimalPrecision(lat), calculateDecimalPrecision(lon))
 
                    return nil
 
                end
 
            end
 
  
            return geoformatdata.errorInvalidPositionalArguments
+
if mapping[6] ~= 0 then
        end
+
assert(mapping[6] == 1 or mapping[6] == -1, "Invalid adjust mode: " .. mapping[6]);
 +
if longitude < 0 then
 +
return false, string.format(geoformatdata.errorExpectedNonNegativeLongitude, longitude)
 +
end
 +
longitude = mapping[6] * longitude
 +
end
  
        if (types == "X________") or (types == "XX_______") then
+
self.latitude  = latitude
            local errorMessage = parseSimpleText()
+
self.longitude = longitude
            if errorMessage then
+
selectAutoPrecision(latPrecision, lonPrecision)
                return false, errorMessage
+
end
            end
+
 
        else
+
if self.latitude < -90 or self.latitude > 90 then
            local mapping = mw.loadData("Module:Koordynaty/parserData")[types]
+
return false, string.format(geoformatdata.errorLatitudeOutOfRange, self.latitude)
            if not mapping then
+
end
                return false, geoformatdata.errorInvalidPositionalArguments
 
            end
 
   
 
            if mapping[7] ~= 0 then
 
                self.params = mw.text.trim(args[mapping[7]])
 
            end
 
           
 
            local status1, latitude, latPrecision = parseAngle(mapping[1], mapping[2])
 
            if not status1 then
 
                return false, latitude
 
            end
 
   
 
            if mapping[3] ~= 0 then
 
                assert(mapping[3] == 1 or mapping[3] == -1, "Invalid adjust mode: " .. mapping[3]);
 
                if latitude < 0 then
 
                    return false, string.format(geoformatdata.errorExpectedNonNegativeLatitude, latitude)
 
                end
 
                latitude = mapping[3] * latitude
 
            end
 
   
 
            local status2, longitude, lonPrecision = parseAngle(mapping[4], mapping[5])
 
            if not status2 then
 
                return false, longitude
 
            end
 
   
 
            if mapping[6] ~= 0 then
 
                assert(mapping[6] == 1 or mapping[6] == -1, "Invalid adjust mode: " .. mapping[6]);
 
                if longitude < 0 then
 
                    return false, string.format(geoformatdata.errorExpectedNonNegativeLongitude, longitude)
 
                end
 
                longitude = mapping[6] * longitude
 
            end
 
           
 
            self.latitude  = latitude
 
            self.longitude = longitude
 
            selectAutoPrecision(latPrecision, lonPrecision)
 
        end
 
   
 
        if self.latitude < -90 or self.latitude > 90 then
 
            return false, string.format(geoformatdata.errorLatitudeOutOfRange, self.latitude)
 
        end
 
 
   
 
   
        if self.longitude < -360 or self.longitude > 360 then
+
if self.longitude < -360 or self.longitude > 360 then
            return false, string.format(geoformatdata.errorLongitudeOutOfRange, self.longitude)
+
return false, string.format(geoformatdata.errorLongitudeOutOfRange, self.longitude)
        end
+
end
       
+
        return true, nil
+
return true, nil
    end
+
end
   
+
 
    function result:normalize()
+
function result:normalize()
        assert(self,"Did you use '.' instead of ':' while calling the function?")
+
assert(self,"Did you use '.' instead of ':' while calling the function?")
        local mode = false
+
local mode = false
        if self.params then
+
if self.params then
            for i, v in ipairs(mw.text.split( self.params, '_', true )) do
+
for i, v in ipairs(mw.text.split( self.params, '_', true )) do
                if mode then
+
if mode then
                    -- more than one globe, display as given
+
-- more than one globe, display as given
                    return
+
return
                end
+
end
               
+
                local globe = string.match(v, "^globe:(%a+)$")
+
local globe = string.match(v, "^globe:(%a+)$")
                if globe then
+
if globe then
                    mode = geoformatdata.displayGlobes[string.lower(globe)]
+
mode = geoformatdata.displayGlobes[string.lower(globe)]
                    if not mode then
+
if not mode then
                        -- unrecognized display as given
+
-- unrecognized display as given
                        return
+
return
                    end
+
end
                end
+
end
            end
+
end
        end
+
end
   
+
 
        if mode == "?" then
+
if mode == "?" then
            -- unrecognized left as given
+
-- unrecognized left as given
        elseif mode == "W" then
+
elseif mode == "W" then
            if self.longitude > 0 then
+
if self.longitude > 0 then
                self.longitude = self.longitude - 360
+
self.longitude = self.longitude - 360
            end
+
end
        elseif mode == "E" then
+
elseif mode == "E" then
            if self.longitude < 0 then
+
if self.longitude < 0 then
                self.longitude = self.longitude + 360
+
self.longitude = self.longitude + 360
            end
+
end
        elseif self.longitude < -180 then
+
elseif self.longitude < -180 then
            self.longitude = self.longitude + 360
+
self.longitude = self.longitude + 360
        elseif self.longitude > 180 then
+
elseif self.longitude > 180 then
            self.longitude = self.longitude - 360
+
self.longitude = self.longitude - 360
        end
+
end
    end
+
end
   
+
 
    function result:parseOptions(args)
+
function result:parseOptions(args)
        -- TODO process notation in conjuction with precision
+
 
        local precision = args[geoformatdata.argPrecision]
+
local function adjustPrecision(dms)
        if precision and (precision ~= geoformatdata.valPrecisionAutoDecimal) and (precision ~= geoformatdata.valPrecisionAutoDMS) then
+
if not self.precision or (self.precision >= 1) then
            self.precision = precision
+
return
        end
+
end
       
+
 
        self.name = args[geoformatdata.argName]
+
local eps = self.precision / 1024
     
+
for i, v in ipairs(geoformatdata.supportedFormats) do
        local link = args[geoformatdata.argLink]
+
if (v.dms == dms) and ((v.precision - self.precision) < eps) then
        if link == geoformatdata.valLinkYes then
+
self.precision = v.precision
            self.link = true
+
break
        elseif link == geoformatdata.valLinkNo then
+
end
            self.link = false
+
end
        elseif link then
+
end
            return false, string.format(geoformatdata.errorUnrecognizedLinkOption, link)
+
        else -- default is yes
+
local precision = args[geoformatdata.argPrecision]
            self.link = true
+
if precision then
        end
+
if precision == geoformatdata.valPrecisionAutoDMS then
 +
adjustPrecision(true)
 +
elseif precision == geoformatdata.valPrecisionAutoDecimal then
 +
adjustPrecision(false)
 +
else
 +
self.precision = precision
 +
end
 +
end
 +
 
 +
self.name = args[geoformatdata.argName]
 +
 
 +
local link = args[geoformatdata.argLink]
 +
if link == geoformatdata.valLinkYes then
 +
self.link = true
 +
elseif link == geoformatdata.valLinkNo then
 +
self.link = false
 +
elseif link then
 +
return false, string.format(geoformatdata.errorUnrecognizedLinkOption, link)
 +
else -- default is yes
 +
self.link = true
 +
end
  
        local location = args[geoformatdata.argLocation]
+
local location = args[geoformatdata.argLocation]
        if location == geoformatdata.valLocationTop then
+
if location == geoformatdata.valLocationTop then
            self.top = true
+
self.top = true
            self.inline = false
+
self.inline = false
        elseif location == geoformatdata.valLocationInline then
+
elseif location == geoformatdata.valLocationInline then
            self.top = false
+
self.top = false
            self.inline = true
+
self.inline = true
        elseif location == geoformatdata.valLocationTopAndInline then
+
elseif location == geoformatdata.valLocationTopAndInline then
            self.top = true
+
self.top = true
            self.inline = true
+
self.inline = true
        elseif location then
+
elseif location then
            return false, string.format(geoformatdata.errorUnrecognizedLocationOption, location)
+
return false, string.format(geoformatdata.errorUnrecognizedLocationOption, location)
        elseif mw.title.getCurrentTitle().isTalkPage then
+
elseif mw.title.getCurrentTitle().isTalkPage then
            -- an exception for talk pages
+
-- an exception for talk pages
            self.top = false
+
self.top = false
            self.inline = true
+
self.inline = true
        else -- default if not given
+
else -- default if not given
            self.top = true
+
self.top = true
            self.inline = false
+
self.inline = false
        end
+
end
  
        return true, nil
+
return true, nil
    end
+
end
 
   
 
   
    function result:display(inlinePrefix)
+
function result:format()
       
+
        local function selectFormat(precision)
+
local function selectFormat(precision)
            local supportedFormats = geoformatdata.supportedFormats
+
local supportedFormats = geoformatdata.supportedFormats
            local precisionType = type(precision)
+
local precisionType = type(precision)
            if precisionType == "string" then
+
if precisionType == "string" then
                -- find wikipedia template precision
+
-- find wikipedia template precision
                for i, v in ipairs(supportedFormats) do
+
for i, v in ipairs(supportedFormats) do
                    if (precision == v.prec) then
+
if (precision == v.prec) then
                        return true, v
+
return true, v
                    end
+
end
                end
+
end
            elseif precisionType == "number" then
+
elseif precisionType == "number" then
                -- find wikidata precision
+
-- find wikidata precision
                for i, v in ipairs(supportedFormats) do
+
for i, v in ipairs(supportedFormats) do
                    local prec = v.precision
+
local prec = v.precision
                    local eps = prec / 64
+
local eps = prec / 64
                    local minPrec = prec - eps
+
local minPrec = prec - eps
                    local maxPrec = prec + eps
+
local maxPrec = prec + eps
                    if (minPrec < precision) and (precision < maxPrec) then
+
if (minPrec < precision) and (precision < maxPrec) then
                        return true, v
+
return true, v
                    end
+
end
                end
+
end
            end
+
end
 +
 
 +
-- use the last one with highest precision
 +
return false, supportedFormats[#supportedFormats]
 +
end
  
            -- use the last one with highest precision
+
local function formatAngle(value, format, markers, decimalSeparator)
            return false, supportedFormats[#supportedFormats]
+
assert(type(value) == "number")
        end
+
local prefix = value < 0 and markers.negativePrefix or markers.positivePrefix
   
+
local suffix = value < 0 and markers.negativeSuffix or markers.positiveSuffix
        local function formatAngle(value, format, markers, decimalSeparator)
 
            assert(type(value) == "number")
 
            local prefix = value < 0 and markers.negativePrefix or markers.positivePrefix
 
            local suffix = value < 0 and markers.negativeSuffix or markers.positiveSuffix
 
       
 
            value = math.abs(value)
 
       
 
            local result = nil
 
       
 
            if not format.dms then
 
                -- format decimal value
 
                if format.precision > 1 then
 
                    -- round the value
 
                    value = math.floor(value / format.precision) * format.precision
 
                end
 
       
 
                result = string.format(format.format, value, markers.degree)
 
            else
 
                -- format dms value
 
                local angle  = math.floor(value)
 
                local minutes = math.floor((value - angle) * 60)
 
                local seconds = tonumber(string.format(format.secondsFormat, (value - angle) * 3600 - minutes * 60))
 
               
 
                -- fix rounded seconds
 
                if seconds == 60 then
 
                    minutes = minutes + 1
 
                    seconds = 0
 
                    if minutes == 60 then
 
                        angle = angle + 1
 
                        minutes = 0
 
                    end
 
                end
 
       
 
                if format.precision > 0.01 then
 
                    -- round the value
 
                    if seconds >= 30 then
 
                        minutes = minutes + 1
 
                    end
 
                    seconds = 0
 
                    if minutes == 60 then
 
                        angle = angle + 1
 
                        minutes = 0
 
                    end
 
                end
 
       
 
                result = string.format(format.format, angle, markers.degree, minutes, markers.minute, seconds, markers.second)
 
            end
 
       
 
            if decimalSeparator then
 
                result = string.gsub(result, "%.", decimalSeparator)
 
            end
 
       
 
            return prefix .. result .. suffix
 
        end
 
       
 
        local function formatDegree(value, decimalSeparator)
 
            local result = string.format("%f", value)
 
       
 
            if decimalSeparator then
 
                result = string.gsub(result, "%.", decimalSeparator)
 
            end
 
       
 
            return result
 
        end
 
   
 
        local function fullpagenamee()
 
            local title = mw.title.getCurrentTitle()
 
            return title.namespace == 0
 
                and title:partialUrl()
 
                or  title.nsText .. ":" .. title:partialUrl()
 
        end
 
   
 
        local status, format = selectFormat(self.precision)
 
        assert(format)
 
   
 
        local prettyLatitude  = formatAngle(self.latitude,  format, geoformatdata.latitudeGlobeMarkers,  geoformatdata.displayDecimalSeparator)
 
        local prettyLongitude = formatAngle(self.longitude, format, geoformatdata.longitudeGlobeMarkers, geoformatdata.displayDecimalSeparator)
 
   
 
        if not self.link then
 
            return mw.text.nowiki(prettyLatitude .. geoformatdata.coordinatesSeparator .. prettyLongitude)
 
        end
 
   
 
        local params = {
 
            formatAngle(self.latitude,  format, geoformatdata.latitudeLinkMarkers),
 
            formatAngle(self.longitude, format, geoformatdata.longitudeLinkMarkers),
 
        }
 
   
 
        if self.params then
 
            table.insert(params, self.params)
 
        end
 
   
 
        local degreeLatitude  = formatDegree(self.latitude,  geoformatdata.displayDecimalSeparator)
 
        local degreeLongitude = formatDegree(self.longitude, geoformatdata.displayDecimalSeparator)
 
   
 
        local geohack_link = string.format(geoformatdata.geohack_link, fullpagenamee(), table.concat(params,"_"))
 
        if self.name then
 
            geohack_link = geohack_link .. "&title=" .. mw.uri.encode(self.name)
 
        end
 
   
 
        local pretty_hint = string.format(geoformatdata.geohack_hint, prettyLatitude, prettyLongitude)
 
        local degree_hint = string.format(geoformatdata.geohack_hint, degreeLatitude, degreeLongitude)
 
        local separator = mw.text.nowiki(geoformatdata.coordinatesSeparator)
 
   
 
        local result = {
 
            "[", geohack_link, " ",
 
            "<span class=\"geo-default\">",
 
                "<span class=\"geo-dms\" title=\"", mw.text.nowiki(pretty_hint), "\">",
 
                    "<span class=\"latitude\">", mw.text.nowiki(prettyLatitude), "</span>",
 
                    separator,
 
                    "<span class=\"longitude\">", mw.text.nowiki(prettyLongitude), "</span>",
 
                "</span>",
 
            "</span>",
 
            "<span class=\"geo-multi-punct\">/</span>",
 
            "<span class=\"geo-nondefault\">",
 
                "<span class=\"geo-dms\" title=\"", mw.text.nowiki(degree_hint), "\">",
 
                    "<span class=\"latitude\">", mw.text.nowiki(degreeLatitude), "</span>",
 
                    separator,
 
                    "<span class=\"longitude\">", mw.text.nowiki(degreeLongitude), "</span>",
 
                "</span>",
 
            "</span>",
 
            "]"
 
        }
 
   
 
        local text = table.concat(result, "")
 
   
 
        if not self.inline and not self.top then
 
            return text
 
        end
 
       
 
        result = {}
 
   
 
        if self.inline then
 
            if inlinePrefix then
 
                table.insert(result, inlinePrefix)
 
            end
 
            if self.top then
 
                table.insert(result, "<span class=\"coordinates inline inline-and-top plainlinks\">")
 
            else
 
                table.insert(result, "<span class=\"coordinates inline plainlinks\">")
 
            end
 
            table.insert(result, text)
 
            table.insert(result, "</span>")
 
        end
 
   
 
        if self.top then
 
            table.insert(result, "<span id=\"coordinates\" class=\"coordinates put-in-header plainlinks\">")
 
            table.insert(result, geoformatdata.topPrefix)
 
            table.insert(result, text)
 
            table.insert(result, "</span>")
 
        end
 
   
 
        return table.concat(result, "")
 
    end
 
  
    function result:extensionGeoData(frame)
+
value = math.abs(value)
        local params = {}
+
 
 +
local result = nil
 +
 
 +
if not format.dms then
 +
-- format decimal value
 +
if format.precision > 1 then
 +
-- round the value
 +
value = math.floor(value / format.precision) * format.precision
 +
end
 +
 
 +
result = string.format(format.format, value, markers.degree)
 +
else
 +
-- format dms value
 +
local angle  = math.floor(value)
 +
local minutes = math.floor((value - angle) * 60)
 +
local seconds = tonumber(string.format(format.secondsFormat, (value - angle) * 3600 - minutes * 60))
 +
 
 +
-- fix rounded seconds
 +
if seconds == 60 then
 +
minutes = minutes + 1
 +
seconds = 0
 +
if minutes == 60 then
 +
angle = angle + 1
 +
minutes = 0
 +
end
 +
end
 +
 
 +
if format.precision > 0.01 then
 +
-- round the value
 +
if seconds >= 30 then
 +
minutes = minutes + 1
 +
end
 +
seconds = 0
 +
if minutes == 60 then
 +
angle = angle + 1
 +
minutes = 0
 +
end
 +
end
 +
 
 +
result = string.format(format.format, angle, markers.degree, minutes, markers.minute, seconds, markers.second)
 +
end
 +
 
 +
if decimalSeparator then
 +
result = string.gsub(result, "%.", decimalSeparator)
 +
end
 +
 
 +
return prefix .. result .. suffix
 +
end
 +
 
 +
local function formatDegree(value, decimalSeparator)
 +
local result = string.format("%f", value)
 +
 
 +
if decimalSeparator then
 +
result = string.gsub(result, "%.", decimalSeparator)
 +
end
 +
 +
return result
 +
end
 +
 
 +
local function fullpagenamee()
 +
local title = mw.title.getCurrentTitle()
 +
return title.namespace == 0
 +
and title:partialUrl()
 +
or  title.nsText .. ":" .. title:partialUrl()
 +
end
 +
 
 +
local status, format = selectFormat(self.precision)
 +
assert(format)
 +
 
 +
local prettyLatitude  = formatAngle(self.latitude,  format, geoformatdata.latitudeGlobeMarkers,  geoformatdata.displayDecimalSeparator)
 +
local prettyLongitude = formatAngle(self.longitude, format, geoformatdata.longitudeGlobeMarkers, geoformatdata.displayDecimalSeparator)
 +
 
 +
if not self.link then
 +
return mw.text.nowiki(prettyLatitude .. geoformatdata.coordinatesSeparator .. prettyLongitude)
 +
end
 +
 
 +
local params = {
 +
formatAngle(self.latitude,  format, geoformatdata.latitudeLinkMarkers),
 +
formatAngle(self.longitude, format, geoformatdata.longitudeLinkMarkers),
 +
}
 +
 
 +
if self.params then
 +
table.insert(params, self.params)
 +
end
 +
 
 +
local degreeLatitude  = formatDegree(self.latitude,  geoformatdata.displayDecimalSeparator)
 +
local degreeLongitude = formatDegree(self.longitude, geoformatdata.displayDecimalSeparator)
 +
 
 +
local geohack_link = string.format(geoformatdata.geohack_link, fullpagenamee(), table.concat(params,"_"))
 +
if self.name then
 +
geohack_link = geohack_link .. "&title=" .. mw.uri.encode(self.name)
 +
end
 +
 
 +
local pretty_hint = string.format(geoformatdata.geohack_hint, prettyLatitude, prettyLongitude)
 +
local degree_hint = string.format(geoformatdata.geohack_hint, degreeLatitude, degreeLongitude)
 +
local separator = mw.text.nowiki(geoformatdata.coordinatesSeparator)
 +
 
 +
local node = false
 +
local result = mw.html.create():wikitext("[", geohack_link, " ")
 +
node = result:tag("span"):attr("class", "geo-default")
 +
:tag("span"):attr("class", "geo-dms"):attr("title", mw.text.nowiki(pretty_hint))
 +
node:tag("span"):attr("class", "latitude"):wikitext(mw.text.nowiki(prettyLatitude))
 +
node:wikitext(separator)
 +
node:tag("span"):attr("class", "longitude"):wikitext(mw.text.nowiki(prettyLongitude))
 +
result:tag("span"):attr("class", "geo-multi-punct"):wikitext("/")
 +
node = result:tag("span"):attr("class", "geo-nondefault")
 +
:tag("span"):attr("class", "geo-dms"):attr("title", mw.text.nowiki(degree_hint))
 +
node:tag("span"):attr("class", "latitude"):wikitext(mw.text.nowiki(degreeLatitude))
 +
node:wikitext(separator)
 +
node:tag("span"):attr("class", "longitude"):wikitext(mw.text.nowiki(degreeLongitude))
 +
result:wikitext("]")
 +
 +
return tostring(result)
 +
end
 +
 
 +
function result:display(inlinePrefix)
 +
local text = self:format()
 +
 
 +
if not self.top and not self.inline then
 +
return text
 +
end
 +
 
 +
local result = mw.html.create()
 +
 
 +
if self.top then
 +
local indicator = mw.html.create("span")
 +
:attr("id", "coordinates")
 +
:attr("class", "coordinates plainlinks")
 +
:wikitext(geoformatdata.topPrefix, text)
 +
result:wikitext(mw.getCurrentFrame():extensionTag{name = 'indicator', content = tostring(indicator), args = { name='coordinates' } } or "")
 +
end
 +
 
 +
if self.inline then
 +
result:wikitext(inlinePrefix or "")
 +
:tag("span")
 +
:attr("class", self.top and "coordinates inline inline-and-top plainlinks" or "coordinates inline plainlinks")
 +
:wikitext(text)
 +
end
 +
 
 +
return tostring(result)
 +
end
 +
 
 +
function result:extensionGeoData()
 +
local params = {}
 
   
 
   
        local title = mw.title.getCurrentTitle()
+
local title = mw.title.getCurrentTitle()
        if self.top and not title.isTalkPage and (title.subpageText ~= geoformatdata.documentationSubpage) then
+
if self.top and not title.isTalkPage and (title.subpageText ~= geoformatdata.documentationSubpage) then
            table.insert(params, "primary")
+
table.insert(params, "primary")
        end
+
end
 +
 
 +
if self.latitude >= 0 then
 +
table.insert(params, string.format("%f", self.latitude))
 +
table.insert(params, "N")
 +
else
 +
table.insert(params, string.format("%f", -self.latitude))
 +
table.insert(params, "S")
 +
end
  
        if self.latitude >= 0 then
+
if mode == "W" then
            table.insert(params, string.format("%f", self.latitude))
+
if self.longitude > 0 then
            table.insert(params, "N")
+
table.insert(params, string.format("%f", 360-self.longitude))
        else
+
else
            table.insert(params, string.format("%f", -self.latitude))
+
table.insert(params, string.format("%f", -self.longitude))
            table.insert(params, "S")
+
end
        end
+
table.insert(params, "W")
 +
elseif mode == "E" then
 +
if self.longitude >= 0 then
 +
table.insert(params, string.format("%f", self.longitude))
 +
else
 +
table.insert(params, string.format("%f", 360+self.longitude))
 +
end
 +
table.insert(params, "E")
 +
elseif self.longitude >= 0 then
 +
table.insert(params, string.format("%f", self.longitude))
 +
table.insert(params, "E")
 +
else
 +
table.insert(params, string.format("%f", -self.longitude))
 +
table.insert(params, "W")
 +
end
  
        if mode == "W" then
+
if self.params then
            if self.longitude > 0 then
+
table.insert(params, self.params)
                table.insert(params, string.format("%f", 360-self.longitude))
+
end
            else
 
                table.insert(params, string.format("%f", -self.longitude))
 
            end
 
            table.insert(params, "W")
 
        elseif mode == "E" then
 
            if self.longitude >= 0 then
 
                table.insert(params, string.format("%f", self.longitude))
 
            else
 
                table.insert(params, string.format("%f", 360+self.longitude))
 
            end
 
            table.insert(params, "E")
 
        elseif self.longitude >= 0 then
 
            table.insert(params, string.format("%f", self.longitude))
 
            table.insert(params, "E")
 
        else
 
            table.insert(params, string.format("%f", -self.longitude))
 
            table.insert(params, "W")
 
        end
 
  
        if self.params then
+
if self.name then
            table.insert(params, self.params)
+
params.name = self.name
        end
+
end
 +
 +
-- https://bugzilla.wikimedia.org/show_bug.cgi?id=50863 RESOLVED
 +
return mw.getCurrentFrame():callParserFunction("#coordinates", params) or ""
 +
end
  
        if self.name then
+
function result:geohack(pagename)
            params.name = self.name
+
local result = {}
        end
+
table.insert(result, "//tools.wmflabs.org/geohack/geohack.php?language=pl")
 +
if pagename then
 +
table.insert(result, "&pagename=")
 +
table.insert(result, pagename)
 +
end
 +
 +
table.insert(result, "&params=")
 +
if self.latitude < 0 then
 +
table.insert(result, tostring(-self.latitude))
 +
table.insert(result, "_S")
 +
else
 +
table.insert(result, tostring(self.latitude))
 +
table.insert(result, "_N")
 +
end
 +
 +
table.insert(result, "_")
 +
if self.longitude < 0 then
 +
table.insert(result, tostring(-self.longitude))
 +
table.insert(result, "_W")
 +
else
 +
table.insert(result, tostring(self.longitude))
 +
table.insert(result, "_E")
 +
end
 +
 +
if self.params then
 +
table.insert(result, "_")
 +
table.insert(result, self.params)
 +
end
  
        --  https://bugzilla.wikimedia.org/show_bug.cgi?id=50863 RESOLVED
+
if self.name then
        return frame:callParserFunction("#coordinates", params) or ""
+
table.insert(result, "&title=")
    end
+
table.insert(result, mw.uri.encode(self.name))
 +
end
 +
 +
return table.concat(result)
 +
end
  
    return result;
+
return result;
 
end
 
end
  
 
local function showError(message, args)
 
local function showError(message, args)
    if not message then
+
if not message then
        return geoformatdata.errorCategory
+
return geoformatdata.errorCategory
    end
+
end
   
+
 
    local result = {}
+
local result = {}
    table.insert(result, "<span style=\"color:red\">")
+
table.insert(result, "<span style=\"color:red\">")
    assert(type(message) == "string", "Expected string message")
+
assert(type(message) == "string", "Expected string message")
    table.insert(result, message)
+
table.insert(result, message)
    local i = 1
+
local i = 1
    while args[i] do
+
while args[i] do
        if i == 1 then
+
if i == 1 then
            table.insert(result, ": {")
+
table.insert(result, ": {")
        else
+
else
            table.insert(result, "&#x7C;")
+
table.insert(result, "&#x7C;")
        end
+
end
       
+
        table.insert(result, args[i])
+
table.insert(result, args[i])
        i = i + 1
+
i = i + 1
    end
+
end
    if i > 1 then
+
if i > 1 then
        table.insert(result, "}")
+
table.insert(result, "}")
    end
+
end
   
+
 
    table.insert(result, "</span>")
+
table.insert(result, "</span>")
   
+
 
    if mw.title.getCurrentTitle().namespace == 0 then
+
if mw.title.getCurrentTitle().namespace == 0 then
        table.insert(result, geoformatdata.errorCategory)
+
table.insert(result, geoformatdata.errorCategory)
    end
+
end
   
+
 
    return table.concat(result, "")
+
return table.concat(result, "")
 
end
 
end
  
 
local function parse(frame, link)
 
local function parse(frame, link)
    local coordinates = create()
+
local coordinates = create()
    local args = frame.args
+
local args = frame.args
    local status, errorMessage = coordinates:parseCoordinates(args)
+
local status, errorMessage = coordinates:parseCoordinates(args)
    if not status then
+
if not status then
        return showError(errorMessage, args)
+
return showError(errorMessage, args)
    end
+
end
 +
 
 +
local status, errorMessage = coordinates:parseOptions(args)
 +
if not status then
 +
return showError(errorMessage, args)
 +
end
 +
 
 +
coordinates.link = link
 +
coordinates:normalize()
 +
return coordinates:display()..coordinates:extensionGeoData()
 +
end
 +
 
 +
local function wd(property, argGlobe)
 +
 
 +
local entity = mw.wikibase.getEntityObject() if not entity then mw.log("missing entity") return nil end -- missing entity
 +
local claims = entity.claims if not claims then mw.log("missing claims") return nil end -- missing claims
 +
 
 +
local function selectProperty(pid)
 +
local prop = claims[pid] if not prop then return false end -- missing property
 +
 
 +
-- load preferred statements
 +
local result = {}
 +
for _, v in ipairs(prop) do
 +
if v.rank == "preferred" then
 +
table.insert(result, v)
 +
end
 +
end
 +
 
 +
if #result ~= 0 then return true, result end
 +
 
 +
for _, v in  ipairs(prop) do
 +
if v.rank == "normal" then
 +
table.insert(result, v)
 +
end
 +
end
 +
 
 +
if #result ~= 0 then return true, result end
 +
 
 +
mw.log("empty property table")
 +
return false -- empty property table
 +
end
 +
 
 +
local function selectValue(prop, expectedType)
 +
if not prop then return false end
 +
if prop.type ~= "statement" then return false end
 +
local snak = prop.mainsnak
 +
if not snak or snak.snaktype ~= "value" then return false end
 +
local datavalue = snak.datavalue
 +
if not datavalue or datavalue.type ~= expectedType then return false end
 +
local value = datavalue.value
 +
if not value then return false end
 +
return true, value
 +
end
 +
 
 +
function selectGlobe(globe)
 +
local globes = {
 +
["http://www.wikidata.org/entity/Q2"]  = { symbol="[[Plik:Geographylogo.svg|20px|alt=Ziemia|link=Ziemia]] ", link="" },
 +
["http://www.wikidata.org/entity/Q405"] = { symbol="[[Plik:Nuvola apps kmoon left.png|15px|alt=Księżyc|link=Księżyc]] ", link="globe:Moon" },
 +
["http://www.wikidata.org/entity/Q111"] = { symbol="[[Plik:Blue Mars symbol.svg|15px|alt=Mars|link=Mars]] ", link="globe:Mars" },
 +
["http://www.wikidata.org/entity/Q308"] = { symbol="[[Plik:Blue Mercury symbol.svg|12px|alt=Merkury|link=Merkury]] ", link="globe:Mercury" },
 +
["http://www.wikidata.org/entity/Q313"] = { symbol="[[Plik:Symbol venus blue.svg|12px|alt=Wenus|link=Wenus]] ", link="globe:Venus" },
 +
}
 +
 
 +
mw.log("GLOBE: "..(globe or "http://www.wikidata.org/entity/Q2"))
 +
return globes[globe or "http://www.wikidata.org/entity/Q2"]
 +
end
 +
 
 +
function selectType()
 +
local types = {
 +
unknownType = "type:city",
 +
{
 +
property = "P300",
 +
[150093] = "type:adm1st",
 +
[247073] = "type:adm2nd",
 +
[925381] = "type:adm2nd",
 +
[3504085] = "type:adm3rd",
 +
[3491915] = "type:adm3rd",
 +
[2616791] = "type:adm3rd",
 +
},
 +
{
 +
property = "P31",
 +
[515]  = "type:city",
 +
[6256] = "type:country",
 +
[5107] = "type:satellite",
 +
[165]  = "type:satellite",
 +
},
 +
}
 +
 
 +
for _, pset in ipairs(types) do
 +
local status, classes = selectProperty(pset.property)
 +
if status then
 +
for _, p in ipairs(classes) do
 +
local status2, v = selectValue(p, "wikibase-entityid")
 +
if status2 and v["entity-type"] == "item" then
 +
local result = pset[v["numeric-id"]]
 +
if result then return result end
 +
end
 +
end
 +
end
 +
end
 +
 +
return types.unknownType
 +
end
 +
 
 +
local status1, coordinates = selectProperty(property) if not status1 then return nil end
 +
local status2, autocoords = selectValue(coordinates[1], "globecoordinate") if not status2 then return nil end
 +
local globe = argGlobe == "" and { symbol="", link="" } or selectGlobe(argGlobe or autocoords.globe) or { symbol="", link=false }
 +
if not globe.link then return nil end -- not supported globe
  
    local status, errorMessage = coordinates:parseOptions(args)
+
local params = {
    if not status then
+
selectType(),
        return showError(errorMessage, args)
+
}
    end
+
if #globe.link > 0 then
 +
table.insert(params, globe.link)
 +
end
 +
 +
local result = {
 +
latitude = autocoords.latitude,
 +
longitude = autocoords.longitude,
 +
precision = autocoords.precision or 1,
 +
params = table.concat(params,"_"),
 +
globeSymbol = globe.symbol,
 +
}
  
    coordinates.link = link
+
return result
    coordinates:normalize()
 
    return coordinates:display() .. coordinates:extensionGeoData(frame)
 
 
end
 
end
  
 
function m.format(frame)
 
function m.format(frame)
    return parse(frame, false)
+
return parse(frame, false)
 
end
 
end
  
 
function m.link(frame)
 
function m.link(frame)
    return parse(frame, true)
+
return parse(frame, true)
 
end
 
end
  
 
m[geoformatdata.apiTemplateName] = function (frame)
 
m[geoformatdata.apiTemplateName] = function (frame)
    local coordinates = create()
+
local coordinates = create()
    local args = frame:getParent().args
+
local args = frame:getParent().args
    local status, errorMessage = coordinates:parseCoordinates(args)
+
local status, errorMessage = coordinates:parseCoordinates(args)
    if not status then
+
if not status then
        return showError(errorMessage, args)
+
return showError(errorMessage, args)
    end
+
end
  
    local status, errorMessage = coordinates:parseOptions(args)
+
local status, errorMessage = coordinates:parseOptions(args)
    if not status then
+
if not status then
        return showError(errorMessage, args)
+
return showError(errorMessage, args)
    end
+
end
   
+
 
    coordinates:normalize()
+
coordinates:normalize()
    return coordinates:display() .. coordinates:extensionGeoData(frame)
+
return coordinates:display()..coordinates:extensionGeoData()
 
end
 
end
  
 
m[geoformatdata.apiMicroName] = function (frame)
 
m[geoformatdata.apiMicroName] = function (frame)
    local coordinates = create()
+
local coordinates = create()
    local args = frame:getParent().args
+
local args = frame:getParent().args
    local status, errorMessage = coordinates:parseCoordinates(args)
+
local status, errorMessage = coordinates:parseCoordinates(args)
    if not status then
+
if not status then
        return showError(errorMessage, args)
+
return showError(errorMessage, args)
    end
+
end
 +
 
 +
-- the only available option
 +
coordinates.name = args[geoformatdata.argName]
 +
 
 +
-- options are implied in micro variant
 +
if coordinates.precision > 0.00027777777777777800 then
 +
coordinates.precision = 0.00027777777777777800 -- seconds
 +
elseif coordinates.precision < 0.00002777777777777780 then
 +
coordinates.precision = 0.00002777777777777780 -- seconds with one decimal digit
 +
end
  
    -- the only available option
+
if not coordinates.params then
    coordinates.name = args[geoformatdata.argName]
+
coordinates.params = "scale:5000" -- bonus
 +
end
  
    -- options are implied in micro variant
+
coordinates.inline = true
    if coordinates.precision > 0.00027777777777777800 then
+
coordinates.top   = false
        coordinates.precision = 0.00027777777777777800 -- seconds
+
coordinates.link   = true
    elseif coordinates.precision < 0.00002777777777777780 then
 
        coordinates.precision = 0.00002777777777777780 -- seconds with one decimal digit
 
    end
 
   
 
    if not coordinates.params then
 
        coordinates.params    = "scale:5000" -- bonus
 
    end
 
   
 
    coordinates.inline   = true
 
    coordinates.top       = false
 
    coordinates.link     = true
 
  
    -- simple link without geodata extension
+
-- simple link without geodata extension
    coordinates:normalize()
+
coordinates:normalize()
    return coordinates:display()
+
return coordinates:display()
 
end
 
end
  
 
m[geoformatdata.apiAutoName] = function(frame)
 
m[geoformatdata.apiAutoName] = function(frame)
   
+
local autocoords = wd("P" .. (frame.args[1] or "625"), frame.args[geoformatdata.argGlobe])
    local entity = mw.wikibase.getEntityObject() if not entity then return nil end -- missing entity
+
if not autocoords then
    local claims = entity.claims if not claims then return nil end -- missing claims
+
return
   
+
end
    local function selectProperty(pid)
+
        local prop = claims[pid] if not prop then return false end -- missing property
+
local coords = create()
+
coords:parseOptions(frame.args)
        -- load preferred statements
+
coords.latitude = autocoords.latitude
        local result = {}
+
coords.longitude = autocoords.longitude
        for _, v in ipairs(prop) do
+
coords.precision = autocoords.precision or 1
            if v.rank == "preferred" then
+
coords.params = autocoords.params
                table.insert(result, v)
+
coords:normalize()
            end
+
return coords:display(autocoords.globeSymbol)
        end
 
 
        if #result ~= 0 then return true, prop end
 
 
        for _, v in  ipairs(prop) do
 
            if v.rank == "normal" then
 
                table.insert(result, v)
 
            end
 
        end
 
 
        if #result ~= 0 then return true, prop end
 
 
        return false -- empty property table
 
    end
 
 
    local function selectValue(prop, expectedType)
 
        if not prop then return false end
 
        if prop.type ~= "statement" then return false end
 
        local snak = prop.mainsnak
 
        if not snak or snak.snaktype ~= "value" then return false end
 
        local datavalue = snak.datavalue
 
        if not datavalue or datavalue.type ~= expectedType then return false end
 
        local value = datavalue.value
 
        if not value then return false end
 
        return true, value
 
    end
 
 
    function selectGlobe(globe)
 
        local globes = {
 
            unknownGlobe = { symbol="", link=false },
 
            ["http://www.wikidata.org/entity/Q2"]  = { symbol="[[Plik:Geographylogo.svg|20px|alt=Ziemia|link=Ziemia]] ", link="" },
 
            ["http://www.wikidata.org/entity/Q405"] = { symbol="[[Plik:Nuvola apps kmoon left.png|15px|alt=Księżyc|link=Księżyc]] ", link="globe:Moon" },
 
            ["http://www.wikidata.org/entity/Q111"] = { symbol="[[Plik:Blue Mars symbol.svg|15px|alt=Mars|link=Mars]] ", link="globe:Mars" },
 
            ["http://www.wikidata.org/entity/Q308"] = { symbol="[[Plik:Blue Mercury symbol.svg|12px|alt=Merkury|link=Merkury]] ", link="globe:Mercury" },
 
            ["http://www.wikidata.org/entity/Q313"] = { symbol="[[Plik:Symbol venus blue.svg|12px|alt=Wenus|link=Wenus]] ", link="globe:Venus" },
 
        }
 
 
        return globes[globe or "http://www.wikidata.org/entity/Q2"] or globes.unknownGlobe
 
    end
 
 
    function selectType()
 
        local types = {
 
            unknownType = "type:city",
 
            [515]  = "type:city",
 
            [6256] = "type:country",
 
            [5107] = "type:satellite",
 
            [165]  = "type:satellite",
 
        }
 
 
        local status, classes = selectProperty("P31") if not status then return types.unknownType end
 
        for _, p in ipairs(classes) do
 
            local status2, v = selectValue(p, "wikibase-entityid")
 
            if status2 and v["entity-type"] == "item" then
 
                local result = types[v["numeric-id"]]
 
                if result then return result end
 
            end
 
        end
 
 
        return types.unknownType
 
    end
 
 
 
    local status1, coordinates = selectProperty("P" .. (frame.args[1] or "625")) if not status1 then return nil end
 
    local status2, autocoords = selectValue(coordinates[1], "globecoordinate") if not status2 then return nil end
 
    local globe = selectGlobe(autocoords.globe)
 
    if not globe.link then return nil end -- not supported globe
 
 
    local params = {
 
        selectType(),
 
    }
 
    if #globe.link > 0 then
 
        table.insert(params, globe.link)
 
    end
 
   
 
    local coords = create()
 
    coords:parseOptions(frame.args)
 
    coords.latitude = autocoords.latitude
 
    coords.longitude = autocoords.longitude
 
    coords.precision = autocoords.precision or 1
 
    coords.params = table.concat(params,"_")
 
 
    coords:normalize()
 
    return coords:display(globe.symbol)
 
 
end
 
end
  
 
m[geoformatdata.apiLatitude] = function (frame)
 
m[geoformatdata.apiLatitude] = function (frame)
    local coordinates = create()
+
local coordinates = create()
    local status = coordinates:parseCoordinates(frame.args)
+
local status = coordinates:parseCoordinates(frame.args)
    return status and coordinates.latitude or ""
+
return status and coordinates.latitude or ""
 
end
 
end
  
 
m[geoformatdata.apiLongitude] = function (frame)
 
m[geoformatdata.apiLongitude] = function (frame)
    local coordinates = create()
+
local coordinates = create()
    local status = coordinates:parseCoordinates(frame.args)
+
local status = coordinates:parseCoordinates(frame.args)
    return status and coordinates.longitude or ""
+
return status and coordinates.longitude or ""
 +
end
 +
 
 +
m[geoformatdata.apiMapPoint] = function(frame)
 +
local args = require('Module:Arguments').getArgs(frame, {
 +
trim = false,
 +
removeBlanks = false,
 +
})
 +
local coordinates = create()
 +
local description = args[geoformatdata.argDescription]
 +
geohack = geoformatdata.mapPointMapping[description] or args[geoformatdata.argGeohack] or geoformatdata.defArgGeohack
 +
local latitude = args[geoformatdata.argLatitude]
 +
local longitude = args[geoformatdata.argLongitude]
 +
local status, errorMessage, inlinePrefix, fromWD = false, false, "", false
 +
if latitude and longitude then
 +
status, errorMessage = coordinates:parseCoordinates({latitude,longitude,geohack})
 +
elseif args[6] then
 +
status, errorMessage = coordinates:parseCoordinates({args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],geohack})
 +
elseif args[2] then
 +
status, errorMessage = coordinates:parseCoordinates({args[2],geohack})
 +
elseif args[1] then
 +
status, errorMessage = coordinates:parseCoordinates({args[1],geohack})
 +
else
 +
local autocoords = wd("P625", false)
 +
if not autocoords then
 +
mw.log("Brak danych w WD")
 +
return
 +
end
 +
 
 +
coordinates.latitude = autocoords.latitude
 +
coordinates.longitude = autocoords.longitude
 +
coordinates.precision = autocoords.precision or 1
 +
coordinates.params = autocoords.params
 +
inlinePrefix = autocoords.globeSymbol
 +
status = true
 +
fromWD = true
 +
end
 +
 
 +
local point = {}
 +
if not status then
 +
point.error = showError(errorMessage, args)
 +
else
 +
coordinates:parseOptions(args)
 +
coordinates:normalize()
 +
point.latitude = coordinates.latitude
 +
point.longitude = coordinates.longitude
 +
point.link = coordinates:geohack(false)
 +
if args.display then
 +
coordinates.top = mw.title.getCurrentTitle().namespace == 0
 +
coordinates.inline = true
 +
if fromWD and (args.display == "#coordinates") then
 +
point.display = coordinates:display(inlinePrefix)..coordinates:extensionGeoData()
 +
else
 +
point.display = coordinates:display(inlinePrefix)
 +
end
 +
end
 +
end
 +
 
 +
point.mark = args[geoformatdata.argMark] or geoformatdata.defArgMark
 +
point.size = tonumber(args[geoformatdata.argMarkSize] or geoformatdata.defArgMarkSize)
 +
point.description = description
 +
point.position = args[geoformatdata.argDescriptionPosition]
 +
return mw.text.jsonEncode(point)..","
 
end
 
end
  
 
return m
 
return m

Aktualna wersja na dzień 14:50, 18 wrz 2016

Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:Koordynaty/opis

Błąd skryptu: Błąd Lua: Błąd wewnętrzny: Proces interpretera został zakończony z sygnałem "-129".

local m = {}

local geoformatdata = {
	supportedFormats = {
		{ prec = "10st", precision = 10.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
		{ prec = "st",   precision =  1.00000000000000000000, dms = false, secondsFormat = nil,      format = "%0.0f%s" },
		{ prec = "1",    precision =  0.10000000000000000000, dms = false, secondsFormat = nil,      format = "%0.1f%s" },
		{ prec = "min",  precision =  0.01666666666666670000, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s" },
		{ prec = "2",    precision =  0.01000000000000000000, dms = false, secondsFormat = nil,      format = "%0.2f%s" },
		{ prec = "3",    precision =  0.00100000000000000000, dms = false, secondsFormat = nil,      format = "%0.3f%s" },
		{ prec = "sek",  precision =  0.00027777777777777800, dms = true,  secondsFormat = "%02.0f", format = "%0.0f%s%02.0f%s%02.0f%s" },
		{ prec = "4",    precision =  0.00010000000000000000, dms = false, secondsFormat = nil,      format = "%0.4f%s" },
		{ prec = "sek+", precision =  0.00002777777777777780, dms = true,  secondsFormat = "%04.1f", format = "%0.0f%s%02.0f%s%04.1f%s" },
		{ prec = "5",    precision =  0.00001000000000000000, dms = false, secondsFormat = nil,      format = "%0.5f%s" },
		{ prec = "sek2", precision =  0.00000277777777777778, dms = true,  secondsFormat = "%05.2f", format = "%0.0f%s%02.0f%s%05.2f%s" },
		{ prec = "6",    precision =  0.00000100000000000000, dms = false, secondsFormat = nil,      format = "%0.6f%s" },
		{ prec = "sek3", precision =  0.00000027777777777778, dms = true,  secondsFormat = "%06.3f", format = "%0.0f%s%02.0f%s%06.3f%s" },
		{ prec = "7",    precision =  0.00000010000000000000, dms = false, secondsFormat = nil,      format = "%0.7f%s" },
		{ prec = "sek4", precision =  0.00000002777777777778, dms = true,  secondsFormat = "%07.4f", format = "%0.0f%s%02.0f%s%07.4f%s" },
	},

	displayGlobes = {
		earth     = "EW",
		moon      = "EW",
		mercury   = "W",
		mars      = "W",
		phobos    = "W",
		deimos    = "W",
		ganymede  = "W",
		callisto  = "W",
		io        = "W",
		europa    = "W",
		mimas     = "W",
		enceladus = "W",
		tethys    = "W",
		dione     = "W",
		rhea      = "W",
		titan     = "W",
		lapetus   = "W",
		phoebe    = "W",
		venus     = "E",
		ceres     = "E",
		vesta     = "E",
		miranda   = "E",
		ariel     = "E",
		umbriel   = "E",
		titania   = "E",
		oberon    = "E",
		triton    = "E",
		pluto     = "E",
	},

	latitudeLinkMarkers   = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
	longitudeLinkMarkers  = { degree="_", minute="_", second="_", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },
	latitudeGlobeMarkers  = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="N", negativePrefix="", negativeSuffix="S", },
	longitudeGlobeMarkers = { degree="°", minute="′", second="″", positivePrefix="", positiveSuffix="E", negativePrefix="", negativeSuffix="W", },

	displayDecimalSeparator = ",",
	coordinatesSeparator = "\194\160",
	topPrefix = "Na mapach: ",
	documentationSubpage = "opis",

	geohack_link = "//tools.wmflabs.org/geohack/geohack.php?language=pl&pagename=%s&params=%s",
	geohack_hint = "Mapy, zdjęcia satelitarne i inne informacje dotyczące miejsca o współrzędnych geograficznych %s %s",

	-- template API data
	apiTemplateName = "szablon",
	apiMicroName = "mikro",
	apiAutoName = "auto",
	apiLatitude = "szerokość",
	apiLongitude = "długość",
	apiMapPoint = "punkt",

	argLocation = "umieść",
	valLocationTop = "na górze",
	valLocationInline = "w tekście",
	valLocationTopAndInline = "w tekście i na górze",

	argPrecision = "dokładność",
	valPrecisionAutoDecimal = "dziesiętnie",
	valPrecisionAutoDMS = "kątowo",

	argLink = "linkuj",
	valLinkYes = "tak",
	valLinkNo = "nie",

	argName = "nazwa",
	
	argGlobe = "glob", -- for 'auto'

	-- apiMapPoint
	argLatitude = "szerokość",
	argLongitude = "długość",
	argMark = "znak",
	argMarkSize = "rozmiar znaku",
	argDescription = "opis",
	argGeohack = "opcje geohack",
	argDescriptionPosition = "pozycja",
	
	defArgMark = "Red pog.svg",
	defArgMarkSize = 6,
	defArgGeohack = "type:city",

	mapPointMapping = {
		["Mars"] = "globe:Mars",
		["Księżyc"] = "globe:Moon",
		["Wenus"] = "globe:Venus",
		["Merkury"] = "globe:Mercury",
	},

	-- categories
	errorCategory = "[[Kategoria:Strony z błędami w parametrach szablonów współrzędnych geograficznych]]",

	-- error messages
	errorTooManyPositionalArguments = "Za dużo parametrów", 
	errorExpectedIntegerDegree = "Oczekiwana liczba stopni bez kropki dziesiętnej jeśli podawane są minuty (%s°%s')",
	errorInvalidMinutes = "Wartość minut jest nieprawidłowa (%s°%s')",
	errorExpectedIntegerMinutes = "Oczekiwana liczba minut bez kropki dziesiętnej jeśli podawane są sekundy (%s°%s'%s”)",
	errorInvalidSeconds = "Wartość sekund jest nieprawidłowa (%s°%s'%s”)",
	errorInvalidPositionalArguments = "Nieprawidłowe parametry",
	errorExpectedNonNegativeLatitude = "Oczekiwana nieujemna wartość szerokości geograficznej: %f",
	errorLatitudeOutOfRange = "Przekroczony zakres szerokości geograficznej (%f)",
	errorExpectedNonNegativeLongitude = "Oczekiwana nieujemna wartość długości geograficznej: %f",
	errorLongitudeOutOfRange = "Przekroczony zakres długości geograficznej (%f)",
	errorUnrecognizedLinkOption = "Niedozwolona wartość parametru ''link'': %s",
	errorUnrecognizedLocationOption = "Niedozwolona wartość parametru ''umieść'': %s",
}

local function create()

	-- initialize default data
	local result = {
		latitude  = 0,
		longitude = 0,
		precision = 1,
		params    = nil,
		inline    = false,
		top       = false,
		link      = true,
	}

	function result:parseCoordinates(args)

		local function isInt(s)
			-- up to 3 digits is enough for coordinates
			return s:match"^-?%d%d?%d?$"
		end

		local lang = mw.getContentLanguage()

		local function parseTypes()
			local types = {}
			for i = 1, 9 do
				local arg = mw.text.trim(args[i] or "")
				if #arg==0 then
					table.insert(types, "_")
				elseif arg == "N" or arg=="E" or arg=="S" or arg=="W" then
					table.insert(types, arg)
				elseif lang:parseFormattedNumber(arg) then
					local scientific = arg:match"[eE]"
					table.insert(types, scientific and "X" or "#")
				else
					table.insert(types, "X")
				end
			end
 
			return table.concat(types, "")
		end
 
		local function calculateDecimalPrecision(s)
			local s1 = string.gsub(s,"%d","0")
			local s2 = string.gsub(s1,"^-","0")
			local s3 = string.gsub(s2,"0$","1")
			local result = lang:parseFormattedNumber(s3)
			return result > 0 and result or 1.0
		end

		local function selectAutoPrecision(p1, p2)
			local dms = nil
			if (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDecimal)  then
				dms = false
			elseif not args[geoformatdata.argPrecision] or (args[geoformatdata.argPrecision] == geoformatdata.valPrecisionAutoDMS) then
				dms = true
			else
				-- precision is selected explicit in the parameter
				return
			end

			-- select automatic precision
			local precision = p1 < p2 and p1 or p2
 
			-- find best DMS or decimal precision
			if precision < 1 then
				local eps = precision / 1024
				for i,v in ipairs(geoformatdata.supportedFormats) do
					if (v.dms == dms) and ((v.precision - precision) < eps) then
						precision = v.precision
						break
					end
				end
			end

			self.precision = precision
		end
		
		local function parseAngle(index, extra)
 
			local degree = mw.text.trim(args[index])
			local result = lang:parseFormattedNumber(degree)
			if extra == 0 then
				return true, result, calculateDecimalPrecision(degree)
			end
 
			local minutes = mw.text.trim(args[index+1])
			if not isInt(degree) then
				return false, string.format(geoformatdata.errorExpectedIntegerDegree, degree, minutes)
			end
 
			local precision = isInt(minutes) and 0.01666666666666670000 or 0.00027777777777777800
 
			local value = lang:parseFormattedNumber(minutes)
			if value < 0 or value >= 60 then
				return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
			end
 
			if result < 0 then
				result = result * 60 - value
			else
				result = result * 60 + value
			end
 
			if extra == 1 then
				return true, result / 60, precision
			end
 
			local seconds = mw.text.trim(args[index+2])
			if not isInt(minutes) then
				return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
			end
 
			precision = 0.00027777777777777800 * calculateDecimalPrecision(seconds)
			local value = lang:parseFormattedNumber(seconds)
			if value < 0 or value >= 60 then
				return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
			end
 
			if result < 0 then
				result = result * 60 - value
			else
				result = result * 60 + value
			end
 
			return true, result / 3600, precision
		end
		
		local function analyzeAngle(degree, minutes, seconds)
			local result = lang:parseFormattedNumber(degree)
			if not result then
				return false, geoformatdata.errorInvalidPositionalArguments
			end
 
			if not string.match(degree, "^%d+$") then
				if (#minutes > 0) or (#seconds > 0) then
					-- expected empty minutes and empty seconds if float degree is given
					return false, geoformatdata.errorInvalidPositionalArguments
				end
 
				return true, result, calculateDecimalPrecision(degree)
			end
 
			if #minutes == 0 then
				if #seconds > 0 then
					-- expected empty seconds if minute is not given
					return false, geoformatdata.errorInvalidPositionalArguments
				end
			
				return true, result, calculateDecimalPrecision(degree)
			end
 
			local minute = lang:parseFormattedNumber(minutes)
			if not minute or (minute >= 60) then
				return false, string.format(geoformatdata.errorInvalidMinutes, degree, minutes)
			end
 
			result = result * 60 + minute
			if not string.match(minutes, "^%d+$") then
				if #seconds > 0 then
					return false, string.format(geoformatdata.errorExpectedIntegerMinutes, degree, minutes, seconds)
				end

				return true, result/60, 0.00027777777777777800
			end
 
			if #seconds == 0 then
				return true, result/60, 0.01666666666666670000
			end
 
			local second = lang:parseFormattedNumber(seconds)
			if not second or (second >= 60) then
				return false, string.format(geoformatdata.errorInvalidSeconds, degree, minutes, seconds)
			end

			result = result*60 + second
			return true, result/3600, calculateDecimalPrecision(seconds)*0.00027777777777777800
		end
 
		assert(args, "Missing template arguments")
		if not args[1] then
			-- display nothing if no positional arguments are provided
			return false, nil
		end
 
		if args[10] then
			return false, geoformatdata.errorTooManyPositionalArguments
		end

		local types = parseTypes()

		local function parseSimpleText()
			local arg = mw.text.trim(args[1])
			if types == "XX_______" then
				self.params = mw.text.trim(args[2])
			end

			local d1, m1, s1, h1, d2, m2, s2, h2 = mw.ustring.match(arg, "^([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([NSEW])[,;]?%s+([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([EWNS])$")
			if d1 then
				if (((h1 == "N") or (h1 == "S")) and ((h2 == "N") or (h2 == "S"))) or (((h1 == "E") or (h1 == "W")) and ((h2 == "E") or (h2 == "W"))) then
					return geoformatdata.errorInvalidPositionalArguments
				end

				local status1, v1, p1 = analyzeAngle(d1, m1, s1)
				if not status1 then
					return v1
				end

				local status2, v2, p2 = analyzeAngle(d2, m2, s2)
				if not status2 then
					return v2
				end

				if (h1 == "S") or (h1 == "W") then
					v1 = -v1;
				end
				if (h2 == "S") or (h2 == "W") then
					v2 = -v2;
				end

				self.latitude  = ((h1 == "N") or (h1 == "S")) and v1 or v2
				self.longitude = ((h1 == "E") or (h1 == "W")) and v1 or v2
				selectAutoPrecision(p1, p2)
				return nil
			end

			local lat, lon = string.match(arg, "^(-?[0-9%.,]+)%s+(-?[0-9%.,]+)$")
			if lat then
				local latitude = lang:parseFormattedNumber(lat)
				local longitude = lang:parseFormattedNumber(lon)
				if latitude and longitude then
					self.latitude = latitude
					self.longitude = longitude
					selectAutoPrecision(calculateDecimalPrecision(lat), calculateDecimalPrecision(lon))
					return nil
				end
			end

			return geoformatdata.errorInvalidPositionalArguments
		end

		if (types == "X________") or (types == "XX_______") then
			local errorMessage = parseSimpleText()
			if errorMessage then
				return false, errorMessage
			end
		else
			local mapping = mw.loadData("Module:Koordynaty/parserData")[types]
			if not mapping then
				return false, geoformatdata.errorInvalidPositionalArguments
			end

			if mapping[7] ~= 0 then
				self.params = mw.text.trim(args[mapping[7]])
			end

			local status1, latitude, latPrecision = parseAngle(mapping[1], mapping[2])
			if not status1 then
				return false, latitude
			end

			if mapping[3] ~= 0 then
				assert(mapping[3] == 1 or mapping[3] == -1, "Invalid adjust mode: " .. mapping[3]);
				if latitude < 0 then
					return false, string.format(geoformatdata.errorExpectedNonNegativeLatitude, latitude)
				end
				latitude = mapping[3] * latitude
			end

			local status2, longitude, lonPrecision = parseAngle(mapping[4], mapping[5])
			if not status2 then
				return false, longitude
			end

			if mapping[6] ~= 0 then
				assert(mapping[6] == 1 or mapping[6] == -1, "Invalid adjust mode: " .. mapping[6]);
				if longitude < 0 then
					return false, string.format(geoformatdata.errorExpectedNonNegativeLongitude, longitude)
				end
				longitude = mapping[6] * longitude
			end

			self.latitude  = latitude
			self.longitude = longitude
			selectAutoPrecision(latPrecision, lonPrecision)
		end

		if self.latitude < -90 or self.latitude > 90 then
			return false, string.format(geoformatdata.errorLatitudeOutOfRange, self.latitude)
		end
 
		if self.longitude < -360 or self.longitude > 360 then
			return false, string.format(geoformatdata.errorLongitudeOutOfRange, self.longitude)
		end
		
		return true, nil
	end

	function result:normalize()
		assert(self,"Did you use '.' instead of ':' while calling the function?")
		local mode = false
		if self.params then
			for i, v in ipairs(mw.text.split( self.params, '_', true )) do
				if mode then
					-- more than one globe, display as given
					return
				end
				
				local globe = string.match(v, "^globe:(%a+)$")
				if globe then
					mode = geoformatdata.displayGlobes[string.lower(globe)]
					if not mode then
						-- unrecognized display as given
						return
					end
				end
			end
		end

		if mode == "?" then
			-- unrecognized left as given
		elseif mode == "W" then
			if self.longitude > 0 then
				self.longitude = self.longitude - 360
			end
		elseif mode == "E" then
			if self.longitude < 0 then
				self.longitude = self.longitude + 360
			end
		elseif self.longitude < -180 then
			self.longitude = self.longitude + 360
		elseif self.longitude > 180 then
			self.longitude = self.longitude - 360
		end
	end

	function result:parseOptions(args)

		local function adjustPrecision(dms)
			if not self.precision or (self.precision >= 1) then
				return
			end

			local eps = self.precision / 1024
			for i, v in ipairs(geoformatdata.supportedFormats) do
				if (v.dms == dms) and ((v.precision - self.precision) < eps) then
					self.precision = v.precision
					break
				end
			end
		end
		
		local precision = args[geoformatdata.argPrecision]
		if precision then
			if precision == geoformatdata.valPrecisionAutoDMS then
				adjustPrecision(true)
			elseif precision == geoformatdata.valPrecisionAutoDecimal then
				adjustPrecision(false)
			else
				self.precision = precision
			end
		end

		self.name = args[geoformatdata.argName]

		local link = args[geoformatdata.argLink]
		if link == geoformatdata.valLinkYes then
			self.link = true
		elseif link == geoformatdata.valLinkNo then
			self.link = false
		elseif link then
			return false, string.format(geoformatdata.errorUnrecognizedLinkOption, link)
		else -- default is yes
			self.link = true
		end

		local location = args[geoformatdata.argLocation]
		if location == geoformatdata.valLocationTop then
			self.top = true
			self.inline = false
		elseif location == geoformatdata.valLocationInline then
			self.top = false
			self.inline = true
		elseif location == geoformatdata.valLocationTopAndInline then
			self.top = true
			self.inline = true
		elseif location then
			return false, string.format(geoformatdata.errorUnrecognizedLocationOption, location)
		elseif mw.title.getCurrentTitle().isTalkPage then
			-- an exception for talk pages
			self.top = false
			self.inline = true
		else -- default if not given
			self.top = true
			self.inline = false
		end

		return true, nil
	end
 
	function result:format()
		
		local function selectFormat(precision)
			local supportedFormats = geoformatdata.supportedFormats
			local precisionType = type(precision)
			if precisionType == "string" then
				-- find wikipedia template precision
				for i, v in ipairs(supportedFormats) do
					if (precision == v.prec) then
						return true, v
					end
				end
			elseif precisionType == "number" then
				-- find wikidata precision
				for i, v in ipairs(supportedFormats) do
					local prec = v.precision
					local eps = prec / 64
					local minPrec = prec - eps
					local maxPrec = prec + eps
					if (minPrec < precision) and (precision < maxPrec) then
						return true, v
					end
				end
			end

			-- use the last one with highest precision
			return false, supportedFormats[#supportedFormats]
		end

		local function formatAngle(value, format, markers, decimalSeparator)
			assert(type(value) == "number")
			local prefix = value < 0 and markers.negativePrefix or markers.positivePrefix
			local suffix = value < 0 and markers.negativeSuffix or markers.positiveSuffix

			value = math.abs(value)

			local result = nil

			if not format.dms then
				-- format decimal value
				if format.precision > 1 then
					-- round the value
					value = math.floor(value / format.precision) * format.precision
				end

				result = string.format(format.format, value, markers.degree)
			else
				-- format dms value
				local angle   = math.floor(value)
				local minutes = math.floor((value - angle) * 60)
				local seconds = tonumber(string.format(format.secondsFormat, (value - angle) * 3600 - minutes * 60))

				-- fix rounded seconds
				if seconds == 60 then
					minutes = minutes + 1
					seconds = 0
					if minutes == 60 then
						angle = angle + 1
						minutes = 0
					end
				end

				if format.precision > 0.01 then
					-- round the value
					if seconds >= 30 then
						minutes = minutes + 1
					end
					seconds = 0
					if minutes == 60 then
						angle = angle + 1
						minutes = 0
					end
				end

				result = string.format(format.format, angle, markers.degree, minutes, markers.minute, seconds, markers.second)
			end

			if decimalSeparator then
				result = string.gsub(result, "%.", decimalSeparator)
			end

			return prefix .. result .. suffix
		end

		local function formatDegree(value, decimalSeparator)
			local result = string.format("%f", value)

			if decimalSeparator then
				result = string.gsub(result, "%.", decimalSeparator)
			end
		
			return result
		end

		local function fullpagenamee()
			local title = mw.title.getCurrentTitle()
			return title.namespace == 0
				and title:partialUrl()
				or  title.nsText .. ":" .. title:partialUrl()
		end

		local status, format = selectFormat(self.precision)
		assert(format)

		local prettyLatitude  = formatAngle(self.latitude,  format, geoformatdata.latitudeGlobeMarkers,  geoformatdata.displayDecimalSeparator)
		local prettyLongitude = formatAngle(self.longitude, format, geoformatdata.longitudeGlobeMarkers, geoformatdata.displayDecimalSeparator)

		if not self.link then
			return mw.text.nowiki(prettyLatitude .. geoformatdata.coordinatesSeparator .. prettyLongitude)
		end

		local params = {
			formatAngle(self.latitude,  format, geoformatdata.latitudeLinkMarkers),
			formatAngle(self.longitude, format, geoformatdata.longitudeLinkMarkers),
		}

		if self.params then
			table.insert(params, self.params)
		end

		local degreeLatitude  = formatDegree(self.latitude,  geoformatdata.displayDecimalSeparator)
		local degreeLongitude = formatDegree(self.longitude, geoformatdata.displayDecimalSeparator)

		local geohack_link = string.format(geoformatdata.geohack_link, fullpagenamee(), table.concat(params,"_"))
		if self.name then
			geohack_link = geohack_link .. "&title=" .. mw.uri.encode(self.name)
		end

		local pretty_hint = string.format(geoformatdata.geohack_hint, prettyLatitude, prettyLongitude)
		local degree_hint = string.format(geoformatdata.geohack_hint, degreeLatitude, degreeLongitude)
		local separator = mw.text.nowiki(geoformatdata.coordinatesSeparator)

		local node = false
		local result = mw.html.create():wikitext("[", geohack_link, " ")
		node = result:tag("span"):attr("class", "geo-default")
			:tag("span"):attr("class", "geo-dms"):attr("title", mw.text.nowiki(pretty_hint))
		node:tag("span"):attr("class", "latitude"):wikitext(mw.text.nowiki(prettyLatitude))
		node:wikitext(separator)
		node:tag("span"):attr("class", "longitude"):wikitext(mw.text.nowiki(prettyLongitude))
		result:tag("span"):attr("class", "geo-multi-punct"):wikitext("/")
		node = result:tag("span"):attr("class", "geo-nondefault")
			:tag("span"):attr("class", "geo-dms"):attr("title", mw.text.nowiki(degree_hint))
		node:tag("span"):attr("class", "latitude"):wikitext(mw.text.nowiki(degreeLatitude))
		node:wikitext(separator)
		node:tag("span"):attr("class", "longitude"):wikitext(mw.text.nowiki(degreeLongitude))
		result:wikitext("]")
		
		return tostring(result)
	end

	function result:display(inlinePrefix)
		local text = self:format()

		if not self.top and not self.inline then
			return text
		end

		local result = mw.html.create()

		if self.top then
			local indicator = mw.html.create("span")
				:attr("id", "coordinates")
				:attr("class", "coordinates plainlinks")
				:wikitext(geoformatdata.topPrefix, text)
			result:wikitext(mw.getCurrentFrame():extensionTag{name = 'indicator', content = tostring(indicator), args = { name='coordinates' } } or "")
		end

		if self.inline then
			result:wikitext(inlinePrefix or "")
				:tag("span")
					:attr("class", self.top and "coordinates inline inline-and-top plainlinks" or "coordinates inline plainlinks")
					:wikitext(text)
		end

		return tostring(result)
	end

	function result:extensionGeoData()
		local params = {}
 
		local title = mw.title.getCurrentTitle()
		if self.top and not title.isTalkPage and (title.subpageText ~= geoformatdata.documentationSubpage) then
			table.insert(params, "primary")
		end

		if self.latitude >= 0 then
			table.insert(params, string.format("%f", self.latitude))
			table.insert(params, "N")
		else
			table.insert(params, string.format("%f", -self.latitude))
			table.insert(params, "S")
		end

		if mode == "W" then
			if self.longitude > 0 then
				table.insert(params, string.format("%f", 360-self.longitude))
			else
				table.insert(params, string.format("%f", -self.longitude))
			end
			table.insert(params, "W")
		elseif mode == "E" then
			if self.longitude >= 0 then
				table.insert(params, string.format("%f", self.longitude))
			else
				table.insert(params, string.format("%f", 360+self.longitude))
			end
			table.insert(params, "E")
		elseif self.longitude >= 0 then
			table.insert(params, string.format("%f", self.longitude))
			table.insert(params, "E")
		else
			table.insert(params, string.format("%f", -self.longitude))
			table.insert(params, "W")
		end

		if self.params then
			table.insert(params, self.params)
		end

		if self.name then
			params.name = self.name
		end
		
		-- https://bugzilla.wikimedia.org/show_bug.cgi?id=50863 RESOLVED
		return mw.getCurrentFrame():callParserFunction("#coordinates", params) or ""
	end

	function result:geohack(pagename)
		local result = {}
		table.insert(result, "//tools.wmflabs.org/geohack/geohack.php?language=pl")
		if pagename then
			table.insert(result, "&pagename=")
			table.insert(result, pagename)
		end
		
		table.insert(result, "&params=")
		if self.latitude < 0 then
			table.insert(result, tostring(-self.latitude))
			table.insert(result, "_S")
		else
			table.insert(result, tostring(self.latitude))
			table.insert(result, "_N")
		end
		
		table.insert(result, "_")
		if self.longitude < 0 then
			table.insert(result, tostring(-self.longitude))
			table.insert(result, "_W")
		else
			table.insert(result, tostring(self.longitude))
			table.insert(result, "_E")
		end
		
		if self.params then
			table.insert(result, "_")
			table.insert(result, self.params)
		end

		if self.name then
			table.insert(result, "&title=")
			table.insert(result, mw.uri.encode(self.name))
		end
		
		return table.concat(result)
	end

	return result;
end

local function showError(message, args)
	if not message then
		return geoformatdata.errorCategory
	end

	local result = {}
	table.insert(result, "<span style=\"color:red\">")
	assert(type(message) == "string", "Expected string message")
	table.insert(result, message)
	local i = 1
	while args[i] do
		if i == 1 then
			table.insert(result, ": {")
		else
			table.insert(result, "&#x7C;")
		end
		
		table.insert(result, args[i])
		i = i + 1
	end
	if i > 1 then
		table.insert(result, "}")
	end

	table.insert(result, "</span>")

	if mw.title.getCurrentTitle().namespace == 0 then
		table.insert(result, geoformatdata.errorCategory)
	end

	return table.concat(result, "")
end

local function parse(frame, link)
	local coordinates = create()
	local args = frame.args
	local status, errorMessage = coordinates:parseCoordinates(args)
	if not status then
		return showError(errorMessage, args)
	end

	local status, errorMessage = coordinates:parseOptions(args)
	if not status then
		return showError(errorMessage, args)
	end

	coordinates.link = link
	coordinates:normalize()
	return coordinates:display()..coordinates:extensionGeoData()
end

local function wd(property, argGlobe)

	local entity = mw.wikibase.getEntityObject() if not entity then mw.log("missing entity") return nil end -- missing entity
	local claims = entity.claims if not claims then mw.log("missing claims") return nil end -- missing claims

	local function selectProperty(pid)
		local prop = claims[pid] if not prop then return false end -- missing property

		-- load preferred statements
		local result = {}
		for _, v in ipairs(prop) do
			if v.rank == "preferred" then
				table.insert(result, v)
			end
		end

		if #result ~= 0 then return true, result end

		for _, v in  ipairs(prop) do
			if v.rank == "normal" then
				table.insert(result, v)
			end
		end

		if #result ~= 0 then return true, result end

		mw.log("empty property table")
		return false -- empty property table
	end

	local function selectValue(prop, expectedType)
		if not prop then return false end
		if prop.type ~= "statement" then return false end
		local snak = prop.mainsnak
		if not snak or snak.snaktype ~= "value" then return false end
		local datavalue = snak.datavalue
		if not datavalue or datavalue.type ~= expectedType then return false end
		local value = datavalue.value
		if not value then return false end
		return true, value
	end

	function selectGlobe(globe)
		local globes = {
			["http://www.wikidata.org/entity/Q2"]   = { symbol="[[Plik:Geographylogo.svg|20px|alt=Ziemia|link=Ziemia]] ", link="" },
			["http://www.wikidata.org/entity/Q405"] = { symbol="[[Plik:Nuvola apps kmoon left.png|15px|alt=Księżyc|link=Księżyc]] ", link="globe:Moon" },
			["http://www.wikidata.org/entity/Q111"] = { symbol="[[Plik:Blue Mars symbol.svg|15px|alt=Mars|link=Mars]] ", link="globe:Mars" },
			["http://www.wikidata.org/entity/Q308"] = { symbol="[[Plik:Blue Mercury symbol.svg|12px|alt=Merkury|link=Merkury]] ", link="globe:Mercury" },
			["http://www.wikidata.org/entity/Q313"] = { symbol="[[Plik:Symbol venus blue.svg|12px|alt=Wenus|link=Wenus]] ", link="globe:Venus" },
		}

		mw.log("GLOBE: "..(globe or "http://www.wikidata.org/entity/Q2"))
		return globes[globe or "http://www.wikidata.org/entity/Q2"]
	end

	function selectType()
		local types = {
			unknownType = "type:city",
			{
				property = "P300",
				[150093] = "type:adm1st",
				[247073] = "type:adm2nd",
				[925381] = "type:adm2nd",
				[3504085] = "type:adm3rd",
				[3491915] = "type:adm3rd",
				[2616791] = "type:adm3rd",
			},
			{
				property = "P31",
				[515]  = "type:city",
				[6256] = "type:country",
				[5107] = "type:satellite",
				[165]  = "type:satellite",
			},
		}

		for _, pset in ipairs(types) do
			local status, classes = selectProperty(pset.property)
			if status then
				for _, p in ipairs(classes) do
					local status2, v = selectValue(p, "wikibase-entityid")
					if status2 and v["entity-type"] == "item" then
						local result = pset[v["numeric-id"]]
						if result then return result end
					end
				end
			end
		end
	
		return types.unknownType
	end

	local status1, coordinates = selectProperty(property) if not status1 then return nil end
	local status2, autocoords = selectValue(coordinates[1], "globecoordinate") if not status2 then return nil end
	local globe = argGlobe == "" and { symbol="", link="" } or selectGlobe(argGlobe or autocoords.globe) or { symbol="", link=false }
	if not globe.link then return nil end -- not supported globe

	local params = {
		selectType(),
	}
	if #globe.link > 0 then
		table.insert(params, globe.link)
	end
	
	local result = {
		latitude = autocoords.latitude,
		longitude = autocoords.longitude,
		precision = autocoords.precision or 1,
		params = table.concat(params,"_"),
		globeSymbol = globe.symbol,
	}

	return result
end

function m.format(frame)
	return parse(frame, false)
end

function m.link(frame)
	return parse(frame, true)
end

m[geoformatdata.apiTemplateName] = function (frame)
	local coordinates = create()
	local args = frame:getParent().args
	local status, errorMessage = coordinates:parseCoordinates(args)
	if not status then
		return showError(errorMessage, args)
	end

	local status, errorMessage = coordinates:parseOptions(args)
	if not status then
		return showError(errorMessage, args)
	end

	coordinates:normalize()
	return coordinates:display()..coordinates:extensionGeoData()
end

m[geoformatdata.apiMicroName] = function (frame)
	local coordinates = create()
	local args = frame:getParent().args
	local status, errorMessage = coordinates:parseCoordinates(args)
	if not status then
		return showError(errorMessage, args)
	end

	-- the only available option
	coordinates.name = args[geoformatdata.argName]

	-- options are implied in micro variant
	if coordinates.precision > 0.00027777777777777800 then
		coordinates.precision = 0.00027777777777777800 -- seconds
	elseif coordinates.precision < 0.00002777777777777780 then
		coordinates.precision = 0.00002777777777777780 -- seconds with one decimal digit
	end

	if not coordinates.params then
		coordinates.params	= "scale:5000" -- bonus
	end

	coordinates.inline = true
	coordinates.top	   = false
	coordinates.link   = true

	-- simple link without geodata extension
	coordinates:normalize()
	return coordinates:display()
end

m[geoformatdata.apiAutoName] = function(frame)
	local autocoords = wd("P" .. (frame.args[1] or "625"), frame.args[geoformatdata.argGlobe])
	if not autocoords then
		return
	end
	
	local coords = create()
	coords:parseOptions(frame.args)
	coords.latitude = autocoords.latitude
	coords.longitude = autocoords.longitude
	coords.precision = autocoords.precision or 1
	coords.params = autocoords.params
	coords:normalize()
	return coords:display(autocoords.globeSymbol)
end

m[geoformatdata.apiLatitude] = function (frame)
	local coordinates = create()
	local status = coordinates:parseCoordinates(frame.args)
	return status and coordinates.latitude or ""
end

m[geoformatdata.apiLongitude] = function (frame)
	local coordinates = create()
	local status = coordinates:parseCoordinates(frame.args)
	return status and coordinates.longitude or ""
end

m[geoformatdata.apiMapPoint] = function(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		trim = false,
		removeBlanks = false,
	})
	local coordinates = create()
	local description = args[geoformatdata.argDescription]
	geohack = geoformatdata.mapPointMapping[description] or args[geoformatdata.argGeohack] or geoformatdata.defArgGeohack
	local latitude = args[geoformatdata.argLatitude]
	local longitude = args[geoformatdata.argLongitude]
	local status, errorMessage, inlinePrefix, fromWD = false, false, "", false
	if latitude and longitude then
		status, errorMessage = coordinates:parseCoordinates({latitude,longitude,geohack})
	elseif args[6] then
		status, errorMessage = coordinates:parseCoordinates({args[2],args[3],args[4],args[5],args[6],args[7],args[8],args[9],geohack})
	elseif args[2] then
		status, errorMessage = coordinates:parseCoordinates({args[2],geohack})
	elseif args[1] then
		status, errorMessage = coordinates:parseCoordinates({args[1],geohack})
	else
		local autocoords = wd("P625", false)
		if not autocoords then
			mw.log("Brak danych w WD")
			return
		end

		coordinates.latitude = autocoords.latitude
		coordinates.longitude = autocoords.longitude
		coordinates.precision = autocoords.precision or 1
		coordinates.params = autocoords.params
		inlinePrefix = autocoords.globeSymbol
		status = true
		fromWD = true
	end

	local point = {}
	if not status then
		point.error = showError(errorMessage, args)
	else
		coordinates:parseOptions(args)
		coordinates:normalize()
		point.latitude = coordinates.latitude
		point.longitude = coordinates.longitude
		point.link = coordinates:geohack(false)
		if args.display then
			coordinates.top = mw.title.getCurrentTitle().namespace == 0
			coordinates.inline = true
			if fromWD and (args.display == "#coordinates") then
				point.display = coordinates:display(inlinePrefix)..coordinates:extensionGeoData()
			else
				point.display = coordinates:display(inlinePrefix)
			end
		end
	end

	point.mark = args[geoformatdata.argMark] or geoformatdata.defArgMark
	point.size = tonumber(args[geoformatdata.argMarkSize] or geoformatdata.defArgMarkSize)
	point.description = description
	point.position = args[geoformatdata.argDescriptionPosition]
	return mw.text.jsonEncode(point)..","
end

return m