tmerc.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Heavily based on this tmerc projection implementation
  2. // https://github.com/mbloch/mapshaper-proj/blob/master/src/projections/tmerc.js
  3. import pj_enfn from '../common/pj_enfn';
  4. import pj_mlfn from '../common/pj_mlfn';
  5. import pj_inv_mlfn from '../common/pj_inv_mlfn';
  6. import adjust_lon from '../common/adjust_lon';
  7. import {EPSLN, HALF_PI} from '../constants/values';
  8. import sign from '../common/sign';
  9. export function init() {
  10. this.x0 = this.x0 !== undefined ? this.x0 : 0;
  11. this.y0 = this.y0 !== undefined ? this.y0 : 0;
  12. this.long0 = this.long0 !== undefined ? this.long0 : 0;
  13. this.lat0 = this.lat0 !== undefined ? this.lat0 : 0;
  14. if (this.es) {
  15. this.en = pj_enfn(this.es);
  16. this.ml0 = pj_mlfn(this.lat0, Math.sin(this.lat0), Math.cos(this.lat0), this.en);
  17. }
  18. }
  19. /**
  20. Transverse Mercator Forward - long/lat to x/y
  21. long/lat in radians
  22. */
  23. export function forward(p) {
  24. var lon = p.x;
  25. var lat = p.y;
  26. var delta_lon = adjust_lon(lon - this.long0);
  27. var con;
  28. var x, y;
  29. var sin_phi = Math.sin(lat);
  30. var cos_phi = Math.cos(lat);
  31. if (!this.es) {
  32. var b = cos_phi * Math.sin(delta_lon);
  33. if ((Math.abs(Math.abs(b) - 1)) < EPSLN) {
  34. return (93);
  35. }
  36. else {
  37. x = 0.5 * this.a * this.k0 * Math.log((1 + b) / (1 - b)) + this.x0;
  38. y = cos_phi * Math.cos(delta_lon) / Math.sqrt(1 - Math.pow(b, 2));
  39. b = Math.abs(y);
  40. if (b >= 1) {
  41. if ((b - 1) > EPSLN) {
  42. return (93);
  43. }
  44. else {
  45. y = 0;
  46. }
  47. }
  48. else {
  49. y = Math.acos(y);
  50. }
  51. if (lat < 0) {
  52. y = -y;
  53. }
  54. y = this.a * this.k0 * (y - this.lat0) + this.y0;
  55. }
  56. }
  57. else {
  58. var al = cos_phi * delta_lon;
  59. var als = Math.pow(al, 2);
  60. var c = this.ep2 * Math.pow(cos_phi, 2);
  61. var cs = Math.pow(c, 2);
  62. var tq = Math.abs(cos_phi) > EPSLN ? Math.tan(lat) : 0;
  63. var t = Math.pow(tq, 2);
  64. var ts = Math.pow(t, 2);
  65. con = 1 - this.es * Math.pow(sin_phi, 2);
  66. al = al / Math.sqrt(con);
  67. var ml = pj_mlfn(lat, sin_phi, cos_phi, this.en);
  68. x = this.a * (this.k0 * al * (1 +
  69. als / 6 * (1 - t + c +
  70. als / 20 * (5 - 18 * t + ts + 14 * c - 58 * t * c +
  71. als / 42 * (61 + 179 * ts - ts * t - 479 * t))))) +
  72. this.x0;
  73. y = this.a * (this.k0 * (ml - this.ml0 +
  74. sin_phi * delta_lon * al / 2 * (1 +
  75. als / 12 * (5 - t + 9 * c + 4 * cs +
  76. als / 30 * (61 + ts - 58 * t + 270 * c - 330 * t * c +
  77. als / 56 * (1385 + 543 * ts - ts * t - 3111 * t)))))) +
  78. this.y0;
  79. }
  80. p.x = x;
  81. p.y = y;
  82. return p;
  83. }
  84. /**
  85. Transverse Mercator Inverse - x/y to long/lat
  86. */
  87. export function inverse(p) {
  88. var con, phi;
  89. var lat, lon;
  90. var x = (p.x - this.x0) * (1 / this.a);
  91. var y = (p.y - this.y0) * (1 / this.a);
  92. if (!this.es) {
  93. var f = Math.exp(x / this.k0);
  94. var g = 0.5 * (f - 1 / f);
  95. var temp = this.lat0 + y / this.k0;
  96. var h = Math.cos(temp);
  97. con = Math.sqrt((1 - Math.pow(h, 2)) / (1 + Math.pow(g, 2)));
  98. lat = Math.asin(con);
  99. if (y < 0) {
  100. lat = -lat;
  101. }
  102. if ((g === 0) && (h === 0)) {
  103. lon = 0;
  104. }
  105. else {
  106. lon = adjust_lon(Math.atan2(g, h) + this.long0);
  107. }
  108. }
  109. else { // ellipsoidal form
  110. con = this.ml0 + y / this.k0;
  111. phi = pj_inv_mlfn(con, this.es, this.en);
  112. if (Math.abs(phi) < HALF_PI) {
  113. var sin_phi = Math.sin(phi);
  114. var cos_phi = Math.cos(phi);
  115. var tan_phi = Math.abs(cos_phi) > EPSLN ? Math.tan(phi) : 0;
  116. var c = this.ep2 * Math.pow(cos_phi, 2);
  117. var cs = Math.pow(c, 2);
  118. var t = Math.pow(tan_phi, 2);
  119. var ts = Math.pow(t, 2);
  120. con = 1 - this.es * Math.pow(sin_phi, 2);
  121. var d = x * Math.sqrt(con) / this.k0;
  122. var ds = Math.pow(d, 2);
  123. con = con * tan_phi;
  124. lat = phi - (con * ds / (1 - this.es)) * 0.5 * (1 -
  125. ds / 12 * (5 + 3 * t - 9 * c * t + c - 4 * cs -
  126. ds / 30 * (61 + 90 * t - 252 * c * t + 45 * ts + 46 * c -
  127. ds / 56 * (1385 + 3633 * t + 4095 * ts + 1574 * ts * t))));
  128. lon = adjust_lon(this.long0 + (d * (1 -
  129. ds / 6 * (1 + 2 * t + c -
  130. ds / 20 * (5 + 28 * t + 24 * ts + 8 * c * t + 6 * c -
  131. ds / 42 * (61 + 662 * t + 1320 * ts + 720 * ts * t)))) / cos_phi));
  132. }
  133. else {
  134. lat = HALF_PI * sign(y);
  135. lon = 0;
  136. }
  137. }
  138. p.x = lon;
  139. p.y = lat;
  140. return p;
  141. }
  142. export var names = ["Fast_Transverse_Mercator", "Fast Transverse Mercator"];
  143. export default {
  144. init: init,
  145. forward: forward,
  146. inverse: inverse,
  147. names: names
  148. };