do_proj.c 22 KB

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