Rectangle.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  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 Ellipsoid from "./Ellipsoid.js";
  6. import CesiumMath from "./Math.js";
  7. /**
  8. * A two dimensional region specified as longitude and latitude coordinates.
  9. *
  10. * @alias Rectangle
  11. * @constructor
  12. *
  13. * @param {number} [west=0.0] The westernmost longitude, in radians, in the range [-Pi, Pi].
  14. * @param {number} [south=0.0] The southernmost latitude, in radians, in the range [-Pi/2, Pi/2].
  15. * @param {number} [east=0.0] The easternmost longitude, in radians, in the range [-Pi, Pi].
  16. * @param {number} [north=0.0] The northernmost latitude, in radians, in the range [-Pi/2, Pi/2].
  17. *
  18. * @see Packable
  19. */
  20. function Rectangle(west, south, east, north) {
  21. /**
  22. * The westernmost longitude in radians in the range [-Pi, Pi].
  23. *
  24. * @type {number}
  25. * @default 0.0
  26. */
  27. this.west = defaultValue(west, 0.0);
  28. /**
  29. * The southernmost latitude in radians in the range [-Pi/2, Pi/2].
  30. *
  31. * @type {number}
  32. * @default 0.0
  33. */
  34. this.south = defaultValue(south, 0.0);
  35. /**
  36. * The easternmost longitude in radians in the range [-Pi, Pi].
  37. *
  38. * @type {number}
  39. * @default 0.0
  40. */
  41. this.east = defaultValue(east, 0.0);
  42. /**
  43. * The northernmost latitude in radians in the range [-Pi/2, Pi/2].
  44. *
  45. * @type {number}
  46. * @default 0.0
  47. */
  48. this.north = defaultValue(north, 0.0);
  49. }
  50. Object.defineProperties(Rectangle.prototype, {
  51. /**
  52. * Gets the width of the rectangle in radians.
  53. * @memberof Rectangle.prototype
  54. * @type {number}
  55. * @readonly
  56. */
  57. width: {
  58. get: function () {
  59. return Rectangle.computeWidth(this);
  60. },
  61. },
  62. /**
  63. * Gets the height of the rectangle in radians.
  64. * @memberof Rectangle.prototype
  65. * @type {number}
  66. * @readonly
  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. let east = rectangle.east;
  132. const 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. * const 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. * const 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. let west = Number.MAX_VALUE;
  211. let east = -Number.MAX_VALUE;
  212. let westOverIDL = Number.MAX_VALUE;
  213. let eastOverIDL = -Number.MAX_VALUE;
  214. let south = Number.MAX_VALUE;
  215. let north = -Number.MAX_VALUE;
  216. for (let i = 0, len = cartographics.length; i < len; i++) {
  217. const 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. const lonAdjusted =
  223. position.longitude >= 0
  224. ? position.longitude
  225. : position.longitude + CesiumMath.TWO_PI;
  226. westOverIDL = Math.min(westOverIDL, lonAdjusted);
  227. eastOverIDL = Math.max(eastOverIDL, lonAdjusted);
  228. }
  229. if (east - west > eastOverIDL - westOverIDL) {
  230. west = westOverIDL;
  231. east = eastOverIDL;
  232. if (east > CesiumMath.PI) {
  233. east = east - CesiumMath.TWO_PI;
  234. }
  235. if (west > CesiumMath.PI) {
  236. west = west - CesiumMath.TWO_PI;
  237. }
  238. }
  239. if (!defined(result)) {
  240. return new Rectangle(west, south, east, north);
  241. }
  242. result.west = west;
  243. result.south = south;
  244. result.east = east;
  245. result.north = north;
  246. return result;
  247. };
  248. /**
  249. * Creates the smallest possible Rectangle that encloses all positions in the provided array.
  250. *
  251. * @param {Cartesian3[]} cartesians The list of Cartesian instances.
  252. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid the cartesians are on.
  253. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  254. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  255. */
  256. Rectangle.fromCartesianArray = function (cartesians, ellipsoid, result) {
  257. //>>includeStart('debug', pragmas.debug);
  258. Check.defined("cartesians", cartesians);
  259. //>>includeEnd('debug');
  260. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  261. let west = Number.MAX_VALUE;
  262. let east = -Number.MAX_VALUE;
  263. let westOverIDL = Number.MAX_VALUE;
  264. let eastOverIDL = -Number.MAX_VALUE;
  265. let south = Number.MAX_VALUE;
  266. let north = -Number.MAX_VALUE;
  267. for (let i = 0, len = cartesians.length; i < len; i++) {
  268. const position = ellipsoid.cartesianToCartographic(cartesians[i]);
  269. west = Math.min(west, position.longitude);
  270. east = Math.max(east, position.longitude);
  271. south = Math.min(south, position.latitude);
  272. north = Math.max(north, position.latitude);
  273. const lonAdjusted =
  274. position.longitude >= 0
  275. ? position.longitude
  276. : position.longitude + CesiumMath.TWO_PI;
  277. westOverIDL = Math.min(westOverIDL, lonAdjusted);
  278. eastOverIDL = Math.max(eastOverIDL, lonAdjusted);
  279. }
  280. if (east - west > eastOverIDL - westOverIDL) {
  281. west = westOverIDL;
  282. east = eastOverIDL;
  283. if (east > CesiumMath.PI) {
  284. east = east - CesiumMath.TWO_PI;
  285. }
  286. if (west > CesiumMath.PI) {
  287. west = west - CesiumMath.TWO_PI;
  288. }
  289. }
  290. if (!defined(result)) {
  291. return new Rectangle(west, south, east, north);
  292. }
  293. result.west = west;
  294. result.south = south;
  295. result.east = east;
  296. result.north = north;
  297. return result;
  298. };
  299. /**
  300. * Duplicates a Rectangle.
  301. *
  302. * @param {Rectangle} rectangle The rectangle to clone.
  303. * @param {Rectangle} [result] The object onto which to store the result, or undefined if a new instance should be created.
  304. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided. (Returns undefined if rectangle is undefined)
  305. */
  306. Rectangle.clone = function (rectangle, result) {
  307. if (!defined(rectangle)) {
  308. return undefined;
  309. }
  310. if (!defined(result)) {
  311. return new Rectangle(
  312. rectangle.west,
  313. rectangle.south,
  314. rectangle.east,
  315. rectangle.north
  316. );
  317. }
  318. result.west = rectangle.west;
  319. result.south = rectangle.south;
  320. result.east = rectangle.east;
  321. result.north = rectangle.north;
  322. return result;
  323. };
  324. /**
  325. * Compares the provided Rectangles componentwise and returns
  326. * <code>true</code> if they pass an absolute or relative tolerance test,
  327. * <code>false</code> otherwise.
  328. *
  329. * @param {Rectangle} [left] The first Rectangle.
  330. * @param {Rectangle} [right] The second Rectangle.
  331. * @param {number} [absoluteEpsilon=0] The absolute epsilon tolerance to use for equality testing.
  332. * @returns {boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise.
  333. */
  334. Rectangle.equalsEpsilon = function (left, right, absoluteEpsilon) {
  335. absoluteEpsilon = defaultValue(absoluteEpsilon, 0);
  336. return (
  337. left === right ||
  338. (defined(left) &&
  339. defined(right) &&
  340. Math.abs(left.west - right.west) <= absoluteEpsilon &&
  341. Math.abs(left.south - right.south) <= absoluteEpsilon &&
  342. Math.abs(left.east - right.east) <= absoluteEpsilon &&
  343. Math.abs(left.north - right.north) <= absoluteEpsilon)
  344. );
  345. };
  346. /**
  347. * Duplicates this Rectangle.
  348. *
  349. * @param {Rectangle} [result] The object onto which to store the result.
  350. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  351. */
  352. Rectangle.prototype.clone = function (result) {
  353. return Rectangle.clone(this, result);
  354. };
  355. /**
  356. * Compares the provided Rectangle with this Rectangle componentwise and returns
  357. * <code>true</code> if they are equal, <code>false</code> otherwise.
  358. *
  359. * @param {Rectangle} [other] The Rectangle to compare.
  360. * @returns {boolean} <code>true</code> if the Rectangles are equal, <code>false</code> otherwise.
  361. */
  362. Rectangle.prototype.equals = function (other) {
  363. return Rectangle.equals(this, other);
  364. };
  365. /**
  366. * Compares the provided rectangles and returns <code>true</code> if they are equal,
  367. * <code>false</code> otherwise.
  368. *
  369. * @param {Rectangle} [left] The first Rectangle.
  370. * @param {Rectangle} [right] The second Rectangle.
  371. * @returns {boolean} <code>true</code> if left and right are equal; otherwise <code>false</code>.
  372. */
  373. Rectangle.equals = function (left, right) {
  374. return (
  375. left === right ||
  376. (defined(left) &&
  377. defined(right) &&
  378. left.west === right.west &&
  379. left.south === right.south &&
  380. left.east === right.east &&
  381. left.north === right.north)
  382. );
  383. };
  384. /**
  385. * Compares the provided Rectangle with this Rectangle componentwise and returns
  386. * <code>true</code> if they are within the provided epsilon,
  387. * <code>false</code> otherwise.
  388. *
  389. * @param {Rectangle} [other] The Rectangle to compare.
  390. * @param {number} [epsilon=0] The epsilon to use for equality testing.
  391. * @returns {boolean} <code>true</code> if the Rectangles are within the provided epsilon, <code>false</code> otherwise.
  392. */
  393. Rectangle.prototype.equalsEpsilon = function (other, epsilon) {
  394. return Rectangle.equalsEpsilon(this, other, epsilon);
  395. };
  396. /**
  397. * Checks a Rectangle's properties and throws if they are not in valid ranges.
  398. *
  399. * @param {Rectangle} rectangle The rectangle to validate
  400. *
  401. * @exception {DeveloperError} <code>north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  402. * @exception {DeveloperError} <code>south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>].
  403. * @exception {DeveloperError} <code>east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  404. * @exception {DeveloperError} <code>west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>].
  405. */
  406. Rectangle.validate = function (rectangle) {
  407. //>>includeStart('debug', pragmas.debug);
  408. Check.typeOf.object("rectangle", rectangle);
  409. const north = rectangle.north;
  410. Check.typeOf.number.greaterThanOrEquals(
  411. "north",
  412. north,
  413. -CesiumMath.PI_OVER_TWO
  414. );
  415. Check.typeOf.number.lessThanOrEquals("north", north, CesiumMath.PI_OVER_TWO);
  416. const south = rectangle.south;
  417. Check.typeOf.number.greaterThanOrEquals(
  418. "south",
  419. south,
  420. -CesiumMath.PI_OVER_TWO
  421. );
  422. Check.typeOf.number.lessThanOrEquals("south", south, CesiumMath.PI_OVER_TWO);
  423. const west = rectangle.west;
  424. Check.typeOf.number.greaterThanOrEquals("west", west, -Math.PI);
  425. Check.typeOf.number.lessThanOrEquals("west", west, Math.PI);
  426. const east = rectangle.east;
  427. Check.typeOf.number.greaterThanOrEquals("east", east, -Math.PI);
  428. Check.typeOf.number.lessThanOrEquals("east", east, Math.PI);
  429. //>>includeEnd('debug');
  430. };
  431. /**
  432. * Computes the southwest corner of a rectangle.
  433. *
  434. * @param {Rectangle} rectangle The rectangle for which to find the corner
  435. * @param {Cartographic} [result] The object onto which to store the result.
  436. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  437. */
  438. Rectangle.southwest = function (rectangle, result) {
  439. //>>includeStart('debug', pragmas.debug);
  440. Check.typeOf.object("rectangle", rectangle);
  441. //>>includeEnd('debug');
  442. if (!defined(result)) {
  443. return new Cartographic(rectangle.west, rectangle.south);
  444. }
  445. result.longitude = rectangle.west;
  446. result.latitude = rectangle.south;
  447. result.height = 0.0;
  448. return result;
  449. };
  450. /**
  451. * Computes the northwest corner of a rectangle.
  452. *
  453. * @param {Rectangle} rectangle The rectangle for which to find the corner
  454. * @param {Cartographic} [result] The object onto which to store the result.
  455. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  456. */
  457. Rectangle.northwest = function (rectangle, result) {
  458. //>>includeStart('debug', pragmas.debug);
  459. Check.typeOf.object("rectangle", rectangle);
  460. //>>includeEnd('debug');
  461. if (!defined(result)) {
  462. return new Cartographic(rectangle.west, rectangle.north);
  463. }
  464. result.longitude = rectangle.west;
  465. result.latitude = rectangle.north;
  466. result.height = 0.0;
  467. return result;
  468. };
  469. /**
  470. * Computes the northeast corner of a rectangle.
  471. *
  472. * @param {Rectangle} rectangle The rectangle for which to find the corner
  473. * @param {Cartographic} [result] The object onto which to store the result.
  474. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  475. */
  476. Rectangle.northeast = function (rectangle, result) {
  477. //>>includeStart('debug', pragmas.debug);
  478. Check.typeOf.object("rectangle", rectangle);
  479. //>>includeEnd('debug');
  480. if (!defined(result)) {
  481. return new Cartographic(rectangle.east, rectangle.north);
  482. }
  483. result.longitude = rectangle.east;
  484. result.latitude = rectangle.north;
  485. result.height = 0.0;
  486. return result;
  487. };
  488. /**
  489. * Computes the southeast corner of a rectangle.
  490. *
  491. * @param {Rectangle} rectangle The rectangle for which to find the corner
  492. * @param {Cartographic} [result] The object onto which to store the result.
  493. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  494. */
  495. Rectangle.southeast = function (rectangle, result) {
  496. //>>includeStart('debug', pragmas.debug);
  497. Check.typeOf.object("rectangle", rectangle);
  498. //>>includeEnd('debug');
  499. if (!defined(result)) {
  500. return new Cartographic(rectangle.east, rectangle.south);
  501. }
  502. result.longitude = rectangle.east;
  503. result.latitude = rectangle.south;
  504. result.height = 0.0;
  505. return result;
  506. };
  507. /**
  508. * Computes the center of a rectangle.
  509. *
  510. * @param {Rectangle} rectangle The rectangle for which to find the center
  511. * @param {Cartographic} [result] The object onto which to store the result.
  512. * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided.
  513. */
  514. Rectangle.center = function (rectangle, result) {
  515. //>>includeStart('debug', pragmas.debug);
  516. Check.typeOf.object("rectangle", rectangle);
  517. //>>includeEnd('debug');
  518. let east = rectangle.east;
  519. const west = rectangle.west;
  520. if (east < west) {
  521. east += CesiumMath.TWO_PI;
  522. }
  523. const longitude = CesiumMath.negativePiToPi((west + east) * 0.5);
  524. const latitude = (rectangle.south + rectangle.north) * 0.5;
  525. if (!defined(result)) {
  526. return new Cartographic(longitude, latitude);
  527. }
  528. result.longitude = longitude;
  529. result.latitude = latitude;
  530. result.height = 0.0;
  531. return result;
  532. };
  533. /**
  534. * Computes the intersection of two rectangles. This function assumes that the rectangle's coordinates are
  535. * latitude and longitude in radians and produces a correct intersection, taking into account the fact that
  536. * the same angle can be represented with multiple values as well as the wrapping of longitude at the
  537. * anti-meridian. For a simple intersection that ignores these factors and can be used with projected
  538. * coordinates, see {@link Rectangle.simpleIntersection}.
  539. *
  540. * @param {Rectangle} rectangle On rectangle to find an intersection
  541. * @param {Rectangle} otherRectangle Another rectangle to find an intersection
  542. * @param {Rectangle} [result] The object onto which to store the result.
  543. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection.
  544. */
  545. Rectangle.intersection = function (rectangle, otherRectangle, result) {
  546. //>>includeStart('debug', pragmas.debug);
  547. Check.typeOf.object("rectangle", rectangle);
  548. Check.typeOf.object("otherRectangle", otherRectangle);
  549. //>>includeEnd('debug');
  550. let rectangleEast = rectangle.east;
  551. let rectangleWest = rectangle.west;
  552. let otherRectangleEast = otherRectangle.east;
  553. let 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. const west = CesiumMath.negativePiToPi(
  565. Math.max(rectangleWest, otherRectangleWest)
  566. );
  567. const east = CesiumMath.negativePiToPi(
  568. Math.min(rectangleEast, otherRectangleEast)
  569. );
  570. if (
  571. (rectangle.west < rectangle.east ||
  572. otherRectangle.west < otherRectangle.east) &&
  573. east <= west
  574. ) {
  575. return undefined;
  576. }
  577. const south = Math.max(rectangle.south, otherRectangle.south);
  578. const north = Math.min(rectangle.north, otherRectangle.north);
  579. if (south >= north) {
  580. return undefined;
  581. }
  582. if (!defined(result)) {
  583. return new Rectangle(west, south, east, north);
  584. }
  585. result.west = west;
  586. result.south = south;
  587. result.east = east;
  588. result.north = north;
  589. return result;
  590. };
  591. /**
  592. * Computes a simple intersection of two rectangles. Unlike {@link Rectangle.intersection}, this function
  593. * does not attempt to put the angular coordinates into a consistent range or to account for crossing the
  594. * anti-meridian. As such, it can be used for rectangles where the coordinates are not simply latitude
  595. * and longitude (i.e. projected coordinates).
  596. *
  597. * @param {Rectangle} rectangle On rectangle to find an intersection
  598. * @param {Rectangle} otherRectangle Another rectangle to find an intersection
  599. * @param {Rectangle} [result] The object onto which to store the result.
  600. * @returns {Rectangle|undefined} The modified result parameter, a new Rectangle instance if none was provided or undefined if there is no intersection.
  601. */
  602. Rectangle.simpleIntersection = function (rectangle, otherRectangle, result) {
  603. //>>includeStart('debug', pragmas.debug);
  604. Check.typeOf.object("rectangle", rectangle);
  605. Check.typeOf.object("otherRectangle", otherRectangle);
  606. //>>includeEnd('debug');
  607. const west = Math.max(rectangle.west, otherRectangle.west);
  608. const south = Math.max(rectangle.south, otherRectangle.south);
  609. const east = Math.min(rectangle.east, otherRectangle.east);
  610. const north = Math.min(rectangle.north, otherRectangle.north);
  611. if (south >= north || west >= east) {
  612. return undefined;
  613. }
  614. if (!defined(result)) {
  615. return new Rectangle(west, south, east, north);
  616. }
  617. result.west = west;
  618. result.south = south;
  619. result.east = east;
  620. result.north = north;
  621. return result;
  622. };
  623. /**
  624. * Computes a rectangle that is the union of two rectangles.
  625. *
  626. * @param {Rectangle} rectangle A rectangle to enclose in rectangle.
  627. * @param {Rectangle} otherRectangle A rectangle to enclose in a rectangle.
  628. * @param {Rectangle} [result] The object onto which to store the result.
  629. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  630. */
  631. Rectangle.union = function (rectangle, otherRectangle, result) {
  632. //>>includeStart('debug', pragmas.debug);
  633. Check.typeOf.object("rectangle", rectangle);
  634. Check.typeOf.object("otherRectangle", otherRectangle);
  635. //>>includeEnd('debug');
  636. if (!defined(result)) {
  637. result = new Rectangle();
  638. }
  639. let rectangleEast = rectangle.east;
  640. let rectangleWest = rectangle.west;
  641. let otherRectangleEast = otherRectangle.east;
  642. let otherRectangleWest = otherRectangle.west;
  643. if (rectangleEast < rectangleWest && otherRectangleEast > 0.0) {
  644. rectangleEast += CesiumMath.TWO_PI;
  645. } else if (otherRectangleEast < otherRectangleWest && rectangleEast > 0.0) {
  646. otherRectangleEast += CesiumMath.TWO_PI;
  647. }
  648. if (rectangleEast < rectangleWest && otherRectangleWest < 0.0) {
  649. otherRectangleWest += CesiumMath.TWO_PI;
  650. } else if (otherRectangleEast < otherRectangleWest && rectangleWest < 0.0) {
  651. rectangleWest += CesiumMath.TWO_PI;
  652. }
  653. const west = CesiumMath.negativePiToPi(
  654. Math.min(rectangleWest, otherRectangleWest)
  655. );
  656. const east = CesiumMath.negativePiToPi(
  657. Math.max(rectangleEast, otherRectangleEast)
  658. );
  659. result.west = west;
  660. result.south = Math.min(rectangle.south, otherRectangle.south);
  661. result.east = east;
  662. result.north = Math.max(rectangle.north, otherRectangle.north);
  663. return result;
  664. };
  665. /**
  666. * Computes a rectangle by enlarging the provided rectangle until it contains the provided cartographic.
  667. *
  668. * @param {Rectangle} rectangle A rectangle to expand.
  669. * @param {Cartographic} cartographic A cartographic to enclose in a rectangle.
  670. * @param {Rectangle} [result] The object onto which to store the result.
  671. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if one was not provided.
  672. */
  673. Rectangle.expand = function (rectangle, cartographic, result) {
  674. //>>includeStart('debug', pragmas.debug);
  675. Check.typeOf.object("rectangle", rectangle);
  676. Check.typeOf.object("cartographic", cartographic);
  677. //>>includeEnd('debug');
  678. if (!defined(result)) {
  679. result = new Rectangle();
  680. }
  681. result.west = Math.min(rectangle.west, cartographic.longitude);
  682. result.south = Math.min(rectangle.south, cartographic.latitude);
  683. result.east = Math.max(rectangle.east, cartographic.longitude);
  684. result.north = Math.max(rectangle.north, cartographic.latitude);
  685. return result;
  686. };
  687. /**
  688. * Returns true if the cartographic is on or inside the rectangle, false otherwise.
  689. *
  690. * @param {Rectangle} rectangle The rectangle
  691. * @param {Cartographic} cartographic The cartographic to test.
  692. * @returns {boolean} true if the provided cartographic is inside the rectangle, false otherwise.
  693. */
  694. Rectangle.contains = function (rectangle, cartographic) {
  695. //>>includeStart('debug', pragmas.debug);
  696. Check.typeOf.object("rectangle", rectangle);
  697. Check.typeOf.object("cartographic", cartographic);
  698. //>>includeEnd('debug');
  699. let longitude = cartographic.longitude;
  700. const latitude = cartographic.latitude;
  701. const west = rectangle.west;
  702. let east = rectangle.east;
  703. if (east < west) {
  704. east += CesiumMath.TWO_PI;
  705. if (longitude < 0.0) {
  706. longitude += CesiumMath.TWO_PI;
  707. }
  708. }
  709. return (
  710. (longitude > west ||
  711. CesiumMath.equalsEpsilon(longitude, west, CesiumMath.EPSILON14)) &&
  712. (longitude < east ||
  713. CesiumMath.equalsEpsilon(longitude, east, CesiumMath.EPSILON14)) &&
  714. latitude >= rectangle.south &&
  715. latitude <= rectangle.north
  716. );
  717. };
  718. const subsampleLlaScratch = new Cartographic();
  719. /**
  720. * Samples a rectangle so that it includes a list of Cartesian points suitable for passing to
  721. * {@link BoundingSphere#fromPoints}. Sampling is necessary to account
  722. * for rectangles that cover the poles or cross the equator.
  723. *
  724. * @param {Rectangle} rectangle The rectangle to subsample.
  725. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use.
  726. * @param {number} [surfaceHeight=0.0] The height of the rectangle above the ellipsoid.
  727. * @param {Cartesian3[]} [result] The array of Cartesians onto which to store the result.
  728. * @returns {Cartesian3[]} The modified result parameter or a new Array of Cartesians instances if none was provided.
  729. */
  730. Rectangle.subsample = function (rectangle, ellipsoid, surfaceHeight, result) {
  731. //>>includeStart('debug', pragmas.debug);
  732. Check.typeOf.object("rectangle", rectangle);
  733. //>>includeEnd('debug');
  734. ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
  735. surfaceHeight = defaultValue(surfaceHeight, 0.0);
  736. if (!defined(result)) {
  737. result = [];
  738. }
  739. let length = 0;
  740. const north = rectangle.north;
  741. const south = rectangle.south;
  742. const east = rectangle.east;
  743. const west = rectangle.west;
  744. const lla = subsampleLlaScratch;
  745. lla.height = surfaceHeight;
  746. lla.longitude = west;
  747. lla.latitude = north;
  748. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  749. length++;
  750. lla.longitude = east;
  751. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  752. length++;
  753. lla.latitude = south;
  754. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  755. length++;
  756. lla.longitude = west;
  757. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  758. length++;
  759. if (north < 0.0) {
  760. lla.latitude = north;
  761. } else if (south > 0.0) {
  762. lla.latitude = south;
  763. } else {
  764. lla.latitude = 0.0;
  765. }
  766. for (let i = 1; i < 8; ++i) {
  767. lla.longitude = -Math.PI + i * CesiumMath.PI_OVER_TWO;
  768. if (Rectangle.contains(rectangle, lla)) {
  769. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  770. length++;
  771. }
  772. }
  773. if (lla.latitude === 0.0) {
  774. lla.longitude = west;
  775. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  776. length++;
  777. lla.longitude = east;
  778. result[length] = ellipsoid.cartographicToCartesian(lla, result[length]);
  779. length++;
  780. }
  781. result.length = length;
  782. return result;
  783. };
  784. /**
  785. * Computes a subsection of a rectangle from normalized coordinates in the range [0.0, 1.0].
  786. *
  787. * @param {Rectangle} rectangle The rectangle to subsection.
  788. * @param {number} westLerp The west interpolation factor in the range [0.0, 1.0]. Must be less than or equal to eastLerp.
  789. * @param {number} southLerp The south interpolation factor in the range [0.0, 1.0]. Must be less than or equal to northLerp.
  790. * @param {number} eastLerp The east interpolation factor in the range [0.0, 1.0]. Must be greater than or equal to westLerp.
  791. * @param {number} northLerp The north interpolation factor in the range [0.0, 1.0]. Must be greater than or equal to southLerp.
  792. * @param {Rectangle} [result] The object onto which to store the result.
  793. * @returns {Rectangle} The modified result parameter or a new Rectangle instance if none was provided.
  794. */
  795. Rectangle.subsection = function (
  796. rectangle,
  797. westLerp,
  798. southLerp,
  799. eastLerp,
  800. northLerp,
  801. result
  802. ) {
  803. //>>includeStart('debug', pragmas.debug);
  804. Check.typeOf.object("rectangle", rectangle);
  805. Check.typeOf.number.greaterThanOrEquals("westLerp", westLerp, 0.0);
  806. Check.typeOf.number.lessThanOrEquals("westLerp", westLerp, 1.0);
  807. Check.typeOf.number.greaterThanOrEquals("southLerp", southLerp, 0.0);
  808. Check.typeOf.number.lessThanOrEquals("southLerp", southLerp, 1.0);
  809. Check.typeOf.number.greaterThanOrEquals("eastLerp", eastLerp, 0.0);
  810. Check.typeOf.number.lessThanOrEquals("eastLerp", eastLerp, 1.0);
  811. Check.typeOf.number.greaterThanOrEquals("northLerp", northLerp, 0.0);
  812. Check.typeOf.number.lessThanOrEquals("northLerp", northLerp, 1.0);
  813. Check.typeOf.number.lessThanOrEquals("westLerp", westLerp, eastLerp);
  814. Check.typeOf.number.lessThanOrEquals("southLerp", southLerp, northLerp);
  815. //>>includeEnd('debug');
  816. if (!defined(result)) {
  817. result = new Rectangle();
  818. }
  819. // This function doesn't use CesiumMath.lerp because it has floating point precision problems
  820. // when the start and end values are the same but the t changes.
  821. if (rectangle.west <= rectangle.east) {
  822. const width = rectangle.east - rectangle.west;
  823. result.west = rectangle.west + westLerp * width;
  824. result.east = rectangle.west + eastLerp * width;
  825. } else {
  826. const width = CesiumMath.TWO_PI + rectangle.east - rectangle.west;
  827. result.west = CesiumMath.negativePiToPi(rectangle.west + westLerp * width);
  828. result.east = CesiumMath.negativePiToPi(rectangle.west + eastLerp * width);
  829. }
  830. const height = rectangle.north - rectangle.south;
  831. result.south = rectangle.south + southLerp * height;
  832. result.north = rectangle.south + northLerp * height;
  833. // Fix floating point precision problems when t = 1
  834. if (westLerp === 1.0) {
  835. result.west = rectangle.east;
  836. }
  837. if (eastLerp === 1.0) {
  838. result.east = rectangle.east;
  839. }
  840. if (southLerp === 1.0) {
  841. result.south = rectangle.north;
  842. }
  843. if (northLerp === 1.0) {
  844. result.north = rectangle.north;
  845. }
  846. return result;
  847. };
  848. /**
  849. * The largest possible rectangle.
  850. *
  851. * @type {Rectangle}
  852. * @constant
  853. */
  854. Rectangle.MAX_VALUE = Object.freeze(
  855. new Rectangle(
  856. -Math.PI,
  857. -CesiumMath.PI_OVER_TWO,
  858. Math.PI,
  859. CesiumMath.PI_OVER_TWO
  860. )
  861. );
  862. export default Rectangle;