Moduł:Spōłrzyndne
Dokumentacja dla tego modułu może zostać utworzona pod nazwą Moduł:Spōłrzyndne/opis
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 = { mode = "EW", Q="Q2", symbol="⨁", },
moon = { mode = "EW", Q="Q405", symbol="☾", },
mercury = { mode = "W", Q="Q308", symbol="☿", },
mars = { mode = "W", Q="Q111", symbol="♂", },
phobos = { mode = "W", Q="Q7547", },
deimos = { mode = "W", Q="Q7548", },
ganymede = { mode = "W", Q="Q3169", },
callisto = { mode = "W", Q="Q3134", },
io = { mode = "W", Q="Q3123", },
europa = { mode = "W", Q="Q3143", },
mimas = { mode = "W", Q="Q15034", },
enceladus = { mode = "W", Q="Q3303", },
tethys = { mode = "W", Q="Q15047", },
dione = { mode = "W", Q="Q15040", },
rhea = { mode = "W", Q="Q15050", },
titan = { mode = "W", Q="Q2565", },
hyperion = { mode = "?", Q="Q15037", },
iapetus = { mode = "W", Q="Q17958", },
phoebe = { mode = "W", Q="Q17975", },
venus = { mode = "E", Q="Q313", symbol="♀", },
ceres = { mode = "E", Q="Q596", symbol="⚳", },
vesta = { mode = "E", Q="Q3030", symbol="⚶", },
miranda = { mode = "E", Q="Q3352", },
ariel = { mode = "E", Q="Q3343", },
umbriel = { mode = "E", Q="Q3338", },
titania = { mode = "E", Q="Q3322", },
oberon = { mode = "E", Q="Q3332", },
triton = { mode = "E", Q="Q3359", },
pluto = { mode = "E", Q="Q339", symbol="♇", },
},
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 = "Spōłrzyndne: ",
displayGlobesDefaultSymbol = "⌘",
defaultSymbolSeparator = "\194\160",
documentationSubpage = "ôpis",
defaultWDPrecision = 0.00027777777777777800,
defaultSizePrecision = 1.0,
defaultDistanceScalingFactor = 6731000,
geohack_root = "//tools.wmflabs.org/geohack/geohack.php?language=en",
geohack_hint = "Karty, satelitarne fotki i inksze informacyje ô placu we tych spōłrzyndnych %s %s",
-- template API data
apiCoordinates = "spōłrzyndne",
apiMapPoint = "pōnkt",
apiCheckDistance = "Ôbocz dystans spōłrzyndnych",
apiDistance = "Dystans",
apiPrecisionRadius = "Prōmiyń akuratności",
argScalingFactor = "mnożnik",
argMaximumDistance = "dystans",
argErrorMessage = "kōmunikat",
wrappersCoordinates = "Muster:Spōłrzyndne",
wrappersMapPoint = "Muster:Pōnkt na karcie",
argCoordinatesCoordinates = 1,
argCoordinatesGeohack = 2,
argLocation = "wkludź",
valLocationTop = "na wiyrchu",
valLocationInline = "w tekście",
valLocationTopAndInline = "w tekście i na wiyrchu",
argPrecision = "akuratność",
valPrecisionAutoDecimal = "dziesiytnie",
valPrecisionAutoDMS = "kōntowo",
argLink = "linkuj",
valLinkYes = "ja",
valLinkNo = "niy",
valLinkGMS = "zgodliwie",
argSymbol = "symbol",
valSymbolYes = "ja",
valSymbolNo = "niy",
argName = "miano",
-- apiMapPoint
argMapPointCoordinates = 1,
argMark = "znak",
argMarkSize = "miara znaku",
argDescription = "ôpis",
argMapPointGeohack = "ôpcyje geohack",
argDescriptionPosition = "pozycyjo",
argAlt = "alt",
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 = "[[Kategoryjo:Zajty ze felerami we parametrach mustrōw spōłrzyndnych]]",
-- error messages
errorInvalidMinutes = "Wielość minut je felerno (%s°%s')",
errorExpectedIntegerMinutes = "Ôczekowano wielość minut bez kropki dziesiyntnyj eli podowane sōm sekōndy (%s°%s'%s”)",
errorInvalidSeconds = "Wielość sekōnd je felerno (%s°%s'%s”)",
errorInvalidPositionalArguments = "Felerne parametry",
errorLatitudeOutOfRange = "Przekroczōny zakres szyrokości geograficznyj (%f)",
errorLongitudeOutOfRange = "Przekroczōny zakres szyrokości geograficznyj (%f)",
errorUnrecognizedLinkOption = "Niyprzizwolōno wartość parametru ''linkuj'': %s",
errorUnrecognizedLocationOption = "Niyprzizwolōno wartość parametru ''wkludź'': %s",
errorUnrecognizedPrecisionOption = "Niedozwolona wartość parametru ''akuratność'': %s",
errorEmptySymbolOption = "Prōżny parametr ''symbol''",
errorMissingCoordinates = "Niy mo spōłrzyndnych",
}
--------------------------------------------------------------------------------
-- Coordinates class methods
--------------------------------------------------------------------------------
local CoordinatesMetatable = {}
local CoordinatesMethodtable = {}
CoordinatesMetatable.__index = CoordinatesMethodtable
function CoordinatesMethodtable:parse(coordinates, params, displayPrecision)
local lang = mw.getContentLanguage()
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 (displayPrecision == geoformatdata.valPrecisionAutoDecimal) then
dms = false
elseif not displayPrecision or (displayPrecision == 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 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
if not coordinates or (#mw.text.trim(coordinates) <= 0) then
return false, geoformatdata.errorMissingCoordinates
end
local function parseSimpleText()
local d1, m1, s1, h1, d2, m2, s2, h2 = mw.ustring.match(coordinates, "^%s*([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([NSEW])[,;]?%s+([%d,%.]+)[°_]?%s*([%d,%.]*)['′_]?%s*([%d,%.]*)[\"″”_]?%s*([EWNS])%s*$")
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(coordinates, "^%s*(-?[0-9%.,]+)%s+(-?[0-9%.,]+)%s*$")
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
local data = false
if params then
local p = mw.text.trim(params)
if #p > 0 then
self.params = p
local trace = false
for i, v in ipairs(mw.text.split(p, '_', true)) do
local globe = string.match(v, "^globe:(%a+)$")
if globe then
if data then
-- more than one globe, data undetermined
trace = "undetermined"
data = nil
break
end
globe = string.lower(globe)
data = geoformatdata.displayGlobes[globe]
if not data then
-- unrecognized data
trace = "unrecognized"
data = nil
break
else
trace = globe
end
end
end
if trace then
_ = mw.title.new("Module:Spōłrzyndne/globe:"..trace).id
end
end
end
if data and not displayPrecision then
displayPrecision = data.Q == "Q2" and geoformatdata.valPrecisionAutoDMS or geoformatdata.valPrecisionAutoDecimal
end
self.displayData = data or geoformatdata.displayGlobes.earth
local errorMessage = parseSimpleText()
if errorMessage then
return false, errorMessage
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 CoordinatesMethodtable:normalize()
assert(self,"Did you use '.' instead of ':' while calling the function?")
local mode = false
if self.displayData then
mode = self.displayData.mode
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 CoordinatesMethodtable:format()
local function selectFormat(precision)
local supportedFormats = geoformatdata.supportedFormats
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 v
end
end
-- use the last one with highest precision
return 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 format = selectFormat(self.precision)
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 linkLatitude = false
local linkLongitude = false
if self.link == "gms" then
linkLatitude = formatAngle(self.latitude, format, geoformatdata.latitudeLinkMarkers)
linkLongitude = formatAngle(self.longitude, format, geoformatdata.longitudeLinkMarkers)
end
local geohack_link = self:geohack(fullpagenamee(), linkLatitude, linkLongitude)
local degreeLatitude = formatDegree(self.latitude, geoformatdata.displayDecimalSeparator)
local degreeLongitude = formatDegree(self.longitude, geoformatdata.displayDecimalSeparator)
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 CoordinatesMethodtable:display(inlinePrefix)
local text = self:format{}
if not self.top and not self.inline then
return text
end
local function drawGlobeSymbol(displayData)
local symbol = displayData.symbol or geoformatdata.displayGlobesDefaultSymbol
if not displayData.Q then
return symbol..geoformatdata.defaultSymbolSeparator
end
local link = mw.wikibase.sitelink(displayData.Q)
if not link then
return symbol..geoformatdata.defaultSymbolSeparator
end
return "[["..link.."|"..symbol.."]]"..geoformatdata.defaultSymbolSeparator
end
if inlinePrefix == nil then
if self.symbol == false then
inlinePrefix = ""
elseif self.symbol == true then
inlinePrefix = drawGlobeSymbol(self.displayData) or ""
elseif self.symbol then
inlinePrefix = self.symbol
elseif self.displayData.Q == "Q2" then
-- !symbol & Q2
inlinePrefix = ""
else
-- !symbol & !Q2
inlinePrefix = drawGlobeSymbol(self.displayData) or ""
end
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, inlinePrefix or "", text)
result:wikitext(mw.getCurrentFrame():extensionTag{name = 'indicator', content = tostring(indicator), args = { name='coordinates' } } or "")
end
if self.inline then
result:tag("span")
:attr("class", self.top and "coordinates inline inline-and-top plainlinks" or "coordinates inline plainlinks")
:wikitext(inlinePrefix or "", text)
end
return tostring(result)
end
function CoordinatesMethodtable: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 CoordinatesMethodtable:geohack(pagename, linkLatitude, linkLongitude)
local result = {}
table.insert(result, geoformatdata.geohack_root)
if pagename then
table.insert(result, "&pagename=")
table.insert(result, pagename)
end
table.insert(result, "¶ms=")
if linkLatitude and linkLongitude then
table.insert(result, linkLatitude)
elseif 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 linkLatitude and linkLongitude then
table.insert(result, linkLongitude)
elseif 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
local function create()
-- initialize default data
local self = {
latitude = 0,
longitude = 0,
precision = 1,
params = nil,
inline = false,
top = false,
link = true,
}
setmetatable(self, CoordinatesMetatable)
return self;
end
--------------------------------------------------------------------------------
-- utilities
--------------------------------------------------------------------------------
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
--------------------------------------------------------------------------------
-- Minimalistic Wikidata support
--------------------------------------------------------------------------------
local function selectProperty(claims, 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
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
local function wd(property, argGlobe)
local entity = mw.wikibase.getEntity() if not entity then return nil end -- missing entity
local claims = entity.claims if not claims then return nil end -- missing claims
function selectGlobe(globe)
-- the most often case
if not globe or (globe == "http://www.wikidata.org/entity/Q2") then
return { symbol=geoformatdata.displayGlobes.earth.symbol, link="" }
end
for k, v in pairs(geoformatdata.displayGlobes) do
if globe == mw.wikibase.getEntityUrl(v.Q) then
return { link="globe:"..k, data=v }
end
end
return nil
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(claims, 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(claims, 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="", data=false } or selectGlobe(argGlobe or autocoords.globe) or { symbol="", link=false, data=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 geoformatdata.defaultWDPrecision,
params = table.concat(params,"_"),
displayData = data or geoformatdata.displayGlobes.earth,
globeSymbol = globe.symbol,
}
return result
end
local function parseDisplayPrecision(coordinates, displayPrecision)
local function adjustPrecision(dms)
if not coordinates.precision or (coordinates.precision >= 1) then
return
end
local eps = coordinates.precision / 1024
for i, v in ipairs(geoformatdata.supportedFormats) do
if (v.dms == dms) and ((v.precision - coordinates.precision) < eps) then
coordinates.precision = v.precision
break
end
end
end
local function findAndSetPrecision()
-- find wikipedia template precision
for i, v in ipairs(geoformatdata.supportedFormats) do
if displayPrecision == v.prec then
coordinates.precision = v.precision
return true
end
end
end
if displayPrecision == geoformatdata.valPrecisionAutoDMS then
adjustPrecision(true)
elseif displayPrecision == geoformatdata.valPrecisionAutoDecimal then
adjustPrecision(false)
elseif not findAndSetPrecision() then
return false
end
return true
end
local function distance(A, B)
-- [[Ortodroma]]
-- <math>D = \operatorname{arc cos}((\sin \varphi_1 \sin \varphi_2)+(\cos \varphi_1 \cos \varphi_2 \cos \Delta\lambda)),</math>
local phiA = math.pi * A.latitude / 180.0
local phiB = math.pi * B.latitude / 180.0
local delta = math.pi * (B.longitude - A.longitude) / 180.0
return math.acos(math.sin(phiA)*math.sin(phiB) + math.cos(phiA)*math.cos(phiB)*math.cos(delta))
end
local function size(A)
local precision = A.precision or geoformatdata.defaultSizePrecision
local B = {}
B.latitude = A.latitude < 0 and A.latitude + precision or A.latitude - precision
B.longitude = A.longitude + precision
return distance(A,B)
end
--------------------------------------------------------------------------------
-- public module methods
--------------------------------------------------------------------------------
return {
[geoformatdata.apiCoordinates] = function (frame)
local args = require('Module:Arguments').getArgs(frame, {
trim = false,
removeBlanks = false,
wrappers = geoformatdata.wrappersCoordinates,
})
local coords = args[geoformatdata.argCoordinatesCoordinates]
local geohack = args[geoformatdata.argCoordinatesGeohack]
local name = args[geoformatdata.argName]
local location = args[geoformatdata.argLocation]
local displayPrecision = args[geoformatdata.argPrecision]
local link = args[geoformatdata.argLink]
local symbol = args[geoformatdata.argSymbol]
if symbol == geoformatdata.valSymbolYes then
symbol = true
elseif symbol == geoformatdata.valSymbolNo then
symbol = false
elseif symbol and (#symbol==0) then
return showError(geoformatdata.errorEmptySymbolOption, {})
end
if not coords and not geohack and not name and not displayPrecision and not link and (location == geoformatdata.valLocationTop) and (symbol == nil) then
local autocoords = wd("P625", false)
if not autocoords then
-- missing data in WD
return
end
local coordinates = create()
coordinates.latitude = autocoords.latitude
coordinates.longitude = autocoords.longitude
coordinates.precision = autocoords.precision
coordinates.params = autocoords.params
coordinates.displayData = autocoords.displayData
coordinates.inline = false
coordinates.top = true
coordinates.link = true
coordinates:normalize()
return coordinates:display()..coordinates:extensionGeoData()
end
local coordinates = create()
local status, errorMessage = coordinates:parse(coords, geohack, displayPrecision)
if not status then
return showError(errorMessage, args)
end
coordinates.symbol = symbol
coordinates.name = name
local full = location or displayPrecision or link or (symbol ~= nil) or (coordinates.displayData and (coordinates.displayData.Q ~= "Q2"))
if full then
if displayPrecision and not parseDisplayPrecision(coordinates, displayPrecision) then
return showError(string.format(geoformatdata.errorUnrecognizedPrecisionOption, displayPrecision), {})
end
if link == geoformatdata.valLinkYes then
coordinates.link = true
elseif link == geoformatdata.valLinkNo then
coordinates.link = false
elseif link == geoformatdata.valLinkGMS then
coordinates.link = "gms"
elseif link then
return showError(string.format(geoformatdata.errorUnrecognizedLinkOption, link), {})
else -- default is "yes"
coordinates.link = true
end
if location == geoformatdata.valLocationTop then
coordinates.top = true
coordinates.inline = false
elseif location == geoformatdata.valLocationInline then
coordinates.top = false
coordinates.inline = true
elseif location == geoformatdata.valLocationTopAndInline then
coordinates.top = true
coordinates.inline = true
elseif location then
return showError(string.format(geoformatdata.errorUnrecognizedLocationOption, location), {})
else -- default if not given
coordinates.top = false
coordinates.inline = true
end
else -- micro
-- 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
end
coordinates:normalize()
local result = {}
table.insert(result, coordinates:display())
if full then
table.insert(result, coordinates:extensionGeoData())
end
return table.concat(result)
end,
[geoformatdata.apiMapPoint] = function(frame)
local args = require('Module:Arguments').getArgs(frame, {
trim = false,
removeBlanks = false,
wrappers = geoformatdata.wrappersMapPoint,
})
local coordinates = create()
local description = args[geoformatdata.argDescription]
local symbol = args[geoformatdata.argSymbol]
geohack = geoformatdata.mapPointMapping[description] or args[geoformatdata.argMapPointGeohack]
local status, errorMessage, fromWD = false, false, false
if args[geoformatdata.argMapPointCoordinates] then
status, errorMessage = coordinates:parse(args[geoformatdata.argMapPointCoordinates],geohack)
else
local autocoords = wd("P625", false)
if not autocoords then
-- missing data in WD
return
end
coordinates.latitude = autocoords.latitude
coordinates.longitude = autocoords.longitude
coordinates.precision = autocoords.precision
coordinates.params = autocoords.params or geohack
coordinates.displayData = autocoords.displayData
status = true
fromWD = true
end
local point = {}
if not status then
point.error = showError(errorMessage, args)
else
coordinates:normalize()
point.latitude = coordinates.latitude
point.longitude = coordinates.longitude
point.link = coordinates:geohack(false, false, false)
if args.display then
if symbol == geoformatdata.valSymbolYes then
coordinates.symbol = true
elseif symbol == geoformatdata.valSymbolNo then
coordinates.symbol = false
elseif symbol and (#symbol==0) then
point.error = showError(geoformatdata.errorEmptySymbolOption, {})
else
coordinates.symbol = symbol
end
coordinates.top = mw.title.getCurrentTitle().namespace == 0
coordinates.inline = true
if fromWD and (args.display == "#coordinates") then
point.display = coordinates:display()..coordinates:extensionGeoData()
else
point.display = coordinates:display()
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]
point.alt = args[geoformatdata.argAlt]
if not coordinates.params then
point.geohack = geoformatdata.defArgGeohack
end
return mw.text.jsonEncode(point)..","
end,
[geoformatdata.apiCheckDistance] = function(frame)
local args = require('Module:Arguments').getArgs(frame, {
trim = false,
removeBlanks = false,
})
local scalingFactor = tonumber(args[geoformatdata.argScalingFactor]) or 6731000
local displayPrecision = args[geoformatdata.argPrecision]
local maximumDistance = tonumber(args[geoformatdata.argMaximumDistance])
local errorMessage = args[geoformatdata.argErrorMessage]
local coords = args[geoformatdata.argCoordinatesCoordinates]
if not errorMessage or not maximumDistance or not coords then
-- nie ma nic do wyślwietlenia lub sprawdzenia
mw.log("apiCheckDistance: nic nie ma")
return
end
local A = create()
local status, error
status, error = A:parse(coords, nil, displayPrecision)
if not status then
mw.logObject(error, "apiCheckDistance: parse error")
return
end
if displayPrecision and not parseDisplayPrecision(A, display) then
mw.logObject(error, "apiCheckDistance: parsePrecision error")
return
end
local B = wd("P625", false)
if not B then
mw.logObject(B, "apiCheckDistance: missing data in WD")
return
end
A.radius = scalingFactor * size(A)
B.radius = scalingFactor * size(B)
local distance = scalingFactor * distance(A,B)
if distance <= maximumDistance then
-- brak błędów
return
end
-- zalogujmy co się da
mw.logObject({A, WD = B, distance = distance}, "apiCheckDistance")
-- parametry komunikatu
local parameters =
{
distance = tostring(math.floor(distance + 0.5)),
radiusA = tostring(math.floor(A.radius + 0.5)),
radiusB = tostring(math.floor(B.radius + 0.5)),
}
local message, _ = string.gsub(errorMessage, "%(%(%((.-)%)%)%)", parameters)
return message
end,
[geoformatdata.apiDistance] = function(frame)
local args = require('Module:Arguments').getArgs(frame, {
trim = false,
removeBlanks = false,
})
local scalingFactor = tonumber(args[geoformatdata.argScalingFactor]) or geoformatdata.defaultDistanceScalingFactor
local coordsA = args[1]
local coordsB = args[2]
local A = create()
local status, error
status, error = A:parse(coordsA, nil, nil)
if not status then
mw.logObject(error, "apiDistance: parse error A")
return
end
local B
if coordsB then
B = create()
status, error = B:parse(coordsB, nil, nil)
if not status then
mw.logObject(error, "apiDistance: parse error B")
return
end
else
B = wd("P625", false)
if not B then
mw.logObject(B, "apiDistance: missing data in WD")
return
end
end
return scalingFactor * distance(A,B)
end,
[geoformatdata.apiPrecisionRadius] = function(frame)
local args = require('Module:Arguments').getArgs(frame, {
trim = false,
removeBlanks = false,
})
local scalingFactor = tonumber(args[geoformatdata.argScalingFactor]) or geoformatdata.defaultDistanceScalingFactor
local displayPrecision = args[geoformatdata.argPrecision]
local coords = args[geoformatdata.argCoordinatesCoordinates]
local A
if coords then
A = create()
local status, error
status, error = A:parse(coords, nil, displayPrecision)
if not status then
mw.logObject(error, "apiPrecisionRadius: parse error")
return
end
if displayPrecision and not parseDisplayPrecision(A, displayPrecision) then
mw.logObject(displayPrecision, "apiPrecisionRadius: parsePrecision error")
return
end
else
A = wd("P625", false)
if not A then
mw.logObject(A, "apiPrecisionRadius: missing data in WD")
return
end
end
return scalingFactor * size(A)
end,
}