do_proj.c 23 KB

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