/*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 += '' + key + ' | ' + describe(value) + ' |
';
} else {
html += '' + key + ' | ' + value + ' |
';
}
}
}
}
html += '
';
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;
});