Module:Settlement Wikidata
| This module is rated as ready for general use. It has reached a mature state, is considered relatively stable and bug-free, and may be used wherever appropriate. It can be mentioned on help pages and other Wikipedia resources as an option for new users. To minimise server load and avoid disruptive output, improvements should be developed through sandbox testing rather than repeated trial-and-error editing. |
| This module is currently protected from editing. See the protection policy and protection log for more details. Please discuss any changes on the talk page; you may submit an edit request to ask an administrator to make an edit if it is uncontroversial or supported by consensus. You may also request that this page be unprotected. |
| This Lua module is used on approximately 52,000 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
Uses Wikidata to look up area or population of settlement for use in {{Infobox settlement}} (or infoboxes that wrap it). Implements the following templates, see their test cases to test this module:
- {{Austria metadata Wikidata}} (test cases)
- {{France metadata Wikidata}} (test cases)
- {{Romania metadata Wikidata}} (test cases)
- {{Spain metadata Wikidata}} (test cases)
- {{Swiss metadata Wikidata}} (test cases)
See also: Lua test cases that gather these tests into one place.
Note that the module implements the community standard of returning values from Wikidata only if sourced to external sources.
Usage
{{#invoke:Settlement Wikidata|main}}
Parameters
- args[1]
- The name of the parameter in {{Infobox settlement}} whose value should be looked up in Wikidata, e.g.,
|area_total_km2=or|population_footnotes= - args[2]
- Optionally, the format of the returned date for "as of" look ups (e.g., "dmy" or "mdy")
- country
- the name of the country that contains the settlement
Returns
Depending on the parameter, either return a value, a date, or an auto-generated reference
require('strict')
local p = {}
-- Is the string a valid QID?
local function validQid(qid)
return qid and mw.ustring.find(qid,"^[Qq]%d+$")
end
-- lookup nestedIndex in outer table
-- Example: fetchNested(foo,{'bar','baz','quux'}) will return
-- foo.bar and foo.bar.baz and foo.bar.baz.quux
local function fetchNested(outer,nestedIndex)
if not outer or type(outer) ~= 'table' or not nestedIndex or type(nestedIndex) ~= 'table' then
return nil
end
local current = outer
for _, indx in ipairs(nestedIndex) do
current = current[indx]
if not current then
return current
end
end
return current
end
-- Mapping from wikidata properties (of a reference or "stated in" entity) to citation parameters
local pid2param = {
P50 = "author",
P123 = "publisher",
P407 = "language",
P577 = "date",
P585 = "date",
P813 = "access-date",
P854 = "url",
P856 = "url",
P953 = "url",
P1476 = "title",
P2699 = "url",
}
-- scan through snaks, loading citation args with correct values
local function scanSnaks(args, snaks)
for pid, data in pairs(snaks) do
data = fetchNested(data,{1,'mainsnak'}) or data[1] -- handle the case where the snak is buried in the claims
local param = pid2param[pid]
if pid == "P248" then --- "stated in": must look up different entity for data
local statedId = fetchNested(data,{'datavalue','value','id'})
local statedEntity = statedId and mw.wikibase.getEntity(statedId)
local statedClaims = statedEntity and statedEntity.claims
if statedClaims then
scanSnaks(args, statedClaims)
end
local statedLabel = fetchNested(statedEntity,{'labels','en','value'})
args.title = args.title or statedLabel
elseif param then
local renderedData = data and mw.wikibase.renderSnak(data)
if param == "title" then
-- set language from title if not otherwise specified
args.language = args.language or fetchNested(data,{'datavalue','value','language'})
end
if pid == "P856" then --- use official URL only if URL is missing
args[param] = args[param] or renderedData
else
args[param] = renderedData
end
end
end
end
-- In some situations (e.g., France census) we want to link to a specific data page instead of
-- a generic census landing page. urlMap is a list of tuples:
-- (url to substitute, property to use in the substitution, url to use in the substitution)
-- the url to use in the substitution has ***** where it wants to swap in the property
local urlMap = {
{'https://www.insee.fr/fr/statistiques/8681011','P374','https://www.insee.fr/fr/statistiques/8643952?geo=COM-*****'},
{'https://www.insee.fr/fr/statistiques/8290669','P374','https://www.insee.fr/fr/statistiques/8288323?geo=COM-*****'}
}
local function mapURL(qid,origURL)
if not qid or not origURL then
return origURL
end
for _, mapper in ipairs(urlMap) do
if origURL == mapper[1] then
local statements = mw.wikibase.getBestStatements(qid,mapper[2])
for _, statement in ipairs(statements) do
local prop = fetchNested(statement,{'mainsnak','datavalue','value'})
if prop then
return mw.ustring.gsub(mapper[3],'*****',prop)
end
end
end
end
return origURL
end
-- function to assemble wikicode to create citation, given reference property
local function citeSource(qid,snaks)
local args = {}
args.mode = "cs1"
scanSnaks(args, snaks)
args.url = mapURL(qid,args.url)
-- citation templates are unhappy without a title, so return a wikilink
if not args.title then
return args.url and "["..args.url.."]"
end
if not args.url then
args["access-date"] = nil -- don't allow access date without url
end
local result = '{{'..(args.url and 'cite web' or 'citation')
for k,v in pairs(args) do
result = result..'|'..k..'='..v
end
result = result..'}}'
return result
end
local rankToNumeric = {truth = 3, preferred = 2, normal = 1, deprecated = -1}
local function numericRank(rank)
if not rank then
return 0
end
return rankToNumeric[rank] or 0
end
local function compareRanks(rank1,rank2)
local numeric1 = numericRank(rank1)
local numeric2 = numericRank(rank2)
if numeric1 > numeric2 then
return 1
elseif numeric1 < numeric2 then
return -1
else
return 0
end
end
-- Core function that looks up value, reference, and data for property
-- Arguments:
-- qid = entity id in Wikidata
-- pid = property id on that entity
-- unit = if non-nil, desired unit for value (expressed as a qid for that unit)
-- Returns table only if Wikidata property is source externally
-- amount = value that is looked up (or nil)
-- source = wiki markup for reference
-- asOf = date that value is valid (if given)
function p._getValueAndRef(qid,pid,unit)
local results = nil
local storedDate = nil
local storedRank = nil
local statements = mw.wikibase.getAllStatements(qid, pid)
for _, statement in pairs(statements) do
local amount
local value = fetchNested(statement,{'mainsnak','datavalue','value'})
if value then
amount = value.amount
end
-- check if unit is correct, if given
if unit then
local storedUnit = value.unit
if not storedUnit or not mw.ustring.find(value.unit or '',unit,1,true) then
amount = nil
end
end
local storedAsOf = fetchNested(statement,{'qualifiers','P585',1,'datavalue','value','time'})
local date
if storedAsOf then
date = mw.ustring.match(storedAsOf,'^%+(%d+%-%d+%-%d+)')
local justYear = mw.ustring.match(date,'^(%d+)%-0+%-0+$')
date = justYear or date
end
local rank = fetchNested(statement,{'rank'})
-- check to see if this statement is adequately sourced (filter out "imported from" refs)
local source
local refs = statement['references']
if refs then
for _, ref in ipairs(refs) do
local renderedRef = ref.snaks and mw.wikibase.renderSnaks(ref.snaks)
if renderedRef and not mw.ustring.find(renderedRef,'Wiki') then
local citation = citeSource(qid,ref.snaks)
if citation then
source = (source or '')..'<ref>'..citation..'</ref>'
end
end
end
end
if amount and source then -- yes, it is adequately sourced,
local theseResults = {}
theseResults.amount = tonumber(amount)
theseResults.source = source
theseResults.asOf = date
local betterRank = compareRanks(rank,storedRank)
-- ranking logic:
-- 1. Higher ranked statements are better than lower ranked
-- 2. At the same rank, dated statements are better than undated
-- 3. Between dated statements at the same rank, choose the latest one
-- 4. All else being equal, choose the first statement
-- 5. Ignore deprecated statements
if betterRank > 0 or (betterRank == 0 and date and (not storedDate or date > storedDate)) then
results = theseResults
storedDate = date
storedRank = rank
end
end
end
return results
end
-- for a qid, find the entity's population
-- if asOf is specified, prefer that population value
function p._population(qid)
return p._getValueAndRef(qid,"P1082",nil)
end
-- for a qid, find the area of the entity in square kilometers
function p._area(qid)
return p._getValueAndRef(qid,"P2046","Q712226") -- look for square kilometers only
end
-- main entry point
-- Arguments:
-- args[1] = name of infobox parameter to lookup in Wikidata (e.g., "area_total_km2" or "population_footnotes")
-- args[2] = date format for returned date
-- country = name of country which contains entity
-- qid = qid for entity (for testing, by default, use qid of current page)
-- Returns (depending on args[1]):
-- the value of the population or area, or
-- a reference for the value, or
-- the date the value is valid
function p._main(args)
local country = args.country
local param = args[1]
local qid = validQid(args.qid) and args.qid or mw.wikibase.getEntityIdForCurrentPage()
if not param or not qid then
return nil
end
qid = qid:upper()
country = country and string.lower(country)
param = string.lower(param)
--- France infobox is narrow, so display only year (unless otherwise specified)
args[2] = args[2] or country == "france" and 'y'
local results
-- currently supports area and population: check for substring in parameter
local wantArea = mw.ustring.find(param,"area",1,true)
local wantPop = mw.ustring.find(param,"population",1,true)
if wantArea then
results = p._area(qid)
elseif wantPop then
results = p._population(qid)
end
if not results then
return nil
elseif mw.ustring.find(param,"as_of",1,true) then
-- return date of validity
if not results.asOf then
return nil
end
--- if asOf has only year, return it
if mw.ustring.find(results.asOf,'^%d+$') then
return results.asOf
end
local lang = mw.getContentLanguage()
if args[2] == "ymd" then
return lang:formatDate("Y-m-d",results.asOf)
elseif args[2] == "dmy" then
return lang:formatDate("j F Y",results.asOf)
elseif args[2] == "mdy" then
return lang:formatDate("F j, Y",results.asOf)
elseif args[2] == "y" then
return lang:formatDate("Y",results.asOf)
else
return results.asOf
end
elseif mw.ustring.find(param,"footnotes",1,true) then
if not results.source then
return nil
end
-- run the pre-processor exactly once if returning a reference, because otherwise you get multiple (unused) reference
local frame = mw.getCurrentFrame()
return frame:preprocess(results.source)
elseif wantArea and mw.ustring.find(param,"km2",1,true) then
-- if parameter is for area in km2, return value
return results.amount
elseif wantPop then
return results.amount
end
-- either parameter unrecognized or something bad happened
return nil
end
-- template-facing entry point: do the usual argument processing
function p.main(frame)
local getArgs = require('Module:Arguments').getArgs
local args = getArgs(frame)
return p._main(args) or ""
end
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.