File: ia\data\MapData.js
/**
* Contains information about a map file which has been published
* through the InstantAtlas publisher.
*
* @author J Clare
* @class ia.MapData
* @extends ia.EventDispatcher
* @constructor
*/
ia.MapData = function()
{
ia.MapData.baseConstructor.call(this);
this._layerCount = -1;
this._visibleLayers = new Array();
this._callbackFnc = undefined;
this._layerHash = new Object();
this.tilePath = "./";
this.useGoogleMaps = false;
this.path = "";
};
ia.extend(ia.EventDispatcher, ia.MapData);
/**
* The template number.
*
* @property template
* @type String
*/
ia.MapData.prototype.template;
/**
* The version number.
*
* @property version
* @type String
*/
ia.MapData.prototype.version;
/**
* The maps bounding box.
*
* @property mapBBox
* @type ia.BoundingBox
*/
ia.MapData.prototype.mapBBox;
/**
* All layers.
*
* @property layers
* @type ia.LayerBase[]
*/
ia.MapData.prototype.layers;
/**
* Base layers.
*
* @property baseLayers
* @type ia.LayerBase[]
*/
ia.MapData.prototype.baseLayers;
/**
* The current base layer.
*
* @property baseLayer
* @type ia.LayerBase
*/
ia.MapData.prototype.baseLayer;
/**
* None Base layers.
*
* @property noneBaseLayers
* @type ia.LayerBase[]
*/
ia.MapData.prototype.noneBaseLayers;
/**
* Icon layers.
*
* @property iconLayers
* @type ia.LayerBase[]
*/
ia.MapData.prototype.iconLayers;
/**
* The path to tiles for tile layers.
*
* @property tilePath
* @type String
*/
ia.MapData.prototype.tilePath;
/**
* Set to true if using google maps.
*
* @property useGoogleMaps
* @type Boolean
* @default false
*/
ia.MapData.prototype.useGoogleMaps;
/**
* The google map type.
*
* @property googleMapType
* @type String
*/
ia.MapData.prototype.googleMapType;
/**
* The google map api key.
*
* @property googleApiKey
* @type String
*/
ia.MapData.prototype.googleApiKey;
/**
* The url of the data source.
*
* @property url
* @type String
*/
ia.MapData.prototype.url;
/**
* The directory path to the data source.
*
* @property path
* @type String
* @default ""
*/
ia.MapData.prototype.path;
/**
* Loads and parses the source file then calls the given function with the ReportConfig object
* as the first parameter.
*
* @method parseData
* @param {String} url The url to the data.
* @param {Function} callbackFunction The call back function.
*/
ia.MapData.prototype.loadSource = function(url, callbackFunction)
{
var me = this;
this.url = url;
this.path = ia.File.getFileDirectory(url);
ia.File.load(
{
url: url,
dataType: "json",
onSuccess:function(json)
{
me.parseData(json, callbackFunction);
}
});
};
/**
* Returns the layer that corresponds to the id.
*
* @method getLayer
* @param {String} id The layer id.
* @return {ia.LayerBase} The layer for the given id.
*/
ia.MapData.prototype.getLayer = function(id)
{
return this._layerHash[id];
};
/**
* Updates the data.
*
* @method parseData
* @param {JSON} data The raw data.
* @param {Function} callbackFunction The call back function.
*/
ia.MapData.prototype.parseData = function(data, callbackFunction)
{
this._layerCount = -1;
this.useGoogleMaps = false;
this._callbackFnc = callbackFunction;
// delete any previous layers
for (var id in this._layerHash)
{
delete this._layerHash[id];
};
this._visibleLayers = new Array();
this.layers = new Array();
this.baseLayers = new Array();
this.noneBaseLayers = new Array(); // Image and contextual.
var imageLayers = new Array();
var referenceLayers = new Array();
var pointLayers = new Array();
var lineLayers = new Array();
var polygonLayers = new Array();
this._layerHash = new Object();
// Map properties.
if (data.template !== undefined) this.template = data.template;
if (data.version !== undefined) this.version = data.version;
if (data.boundingBox !== undefined)
{
var bb = data.boundingBox.split(" ");
this.mapBBox = new ia.BoundingBox(parseFloat(bb[0]),
parseFloat(bb[1]),
parseFloat(bb[2]),
parseFloat(bb[3]));
}
// Layers.
var layers = data.layers;
if (layers !== undefined)
{
// Check for google layer first.
var n = layers.length;
for (var i = 0; i < n; i++)
{
var layerData = layers[i];
if (layerData.type === "google-layer" && navigator.onLine)
{
this.useGoogleMaps = true;
this.googleMapType = layerData.mapType;
this.googleApiKey = layerData.apiKey;
}
}
for (var i = 0; i < n; i++)
{
var layerData = layers[i];
// Ignore background layers when using google maps.
if (layerData.geometry === "image" && this.useGoogleMaps === false)
{
var layer;
if (layerData.type === "tile-layer")
{
var layer = new ia.TileLayer(this.path+layerData.url);
layer.path = this.tilePath;
this._parseLayerProperties(layerData, layer);
}
else if (layerData.type === "ags-layer" && navigator.onLine)
{
var layer = new ia.AGSLayer();
this._parseLayerProperties(layerData, layer);
}
else if (layerData.type === "wms-layer" && navigator.onLine)
{
var layer = new ia.WMSLayer();
this._parseLayerProperties(layerData, layer);
}
if (layerData.isReference) referenceLayers[referenceLayers.length] = layer;
else imageLayers[imageLayers.length] = layer;
}
if (layerData.geometry !== "image")
{
var layer;
var isFeatureServiceLayer = (layerData.idField !== undefined);
if (layerData.type === "base-layer")
{
if (isFeatureServiceLayer)
layer = new ia.FeatureServiceLayer(layerData.url);
else
layer = new ia.FeatureLayer(this.path+layerData.url);
this.baseLayers[this.baseLayers.length] = layer;
// Make first base layer the active layer.
if (this.baseLayers.length === 1) this.baseLayer = layer;
}
else if (layerData.type === "contextual-layer")
{
if (isFeatureServiceLayer)
layer = new ia.FeatureServiceLayer(layerData.url);
else
layer = new ia.FeatureLayer(this.path+layerData.url);
}
else if (layerData.type === "rss-layer")
{
layer = new ia.GeoRSSLayer(layerData.url);
}
this._parseLayerProperties(layerData, layer);
// Remove extra base layers from visible layers list
// so theyre not loaded at startup. And set to invisible.
if (layerData.type === "base-layer" && this.baseLayers.length > 1)
{
var layer = this._visibleLayers.pop();
// This was originally commented out but it caused a bug
// where if the second base layer was point data you couldnt
// select the first base layer in the map.
layer.setVisible(false);
}
if (layerData.type !== "base-layer")
{
if (layer.geometry === "point") pointLayers[pointLayers.length] = layer;
else if (layer.geometry === "line") lineLayers[lineLayers.length] = layer;
else if (layer.geometry === "polygon") polygonLayers[polygonLayers.length] = layer;
}
this._layerHash[layer.id] = layer;
}
}
// None base layers.
this.noneBaseLayers = imageLayers.concat(polygonLayers)
.concat(lineLayers)
.concat(pointLayers)
.concat(referenceLayers);
// Add the base layers in the correct position.
for (var i = 0; i < this.baseLayers.length; i++)
{
var layer = this.baseLayers[i];
if (layer.geometry === "point") pointLayers.unshift(layer);
else if (layer.geometry === "line") lineLayers.unshift(layer);
else if (layer.geometry === "polygon") polygonLayers.unshift(layer);
}
this.layers = imageLayers.concat(polygonLayers)
.concat(lineLayers)
.concat(pointLayers)
.concat(referenceLayers);
}
if (this.useGoogleMaps && ia.googleMapsLoaded === false)
{
var script = document.createElement("script");
script.type = "text/javascript";
if (this.googleApiKey !== "")
script.src = "http://maps.googleapis.com/maps/api/js?key="+this.googleApiKey+"&sensor=false&callback=ia.initGoogleMaps";
else
script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=ia.initGoogleMaps";
document.body.appendChild(script);
}
else this.loadVisibleLayers();
};
/**
* Parses layer properties.
*
* @method _parseLayerProperties
* @param {JSON} data The raw data.
* @param {ia.LayerBase} layer The layer.
* @private
*/
ia.MapData.prototype._parseLayerProperties = function(data, layer)
{
layer.id = data.id;
layer.name = data.name;
layer.showInLayerList = data.showInLayerList;
// Visibility
if (data.visible !== undefined)
{
layer.setVisible(data.visible);
// Base layer is included whether its visible or not so that we can read in whether its an evaluation version.
if (data.visible === true || data.type === "base-layer") this._visibleLayers[this._visibleLayers.length] = layer;
}
// Feature layers
if (data.minLabelExtent !== undefined) layer.minLabelExtent = data.minLabelExtent;
if (data.maxLabelExtent !== undefined) layer.maxLabelExtent = data.maxLabelExtent;
if (data.showLabels !== undefined) layer.showLabels = data.showLabels;
if (data.showDataTips !== undefined) layer.showDataTips = data.showDataTips;
if (data.iconPath !== undefined && data.iconPath !== "")
{
layer.iconPath = data.iconPath;
if (layer.iconPath.indexOf("http") === -1 && layer.iconPath.indexOf("javascript") === -1)
layer.iconPath = ia.IAS_PATH + layer.iconPath;
}
// AGOL FeatureServer layers.
if (data.idField !== undefined) layer.idField = data.idField;
if (data.nameField !== undefined) layer.nameField = data.nameField;
if (data.srs !== undefined) layer.srs = data.srs;
if (data.featureIds !== undefined) layer.featureIds = data.featureIds;
if (data.objectIds !== undefined) layer.objectIds = data.objectIds;
if (data.boundingBox !== undefined)
{
var bb = data.boundingBox.split(" ");
layer.bBox = new ia.BoundingBox(parseFloat(bb[0]),
parseFloat(bb[1]),
parseFloat(bb[2]),
parseFloat(bb[3]));
}
// Styles
layer.geometry = data.geometry;
if (data.geometry !== "image")
{
var style = layer.style;
if (data.symbolSize !== undefined) layer.symbolSize = data.symbolSize;
if (data.fillColor !== undefined)
{
// Convert to rgba.
style.fillStyle = ia.Color.toRGBA(data.fillColor, data.fillOpacity);
}
if (data.borderThickness !== undefined) style.lineWidth = data.borderThickness;
if (data.borderColor !== undefined)
{
// Convert to rgba.
var alpha = 1;
if (style.lineWidth === 0) alpha = 0
style.strokeStyle = ia.Color.toRGBA(data.borderColor, alpha);
}
layer.style = style;
}
// WMS / AGS layers.
if (data.url !== undefined) layer.url = data.url;
if (data.layers !== undefined) layer.layers = data.layers;
if (data.srs !== undefined) layer.srs = data.srs;
if (data.version !== undefined) layer.version = data.version;
if (data.params !== undefined) layer.params = data.params;
if (data.requiresAxisSwitch !== undefined) layer.requiresAxisSwitch = data.requiresAxisSwitch;
// Tile layers
if (data.minExtent !== undefined) layer.minExtent = data.minExtent;
if (data.maxExtent !== undefined) layer.maxExtent = data.maxExtent;
};
/**
* Loads the visible data layers.
*
* @method loadVisibleLayers
*/
ia.MapData.prototype.loadVisibleLayers = function()
{
this._layerCount++;
var me = this;
// Ensures all layers are loaded before moving on.
if (this._visibleLayers.length > this._layerCount)
{
var layer = this._visibleLayers[this._layerCount];
layer.addEventListener(ia.Event.LAYER_READY, function ()
{
me.loadVisibleLayers();
});
layer.loadSource();
}
else
{
// callback function
this._callbackFnc.call(null, this);
me.dispatchEvent(new ia.DataEvent(ia.DataEvent.MAP_DATA_CHANGED, me));
}
};