datum_transform.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import {
  2. PJD_3PARAM,
  3. PJD_7PARAM,
  4. PJD_GRIDSHIFT,
  5. PJD_NODATUM,
  6. R2D,
  7. SRS_WGS84_ESQUARED,
  8. SRS_WGS84_SEMIMAJOR, SRS_WGS84_SEMIMINOR
  9. } from './constants/values';
  10. import {geodeticToGeocentric, geocentricToGeodetic, geocentricToWgs84, geocentricFromWgs84, compareDatums} from './datumUtils';
  11. import adjust_lon from "./common/adjust_lon";
  12. function checkParams(type) {
  13. return (type === PJD_3PARAM || type === PJD_7PARAM);
  14. }
  15. export default function(source, dest, point) {
  16. // Short cut if the datums are identical.
  17. if (compareDatums(source, dest)) {
  18. return point; // in this case, zero is sucess,
  19. // whereas cs_compare_datums returns 1 to indicate TRUE
  20. // confusing, should fix this
  21. }
  22. // Explicitly skip datum transform by setting 'datum=none' as parameter for either source or dest
  23. if (source.datum_type === PJD_NODATUM || dest.datum_type === PJD_NODATUM) {
  24. return point;
  25. }
  26. // If this datum requires grid shifts, then apply it to geodetic coordinates.
  27. var source_a = source.a;
  28. var source_es = source.es;
  29. if (source.datum_type === PJD_GRIDSHIFT) {
  30. var gridShiftCode = applyGridShift(source, false, point);
  31. if (gridShiftCode !== 0) {
  32. return undefined;
  33. }
  34. source_a = SRS_WGS84_SEMIMAJOR;
  35. source_es = SRS_WGS84_ESQUARED;
  36. }
  37. var dest_a = dest.a;
  38. var dest_b = dest.b;
  39. var dest_es = dest.es;
  40. if (dest.datum_type === PJD_GRIDSHIFT) {
  41. dest_a = SRS_WGS84_SEMIMAJOR;
  42. dest_b = SRS_WGS84_SEMIMINOR;
  43. dest_es = SRS_WGS84_ESQUARED;
  44. }
  45. // Do we need to go through geocentric coordinates?
  46. if (source_es === dest_es && source_a === dest_a && !checkParams(source.datum_type) && !checkParams(dest.datum_type)) {
  47. return point;
  48. }
  49. // Convert to geocentric coordinates.
  50. point = geodeticToGeocentric(point, source_es, source_a);
  51. // Convert between datums
  52. if (checkParams(source.datum_type)) {
  53. point = geocentricToWgs84(point, source.datum_type, source.datum_params);
  54. }
  55. if (checkParams(dest.datum_type)) {
  56. point = geocentricFromWgs84(point, dest.datum_type, dest.datum_params);
  57. }
  58. point = geocentricToGeodetic(point, dest_es, dest_a, dest_b);
  59. if (dest.datum_type === PJD_GRIDSHIFT) {
  60. var destGridShiftResult = applyGridShift(dest, true, point);
  61. if (destGridShiftResult !== 0) {
  62. return undefined;
  63. }
  64. }
  65. return point;
  66. }
  67. export function applyGridShift(source, inverse, point) {
  68. if (source.grids === null || source.grids.length === 0) {
  69. console.log('Grid shift grids not found');
  70. return -1;
  71. }
  72. var input = {x: -point.x, y: point.y};
  73. var output = {x: Number.NaN, y: Number.NaN};
  74. var onlyMandatoryGrids = false;
  75. var attemptedGrids = [];
  76. for (var i = 0; i < source.grids.length; i++) {
  77. var grid = source.grids[i];
  78. attemptedGrids.push(grid.name);
  79. if (grid.isNull) {
  80. output = input;
  81. break;
  82. }
  83. onlyMandatoryGrids = grid.mandatory;
  84. if (grid.grid === null) {
  85. if (grid.mandatory) {
  86. console.log("Unable to find mandatory grid '" + grid.name + "'");
  87. return -1;
  88. }
  89. continue;
  90. }
  91. var subgrid = grid.grid.subgrids[0];
  92. // skip tables that don't match our point at all
  93. var epsilon = (Math.abs(subgrid.del[1]) + Math.abs(subgrid.del[0])) / 10000.0;
  94. var minX = subgrid.ll[0] - epsilon;
  95. var minY = subgrid.ll[1] - epsilon;
  96. var maxX = subgrid.ll[0] + (subgrid.lim[0] - 1) * subgrid.del[0] + epsilon;
  97. var maxY = subgrid.ll[1] + (subgrid.lim[1] - 1) * subgrid.del[1] + epsilon;
  98. if (minY > input.y || minX > input.x || maxY < input.y || maxX < input.x ) {
  99. continue;
  100. }
  101. output = applySubgridShift(input, inverse, subgrid);
  102. if (!isNaN(output.x)) {
  103. break;
  104. }
  105. }
  106. if (isNaN(output.x)) {
  107. console.log("Failed to find a grid shift table for location '"+
  108. -input.x * R2D + " " + input.y * R2D + " tried: '" + attemptedGrids + "'");
  109. return -1;
  110. }
  111. point.x = -output.x;
  112. point.y = output.y;
  113. return 0;
  114. }
  115. function applySubgridShift(pin, inverse, ct) {
  116. var val = {x: Number.NaN, y: Number.NaN};
  117. if (isNaN(pin.x)) { return val; }
  118. var tb = {x: pin.x, y: pin.y};
  119. tb.x -= ct.ll[0];
  120. tb.y -= ct.ll[1];
  121. tb.x = adjust_lon(tb.x - Math.PI) + Math.PI;
  122. var t = nadInterpolate(tb, ct);
  123. if (inverse) {
  124. if (isNaN(t.x)) {
  125. return val;
  126. }
  127. t.x = tb.x - t.x;
  128. t.y = tb.y - t.y;
  129. var i = 9, tol = 1e-12;
  130. var dif, del;
  131. do {
  132. del = nadInterpolate(t, ct);
  133. if (isNaN(del.x)) {
  134. console.log("Inverse grid shift iteration failed, presumably at grid edge. Using first approximation.");
  135. break;
  136. }
  137. dif = {x: tb.x - (del.x + t.x), y: tb.y - (del.y + t.y)};
  138. t.x += dif.x;
  139. t.y += dif.y;
  140. } while (i-- && Math.abs(dif.x) > tol && Math.abs(dif.y) > tol);
  141. if (i < 0) {
  142. console.log("Inverse grid shift iterator failed to converge.");
  143. return val;
  144. }
  145. val.x = adjust_lon(t.x + ct.ll[0]);
  146. val.y = t.y + ct.ll[1];
  147. } else {
  148. if (!isNaN(t.x)) {
  149. val.x = pin.x + t.x;
  150. val.y = pin.y + t.y;
  151. }
  152. }
  153. return val;
  154. }
  155. function nadInterpolate(pin, ct) {
  156. var t = {x: pin.x / ct.del[0], y: pin.y / ct.del[1]};
  157. var indx = {x: Math.floor(t.x), y: Math.floor(t.y)};
  158. var frct = {x: t.x - 1.0 * indx.x, y: t.y - 1.0 * indx.y};
  159. var val= {x: Number.NaN, y: Number.NaN};
  160. var inx;
  161. if (indx.x < 0 || indx.x >= ct.lim[0]) {
  162. return val;
  163. }
  164. if (indx.y < 0 || indx.y >= ct.lim[1]) {
  165. return val;
  166. }
  167. inx = (indx.y * ct.lim[0]) + indx.x;
  168. var f00 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};
  169. inx++;
  170. var f10= {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};
  171. inx += ct.lim[0];
  172. var f11 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};
  173. inx--;
  174. var f01 = {x: ct.cvs[inx][0], y: ct.cvs[inx][1]};
  175. var m11 = frct.x * frct.y, m10 = frct.x * (1.0 - frct.y),
  176. m00 = (1.0 - frct.x) * (1.0 - frct.y), m01 = (1.0 - frct.x) * frct.y;
  177. val.x = (m00 * f00.x + m10 * f10.x + m01 * f01.x + m11 * f11.x);
  178. val.y = (m00 * f00.y + m10 * f10.y + m01 * f01.y + m11 * f11.y);
  179. return val;
  180. }