Module:Sandbox/Bawolff/graph

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

-- Very experimental. Meant to be a wikitext only replacement for {{Graph:lines}}

local p = {}
local canvas = require( 'Module:Sandbox/Bawolff/canvas' )

local function convertToPixelHoriz( value, minValue, maxValue, minPixel, maxPixel ) 
	return ((value-minValue)/(maxValue-minValue))*(maxPixel-minPixel)+minPixel
end
-- Have to reverse, because 0 is top corner not bottom
local function convertToPixelVert( value, minValue, maxValue, minPixel, maxPixel ) 
	return maxPixel - ((value-minValue)/(maxValue-minValue))*(maxPixel-minPixel) - minPixel
end

local function writeVertical( ctx, text, x, y )
	-- TODO this doesn't seem right, maybe module:canvas has a bug.
	ctx:save()
	local height = ctx:getContextAttributes()['height']
	ctx:translate( 0, height )
	ctx:rotate( -math.pi/2 )
	ctx.textBaseline = 'top'
	ctx:fillText( text, y, x )
	ctx:restore()
end

function p.draw( frame )
	assert( frame.args.tabletype or 'tab' == 'tab', "tabletype not supported" )

	local width = frame.args.width or 350
	local height = frame.args.height or 250
	local ctx = canvas.getContext('2d', { width = width, height = height } )

	local vertGutter = frame.args.vertGutter or 40
	local horizGutter = frame.args.horizGutter or 25

	ctx:moveTo( vertGutter, 0 )
	ctx:lineTo( vertGutter, height-horizGutter )
	ctx:lineTo( width, height-horizGutter )

	-- Main example i am testing with: GDP per capita indexed at 1950 - Maddison Project Data (2018) (OWID 2807).tab
	local data = mw.ext.data.get( frame.args.data )
	assert( data ~= nil )

	-- For now, hardcoded first is which line segment, second is year, third is GDP.
	local maxYear = -1000000
	local minYear = 1000000000
	local maxGDP = -10000000000000
	for i, v in ipairs( data.data ) do
		if ( v[2] > maxYear ) then
			maxYear = math.ceil(v[2])
		end
		if ( v[2] < minYear ) then
			minYear = math.floor(v[2])
		end
		if ( v[3] > maxGDP ) then
			maxGDP = math.ceil(v[3])
		end
	end

	ctx.fillStyle = 'var(--color-base,#202122)'
	ctx.font = '10px sans-serif'
	ctx.textAlign = 'end'
	ctx.textBaseline = 'bottom'
	ctx:fillText( '0', vertGutter - 3, height-horizGutter )
	ctx.textBaseline = 'top'
	ctx:fillText( maxGDP, vertGutter -3, 0 )
	
	ctx.textAlign = 'start'
	ctx.textBaseline = 'bottom'
	ctx.font = 'bold 12px sans-serif'
	writeVertical( ctx, frame.args.yAxis or data.schema.fields[3].title, 0, (height - horizGutter)/2 -15 )
	--ctx:fillText( "GDP (nominal)", 0, (height - horizGutter)/2  )
	ctx.font = '10px sans-serif'

	ctx.textBaseline = 'top'
	ctx:fillText( minYear, vertGutter, height - horizGutter + 3 )
	ctx.textAlign = 'end'
	ctx:fillText( maxYear, width, height - horizGutter + 3 )
	ctx.textBaseline = 'bottom'
	ctx.font = 'bold 12px sans-serif'
	ctx:fillText( frame.args.xAxis or data.schema.fields[2].title, (width-vertGutter)/2+vertGutter+10, height )

	-- Because this is hacky, assuming they are ordered by entity, year
	local color = { 'red', 'blue', 'orange', 'green', 'purple', "teal" }
	local curColor = 0
	local curEntity = nil
	for i, v in ipairs( data.data ) do
		local xPt = convertToPixelHoriz( v[2], minYear, maxYear, vertGutter, width )
		local yPt = convertToPixelVert( v[3], 0, maxGDP, horizGutter, height )
		if v[1] ~= curEntity then
			curEntity = v[1]
			curColor = (curColor + 1) % ( #color+1 )
			ctx:stroke()
			ctx:beginPath()
			ctx.strokeStyle = ctx:createWikitextPattern{
				background = color[curColor],
				class = "calculator-field calculator-hideifzero",
				attr = { ["data-calculator-type"] = "passthru", ["data-calculator-formula"] = mw.uri.anchorEncode(curEntity) }
			}
			ctx:moveTo( xPt, yPt )
		else
			ctx:lineTo( xPt, yPt )
		end
	end
	ctx:stroke()
	return tostring(ctx) .. frame:preprocess('<templatestyles src="Template:Calculator-hideifzero/styles.css"/>')
end



return p