do_proj.c 21 KB

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