Rectangle.js 33 KB

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