Show:

File: ia\factories\MapFactory.js

/** 
 * Factory for creating maps.
 *
 * @author J Clare
 * @class ia.MapFactory
 * @param {ia.ComponentConfig} config The component config.
 * @param {ia.Report} report The report object.
 * @param {Object} componentGroup  Hash table containing the Data and  Interaction Groups that the component belongs to:
 * {dataGroup:ia.DataGroup, interactionGroup:ia.InteractionGroup, comparisonInteractionGroup:ia.InteractionGroup}.
 */
ia.MapFactory = function(config, report, componentGroup)
{
	var me = this;

	// Data and Interaction groups that the components belongs to.
	var interactionGroup = componentGroup.interactionGroup;
	var dataGroup = componentGroup.dataGroup;
	var comparisonInteractionGroup = componentGroup.comparisonInteractionGroup;
	
	// This code executes every time the map data has changed.
	dataGroup.mapData.addEventListener(ia.DataEvent.MAP_DATA_CHANGED, function(event)
	{
		onMapDataChange();
	});

	// This code executes every time the data groups thematic has changed.
	// The thematic will change after any data change so only render
	// here to avoid multiple rendering.
	dataGroup.addEventListener(ia.Event.THEMATIC_CHANGED, function(event)
	{
		if (buildingMap) thematicChanged = true;
		else
		{
			me.update();
			me.render();
		}
	});

	// This code executes every time a filter has changed.
	dataGroup.addEventListener(ia.FilterEvent.FILTER_CHANGED, function(event)
	{
		if (event.filterFeatures.length > 0)
			activeMap.zoomToFeatures(event.filterFeatures, [mapData.baseLayer]); 	// Zoom to filtered features.
		else 
			activeMap.controller.zoomFull(); 										// Zoom full when filter is removed.
	});

	// Panel.
	var panel;

	// Map.
	var thematicChanged = false; 	// Indicates that the thematic has changed.
	var buildingMap = false; 		// Indicates that the map is currently being built.
	var mapData; 					// ia.MapData
	var map; 						// ia.Map
	var gMap;						// ia.GoogleMap
	var activeMap; 					// Can be and ia.Map or ia.GoogleMap.

	// Bounding box set from url parameters.
	var bBoxFromParams;
	if (report.url.params["bbox"+dataGroup.suffix])
	{
		var bb = report.url.params["bbox"+dataGroup.suffix].split(",");
		bBoxFromParams = new ia.BoundingBox(parseFloat(bb[0]),
				parseFloat(bb[1]),
				parseFloat(bb[2]),
				parseFloat(bb[3]));  
	}

	// Updates the map when the map data has changed
	function onMapDataChange()
	{
		// Second layer of double base layer reports are added to same map as first layer.
		if (config === undefined) 
		{
			updateDoubleBaseLayer();
		}
		else
		{	
			// Remove any previous layers from the interaction group.
			var mapLayers = map.getLayers();
			for (var i = 0; i < mapLayers.length; i++)
			{
				var layer = mapLayers[i];
				interactionGroup.removeComponent(layer);
			}

			// Remove map layers.
			map.removeLayers();

			// Set special properties for baselayers.
			for (var i = 0; i < mapData.baseLayers.length; i++)
			{
				var layer = mapData.baseLayers[i];

				layer.dataLabel = report.config.getComponent(config.id).getProperty("tip");
				layer.interactive = true;
				layer.tipFunction = function(item)
				{
					var s = ia.tipFunction(item, config.id);
					return s;
				};

				layer.highlightColor = report.highlightColor;
				layer.selectionColor = report.selectionColor;
				layer.highlightOpacity = report.highlightOpacity;
				layer.selectionOpacity = report.selectionOpacity;

				interactionGroup.addComponent(layer);
			}

			// Add layers to map first to maintain layer order.
			for (var i = 0; i < mapData.layers.length; i++)
			{
				var layer = mapData.layers[i];
				map.addLayer(mapData.layers[i]);
			}

			// Set the bounding box.
			map.controller.defaultBBox = mapData.mapBBox;
			if (!mapData.useGoogleMaps) 
			{
				if (bBoxFromParams) map.controller.zoomToBBox(bBoxFromParams);
				else map.controller.zoomFull();
			}
			else map.render();
			bBoxFromParams = undefined;

			if (thematicChanged)
			{
				thematicChanged = false;
				me.update();
				me.render();
			}
		}
	};

	// Updates the double base layer.
	function updateDoubleBaseLayer()
	{
		map = report.getComponent("map");
		activeMap = report.getComponent("activeMap");

		mapData.noneBaseLayers = []; // Remove non-base layers from layer list.
		var layer = mapData.baseLayers[1];

		if (report.config.template === ia.DOUBLE_BASELAYER_REPORT)
		{
			layer.dataLabel = report.config.getComponent("map").getProperty("tip"+dataGroup.suffix);
			layer.highlightColor = report.highlightColor;
			layer.selectionColor = report.selectionColor;
			layer.highlightOpacity = report.highlightOpacity;
			layer.selectionOpacity = report.selectionOpacity;
			layer.interactive = true;
			layer.dataLabel = report.config.getComponent("map").getProperty("tip"+dataGroup.suffix);
			layer.tipFunction = function(item)
			{
				var s = ia.tipFunction(item, "map", dataGroup.suffix);
				return s;
			};

			interactionGroup.addComponent(layer);
		}
		else  if (report.config.template === ia.DOUBLE_BASELAYER_REPORT_NEW)
		{
			layer.showDataTips = false;
			layer.showLabels = false;
		}
		map.addLayer(layer);
	};

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

		// Second layer of double base layer reports are added to same map as first layer.
		if (config === undefined) 
		{
			updateDoubleBaseLayer();
			if (callbackFunction !== undefined) callbackFunction.call(null, config ? config.id : 'map'); // Return
		}
		else
		{	
			buildingMap = true;

			// Panel.
			panel = report.getWidget(config.id); 
			panel.exportFunction = function() {map.exportData(report.config.getProperty("saveImageText"));}; 
	
			// Empty panel.
			panel.content.empty();

			// Map.
			map = new ia.Map(config.id);
			report.addComponent(config.id, map);

			// Add listeners for map bounding box events.
			function bBoxEventHandler(event)
			{
				// Update the map bbox url param.
				var bb = map.getBBox();
				report.url.params["bbox"+dataGroup.suffix] = bb.getXMin() + "," + bb.getYMin()  + "," + bb.getXMax() + "," + bb.getYMax();
			}
			map.addEventListener(ia.BBoxEvent.BBOX_TRANSLATE, bBoxEventHandler);
			map.addEventListener(ia.BBoxEvent.BBOX_SCALE, bBoxEventHandler);

			var minZoom = config.getProperty("minZoomAllowed"); 	
			var maxZoom = config.getProperty("maxZoomAllowed"); 

			if (mapData.useGoogleMaps) 
			{
				gMap = new ia.GoogleMap(config.id, 
							mapData.googleMapType, 
							mapData.googleApiKey, 
							minZoom, 
							maxZoom,
							config.getProperty("googleGreyscaleText"),
							config.getProperty("googleOffText"));
				activeMap = gMap;

				gMap.addEventListener(ia.Event.MAP_READY, function()
				{
					// Set map bounding box.
					if (mapData.mapBBox.getXMin() < -20000000) mapData.mapBBox.setXMin(-20000000);
					if (mapData.mapBBox.getXMax() > 20000000) mapData.mapBBox.setXMax(20000000);

					gMap.controller.defaultBBox = mapData.mapBBox;
					if (bBoxFromParams) gMap.controller.zoomToBBox(bBoxFromParams);
					else gMap.controller.zoomFull();

					// Add IA Map as an overlay.
					gMap.addMapOverlay(map);
				});

				report.addComponent("gMap"+dataGroup.suffix, gMap);
			}
			else
			{
				if (minZoom !== -1) map.minZoom = minZoom;
				if (maxZoom !== -1) map.maxZoom = maxZoom
				map.useNavigation(true);
				activeMap = map;
			}

			map.addEventListener(ia.Event.MAP_READY, function()
			{
				onMapDataChange();
				buildingMap = false;
				if (callbackFunction !== undefined) callbackFunction.call(null, config.id);
			});

			// Append the map panel - this will trigger the MAP_READY event.
			report.addComponent("activeMap"+dataGroup.suffix, activeMap);
			panel.append(activeMap.container);

			// Map copyright.
			var copyright = $j('<div id="map-copyright" class="ia-map-copyright"></div>');
			var txt = config.getProperty("copyrightText");
			if (txt !== "" && txt !== undefined) 
			{
				copyright.html(txt);
				panel.append(copyright);
			}

			// Evaluation message.
			if (report.evaluation) report.displayEvaluationMessage(panel);

			// Map Tools.
			var includeSearchTool = config.getProperty("includeSearchTool");
			if (includeSearchTool === undefined) includeSearchTool = false;

			if (includeSearchTool && dataGroup.suffix === "")
			{
				// For geocoding autocomplete ui.
				var script = document.createElement("script");
				script.type = "text/javascript";
				script.src = "http://code.jquery.com/ui/1.9.2/jquery-ui.js";
				document.body.appendChild(script);
			}

			var mapTools = new ia.MapTools(activeMap, dataGroup, interactionGroup, mapData.useGoogleMaps, includeSearchTool);
			mapTools.clearButtonText = config.getProperty("clearButtonText");
			mapTools.filterButtonText = config.getProperty("filterButtonText");
			mapTools.filterFunction = function()
			{
				if (interactionGroup.getSelection().length > 0)
					dataGroup.setFilteredFeatures(interactionGroup.getSelection());
				else if (dataGroup.getFilteredFeatures().length > 0)
					dataGroup.clearFilter();
			}
			mapTools.clearFunction = function()
			{
				interactionGroup.clearSelection();
			}

			panel.append(mapTools.container);
			report.addComponent("mapTools"+dataGroup.suffix, mapTools);
			mapTools.render();
		}
	};

	/** 
	 * Updates the component.
	 *
	 * @method update
 	 * @param {Function} callbackFunction Called on completion of function, with the component id as the parameter.
	 */
	this.update = function(callbackFunction)
	{
		mapData.baseLayer.setData(dataGroup.indicatorData); 
		if (callbackFunction !== undefined) callbackFunction.call(null, config ? config.id : 'map');
	};

	/** 
	 * Renders the component.
	 *
	 * @method render
 	 * @param {Function} callbackFunction Called on completion of function, with the component id as the parameter.
	 */
	this.render = function(callbackFunction)
	{
		map.render();
		
		if (callbackFunction !== undefined) callbackFunction.call(null, config ? config.id : 'map');
	};
};