Modul:Infobox mapframe

PenggunaanSunting

Modul yang otomatis membuat mapframe dalam kotak info dengan masukan pengguna minimum

Penggunaan umumSunting

Tempatkan templat {{Infobox mapframe}}, mengikuti dokumentasinya. Modul ini dapat diimpor ke modul lua lainnya.

Peta otomatis di kotak infoSunting

Contoh: Template:Infobox prison, Template:Infobox prison/doc

Sunting templat kotak infonya (atau bak pasirnya). Tambah baris seperti contoh di bawah pada kotak info Anda. Ganti angka (4, atau 97 ke 99), dengan angka yang sesuai dengan jumlah parameter yang Anda rencanakan.

Jika ditempatkan di atas kotak info Jika ditempatkan di bawah kotak info
| image4     = {{#invoke:Infobox mapframe|auto}}
| caption4   = {{#invoke:Infobox mapframe|autocaption}}
| header97   = {{#if:{{{mapframe|}}}|Location}}
| data98     = {{#invoke:Infobox mapframe|auto}}
| data99     = {{#invoke:Infobox mapframe|autocaption}}

Jika templat tersebut mengandung{{#invoke:Check for unknown parameters}}, tambah parameter berikut:

| mapframe | mapframe-caption | mapframe-custom | mapframe-id | mapframe-coord | mapframe-wikidata | mapframe-point | mapframe-shape | mapframe-frame-width | mapframe-frame-height | mapframe-shape-fill | mapframe-shape-fill-opacity | mapframe-stroke-color | mapframe-stroke-colour | mapframe-stroke-width | mapframe-marker | mapframe-marker-color | mapframe-marker-colour | mapframe-geomask | mapframe-geomask-stroke-color | mapframe-geomask-stroke-colour | mapframe-geomask-stroke-width | mapframe-geomask-fill | mapframe-geomask-fill-opacity | mapframe-zoom | mapframe-length_km | mapframe-length_mi | mapframe-area_km2 | mapframe-area_mi2 | mapframe-frame-coordinates | mapframe-frame-coord | mapframe-switcher | mapframe-height | mapframe-width 

Begitu selesai, parameter di atas akan tersedia kepada pengguna.

  • Nilai bawaan parameter-parameter ini dapat ditentukan dengan menyebut #invoke, contohnya {{#invoke:Infobox mapframe|auto|mapframe-marker=library}} berarti library marker digunakan kecuali nilai yang berbeda diberikan.
  • Secara bawaan peta tidak ditampilkan, kecuali menyebutkan |mapframe=yes. Untuk menyalakannya, pada sebutan #invoke tambahkan |onByDefault=yes – yang berarti peta tersebut akan ditampilkan kecuali menyebutkan |mapframe=no. onByDefault dapat kondisional, seperti jika parameter lainnya muncul, misal |onByDefault={{#if:{{{pushpin_map|}}}|no|yes}}
  • Tambah parameter baru ke dokumentasi. Gunakan {{Infobox mapframe/doc/parameters}}. Tentukan nilai bawaan (jika menggunakan penyebutan #invoke) dengan menambahkan parameter |nama parameter=nilai. Luaran otomatisnya ditunjukkan di bawah:

Lihat pulaSunting


local mf = require('Module:Mapframe')
local getArgs = require('Module:Arguments').getArgs
local yesno = require('Module:Yesno')
local infoboxImage = require('Module:InfoboxImage').InfoboxImage

-- Defaults
local DEFAULT_FRAME_WIDTH = "270"
local DEFAULT_FRAME_HEIGHT = "200"
local DEFAULT_ZOOM = 10
local DEFAULT_GEOMASK_STROKE_WIDTH = "1"
local DEFAULT_GEOMASK_STROKE_COLOR = "#777777"
local DEFAULT_GEOMASK_FILL = "#888888"
local DEFAULT_GEOMASK_FILL_OPACITY = "0.5"
local DEFAULT_SHAPE_STROKE_WIDTH = "3"
local DEFAULT_SHAPE_STROKE_COLOR = "#FF0000"
local DEFAULT_SHAPE_FILL = "#606060"
local DEFAULT_SHAPE_FILL_OPACITY = "0.5"
local DEFAULT_LINE_STROKE_WIDTH = "5"
local DEFAULT_LINE_STROKE_COLOR = "#FF0000"
local DEFAULT_MARKER_COLOR = "#5E74F3"


-- Trim whitespace from args, and remove empty args
function trimArgs(argsTable)
	local cleanArgs = {}
	for key, val in pairs(argsTable) do
		if type(val) == 'string' then
			val = val:match('^%s*(.-)%s*$')
			if val ~= '' then
				cleanArgs[key] = val
			end
		else
			cleanArgs[key] = val
		end
	end
	return cleanArgs
end

function getBestStatement(item_id, property_id)
	if not(item_id) or not(mw.wikibase.isValidEntityId(item_id)) or not(mw.wikibase.entityExists(item_id)) then
		return false
	end
	local statements = mw.wikibase.getBestStatements(item_id, property_id)
	if not statements or #statements == 0 then
		return false
	end
	local hasNoValue = ( statements[1].mainsnak and statements[1].mainsnak.snaktype == 'novalue' )
	if hasNoValue then
		return false
	end
	return statements[1]
end

function hasWikidataProperty(item_id, property_id)
	return getBestStatement(item_id, property_id) and true or false
end

function getStatementValue(statement)
	return statement and statement.mainsnak and statement.mainsnak.datavalue and statement.mainsnak.datavalue.value or nil
end

function relatedEntity(item_id, property_id)
	local value = getStatementValue( getBestStatement(item_id, property_id) )
	return value and value.id or false
end

function idType(id)
	if not id then 
		return nil
	elseif mw.ustring.match(id, "[Pp]%d+") then
		return "property"
	elseif mw.ustring.match(id, "[Qq]%d+") then
		return "item"
	else
		return nil
	end
end

function getZoom(value, unit)
	local length_km
	if unit == 'km' then
		length_km = tonumber(value)
	elseif unit == 'mi' then
		length_km = tonumber(value)*1.609344
	elseif unit == 'km2' then
		length_km = math.sqrt(tonumber(value))
	elseif unit == 'mi2' then
		length_km = math.sqrt(tonumber(value))*1.609344
	end
	-- max for zoom 2 is 6400km, for zoom 3 is 3200km, for zoom 4 is 1600km, etc
	local zoom = math.floor(8 - (math.log10(length_km) - 2)/(math.log10(2)))
	-- limit to values below 17
	zoom = math.min(17, zoom)
	-- take off 1 when calculated from area, to account for unusual shapes
	if unit == 'km2' or unit == 'mi2' then
		zoom = zoom - 1
	end
	-- minimum value is 1
	return math.max(1, zoom)
end

function shouldAutoRun(frame)
	-- Check if should be running
	local explicitlyOn = yesno(mw.text.trim(frame.getParent(frame).args.mapframe or "")) -- true of false or nil
	local onByDefault = (explicitlyOn == nil) and yesno(mw.text.trim(frame.args.onByDefault or ""), false) -- true or false
	return explicitlyOn or onByDefault
end

function argsFromAuto(frame)
	-- Get args from the frame (invoke call) and the parent (template call).
	-- Frame arguments are default values which are overridden by parent values
	-- when both are present
	local args = getArgs(frame, {parentFirst = true})
	
	-- Discard args not prefixed with "mapframe-", remove that prefix from those that remain
	local fixedArgs = {}
	for name, val in pairs(args) do
		local fixedName = string.match(name, "^mapframe%-(.+)$" )
		if fixedName then
			fixedArgs[fixedName] = val
		-- allow coord, coordinates, etc to be unprefixed
		elseif name == "coordinates" or name == "coord" or name == "coordinate" and not fixedArgs.coord then
			fixedArgs.coord = val
		-- allow id, qid to be unprefixed, map to id (if not already present)
		elseif name == "id" or name == "qid" and not fixedArgs.id then
			fixedArgs.id = val
		end
	end
	return fixedArgs
end

local p = {}

p.autocaption = function(frame)
	if not shouldAutoRun(frame) then return "" end
	local args = argsFromAuto(frame)
	if args.caption then
		return args.caption
	elseif args.switcher then 
		return ""
	end
	local maskItem
	local maskType = idType(args.geomask)
	if maskType == 'item' then
		maskItem = args.geomask
	elseif maskType == "property" then
		maskItem = relatedEntity(args.id or mw.wikibase.getEntityIdForCurrentPage(), args.geomask)
	end
	local maskItemLabel = maskItem and mw.wikibase.getLabel( maskItem )
	return maskItemLabel and "Location in "..maskItemLabel or ""
end

function parseCustomWikitext(customWikitext)
	-- infoboxImage will format an image if given wikitext containing an
	-- image, or else pass through the wikitext unmodified
	return infoboxImage({
		args = {
			image = customWikitext
		}
	})
end

p.auto = function(frame)
	if not shouldAutoRun(frame) then return "" end
	local args = argsFromAuto(frame)
	if args.custom then
		return frame:preprocess(parseCustomWikitext(args.custom))
	end
	local mapframe = p._main(args)
	return frame:preprocess(mapframe)
end

p.main = function(frame)
	local parent = frame.getParent(frame)
	local parentArgs = parent.args
	local mapframe = p._main(parentArgs)
	return frame:preprocess(mapframe)
end

p._main = function(_config)
	-- `config` is the args passed to this module
	local config = trimArgs(_config)
	
	-- Require wikidata item, or specified coords
	local wikidataId = config.id or mw.wikibase.getEntityIdForCurrentPage()
	if not(wikidataId) and not(config.coord) then
		return ''
	end

	-- Require coords (specified or from wikidata), so that map will be centred somewhere
	-- (P625 = coordinate location)
	local hasCoordinates = hasWikidataProperty(wikidataId, 'P625') or config.coordinates or config.coord
	if not hasCoordinates then  
		return ''
	end

	-- `args` is the arguments which will be passed to the mapframe module
	local args = {}

	-- Some defaults/overrides for infobox presentation
	args.display = "inline"
	args.frame = "yes"
	args.plain = "yes"
	args["frame-width"]  = config["frame-width"] or config.width or DEFAULT_FRAME_WIDTH
	args["frame-height"] = config["frame-height"] or config.height or DEFAULT_FRAME_HEIGHT
	args["frame-align"]  = "center"

	args["frame-coord"] = config["frame-coordinates"] or config["frame-coord"] or ""
	-- Note: config["coordinates"] or config["coord"] should not be used for the alignment of the frame;
	-- see talk page ( https://en.wikipedia.org/wiki/Special:Diff/876492931 )

	-- deprecated lat and long parameters
	args["frame-lat"]    = config["frame-lat"] or config["frame-latitude"] or ""
	args["frame-long"]   = config["frame-long"] or config["frame-longitude"] or ""

	-- Calculate zoom from length or area (converted to km or km2)
	if config.length_km then
		args.zoom = getZoom(config.length_km, 'km')
	elseif config.length_mi then
		args.zoom = getZoom(config.length_mi, 'mi')
	elseif config.area_km2 then
		args.zoom = getZoom(config.area_km2, 'km2')
	elseif config.area_mi2 then
		args.zoom = getZoom(config.area_mi2, 'mi2')
	else
		args.zoom = config.zoom or DEFAULT_ZOOM
	end

	-- Conditionals: whether point, geomask should be shown
	local hasOsmRelationId = hasWikidataProperty(wikidataId, 'P402') -- P402 is OSM relation ID
	local shouldShowPointMarker;
	if config.point == "on" then
		shouldShowPointMarker = true 
	elseif config.point == "none" then
		shouldShowPointMarker = false
	else
		shouldShowPointMarker = not(hasOsmRelationId) or (config.marker and config.marker ~= 'none') or (config.coordinates or config.coord)
	end
	local shouldShowShape = config.shape ~= 'none'
	local shapeType = config.shape == 'inverse' and 'shape-inverse' or 'shape'
	local shouldShowLine = config.line ~= 'none'
	local maskItem
	local useWikidata = wikidataId and true or false -- Use shapes/lines based on wikidata id, if there is one
	-- But do not use wikidata when local coords are specified (and not turned off), unless explicitly set
	if useWikidata and config.coord and shouldShowPointMarker then
		useWikidata = config.wikidata and true or false
	end
	
	-- Switcher
	if config.switcher == "zooms" then
		-- switching between zoom levels
		local maxZoom = math.max(tonumber(args.zoom), 3) -- what zoom would have otherwise been (if 3 or more, otherwise 3)
		local minZoom = 1 -- completely zoomed out
		local midZoom = math.floor((maxZoom + minZoom)/2) -- midway between maxn and min
		args.switch = "zoomed in, zoomed midway, zoomed out"
		args.zoom = string.format("SWITCH:%d,%d,%d", maxZoom, midZoom, minZoom)
	elseif config.switcher == "auto" then
		-- switching between P276 and P131 areas with recursive lookup, e.g. item's city,
		-- that city's state, and that state's country
		args.zoom = nil -- let kartographer determine the zoom
		local maskLabels = {}
		local maskItems = {}
		local maskItemId = relatedEntity(wikidataId, "P276") or  relatedEntity(wikidataId, "P131") 
		local maskLabel = mw.wikibase.getLabel(maskItemId)
		while maskItemId and maskLabel and mw.text.trim(maskLabel) ~= "" do
			table.insert(maskLabels, maskLabel)
			table.insert(maskItems, maskItemId)
			maskItemId = maskItemId and relatedEntity(maskItemId, "P131")
			maskLabel = maskItemId and mw.wikibase.getLabel(maskItemId)
		end
		if #maskLabels > 1 then
			args.switch = table.concat(maskLabels, "###")
			maskItem = "SWITCH:" .. table.concat(maskItems, ",")
		elseif #maskLabels == 1 then
			maskItem = maskItemId[1]
		end
	elseif config.switcher == "geomasks" and config.geomask then
		-- switching between items in geomask parameter
		args.zoom = nil -- let kartographer determine the zoom
		local separator = (mw.ustring.find(config.geomask, "###", 0, true ) and "###") or
			(mw.ustring.find(config.geomask, ";", 0, true ) and ";") or ","
		local pattern = "%s*"..separator.."%s*"
		local maskItems = mw.text.split(mw.ustring.gsub(config.geomask, "SWITCH:", ""), pattern)
		local maskLabels = {}
		if #maskItems > 1 then
			for i, item in ipairs(maskItems) do
				table.insert(maskLabels, mw.wikibase.getLabel(item))
			end
			args.switch = table.concat(maskLabels, "###")
			maskItem = "SWITCH:" .. table.concat(maskItems, ",")
		end
	end
	
	-- resolve geomask item id (if not using geomask switcher)
	if not maskItem then --  
		local maskType = idType(config.geomask)
		if maskType == 'item' then
			maskItem = config.geomask
		elseif maskType == "property" then
			maskItem = relatedEntity(wikidataId, config.geomask)
		end
	end
	
	-- Keep track of arg numbering
	local argNumber = ''
	local function incrementArgNumber()
		if argNumber == '' then
			argNumber = 2
		else
			argNumber = argNumber + 1
		end
	end
	
	-- Geomask
	if maskItem then
		args["type"..argNumber] = "shape-inverse"
		args["id"..argNumber] = maskItem
		args["stroke-width"..argNumber] = config["geomask-stroke-width"] or DEFAULT_GEOMASK_STROKE_WIDTH
		args["stroke-color"..argNumber] = config["geomask-stroke-color"] or config["geomask-stroke-colour"] or DEFAULT_GEOMASK_STROKE_COLOR
		args["fill"..argNumber] = config["geomask-fill"] or DEFAULT_GEOMASK_FILL
		args["fill-opacity"..argNumber] = config["geomask-fill-opacity"] or DEFAULT_SHAPE_FILL_OPACITY
		-- Let kartographer determine zoom and position, unless it is explicitly set in config
		if not config.zoom and not config.switcher then
			args.zoom = nil
			args["frame-coord"] = nil
			args["frame-lat"] = nil
			args["frame-long"] = nil 	
			local maskArea = getStatementValue( getBestStatement(maskItem, 'P2046') )
		end
		incrementArgNumber()
		-- Hack to fix phab:T255932
		if not args.zoom then
			args["type"..argNumber] = "line"
			args["id"..argNumber] = maskItem
			args["stroke-width"..argNumber] = 0
			incrementArgNumber()
		end
	end
	
	-- Shape (or shape-inverse)
	if useWikidata and shouldShowShape then
		args["type"..argNumber] = shapeType
		if config.id then args["id"..argNumber] = config.id end
		args["stroke-width"..argNumber] = config["shape-stroke-width"] or config["stroke-width"] or DEFAULT_SHAPE_STROKE_WIDTH
		args["stroke-color"..argNumber] = config["shape-stroke-color"] or config["shape-stroke-colour"] or config["stroke-color"] or config["stroke-colour"] or DEFAULT_SHAPE_STROKE_COLOR
		args["fill"..argNumber] = config["shape-fill"] or DEFAULT_SHAPE_FILL
		args["fill-opacity"..argNumber] = config["shape-fill-opacity"] or DEFAULT_SHAPE_FILL_OPACITY
		incrementArgNumber()
	end
	
	-- Line
	if useWikidata and shouldShowLine then
		args["type"..argNumber] = "line"
		if config.id then args["id"..argNumber] = config.id end
		args["stroke-width"..argNumber] = config["line-stroke-width"] or config["stroke-width"] or DEFAULT_LINE_STROKE_WIDTH
		args["stroke-color"..argNumber] = config["line-stroke-color"] or config["line-stroke-colour"] or config["stroke-color"] or config["stroke-colour"] or DEFAULT_LINE_STROKE_COLOR
		incrementArgNumber()
	end

	-- Point
	if shouldShowPointMarker then
		args["type"..argNumber] = "point"
		if config.id then args["id"..argNumber] = config.id end
		if config.coord then args["coord"..argNumber] = config.coord end
		if config.marker then args["marker"..argNumber] = config.marker end
		args["marker-color"..argNumber] = config["marker-color"] or config["marker-colour"] or DEFAULT_MARKER_COLOR
		incrementArgNumber()
	end

	local mapframe = args.switch and mf.multi(args) or mf._main(args)
	local tracking = hasOsmRelationId and '' or '[[Category:Mapframe Infobox tanpa hubungan OSM di Wikidata]]'
	return mapframe .. tracking
end

return p