Show:

File: ia\factories\ScatterPlotFactory.js

/** 
 * Factory for creating scatter plots.
 *
 * @author J Clare
 * @class ia.ScatterPlotFactory
 * @param {ia.ComponentConfig} config The component config.
 * @param {ia.Report} report The report object.
 * @param {ia.DataGroup[]} dataGroups The associated data groups.
 * @param {ia.InteractionGroup} interactionGroup The associated interaction group.
 * @param {ia.InteractionGroup} comparisonInteractionGroup The associated comparison interaction group.
 */
ia.ScatterPlotFactory = function(config, report, dataGroups, interactionGroup, comparisonInteractionGroup)
{
	var me = this;

	// Event handlers.
	var colorDataGroup;
	var sizeDataGroup;
	var xDataGroup;
	var yDataGroup;
	if (dataGroups.length === 1)
	{
		xDataGroup = dataGroups[0];
		yDataGroup = dataGroups[0];
	}
	else if (dataGroups.length === 2)
	{
		xDataGroup = dataGroups[0];
		yDataGroup = dataGroups[1];
	}
	else if (dataGroups.length === 3)
	{
		colorDataGroup = dataGroups[0];
		xDataGroup = dataGroups[1];
		yDataGroup = dataGroups[2];
	}
	else if (dataGroups.length === 4)
	{
		colorDataGroup = dataGroups[0];
		sizeDataGroup = dataGroups[1];
		xDataGroup = dataGroups[2];
		yDataGroup = dataGroups[3];
	}

	if (colorDataGroup)
	{
		colorDataGroup.addEventListener(ia.Event.THEMATIC_CHANGED, function(event)
		{
			triggerUpdate();
		});
	}
	if (sizeDataGroup)
	{
		sizeDataGroup.addEventListener(ia.Event.THEMATIC_CHANGED, function(event)
		{
			triggerUpdate();
		});
	}
	xDataGroup.addEventListener(ia.Event.THEMATIC_CHANGED, function(event)
	{
		triggerUpdate();
	});
	yDataGroup.addEventListener(ia.Event.THEMATIC_CHANGED, function(event)
	{
		triggerUpdate();
	})

	// This code executes every time the comparison selection has changed.
	comparisonInteractionGroup.addEventListener(ia.InteractionEvent.SELECTION_CHANGED, function(event) 
	{
		triggerUpdate();
	});

	// Trigger a chart update. Use this to prevent multiple rendering of the chart
	// when multiple data groups are updated at the same time.
	var timeout = null;
	function triggerUpdate()
	{
		if (!timeout) 
		{
			timeout = setTimeout(function()
			{
				me.update();
				me.render();
			}, 250);
		}
	};


	// Components.

	// Panel.
	var panel = report.getWidget(config.id); 
	panel.exportFunction = function() {chart.exportData(report.config.getProperty("saveImageText"));}; 

	// Chart.
	var chart;
	var layer;
	var comparisonLayer;
	var sizeThematic;

	/** 
	 * Builds the component.
	 *
	 * @method build
 	 * @param {Function} callbackFunction Called on completion of function, with the component id as the parameter.
	 */
	this.build = function(callbackFunction)
	{
		// Empty panel.
		panel.content.empty();

		// Chart.
		chart = new ia.Plot(config.id);
		chart.formatter = report.locale.formatter;
		panel.append(chart.container);
		report.addComponent(config.id, chart);

		// Layer.
		layer = new ia.PlotLayer();
		layer.setVisible(true);
		layer.interactive = true;
		layer.tipFunction = function(item)
		{
			var s = ia.tipFunction(item, config.id);
			return s;
		};
		layer.highlightColor = report.highlightColor;
		layer.selectionColor = report.selectionColor;
		chart.addLayer(layer);
		interactionGroup.addComponent(layer);

		// Comparison Layer.
		comparisonLayer = new ia.ComparisonPlotLayer();
		comparisonLayer.setVisible(true);
		comparisonLayer.interactive = true;
		comparisonLayer.tipFunction = function(item)
		{
			var s = ia.tipFunction(item, config.id);
			return s;
		};
		comparisonLayer.highlightColor = report.highlightColor;
		comparisonLayer.selectionColor = report.selectionColor;	
		chart.addLayer(comparisonLayer);		
		comparisonInteractionGroup.addComponent(comparisonLayer);

		// Funnel plot.
		for (var i = 1; i < 6; i++) 
		{
			var xField = config.getProperty("line_xfield_"+i);
			var yField = config.getProperty("line_yfield_"+i);
			if (xField !== undefined && yField !== undefined )
			{
				var lineLayer = new ia.LineLayer();
				lineLayer.highlightColor = report.highlightColor;
				lineLayer.selectionColor = report.selectionColor;
				lineLayer.style.strokeStyle = config.getProperty("line_color_"+i);
				lineLayer.setVisible(true);
				lineLayer.interactive = true;			

				(function() // Execute immediately
				{ 
					var label =  config.getProperty("line_label_"+i)
					lineLayer.tipFunction = function(item)
					{
						return label;
					};
				})();

				chart.addLayer(lineLayer);
			}
		}

		// Size thematic for single variable reports.
		if (config.getProperty("sizeData") !== undefined)
		{
			sizeThematic = new ia.Thematic();
			sizeThematic.setDataField(config.getProperty("sizeData"));
			sizeThematic.classificationName = ia.Thematic.CONTINUOUS;
			var pal = sizeThematic.numericClassifier.sizePalette;
			pal.minSize = config.getProperty("minBubbleSize");
			pal.maxSize = config.getProperty("maxBubbleSize");
			sizeThematic.categoricClassifier.symbolSize = 8;
		}
				
		// Wait till charts ready before returning.
		chart.addEventListener(ia.Event.MAP_READY, function() 
		{
			if (callbackFunction !== undefined) callbackFunction.call(null, config.id);
		});
	};

	/** 
	 * Updates the component.
	 *
	 * @method update
 	 * @param {Function} callbackFunction Called on completion of function, with the component id as the parameter.
	 */
	this.update = function(callbackFunction)
	{
		timeout = null

		layer.pointSize = config.getProperty("minBubbleSize") || config.getProperty("pointSize");
		layer.xDataField = config.getProperty("xData");
		layer.yDataField = config.getProperty("yData");
		layer.showCorrelationLine = config.getProperty("showCorrelationLine");

		comparisonLayer.pointSize = config.getProperty("minBubbleSize") || config.getProperty("pointSize");
		comparisonLayer.xDataField = config.getProperty("xData");
		comparisonLayer.yDataField = config.getProperty("yData");
		comparisonLayer.displayAll = config.getProperty("showComparison");

		var colorIndicator;
		if (colorDataGroup) colorIndicator = colorDataGroup.indicator;
		var sizeIndicator;
		if (sizeDataGroup) sizeIndicator = sizeDataGroup.indicator;
		var xIndicator = xDataGroup.indicator;
		var yIndicator = yDataGroup.indicator;

		// Update size data.
		var sizeData;
		if (sizeDataGroup && config.getProperty("sizeData") !== undefined) 
		{
			if (sizeDataGroup.getFilteredFeatures().length > 0) 
				sizeData = sizeIndicator.getData(sizeDataGroup.getFilteredFeatures());
			else 
				sizeData = sizeIndicator.getData();

			sizeThematic.setData(sizeData);
			sizeThematic.commitChanges();
		}

		var xDataType = xIndicator.getDataType(layer.xDataField);
		var yDataType = yIndicator.getDataType(layer.yDataField);

		if (xDataType === ia.Thematic.CATEGORIC || yDataType === ia.Thematic.CATEGORIC)
		{
			// Hide.
			chart.hide();
			panel.text(config.getProperty("notAvailableText"));
		}
		else
		{
			// Check for custom fixed values.
			chart.fixedMinValueX = xIndicator.getProperty(config.getProperty("minChartValueX"));
			chart.fixedMaxValueX = xIndicator.getProperty(config.getProperty("maxChartValueX"));
			chart.fixedMinValueY = yIndicator.getProperty(config.getProperty("minChartValueY"));
			chart.fixedMaxValueY = yIndicator.getProperty(config.getProperty("maxChartValueY"));

			if (chart.fixedMinValueX !== undefined && 
			chart.fixedMaxValueX !== undefined && 
			chart.fixedMinValueY !== undefined && 
			chart.fixedMaxValueY !== undefined)
			{
				chart.useTightLabels = true;
			}
			else
			{
				// If theres no custom values then check for config.xml fixed values.
				if (config.getProperty("useFixedValues"))
				{
					chart.useTightLabels = true;
					chart.fixedMinValueX = config.getProperty("fixedMinValueX");
					chart.fixedMaxValueX = config.getProperty("fixedMaxValueX");
					chart.fixedMinValueY = config.getProperty("fixedMinValueY");
					chart.fixedMaxValueY = config.getProperty("fixedMaxValueY");
				}
				else chart.useTightLabels = config.getProperty("useTightLabels");
			}

			// Axis titles.
			var xTitle = config.getProperty("xAxisTitle");
			if (xTitle) chart.xAxisTitle = report.textSubstitution.formatMessage(xTitle);
			var yTitle = config.getProperty("yAxisTitle");
			if (yTitle) chart.yAxisTitle = report.textSubstitution.formatMessage(yTitle);

			// Show.
			chart.show();
			panel.text("");

			layer.setXData(xDataGroup.indicatorData);
			layer.setYData(yDataGroup.indicatorData);
			if (colorDataGroup) layer.setColorData(colorDataGroup.indicatorData);
			if (sizeData && config.getProperty("sizeData") !== undefined) 
				layer.setSizeData(sizeData);

			comparisonLayer.setXData(xDataGroup.comparisonData);
			comparisonLayer.setYData(yDataGroup.comparisonData);
			if (colorDataGroup) comparisonLayer.setColorData(colorDataGroup.comparisonData);

			// Funnel plot.
			var xList = [];
			var yList = [];
			for (var i = 1; i < 6; i++) 
			{
				var xField = config.getProperty("line_xfield_"+i);
				var yField = config.getProperty("line_yfield_"+i);
				if (xField !== undefined && yField !== undefined )
				{
					xList.push(xField);
					yList.push(yField);
				}
			}
			var layers = chart.getLayers();
			for (var i = 0; i < xList.length; i++) 
			{
				var l = layers[i+2]; // Skip 2 bubble layers.

				var xProp = xIndicator.getProperty(xList[i]);
				var yProp = yIndicator.getProperty(yList[i]);
				if (xProp !== undefined && yProp !== undefined )
				{
					var xValues = xProp.split(",");

					var yValues = new Array();
					if (ia.isNumber(yProp)) // National avg is single values.
					{
						for (var j = 0; j < xValues.length; j++)  {yValues.push(yProp); }
					}
					else // All others are arrays,
					{
						yValues = yProp.split(",");
					}

					l.setXData(xValues);
					l.setYData(yValues);
				}
			}

			// Correlation.
			var p = xIndicator.precision || 2;
			var f = report.locale.formatter;
			var cinf = layer.correlationInfo;
			report.textSubstitution.setVariable("correlationCoeff", f.format(cinf.correlationCoeff, p));
			report.textSubstitution.setVariable("rSquare", f.format(cinf.rSquare, p));
			report.textSubstitution.setVariable("gradient", f.format(cinf.gradient, p));
			report.textSubstitution.setVariable("intercept", f.format(cinf.intercept, p));
			report.updateDynamicText(report.textSubstitution);
		}

		if (callbackFunction !== undefined) callbackFunction.call(null, config.id);
	};

	/** 
	 * Renders the component.
	 *
	 * @method render
 	 * @param {Function} callbackFunction Called on completion of function, with the component id as the parameter.
	 */
	this.render = function(callbackFunction)
	{
		chart.render();

		if (callbackFunction !== undefined) callbackFunction.call(null, config.id);
	};
};