/Proj4/pj_transform.c

http://github.com/route-me/route-me · C · 740 lines · 409 code · 100 blank · 231 comment · 190 complexity · 18919984cdbeb6f07b2e8eac21735a21 MD5 · raw file

  1. /******************************************************************************
  2. * $Id: pj_transform.c,v 1.24 2007/12/03 15:48:20 fwarmerdam Exp $
  3. *
  4. * Project: PROJ.4
  5. * Purpose: Perform overall coordinate system to coordinate system
  6. * transformations (pj_transform() function) including reprojection
  7. * and datum shifting.
  8. * Author: Frank Warmerdam, warmerdam@pobox.com
  9. *
  10. ******************************************************************************
  11. * Copyright (c) 2000, Frank Warmerdam
  12. *
  13. * Permission is hereby granted, free of charge, to any person obtaining a
  14. * copy of this software and associated documentation files (the "Software"),
  15. * to deal in the Software without restriction, including without limitation
  16. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  17. * and/or sell copies of the Software, and to permit persons to whom the
  18. * Software is furnished to do so, subject to the following conditions:
  19. *
  20. * The above copyright notice and this permission notice shall be included
  21. * in all copies or substantial portions of the Software.
  22. *
  23. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  24. * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  25. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  26. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  27. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  28. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  29. * DEALINGS IN THE SOFTWARE.
  30. ******************************************************************************
  31. *
  32. * $Log: pj_transform.c,v $
  33. * Revision 1.24 2007/12/03 15:48:20 fwarmerdam
  34. * Improve WGS84 ES precision to avoid unnecesary transformation (#1531)
  35. *
  36. * Revision 1.23 2007/11/26 00:21:59 fwarmerdam
  37. * Modified PJ structure to hold a_orig, es_orig, ellipsoid definition before
  38. * adjustment for spherical projections.
  39. * Modified pj_datum_transform() to use the original ellipsoid parameters,
  40. * not the ones adjusted for spherical projections.
  41. * Modified pj_datum_transform() to not attempt any datum shift via
  42. * geocentric coordinates if the source *or* destination are raw ellipsoids
  43. * (ie. PJD_UNKNOWN). All per PROJ bug #1602, GDAL bug #2025.
  44. *
  45. * Revision 1.22 2007/09/11 20:32:25 fwarmerdam
  46. * mark the transient error array const
  47. *
  48. * Revision 1.21 2007/09/11 20:19:36 fwarmerdam
  49. * avoid use of static variables to make reentrant
  50. *
  51. * Revision 1.20 2006/10/12 21:04:39 fwarmerdam
  52. * Added experimental +lon_wrap argument to set a "center point" for
  53. * longitude wrapping of longitude values coming out of pj_transform().
  54. *
  55. * Revision 1.19 2006/05/10 19:23:47 fwarmerdam
  56. * Don't apply to_meter in pj_transform() if the value is HUGE_VAL.
  57. *
  58. * Revision 1.18 2006/05/01 21:13:54 fwarmerdam
  59. * make out of range errors in geodetic to geocentric a transient error
  60. *
  61. * Revision 1.17 2006/03/20 17:54:34 fwarmerdam
  62. * pj_geodetic_to_geocentric returns -14 now for lat out of range
  63. *
  64. * Revision 1.16 2006/02/17 02:26:14 fwarmerdam
  65. * ERANGE/EDOM treated as transient errors
  66. *
  67. * Revision 1.15 2005/12/04 14:47:37 fwarmerdam
  68. * use symbolic names as per patch from Martin Vermeer
  69. *
  70. * Revision 1.14 2004/11/05 06:05:11 fwarmerdam
  71. * Fixed pj_geocentric_to_geodetic() to not try and process HUGE_VAL values
  72. * (those that have failed some previous transform step). Related to bug:5B
  73. * http://bugzilla.remotesensing.org/show_bug.cgi?id=642
  74. *
  75. * Revision 1.13 2004/10/25 15:34:36 fwarmerdam
  76. * make names of geodetic funcs from geotrans unique
  77. *
  78. * Revision 1.12 2004/05/03 19:45:23 warmerda
  79. * Altered so that raw ellpses are treated as a essentially having a
  80. * +towgs84=0,0,0 specification so ellpisoid shifts are applied.
  81. * Fixed so that prime meridian shifts are applied if the coordinate system is
  82. * not lat/long (ie. if it is projected). This fixes:
  83. * http://bugzilla.remotesensing.org/show_bug.cgi?id=510
  84. *
  85. * Revision 1.11 2004/01/24 09:37:19 warmerda
  86. * pj_transform() will no longer return an error code if any of the points are
  87. * transformable. In this case the application is expected to check for
  88. * HUGE_VAL to identify failed points.
  89. * As part of the implementation, I added a list of pj_errno values that
  90. * are transient (ie per-point) rather than indicating global failure for the
  91. * coordinate system definition. We use this in deciding which pj_fwd and
  92. * pj_inv error codes are really fatal and should be reported.
  93. *
  94. * Revision 1.10 2003/08/21 02:09:06 warmerda
  95. * added a bunch of HUGE_VAL checking
  96. *
  97. * Revision 1.9 2003/03/26 16:52:30 warmerda
  98. * added check that an inverse transformation func exists
  99. *
  100. * Revision 1.8 2002/12/14 20:35:43 warmerda
  101. * implement units support for geocentric coordinates
  102. *
  103. * Revision 1.7 2002/12/14 20:14:35 warmerda
  104. * added geocentric support
  105. *
  106. * Revision 1.6 2002/12/09 16:01:02 warmerda
  107. * added prime meridian support
  108. *
  109. * Revision 1.5 2002/12/01 19:25:26 warmerda
  110. * applied fix for 7 param shift in pj_geocentric_from_wgs84, see bug 194
  111. *
  112. * Revision 1.4 2002/02/15 14:30:36 warmerda
  113. * provide default Z array if none passed in in pj_datum_transform()
  114. *
  115. * Revision 1.3 2001/04/04 21:13:21 warmerda
  116. * do arcsecond/radian and ppm datum parm transformation in pj_set_datum()
  117. *
  118. * Revision 1.2 2001/04/04 16:08:08 warmerda
  119. * rewrote 7 param datum shift to match EPSG:9606, now works with example
  120. *
  121. * Revision 1.1 2000/07/06 23:32:27 warmerda
  122. * New
  123. *
  124. */
  125. #include "projects.h"
  126. #include <string.h>
  127. #include <math.h>
  128. #include "geocent.h"
  129. PJ_CVSID("$Id: pj_transform.c,v 1.24 2007/12/03 15:48:20 fwarmerdam Exp $");
  130. #ifndef SRS_WGS84_SEMIMAJOR
  131. #define SRS_WGS84_SEMIMAJOR 6378137.0
  132. #endif
  133. #ifndef SRS_WGS84_ESQUARED
  134. #define SRS_WGS84_ESQUARED 0.0066943799901413165
  135. #endif
  136. #define Dx_BF (defn->datum_params[0])
  137. #define Dy_BF (defn->datum_params[1])
  138. #define Dz_BF (defn->datum_params[2])
  139. #define Rx_BF (defn->datum_params[3])
  140. #define Ry_BF (defn->datum_params[4])
  141. #define Rz_BF (defn->datum_params[5])
  142. #define M_BF (defn->datum_params[6])
  143. /*
  144. ** This table is intended to indicate for any given error code in
  145. ** the range 0 to -44, whether that error will occur for all locations (ie.
  146. ** it is a problem with the coordinate system as a whole) in which case the
  147. ** value would be 0, or if the problem is with the point being transformed
  148. ** in which case the value is 1.
  149. **
  150. ** At some point we might want to move this array in with the error message
  151. ** list or something, but while experimenting with it this should be fine.
  152. */
  153. static const int transient_error[45] = {
  154. /* 0 1 2 3 4 5 6 7 8 9 */
  155. /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  156. /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1,
  157. /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0,
  158. /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
  159. /* 40 to 44 */ 0, 0, 0, 0, 0 };
  160. /************************************************************************/
  161. /* pj_transform() */
  162. /* */
  163. /* Currently this function doesn't recognise if two projections */
  164. /* are identical (to short circuit reprojection) because it is */
  165. /* difficult to compare PJ structures (since there are some */
  166. /* projection specific components). */
  167. /************************************************************************/
  168. int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset,
  169. double *x, double *y, double *z )
  170. {
  171. long i;
  172. int need_datum_shift;
  173. pj_errno = 0;
  174. if( point_offset == 0 )
  175. point_offset = 1;
  176. /* -------------------------------------------------------------------- */
  177. /* Transform geocentric source coordinates to lat/long. */
  178. /* -------------------------------------------------------------------- */
  179. if( srcdefn->is_geocent )
  180. {
  181. if( z == NULL )
  182. {
  183. pj_errno = PJD_ERR_GEOCENTRIC;
  184. return PJD_ERR_GEOCENTRIC;
  185. }
  186. if( srcdefn->to_meter != 1.0 )
  187. {
  188. for( i = 0; i < point_count; i++ )
  189. {
  190. if( x[point_offset*i] != HUGE_VAL )
  191. {
  192. x[point_offset*i] *= srcdefn->to_meter;
  193. y[point_offset*i] *= srcdefn->to_meter;
  194. }
  195. }
  196. }
  197. if( pj_geocentric_to_geodetic( srcdefn->a_orig, srcdefn->es_orig,
  198. point_count, point_offset,
  199. x, y, z ) != 0)
  200. return pj_errno;
  201. }
  202. /* -------------------------------------------------------------------- */
  203. /* Transform source points to lat/long, if they aren't */
  204. /* already. */
  205. /* -------------------------------------------------------------------- */
  206. else if( !srcdefn->is_latlong )
  207. {
  208. if( srcdefn->inv == NULL )
  209. {
  210. pj_errno = -17; /* this isn't correct, we need a no inverse err */
  211. if( getenv( "PROJ_DEBUG" ) != NULL )
  212. {
  213. fprintf( stderr,
  214. "pj_transform(): source projection not invertable\n" );
  215. }
  216. return pj_errno;
  217. }
  218. for( i = 0; i < point_count; i++ )
  219. {
  220. XY projected_loc;
  221. LP geodetic_loc;
  222. projected_loc.u = x[point_offset*i];
  223. projected_loc.v = y[point_offset*i];
  224. if( projected_loc.u == HUGE_VAL )
  225. continue;
  226. geodetic_loc = pj_inv( projected_loc, srcdefn );
  227. if( pj_errno != 0 )
  228. {
  229. if( (pj_errno != 33 /*EDOM*/ && pj_errno != 34 /*ERANGE*/ )
  230. && (pj_errno > 0 || pj_errno < -44 || point_count == 1
  231. || transient_error[-pj_errno] == 0 ) )
  232. return pj_errno;
  233. else
  234. {
  235. geodetic_loc.u = HUGE_VAL;
  236. geodetic_loc.v = HUGE_VAL;
  237. }
  238. }
  239. x[point_offset*i] = geodetic_loc.u;
  240. y[point_offset*i] = geodetic_loc.v;
  241. }
  242. }
  243. /* -------------------------------------------------------------------- */
  244. /* But if they are already lat long, adjust for the prime */
  245. /* meridian if there is one in effect. */
  246. /* -------------------------------------------------------------------- */
  247. if( srcdefn->from_greenwich != 0.0 )
  248. {
  249. for( i = 0; i < point_count; i++ )
  250. {
  251. if( x[point_offset*i] != HUGE_VAL )
  252. x[point_offset*i] += srcdefn->from_greenwich;
  253. }
  254. }
  255. /* -------------------------------------------------------------------- */
  256. /* Convert datums if needed, and possible. */
  257. /* -------------------------------------------------------------------- */
  258. if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset,
  259. x, y, z ) != 0 )
  260. return pj_errno;
  261. /* -------------------------------------------------------------------- */
  262. /* But if they are staying lat long, adjust for the prime */
  263. /* meridian if there is one in effect. */
  264. /* -------------------------------------------------------------------- */
  265. if( dstdefn->from_greenwich != 0.0 )
  266. {
  267. for( i = 0; i < point_count; i++ )
  268. {
  269. if( x[point_offset*i] != HUGE_VAL )
  270. x[point_offset*i] -= dstdefn->from_greenwich;
  271. }
  272. }
  273. /* -------------------------------------------------------------------- */
  274. /* Transform destination latlong to geocentric if required. */
  275. /* -------------------------------------------------------------------- */
  276. if( dstdefn->is_geocent )
  277. {
  278. if( z == NULL )
  279. {
  280. pj_errno = PJD_ERR_GEOCENTRIC;
  281. return PJD_ERR_GEOCENTRIC;
  282. }
  283. pj_geodetic_to_geocentric( dstdefn->a_orig, dstdefn->es_orig,
  284. point_count, point_offset, x, y, z );
  285. if( dstdefn->fr_meter != 1.0 )
  286. {
  287. for( i = 0; i < point_count; i++ )
  288. {
  289. if( x[point_offset*i] != HUGE_VAL )
  290. {
  291. x[point_offset*i] *= dstdefn->fr_meter;
  292. y[point_offset*i] *= dstdefn->fr_meter;
  293. }
  294. }
  295. }
  296. }
  297. /* -------------------------------------------------------------------- */
  298. /* Transform destination points to projection coordinates, if */
  299. /* desired. */
  300. /* -------------------------------------------------------------------- */
  301. else if( !dstdefn->is_latlong )
  302. {
  303. for( i = 0; i < point_count; i++ )
  304. {
  305. XY projected_loc;
  306. LP geodetic_loc;
  307. geodetic_loc.u = x[point_offset*i];
  308. geodetic_loc.v = y[point_offset*i];
  309. if( geodetic_loc.u == HUGE_VAL )
  310. continue;
  311. projected_loc = pj_fwd( geodetic_loc, dstdefn );
  312. if( pj_errno != 0 )
  313. {
  314. if( (pj_errno != 33 /*EDOM*/ && pj_errno != 34 /*ERANGE*/ )
  315. && (pj_errno > 0 || pj_errno < -44 || point_count == 1
  316. || transient_error[-pj_errno] == 0 ) )
  317. return pj_errno;
  318. else
  319. {
  320. projected_loc.u = HUGE_VAL;
  321. projected_loc.v = HUGE_VAL;
  322. }
  323. }
  324. x[point_offset*i] = projected_loc.u;
  325. y[point_offset*i] = projected_loc.v;
  326. }
  327. }
  328. /* -------------------------------------------------------------------- */
  329. /* If a wrapping center other than 0 is provided, rewrap around */
  330. /* the suggested center (for latlong coordinate systems only). */
  331. /* -------------------------------------------------------------------- */
  332. else if( dstdefn->is_latlong && dstdefn->long_wrap_center != 0 )
  333. {
  334. for( i = 0; i < point_count; i++ )
  335. {
  336. if( x[point_offset*i] == HUGE_VAL )
  337. continue;
  338. while( x[point_offset*i] < dstdefn->long_wrap_center - HALFPI )
  339. x[point_offset*i] += PI;
  340. while( x[point_offset*i] > dstdefn->long_wrap_center + HALFPI )
  341. x[point_offset*i] -= PI;
  342. }
  343. }
  344. return 0;
  345. }
  346. /************************************************************************/
  347. /* pj_geodetic_to_geocentric() */
  348. /************************************************************************/
  349. int pj_geodetic_to_geocentric( double a, double es,
  350. long point_count, int point_offset,
  351. double *x, double *y, double *z )
  352. {
  353. double b;
  354. int i;
  355. GeocentricInfo gi;
  356. pj_errno = 0;
  357. if( es == 0.0 )
  358. b = a;
  359. else
  360. b = a * sqrt(1-es);
  361. if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 )
  362. {
  363. pj_errno = PJD_ERR_GEOCENTRIC;
  364. return pj_errno;
  365. }
  366. for( i = 0; i < point_count; i++ )
  367. {
  368. long io = i * point_offset;
  369. if( x[io] == HUGE_VAL )
  370. continue;
  371. if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io],
  372. x+io, y+io, z+io ) != 0 )
  373. {
  374. pj_errno = -14;
  375. x[io] = y[io] = HUGE_VAL;
  376. /* but keep processing points! */
  377. }
  378. }
  379. return pj_errno;
  380. }
  381. /************************************************************************/
  382. /* pj_geodetic_to_geocentric() */
  383. /************************************************************************/
  384. int pj_geocentric_to_geodetic( double a, double es,
  385. long point_count, int point_offset,
  386. double *x, double *y, double *z )
  387. {
  388. double b;
  389. int i;
  390. GeocentricInfo gi;
  391. if( es == 0.0 )
  392. b = a;
  393. else
  394. b = a * sqrt(1-es);
  395. if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 )
  396. {
  397. pj_errno = PJD_ERR_GEOCENTRIC;
  398. return pj_errno;
  399. }
  400. for( i = 0; i < point_count; i++ )
  401. {
  402. long io = i * point_offset;
  403. if( x[io] == HUGE_VAL )
  404. continue;
  405. pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io],
  406. y+io, x+io, z+io );
  407. }
  408. return 0;
  409. }
  410. /************************************************************************/
  411. /* pj_compare_datums() */
  412. /* */
  413. /* Returns TRUE if the two datums are identical, otherwise */
  414. /* FALSE. */
  415. /************************************************************************/
  416. int pj_compare_datums( PJ *srcdefn, PJ *dstdefn )
  417. {
  418. if( srcdefn->datum_type != dstdefn->datum_type )
  419. {
  420. return 0;
  421. }
  422. else if( srcdefn->a_orig != dstdefn->a_orig
  423. || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 )
  424. {
  425. /* the tolerence for es is to ensure that GRS80 and WGS84 are
  426. considered identical */
  427. return 0;
  428. }
  429. else if( srcdefn->datum_type == PJD_3PARAM )
  430. {
  431. return (srcdefn->datum_params[0] == dstdefn->datum_params[0]
  432. && srcdefn->datum_params[1] == dstdefn->datum_params[1]
  433. && srcdefn->datum_params[2] == dstdefn->datum_params[2]);
  434. }
  435. else if( srcdefn->datum_type == PJD_7PARAM )
  436. {
  437. return (srcdefn->datum_params[0] == dstdefn->datum_params[0]
  438. && srcdefn->datum_params[1] == dstdefn->datum_params[1]
  439. && srcdefn->datum_params[2] == dstdefn->datum_params[2]
  440. && srcdefn->datum_params[3] == dstdefn->datum_params[3]
  441. && srcdefn->datum_params[4] == dstdefn->datum_params[4]
  442. && srcdefn->datum_params[5] == dstdefn->datum_params[5]
  443. && srcdefn->datum_params[6] == dstdefn->datum_params[6]);
  444. }
  445. else if( srcdefn->datum_type == PJD_GRIDSHIFT )
  446. {
  447. return strcmp( pj_param(srcdefn->params,"snadgrids").s,
  448. pj_param(dstdefn->params,"snadgrids").s ) == 0;
  449. }
  450. else
  451. return 1;
  452. }
  453. /************************************************************************/
  454. /* pj_geocentic_to_wgs84() */
  455. /************************************************************************/
  456. int pj_geocentric_to_wgs84( PJ *defn,
  457. long point_count, int point_offset,
  458. double *x, double *y, double *z )
  459. {
  460. int i;
  461. pj_errno = 0;
  462. if( defn->datum_type == PJD_3PARAM )
  463. {
  464. for( i = 0; i < point_count; i++ )
  465. {
  466. long io = i * point_offset;
  467. if( x[io] == HUGE_VAL )
  468. continue;
  469. x[io] = x[io] + Dx_BF;
  470. y[io] = y[io] + Dy_BF;
  471. z[io] = z[io] + Dz_BF;
  472. }
  473. }
  474. else if( defn->datum_type == PJD_7PARAM )
  475. {
  476. for( i = 0; i < point_count; i++ )
  477. {
  478. long io = i * point_offset;
  479. double x_out, y_out, z_out;
  480. if( x[io] == HUGE_VAL )
  481. continue;
  482. x_out = M_BF*( x[io] - Rz_BF*y[io] + Ry_BF*z[io]) + Dx_BF;
  483. y_out = M_BF*( Rz_BF*x[io] + y[io] - Rx_BF*z[io]) + Dy_BF;
  484. z_out = M_BF*(-Ry_BF*x[io] + Rx_BF*y[io] + z[io]) + Dz_BF;
  485. x[io] = x_out;
  486. y[io] = y_out;
  487. z[io] = z_out;
  488. }
  489. }
  490. return 0;
  491. }
  492. /************************************************************************/
  493. /* pj_geocentic_from_wgs84() */
  494. /************************************************************************/
  495. int pj_geocentric_from_wgs84( PJ *defn,
  496. long point_count, int point_offset,
  497. double *x, double *y, double *z )
  498. {
  499. int i;
  500. pj_errno = 0;
  501. if( defn->datum_type == PJD_3PARAM )
  502. {
  503. for( i = 0; i < point_count; i++ )
  504. {
  505. long io = i * point_offset;
  506. if( x[io] == HUGE_VAL )
  507. continue;
  508. x[io] = x[io] - Dx_BF;
  509. y[io] = y[io] - Dy_BF;
  510. z[io] = z[io] - Dz_BF;
  511. }
  512. }
  513. else if( defn->datum_type == PJD_7PARAM )
  514. {
  515. for( i = 0; i < point_count; i++ )
  516. {
  517. long io = i * point_offset;
  518. double x_tmp, y_tmp, z_tmp;
  519. if( x[io] == HUGE_VAL )
  520. continue;
  521. x_tmp = (x[io] - Dx_BF) / M_BF;
  522. y_tmp = (y[io] - Dy_BF) / M_BF;
  523. z_tmp = (z[io] - Dz_BF) / M_BF;
  524. x[io] = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp;
  525. y[io] = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp;
  526. z[io] = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp;
  527. }
  528. }
  529. return 0;
  530. }
  531. /************************************************************************/
  532. /* pj_datum_transform() */
  533. /* */
  534. /* The input should be long/lat/z coordinates in radians in the */
  535. /* source datum, and the output should be long/lat/z */
  536. /* coordinates in radians in the destination datum. */
  537. /************************************************************************/
  538. int pj_datum_transform( PJ *srcdefn, PJ *dstdefn,
  539. long point_count, int point_offset,
  540. double *x, double *y, double *z )
  541. {
  542. double src_a, src_es, dst_a, dst_es;
  543. int z_is_temp = FALSE;
  544. pj_errno = 0;
  545. /* -------------------------------------------------------------------- */
  546. /* We cannot do any meaningful datum transformation if either */
  547. /* the source or destination are of an unknown datum type */
  548. /* (ie. only a +ellps declaration, no +datum). This is new */
  549. /* behavior for PROJ 4.6.0. */
  550. /* -------------------------------------------------------------------- */
  551. if( srcdefn->datum_type == PJD_UNKNOWN
  552. || dstdefn->datum_type == PJD_UNKNOWN )
  553. return 0;
  554. /* -------------------------------------------------------------------- */
  555. /* Short cut if the datums are identical. */
  556. /* -------------------------------------------------------------------- */
  557. if( pj_compare_datums( srcdefn, dstdefn ) )
  558. return 0;
  559. src_a = srcdefn->a_orig;
  560. src_es = srcdefn->es_orig;
  561. dst_a = dstdefn->a_orig;
  562. dst_es = dstdefn->es_orig;
  563. /* -------------------------------------------------------------------- */
  564. /* Create a temporary Z array if one is not provided. */
  565. /* -------------------------------------------------------------------- */
  566. if( z == NULL )
  567. {
  568. int bytes = sizeof(double) * point_count * point_offset;
  569. z = (double *) pj_malloc(bytes);
  570. memset( z, 0, bytes );
  571. z_is_temp = TRUE;
  572. }
  573. #define CHECK_RETURN {if( pj_errno != 0 && (pj_errno > 0 || transient_error[-pj_errno] == 0) ) { if( z_is_temp ) pj_dalloc(z); return pj_errno; }}
  574. /* -------------------------------------------------------------------- */
  575. /* If this datum requires grid shifts, then apply it to geodetic */
  576. /* coordinates. */
  577. /* -------------------------------------------------------------------- */
  578. if( srcdefn->datum_type == PJD_GRIDSHIFT )
  579. {
  580. pj_apply_gridshift( pj_param(srcdefn->params,"snadgrids").s, 0,
  581. point_count, point_offset, x, y, z );
  582. CHECK_RETURN;
  583. src_a = SRS_WGS84_SEMIMAJOR;
  584. src_es = SRS_WGS84_ESQUARED;
  585. }
  586. if( dstdefn->datum_type == PJD_GRIDSHIFT )
  587. {
  588. dst_a = SRS_WGS84_SEMIMAJOR;
  589. dst_es = SRS_WGS84_ESQUARED;
  590. }
  591. /* ==================================================================== */
  592. /* Do we need to go through geocentric coordinates? */
  593. /* ==================================================================== */
  594. if( src_es != dst_es || src_a != dst_a
  595. || srcdefn->datum_type == PJD_3PARAM
  596. || srcdefn->datum_type == PJD_7PARAM
  597. || dstdefn->datum_type == PJD_3PARAM
  598. || dstdefn->datum_type == PJD_7PARAM)
  599. {
  600. /* -------------------------------------------------------------------- */
  601. /* Convert to geocentric coordinates. */
  602. /* -------------------------------------------------------------------- */
  603. pj_geodetic_to_geocentric( src_a, src_es,
  604. point_count, point_offset, x, y, z );
  605. CHECK_RETURN;
  606. /* -------------------------------------------------------------------- */
  607. /* Convert between datums. */
  608. /* -------------------------------------------------------------------- */
  609. if( srcdefn->datum_type == PJD_3PARAM
  610. || srcdefn->datum_type == PJD_7PARAM )
  611. {
  612. pj_geocentric_to_wgs84( srcdefn, point_count, point_offset,x,y,z);
  613. CHECK_RETURN;
  614. }
  615. if( dstdefn->datum_type == PJD_3PARAM
  616. || dstdefn->datum_type == PJD_7PARAM )
  617. {
  618. pj_geocentric_from_wgs84( dstdefn, point_count,point_offset,x,y,z);
  619. CHECK_RETURN;
  620. }
  621. /* -------------------------------------------------------------------- */
  622. /* Convert back to geodetic coordinates. */
  623. /* -------------------------------------------------------------------- */
  624. pj_geocentric_to_geodetic( dst_a, dst_es,
  625. point_count, point_offset, x, y, z );
  626. CHECK_RETURN;
  627. }
  628. /* -------------------------------------------------------------------- */
  629. /* Apply grid shift to destination if required. */
  630. /* -------------------------------------------------------------------- */
  631. if( dstdefn->datum_type == PJD_GRIDSHIFT )
  632. {
  633. pj_apply_gridshift( pj_param(dstdefn->params,"snadgrids").s, 1,
  634. point_count, point_offset, x, y, z );
  635. CHECK_RETURN;
  636. }
  637. if( z_is_temp )
  638. pj_dalloc( z );
  639. return 0;
  640. }