Moduł:Współrzędne


Moduł:Współrzędne w encyklopedii

Z Wikipedii, wolnej encyklopedii Przejdź do nawigacji Przejdź do wyszukiwania  Dokumentacja modułu zobacz historia odśwież

Moduł pomocniczy do obsługi współrzędnych geograficznych.

Spis treści

współrzędneedytuj kod

Implementacja szablonu {{współrzędne}}. Jeśli argumenty przekazane do szablonu są nieprawidłowe, to wynikiem jest komunikat błędu.

parametry szablonuedytuj kod

przykładyedytuj kod

punktedytuj kod

Implementacja szablonu {{Mapa lokalizacyjna/punkt}}. Funkcja przyjmuje parametry bezpośrednie, szablonowe lub bezpośrednio w tablicy dla wywołań z innych modułów. Jeśli nie podano żadnych współrzędnych to ładuje dane z Wikidanych wskazując cechę współrzędne geograficzne.

Opis parametrówedytuj kod


Odległośćedytuj kod

Funkcja diagnostyczna obliczająca przybliżoną odległość między dwoma punktami na globie. Domyślnie zwraca wynik w metrach dla Ziemi.

parametryedytuj kod

Promień dokładnościedytuj kod

Funkcja diagnostyczna obliczająca przybliżony promień okręgu opisanego na „prostokącie błędu” wynikającego z dokładności prezentacji współrzędnych. Domyślnie zwraca wynik w metrach dla Ziemi.

parametryedytuj kod

Błędyedytuj kod

Błędy należy zgłaszać na stronie Wikipedia:Kawiarenka/Kwestie techniczne lub Dyskusja wikiprojektu:Szablony lokalizacyjne.

Zobacz teżedytuj kod

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 = "Na mapach: ", displayGlobesDefaultSymbol = "⌘", defaultSymbolSeparator = "\194\160", documentationSubpage = "opis", defaultWDPrecision = 0.00027777777777777800, -- WD wyświetla domyślnie z dokładnością do sekund defaultSizePrecision = 1.0, defaultDistanceScalingFactor = 6731000, -- promień Ziemi w metrach geohack_root = "//tools.wmflabs.org/geohack/geohack.php?language=pl", geohack_hint = "Mapy, zdjęcia satelitarne i inne informacje dotyczące miejsca o współrzędnych geograficznych %s %s", -- template API data apiCoordinates = "współrzędne", apiMapPoint = "punkt", apiCheckDistance = "Sprawdź odległość współrzędnych", apiDistance = "Odległość", apiPrecisionRadius = "Promień dokładności", argScalingFactor = "mnożnik", argMaximumDistance = "odległość", argErrorMessage = "komunikat", wrappersCoordinates = "Szablon:Współrzędne", wrappersMapPoint = "Szablon:Mapa lokalizacyjna/punkt", argCoordinatesCoordinates = 1, argCoordinatesGeohack = 2, 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", valLinkGMS = "zgodnie", argSymbol = "symbol", valSymbolYes = "tak", valSymbolNo = "nie", argName = "nazwa", -- apiMapPoint argMapPointCoordinates = 1, argMark = "znak", argMarkSize = "rozmiar znaku", argDescription = "opis", argMapPointGeohack = "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 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", errorLatitudeOutOfRange = "Przekroczony zakres szerokości geograficznej (%f)", errorLongitudeOutOfRange = "Przekroczony zakres długości geograficznej (%f)", errorUnrecognizedLinkOption = "Niedozwolona wartość parametru ''linkuj'': %s", errorUnrecognizedLocationOption = "Niedozwolona wartość parametru ''umieść'': %s", errorUnrecognizedPrecisionOption = "Niedozwolona wartość parametru ''dokładność'': %s", errorEmptySymbolOption = "Pusty parametr ''symbol''", errorMissingCoordinates = "Brak współrzędnych", } -------------------------------------------------------------------------------- -- 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:Współrzędne/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, "&params=") 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, "&#x7C;") end table.insert(result, args[i]) i = i + 1 end if i > 1 then table.insert(result, "}") end table.insert(result, "</span>") if mw.title.getCurrentTitle().namespace == 0 then table.insert(result, geoformatdata.errorCategory) end return table.concat(result, "") end -------------------------------------------------------------------------------- -- 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] 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 -- precyzja jeszcze nie jest używana... 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 -- ... ale już jest wykorzystana na wyznaczenie promienia błędu 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, } 
Na podstawie artykułu: "Moduł:Współrzędne" pochodzącego z Wikipedii
OryginałEdytujHistoria i autorzy