GeoJsonDataSource.js 31 KB

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