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