GeoJsonDataSource.js 36 KB

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