Module:Map

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search

local Map = {}

function Map.main()

	-- Get the arguments
	local map = Map.getArg( 'map', 'world' )
	local region = Map.getArg( 'region' )
	local color = Map.getArg( 'color', 'red' )
	local width = Map.getArg( 'width', '100%' )
	local height = Map.getArg( 'height', 'auto' )
	local countries = Map.getArg( 'countries', '' )

	-- Get the map data
	local data = mw.loadData( 'Module:Map/' .. map )
	local paths = data.paths

	-- If any color directive is an absolute value
	-- we need to figure out what the top value is
	-- before we can color any of them
	local topValue
	for directive in mw.text.gsplit( countries, ',' ) do
		directive = mw.text.trim( directive )
		local value = directive:match( '[A-Za-z ]- *%(([0-9]-)%)' )
		if value then
			value = tonumber( value )
			if not topValue or value > topValue then
				topValue = value
			end
		end
	end

	-- Color the map
	for directive in mw.text.gsplit( countries, ',' ) do
		directive = mw.text.trim( directive )
		local countryDirective, colorDirective = directive:match( '([A-Za-z ]-) *%(([#0-9A-Za-z .%%]-)%)' )
		local country, opacity
		if countryDirective and colorDirective then
			country = countryDirective
			local percentage = colorDirective:match( '^([0-9.]+)%%$' )
			local value = colorDirective:match( '^[0-9.]+$' )
			if percentage then
				opacity = tonumber( percentage ) / 100
			elseif value then
				opacity = tonumber( value ) / topValue
			else
				color = colorDirective
			end
		else
			country = directive
		end
		if data.aliases then
			for id, aliases in pairs( data.aliases ) do
				for _, alias in pairs( aliases ) do
					if alias == country then
						country = id
					end
				end
			end
		end
		local id = 'id="' .. country .. '"'
		local fill = 'fill="' .. color .. '"'
		if opacity then
			fill = fill .. ' fill-opacity="' .. tostring( opacity ) .. '"'
		end
		paths = paths:gsub( id, id .. ' ' .. fill )
	end

	-- Set the SVG properties
	local svgWidth = tostring( data.width )
	local svgHeight = tostring( data.height )
	local svgViewBox = ''
	if region then
		local regionData = data.regions[ region ]
		local minX = tostring( regionData.minX )
		local minY = tostring( regionData.minY )
		svgWidth = tostring( regionData.width )
		svgHeight = tostring( regionData.height )
		svgViewBox = minX .. ',' .. minY .. ',' .. svgWidth .. ',' .. svgHeight
		if regionData.paths then
			local ids
			for _, id in pairs( regionData.paths ) do
				ids = ( ids and ( ids .. ', ' ) or '' ) .. '#' .. id
			end
			if ids then
				local style = '<style>g:not(' .. ids .. ') { display: none }</style>'
				paths = style .. paths
			end
		end
	end

	-- Build and return the map
	return mw.svg.new()
		:setContent( paths )
		:setAttribute( 'width', svgWidth )
		:setAttribute( 'height', svgHeight )
		:setAttribute( 'viewBox', svgViewBox )
		:setAttribute( 'stroke', 'black' )
		:setAttribute( 'stroke-width', '.2' )
		:setAttribute( 'fill', '#ececec' )
		:setImgAttribute( 'width', width )
		:setImgAttribute( 'height', height )
		:toImage()
end

-- Helper function to get arguments
-- Arguments from Lua calls have priority over parent arguments from templates
function Map.getArg( key, default )
	local frame = mw.getCurrentFrame()
	local parent = frame:getParent()
	for k, value in pairs( parent.args ) do
		if k == key and mw.text.trim( value ) ~= '' then
			return value
		end
	end
	for k, value in pairs( frame.args ) do
		if k == key and mw.text.trim( value ) ~= '' then
			return value
		end
	end
	return default
end

return Map