diff --git a/Apps/CesiumViewer/CesiumViewer.js b/Apps/CesiumViewer/CesiumViewer.js
index 9668076590c..82aa19e2a2b 100644
--- a/Apps/CesiumViewer/CesiumViewer.js
+++ b/Apps/CesiumViewer/CesiumViewer.js
@@ -1,6 +1,7 @@
/*global define*/
define([
'DynamicScene/CzmlDataSource',
+ 'DynamicScene/GeoJsonDataSource',
'Scene/PerformanceDisplay',
'Widgets/checkForChromeFrame',
'Widgets/Viewer/Viewer',
@@ -9,6 +10,7 @@ define([
'domReady!'
], function(
CzmlDataSource,
+ GeoJsonDataSource,
PerformanceDisplay,
checkForChromeFrame,
Viewer,
@@ -50,6 +52,12 @@ define([
window.alert(e);
});
+ function endsWith(str, suffix) {
+ var strLength = str.length;
+ var suffixLength = suffix.length;
+ return (suffixLength < strLength) && (str.indexOf(suffix, strLength - suffixLength) !== -1);
+ }
+
function startup() {
var viewer = new Viewer('cesiumContainer');
viewer.extend(viewerDragDropMixin);
@@ -75,7 +83,12 @@ define([
}
if (typeof endUserOptions.source !== 'undefined') {
- var source = new CzmlDataSource();
+ var source;
+ if (endsWith(endUserOptions.source.toUpperCase(), ".GEOJSON")) {
+ source = new GeoJsonDataSource();
+ } else {
+ source = new CzmlDataSource();
+ }
source.loadUrl(endUserOptions.source).then(function() {
viewer.dataSources.add(source);
diff --git a/CHANGES.md b/CHANGES.md
index 3057e99b574..9eb7e10f9de 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,6 +15,7 @@ Beta Releases
* `ImageryProvider.loadImage` now requires that the calling imagery provider instance be passed as its first parameter.
* Removed `CesiumViewerWidget` and replaced it with a new `Viewer` widget with mixin architecture. This new widget does not depend on Dojo and is part of the combined Cesium.js file. It is intended to be a flexible base widget for easily building robust applications. See [#838](https://github.com/AnalyticalGraphicsInc/cesium/pull/838) for the full details.
* Removed the Dojo-based `checkForChromeFrame` function, and replaced it with a new standalone version that returns a promise to signal when the asynchronous check has completed.
+* Added initial support for [GeoJSON](http://www.geojson.org/) see [#890](https://github.com/AnalyticalGraphicsInc/cesium/pull/890) for details.
* Added `Context.getAntialias`.
* Added rotation, aligned axis, width, and height properties to `Billboard`s.
* Improved the performance of "missing tile" checking, especially for Bing imagery.
diff --git a/Source/DynamicScene/ConstantProperty.js b/Source/DynamicScene/ConstantProperty.js
new file mode 100644
index 00000000000..5f068cf3511
--- /dev/null
+++ b/Source/DynamicScene/ConstantProperty.js
@@ -0,0 +1,35 @@
+/*global define*/
+define(function() {
+ "use strict";
+
+ /**
+ * Represents a single value which does not change with regard to simulation time.
+ *
+ * @alias ConstantProperty
+ * @constructor
+ *
+ * @see DynamicProperty
+ */
+ var ConstantProperty = function(value) {
+ this._value = value;
+ this._clonable = typeof value !== 'undefined' && typeof value.clone === 'function';
+ };
+
+ /**
+ * Gets the value of the property, optionally cloning it.
+ * @memberof ConstantProperty
+ *
+ * @param {JulianDate} time The time for which to retrieve the value. This parameter is unused.
+ * @param {Object} [result] The object to store the value into if the value is clonable. If the result is omitted or the value does not implement clone, the actual value is returned.
+ * @returns The modified result parameter or the actual value instance if the value is not clonable.
+ */
+ ConstantProperty.prototype.getValue = function(time, result) {
+ var value = this._value;
+ if (this._clonable) {
+ return value.clone(result);
+ }
+ return value;
+ };
+
+ return ConstantProperty;
+});
\ No newline at end of file
diff --git a/Source/DynamicScene/CzmlDataSource.js b/Source/DynamicScene/CzmlDataSource.js
index 3c7e08033c6..a646afd773e 100644
--- a/Source/DynamicScene/CzmlDataSource.js
+++ b/Source/DynamicScene/CzmlDataSource.js
@@ -5,9 +5,9 @@ define(['../Core/ClockRange',
'../Core/Event',
'../Core/Iso8601',
'../Core/loadJson',
- '../DynamicScene/DynamicClock',
- '../DynamicScene/processCzml',
- '../DynamicScene/DynamicObjectCollection'
+ './DynamicClock',
+ './processCzml',
+ './DynamicObjectCollection'
], function(
ClockRange,
ClockStep,
@@ -65,7 +65,7 @@ define(['../Core/ClockRange',
/**
* Gets an event that will be raised when non-time-varying data changes
* or if the return value of getIsTimeVarying changes.
- * @memberof DataSource
+ * @memberof CzmlDataSource
*
* @returns {Event} The event.
*/
@@ -97,7 +97,7 @@ define(['../Core/ClockRange',
/**
* Gets the DynamicObjectCollection generated by this data source.
- * @memberof DataSource
+ * @memberof CzmlDataSource
*
* @returns {DynamicObjectCollection} The collection of objects generated by this data source.
*/
@@ -108,7 +108,7 @@ define(['../Core/ClockRange',
/**
* Gets a value indicating if the data varies with simulation time. If the return value of
* this function changes, the changed event will be raised.
- * @memberof DataSource
+ * @memberof CzmlDataSource
*
* @returns {Boolean} True if the data is varies with simulation time, false otherwise.
*/
diff --git a/Source/DynamicScene/DynamicObject.js b/Source/DynamicScene/DynamicObject.js
index 72dc23d3f24..9b5cbc34efe 100644
--- a/Source/DynamicScene/DynamicObject.js
+++ b/Source/DynamicScene/DynamicObject.js
@@ -192,6 +192,23 @@ define([
return availabilityValue;
};
+ /**
+ * Merge all of the properties of the supplied object onto this object.
+ * Properties which are already defined are not overwritten.
+ * @param other {DynamicObject} The object to merge.
+ * @private
+ */
+ DynamicObject.prototype.merge = function(other) {
+ if (typeof other === 'undefined') {
+ throw new DeveloperError('other is required');
+ }
+ for ( var property in other) {
+ if (other.hasOwnProperty(property)) {
+ this[property] = defaultValue(this[property], other[property]);
+ }
+ }
+ };
+
/**
* Processes a single CZML packet and merges its data into the provided DynamicObject's position
* property. This method is not normally called directly, but is part of the array of CZML processing
diff --git a/Source/DynamicScene/GeoJsonDataSource.js b/Source/DynamicScene/GeoJsonDataSource.js
new file mode 100644
index 00000000000..9335b5dea66
--- /dev/null
+++ b/Source/DynamicScene/GeoJsonDataSource.js
@@ -0,0 +1,456 @@
+/*global define*/
+define(['../Core/createGuid',
+ '../Core/Cartographic',
+ '../Core/Color',
+ '../Core/defineProperties',
+ '../Core/DeveloperError',
+ '../Core/RuntimeError',
+ '../Core/Ellipsoid',
+ '../Core/Event',
+ '../Core/loadJson',
+ './ConstantProperty',
+ './DynamicObject',
+ './DynamicPoint',
+ './DynamicPolyline',
+ './DynamicPolygon',
+ './DynamicMaterialProperty',
+ './DynamicObjectCollection',
+ '../ThirdParty/when'], function(
+ createGuid,
+ Cartographic,
+ Color,
+ defineProperties,
+ DeveloperError,
+ RuntimeError,
+ Ellipsoid,
+ Event,
+ loadJson,
+ ConstantProperty,
+ DynamicObject,
+ DynamicPoint,
+ DynamicPolyline,
+ DynamicPolygon,
+ DynamicMaterialProperty,
+ DynamicObjectCollection,
+ when) {
+ "use strict";
+
+ //DynamicPositionProperty is pretty hard to use with non-CZML based data
+ //For now we create two of our own properties for exposing GeoJSON
+ //data.
+ var ConstantPositionProperty = function(value) {
+ this._value = value;
+ };
+
+ ConstantPositionProperty.prototype.getValueCartesian = function(time, result) {
+ var value = this._value;
+ if (typeof value.clone === 'function') {
+ return value.clone(result);
+ }
+ return value;
+ };
+
+ ConstantPositionProperty.prototype.setValue = function(value) {
+ this._value = value;
+ };
+
+ //GeoJSON specifies only the Feature object has a usable id property
+ //But since "multi" geometries create multiple dynamicObject,
+ //we can't use it for them either.
+ function createObject(geoJson, dynamicObjectCollection) {
+ var id = geoJson.id;
+ if (typeof id === 'undefined' || geoJson.type !== 'Feature') {
+ id = createGuid();
+ } else {
+ var i = 2;
+ var finalId = id;
+ while (typeof dynamicObjectCollection.getObject(finalId) !== 'undefined') {
+ finalId = id + "_" + i;
+ i++;
+ }
+ id = finalId;
+ }
+ var dynamicObject = dynamicObjectCollection.getOrCreateObject(id);
+ dynamicObject.geoJson = geoJson;
+ return dynamicObject;
+ }
+
+ 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, source) {
+ if (typeof feature.geometry === 'undefined') {
+ throw new RuntimeError('feature.geometry is required.');
+ }
+
+ if (feature.geometry === null) {
+ //Null geometry is allowed, so just create an empty dynamicObject instance for it.
+ createObject(feature, dataSource._dynamicObjectCollection);
+ } else {
+ var geometryType = feature.geometry.type;
+ var geometryHandler = geometryTypes[geometryType];
+ if (typeof geometryHandler === 'undefined') {
+ throw new RuntimeError('Unknown geometry type: ' + geometryType);
+ }
+ geometryHandler(dataSource, feature, feature.geometry, crsFunction, source);
+ }
+ }
+
+ function processFeatureCollection(dataSource, featureCollection, notUsed, crsFunction, source) {
+ var features = featureCollection.features;
+ for ( var i = 0, len = features.length; i < len; i++) {
+ processFeature(dataSource, features[i], undefined, crsFunction, source);
+ }
+ }
+
+ function processGeometryCollection(dataSource, geoJson, geometryCollection, crsFunction, source) {
+ 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 (typeof geometryHandler === 'undefined') {
+ throw new RuntimeError('Unknown geometry type: ' + geometryType);
+ }
+ geometryHandler(dataSource, geoJson, geometry, crsFunction, source);
+ }
+ }
+
+ function processPoint(dataSource, geoJson, geometry, crsFunction, source) {
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultPoint);
+ dynamicObject.position = new ConstantPositionProperty(crsFunction(geometry.coordinates));
+ }
+
+ function processMultiPoint(dataSource, geoJson, geometry, crsFunction, source) {
+ var coordinates = geometry.coordinates;
+ for ( var i = 0; i < coordinates.length; i++) {
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultPoint);
+ dynamicObject.position = new ConstantPositionProperty(crsFunction(coordinates[i]));
+ }
+ }
+
+ function processLineString(dataSource, geoJson, geometry, crsFunction, source) {
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultLine);
+ dynamicObject.vertexPositions = new ConstantPositionProperty(coordinatesArrayToCartesianArray(geometry.coordinates, crsFunction));
+ }
+
+ function processMultiLineString(dataSource, geoJson, geometry, crsFunction, source) {
+ var lineStrings = geometry.coordinates;
+ for ( var i = 0; i < lineStrings.length; i++) {
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultLine);
+ dynamicObject.vertexPositions = new ConstantPositionProperty(coordinatesArrayToCartesianArray(lineStrings[i], crsFunction));
+ }
+ }
+
+ function processPolygon(dataSource, geoJson, geometry, crsFunction, source) {
+ //TODO Holes
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultPolygon);
+ dynamicObject.vertexPositions = new ConstantPositionProperty(coordinatesArrayToCartesianArray(geometry.coordinates[0], crsFunction));
+ }
+
+ function processMultiPolygon(dataSource, geoJson, geometry, crsFunction, source) {
+ //TODO holes
+ var polygons = geometry.coordinates;
+ for ( var i = 0; i < polygons.length; i++) {
+ var polygon = polygons[i];
+ var dynamicObject = createObject(geoJson, dataSource._dynamicObjectCollection);
+ dynamicObject.merge(dataSource.defaultPolygon);
+ dynamicObject.vertexPositions = new ConstantPositionProperty(coordinatesArrayToCartesianArray(polygon[0], crsFunction));
+ }
+ }
+
+ var geoJsonObjectTypes = {
+ Feature : processFeature,
+ FeatureCollection : processFeatureCollection,
+ GeometryCollection : processGeometryCollection,
+ LineString : processLineString,
+ MultiLineString : processMultiLineString,
+ MultiPoint : processMultiPoint,
+ MultiPolygon : processMultiPolygon,
+ Point : processPoint,
+ Polygon : processPolygon
+ };
+
+ var geometryTypes = {
+ GeometryCollection : processGeometryCollection,
+ LineString : processLineString,
+ MultiLineString : processMultiLineString,
+ MultiPoint : processMultiPoint,
+ MultiPolygon : processMultiPolygon,
+ Point : processPoint,
+ Polygon : processPolygon
+ };
+
+ /**
+ * A {@link DataSource} which processes GeoJSON. Since GeoJSON has no standard for styling content,
+ * we provide default graphics via the defaultPoint, defaultLine, and defaultPolygon properties.
+ * Any changes to these objects will affect the resulting {@link DynamicObject} collection.
+ * @alias GeoJsonDataSource
+ * @constructor
+ *
+ * @see DataSourceDisplay
+ * @see GeoJSON specification.
+ *
+ * @example
+ * //Use a billboard instead of a point.
+ * var dataSource = new GeoJsonDataSource();
+ * var defaultPoint = dataSource.defaulPoint;
+ * defaultPoint.point = undefined;
+ * var billboard = new DynamicBillboard();
+ * billboard.image = new ConstantProperty('image.png');
+ * defaultPoint.billboard = billboard;
+ * dataSource.loadUrl('sample.geojson');
+ */
+ var GeoJsonDataSource = function() {
+ //default point
+ var defaultPoint = new DynamicObject('GeoJsonDataSource.defaultPoint');
+ var point = new DynamicPoint();
+ point.color = new ConstantProperty(Color.YELLOW);
+ point.pixelSize = new ConstantProperty(10);
+ point.outlineColor = new ConstantProperty(Color.BLACK);
+ point.outlineWidth = new ConstantProperty(1);
+ defaultPoint.point = point;
+
+ //default line
+ var defaultLine = new DynamicObject('GeoJsonDataSource.defaultLine');
+ var polyline = new DynamicPolyline();
+ polyline.color = new ConstantProperty(Color.YELLOW);
+ polyline.width = new ConstantProperty(2);
+ polyline.outlineColor = new ConstantProperty(Color.BLACK);
+ polyline.outlineWidth = new ConstantProperty(1);
+ defaultLine.polyline = polyline;
+
+ //default polygon
+ var defaultPolygon = new DynamicObject('GeoJsonDataSource.defaultPolygon');
+ var polygonMaterial = new DynamicMaterialProperty();
+ polyline = new DynamicPolyline();
+ polyline.color = new ConstantProperty(Color.YELLOW);
+ polyline.width = new ConstantProperty(1);
+ polyline.outlineColor = new ConstantProperty(Color.BLACK);
+ polyline.outlineWidth = new ConstantProperty(0);
+ defaultPolygon.polyline = polyline;
+ var polygon = new DynamicPolygon();
+ polygon.material = polygonMaterial;
+ polygonMaterial.processCzmlIntervals({
+ solidColor : {
+ color : {
+ rgba : [255, 255, 0, 25]
+ }
+ }
+ }, undefined, undefined);
+ defaultPolygon.polygon = polygon;
+
+ this._changed = new Event();
+ this._error = new Event();
+ this._dynamicObjectCollection = new DynamicObjectCollection();
+
+ /**
+ * Gets or sets the default graphics to be applied to GeoJSON Point and MultiPoint geometries.
+ * @type DynamicObject
+ */
+ this.defaultPoint = defaultPoint;
+
+ /**
+ * Gets or sets the default graphics to be applied to GeoJSON LineString and MultiLineString geometries.
+ * @type DynamicObject
+ */
+ this.defaultLine = defaultLine;
+
+ /**
+ * Gets or sets the default graphics to be applied to GeoJSON Polygon and MultiPolygon geometries.
+ * @type DynamicObject
+ */
+ this.defaultPolygon = defaultPolygon;
+ };
+
+ /**
+ * Gets an event that will be raised when non-time-varying data changes
+ * or if the return value of getIsTimeVarying changes.
+ * @memberof GeoJsonDataSource
+ *
+ * @returns {Event} The event.
+ */
+ GeoJsonDataSource.prototype.getChangedEvent = function() {
+ return this._changed;
+ };
+
+ /**
+ * Gets an event that will be raised if an error is encountered during processing.
+ * @memberof GeoJsonDataSource
+ *
+ * @returns {Event} The event.
+ */
+ GeoJsonDataSource.prototype.getErrorEvent = function() {
+ return this._error;
+ };
+
+ /**
+ * Since GeoJSON is a static format, this function always returns undefined.
+ * @memberof GeoJsonDataSource
+ */
+ GeoJsonDataSource.prototype.getClock = function() {
+ return undefined;
+ };
+
+ /**
+ * Gets the DynamicObjectCollection generated by this data source.
+ * @memberof GeoJsonDataSource
+ *
+ * @returns {DynamicObjectCollection} The collection of objects generated by this data source.
+ */
+ GeoJsonDataSource.prototype.getDynamicObjectCollection = function() {
+ return this._dynamicObjectCollection;
+ };
+
+ /**
+ * Gets a value indicating if the data varies with simulation time. If the return value of
+ * this function changes, the changed event will be raised.
+ * @memberof GeoJsonDataSource
+ *
+ * @returns {Boolean} True if the data is varies with simulation time, false otherwise.
+ */
+ GeoJsonDataSource.prototype.getIsTimeVarying = function() {
+ return false;
+ };
+
+ /**
+ * Asynchronously loads the GeoJSON at the provided url, replacing any existing data.
+ *
+ * @param {Object} url The url to be processed.
+ *
+ * @returns {Promise} a promise that will resolve when the GeoJSON is loaded.
+ *
+ * @exception {DeveloperError} url is required.
+ */
+ GeoJsonDataSource.prototype.loadUrl = function(url) {
+ if (typeof url === 'undefined') {
+ throw new DeveloperError('url is required.');
+ }
+
+ var dataSource = this;
+ return loadJson(url).then(function(geoJson) {
+ return dataSource.load(geoJson, url);
+ }, function(error) {
+ dataSource._error.raiseEvent(dataSource, error);
+ });
+ };
+
+ /**
+ * Asynchronously loads the provided GeoJSON object, replacing any existing data.
+ *
+ * @param {Object} geoJson The object to be processed.
+ * @param {String} [source] The base URI of any relative links in the geoJson object.
+ *
+ * @returns {Promise} a promise that will resolve when the GeoJSON is loaded.
+ *
+ * @exception {DeveloperError} geoJson is required.
+ * @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, source) {
+ if (typeof geoJson === 'undefined') {
+ throw new DeveloperError('geoJson is required.');
+ }
+
+ var typeHandler = geoJsonObjectTypes[geoJson.type];
+ if (typeof typeHandler === 'undefined') {
+ throw new DeveloperError('Unsupported GeoJSON object type: ' + geoJson.type);
+ }
+
+ //Check for a Coordinate Reference System.
+ var crsFunction = defaultCrsFunction;
+ var crs = geoJson.crs;
+ if (typeof crs !== 'undefined') {
+ if (crs === null) {
+ throw new RuntimeError('crs is null.');
+ }
+ if (typeof crs.properties === 'undefined') {
+ throw new RuntimeError('crs.properties is undefined.');
+ }
+
+ var properties = crs.properties;
+ if (crs.type === 'name') {
+ crsFunction = GeoJsonDataSource.crsNames[properties.name];
+ if (typeof crsFunction === 'undefined') {
+ throw new RuntimeError('Unknown crs name: ' + properties.name);
+ }
+ } else if (crs.type === 'link') {
+ var handler = GeoJsonDataSource.crsLinkHrefs[properties.href];
+ if (typeof handler === 'undefined') {
+ handler = GeoJsonDataSource.crsLinkTypes[properties.type];
+ }
+
+ if (typeof handler === 'undefined') {
+ throw new RuntimeError('Unable to resolve crs link: ' + JSON.stringify(properties));
+ }
+
+ crsFunction = handler(properties);
+ } else {
+ throw new RuntimeError('Unknown crs type: ' + crs.type);
+ }
+ }
+
+ this._dynamicObjectCollection.clear();
+
+ var that = this;
+ return when(crsFunction, function(crsFunction) {
+ typeHandler(that, geoJson, geoJson, crsFunction, source);
+ that._changed.raiseEvent(that);
+ });
+ };
+
+ function defaultCrsFunction(coordinates) {
+ var cartographic = Cartographic.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
+ return Ellipsoid.WGS84.cartographicToCartesian(cartographic);
+ }
+
+ /**
+ * 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.
+ * @memberof GeoJsonDataSource
+ * @type Object
+ */
+ GeoJsonDataSource.crsNames = {
+ 'urn:ogc:def:crs:OGC:1.3:CRS84' : defaultCrsFunction,
+ 'EPSG:4326' : defaultCrsFunction
+ };
+
+ /**
+ * 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
+ */
+ GeoJsonDataSource.crsLinkHrefs = {};
+
+ /**
+ * 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
+ */
+ GeoJsonDataSource.crsLinkTypes = {};
+
+ return GeoJsonDataSource;
+});
\ No newline at end of file
diff --git a/Source/Scene/CameraController.js b/Source/Scene/CameraController.js
index 7ce3d00eb37..0265149ff47 100644
--- a/Source/Scene/CameraController.js
+++ b/Source/Scene/CameraController.js
@@ -947,7 +947,7 @@ define([
return result;
}
/**
- * Get the camera position neede to view an extent on an ellipsoid or map
+ * Get the camera position needed to view an extent on an ellipsoid or map
* @memberof CameraController
*
* @param {Extent} extent The extent to view.
diff --git a/Source/Widgets/Viewer/viewerDragDropMixin.js b/Source/Widgets/Viewer/viewerDragDropMixin.js
index bc1630d4e6d..1739284b3ec 100644
--- a/Source/Widgets/Viewer/viewerDragDropMixin.js
+++ b/Source/Widgets/Viewer/viewerDragDropMixin.js
@@ -6,6 +6,8 @@ define([
'../../Core/Event',
'../../Core/wrapFunction',
'../../DynamicScene/CzmlDataSource',
+ '../../DynamicScene/GeoJsonDataSource',
+ '../../ThirdParty/when',
'../getElement'
], function(
defaultValue,
@@ -14,8 +16,11 @@ define([
Event,
wrapFunction,
CzmlDataSource,
+ GeoJsonDataSource,
+ when,
getElement) {
"use strict";
+ /*global console*/
/**
* A mixin which adds default drag and drop support for CZML files to the Viewer widget.
@@ -191,22 +196,40 @@ define([
dropTarget.addEventListener('dragexit', stop, false);
}
+ function endsWith(str, suffix) {
+ var strLength = str.length;
+ var suffixLength = suffix.length;
+ return (suffixLength < strLength) && (str.indexOf(suffix, strLength - suffixLength) !== -1);
+ }
+
function createOnLoadCallback(viewer, source, firstTime) {
+ var DataSource;
+ if (endsWith(source.toUpperCase(), ".CZML")) {
+ DataSource = CzmlDataSource;
+ } else if (endsWith(source.toUpperCase(), '.GEOJSON')) {
+ DataSource = GeoJsonDataSource;
+ } else {
+ viewer.onDropError.raiseEvent(viewer, source, 'Unrecognized file extension: ' + source);
+ }
+
return function(evt) {
- var czmlSource = new CzmlDataSource();
+ var dataSource = new DataSource();
try {
- czmlSource.load(JSON.parse(evt.target.result), source);
- viewer.dataSources.add(czmlSource);
- if (firstTime) {
- var dataClock = czmlSource.getClock();
- if (typeof dataClock !== 'undefined') {
- dataClock.clone(viewer.clock);
- if (typeof viewer.timeline !== 'undefined') {
- viewer.timeline.updateFromClock();
- viewer.timeline.zoomTo(dataClock.startTime, dataClock.stopTime);
+ when(dataSource.load(JSON.parse(evt.target.result), source), function() {
+ viewer.dataSources.add(dataSource);
+ if (firstTime) {
+ var dataClock = dataSource.getClock();
+ if (typeof dataClock !== 'undefined') {
+ dataClock.clone(viewer.clock);
+ if (typeof viewer.timeline !== 'undefined') {
+ viewer.timeline.updateFromClock();
+ viewer.timeline.zoomTo(dataClock.startTime, dataClock.stopTime);
+ }
}
}
- }
+ }, function(error) {
+ viewer.onDropError.raiseEvent(viewer, source, error);
+ });
} catch (error) {
viewer.onDropError.raiseEvent(viewer, source, error);
}
diff --git a/Specs/Data/test.geojson b/Specs/Data/test.geojson
new file mode 100644
index 00000000000..71609fe2143
--- /dev/null
+++ b/Specs/Data/test.geojson
@@ -0,0 +1,40 @@
+{
+ "type" : "FeatureCollection",
+ "features" : [
+ {
+ "type" : "Feature",
+ "geometry" : {
+ "type" : "Point",
+ "coordinates" : [ 102.0, 0.5 ]
+ },
+ "properties" : {
+ "prop0" : "value0"
+ }
+ },
+ {
+ "type" : "Feature",
+ "geometry" : {
+ "type" : "LineString",
+ "coordinates" : [ [ 102.0, 0.0 ], [ 103.0, 1.0 ],
+ [ 104.0, 0.0 ], [ 105.0, 1.0 ] ]
+ },
+ "properties" : {
+ "prop0" : "value0",
+ "prop1" : 0.0
+ }
+ },
+ {
+ "type" : "Feature",
+ "geometry" : {
+ "type" : "Polygon",
+ "coordinates" : [ [ [ 100.0, 0.0 ], [ 101.0, 0.0 ],
+ [ 101.0, 1.0 ], [ 100.0, 1.0 ], [ 100.0, 0.0 ] ] ]
+ },
+ "properties" : {
+ "prop0" : "value0",
+ "prop1" : {
+ "this" : "that"
+ }
+ }
+ } ]
+}
\ No newline at end of file
diff --git a/Specs/DynamicScene/GeoJsonDataSourceSpec.js b/Specs/DynamicScene/GeoJsonDataSourceSpec.js
new file mode 100644
index 00000000000..e1682fc3254
--- /dev/null
+++ b/Specs/DynamicScene/GeoJsonDataSourceSpec.js
@@ -0,0 +1,563 @@
+/*global defineSuite*/
+defineSuite(['DynamicScene/GeoJsonDataSource',
+ 'DynamicScene/DynamicObjectCollection',
+ 'Core/Cartographic',
+ 'Core/Cartesian3',
+ 'Core/Ellipsoid',
+ 'Core/Event',
+ 'ThirdParty/when'
+ ], function(
+ GeoJsonDataSource,
+ DynamicObjectCollection,
+ Cartographic,
+ Cartesian3,
+ Ellipsoid,
+ Event,
+ when) {
+ "use strict";
+ /*global jasmine,describe,xdescribe,it,xit,expect,beforeEach,afterEach,beforeAll,afterAll,spyOn,runs,waits,waitsFor*/
+
+ function coordinatesToCartesian(coordinates) {
+ return Ellipsoid.WGS84.cartographicToCartesian(Cartographic.fromDegrees(coordinates[0], coordinates[1]));
+ }
+
+ function coordinatesArrayToCartesian(coordinates) {
+ var result = [];
+ for ( var i = 0; i < coordinates.length; i++) {
+ result.push(coordinatesToCartesian(coordinates[i]));
+ }
+ return result;
+ }
+
+ function multiLineToCartesian(geometry) {
+ var coordinates = geometry.coordinates;
+ var result = [];
+ for (var i = 0; i < coordinates.length; i++) {
+ result.push(coordinatesArrayToCartesian(coordinates[i]));
+ }
+ return result;
+ }
+
+ function polygonCoordinatesToCartesian(coordinates) {
+ return coordinatesArrayToCartesian(coordinates[0]);
+ }
+
+ function multiPolygonCoordinatesToCartesian(coordinates) {
+ var result = [];
+ for (var i = 0; i < coordinates.length; i++) {
+ result.push(coordinatesArrayToCartesian(coordinates[i][0]));
+ }
+ return result;
+ }
+
+ var point = {
+ type : 'Point',
+ coordinates : [102.0, 0.5]
+ };
+
+ var pointNamedCrs = {
+ type : 'Point',
+ coordinates : [102.0, 0.5],
+ crs : {
+ type : 'name',
+ properties : {
+ name : 'EPSG:4326'
+ }
+ }
+ };
+
+ var pointCrsLinkHref = {
+ type : 'Point',
+ coordinates : [102.0, 0.5],
+ crs : {
+ type : 'link',
+ properties : {
+ href : 'http://crs.invalid'
+ }
+ }
+ };
+
+ var lineString = {
+ type : 'LineString',
+ coordinates : [[100.0, 0.0], [101.0, 1.0]]
+ };
+
+ var polygon = {
+ type : 'Polygon',
+ coordinates : [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]
+ };
+
+ var polygonWithHoles = {
+ type : 'Polygon',
+ coordinates : [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]
+ };
+
+ var multiPoint = {
+ type : 'MultiPoint',
+ coordinates : [[100.0, 0.0], [101.0, 1.0], [101.0, 3.0]]
+ };
+
+ var multiLineString = {
+ type : 'MultiLineString',
+ coordinates : [[[100.0, 0.0], [101.0, 1.0]], [[102.0, 2.0], [103.0, 3.0]]]
+ };
+
+ var multiPolygon = {
+ type : 'MultiPolygon',
+ coordinates : [[[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]],
+ [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]],
+ [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]]]
+ };
+
+ var geometryCollection = {
+ type : 'GeometryCollection',
+ 'geometries' : [{
+ type : 'Point',
+ coordinates : [100.0, 0.0]
+ }, {
+ type : 'LineString',
+ coordinates : [[101.0, 0.0], [102.0, 1.0]]
+ }]
+ };
+
+ var feature = {
+ type : 'Feature',
+ geometry : point
+ };
+
+ var featureWithId = {
+ id : 'myId',
+ type : 'Feature',
+ geometry : geometryCollection
+ };
+
+ var featureUndefinedGeometry = {
+ type : 'Feature'
+ };
+
+ var featureNullGeometry = {
+ type : 'Feature',
+ geometry : null
+ };
+
+ var unknownGeometry = {
+ type : 'TimeyWimey',
+ coordinates : [0, 0]
+ };
+
+ var featureUnknownGeometry = {
+ type : 'Feature',
+ geometry : unknownGeometry
+ };
+
+ var geometryCollectionUnknownType = {
+ type : 'GeometryCollection',
+ 'geometries' : [unknownGeometry]
+ };
+
+ it('default constructor has expected values', function() {
+ var dataSource = new GeoJsonDataSource();
+ expect(dataSource.getChangedEvent()).toBeInstanceOf(Event);
+ expect(dataSource.getErrorEvent()).toBeInstanceOf(Event);
+ expect(dataSource.getClock()).toBeUndefined();
+ expect(dataSource.getDynamicObjectCollection()).toBeInstanceOf(DynamicObjectCollection);
+ expect(dataSource.getDynamicObjectCollection().getObjects().length).toEqual(0);
+ expect(dataSource.getIsTimeVarying()).toEqual(false);
+ });
+
+ it('Works with null geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(featureNullGeometry);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.geoJson).toBe(featureNullGeometry);
+ expect(pointObject.position).toBeUndefined();
+ });
+ });
+
+ it('Works with feature', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(feature);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.geoJson).toBe(feature);
+ expect(pointObject.position.getValueCartesian()).toEqual(coordinatesToCartesian(feature.geometry.coordinates));
+ expect(pointObject.point).toBeDefined();
+ });
+ });
+
+ it('Works with feature with id', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(featureWithId);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 2;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.id).toEqual(featureWithId.id);
+ var lineString = dynamicObjectCollection.getObjects()[1];
+ expect(lineString.id).toEqual(featureWithId.id + '_2');
+ });
+ });
+
+ it('Works with point geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(point);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.geoJson).toBe(point);
+ expect(pointObject.position.getValueCartesian()).toEqual(coordinatesToCartesian(point.coordinates));
+ expect(pointObject.point).toBeDefined();
+ });
+ });
+
+ it('Works with multipoint geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(multiPoint);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === multiPoint.coordinates.length;
+ });
+ runs(function() {
+ var objects = dynamicObjectCollection.getObjects();
+ var expectedPositions = coordinatesArrayToCartesian(multiPoint.coordinates);
+ for ( var i = 0; i < multiPoint.coordinates.length; i++) {
+ var object = objects[i];
+ expect(object.geoJson).toBe(multiPoint);
+ expect(object.position.getValueCartesian()).toEqual(expectedPositions[i]);
+ expect(object.point).toBeDefined();
+ }
+ });
+ });
+
+ it('Works with lineString geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(lineString);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var object = dynamicObjectCollection.getObjects()[0];
+ expect(object.geoJson).toBe(lineString);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(coordinatesArrayToCartesian(lineString.coordinates));
+ expect(object.polyline).toBeDefined();
+ });
+ });
+
+ it('Works with multiLineString geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(multiLineString);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 2;
+ });
+ runs(function() {
+ var objects = dynamicObjectCollection.getObjects();
+ var lines = multiLineToCartesian(multiLineString);
+ for ( var i = 0; i < multiLineString.coordinates.length; i++) {
+ var object = objects[i];
+ expect(object.geoJson).toBe(multiLineString);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(lines[i]);
+ expect(object.polyline).toBeDefined();
+ }
+ });
+ });
+
+ it('Works with polygon geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(polygon);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var object = dynamicObjectCollection.getObjects()[0];
+ expect(object.geoJson).toBe(polygon);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(polygonCoordinatesToCartesian(polygon.coordinates));
+ expect(object.polyline).toBeDefined();
+ expect(object.polygon).toBeDefined();
+ });
+ });
+
+ it('Works with polygon geometry with holes', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(polygonWithHoles);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var object = dynamicObjectCollection.getObjects()[0];
+ expect(object.geoJson).toBe(polygonWithHoles);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(polygonCoordinatesToCartesian(polygonWithHoles.coordinates));
+ expect(object.polyline).toBeDefined();
+ expect(object.polygon).toBeDefined();
+ });
+ });
+
+ it('Works with multiPolygon geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(multiPolygon);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 2;
+ });
+ runs(function() {
+ var objects = dynamicObjectCollection.getObjects();
+ var positions = multiPolygonCoordinatesToCartesian(multiPolygon.coordinates);
+ for ( var i = 0; i < multiPolygon.coordinates.length; i++) {
+ var object = objects[i];
+ expect(object.geoJson).toBe(multiPolygon);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(positions[i]);
+ expect(object.polyline).toBeDefined();
+ expect(object.polygon).toBeDefined();
+ }
+ });
+ });
+
+ it('Works with geometrycollection', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(geometryCollection);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 2;
+ });
+ runs(function() {
+ var object = dynamicObjectCollection.getObjects()[0];
+ expect(object.geoJson).toBe(geometryCollection);
+ expect(object.position.getValueCartesian()).toEqual(coordinatesToCartesian(geometryCollection.geometries[0].coordinates));
+ expect(object.point).toBeDefined();
+
+ object = dynamicObjectCollection.getObjects()[1];
+ expect(object.geoJson).toBe(geometryCollection);
+ expect(object.vertexPositions.getValueCartesian()).toEqual(coordinatesArrayToCartesian(geometryCollection.geometries[1].coordinates));
+ expect(object.polyline).toBeDefined();
+ });
+ });
+
+ it('Works with named crs', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.load(pointNamedCrs);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.position.getValueCartesian()).toEqual(coordinatesToCartesian(point.coordinates));
+ });
+ });
+
+ it('Works with link crs href', function() {
+ var projectedPosition = new Cartesian3(1, 2, 3);
+
+ var dataSource = new GeoJsonDataSource();
+ GeoJsonDataSource.crsLinkHrefs[pointCrsLinkHref.crs.properties.href] = function(properties) {
+ expect(properties).toBe(pointCrsLinkHref.crs.properties);
+ return when(properties.href, function(href) {
+ return function(coordinate) {
+ expect(coordinate).toBe(pointCrsLinkHref.coordinates);
+ return projectedPosition;
+ };
+ });
+ };
+ dataSource.load(pointCrsLinkHref);
+
+ var dynamicObjectCollection = dataSource.getDynamicObjectCollection();
+ waitsFor(function() {
+ return dynamicObjectCollection.getObjects().length === 1;
+ });
+ runs(function() {
+ var pointObject = dynamicObjectCollection.getObjects()[0];
+ expect(pointObject.position.getValueCartesian()).toEqual(projectedPosition);
+ });
+ });
+
+ it('loadUrl works', function() {
+ var dataSource = new GeoJsonDataSource();
+ dataSource.loadUrl('Data/test.geojson');
+
+ waitsFor(function() {
+ return dataSource.getDynamicObjectCollection().getObjects().length === 3;
+ });
+ });
+
+ it('Fails when encountering unknown geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+
+ var failed = false;
+ dataSource.load(featureUnknownGeometry).then(undefined, function(e) {
+ failed = true;
+ });
+
+ waitsFor(function() {
+ return failed;
+ });
+ });
+
+ it('Fails with undefined geomeetry', function() {
+ var dataSource = new GeoJsonDataSource();
+
+ var failed = false;
+ dataSource.load(featureUndefinedGeometry).then(undefined, function(e) {
+ failed = true;
+ });
+
+ waitsFor(function() {
+ return failed;
+ });
+ });
+
+ it('Fails with unknown geomeetry in geometryCollection', function() {
+ var dataSource = new GeoJsonDataSource();
+
+ var failed = false;
+ dataSource.load(geometryCollectionUnknownType).then(undefined, function(e) {
+ failed = true;
+ });
+
+ waitsFor(function() {
+ return failed;
+ });
+ });
+
+ it('load throws with undefined geoJson', function() {
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(undefined);
+ }).toThrow();
+ });
+
+ it('load throws with unknown geometry', function() {
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(unknownGeometry);
+ }).toThrow();
+ });
+
+ it('loadUrl throws with undefined Url', function() {
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.loadUrl(undefined);
+ }).toThrow();
+ });
+
+ it('loadUrl raises error with invalud url', function() {
+ var dataSource = new GeoJsonDataSource();
+ var thrown = false;
+ dataSource.getErrorEvent().addEventListener(function() {
+ thrown = true;
+ });
+ dataSource.loadUrl('invalid.geojson');
+ waitsFor(function() {
+ return thrown;
+ });
+ });
+
+ it('load throws with null crs', function() {
+ var featureWithNullCrs = {
+ type : 'Feature',
+ geometry : point,
+ crs : null
+ };
+
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(featureWithNullCrs);
+ }).toThrow();
+ });
+
+ it('load throws with unknown crs type', function() {
+ var featureWithUnknownCrsType = {
+ type : 'Feature',
+ geometry : point,
+ crs : {
+ type : 'potato',
+ properties : {}
+ }
+ };
+
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(featureWithUnknownCrsType);
+ }).toThrow();
+ });
+
+ it('load throws with undefined crs properties', function() {
+ var featureWithUnknownCrsType = {
+ type : 'Feature',
+ geometry : point,
+ crs : {
+ type : 'name'
+ }
+ };
+
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(featureWithUnknownCrsType);
+ }).toThrow();
+ });
+
+ it('load throws with unknown crs', function() {
+ var featureWithUnknownCrsType = {
+ type : 'Feature',
+ geometry : point,
+ crs : {
+ type : 'name',
+ properties : {
+ name : 'failMe'
+ }
+ }
+ };
+
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(featureWithUnknownCrsType);
+ }).toThrow();
+ });
+
+ it('load throws with unknown crs link', function() {
+ var featureWithUnknownCrsType = {
+ type : 'Feature',
+ geometry : point,
+ crs : {
+ type : 'link',
+ properties : {
+ href : 'failMe',
+ type : 'failMeTwice'
+ }
+ }
+ };
+
+ var dataSource = new GeoJsonDataSource();
+ expect(function() {
+ dataSource.load(featureWithUnknownCrsType);
+ }).toThrow();
+ });
+});
diff --git a/Specs/Widgets/Viewer/viewerDragDropMixinSpec.js b/Specs/Widgets/Viewer/viewerDragDropMixinSpec.js
index 73b40915875..1ef8a40ebf6 100644
--- a/Specs/Widgets/Viewer/viewerDragDropMixinSpec.js
+++ b/Specs/Widgets/Viewer/viewerDragDropMixinSpec.js
@@ -93,7 +93,7 @@ defineSuite([
var mockEvent = {
dataTransfer : {
files : [{
- name : 'czml1',
+ name : 'czml1.czml',
czmlString : JSON.stringify(czml1)
}]
},
@@ -129,10 +129,10 @@ defineSuite([
var mockEvent = {
dataTransfer : {
files : [{
- name : 'czml1',
+ name : 'czml1.czml',
czmlString : JSON.stringify(czml1)
}, {
- name : 'czml2',
+ name : 'czml2.czml',
czmlString : JSON.stringify(czml2)
}]
},
@@ -171,10 +171,10 @@ defineSuite([
var mockEvent = {
dataTransfer : {
files : [{
- name : 'czml1',
+ name : 'czml1.czml',
czmlString : JSON.stringify(czml1)
}, {
- name : 'czml2',
+ name : 'czml2.czml',
czmlString : JSON.stringify(czml2)
}]
},
@@ -254,7 +254,7 @@ defineSuite([
var mockEvent = {
dataTransfer : {
files : [{
- name : 'czml1',
+ name : 'czml1.czml',
czmlString : 'bad JSON'
}]
},
@@ -270,7 +270,7 @@ defineSuite([
var called = false;
var callback = function(viewerArg, source, error) {
expect(viewerArg).toBe(viewer);
- expect(source).toEqual('czml1');
+ expect(source).toEqual('czml1.czml');
expect(error).toBeInstanceOf(SyntaxError);
called = true;
};
@@ -291,7 +291,7 @@ defineSuite([
var mockEvent = {
dataTransfer : {
files : [{
- name : 'czml1',
+ name : 'czml1.czml',
errorMessage : 'bad JSON'
}]
},