laea.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. var HALF_PI = Math.PI/2;
  2. var FORTPI = Math.PI/4;
  3. var EPSLN = 1.0e-10;
  4. var qsfnz = require('../common/qsfnz');
  5. var adjust_lon = require('../common/adjust_lon');
  6. /*
  7. reference
  8. "New Equal-Area Map Projections for Noncircular Regions", John P. Snyder,
  9. The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.
  10. */
  11. exports.S_POLE = 1;
  12. exports.N_POLE = 2;
  13. exports.EQUIT = 3;
  14. exports.OBLIQ = 4;
  15. /* Initialize the Lambert Azimuthal Equal Area projection
  16. ------------------------------------------------------*/
  17. exports.init = function() {
  18. var t = Math.abs(this.lat0);
  19. if (Math.abs(t - HALF_PI) < EPSLN) {
  20. this.mode = this.lat0 < 0 ? this.S_POLE : this.N_POLE;
  21. }
  22. else if (Math.abs(t) < EPSLN) {
  23. this.mode = this.EQUIT;
  24. }
  25. else {
  26. this.mode = this.OBLIQ;
  27. }
  28. if (this.es > 0) {
  29. var sinphi;
  30. this.qp = qsfnz(this.e, 1);
  31. this.mmf = 0.5 / (1 - this.es);
  32. this.apa = this.authset(this.es);
  33. switch (this.mode) {
  34. case this.N_POLE:
  35. this.dd = 1;
  36. break;
  37. case this.S_POLE:
  38. this.dd = 1;
  39. break;
  40. case this.EQUIT:
  41. this.rq = Math.sqrt(0.5 * this.qp);
  42. this.dd = 1 / this.rq;
  43. this.xmf = 1;
  44. this.ymf = 0.5 * this.qp;
  45. break;
  46. case this.OBLIQ:
  47. this.rq = Math.sqrt(0.5 * this.qp);
  48. sinphi = Math.sin(this.lat0);
  49. this.sinb1 = qsfnz(this.e, sinphi) / this.qp;
  50. this.cosb1 = Math.sqrt(1 - this.sinb1 * this.sinb1);
  51. this.dd = Math.cos(this.lat0) / (Math.sqrt(1 - this.es * sinphi * sinphi) * this.rq * this.cosb1);
  52. this.ymf = (this.xmf = this.rq) / this.dd;
  53. this.xmf *= this.dd;
  54. break;
  55. }
  56. }
  57. else {
  58. if (this.mode === this.OBLIQ) {
  59. this.sinph0 = Math.sin(this.lat0);
  60. this.cosph0 = Math.cos(this.lat0);
  61. }
  62. }
  63. };
  64. /* Lambert Azimuthal Equal Area forward equations--mapping lat,long to x,y
  65. -----------------------------------------------------------------------*/
  66. exports.forward = function(p) {
  67. /* Forward equations
  68. -----------------*/
  69. var x, y, coslam, sinlam, sinphi, q, sinb, cosb, b, cosphi;
  70. var lam = p.x;
  71. var phi = p.y;
  72. lam = adjust_lon(lam - this.long0);
  73. if (this.sphere) {
  74. sinphi = Math.sin(phi);
  75. cosphi = Math.cos(phi);
  76. coslam = Math.cos(lam);
  77. if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
  78. y = (this.mode === this.EQUIT) ? 1 + cosphi * coslam : 1 + this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
  79. if (y <= EPSLN) {
  80. return null;
  81. }
  82. y = Math.sqrt(2 / y);
  83. x = y * cosphi * Math.sin(lam);
  84. y *= (this.mode === this.EQUIT) ? sinphi : this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;
  85. }
  86. else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
  87. if (this.mode === this.N_POLE) {
  88. coslam = -coslam;
  89. }
  90. if (Math.abs(phi + this.phi0) < EPSLN) {
  91. return null;
  92. }
  93. y = FORTPI - phi * 0.5;
  94. y = 2 * ((this.mode === this.S_POLE) ? Math.cos(y) : Math.sin(y));
  95. x = y * Math.sin(lam);
  96. y *= coslam;
  97. }
  98. }
  99. else {
  100. sinb = 0;
  101. cosb = 0;
  102. b = 0;
  103. coslam = Math.cos(lam);
  104. sinlam = Math.sin(lam);
  105. sinphi = Math.sin(phi);
  106. q = qsfnz(this.e, sinphi);
  107. if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
  108. sinb = q / this.qp;
  109. cosb = Math.sqrt(1 - sinb * sinb);
  110. }
  111. switch (this.mode) {
  112. case this.OBLIQ:
  113. b = 1 + this.sinb1 * sinb + this.cosb1 * cosb * coslam;
  114. break;
  115. case this.EQUIT:
  116. b = 1 + cosb * coslam;
  117. break;
  118. case this.N_POLE:
  119. b = HALF_PI + phi;
  120. q = this.qp - q;
  121. break;
  122. case this.S_POLE:
  123. b = phi - HALF_PI;
  124. q = this.qp + q;
  125. break;
  126. }
  127. if (Math.abs(b) < EPSLN) {
  128. return null;
  129. }
  130. switch (this.mode) {
  131. case this.OBLIQ:
  132. case this.EQUIT:
  133. b = Math.sqrt(2 / b);
  134. if (this.mode === this.OBLIQ) {
  135. y = this.ymf * b * (this.cosb1 * sinb - this.sinb1 * cosb * coslam);
  136. }
  137. else {
  138. y = (b = Math.sqrt(2 / (1 + cosb * coslam))) * sinb * this.ymf;
  139. }
  140. x = this.xmf * b * cosb * sinlam;
  141. break;
  142. case this.N_POLE:
  143. case this.S_POLE:
  144. if (q >= 0) {
  145. x = (b = Math.sqrt(q)) * sinlam;
  146. y = coslam * ((this.mode === this.S_POLE) ? b : -b);
  147. }
  148. else {
  149. x = y = 0;
  150. }
  151. break;
  152. }
  153. }
  154. p.x = this.a * x + this.x0;
  155. p.y = this.a * y + this.y0;
  156. return p;
  157. };
  158. /* Inverse equations
  159. -----------------*/
  160. exports.inverse = function(p) {
  161. p.x -= this.x0;
  162. p.y -= this.y0;
  163. var x = p.x / this.a;
  164. var y = p.y / this.a;
  165. var lam, phi, cCe, sCe, q, rho, ab;
  166. if (this.sphere) {
  167. var cosz = 0,
  168. rh, sinz = 0;
  169. rh = Math.sqrt(x * x + y * y);
  170. phi = rh * 0.5;
  171. if (phi > 1) {
  172. return null;
  173. }
  174. phi = 2 * Math.asin(phi);
  175. if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
  176. sinz = Math.sin(phi);
  177. cosz = Math.cos(phi);
  178. }
  179. switch (this.mode) {
  180. case this.EQUIT:
  181. phi = (Math.abs(rh) <= EPSLN) ? 0 : Math.asin(y * sinz / rh);
  182. x *= sinz;
  183. y = cosz * rh;
  184. break;
  185. case this.OBLIQ:
  186. phi = (Math.abs(rh) <= EPSLN) ? this.phi0 : Math.asin(cosz * this.sinph0 + y * sinz * this.cosph0 / rh);
  187. x *= sinz * this.cosph0;
  188. y = (cosz - Math.sin(phi) * this.sinph0) * rh;
  189. break;
  190. case this.N_POLE:
  191. y = -y;
  192. phi = HALF_PI - phi;
  193. break;
  194. case this.S_POLE:
  195. phi -= HALF_PI;
  196. break;
  197. }
  198. lam = (y === 0 && (this.mode === this.EQUIT || this.mode === this.OBLIQ)) ? 0 : Math.atan2(x, y);
  199. }
  200. else {
  201. ab = 0;
  202. if (this.mode === this.OBLIQ || this.mode === this.EQUIT) {
  203. x /= this.dd;
  204. y *= this.dd;
  205. rho = Math.sqrt(x * x + y * y);
  206. if (rho < EPSLN) {
  207. p.x = 0;
  208. p.y = this.phi0;
  209. return p;
  210. }
  211. sCe = 2 * Math.asin(0.5 * rho / this.rq);
  212. cCe = Math.cos(sCe);
  213. x *= (sCe = Math.sin(sCe));
  214. if (this.mode === this.OBLIQ) {
  215. ab = cCe * this.sinb1 + y * sCe * this.cosb1 / rho;
  216. q = this.qp * ab;
  217. y = rho * this.cosb1 * cCe - y * this.sinb1 * sCe;
  218. }
  219. else {
  220. ab = y * sCe / rho;
  221. q = this.qp * ab;
  222. y = rho * cCe;
  223. }
  224. }
  225. else if (this.mode === this.N_POLE || this.mode === this.S_POLE) {
  226. if (this.mode === this.N_POLE) {
  227. y = -y;
  228. }
  229. q = (x * x + y * y);
  230. if (!q) {
  231. p.x = 0;
  232. p.y = this.phi0;
  233. return p;
  234. }
  235. ab = 1 - q / this.qp;
  236. if (this.mode === this.S_POLE) {
  237. ab = -ab;
  238. }
  239. }
  240. lam = Math.atan2(x, y);
  241. phi = this.authlat(Math.asin(ab), this.apa);
  242. }
  243. p.x = adjust_lon(this.long0 + lam);
  244. p.y = phi;
  245. return p;
  246. };
  247. /* determine latitude from authalic latitude */
  248. exports.P00 = 0.33333333333333333333;
  249. exports.P01 = 0.17222222222222222222;
  250. exports.P02 = 0.10257936507936507936;
  251. exports.P10 = 0.06388888888888888888;
  252. exports.P11 = 0.06640211640211640211;
  253. exports.P20 = 0.01641501294219154443;
  254. exports.authset = function(es) {
  255. var t;
  256. var APA = [];
  257. APA[0] = es * this.P00;
  258. t = es * es;
  259. APA[0] += t * this.P01;
  260. APA[1] = t * this.P10;
  261. t *= es;
  262. APA[0] += t * this.P02;
  263. APA[1] += t * this.P11;
  264. APA[2] = t * this.P20;
  265. return APA;
  266. };
  267. exports.authlat = function(beta, APA) {
  268. var t = beta + beta;
  269. return (beta + APA[0] * Math.sin(t) + APA[1] * Math.sin(t + t) + APA[2] * Math.sin(t + t + t));
  270. };
  271. exports.names = ["Lambert Azimuthal Equal Area", "Lambert_Azimuthal_Equal_Area", "laea"];