Moduł:Koordynaty: Różnice pomiędzy wersjami
Z Czarnobyl Wiki
m (1 wersja) |
(drobne techniczne od #coordinates) |
||
| Linia 2: | Linia 2: | ||
local geoformatdata = { | 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ść", | |
| − | + | 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() | 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, "¶ms=") | ||
| + | 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 | end | ||
local function showError(message, args) | 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 | end | ||
local function parse(frame, link) | 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 | end | ||
function m.format(frame) | function m.format(frame) | ||
| − | + | return parse(frame, false) | |
end | end | ||
function m.link(frame) | function m.link(frame) | ||
| − | + | return parse(frame, true) | |
end | end | ||
m[geoformatdata.apiTemplateName] = function (frame) | 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 | end | ||
m[geoformatdata.apiMicroName] = function (frame) | 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 | end | ||
m[geoformatdata.apiAutoName] = function(frame) | 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 | end | ||
m[geoformatdata.apiLatitude] = function (frame) | m[geoformatdata.apiLatitude] = function (frame) | ||
| − | + | local coordinates = create() | |
| − | + | local status = coordinates:parseCoordinates(frame.args) | |
| − | + | return status and coordinates.latitude or "" | |
end | end | ||
m[geoformatdata.apiLongitude] = function (frame) | 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 | end | ||
return m | return m | ||
Wersja z 15:08, 11 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¶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ść",
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, "¶ms=")
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, "|")
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