GeoJsonDataSource.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. /*global define*/
  2. define([
  3. '../Core/Cartesian3',
  4. '../Core/Color',
  5. '../Core/createGuid',
  6. '../Core/defaultValue',
  7. '../Core/defined',
  8. '../Core/defineProperties',
  9. '../Core/deprecationWarning',
  10. '../Core/DeveloperError',
  11. '../Core/Event',
  12. '../Core/getFilenameFromUri',
  13. '../Core/loadJson',
  14. '../Core/PinBuilder',
  15. '../Core/RuntimeError',
  16. '../Scene/VerticalOrigin',
  17. '../ThirdParty/topojson',
  18. '../ThirdParty/when',
  19. './BillboardGraphics',
  20. './CallbackProperty',
  21. './ColorMaterialProperty',
  22. './ConstantPositionProperty',
  23. './ConstantProperty',
  24. './DataSource',
  25. './EntityCollection',
  26. './PolygonGraphics',
  27. './PolylineGraphics'
  28. ], function(
  29. Cartesian3,
  30. Color,
  31. createGuid,
  32. defaultValue,
  33. defined,
  34. defineProperties,
  35. deprecationWarning,
  36. DeveloperError,
  37. Event,
  38. getFilenameFromUri,
  39. loadJson,
  40. PinBuilder,
  41. RuntimeError,
  42. VerticalOrigin,
  43. topojson,
  44. when,
  45. BillboardGraphics,
  46. CallbackProperty,
  47. ColorMaterialProperty,
  48. ConstantPositionProperty,
  49. ConstantProperty,
  50. DataSource,
  51. EntityCollection,
  52. PolygonGraphics,
  53. PolylineGraphics) {
  54. "use strict";
  55. function defaultCrsFunction(coordinates) {
  56. return Cartesian3.fromDegrees(coordinates[0], coordinates[1], coordinates[2]);
  57. }
  58. var crsNames = {
  59. 'urn:ogc:def:crs:OGC:1.3:CRS84' : defaultCrsFunction,
  60. 'EPSG:4326' : defaultCrsFunction
  61. };
  62. var crsLinkHrefs = {};
  63. var crsLinkTypes = {};
  64. var defaultMarkerSize = 48;
  65. var defaultMarkerSymbol;
  66. var defaultMarkerColor = Color.ROYALBLUE;
  67. var defaultStroke = Color.YELLOW;
  68. var defaultStrokeWidth = 2;
  69. var defaultFill = Color.fromBytes(255, 255, 0, 100);
  70. var defaultStrokeWidthProperty = new ConstantProperty(defaultStrokeWidth);
  71. var defaultStrokeMaterialProperty = ColorMaterialProperty.fromColor(defaultStroke);
  72. var defaultFillMaterialProperty = ColorMaterialProperty.fromColor(defaultFill);
  73. var sizes = {
  74. small : 24,
  75. medium : 48,
  76. large : 64
  77. };
  78. var simpleStyleIdentifiers = ['title', 'description', //
  79. 'marker-size', 'marker-symbol', 'marker-color', 'stroke', //
  80. 'stroke-opacity', 'stroke-width', 'fill', 'fill-opacity'];
  81. var stringifyScratch = new Array(4);
  82. function describe(properties, nameProperty) {
  83. var html = '<table class="cesium-infoBox-defaultTable"><tbody>';
  84. for ( var key in properties) {
  85. if (properties.hasOwnProperty(key)) {
  86. if (key === nameProperty || simpleStyleIdentifiers.indexOf(key) !== -1) {
  87. continue;
  88. }
  89. var value = properties[key];
  90. if (defined(value)) {
  91. if (typeof value === 'object') {
  92. html += '<tr><th>' + key + '</th><td>' + describe(value) + '</td></tr>';
  93. } else {
  94. html += '<tr><th>' + key + '</th><td>' + value + '</td></tr>';
  95. }
  96. }
  97. }
  98. }
  99. html += '</tbody></table>';
  100. return html;
  101. }
  102. function createDescriptionCallback(properties, nameProperty) {
  103. var description;
  104. return function(time, result) {
  105. if (!defined(description)) {
  106. description = describe(properties, nameProperty);
  107. }
  108. return description;
  109. };
  110. }
  111. //GeoJSON specifies only the Feature object has a usable id property
  112. //But since "multi" geometries create multiple entity,
  113. //we can't use it for them either.
  114. function createObject(geoJson, entityCollection) {
  115. var id = geoJson.id;
  116. if (!defined(id) || geoJson.type !== 'Feature') {
  117. id = createGuid();
  118. } else {
  119. var i = 2;
  120. var finalId = id;
  121. while (defined(entityCollection.getById(finalId))) {
  122. finalId = id + "_" + i;
  123. i++;
  124. }
  125. id = finalId;
  126. }
  127. var entity = entityCollection.getOrCreateEntity(id);
  128. var properties = geoJson.properties;
  129. if (defined(properties)) {
  130. entity.addProperty('properties');
  131. entity.properties = properties;
  132. var nameProperty;
  133. //Check for the simplestyle specified name first.
  134. var name = properties.title;
  135. if (defined(name)) {
  136. entity.name = name;
  137. nameProperty = 'title';
  138. } else {
  139. //Else, find the name by selecting an appropriate property.
  140. //The name will be obtained based on this order:
  141. //1) The first case-insensitive property with the name 'title',
  142. //2) The first case-insensitive property with the name 'name',
  143. //3) The first property containing the word 'title'.
  144. //4) The first property containing the word 'name',
  145. var namePropertyPrecedence = Number.MAX_VALUE;
  146. for ( var key in properties) {
  147. if (properties.hasOwnProperty(key) && properties[key]) {
  148. var lowerKey = key.toLowerCase();
  149. if (namePropertyPrecedence > 1 && lowerKey === 'title') {
  150. namePropertyPrecedence = 1;
  151. nameProperty = key;
  152. break;
  153. } else if (namePropertyPrecedence > 2 && lowerKey === 'name') {
  154. namePropertyPrecedence = 2;
  155. nameProperty = key;
  156. } else if (namePropertyPrecedence > 3 && /title/i.test(key)) {
  157. namePropertyPrecedence = 3;
  158. nameProperty = key;
  159. } else if (namePropertyPrecedence > 4 && /name/i.test(key)) {
  160. namePropertyPrecedence = 4;
  161. nameProperty = key;
  162. }
  163. }
  164. }
  165. if (defined(nameProperty)) {
  166. entity.name = properties[nameProperty];
  167. }
  168. }
  169. var description = properties.description;
  170. if (!defined(description)) {
  171. entity.description = new CallbackProperty(createDescriptionCallback(properties, nameProperty), true);
  172. } else {
  173. entity.description = new ConstantProperty(description);
  174. }
  175. }
  176. return entity;
  177. }
  178. function coordinatesArrayToCartesianArray(coordinates, crsFunction) {
  179. var positions = new Array(coordinates.length);
  180. for (var i = 0; i < coordinates.length; i++) {
  181. positions[i] = crsFunction(coordinates[i]);
  182. }
  183. return positions;
  184. }
  185. // GeoJSON processing functions
  186. function processFeature(dataSource, feature, notUsed, crsFunction, options) {
  187. if (!defined(feature.geometry)) {
  188. throw new RuntimeError('feature.geometry is required.');
  189. }
  190. if (feature.geometry === null) {
  191. //Null geometry is allowed, so just create an empty entity instance for it.
  192. createObject(feature, dataSource._entityCollection);
  193. } else {
  194. var geometryType = feature.geometry.type;
  195. var geometryHandler = geometryTypes[geometryType];
  196. if (!defined(geometryHandler)) {
  197. throw new RuntimeError('Unknown geometry type: ' + geometryType);
  198. }
  199. geometryHandler(dataSource, feature, feature.geometry, crsFunction, options);
  200. }
  201. }
  202. function processFeatureCollection(dataSource, featureCollection, notUsed, crsFunction, options) {
  203. var features = featureCollection.features;
  204. for (var i = 0, len = features.length; i < len; i++) {
  205. processFeature(dataSource, features[i], undefined, crsFunction, options);
  206. }
  207. }
  208. function processGeometryCollection(dataSource, geoJson, geometryCollection, crsFunction, options) {
  209. var geometries = geometryCollection.geometries;
  210. for (var i = 0, len = geometries.length; i < len; i++) {
  211. var geometry = geometries[i];
  212. var geometryType = geometry.type;
  213. var geometryHandler = geometryTypes[geometryType];
  214. if (!defined(geometryHandler)) {
  215. throw new RuntimeError('Unknown geometry type: ' + geometryType);
  216. }
  217. geometryHandler(dataSource, geoJson, geometry, crsFunction, options);
  218. }
  219. }
  220. function createPoint(dataSource, geoJson, crsFunction, coordinates, options) {
  221. var symbol = options.markerSymbol;
  222. var color = options.markerColor;
  223. var size = options.markerSize;
  224. var properties = geoJson.properties;
  225. if (defined(properties)) {
  226. var cssColor = properties['marker-color'];
  227. if (defined(cssColor)) {
  228. color = Color.fromCssColorString(cssColor);
  229. }
  230. size = defaultValue(sizes[properties['marker-size']], size);
  231. symbol = defaultValue(properties['marker-symbol'], symbol);
  232. }
  233. stringifyScratch[0] = symbol;
  234. stringifyScratch[1] = color;
  235. stringifyScratch[2] = size;
  236. var id = JSON.stringify(stringifyScratch);
  237. var dataURLPromise = dataSource._pinCache[id];
  238. if (!defined(dataURLPromise)) {
  239. var canvasOrPromise;
  240. if (defined(symbol)) {
  241. if (symbol.length === 1) {
  242. canvasOrPromise = dataSource._pinBuilder.fromText(symbol.toUpperCase(), color, size);
  243. } else {
  244. canvasOrPromise = dataSource._pinBuilder.fromMakiIconId(symbol, color, size);
  245. }
  246. } else {
  247. canvasOrPromise = dataSource._pinBuilder.fromColor(color, size);
  248. }
  249. dataURLPromise = when(canvasOrPromise, function(canvas) {
  250. return canvas.toDataURL();
  251. });
  252. dataSource._pinCache[id] = dataURLPromise;
  253. }
  254. dataSource._promises.push(when(dataURLPromise, function(dataUrl) {
  255. var billboard = new BillboardGraphics();
  256. billboard.verticalOrigin = new ConstantProperty(VerticalOrigin.BOTTOM);
  257. billboard.image = new ConstantProperty(dataUrl);
  258. var entity = createObject(geoJson, dataSource._entityCollection);
  259. entity.billboard = billboard;
  260. entity.position = new ConstantPositionProperty(crsFunction(coordinates));
  261. }));
  262. }
  263. function processPoint(dataSource, geoJson, geometry, crsFunction, options) {
  264. createPoint(dataSource, geoJson, crsFunction, geometry.coordinates, options);
  265. }
  266. function processMultiPoint(dataSource, geoJson, geometry, crsFunction, options) {
  267. var coordinates = geometry.coordinates;
  268. for (var i = 0; i < coordinates.length; i++) {
  269. createPoint(dataSource, geoJson, crsFunction, coordinates[i], options);
  270. }
  271. }
  272. function createLineString(dataSource, geoJson, crsFunction, coordinates, options) {
  273. var material = options.strokeMaterialProperty;
  274. var widthProperty = options.strokeWidthProperty;
  275. var properties = geoJson.properties;
  276. if (defined(properties)) {
  277. var width = properties['stroke-width'];
  278. if (defined(width)) {
  279. widthProperty = new ConstantProperty(width);
  280. }
  281. var color;
  282. var stroke = properties.stroke;
  283. if (defined(stroke)) {
  284. color = Color.fromCssColorString(stroke);
  285. }
  286. var opacity = properties['stroke-opacity'];
  287. if (defined(opacity) && opacity !== 1.0) {
  288. if (!defined(color)) {
  289. color = material.color.clone();
  290. }
  291. color.alpha = opacity;
  292. }
  293. if (defined(color)) {
  294. material = ColorMaterialProperty.fromColor(color);
  295. }
  296. }
  297. var polyline = new PolylineGraphics();
  298. polyline.material = material;
  299. polyline.width = widthProperty;
  300. polyline.positions = new ConstantProperty(coordinatesArrayToCartesianArray(coordinates, crsFunction));
  301. var entity = createObject(geoJson, dataSource._entityCollection);
  302. entity.polyline = polyline;
  303. }
  304. function processLineString(dataSource, geoJson, geometry, crsFunction, options) {
  305. createLineString(dataSource, geoJson, crsFunction, geometry.coordinates, options);
  306. }
  307. function processMultiLineString(dataSource, geoJson, geometry, crsFunction, options) {
  308. var lineStrings = geometry.coordinates;
  309. for (var i = 0; i < lineStrings.length; i++) {
  310. createLineString(dataSource, geoJson, crsFunction, lineStrings[i], options);
  311. }
  312. }
  313. function createPolygon(dataSource, geoJson, crsFunction, coordinates, options) {
  314. var outlineColorProperty = options.strokeMaterialProperty.color;
  315. var material = options.fillMaterialProperty;
  316. var widthProperty = options.strokeWidthProperty;
  317. var properties = geoJson.properties;
  318. if (defined(properties)) {
  319. var width = properties['stroke-width'];
  320. if (defined(width)) {
  321. widthProperty = new ConstantProperty(width);
  322. }
  323. var color;
  324. var stroke = properties.stroke;
  325. if (defined(stroke)) {
  326. color = Color.fromCssColorString(stroke);
  327. }
  328. var opacity = properties['stroke-opacity'];
  329. if (defined(opacity) && opacity !== 1.0) {
  330. if (!defined(color)) {
  331. color = options.strokeMaterialProperty.color.clone();
  332. }
  333. color.alpha = opacity;
  334. }
  335. if (defined(color)) {
  336. outlineColorProperty = new ConstantProperty(color);
  337. }
  338. var fillColor;
  339. var fill = properties.fill;
  340. if (defined(fill)) {
  341. fillColor = Color.fromCssColorString(fill);
  342. fillColor.alpha = material.color.alpha;
  343. }
  344. opacity = properties['fill-opacity'];
  345. if (defined(opacity) && opacity !== material.color.alpha) {
  346. if (!defined(fillColor)) {
  347. fillColor = material.color.clone();
  348. }
  349. fillColor.alpha = opacity;
  350. }
  351. if (defined(fillColor)) {
  352. material = ColorMaterialProperty.fromColor(fillColor);
  353. }
  354. }
  355. var polygon = new PolygonGraphics();
  356. polygon.outline = new ConstantProperty(true);
  357. polygon.outlineColor = outlineColorProperty;
  358. polygon.outlineWidth = widthProperty;
  359. polygon.material = material;
  360. polygon.positions = new ConstantProperty(coordinatesArrayToCartesianArray(coordinates, crsFunction));
  361. if (coordinates.length > 0 && coordinates[0].length > 2) {
  362. polygon.perPositionHeight = new ConstantProperty(true);
  363. }
  364. var entity = createObject(geoJson, dataSource._entityCollection);
  365. entity.polygon = polygon;
  366. }
  367. function processPolygon(dataSource, geoJson, geometry, crsFunction, options) {
  368. createPolygon(dataSource, geoJson, crsFunction, geometry.coordinates[0], options);
  369. }
  370. function processMultiPolygon(dataSource, geoJson, geometry, crsFunction, options) {
  371. var polygons = geometry.coordinates;
  372. for (var i = 0; i < polygons.length; i++) {
  373. createPolygon(dataSource, geoJson, crsFunction, polygons[i][0], options);
  374. }
  375. }
  376. function processTopology(dataSource, geoJson, geometry, crsFunction, options) {
  377. for ( var property in geometry.objects) {
  378. if (geometry.objects.hasOwnProperty(property)) {
  379. var feature = topojson.feature(geometry, geometry.objects[property]);
  380. var typeHandler = geoJsonObjectTypes[feature.type];
  381. typeHandler(dataSource, feature, feature, crsFunction, options);
  382. }
  383. }
  384. }
  385. var geoJsonObjectTypes = {
  386. Feature : processFeature,
  387. FeatureCollection : processFeatureCollection,
  388. GeometryCollection : processGeometryCollection,
  389. LineString : processLineString,
  390. MultiLineString : processMultiLineString,
  391. MultiPoint : processMultiPoint,
  392. MultiPolygon : processMultiPolygon,
  393. Point : processPoint,
  394. Polygon : processPolygon,
  395. Topology : processTopology
  396. };
  397. var geometryTypes = {
  398. GeometryCollection : processGeometryCollection,
  399. LineString : processLineString,
  400. MultiLineString : processMultiLineString,
  401. MultiPoint : processMultiPoint,
  402. MultiPolygon : processMultiPolygon,
  403. Point : processPoint,
  404. Polygon : processPolygon,
  405. Topology : processTopology
  406. };
  407. /**
  408. * A {@link DataSource} which processes both
  409. * {@link http://www.geojson.org/|GeoJSON} and {@link https://github.com/mbostock/topojson|TopoJSON} data.
  410. * {@link https://github.com/mapbox/simplestyle-spec|Simplestyle} properties will also be used if they
  411. * are present.
  412. *
  413. * @alias GeoJsonDataSource
  414. * @constructor
  415. *
  416. * @param {String} [name] The name of this data source. If undefined, a name will be taken from
  417. * the name of the GeoJSON file.
  418. *
  419. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=GeoJSON%20and%20TopoJSON.html|Cesium Sandcastle GeoJSON and TopoJSON Demo}
  420. * @demo {@link http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=GeoJSON%20simplestyle.html|Cesium Sandcastle GeoJSON simplestyle Demo}
  421. *
  422. * @example
  423. * var viewer = new Cesium.Viewer('cesiumContainer');
  424. * viewer.dataSources.add(Cesium.GeoJsonDataSource.fromUrl('../../SampleData/ne_10m_us_states.topojson', {
  425. * stroke: Cesium.Color.HOTPINK,
  426. * fill: Cesium.Color.PINK,
  427. * strokeWidth: 3,
  428. * markerSymbol: '?'
  429. * }));
  430. */
  431. var GeoJsonDataSource = function(name) {
  432. this._name = name;
  433. this._changed = new Event();
  434. this._error = new Event();
  435. this._isLoading = false;
  436. this._loading = new Event();
  437. this._entityCollection = new EntityCollection();
  438. this._promises = [];
  439. this._pinCache = {};
  440. this._pinBuilder = new PinBuilder();
  441. };
  442. /**
  443. * Creates a new instance and asynchronously loads the provided url.
  444. *
  445. * @param {Object} url The url to be processed.
  446. * @param {Object} [options] An object with the following properties:
  447. * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels.
  448. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point.
  449. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point.
  450. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines.
  451. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines.
  452. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors.
  453. *
  454. * @returns {GeoJsonDataSource} A new instance set to load the specified url.
  455. */
  456. GeoJsonDataSource.fromUrl = function(url, options) {
  457. var result = new GeoJsonDataSource();
  458. result.loadUrl(url, options);
  459. return result;
  460. };
  461. defineProperties(GeoJsonDataSource, {
  462. /**
  463. * Gets or sets the default size of the map pin created for each point, in pixels.
  464. * @memberof GeoJsonDataSource
  465. * @type {Number}
  466. * @default 48
  467. */
  468. markerSize : {
  469. get : function() {
  470. return defaultMarkerSize;
  471. },
  472. set : function(value) {
  473. defaultMarkerSize = value;
  474. }
  475. },
  476. /**
  477. * Gets or sets the default symbol of the map pin created for each point.
  478. * This can be any valid {@link http://mapbox.com/maki/|Maki} identifier, any single character,
  479. * or blank if no symbol is to be used.
  480. * @memberof GeoJsonDataSource
  481. * @type {String}
  482. */
  483. markerSymbol : {
  484. get : function() {
  485. return defaultMarkerSymbol;
  486. },
  487. set : function(value) {
  488. defaultMarkerSymbol = value;
  489. }
  490. },
  491. /**
  492. * Gets or sets the default color of the map pin created for each point.
  493. * @memberof GeoJsonDataSource
  494. * @type {Color}
  495. * @default Color.ROYALBLUE
  496. */
  497. markerColor : {
  498. get : function() {
  499. return defaultMarkerColor;
  500. },
  501. set : function(value) {
  502. defaultMarkerColor = value;
  503. }
  504. },
  505. /**
  506. * Gets or sets the default color of polylines and polygon outlines.
  507. * @memberof GeoJsonDataSource
  508. * @type {Color}
  509. * @default Color.BLACK
  510. */
  511. stroke : {
  512. get : function() {
  513. return defaultStroke;
  514. },
  515. set : function(value) {
  516. defaultStroke = value;
  517. defaultStrokeMaterialProperty.color.setValue(value);
  518. }
  519. },
  520. /**
  521. * Gets or sets the default width of polylines and polygon outlines.
  522. * @memberof GeoJsonDataSource
  523. * @type {Number}
  524. * @default 2.0
  525. */
  526. strokeWidth : {
  527. get : function() {
  528. return defaultStrokeWidth;
  529. },
  530. set : function(value) {
  531. defaultStrokeWidth = value;
  532. defaultStrokeWidthProperty.setValue(value);
  533. }
  534. },
  535. /**
  536. * Gets or sets default color for polygon interiors.
  537. * @memberof GeoJsonDataSource
  538. * @type {Color}
  539. * @default Color.YELLOW
  540. */
  541. fill : {
  542. get : function() {
  543. return defaultFill;
  544. },
  545. set : function(value) {
  546. defaultFill = value;
  547. defaultFillMaterialProperty = ColorMaterialProperty.fromColor(defaultFill);
  548. }
  549. },
  550. /**
  551. * Gets an object that maps the name of a crs to a callback function which takes a GeoJSON coordinate
  552. * and transforms it into a WGS84 Earth-fixed Cartesian. Older versions of GeoJSON which
  553. * supported the EPSG type can be added to this list as well, by specifying the complete EPSG name,
  554. * for example 'EPSG:4326'.
  555. * @memberof GeoJsonDataSource
  556. * @type {Object}
  557. */
  558. crsNames : {
  559. get : function() {
  560. return crsNames;
  561. }
  562. },
  563. /**
  564. * Gets an object that maps the href property of a crs link to a callback function
  565. * which takes the crs properties object and returns a Promise that resolves
  566. * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian.
  567. * Items in this object take precedence over those defined in <code>crsLinkHrefs</code>, assuming
  568. * the link has a type specified.
  569. * @memberof GeoJsonDataSource
  570. * @type {Object}
  571. */
  572. crsLinkHrefs : {
  573. get : function() {
  574. return crsLinkHrefs;
  575. }
  576. },
  577. /**
  578. * Gets an object that maps the type property of a crs link to a callback function
  579. * which takes the crs properties object and returns a Promise that resolves
  580. * to a function that takes a GeoJSON coordinate and transforms it into a WGS84 Earth-fixed Cartesian.
  581. * Items in <code>crsLinkHrefs</code> take precedence over this object.
  582. * @memberof GeoJsonDataSource
  583. * @type {Object}
  584. */
  585. crsLinkTypes : {
  586. get : function() {
  587. return crsLinkTypes;
  588. }
  589. }
  590. });
  591. defineProperties(GeoJsonDataSource.prototype, {
  592. /**
  593. * Gets a human-readable name for this instance.
  594. * @memberof GeoJsonDataSource.prototype
  595. * @type {String}
  596. */
  597. name : {
  598. get : function() {
  599. return this._name;
  600. }
  601. },
  602. /**
  603. * This DataSource only defines static data, therefore this property is always undefined.
  604. * @memberof GeoJsonDataSource.prototype
  605. * @type {DataSourceClock}
  606. */
  607. clock : {
  608. value : undefined,
  609. writable : false
  610. },
  611. /**
  612. * Gets the collection of {@link Entity} instances.
  613. * @memberof GeoJsonDataSource.prototype
  614. * @type {EntityCollection}
  615. */
  616. entities : {
  617. get : function() {
  618. return this._entityCollection;
  619. }
  620. },
  621. /**
  622. * Gets a value indicating if the data source is currently loading data.
  623. * @memberof GeoJsonDataSource.prototype
  624. * @type {Boolean}
  625. */
  626. isLoading : {
  627. get : function() {
  628. return this._isLoading;
  629. }
  630. },
  631. /**
  632. * Gets an event that will be raised when the underlying data changes.
  633. * @memberof GeoJsonDataSource.prototype
  634. * @type {Event}
  635. */
  636. changedEvent : {
  637. get : function() {
  638. return this._changed;
  639. }
  640. },
  641. /**
  642. * Gets an event that will be raised if an error is encountered during processing.
  643. * @memberof GeoJsonDataSource.prototype
  644. * @type {Event}
  645. */
  646. errorEvent : {
  647. get : function() {
  648. return this._error;
  649. }
  650. },
  651. /**
  652. * Gets an event that will be raised when the data source either starts or stops loading.
  653. * @memberof GeoJsonDataSource.prototype
  654. * @type {Event}
  655. */
  656. loadingEvent : {
  657. get : function() {
  658. return this._loading;
  659. }
  660. }
  661. });
  662. /**
  663. * Asynchronously loads the GeoJSON at the provided url, replacing any existing data.
  664. *
  665. * @param {Object} url The url to be processed.
  666. * @param {Object} [options] An object with the following properties:
  667. * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels.
  668. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point.
  669. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point.
  670. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines.
  671. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines.
  672. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors.
  673. *
  674. * @returns {Promise} a promise that will resolve when the GeoJSON is loaded.
  675. */
  676. GeoJsonDataSource.prototype.loadUrl = function(url, options) {
  677. //>>includeStart('debug', pragmas.debug);
  678. if (!defined(url)) {
  679. throw new DeveloperError('url is required.');
  680. }
  681. //>>includeEnd('debug');
  682. DataSource.setLoading(this, true);
  683. var that = this;
  684. return when(loadJson(url), function(geoJson) {
  685. return load(that, geoJson, url, options);
  686. }).otherwise(function(error) {
  687. DataSource.setLoading(that, false);
  688. that._error.raiseEvent(that, error);
  689. return when.reject(error);
  690. });
  691. };
  692. /**
  693. * Asynchronously loads the provided GeoJSON object, replacing any existing data.
  694. *
  695. * @param {Object} geoJson The object to be processed.
  696. * @param {Object} [options] An object with the following properties:
  697. * @param {String} [options.sourceUri] The base URI of any relative links in the geoJson object.
  698. * @param {Number} [options.markerSize=GeoJsonDataSource.markerSize] The default size of the map pin created for each point, in pixels.
  699. * @param {String} [options.markerSymbol=GeoJsonDataSource.markerSymbol] The default symbol of the map pin created for each point.
  700. * @param {Color} [options.markerColor=GeoJsonDataSource.markerColor] The default color of the map pin created for each point.
  701. * @param {Color} [options.stroke=GeoJsonDataSource.stroke] The default color of polylines and polygon outlines.
  702. * @param {Number} [options.strokeWidth=GeoJsonDataSource.strokeWidth] The default width of polylines and polygon outlines.
  703. * @param {Color} [options.fill=GeoJsonDataSource.fill] The default color for polygon interiors.
  704. * @returns {Promise} a promise that will resolve when the GeoJSON is loaded.
  705. *
  706. * @exception {DeveloperError} Unsupported GeoJSON object type.
  707. * @exception {RuntimeError} crs is null.
  708. * @exception {RuntimeError} crs.properties is undefined.
  709. * @exception {RuntimeError} Unknown crs name.
  710. * @exception {RuntimeError} Unable to resolve crs link.
  711. * @exception {RuntimeError} Unknown crs type.
  712. */
  713. GeoJsonDataSource.prototype.load = function(geoJson, options) {
  714. //>>includeStart('debug', pragmas.debug);
  715. if (!defined(geoJson)) {
  716. throw new DeveloperError('geoJson is required.');
  717. }
  718. //>>includeEnd('debug');
  719. var sourceUri = options;
  720. if (typeof options === 'string') {
  721. sourceUri = options;
  722. 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');
  723. } else if (defined(options)) {
  724. sourceUri = options.sourceUri;
  725. }
  726. return load(this, geoJson, sourceUri, options);
  727. };
  728. function load(that, geoJson, sourceUri, options) {
  729. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  730. options = {
  731. markerSize : defaultValue(options.markerSize, defaultMarkerSize),
  732. markerSymbol : defaultValue(options.markerSymbol, defaultMarkerSymbol),
  733. markerColor : defaultValue(options.markerColor, defaultMarkerColor),
  734. strokeWidthProperty : new ConstantProperty(defaultValue(options.strokeWidth, defaultStrokeWidth)),
  735. strokeMaterialProperty : ColorMaterialProperty.fromColor(defaultValue(options.stroke, defaultStroke)),
  736. fillMaterialProperty : ColorMaterialProperty.fromColor(defaultValue(options.fill, defaultFill))
  737. };
  738. var name;
  739. if (defined(sourceUri)) {
  740. name = getFilenameFromUri(sourceUri);
  741. }
  742. if (defined(name) && that._name !== name) {
  743. that._name = name;
  744. that._changed.raiseEvent(that);
  745. }
  746. var typeHandler = geoJsonObjectTypes[geoJson.type];
  747. if (!defined(typeHandler)) {
  748. throw new DeveloperError('Unsupported GeoJSON object type: ' + geoJson.type);
  749. }
  750. //Check for a Coordinate Reference System.
  751. var crsFunction = defaultCrsFunction;
  752. var crs = geoJson.crs;
  753. if (defined(crs)) {
  754. if (crs === null) {
  755. throw new RuntimeError('crs is null.');
  756. }
  757. if (!defined(crs.properties)) {
  758. throw new RuntimeError('crs.properties is undefined.');
  759. }
  760. var properties = crs.properties;
  761. if (crs.type === 'name') {
  762. crsFunction = crsNames[properties.name];
  763. if (!defined(crsFunction)) {
  764. throw new RuntimeError('Unknown crs name: ' + properties.name);
  765. }
  766. } else if (crs.type === 'link') {
  767. var handler = crsLinkHrefs[properties.href];
  768. if (!defined(handler)) {
  769. handler = crsLinkTypes[properties.type];
  770. }
  771. if (!defined(handler)) {
  772. throw new RuntimeError('Unable to resolve crs link: ' + JSON.stringify(properties));
  773. }
  774. crsFunction = handler(properties);
  775. } else if (crs.type === 'EPSG') {
  776. crsFunction = crsNames['EPSG:' + properties.code];
  777. if (!defined(crsFunction)) {
  778. throw new RuntimeError('Unknown crs EPSG code: ' + properties.code);
  779. }
  780. } else {
  781. throw new RuntimeError('Unknown crs type: ' + crs.type);
  782. }
  783. }
  784. DataSource.setLoading(that, true);
  785. return when(crsFunction, function(crsFunction) {
  786. that._entityCollection.removeAll();
  787. typeHandler(that, geoJson, geoJson, crsFunction, options);
  788. return when.all(that._promises, function() {
  789. that._promises.length = 0;
  790. that._pinCache = {};
  791. DataSource.setLoading(that, false);
  792. });
  793. }).otherwise(function(error) {
  794. DataSource.setLoading(that, false);
  795. that._error.raiseEvent(that, error);
  796. return when.reject(error);
  797. });
  798. }
  799. return GeoJsonDataSource;
  800. });