tpers.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. var mode = {
  2. N_POLE: 0,
  3. S_POLE: 1,
  4. EQUIT: 2,
  5. OBLIQ: 3
  6. };
  7. import { D2R, HALF_PI, EPSLN } from "../constants/values";
  8. import hypot from "../common/hypot";
  9. var params = {
  10. h: { def: 100000, num: true }, // default is Karman line, no default in PROJ.7
  11. azi: { def: 0, num: true, degrees: true }, // default is North
  12. tilt: { def: 0, num: true, degrees: true }, // default is Nadir
  13. long0: { def: 0, num: true }, // default is Greenwich, conversion to rad is automatic
  14. lat0: { def: 0, num: true } // default is Equator, conversion to rad is automatic
  15. };
  16. export function init() {
  17. Object.keys(params).forEach(function (p) {
  18. if (typeof this[p] === "undefined") {
  19. this[p] = params[p].def;
  20. } else if (params[p].num && isNaN(this[p])) {
  21. throw new Error("Invalid parameter value, must be numeric " + p + " = " + this[p]);
  22. } else if (params[p].num) {
  23. this[p] = parseFloat(this[p]);
  24. }
  25. if (params[p].degrees) {
  26. this[p] = this[p] * D2R;
  27. }
  28. }.bind(this));
  29. if (Math.abs((Math.abs(this.lat0) - HALF_PI)) < EPSLN) {
  30. this.mode = this.lat0 < 0 ? mode.S_POLE : mode.N_POLE;
  31. } else if (Math.abs(this.lat0) < EPSLN) {
  32. this.mode = mode.EQUIT;
  33. } else {
  34. this.mode = mode.OBLIQ;
  35. this.sinph0 = Math.sin(this.lat0);
  36. this.cosph0 = Math.cos(this.lat0);
  37. }
  38. this.pn1 = this.h / this.a; // Normalize relative to the Earth's radius
  39. if (this.pn1 <= 0 || this.pn1 > 1e10) {
  40. throw new Error("Invalid height");
  41. }
  42. this.p = 1 + this.pn1;
  43. this.rp = 1 / this.p;
  44. this.h1 = 1 / this.pn1;
  45. this.pfact = (this.p + 1) * this.h1;
  46. this.es = 0;
  47. var omega = this.tilt;
  48. var gamma = this.azi;
  49. this.cg = Math.cos(gamma);
  50. this.sg = Math.sin(gamma);
  51. this.cw = Math.cos(omega);
  52. this.sw = Math.sin(omega);
  53. }
  54. export function forward(p) {
  55. p.x -= this.long0;
  56. var sinphi = Math.sin(p.y);
  57. var cosphi = Math.cos(p.y);
  58. var coslam = Math.cos(p.x);
  59. var x, y;
  60. switch (this.mode) {
  61. case mode.OBLIQ:
  62. y = this.sinph0 * sinphi + this.cosph0 * cosphi * coslam;
  63. break;
  64. case mode.EQUIT:
  65. y = cosphi * coslam;
  66. break;
  67. case mode.S_POLE:
  68. y = -sinphi;
  69. break;
  70. case mode.N_POLE:
  71. y = sinphi;
  72. break;
  73. }
  74. y = this.pn1 / (this.p - y);
  75. x = y * cosphi * Math.sin(p.x);
  76. switch (this.mode) {
  77. case mode.OBLIQ:
  78. y *= this.cosph0 * sinphi - this.sinph0 * cosphi * coslam;
  79. break;
  80. case mode.EQUIT:
  81. y *= sinphi;
  82. break;
  83. case mode.N_POLE:
  84. y *= -(cosphi * coslam);
  85. break;
  86. case mode.S_POLE:
  87. y *= cosphi * coslam;
  88. break;
  89. }
  90. // Tilt
  91. var yt, ba;
  92. yt = y * this.cg + x * this.sg;
  93. ba = 1 / (yt * this.sw * this.h1 + this.cw);
  94. x = (x * this.cg - y * this.sg) * this.cw * ba;
  95. y = yt * ba;
  96. p.x = x * this.a;
  97. p.y = y * this.a;
  98. return p;
  99. }
  100. export function inverse(p) {
  101. p.x /= this.a;
  102. p.y /= this.a;
  103. var r = { x: p.x, y: p.y };
  104. // Un-Tilt
  105. var bm, bq, yt;
  106. yt = 1 / (this.pn1 - p.y * this.sw);
  107. bm = this.pn1 * p.x * yt;
  108. bq = this.pn1 * p.y * this.cw * yt;
  109. p.x = bm * this.cg + bq * this.sg;
  110. p.y = bq * this.cg - bm * this.sg;
  111. var rh = hypot(p.x, p.y);
  112. if (Math.abs(rh) < EPSLN) {
  113. r.x = 0;
  114. r.y = p.y;
  115. } else {
  116. var cosz, sinz;
  117. sinz = 1 - rh * rh * this.pfact;
  118. sinz = (this.p - Math.sqrt(sinz)) / (this.pn1 / rh + rh / this.pn1);
  119. cosz = Math.sqrt(1 - sinz * sinz);
  120. switch (this.mode) {
  121. case mode.OBLIQ:
  122. r.y = Math.asin(cosz * this.sinph0 + p.y * sinz * this.cosph0 / rh);
  123. p.y = (cosz - this.sinph0 * Math.sin(r.y)) * rh;
  124. p.x *= sinz * this.cosph0;
  125. break;
  126. case mode.EQUIT:
  127. r.y = Math.asin(p.y * sinz / rh);
  128. p.y = cosz * rh;
  129. p.x *= sinz;
  130. break;
  131. case mode.N_POLE:
  132. r.y = Math.asin(cosz);
  133. p.y = -p.y;
  134. break;
  135. case mode.S_POLE:
  136. r.y = -Math.asin(cosz);
  137. break;
  138. }
  139. r.x = Math.atan2(p.x, p.y);
  140. }
  141. p.x = r.x + this.long0;
  142. p.y = r.y;
  143. return p;
  144. }
  145. export var names = ["Tilted_Perspective", "tpers"];
  146. export default {
  147. init: init,
  148. forward: forward,
  149. inverse: inverse,
  150. names: names
  151. };