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_
local DEFAULT_FRAME_
local DEFAULT_ZOOM = 10
local DEFAULT_GEOMASK_STROKE_
local DEFAULT_GEOMASK_STROKE_COLOR = "#777777"
local DEFAULT_GEOMASK_FILL = "#888888"
local DEFAULT_GEOMASK_FILL_OPACITY = "0.25"
local DEFAULT_SHAPE_STROKE_
local DEFAULT_SHAPE_STROKE_COLOR = "#FF0000"
local DEFAULT_SHAPE_FILL = "#606060"
local DEFAULT_SHAPE_FILL_OPACITY = "0.1"
local DEFAULT_LINE_STROKE_
local DEFAULT_LINE_STROKE_COLOR = "#FF0000"
local DEFAULT_MARKER_COLOR = "#5E74F3"  
local util = {}  
function util.noop(info)
local DEFAULT_NOOP_OUTPUT = ""  
-- uncomment this when debugging
-- DEFAULT_NOOP_OUTPUT = "debug: mapframe no-op: " .. info
-- mw.log(DEFAULT_NOOP_OUTPUT)  
return DEFAULT_NOOP_OUTPUT
end  
-- Trim whitespace from args, and remove empty args
function util.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 util.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 util.hasWikidataProperty(item_id, property_id)
return util.getBestStatement(item_id, property_id) and true or false
end  
function util.getStatementValue(statement)
return statement and statement.mainsnak and statement.mainsnak.datavalue and statement.mainsnak.datavalue.value or nil
end  
function util.relatedEntity(item_id, property_id)
local value = util.getStatementValue( util.getBestStatement(item_id, property_id) )
return value and value.id or false
end  
function util.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 util.shouldAutoRun
-- Check if should be running
local pargs = .getParent.args
local explicitlyOn = yesno(mw.text.trim(pargs.mapframe or "")) -- true of false or nil
if pargs.coordinates == "&#123;&#123;&#123;coordinates&#125;&#125;&#125;" then explicitlyOn = false end
local onByDefault = (explicitlyOn == nil) and yesno(mw.text.trim(.args.onByDefault or ""), false) -- true or false
return explicitlyOn or onByDefault
end  
function util.argsFromAuto
-- Get args from the (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(, {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
-- allow captionstyle to be unprefixed, for compatibility with Module:Infobox
elseif name == "captionstyle" and not fixedArgs.captionstyle then
fixedArgs.captionstyle = val
end
end
return fixedArgs
end  
function util.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  
function util.trackAndWarn(trackingCat, warning)
local title = mw.title.getCurrentTitle
local results = title and title.namespace == 0 and trackingCat and * or *
if warning then
local warn = require('Module:If preview')._warning
results = results..warn({warning})
end
return results
end  
function util.ternary(flag, other)
other = other or 'other'
flag = flag == '' and 'no' or flag
local yesNoOut = yesno(flag,other)
local yes = (yesNoOut == true)
local no = (yesNoOut == false)
return yes, no
end  
local p = {}  
p._caption = function(args)
if args.caption then
return args.caption
elseif args.switcher then
return util.noop("no caption or switcher")
end
local maskItem
local maskType = util.idType(args.geomask)
if maskType == 'item' then
maskItem = args.geomask
elseif maskType == "property" then
maskItem = util.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 util.noop("missing maskItemLabel with type " .. (maskType or "nil") .. " and item " .. (maskItem or "nil"))
end  
--A list of types for objects that are too small to allow Kartographer to take over zoom
local tinyType = {
landmark=true,
railwaystation=true,
edu=true,
pass=true,
camera=true
}  
p._main = function(_config)
-- accumulate tracking cats
local tracking = ''
-- `config` is the args passed to this module
local config = util.trimArgs(_config)  
-- allow alias for config.coord
config.coord = config.coord or config.coordinates  
-- Require wikidata item, or specified coords
local wikidataId = config.id or mw.wikibase.getEntityIdForCurrentPage
if not(wikidataId) and not(config.coord) then
return false, util.trackAndWarn('Pages using infobox mapframe with missing coordinates')
end  
-- Require coords (specified or from wikidata), so that map will be centred somewhere
-- (P625 = coordinate location)
local wdCoordinates = util.getStatementValue(util.getBestStatement(wikidataId, 'P625'))
if not (config.coord or wdCoordinates) then
return false, util.trackAndWarn('Pages using infobox mapframe with missing coordinates')
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. = "yes"
args.plain = "yes"
args["-width"] = config["-width"] or config.width or DEFAULT_FRAME_WIDTH
args["-height"] = config["-height"] or config.height or DEFAULT_FRAME_HEIGHT
args["-align"] = ""  
args["-coord"] = config["-coordinates"] or config["-coord"]
-- Note: config["coordinates"] or config["coord"] should not be used for the alignment of the ;
-- see talk page ( https://en.wikipedia.org/wiki/Special:Diff/876492931 )  
-- deprecated lat and long parameters
args["-lat"] = config["-lat"] or config["-latitude"]
args["-long"] = config["-long"] or config["-longitude"]  
-- if zoom isn't specified from config, first check wikidata
local zoom = config.zoom or util.getStatementValue(util.getBestStatement(wikidataId, 'P6592'))
if not zoom then
-- Calculate zoom from length or area (converted to km or km2)
-- Zoom so that length or area is completely included in mapframe
local getZoom = require('Module:Infobox dim')._zoom
zoom = getZoom({length_km=config.length_km, length_mi=config.length_mi,
width_km=config.width_km, width_mi=config.width_mi,
area_km2=config.area_km2, area_mi2=config.area_mi2,
area_ha=config.area_ha, area_acre=config.area_acre,
type=config.type, population=config.population,
viewport_px=math.min(args["-width"],args["-height"]),
latitude=wdCoordinates and wdCoordinates.latitude})
end
args.zoom = zoom or DEFAULT_ZOOM  
-- Use OSM relation ID if available; otherwise use geoshape if that is available
-- (geoshape is required for defunct entities, which are outside OSM's scope)