do_proj.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. /**
  2. \file do_proj.c
  3. \brief GProj library - Functions for re-projecting point data
  4. \author Original Author unknown, probably Soil Conservation Service
  5. Eric Miller, Paul Kelly, Markus Metz
  6. (C) 2003-2008,2018 by the GRASS Development Team
  7. This program is free software under the GNU General Public
  8. License (>=v2). Read the file COPYING that comes with GRASS
  9. for details.
  10. **/
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include <grass/gis.h>
  15. #include <grass/gprojects.h>
  16. #include <grass/glocale.h>
  17. /* a couple defines to simplify reading the function */
  18. #define MULTIPLY_LOOP(x,y,c,m) \
  19. do {\
  20. int i; \
  21. for (i = 0; i < c; ++i) {\
  22. x[i] *= m; \
  23. y[i] *= m; \
  24. }\
  25. } while (0)
  26. #define DIVIDE_LOOP(x,y,c,m) \
  27. do {\
  28. int i; \
  29. for (i = 0; i < c; ++i) {\
  30. x[i] /= m; \
  31. y[i] /= m; \
  32. }\
  33. } while (0)
  34. static double METERS_in = 1.0, METERS_out = 1.0;
  35. /**
  36. * \brief Create a PROJ transformation object to transform coordinates
  37. * from an input SRS to an output SRS
  38. *
  39. * After the transformation has been initialized with this function,
  40. * coordinates can be transformed from input SRS to output SRS with
  41. * GPJ_transform() and direction = PJ_FWD, and back from output SRS to
  42. * input SRS with direction = OJ_INV.
  43. * If coordinates should be transformed between the input SRS and its
  44. * latlong equivalent, an uninitialized info_out with
  45. * info_out->pj = NULL can be passed to the function. In this case,
  46. * coordinates will be transformed between the input SRS and its
  47. * latlong equivalent, and for PROJ 5+, the transformation object is
  48. * created accordingly, while for PROJ 4, the output SRS is created as
  49. * latlong equivalent of the input SRS
  50. *
  51. * PROJ 5+:
  52. * info_in->pj must not be null
  53. * if info_out->pj is null, assume info_out to be the ll equivalent
  54. * of info_in
  55. * create info_trans as conversion from info_in to its ll equivalent
  56. * NOTE: this is the inverse of the logic of PROJ 5 which by default
  57. * converts from ll to a given SRS, not from a given SRS to ll
  58. * thus PROJ 5+ itself uses an inverse transformation in the
  59. * first step of the pipeline for proj_create_crs_to_crs()
  60. * PROJ 4:
  61. * info_in->pj must not be null
  62. * if info_out->pj is null, create info_out as ll equivalent
  63. * else do nothing, info_trans is not used
  64. *
  65. * \param info_in pointer to pj_info struct for input co-ordinate system
  66. * \param info_out pointer to pj_info struct for output co-ordinate system
  67. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  68. *
  69. * \return 1 on success, -1 on failure
  70. **/
  71. int GPJ_init_transform(const struct pj_info *info_in,
  72. const struct pj_info *info_out,
  73. struct pj_info *info_trans)
  74. {
  75. if (info_in->pj == NULL)
  76. G_fatal_error(_("Input coordinate system is NULL"));
  77. #ifdef HAVE_PROJ_H
  78. if (info_out->pj != NULL && info_out->def != NULL)
  79. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s +step %s",
  80. info_in->def, info_out->def);
  81. else
  82. /* assume info_out to be ll equivalent of info_in */
  83. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s",
  84. info_in->def);
  85. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  86. if (info_trans->pj == NULL) {
  87. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  88. return -1;
  89. }
  90. info_trans->meters = 1.;
  91. info_trans->zone = 0;
  92. sprintf(info_trans->proj, "pipeline");
  93. #else
  94. if (info_out->pj == NULL) {
  95. if (GPJ_get_equivalent_latlong(info_out, info_in) < 0) {
  96. G_warning(_("Unable to create latlong equivalent for '%s'"),
  97. info_in->def);
  98. return -1;
  99. }
  100. }
  101. #endif
  102. return 1;
  103. }
  104. /* TODO: rename pj_ to GPJ_ to avoid symbol clash with PROJ lib */
  105. /**
  106. * \brief Re-project a point between two co-ordinate systems using a
  107. * transformation object prepared with GPJ_prepare_pj()
  108. *
  109. * This function takes pointers to three pj_info structures as arguments,
  110. * and projects a point between the input and output co-ordinate system.
  111. * The pj_info structure info_trans must have been initialized with
  112. * GPJ_init_transform().
  113. * The direction determines if a point is projected from input CRS to
  114. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  115. * The easting, northing, and height of the point are contained in the
  116. * pointers passed to the function; these will be overwritten by the
  117. * coordinates of the transformed point.
  118. *
  119. * \param info_in pointer to pj_info struct for input co-ordinate system
  120. * \param info_out pointer to pj_info struct for output co-ordinate system
  121. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  122. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  123. * \param x Pointer to a double containing easting or longitude
  124. * \param y Pointer to a double containing northing or latitude
  125. * \param z Pointer to a double containing height, or NULL
  126. *
  127. * \return Return value from PROJ proj_trans() function
  128. **/
  129. int GPJ_transform(const struct pj_info *info_in,
  130. const struct pj_info *info_out,
  131. const struct pj_info *info_trans, int dir,
  132. double *x, double *y, double *z)
  133. {
  134. int ok = 0;
  135. #ifdef HAVE_PROJ_H
  136. /* PROJ 5+ variant */
  137. int in_is_ll, out_is_ll;
  138. PJ_COORD c;
  139. if (info_in->pj == NULL)
  140. G_fatal_error(_("No input projection"));
  141. if (info_trans->pj == NULL)
  142. G_fatal_error(_("No transformation object"));
  143. if (dir == PJ_FWD) {
  144. /* info_in -> info_out */
  145. METERS_in = info_in->meters;
  146. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  147. if (info_out->pj) {
  148. METERS_out = info_out->meters;
  149. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  150. }
  151. else {
  152. METERS_out = 1.0;
  153. out_is_ll = 1;
  154. }
  155. }
  156. else {
  157. /* info_out -> info_in */
  158. METERS_out = info_in->meters;
  159. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  160. if (info_out->pj) {
  161. METERS_in = info_out->meters;
  162. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  163. }
  164. else {
  165. METERS_in = 1.0;
  166. in_is_ll = 1;
  167. }
  168. }
  169. /* prepare */
  170. if (in_is_ll) {
  171. /* convert to radians */
  172. c.lpzt.lam = (*x) / RAD_TO_DEG;
  173. c.lpzt.phi = (*y) / RAD_TO_DEG;
  174. c.lpzt.z = 0;
  175. if (z)
  176. c.lpzt.z = *z;
  177. c.lpzt.t = 0;
  178. }
  179. else {
  180. /* convert to meters */
  181. c.xyzt.x = *x * METERS_in;
  182. c.xyzt.y = *y * METERS_in;
  183. c.xyzt.z = 0;
  184. if (z)
  185. c.xyzt.z = *z;
  186. c.xyzt.t = 0;
  187. }
  188. /* transform */
  189. c = proj_trans(info_trans->pj, dir, c);
  190. ok = proj_errno(info_trans->pj);
  191. if (ok < 0) {
  192. G_warning(_("proj_trans() failed: %d"), ok);
  193. return ok;
  194. }
  195. /* output */
  196. if (out_is_ll) {
  197. /* convert to degrees */
  198. *x = c.lpzt.lam * RAD_TO_DEG;
  199. *y = c.lpzt.phi * RAD_TO_DEG;
  200. if (z)
  201. *z = c.lpzt.z;
  202. }
  203. else {
  204. /* convert to map units */
  205. *x = c.xyzt.x / METERS_out;
  206. *y = c.xyzt.y / METERS_out;
  207. if (z)
  208. *z = c.xyzt.z;
  209. }
  210. #else
  211. /* PROJ 4 variant */
  212. double u, v;
  213. double h = 0.0;
  214. const struct pj_info *p_in, *p_out;
  215. if (info_out == NULL)
  216. G_fatal_error(_("No output projection"));
  217. if (dir == FWD) {
  218. p_in = info_in;
  219. p_out = info_out;
  220. }
  221. else {
  222. p_in = info_out;
  223. p_out = info_in;
  224. }
  225. METERS_in = p_in->meters;
  226. METERS_out = p_out->meters;
  227. if (z)
  228. h = *z;
  229. if (strncmp(p_in->proj, "ll", 2) == 0) {
  230. u = (*x) / RAD_TO_DEG;
  231. v = (*y) / RAD_TO_DEG;
  232. }
  233. else {
  234. u = *x * METERS_in;
  235. v = *y * METERS_in;
  236. }
  237. ok = pj_transform(p_in->pj, p_out->pj, 1, 0, &u, &v, &h);
  238. if (ok < 0) {
  239. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  240. return ok;
  241. }
  242. if (strncmp(p_out->proj, "ll", 2) == 0) {
  243. *x = u * RAD_TO_DEG;
  244. *y = v * RAD_TO_DEG;
  245. }
  246. else {
  247. *x = u / METERS_out;
  248. *y = v / METERS_out;
  249. }
  250. if (z)
  251. *z = h;
  252. #endif
  253. return ok;
  254. }
  255. /**
  256. * \brief Re-project an array of points between two co-ordinate systems
  257. * using a transformation object prepared with GPJ_prepare_pj()
  258. *
  259. * This function takes pointers to three pj_info structures as arguments,
  260. * and projects an array of pointd between the input and output
  261. * co-ordinate system. The pj_info structure info_trans must have been
  262. * initialized with GPJ_init_transform().
  263. * The direction determines if a point is projected from input CRS to
  264. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  265. * The easting, northing, and height of the point are contained in the
  266. * pointers passed to the function; these will be overwritten by the
  267. * coordinates of the transformed point.
  268. *
  269. * \param info_in pointer to pj_info struct for input co-ordinate system
  270. * \param info_out pointer to pj_info struct for output co-ordinate system
  271. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  272. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  273. * \param x pointer to an array of type double containing easting or longitude
  274. * \param y pointer to an array of type double containing northing or latitude
  275. * \param z pointer to an array of type double containing height, or NULL
  276. * \param n number of points in the arrays to be transformed
  277. *
  278. * \return Return value from PROJ proj_trans() function
  279. **/
  280. int GPJ_transform_array(const struct pj_info *info_in,
  281. const struct pj_info *info_out,
  282. const struct pj_info *info_trans, int dir,
  283. double *x, double *y, double *z, int n)
  284. {
  285. int ok;
  286. int i;
  287. int has_z = 1;
  288. #ifdef HAVE_PROJ_H
  289. /* PROJ 5+ variant */
  290. int in_is_ll, out_is_ll;
  291. PJ_COORD c;
  292. if (info_trans->pj == NULL)
  293. G_fatal_error(_("No transformation object"));
  294. if (dir == PJ_FWD) {
  295. /* info_in -> info_out */
  296. METERS_in = info_in->meters;
  297. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  298. if (info_out->pj) {
  299. METERS_out = info_out->meters;
  300. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  301. }
  302. else {
  303. METERS_out = 1.0;
  304. out_is_ll = 1;
  305. }
  306. }
  307. else {
  308. /* info_out -> info_in */
  309. METERS_out = info_in->meters;
  310. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  311. if (info_out->pj) {
  312. METERS_in = info_out->meters;
  313. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  314. }
  315. else {
  316. METERS_in = 1.0;
  317. in_is_ll = 1;
  318. }
  319. }
  320. if (z == NULL) {
  321. z = G_malloc(sizeof(double) * n);
  322. /* they say memset is only guaranteed for chars ;-( */
  323. for (i = 0; i < n; i++)
  324. z[i] = 0.0;
  325. has_z = 0;
  326. }
  327. ok = 0;
  328. if (in_is_ll) {
  329. c.lpzt.t = 0;
  330. if (out_is_ll) {
  331. /* what is more costly ?
  332. * calling proj_trans for each point
  333. * or having three loops over all points ?
  334. * proj_trans_array() itself calls proj_trans() in a loop
  335. * -> one loop over all points is better than
  336. * three loops over all points
  337. */
  338. for (i = 0; i < n; i++) {
  339. /* convert to radians */
  340. c.lpzt.lam = x[i] / RAD_TO_DEG;
  341. c.lpzt.phi = y[i] / RAD_TO_DEG;
  342. c.lpzt.z = z[i];
  343. c = proj_trans(info_trans->pj, dir, c);
  344. if ((ok = proj_errno(info_trans->pj)) < 0)
  345. break;
  346. /* convert to degrees */
  347. x[i] = c.lp.lam * RAD_TO_DEG;
  348. y[i] = c.lp.phi * RAD_TO_DEG;
  349. }
  350. }
  351. else {
  352. for (i = 0; i < n; i++) {
  353. /* convert to radians */
  354. c.lpzt.lam = x[i] / RAD_TO_DEG;
  355. c.lpzt.phi = y[i] / RAD_TO_DEG;
  356. c.lpzt.z = z[i];
  357. c = proj_trans(info_trans->pj, dir, c);
  358. if ((ok = proj_errno(info_trans->pj)) < 0)
  359. break;
  360. /* convert to map units */
  361. x[i] = c.xy.x / METERS_out;
  362. y[i] = c.xy.y / METERS_out;
  363. }
  364. }
  365. }
  366. else {
  367. c.xyzt.t = 0;
  368. if (out_is_ll) {
  369. for (i = 0; i < n; i++) {
  370. /* convert to meters */
  371. c.xyzt.x = x[i] * METERS_in;
  372. c.xyzt.y = y[i] * METERS_in;
  373. c.xyzt.z = z[i];
  374. c = proj_trans(info_trans->pj, dir, c);
  375. if ((ok = proj_errno(info_trans->pj)) < 0)
  376. break;
  377. /* convert to degrees */
  378. x[i] = c.lp.lam * RAD_TO_DEG;
  379. y[i] = c.lp.phi * RAD_TO_DEG;
  380. }
  381. }
  382. else {
  383. for (i = 0; i < n; i++) {
  384. /* convert to meters */
  385. c.xyzt.x = x[i] * METERS_in;
  386. c.xyzt.y = y[i] * METERS_in;
  387. c.xyzt.z = z[i];
  388. c = proj_trans(info_trans->pj, dir, c);
  389. if ((ok = proj_errno(info_trans->pj)) < 0)
  390. break;
  391. /* convert to map units */
  392. x[i] = c.xy.x / METERS_out;
  393. y[i] = c.xy.y / METERS_out;
  394. }
  395. }
  396. }
  397. if (!has_z)
  398. G_free(z);
  399. if (ok < 0) {
  400. G_warning(_("proj_trans() failed: %d"), ok);
  401. }
  402. #else
  403. /* PROJ 4 variant */
  404. const struct pj_info *p_in, *p_out;
  405. if (dir == FWD) {
  406. p_in = info_in;
  407. p_out = info_out;
  408. }
  409. else {
  410. p_in = info_out;
  411. p_out = info_in;
  412. }
  413. METERS_in = p_in->meters;
  414. METERS_out = p_out->meters;
  415. if (z == NULL) {
  416. z = G_malloc(sizeof(double) * n);
  417. /* they say memset is only guaranteed for chars ;-( */
  418. for (i = 0; i < n; ++i)
  419. z[i] = 0.0;
  420. has_z = 0;
  421. }
  422. if (strncmp(p_in->proj, "ll", 2) == 0) {
  423. if (strncmp(p_out->proj, "ll", 2) == 0) {
  424. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  425. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  426. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  427. }
  428. else {
  429. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  430. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  431. DIVIDE_LOOP(x, y, n, METERS_out);
  432. }
  433. }
  434. else {
  435. if (strncmp(p_out->proj, "ll", 2) == 0) {
  436. MULTIPLY_LOOP(x, y, n, METERS_in);
  437. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  438. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  439. }
  440. else {
  441. MULTIPLY_LOOP(x, y, n, METERS_in);
  442. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  443. DIVIDE_LOOP(x, y, n, METERS_out);
  444. }
  445. }
  446. if (!has_z)
  447. G_free(z);
  448. if (ok < 0)
  449. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  450. #endif
  451. return ok;
  452. }
  453. /*
  454. * old API, to be deleted
  455. */
  456. /**
  457. * \brief Re-project a point between two co-ordinate systems
  458. *
  459. * This function takes pointers to two pj_info structures as arguments,
  460. * and projects a point between the co-ordinate systems represented by them.
  461. * The easting and northing of the point are contained in two pointers passed
  462. * to the function; these will be overwritten by the co-ordinates of the
  463. * re-projected point.
  464. *
  465. * \param x Pointer to a double containing easting or longitude
  466. * \param y Pointer to a double containing northing or latitude
  467. * \param info_in pointer to pj_info struct for input co-ordinate system
  468. * \param info_out pointer to pj_info struct for output co-ordinate system
  469. *
  470. * \return Return value from PROJ proj_trans() function
  471. **/
  472. int pj_do_proj(double *x, double *y,
  473. const struct pj_info *info_in, const struct pj_info *info_out)
  474. {
  475. int ok;
  476. #ifdef HAVE_PROJ_H
  477. struct pj_info info_trans;
  478. PJ_COORD c;
  479. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  480. return -1;
  481. }
  482. METERS_in = info_in->meters;
  483. METERS_out = info_out->meters;
  484. if (strncmp(info_in->proj, "ll", 2) == 0) {
  485. /* convert to radians */
  486. c.lpzt.lam = (*x) / RAD_TO_DEG;
  487. c.lpzt.phi = (*y) / RAD_TO_DEG;
  488. c.lpzt.z = 0;
  489. c.lpzt.t = 0;
  490. c = proj_trans(info_trans.pj, PJ_FWD, c);
  491. ok = proj_errno(info_trans.pj);
  492. if (strncmp(info_out->proj, "ll", 2) == 0) {
  493. /* convert to degrees */
  494. *x = c.lp.lam * RAD_TO_DEG;
  495. *y = c.lp.phi * RAD_TO_DEG;
  496. }
  497. else {
  498. /* convert to map units */
  499. *x = c.xy.x / METERS_out;
  500. *y = c.xy.y / METERS_out;
  501. }
  502. }
  503. else {
  504. /* convert to meters */
  505. c.xyzt.x = *x * METERS_in;
  506. c.xyzt.y = *y * METERS_in;
  507. c.xyzt.z = 0;
  508. c.xyzt.t = 0;
  509. c = proj_trans(info_trans.pj, PJ_FWD, c);
  510. ok = proj_errno(info_trans.pj);
  511. if (strncmp(info_out->proj, "ll", 2) == 0) {
  512. /* convert to degrees */
  513. *x = c.lp.lam * RAD_TO_DEG;
  514. *y = c.lp.phi * RAD_TO_DEG;
  515. }
  516. else {
  517. /* convert to map units */
  518. *x = c.xy.x / METERS_out;
  519. *y = c.xy.y / METERS_out;
  520. }
  521. }
  522. proj_destroy(info_trans.pj);
  523. if (ok < 0) {
  524. G_warning(_("proj_trans() failed: %d"), ok);
  525. }
  526. #else
  527. double u, v;
  528. double h = 0.0;
  529. METERS_in = info_in->meters;
  530. METERS_out = info_out->meters;
  531. if (strncmp(info_in->proj, "ll", 2) == 0) {
  532. if (strncmp(info_out->proj, "ll", 2) == 0) {
  533. u = (*x) / RAD_TO_DEG;
  534. v = (*y) / RAD_TO_DEG;
  535. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  536. *x = u * RAD_TO_DEG;
  537. *y = v * RAD_TO_DEG;
  538. }
  539. else {
  540. u = (*x) / RAD_TO_DEG;
  541. v = (*y) / RAD_TO_DEG;
  542. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  543. *x = u / METERS_out;
  544. *y = v / METERS_out;
  545. }
  546. }
  547. else {
  548. if (strncmp(info_out->proj, "ll", 2) == 0) {
  549. u = *x * METERS_in;
  550. v = *y * METERS_in;
  551. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  552. *x = u * RAD_TO_DEG;
  553. *y = v * RAD_TO_DEG;
  554. }
  555. else {
  556. u = *x * METERS_in;
  557. v = *y * METERS_in;
  558. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  559. *x = u / METERS_out;
  560. *y = v / METERS_out;
  561. }
  562. }
  563. if (ok < 0) {
  564. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  565. }
  566. #endif
  567. return ok;
  568. }
  569. /**
  570. * \brief Re-project an array of points between two co-ordinate systems with
  571. * optional ellipsoidal height conversion
  572. *
  573. * This function takes pointers to two pj_info structures as arguments,
  574. * and projects an array of points between the co-ordinate systems
  575. * represented by them. Pointers to the three arrays of easting, northing,
  576. * and ellipsoidal height of the point (this one may be NULL) are passed
  577. * to the function; these will be overwritten by the co-ordinates of the
  578. * re-projected points.
  579. *
  580. * \param count Number of points in the arrays to be transformed
  581. * \param x Pointer to an array of type double containing easting or longitude
  582. * \param y Pointer to an array of type double containing northing or latitude
  583. * \param h Pointer to an array of type double containing ellipsoidal height.
  584. * May be null in which case a two-dimensional re-projection will be
  585. * done
  586. * \param info_in pointer to pj_info struct for input co-ordinate system
  587. * \param info_out pointer to pj_info struct for output co-ordinate system
  588. *
  589. * \return Return value from PROJ proj_trans() function
  590. **/
  591. int pj_do_transform(int count, double *x, double *y, double *h,
  592. const struct pj_info *info_in, const struct pj_info *info_out)
  593. {
  594. int ok;
  595. int has_h = 1;
  596. #ifdef HAVE_PROJ_H
  597. int i;
  598. struct pj_info info_trans;
  599. PJ_COORD c;
  600. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  601. return -1;
  602. }
  603. METERS_in = info_in->meters;
  604. METERS_out = info_out->meters;
  605. if (h == NULL) {
  606. h = G_malloc(sizeof *h * count);
  607. /* they say memset is only guaranteed for chars ;-( */
  608. for (i = 0; i < count; ++i)
  609. h[i] = 0.0;
  610. has_h = 0;
  611. }
  612. ok = 0;
  613. if (strncmp(info_in->proj, "ll", 2) == 0) {
  614. c.lpzt.t = 0;
  615. if (strncmp(info_out->proj, "ll", 2) == 0) {
  616. for (i = 0; i < count; i++) {
  617. /* convert to radians */
  618. c.lpzt.lam = x[i] / RAD_TO_DEG;
  619. c.lpzt.phi = y[i] / RAD_TO_DEG;
  620. c.lpzt.z = h[i];
  621. c = proj_trans(info_trans.pj, PJ_FWD, c);
  622. if ((ok = proj_errno(info_trans.pj)) < 0)
  623. break;
  624. /* convert to degrees */
  625. x[i] = c.lp.lam * RAD_TO_DEG;
  626. y[i] = c.lp.phi * RAD_TO_DEG;
  627. }
  628. }
  629. else {
  630. for (i = 0; i < count; i++) {
  631. /* convert to radians */
  632. c.lpzt.lam = x[i] / RAD_TO_DEG;
  633. c.lpzt.phi = y[i] / RAD_TO_DEG;
  634. c.lpzt.z = h[i];
  635. c = proj_trans(info_trans.pj, PJ_FWD, c);
  636. if ((ok = proj_errno(info_trans.pj)) < 0)
  637. break;
  638. /* convert to map units */
  639. x[i] = c.xy.x / METERS_out;
  640. y[i] = c.xy.y / METERS_out;
  641. }
  642. }
  643. }
  644. else {
  645. c.xyzt.t = 0;
  646. if (strncmp(info_out->proj, "ll", 2) == 0) {
  647. for (i = 0; i < count; i++) {
  648. /* convert to meters */
  649. c.xyzt.x = x[i] * METERS_in;
  650. c.xyzt.y = y[i] * METERS_in;
  651. c.xyzt.z = h[i];
  652. c = proj_trans(info_trans.pj, PJ_FWD, c);
  653. if ((ok = proj_errno(info_trans.pj)) < 0)
  654. break;
  655. /* convert to degrees */
  656. x[i] = c.lp.lam * RAD_TO_DEG;
  657. y[i] = c.lp.phi * RAD_TO_DEG;
  658. }
  659. }
  660. else {
  661. for (i = 0; i < count; i++) {
  662. /* convert to meters */
  663. c.xyzt.x = x[i] * METERS_in;
  664. c.xyzt.y = y[i] * METERS_in;
  665. c.xyzt.z = h[i];
  666. c = proj_trans(info_trans.pj, PJ_FWD, c);
  667. if ((ok = proj_errno(info_trans.pj)) < 0)
  668. break;
  669. /* convert to map units */
  670. x[i] = c.xy.x / METERS_out;
  671. y[i] = c.xy.y / METERS_out;
  672. }
  673. }
  674. }
  675. if (!has_h)
  676. G_free(h);
  677. proj_destroy(info_trans.pj);
  678. if (ok < 0) {
  679. G_warning(_("proj_trans() failed: %d"), ok);
  680. }
  681. #else
  682. METERS_in = info_in->meters;
  683. METERS_out = info_out->meters;
  684. if (h == NULL) {
  685. int i;
  686. h = G_malloc(sizeof *h * count);
  687. /* they say memset is only guaranteed for chars ;-( */
  688. for (i = 0; i < count; ++i)
  689. h[i] = 0.0;
  690. has_h = 0;
  691. }
  692. if (strncmp(info_in->proj, "ll", 2) == 0) {
  693. if (strncmp(info_out->proj, "ll", 2) == 0) {
  694. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  695. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  696. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  697. }
  698. else {
  699. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  700. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  701. DIVIDE_LOOP(x, y, count, METERS_out);
  702. }
  703. }
  704. else {
  705. if (strncmp(info_out->proj, "ll", 2) == 0) {
  706. MULTIPLY_LOOP(x, y, count, METERS_in);
  707. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  708. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  709. }
  710. else {
  711. MULTIPLY_LOOP(x, y, count, METERS_in);
  712. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  713. DIVIDE_LOOP(x, y, count, METERS_out);
  714. }
  715. }
  716. if (!has_h)
  717. G_free(h);
  718. if (ok < 0) {
  719. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  720. }
  721. #endif
  722. return ok;
  723. }