/*global define*/ define([ '../Core/Cartesian3', '../Core/Color', '../Core/createGuid', '../Core/defaultValue', '../Core/defined', '../Core/defineProperties', '../Core/deprecationWarning', '../Core/DeveloperError', '../Core/Event', '../Core/getFilenameFromUri', '../Core/loadJson', '../Core/PinBuilder', '../Core/RuntimeError', '../Scene/VerticalOrigin', '../ThirdParty/topojson', '../ThirdParty/when', './BillboardGraphics', './CallbackProperty', './ColorMaterialProperty', './ConstantPositionProperty', './ConstantProperty', './DataSource', './EntityCollection', './PolygonGraphics', './PolylineGraphics' ], function( Cartesian3, Color, createGuid, defaultValue, defined, defineProperties, deprecationWarning, DeveloperError, Event, getFilenameFromUri, loadJson, PinBuilder, RuntimeError, VerticalOrigin, topojson, when, BillboardGraphics, CallbackProperty, ColorMaterialProperty, ConstantPositionProperty, ConstantProperty, DataSource, EntityCollection, PolygonGraphics, PolylineGraphics) { "use strict"; function defaultCrsFunction(coordinates) { return Cartesian3.fromDegrees(coordinates[0], coordinates[1], coordinates[2]); } var crsNames = { 'urn:ogc:def:crs:OGC:1.3:CRS84' : defaultCrsFunction, 'EPSG:4326' : defaultCrsFunction }; var crsLinkHrefs = {}; var crsLinkTypes = {}; var defaultMarkerSize = 48; var defaultMarkerSymbol; var defaultMarkerColor = Color.ROYALBLUE; var defaultStroke = Color.YELLOW; var defaultStrokeWidth = 2; var defaultFill = Color.fromBytes(255, 255, 0, 100); var defaultStrokeWidthProperty = new ConstantProperty(defaultStrokeWidth); var defaultStrokeMaterialProperty = ColorMaterialProperty.fromColor(defaultStroke); var defaultFillMaterialProperty = ColorMaterialProperty.fromColor(defaultFill); var sizes = { small : 24, medium : 48, large : 64 }; var simpleStyleIdentifiers = ['title', 'description', // 'marker-size', 'marker-symbol', 'marker-color', 'stroke', // 'stroke-opacity', 'stroke-width', 'fill', 'fill-opacity']; var stringifyScratch = new Array(4); function describe(properties, nameProperty) { var html = ''; for ( var key in properties) { if (properties.hasOwnProperty(key)) { if (key === nameProperty || simpleStyleIdentifiers.indexOf(key) !== -1) { continue; } var value = properties[key]; if (defined(value)) { if (typeof value === 'object') { html += ''; } else { html += ''; } } } } html += '
' + key + '' + describe(value) + '
' + key + '' + value + '
'; return html; } function createDescriptionCallback(properties, nameProperty) { var description; return function(time, result) { if (!defined(description)) { description = describe(properties, nameProperty); } return description; }; } //GeoJSON specifies only the Feature object has a usable id property //But since "multi" geometries create multiple entity, //we can't use it for them either. function createObject(geoJson, entityCollection) { var id = geoJson.id; if (!defined(id) || geoJson.type !== 'Feature') { id = createGuid(); } else { var i = 2; var finalId = id; while (defined(entityCollection.getById(finalId))) { finalId = id + "_" + i; i++; } id = finalId; } var entity = entityCollection.getOrCreateEntity(id); var properties = geoJson.properties; if (defined(properties)) { entity.addProperty('properties'); entity.properties = properties; var nameProperty; //Check for the simplestyle specified name first. var name = properties.title; if (defined(name)) { entity.name = name; nameProperty = 'title'; } else { //Else, find the name by selecting an appropriate property. //The name will be obtained based on this order: //1) The first case-insensitive property with the name 'title', //2) The first case-insensitive property with the name 'name', //3) The first property containing the word 'title'. //4) The first property containing the word 'name', var namePropertyPrecedence = Number.MAX_VALUE; for ( var key in properties) { if (properties.hasOwnProperty(key) && properties[key]) { var lowerKey = key.toLowerCase(); if (namePropertyPrecedence > 1 && lowerKey === 'title') { namePropertyPrecedence = 1; nameProperty = key; break; } else if (namePropertyPrecedence > 2 && lowerKey === 'name') { namePropertyPrecedence = 2; nameProperty = key; } else if (namePropertyPrecedence > 3 && /title/i.test(key)) { namePropertyPrecedence = 3; nameProperty = key; } else if (namePropertyPrecedence > 4 && /name/i.test(key)) { namePropertyPrecedence = 4; nameProperty = key; } } } if (defined(nameProperty)) { entity.name = properties[nameProperty]; } } var description = properties.description; if (!defined(description)) { entity.description = new CallbackProperty(createDescriptionCallback(properties, nameProperty), true); } else { entity.description = new ConstantProperty(description); } } return entity; } function coordinatesArrayToCartesianArray(coordinates, crsFunction) { var positions = new Array(coordinates.length); for (var i = 0; i < coordinates.length; i++) { positions[i] = crsFunction(coordinates[i]); } return positions; } // GeoJSON processing functions function processFeature(dataSource, feature, notUsed, crsFunction, options) { if (!defined(feature.geometry)) { throw new RuntimeError('feature.geometry is required.'); } if (feature.geometry === null) { //Null geometry is allowed, so just create an empty entity instance for it. createObject(feature, dataSource._entityCollection); } else { var geometryType = feature.geometry.type; var geometryHandler = geometryTypes[geometryType]; if (!defined(geometryHandler)) { throw new RuntimeError('Unknown geometry type: ' + geometryType); } geometryHandler(dataSource, feature, feature.geometry, crsFunction, options); } } function processFeatureCollection(dataSource, featureCollection, notUsed, crsFunction, options) { var features = featureCollection.features; for (var i = 0, len = features.length; i < len; i++) { processFeature(dataSource, features[i], undefined, crsFunction, options); } } function processGeometryCollection(dataSource, geoJson, geometryCollection, crsFunction, options) { var geometries = geometryCollection.geometries; for (var i = 0, len = geometries.length; i < len; i++) { var geometry = geometries[i]; var geometryType = geometry.type; var geometryHandler = geometryTypes[geometryType]; if (!defined(geometryHandler)) { throw new RuntimeError('Unknown geometry type: ' + geometryType); } geometryHandler(dataSource, geoJson, geometry, crsFunction, options); } } function createPoint(dataSource, geoJson, crsFunction, coordinates, options) { var symbol = options.markerSymbol; var color = options.markerColor; var size = options.markerSize; var properties = geoJson.properties; if (defined(properties)) { var cssColor = properties['marker-color']; if (defined(cssColor)) { color = Color.fromCssColorString(cssColor); } size = defaultValue(sizes[properties['marker-size']], size); symbol = defaultValue(properties['marker-symbol'], symbol); } stringifyScratch[0] = symbol; stringifyScratch[1] = color; stringifyScratch[2] = size; var id = JSON.stringify(stringifyScratch); var dataURLPromise = dataSource._pinCache[id]; if (!defined(dataURLPromise)) { var canvasOrPromise; if (defined(symbol)) { if (symbol.length === 1) { canvasOrPromise = dataSource._pinBuilder.fromText(symbol.toUpperCase(), color, size); } else { canvasOrPromise = dataSource._pinBuilder.fromMakiIconId(symbol, color, size); } } else { canvasOrPromise = dataSource._pinBuilder.fromColor(color, size); } dataURLPromise = when(canvasOrPromise, function(canvas) { return canvas.toDataURL(); }); dataSource._pinCache[id] = dataURLPromise; } dataSource._promises.push(when(dataURLPromise, function(dataUrl) { var billboard = new BillboardGraphics(); billboard.verticalOrigin = new ConstantProperty(VerticalOrigin.BOTTOM); billboard.image = new ConstantProperty(dataUrl); var entity = createObject(geoJson, dataSource._entityCollection); entity.billboard = billboard; entity.position = new ConstantPositionProperty(crsFunction(coordinates)); })); } function processPoint(dataSource, geoJson, geometry, crsFunction, options) { createPoint(dataSource, geoJson, crsFunction, geometry.coordinates, options); } function processMultiPoint(dataSource, geoJson, geometry, crsFunction, options) { var coordinates = geometry.coordinates; for (var i = 0; i < coordinates.length; i++) { createPoint(dataSource, geoJson, crsFunction, coordinates[i], options); } } function createLineString(dataSource, geoJson, crsFunction, coordinates, options) { var material = options.strokeMaterialProperty; var widthProperty = options.strokeWidthProperty; var properties = geoJson.properties; if (defined(properties)) { var width = properties['stroke-width']; if (defined(width)) { widthProperty = new ConstantProperty(width); } var color; var stroke = properties.stroke; if (defined(stroke)) { color = Color.fromCssColorString(stroke); } var opacity = properties['stroke-opacity']; if (defined(opacity) && opacity !== 1.0) { if (!defined(color)) { color = material.color.clone(); } color.alpha = opacity; } if (defined(color)) { material = ColorMaterialProperty.fromColor(color); } } var polyline = new PolylineGraphics(); polyline.material = material; polyline.width = widthProperty; polyline.positions = new ConstantProperty(coordinatesArrayToCartesianArray(coordinates, crsFunction)); var entity = createObject(geoJson, dataSource._entityCollection); entity.polyline = polyline; } function processLineString(dataSource, geoJson, geometry, crsFunction, options) { createLineString(dataSource, geoJson, crsFunction, geometry.coordinates, options); } function processMultiLineString(dataSource, geoJson, geometry, crsFunction, options) { var lineStrings = geometry.coordinates; for (var i = 0; i < lineStrings.length; i++) { createLineString(dataSource, geoJson, crsFunction, lineStrings[i], options); } } function createPolygon(dataSource, geoJson, crsFunction, coordinates, options) { var outlineColorProperty = options.strokeMaterialProperty.color; var material = options.fillMaterialProperty; var widthProperty = options.strokeWidthProperty; var properties = geoJson.properties; if (defined(properties)) { var width = properties['stroke-width']; if (defined(width)) { widthProperty = new ConstantProperty(width); } var color; var stroke = properties.stroke; if (defined(stroke)) { color = Color.fromCssColorString(stroke); } var opacity = properties['stroke-opacity']; if (defined(opacity) && opacity !== 1.0) { if (!defined(color)) { color = options.strokeMaterialProperty.color.clone(); } color.alpha = opacity; } if (defined(color)) { outlineColorProperty = new ConstantProperty(color); } var fillColor; var fill = properties.fill; if (defined(fill)) { fillColor = Color.fromCssColorString(fill); fillColor.alpha = material.color.alpha; } opacity = properties['fill-opacity']; if (defined(opacity) && opacity !== material.color.alpha) { if (!defined(fillColor)) { fillColor = material.color.clone(); } fillColor.alpha = opacity; } if (defined(fillColor)) { material = ColorMaterialProperty.fromColor(fillColor); } } var polygon = new PolygonGraphics(); polygon.outline = new ConstantProperty(true); polygon.outlineColor = outlineColorProperty; polygon.outlineWidth = widthProperty; polygon.material = material; polygon.positions = new ConstantProperty(coordinatesArrayToCartesianArray(coordinates, crsFunction)); if (coordinates.length > 0 && coordinates[0].length > 2) { polygon.perPositionHeight = new ConstantProperty(true); } var entity = createObject(geoJson, dataSource._entityCollection); entity.polygon = polygon; } function processPolygon(dataSource, geoJson, geometry, crsFunction, options) { createPolygon(dataSource, geoJson, crsFunction, geometry.coordinates[0], options); } function processMultiPolygon(dataSource, geoJson, geometry, crsFunction, options) { var polygons = geometry.coordinates; for (var i = 0; i < polygons.length; i++) { createPolygon(dataSource, geoJson, crsFunction, polygons[i][0], options); } } function processTopology(dataSource, geoJson, geometry, crsFunction, options) { for ( var property in geometry.objects) { if (geometry.objects.hasOwnProperty(property)) { var feature = topojson.feature(geometry, geometry.objects[property]); var typeHandler = geoJsonObjectTypes[feature.type]; typeHandler(dataSource, feature, feature, crsFunction, options); } } } var geoJsonObjectTypes = { Feature : processFeature, FeatureCollection : processFeatureCollection, GeometryCollection : processGeometryCollection, LineString : processLineString, MultiLineString : processMultiLineString, MultiPoint : processMultiPoint, MultiPolygon : processMultiPolygon, Point : processPoint, Polygon : processPolygon, Topology : processTopology }; var geometryTypes = { GeometryCollection : processGeometryCollection, LineString : processLineString, MultiLineString : processMultiLineString, MultiPoint : processMultiPoint, MultiPolygon : processMultiPolygon, Point : processPoint, Polygon : processPolygon, Topology : processTopology }; /** * A {@link DataSource} which processes both * {@link http://www.geojson.org/|GeoJSON} and {@link https://github.com/mbostock/topojson|TopoJSON} data. * {@link https://github.com/mapbox/simplestyle-spec|Simplestyle} properties will also be used if they * are present. * * @alias GeoJsonDataSource * @constructor * * @param {String} [name] The name of this data source. If undefined, a name will be taken from * the name of the GeoJSON file. * * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=GeoJSON%20and%20TopoJSON.html|Cesium Sandcastle GeoJSON and TopoJSON Demo} * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=GeoJSON%20simplestyle.html|Cesium Sandcastle GeoJSON simplestyle Demo} * * @example * var viewer = new Cesium.Viewer('cesiumContainer'); * viewer.dataSources.add(Cesium.GeoJsonDataSource.fromUrl('../../SampleData/ne_10m_us_states.topojson', { * stroke: Cesium.Color.HOTPINK, * fill: Cesium.Color.PINK, * strokeWidth: 3, * markerSymbol: '?' * })); */ var GeoJsonDataSource = function(name) { this._name = name; this._changed = new Event(); this._error = new Event(); this._isLoading = false; this._loading = new Event(); this._entityCollection = new EntityCollection(); this._promises = []; this._pinCache = {}; this._pinBuilder = new PinBuilder(); }; /** * Creates a new instance and asynchronously loads the provided url. * * @param {Object} url The url to be processed. * @param {Object} [options] An object with the following properties: * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors. * * @returns {GeoJsonDataSource} A new instance set to load the specified url. */ GeoJsonDataSource.fromUrl = function(url, options) { var result = new GeoJsonDataSource(); result.loadUrl(url, options); return result; }; defineProperties(GeoJsonDataSource, { /** * Gets or sets the default size of the map pin created for each point, in pixels. * @memberof GeoJsonDataSource * @type {Number} * @default 48 */ markerSize : { get : function() { return defaultMarkerSize; }, set : function(value) { defaultMarkerSize = value; } }, /** * Gets or sets the default symbol of the map pin created for each point. * This can be any valid {@link http://mapbox.com/maki/|Maki} identifier, any single character, * or blank if no symbol is to be used. * @memberof GeoJsonDataSource * @type {String} */ markerSymbol : { get : function() { return defaultMarkerSymbol; }, set : function(value) { defaultMarkerSymbol = value; } }, /** * Gets or sets the default color of the map pin created for each point. * @memberof GeoJsonDataSource * @type {Color} * @default Color.ROYALBLUE */ markerColor : { get : function() { return defaultMarkerColor; }, set : function(value) { defaultMarkerColor = value; } }, /** * Gets or sets the default color of polylines and polygon outlines. * @memberof GeoJsonDataSource * @type {Color} * @default Color.BLACK */ stroke : { get : function() { return defaultStroke; }, set : function(value) { defaultStroke = value; defaultStrokeMaterialProperty.color.setValue(value); } }, /** * Gets or sets the default width of polylines and polygon outlines. * @memberof GeoJsonDataSource * @type {Number} * @default 2.0 */ strokeWidth : { get : function() { return defaultStrokeWidth; }, set : function(value) { defaultStrokeWidth = value; defaultStrokeWidthProperty.setValue(value); } }, /** * Gets or sets default color for polygon interiors. * @memberof GeoJsonDataSource * @type {Color} * @default Color.YELLOW */ fill : { get : function() { return defaultFill; }, set : function(value) { defaultFill = value; defaultFillMaterialProperty = ColorMaterialProperty.fromColor(defaultFill); } }, /** * Gets an object that maps the name of a crs to a callback function which takes a GeoJSON coordinate * and transforms it into a WGS84 Earth-fixed Cartesian. Older versions of GeoJSON which * supported the EPSG type can be added to this list as well, by specifying the complete EPSG name, * for example 'EPSG:4326'. * @memberof GeoJsonDataSource * @type {Object} */ crsNames : { get : function() { return crsNames; } }, /** * Gets an object that maps the href property of a crs link to a callback function * which takes the crs properties object and returns a Promise that resolves * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian. * Items in this object take precedence over those defined in crsLinkHrefs, assuming * the link has a type specified. * @memberof GeoJsonDataSource * @type {Object} */ crsLinkHrefs : { get : function() { return crsLinkHrefs; } }, /** * Gets an object that maps the type property of a crs link to a callback function * which takes the crs properties object and returns a Promise that resolves * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian. * Items in crsLinkHrefs take precedence over this object. * @memberof GeoJsonDataSource * @type {Object} */ crsLinkTypes : { get : function() { return crsLinkTypes; } } }); defineProperties(GeoJsonDataSource.prototype, { /** * Gets a human-readable name for this instance. * @memberof GeoJsonDataSource.prototype * @type {String} */ name : { get : function() { return this._name; } }, /** * This DataSource only defines static data, therefore this property is always undefined. * @memberof GeoJsonDataSource.prototype * @type {DataSourceClock} */ clock : { value : undefined, writable : false }, /** * Gets the collection of {@link Entity} instances. * @memberof GeoJsonDataSource.prototype * @type {EntityCollection} */ entities : { get : function() { return this._entityCollection; } }, /** * Gets a value indicating if the data source is currently loading data. * @memberof GeoJsonDataSource.prototype * @type {Boolean} */ isLoading : { get : function() { return this._isLoading; } }, /** * Gets an event that will be raised when the underlying data changes. * @memberof GeoJsonDataSource.prototype * @type {Event} */ changedEvent : { get : function() { return this._changed; } }, /** * Gets an event that will be raised if an error is encountered during processing. * @memberof GeoJsonDataSource.prototype * @type {Event} */ errorEvent : { get : function() { return this._error; } }, /** * Gets an event that will be raised when the data source either starts or stops loading. * @memberof GeoJsonDataSource.prototype * @type {Event} */ loadingEvent : { get : function() { return this._loading; } } }); /** * Asynchronously loads the GeoJSON at the provided url, replacing any existing data. * * @param {Object} url The url to be processed. * @param {Object} [options] An object with the following properties: * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors. * * @returns {Promise} a promise that will resolve when the GeoJSON is loaded. */ GeoJsonDataSource.prototype.loadUrl = function(url, options) { //>>includeStart('debug', pragmas.debug); if (!defined(url)) { throw new DeveloperError('url is required.'); } //>>includeEnd('debug'); DataSource.setLoading(this, true); var that = this; return when(loadJson(url), function(geoJson) { return load(that, geoJson, url, options); }).otherwise(function(error) { DataSource.setLoading(that, false); that._error.raiseEvent(that, error); return when.reject(error); }); }; /** * Asynchronously loads the provided GeoJSON object, replacing any existing data. * * @param {Object} geoJson The object to be processed. * @param {Object} [options] An object with the following properties: * @param {String} [options.sourceUri] The base URI of any relative links in the geoJson object. * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors. * @returns {Promise} a promise that will resolve when the GeoJSON is loaded. * * @exception {DeveloperError} Unsupported GeoJSON object type. * @exception {RuntimeError} crs is null. * @exception {RuntimeError} crs.properties is undefined. * @exception {RuntimeError} Unknown crs name. * @exception {RuntimeError} Unable to resolve crs link. * @exception {RuntimeError} Unknown crs type. */ GeoJsonDataSource.prototype.load = function(geoJson, options) { //>>includeStart('debug', pragmas.debug); if (!defined(geoJson)) { throw new DeveloperError('geoJson is required.'); } //>>includeEnd('debug'); var sourceUri = options; if (typeof options === 'string') { sourceUri = options; deprecationWarning('GeoJsonDataSource.load', 'GeoJsonDataSource.load now takes an options object instead of a string as its second parameter. Support for passing a string parameter will be removed in Cesium 1.6'); } else if (defined(options)) { sourceUri = options.sourceUri; } return load(this, geoJson, sourceUri, options); }; function load(that, geoJson, sourceUri, options) { options = defaultValue(options, defaultValue.EMPTY_OBJECT); options = { markerSize : defaultValue(options.markerSize, defaultMarkerSize), markerSymbol : defaultValue(options.markerSymbol, defaultMarkerSymbol), markerColor : defaultValue(options.markerColor, defaultMarkerColor), strokeWidthProperty : new ConstantProperty(defaultValue(options.strokeWidth, defaultStrokeWidth)), strokeMaterialProperty : ColorMaterialProperty.fromColor(defaultValue(options.stroke, defaultStroke)), fillMaterialProperty : ColorMaterialProperty.fromColor(defaultValue(options.fill, defaultFill)) }; var name; if (defined(sourceUri)) { name = getFilenameFromUri(sourceUri); } if (defined(name) && that._name !== name) { that._name = name; that._changed.raiseEvent(that); } var typeHandler = geoJsonObjectTypes[geoJson.type]; if (!defined(typeHandler)) { throw new DeveloperError('Unsupported GeoJSON object type: ' + geoJson.type); } //Check for a Coordinate Reference System. var crsFunction = defaultCrsFunction; var crs = geoJson.crs; if (defined(crs)) { if (crs === null) { throw new RuntimeError('crs is null.'); } if (!defined(crs.properties)) { throw new RuntimeError('crs.properties is undefined.'); } var properties = crs.properties; if (crs.type === 'name') { crsFunction = crsNames[properties.name]; if (!defined(crsFunction)) { throw new RuntimeError('Unknown crs name: ' + properties.name); } } else if (crs.type === 'link') { var handler = crsLinkHrefs[properties.href]; if (!defined(handler)) { handler = crsLinkTypes[properties.type]; } if (!defined(handler)) { throw new RuntimeError('Unable to resolve crs link: ' + JSON.stringify(properties)); } crsFunction = handler(properties); } else if (crs.type === 'EPSG') { crsFunction = crsNames['EPSG:' + properties.code]; if (!defined(crsFunction)) { throw new RuntimeError('Unknown crs EPSG code: ' + properties.code); } } else { throw new RuntimeError('Unknown crs type: ' + crs.type); } } DataSource.setLoading(that, true); return when(crsFunction, function(crsFunction) { that._entityCollection.removeAll(); typeHandler(that, geoJson, geoJson, crsFunction, options); return when.all(that._promises, function() { that._promises.length = 0; that._pinCache = {}; DataSource.setLoading(that, false); }); }).otherwise(function(error) { DataSource.setLoading(that, false); that._error.raiseEvent(that, error); return when.reject(error); }); } return GeoJsonDataSource; });