Source: Core/Rectangle.js

  1. /*global define*/
  2. define([
  3. './Cartographic',
  4. './defaultValue',
  5. './defined',
  6. './defineProperties',
  7. './DeveloperError',
  8. './Ellipsoid',
  9. './freezeObject',
  10. './Math'
  11. ], function(
  12. Cartographic,
  13. defaultValue,
  14. defined,
  15. defineProperties,
  16. DeveloperError,
  17. Ellipsoid,
  18. freezeObject,
  19. CesiumMath) {
  20. 'use strict';
  21. /**
  22. * A two dimensional region specified as longitude and latitude coordinates.
  23. *
  24. * @alias Rectangle
  25. * @constructor
  26. *
  27. * @param {Number} [west=0.0] The westernmost longitude, in radians, in the range [-Pi, Pi].
  28. * @param {Number} [south=0.0] The southernmost latitude, in radians, in the range [-Pi/2, Pi/2].
  29. * @param {Number} [east=0.0] The easternmost longitude, in radians, in the range [-Pi, Pi].
  30. * @param {Number} [north=0.0] The northernmost latitude, in radians, in the range [-Pi/2, Pi/2].
  31. *
  32. * @see Packable
  33. */
  34. function Rectangle(west, south, east, north) {
  35. /**
  36. * The westernmost longitude in radians in the range [-Pi, Pi].
  37. *
  38. * @type {Number}
  39. * @default 0.0
  40. */
  41. this.west = defaultValue(west, 0.0);
  42. /**
  43. * The southernmost latitude in radians in the range [-Pi/2, Pi/2].
  44. *
  45. * @type {Number}
  46. * @default 0.0
  47. */
  48. this.south = defaultValue(south, 0.0);
  49. /**
  50. * The easternmost longitude in radians in the range [-Pi, Pi].
  51. *
  52. * @type {Number}
  53. * @default 0.0
  54. */
  55. this.east = defaultValue(east, 0.0);
  56. /**
  57. * The northernmost latitude in radians in the range [-Pi/2, Pi/2].
  58. *
  59. * @type {Number}
  60. * @default 0.0
  61. */
  62. this.north = defaultValue(north, 0.0);
  63. }
  64. defineProperties(Rectangle.prototype, {
  65. /**
  66. * Gets the width of the rectangle in radians.
  67. * @memberof Rectangle.prototype
  68. * @type {Number}
  69. */
  70. width : {
  71. get : function() {
  72. return Rectangle.computeWidth(this);
  73. }
  74. },
  75. /**
  76. * Gets the height of the rectangle in radians.
  77. * @memberof Rectangle.prototype
  78. * @type {Number}
  79. */
  80. height : {
  81. get : function() {
  82. return Rectangle.computeHeight(this);
  83. }
  84. }
  85. });
  86. /**
  87. * The number of elements used to pack the object into an array.
  88. * @type {Number}
  89. */
  90. Rectangle.packedLength = 4;
  91. /**
  92. * Stores the provided instance into the provided array.
  93. *
  94. * @param {Rectangle} value The value to pack.
  95. * @param {Number[]} array The array to pack into.
  96. * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements.
  97. *
  98. * @returns {Number[]} The array that was packed into
  99. */
  100. Rectangle.pack = function(value, array, startingIndex) {
  101. //>>includeStart('debug', pragmas.debug);
  102. if (!defined(value)) {
  103. throw new DeveloperError('value is required');
  104. }
  105. if (!defined(array)) {
  106. throw new DeveloperError('array is required');
  107. }
  108. //>>includeEnd('debug');
  109. startingIndex = defaultValue(startingIndex, 0);
  110. array[startingIndex++] = value.west;
  111. array[startingIndex++] = value.south;
  112. array[startingIndex++] = value.east;
  113. array[startingIndex] = value.north;
  114. return array;
  115. };
  116. /**
  117. * Retrieves an instance from a packed array.
  118. *
  119. * @param {Number[]} array The packed array.
  120. * @param {Number} [startingIndex=0] The starting index of the element to be unpacked.
  121. * @param {Rectangle} [result] The object into which to store the result.
  122. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if one was not provided.
  123. */
  124. Rectangle.unpack = function(array, startingIndex, result) {
  125. //>>includeStart('debug', pragmas.debug);
  126. if (!defined(array)) {
  127. throw new DeveloperError('array is required');
  128. }
  129. //>>includeEnd('debug');
  130. startingIndex = defaultValue(startingIndex, 0);
  131. if (!defined(result)) {
  132. result = new Rectangle();
  133. }
  134. result.west = array[startingIndex++];
  135. result.south = array[startingIndex++];
  136. result.east = array[startingIndex++];
  137. result.north = array[startingIndex];
  138. return result;
  139. };
  140. /**
  141. * Computes the width of a rectangle in radians.
  142. * @param {Rectangle} rectangle The rectangle to compute the width of.
  143. * @returns {Number} The width.
  144. */
  145. Rectangle.computeWidth = function(rectangle) {
  146. //>>includeStart('debug', pragmas.debug);
  147. if (!defined(rectangle)) {
  148. throw new DeveloperError('rectangle is required.');
  149. }
  150. //>>includeEnd('debug');
  151. var east = rectangle.east;
  152. var west = rectangle.west;
  153. if (east < west) {
  154. east += CesiumMath.TWO_PI;
  155. }
  156. return east - west;
  157. };
  158. /**
  159. * Computes the height of a rectangle in radians.
  160. * @param {Rectangle} rectangle The rectangle to compute the height of.
  161. * @returns {Number} The height.
  162. */
  163. Rectangle.computeHeight = function(rectangle) {
  164. //>>includeStart('debug', pragmas.debug);
  165. if (!defined(rectangle)) {
  166. throw new DeveloperError('rectangle is required.');
  167. }
  168. //>>includeEnd('debug');
  169. return rectangle.north - rectangle.south;
  170. };
  171. /**
  172. * Creates an rectangle given the boundary longitude and latitude in degrees.
  173. *
  174. * @param {Number} [west=0.0] The westernmost longitude in degrees in the range [-180.0, 180.0].
  175. * @param {Number} [south=0.0] The southernmost latitude in degrees in the range [-90.0, 90.0].
  176. * @param {Number} [east=0.0] The easternmost longitude in degrees in the range [-180.0, 180.0].
  177. * @param {Number} [north=0.0] The northernmost latitude in degrees in the range [-90.0, 90.0].
  178. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  179. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  180. *
  181. * @example
  182. * var rectangle = Cesium.Rectangle.fromDegrees(0.0, 20.0, 10.0, 30.0);
  183. */
  184. Rectangle.fromDegrees = function(west, south, east, north, result) {
  185. west = CesiumMath.toRadians(defaultValue(west, 0.0));
  186. south = CesiumMath.toRadians(defaultValue(south, 0.0));
  187. east = CesiumMath.toRadians(defaultValue(east, 0.0));
  188. north = CesiumMath.toRadians(defaultValue(north, 0.0));
  189. if (!defined(result)) {
  190. return new Rectangle(west, south, east, north);
  191. }
  192. result.west = west;
  193. result.south = south;
  194. result.east = east;
  195. result.north = north;
  196. return result;
  197. };
  198. /**
  199. * Creates the smallest possible Rectangle that encloses all positions in the provided array.
  200. *
  201. * @param {Cartographic[]} cartographics The list of Cartographic instances.
  202. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  203. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  204. */
  205. Rectangle.fromCartographicArray = function(cartographics, result) {
  206. //>>includeStart('debug', pragmas.debug);
  207. if (!defined(cartographics)) {
  208. throw new DeveloperError('cartographics is required.');
  209. }
  210. //>>includeEnd('debug');
  211. var west = Number.MAX_VALUE;
  212. var east = -Number.MAX_VALUE;
  213. var westOverIDL = Number.MAX_VALUE;
  214. var eastOverIDL = -Number.MAX_VALUE;
  215. var south = Number.MAX_VALUE;
  216. var north = -Number.MAX_VALUE;
  217. for ( var i = 0, len = cartographics.length; i < len; i++) {
  218. var position = cartographics[i];
  219. west = Math.min(west, position.longitude);
  220. east = Math.max(east, position.longitude);
  221. south = Math.min(south, position.latitude);
  222. north = Math.max(north, position.latitude);
  223. var lonAdjusted = position.longitude >= 0 ? position.longitude : position.longitude + CesiumMath.TWO_PI;
  224. westOverIDL = Math.min(westOverIDL, lonAdjusted);
  225. eastOverIDL = Math.max(eastOverIDL, lonAdjusted);
  226. }
  227. if(east - west > eastOverIDL - westOverIDL) {
  228. west = westOverIDL;
  229. east = eastOverIDL;
  230. if (east > CesiumMath.PI) {
  231. east = east - CesiumMath.TWO_PI;
  232. }
  233. if (west > CesiumMath.PI) {
  234. west = west - CesiumMath.TWO_PI;
  235. }
  236. }
  237. if (!defined(result)) {
  238. return new Rectangle(west, south, east, north);
  239. }
  240. result.west = west;
  241. result.south = south;
  242. result.east = east;
  243. result.north = north;
  244. return result;
  245. };
  246. /**
  247. * Creates the smallest possible Rectangle that encloses all positions in the provided array.
  248. *
  249. * @param {Cartesian[]} cartesians The list of Cartesian instances.
  250. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid the cartesians are on.
  251. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  252. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  253. */
  254. Rectangle.fromCartesianArray = function(cartesians, ellipsoid, result) {
  255. //>>includeStart('debug', pragmas.debug);
  256. if (!defined(cartesians)) {
  257. throw new DeveloperError('cartesians is required.');
  258. }
  259. //>>includeEnd('debug');
  260. var west = Number.MAX_VALUE;
  261. var east = -Number.MAX_VALUE;
  262. var westOverIDL = Number.MAX_VALUE;
  263. var eastOverIDL = -Number.MAX_VALUE;
  264. var south = Number.MAX_VALUE;
  265. var north = -Number.MAX_VALUE;
  266. for ( var i = 0, len = cartesians.length; i < len; i++) {
  267. var position = ellipsoid.cartesianToCartographic(cartesians[i]);
  268. west = Math.min(west, position.longitude);
  269. east = Math.max(east, position.longitude);
  270. south = Math.min(south, position.latitude);
  271. north = Math.max(north, position.latitude);
  272. var lonAdjusted = position.longitude >= 0 ? position.longitude : position.longitude + CesiumMath.TWO_PI;
  273. westOverIDL = Math.min(westOverIDL, lonAdjusted);
  274. eastOverIDL = Math.max(eastOverIDL, lonAdjusted);
  275. }
  276. if(east - west > eastOverIDL - westOverIDL) {
  277. west = westOverIDL;
  278. east = eastOverIDL;
  279. if (east > CesiumMath.PI) {
  280. east = east - CesiumMath.TWO_PI;
  281. }
  282. if (west > CesiumMath.PI) {
  283. west = west - CesiumMath.TWO_PI;
  284. }
  285. }
  286. if (!defined(result)) {
  287. return new Rectangle(west, south, east, north);
  288. }
  289. result.west = west;
  290. result.south = south;
  291. result.east = east;
  292. result.north = north;
  293. return result;
  294. };
  295. /**
  296. * Duplicates an Rectangle.
  297. *
  298. * @param {Rectangle} rectangle The rectangle to clone.
  299. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  300. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. (Returns undefined if rectangle is undefined)
  301. */
  302. Rectangle.clone = function(rectangle, result) {
  303. if (!defined(rectangle)) {
  304. return undefined;
  305. }
  306. if (!defined(result)) {
  307. return new Rectangle(rectangle.west, rectangle.south, rectangle.east, rectangle.north);
  308. }
  309. result.west = rectangle.west;
  310. result.south = rectangle.south;
  311. result.east = rectangle.east;
  312. result.north = rectangle.north;
  313. return result;
  314. };
  315. /**
  316. * Duplicates this Rectangle.
  317. *
  318. * @param {Rectangle} [result] The object onto which to store the result.
  319. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  320. */
  321. Rectangle.prototype.clone = function(result) {
  322. return Rectangle.clone(this, result);
  323. };
  324. /**
  325. * Compares the provided Rectangle with this Rectangle componentwise and returns
  326. * <code>true</code> if they are equal, <code>false</code> otherwise.
  327. *
  328. * @param {Rectangle} [other] The Rectangle to compare.
  329. * @returns {Boolean} <code>true</code> if the Rectangles are equal, <code>false</code> otherwise.
  330. */
  331. Rectangle.prototype.equals = function(other) {
  332. return Rectangle.equals(this, other);
  333. };
  334. /**
  335. * Compares the provided rectangles and returns <code>true</code> if they are equal,
  336. * <code>false</code> otherwise.
  337. *
  338. * @param {Rectangle} [left] The first Rectangle.
  339. * @param {Rectangle} [right] The second Rectangle.
  340. * @returns {Boolean} <code>true</code> if left and right are equal; otherwise <code>false</code>.
  341. */
  342. Rectangle.equals = function(left, right) {
  343. return (left === right) ||
  344. ((defined(left)) &&
  345. (defined(right)) &&
  346. (left.west === right.west) &&
  347. (left.south === right.south) &&
  348. (left.east === right.east) &&
  349. (left.north === right.north));
  350. };
  351. /**
  352. * Compares the provided Rectangle with this Rectangle componentwise and returns
  353. * <code>true</code> if they are within the provided epsilon,
  354. * <code>false</code> otherwise.
  355. *
  356. * @param {Rectangle} [other] The Rectangle to compare.
  357. * @param {Number} epsilon The epsilon to use for equality testing.
  358. * @returns {Boolean} <code>true</code> if the Rectangles are within the provided epsilon, <code>false</code> otherwise.
  359. */
  360. Rectangle.prototype.equalsEpsilon = function(other, epsilon) {
  361. //>>includeStart('debug', pragmas.debug);
  362. if (typeof epsilon !== 'number') {
  363. throw new DeveloperError('epsilon is required and must be a number.');
  364. }
  365. //>>includeEnd('debug');
  366. return defined(other) &&
  367. (Math.abs(this.west - other.west) <= epsilon) &&
  368. (Math.abs(this.south - other.south) <= epsilon) &&
  369. (Math.abs(this.east - other.east) <= epsilon) &&
  370. (Math.abs(this.north - other.north) <= epsilon);
  371. };
  372. /**
  373. * Checks an Rectangle's properties and throws if they are not in valid ranges.
  374. *
  375. * @param {Rectangle} rectangle The rectangle to validate
  376. *
  377. * @exception {DeveloperError} <code>north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  378. * @exception {DeveloperError} <code>south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  379. * @exception {DeveloperError} <code>east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  380. * @exception {DeveloperError} <code>west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  381. */
  382. Rectangle.validate = function(rectangle) {
  383. //>>includeStart('debug', pragmas.debug);
  384. if (!defined(rectangle)) {
  385. throw new DeveloperError('rectangle is required');
  386. }
  387. var north = rectangle.north;
  388. if (typeof north !== 'number') {
  389. throw new DeveloperError('north is required to be a number.');
  390. }
  391. if (north < -CesiumMath.PI_OVER_TWO || north > CesiumMath.PI_OVER_TWO) {
  392. throw new DeveloperError('north must be in the interval [-Pi/2, Pi/2].');
  393. }
  394. var south = rectangle.south;
  395. if (typeof south !== 'number') {
  396. throw new DeveloperError('south is required to be a number.');
  397. }
  398. if (south < -CesiumMath.PI_OVER_TWO || south > CesiumMath.PI_OVER_TWO) {
  399. throw new DeveloperError('south must be in the interval [-Pi/2, Pi/2].');
  400. }
  401. var west = rectangle.west;
  402. if (typeof west !== 'number') {
  403. throw new DeveloperError('west is required to be a number.');
  404. }
  405. if (west < -Math.PI || west > Math.PI) {
  406. throw new DeveloperError('west must be in the interval [-Pi, Pi].');
  407. }
  408. var east = rectangle.east;
  409. if (typeof east !== 'number') {
  410. throw new DeveloperError('east is required to be a number.');
  411. }
  412. if (east < -Math.PI || east > Math.PI) {
  413. throw new DeveloperError('east must be in the interval [-Pi, Pi].');
  414. }
  415. //>>includeEnd('debug');
  416. };
  417. /**
  418. * Computes the southwest corner of an rectangle.
  419. *
  420. * @param {Rectangle} rectangle The rectangle for which to find the corner
  421. * @param {Cartographic} [result] The object onto which to store the result.
  422. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  423. */
  424. Rectangle.southwest = function(rectangle, result) {
  425. //>>includeStart('debug', pragmas.debug);
  426. if (!defined(rectangle)) {
  427. throw new DeveloperError('rectangle is required');
  428. }
  429. //>>includeEnd('debug');
  430. if (!defined(result)) {
  431. return new Cartographic(rectangle.west, rectangle.south);
  432. }
  433. result.longitude = rectangle.west;
  434. result.latitude = rectangle.south;
  435. result.height = 0.0;
  436. return result;
  437. };
  438. /**
  439. * Computes the northwest corner of an rectangle.
  440. *
  441. * @param {Rectangle} rectangle The rectangle for which to find the corner
  442. * @param {Cartographic} [result] The object onto which to store the result.
  443. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  444. */
  445. Rectangle.northwest = function(rectangle, result) {
  446. //>>includeStart('debug', pragmas.debug);
  447. if (!defined(rectangle)) {
  448. throw new DeveloperError('rectangle is required');
  449. }
  450. //>>includeEnd('debug');
  451. if (!defined(result)) {
  452. return new Cartographic(rectangle.west, rectangle.north);
  453. }
  454. result.longitude = rectangle.west;
  455. result.latitude = rectangle.north;
  456. result.height = 0.0;
  457. return result;
  458. };
  459. /**
  460. * Computes the northeast corner of an rectangle.
  461. *
  462. * @param {Rectangle} rectangle The rectangle for which to find the corner
  463. * @param {Cartographic} [result] The object onto which to store the result.
  464. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  465. */
  466. Rectangle.northeast = function(rectangle, result) {
  467. //>>includeStart('debug', pragmas.debug);
  468. if (!defined(rectangle)) {
  469. throw new DeveloperError('rectangle is required');
  470. }
  471. //>>includeEnd('debug');
  472. if (!defined(result)) {
  473. return new Cartographic(rectangle.east, rectangle.north);
  474. }
  475. result.longitude = rectangle.east;
  476. result.latitude = rectangle.north;
  477. result.height = 0.0;
  478. return result;
  479. };
  480. /**
  481. * Computes the southeast corner of an rectangle.
  482. *
  483. * @param {Rectangle} rectangle The rectangle for which to find the corner
  484. * @param {Cartographic} [result] The object onto which to store the result.
  485. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  486. */
  487. Rectangle.southeast = function(rectangle, result) {
  488. //>>includeStart('debug', pragmas.debug);
  489. if (!defined(rectangle)) {
  490. throw new DeveloperError('rectangle is required');
  491. }
  492. //>>includeEnd('debug');
  493. if (!defined(result)) {
  494. return new Cartographic(rectangle.east, rectangle.south);
  495. }
  496. result.longitude = rectangle.east;
  497. result.latitude = rectangle.south;
  498. result.height = 0.0;
  499. return result;
  500. };
  501. /**
  502. * Computes the center of an rectangle.
  503. *
  504. * @param {Rectangle} rectangle The rectangle for which to find the center
  505. * @param {Cartographic} [result] The object onto which to store the result.
  506. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  507. */
  508. Rectangle.center = function(rectangle, result) {
  509. //>>includeStart('debug', pragmas.debug);
  510. if (!defined(rectangle)) {
  511. throw new DeveloperError('rectangle is required');
  512. }
  513. //>>includeEnd('debug');
  514. var east = rectangle.east;
  515. var west = rectangle.west;
  516. if (east < west) {
  517. east += CesiumMath.TWO_PI;
  518. }
  519. var longitude = CesiumMath.negativePiToPi((west + east) * 0.5);
  520. var latitude = (rectangle.south + rectangle.north) * 0.5;
  521. if (!defined(result)) {
  522. return new Cartographic(longitude, latitude);
  523. }
  524. result.longitude = longitude;
  525. result.latitude = latitude;
  526. result.height = 0.0;
  527. return result;
  528. };
  529. /**
  530. * Computes the intersection of two rectangles. This function assumes that the rectangle's coordinates are
  531. * latitude and longitude in radians and produces a correct intersection, taking into account the fact that
  532. * the same angle can be represented with multiple values as well as the wrapping of longitude at the
  533. * anti-meridian. For a simple intersection that ignores these factors and can be used with projected
  534. * coordinates, see {@link Rectangle.simpleIntersection}.
  535. *
  536. * @param {Rectangle} rectangle On rectangle to find an intersection
  537. * @param {Rectangle} otherRectangle Another rectangle to find an intersection
  538. * @param {Rectangle} [result] The object onto which to store the result.
  539. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection.
  540. */
  541. Rectangle.intersection = function(rectangle, otherRectangle, result) {
  542. //>>includeStart('debug', pragmas.debug);
  543. if (!defined(rectangle)) {
  544. throw new DeveloperError('rectangle is required');
  545. }
  546. if (!defined(otherRectangle)) {
  547. throw new DeveloperError('otherRectangle is required.');
  548. }
  549. //>>includeEnd('debug');
  550. var rectangleEast = rectangle.east;
  551. var rectangleWest = rectangle.west;
  552. var otherRectangleEast = otherRectangle.east;
  553. var otherRectangleWest = otherRectangle.west;
  554. if (rectangleEast < rectangleWest && otherRectangleEast > 0.0) {
  555. rectangleEast += CesiumMath.TWO_PI;
  556. } else if (otherRectangleEast < otherRectangleWest && rectangleEast > 0.0) {
  557. otherRectangleEast += CesiumMath.TWO_PI;
  558. }
  559. if (rectangleEast < rectangleWest && otherRectangleWest < 0.0) {
  560. otherRectangleWest += CesiumMath.TWO_PI;
  561. } else if (otherRectangleEast < otherRectangleWest && rectangleWest < 0.0) {
  562. rectangleWest += CesiumMath.TWO_PI;
  563. }
  564. var west = CesiumMath.negativePiToPi(Math.max(rectangleWest, otherRectangleWest));
  565. var east = CesiumMath.negativePiToPi(Math.min(rectangleEast, otherRectangleEast));
  566. if ((rectangle.west < rectangle.east || otherRectangle.west < otherRectangle.east) && east <= west) {
  567. return undefined;
  568. }
  569. var south = Math.max(rectangle.south, otherRectangle.south);
  570. var north = Math.min(rectangle.north, otherRectangle.north);
  571. if (south >= north) {
  572. return undefined;
  573. }
  574. if (!defined(result)) {
  575. return new Rectangle(west, south, east, north);
  576. }
  577. result.west = west;
  578. result.south = south;
  579. result.east = east;
  580. result.north = north;
  581. return result;
  582. };
  583. /**
  584. * Computes a simple intersection of two rectangles. Unlike {@link Rectangle.intersection}, this function
  585. * does not attempt to put the angular coordinates into a consistent range or to account for crossing the
  586. * anti-meridian. As such, it can be used for rectangles where the coordinates are not simply latitude
  587. * and longitude (i.e. projected coordinates).
  588. *
  589. * @param {Rectangle} rectangle On rectangle to find an intersection
  590. * @param {Rectangle} otherRectangle Another rectangle to find an intersection
  591. * @param {Rectangle} [result] The object onto which to store the result.
  592. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection.
  593. */
  594. Rectangle.simpleIntersection = function(rectangle, otherRectangle, result) {
  595. //>>includeStart('debug', pragmas.debug);
  596. if (!defined(rectangle)) {
  597. throw new DeveloperError('rectangle is required');
  598. }
  599. if (!defined(otherRectangle)) {
  600. throw new DeveloperError('otherRectangle is required.');
  601. }
  602. //>>includeEnd('debug');
  603. var west = Math.max(rectangle.west, otherRectangle.west);
  604. var south = Math.max(rectangle.south, otherRectangle.south);
  605. var east = Math.min(rectangle.east, otherRectangle.east);
  606. var north = Math.min(rectangle.north, otherRectangle.north);
  607. if (south >= north || west >= east) {
  608. return undefined;
  609. }
  610. if (!defined(result)) {
  611. return new Rectangle(west, south, east, north);
  612. }
  613. result.west = west;
  614. result.south = south;
  615. result.east = east;
  616. result.north = north;
  617. return result;
  618. };
  619. /**
  620. * Computes a rectangle that is the union of two rectangles.
  621. *
  622. * @param {Rectangle} rectangle A rectangle to enclose in rectangle.
  623. * @param {Rectangle} otherRectangle A rectangle to enclose in a rectangle.
  624. * @param {Rectangle} [result] The object onto which to store the result.
  625. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  626. */
  627. Rectangle.union = function(rectangle, otherRectangle, result) {
  628. //>>includeStart('debug', pragmas.debug);
  629. if (!defined(rectangle)) {
  630. throw new DeveloperError('rectangle is required');
  631. }
  632. if (!defined(otherRectangle)) {
  633. throw new DeveloperError('otherRectangle is required.');
  634. }
  635. //>>includeEnd('debug');
  636. if (!defined(result)) {
  637. result = new Rectangle();
  638. }
  639. result.west = Math.min(rectangle.west, otherRectangle.west);
  640. result.south = Math.min(rectangle.south, otherRectangle.south);
  641. result.east = Math.max(rectangle.east, otherRectangle.east);
  642. result.north = Math.max(rectangle.north, otherRectangle.north);
  643. return result;
  644. };
  645. /**
  646. * Computes a rectangle by enlarging the provided rectangle until it contains the provided cartographic.
  647. *
  648. * @param {Rectangle} rectangle A rectangle to expand.
  649. * @param {Cartographic} cartographic A cartographic to enclose in a rectangle.
  650. * @param {Rectangle} [result] The object onto which to store the result.
  651. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if one was not provided.
  652. */
  653. Rectangle.expand = function(rectangle, cartographic, result) {
  654. //>>includeStart('debug', pragmas.debug);
  655. if (!defined(rectangle)) {
  656. throw new DeveloperError('rectangle is required.');
  657. }
  658. if (!defined(cartographic)) {
  659. throw new DeveloperError('cartographic is required.');
  660. }
  661. //>>includeEnd('debug');
  662. if (!defined(result)) {
  663. result = new Rectangle();
  664. }
  665. result.west = Math.min(rectangle.west, cartographic.longitude);
  666. result.south = Math.min(rectangle.south, cartographic.latitude);
  667. result.east = Math.max(rectangle.east, cartographic.longitude);
  668. result.north = Math.max(rectangle.north, cartographic.latitude);
  669. return result;
  670. };
  671. /**
  672. * Returns true if the cartographic is on or inside the rectangle, false otherwise.
  673. *
  674. * @param {Rectangle} rectangle The rectangle
  675. * @param {Cartographic} cartographic The cartographic to test.
  676. * @returns {Boolean} true if the provided cartographic is inside the rectangle, false otherwise.
  677. */
  678. Rectangle.contains = function(rectangle, cartographic) {
  679. //>>includeStart('debug', pragmas.debug);
  680. if (!defined(rectangle)) {
  681. throw new DeveloperError('rectangle is required');
  682. }
  683. if (!defined(cartographic)) {
  684. throw new DeveloperError('cartographic is required.');
  685. }
  686. //>>includeEnd('debug');
  687. var longitude = cartographic.longitude;
  688. var latitude = cartographic.latitude;
  689. var west = rectangle.west;
  690. var east = rectangle.east;
  691. if (east < west) {
  692. east += CesiumMath.TWO_PI;
  693. if (longitude < 0.0) {
  694. longitude += CesiumMath.TWO_PI;
  695. }
  696. }
  697. return (longitude > west || CesiumMath.equalsEpsilon(longitude, west, CesiumMath.EPSILON14)) &&
  698. (longitude < east || CesiumMath.equalsEpsilon(longitude, east, CesiumMath.EPSILON14)) &&
  699. latitude >= rectangle.south &&
  700. latitude <= rectangle.north;
  701. };
  702. var subsampleLlaScratch = new Cartographic();
  703. /**
  704. * Samples an rectangle so that it includes a list of Cartesian points suitable for passing to
  705. * {@link BoundingSphere#fromPoints}. Sampling is necessary to account
  706. * for rectangles that cover the poles or cross the equator.
  707. *
  708. * @param {Rectangle} rectangle The rectangle to subsample.
  709. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
  710. * @param {Number} [surfaceHeight=0.0] The height of the rectangle above the ellipsoid.
  711. * @param {Cartesian3[]} [result] The array of Cartesians onto which to store the result.
  712. * @returns {Cartesian3[]} The modified result parameter or a new Array of Cartesians instances if none was provided.
  713. */
  714. Rectangle.subsample = function(rectangle, ellipsoid, surfaceHeight, result) {
  715. //>>includeStart('debug', pragmas.debug);
  716. if (!defined(rectangle)) {
  717. throw new DeveloperError('rectangle is required');
  718. }
  719. //>>includeEnd('debug');
  720. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  721. surfaceHeight = defaultValue(surfaceHeight, 0.0);
  722. if (!defined(result)) {
  723. result = [];
  724. }
  725. var length = 0;
  726. var north = rectangle.north;
  727. var south = rectangle.south;
  728. var east = rectangle.east;
  729. var west = rectangle.west;
  730. var lla = subsampleLlaScratch;
  731. lla.height = surfaceHeight;
  732. lla.longitude = west;
  733. lla.latitude = north;
  734. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  735. length++;
  736. lla.longitude = east;
  737. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  738. length++;
  739. lla.latitude = south;
  740. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  741. length++;
  742. lla.longitude = west;
  743. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  744. length++;
  745. if (north < 0.0) {
  746. lla.latitude = north;
  747. } else if (south > 0.0) {
  748. lla.latitude = south;
  749. } else {
  750. lla.latitude = 0.0;
  751. }
  752. for ( var i = 1; i < 8; ++i) {
  753. lla.longitude = -Math.PI + i * CesiumMath.PI_OVER_TWO;
  754. if (Rectangle.contains(rectangle, lla)) {
  755. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  756. length++;
  757. }
  758. }
  759. if (lla.latitude === 0.0) {
  760. lla.longitude = west;
  761. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  762. length++;
  763. lla.longitude = east;
  764. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  765. length++;
  766. }
  767. result.length = length;
  768. return result;
  769. };
  770. /**
  771. * The largest possible rectangle.
  772. *
  773. * @type {Rectangle}
  774. * @constant
  775. */
  776. Rectangle.MAX_VALUE = freezeObject(new Rectangle(-Math.PI, -CesiumMath.PI_OVER_TWO, Math.PI, CesiumMath.PI_OVER_TWO));
  777. return Rectangle;
  778. });