laea.js 7.4 KB

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