Moduł:koordynaty
Moduł pomocniczy do obsługi współrzędnych geograficznych.
szablon
edytuj
Funkcja nie przyjmuje żadnych parametrów bezpośrednich. Interpretuje tylko parametry przekazane do szablonu (obecnie bez implementacji). Jeśli argumenty pozycyjne przekazane do szablonu są nieprawidłowe, to wynikiem jest komunikat błędu.
parametry szablonu edytuj
Pole | Do czego służy? | Jak wypełnić? |
---|---|---|
od 1 do 9 | Parametry ze współrzędnymi geograficznymi tj. stopnie, minuty i sekundy szerokości geograficznej oraz stopnie, minuty i sekundy długości geograficznej. Ostatni opcjonalny parametr to dodatkowe parametry przekazywane w linku do geohacka. Uwaga! Koordynaty można również podać w jednym parametrze, w takim przypadku podany tekst będzie odpowiednio zinterpretowany. Akceptowane są dwie liczby interpretowane odpowiednio jako szerokość i długość geograficzna lub dwa zbiory do trzech nieujemnych liczb zakończonych znakiem N, S, W, E określających półkulę. Liczby mogą być ozdobione znakami stopni, minut i sekund lecz są one ignorowane. |
{{...|54|22|11|N|18.913|W}}
|
umieść
|
Parametr opcjonalny do pozycjonowania wyniku:
|
{{...|54|22|11|N|18.913|W|umieść=na górze}}
|
dokładność
|
Określa sposób formatowania wyniku:
|
{{...|54|22|11|N|18.913|W|dokładność=min}}
|
nazwa
|
Opcjonalna nazwa obiektu geograficznego. | {{...|54|22|11|N|18.913|W|nazwa=Trójmiasto}}
|
szablon/mikro
edytuj
Funkcja nie przyjmuje żadnych parametrów bezpośrednich. Interpretuje tylko parametry przekazane do szablonu (obecnie bez implementacji). Jeśli argumenty pozycyjne przekazane do szablonu są nieprawidłowe to wynikiem jest komunikat błędu.
parametry szablonu edytuj
Pole | Do czego służy? | Jak wypełnić? |
---|---|---|
od 1 do 9 | Parametry ze współrzędnymi geograficznymi tj. stopnie, minuty i sekundy szerokości geograficznej oraz stopnie, minuty i sekundy długości geograficznej. Ostatni opcjonalny parametr to dodatkowe parametry przekazywane w linku do geohacka. | {{...|54|22|11|N|18.913|W}}
|
nazwa
|
Opcjonalna nazwa obiektu geograficznego. | {{...|54|22|11|N|18.913|W|nazwa=Trójmiasto}}
|
szerokość
, długość
edytuj
Pomocnicze funkcje do konwersji koordynat na liczbową wartość szerokości i długości geograficznej. Głównym przeznaczeniem funkcji jest podawanie szerokości i długości geograficznej dla szablonów map w przypadku przekazywania koordynat w skompresowanej formie za pomocą jednego parametru.
W przypadku błędu funkcja zwraca pusty tekst.
parametry edytuj
Pole | Do czego służy? | Jak wypełnić? |
---|---|---|
od 1 do 9 | Parametry ze współrzędnymi geograficznymi tj. stopnie, minuty i sekundy szerokości geograficznej oraz stopnie, minuty i sekundy długości geograficznej. Ostatni opcjonalny parametr to dodatkowe parametry przekazywane w linku do geohacka. | {{...|54|22|11|N|18.913|W}}
|
przykłady edytuj
dane koordynat | {{#invoke:koordynaty|szerokość|...}}
|
{{#invoke:koordynaty|długość|...}}
|
---|---|---|
57|18|22|N|4|27|32|W
|
57.306111111111 | -4.4588888888889 |
57 18 22 N 4 27 32 W
|
57.306111111111 | -4.4588888888889 |
44.112|N|87.913|W
|
44.112 | -87.913 |
44.112N 87.913W
|
44.112 | -87.913 |
54|18|type:city
|
54 | 18 |
54 18|type:city
|
54 | 18 |
44.112|N|4|27|32|W
|
44.112 | -4.4588888888889 |
44.112°N 4°27'32W
|
44.112 | -4.4588888888889 |
bzdury|60|80
|
||
bzdury 60 80
|
||
dokładność=3|60|80
|
60 | 80 |
dokładność=3|60 80
|
60 | 80 |
dokładność=sek+|60|80
|
60 | 80 |
dokładność=sek+|60 80
|
60 | 80 |
60.000000|80
|
60 | 80 |
60.000000 80
|
60 | 80 |
54|22|00.00|18|38|00
|
54.366666666667 | 18.633333333333 |
54 22 00.00 18 38 00
|
||
57|18|22.8|N|4|27|32.2|W
|
57.306333333333 | -4.4589444444444 |
57°18'22,8″N 4°27'32,2″W
|
57.306333333333 | -4.4589444444444 |
57|18|22.812378|N|4|27|32.24569|W
|
57.306336771667 | -4.4589571361111 |
97|18|22.8|N|4|27|32.2|W
|
||
97 18 22.8 N 4 27 32.2 W
|
||
57|18|22.8|N|184|27|32.2|W
|
57.306333333333 | -184.45894444444 |
57|18|22.8|N|184|27|32.2|W
|
57.306333333333 | -184.45894444444 |
57|18|-22.8|N|4|27|32.2|W
|
||
57 18 -22.8 N 4 27 32.2 W
|
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¶ms=%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ść",
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
errorCategory = "[[Kategoria:Złe użycie szablonu]]",
-- 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, "^([0-9,.]+)[°_]?%s*([0-9,.]*)['′_]?%s*([0-9,.]*)[\"″_]?%s*([NSEW])[,;]?%s+([0-9,.]+)[°_]?%s*([0-9,.]*)['′_]?%s*([0-9,.]*)[\"″_]?%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)
-- TODO process notation in conjuction with precision
local precision = args[geoformatdata.argPrecision]
if precision and (precision ~= geoformatdata.valPrecisionAutoDecimal) and (precision ~= geoformatdata.valPrecisionAutoDMS) then
self.precision = precision
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:display(inlinePrefix)
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 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)
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 frame:callParserFunction("#coordinates", params) or ""
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, "|")
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(frame)
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(frame)
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 entity = mw.wikibase.getEntityObject() if not entity then return nil end -- missing entity
local claims = entity.claims if not claims then 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, 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
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
return m