do_proj.c 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681
  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. #ifdef HAVE_PROJ_H
  34. #if PROJ_VERSION_MAJOR >= 6
  35. int get_pj_area(const struct pj_info *iproj, double *xmin, double *xmax,
  36. double *ymin, double *ymax)
  37. {
  38. struct Cell_head window;
  39. /* modules must set the current window, do not unset this window here */
  40. /* G_unset_window(); */
  41. G_get_set_window(&window);
  42. *xmin = window.west;
  43. *xmax = window.east;
  44. *ymin = window.south;
  45. *ymax = window.north;
  46. if (window.proj != PROJECTION_LL) {
  47. /* transform to ll equivalent */
  48. double estep, nstep;
  49. double x[85], y[85];
  50. int i;
  51. const char *projstr = NULL;
  52. char *indef = NULL;
  53. struct pj_info oproj, tproj; /* proj parameters */
  54. oproj.pj = NULL;
  55. oproj.proj[0] = '\0';
  56. tproj.def = NULL;
  57. if (proj_get_type(iproj->pj) == PJ_TYPE_BOUND_CRS) {
  58. PJ *source_crs;
  59. source_crs = proj_get_source_crs(NULL, iproj->pj);
  60. if (source_crs) {
  61. projstr = proj_as_proj_string(NULL, source_crs, PJ_PROJ_5, NULL);
  62. if (projstr) {
  63. indef = G_store(projstr);
  64. }
  65. proj_destroy(source_crs);
  66. }
  67. }
  68. else {
  69. projstr = proj_as_proj_string(NULL, iproj->pj, PJ_PROJ_5, NULL);
  70. if (projstr) {
  71. indef = G_store(projstr);
  72. }
  73. }
  74. if (indef == NULL)
  75. indef = G_store(iproj->def);
  76. G_asprintf(&tproj.def, "+proj=pipeline +step +inv %s",
  77. indef);
  78. G_debug(1, "get_pj_area() tproj.def: %s", tproj.def);
  79. tproj.pj = proj_create(PJ_DEFAULT_CTX, tproj.def);
  80. if (tproj.pj == NULL) {
  81. G_warning(_("proj_create() failed for '%s'"), tproj.def);
  82. G_free(indef);
  83. G_free(tproj.def);
  84. proj_destroy(tproj.pj);
  85. return 0;
  86. }
  87. projstr = proj_as_proj_string(NULL, tproj.pj, PJ_PROJ_5, NULL);
  88. if (projstr == NULL) {
  89. G_warning(_("proj_create() failed for '%s'"), tproj.def);
  90. G_free(indef);
  91. G_free(tproj.def);
  92. proj_destroy(tproj.pj);
  93. return 0;
  94. }
  95. else {
  96. G_debug(1, "proj_create() projstr '%s'", projstr);
  97. }
  98. G_free(indef);
  99. estep = (window.west + window.east) / 21.;
  100. nstep = (window.north + window.south) / 21.;
  101. for (i = 0; i < 20; i++) {
  102. x[i] = window.west + estep * (i + 1);
  103. y[i] = window.north;
  104. x[i + 20] = window.west + estep * (i + 1);
  105. y[i + 20] = window.south;
  106. x[i + 40] = window.west;
  107. y[i + 40] = window.south + nstep * (i + 1);
  108. x[i + 60] = window.east;
  109. y[i + 60] = window.south + nstep * (i + 1);
  110. }
  111. x[80] = window.west;
  112. y[80] = window.north;
  113. x[81] = window.west;
  114. y[81] = window.south;
  115. x[82] = window.east;
  116. y[82] = window.north;
  117. x[83] = window.east;
  118. y[83] = window.south;
  119. x[84] = (window.west + window.east) / 2.;
  120. y[84] = (window.north + window.south) / 2.;
  121. GPJ_transform_array(iproj, &oproj, &tproj, PJ_FWD, x, y, NULL, 85);
  122. proj_destroy(tproj.pj);
  123. G_free(tproj.def);
  124. *xmin = *xmax = x[84];
  125. *ymin = *ymax = y[84];
  126. for (i = 0; i < 84; i++) {
  127. if (*xmin > x[i])
  128. *xmin = x[i];
  129. if (*xmax < x[i])
  130. *xmax = x[i];
  131. if (*ymin > y[i])
  132. *ymin = y[i];
  133. if (*ymax < y[i])
  134. *ymax = y[i];
  135. }
  136. G_debug(1, "input window north: %.8f", window.north);
  137. G_debug(1, "input window south: %.8f", window.south);
  138. G_debug(1, "input window east: %.8f", window.east);
  139. G_debug(1, "input window west: %.8f", window.west);
  140. G_debug(1, "transformed xmin: %.8f", *xmin);
  141. G_debug(1, "transformed xmax: %.8f", *xmax);
  142. G_debug(1, "transformed ymin: %.8f", *ymin);
  143. G_debug(1, "transformed ymax: %.8f", *ymax);
  144. }
  145. G_debug(1, "get_pj_area(): xmin %g, xmax %g, ymin %g, ymax %g",
  146. *xmin, *xmax, *ymin, *ymax);
  147. return 1;
  148. }
  149. char *get_pj_type_string(PJ *pj)
  150. {
  151. char *pj_type = NULL;
  152. switch (proj_get_type(pj)) {
  153. case PJ_TYPE_UNKNOWN:
  154. G_asprintf(&pj_type, "unknown");
  155. break;
  156. case PJ_TYPE_ELLIPSOID:
  157. G_asprintf(&pj_type, "ellipsoid");
  158. break;
  159. case PJ_TYPE_PRIME_MERIDIAN:
  160. G_asprintf(&pj_type, "prime meridian");
  161. break;
  162. case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
  163. G_asprintf(&pj_type, "geodetic reference frame");
  164. break;
  165. case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
  166. G_asprintf(&pj_type, "dynamic geodetic reference frame");
  167. break;
  168. case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
  169. G_asprintf(&pj_type, "vertical reference frame");
  170. break;
  171. case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
  172. G_asprintf(&pj_type, "dynamic vertical reference frame");
  173. break;
  174. case PJ_TYPE_DATUM_ENSEMBLE:
  175. G_asprintf(&pj_type, "datum ensemble");
  176. break;
  177. /** Abstract type, not returned by proj_get_type() */
  178. case PJ_TYPE_CRS:
  179. G_asprintf(&pj_type, "crs");
  180. break;
  181. case PJ_TYPE_GEODETIC_CRS:
  182. G_asprintf(&pj_type, "geodetic crs");
  183. break;
  184. case PJ_TYPE_GEOCENTRIC_CRS:
  185. G_asprintf(&pj_type, "geocentric crs");
  186. break;
  187. /** proj_get_type() will never return that type, but
  188. * PJ_TYPE_GEOGRAPHIC_2D_CRS or PJ_TYPE_GEOGRAPHIC_3D_CRS. */
  189. case PJ_TYPE_GEOGRAPHIC_CRS:
  190. G_asprintf(&pj_type, "geographic crs");
  191. break;
  192. case PJ_TYPE_GEOGRAPHIC_2D_CRS:
  193. G_asprintf(&pj_type, "geographic 2D crs");
  194. break;
  195. case PJ_TYPE_GEOGRAPHIC_3D_CRS:
  196. G_asprintf(&pj_type, "geographic 3D crs");
  197. break;
  198. case PJ_TYPE_VERTICAL_CRS:
  199. G_asprintf(&pj_type, "vertical crs");
  200. break;
  201. case PJ_TYPE_PROJECTED_CRS:
  202. G_asprintf(&pj_type, "projected crs");
  203. break;
  204. case PJ_TYPE_COMPOUND_CRS:
  205. G_asprintf(&pj_type, "compound crs");
  206. break;
  207. case PJ_TYPE_TEMPORAL_CRS:
  208. G_asprintf(&pj_type, "temporal crs");
  209. break;
  210. case PJ_TYPE_ENGINEERING_CRS:
  211. G_asprintf(&pj_type, "engineering crs");
  212. break;
  213. case PJ_TYPE_BOUND_CRS:
  214. G_asprintf(&pj_type, "bound crs");
  215. break;
  216. case PJ_TYPE_OTHER_CRS:
  217. G_asprintf(&pj_type, "other crs");
  218. break;
  219. case PJ_TYPE_CONVERSION:
  220. G_asprintf(&pj_type, "conversion");
  221. break;
  222. case PJ_TYPE_TRANSFORMATION:
  223. G_asprintf(&pj_type, "transformation");
  224. break;
  225. case PJ_TYPE_CONCATENATED_OPERATION:
  226. G_asprintf(&pj_type, "concatenated operation");
  227. break;
  228. case PJ_TYPE_OTHER_COORDINATE_OPERATION:
  229. G_asprintf(&pj_type, "other coordinate operation");
  230. break;
  231. default:
  232. G_asprintf(&pj_type, "unknown");
  233. break;
  234. }
  235. return pj_type;
  236. }
  237. PJ *get_pj_object(const struct pj_info *in_gpj, char **in_defstr)
  238. {
  239. PJ *in_pj = NULL;
  240. *in_defstr = NULL;
  241. /* 1. SRID, 2. WKT, 3. standard pj from pj_get_kv */
  242. if (in_gpj->srid) {
  243. G_debug(1, "Trying SRID '%s' ...", in_gpj->srid);
  244. in_pj = proj_create(PJ_DEFAULT_CTX, in_gpj->srid);
  245. if (!in_pj) {
  246. G_warning(_("Unrecognized SRID '%s'"), in_gpj->srid);
  247. }
  248. else {
  249. *in_defstr = G_store(in_gpj->srid);
  250. /* PROJ will do the unit conversion if set up from srid
  251. * -> disable unit conversion for GPJ_transform */
  252. /* ugly hack */
  253. ((struct pj_info *)in_gpj)->meters = 1;
  254. }
  255. }
  256. if (!in_pj && in_gpj->wkt) {
  257. G_debug(1, "Trying WKT '%s' ...", in_gpj->wkt);
  258. in_pj = proj_create(PJ_DEFAULT_CTX, in_gpj->wkt);
  259. if (!in_pj) {
  260. G_warning(_("Unrecognized WKT '%s'"), in_gpj->wkt);
  261. }
  262. else {
  263. *in_defstr = G_store(in_gpj->wkt);
  264. /* PROJ will do the unit conversion if set up from wkt
  265. * -> disable unit conversion for GPJ_transform */
  266. /* ugly hack */
  267. ((struct pj_info *)in_gpj)->meters = 1;
  268. }
  269. }
  270. if (!in_pj && in_gpj->pj) {
  271. in_pj = proj_clone(PJ_DEFAULT_CTX, in_gpj->pj);
  272. *in_defstr = G_store(proj_as_wkt(NULL, in_pj, PJ_WKT2_LATEST, NULL));
  273. if (*in_defstr && !**in_defstr)
  274. *in_defstr = NULL;
  275. }
  276. if (!in_pj) {
  277. G_warning(_("Unable to create PROJ object"));
  278. return NULL;
  279. }
  280. /* Even Rouault:
  281. * if info_in->def contains a +towgs84/+nadgrids clause,
  282. * this pipeline would apply it, whereas you probably only want
  283. * the reverse projection, and no datum shift.
  284. * The easiest would probably to mess up with the PROJ string.
  285. * Otherwise with the PROJ API, you could
  286. * instanciate a PJ object from the string,
  287. * check if it is a BoundCRS with proj_get_source_crs(),
  288. * and in that case, take the source CRS with proj_get_source_crs(),
  289. * and do the inverse transform on it */
  290. if (proj_get_type(in_pj) == PJ_TYPE_BOUND_CRS) {
  291. PJ *source_crs;
  292. G_debug(1, "found bound crs");
  293. source_crs = proj_get_source_crs(NULL, in_pj);
  294. if (source_crs) {
  295. *in_defstr = G_store(proj_as_wkt(NULL, source_crs, PJ_WKT2_LATEST, NULL));
  296. if (*in_defstr && !**in_defstr)
  297. *in_defstr = NULL;
  298. in_pj = source_crs;
  299. }
  300. }
  301. return in_pj;
  302. }
  303. #endif
  304. #endif
  305. /**
  306. * \brief Create a PROJ transformation object to transform coordinates
  307. * from an input SRS to an output SRS
  308. *
  309. * After the transformation has been initialized with this function,
  310. * coordinates can be transformed from input SRS to output SRS with
  311. * GPJ_transform() and direction = PJ_FWD, and back from output SRS to
  312. * input SRS with direction = OJ_INV.
  313. * If coordinates should be transformed between the input SRS and its
  314. * latlong equivalent, an uninitialized info_out with
  315. * info_out->pj = NULL can be passed to the function. In this case,
  316. * coordinates will be transformed between the input SRS and its
  317. * latlong equivalent, and for PROJ 5+, the transformation object is
  318. * created accordingly, while for PROJ 4, the output SRS is created as
  319. * latlong equivalent of the input SRS
  320. *
  321. * PROJ 5+:
  322. * info_in->pj must not be null
  323. * if info_out->pj is null, assume info_out to be the ll equivalent
  324. * of info_in
  325. * create info_trans as conversion from info_in to its ll equivalent
  326. * NOTE: this is the inverse of the logic of PROJ 5 which by default
  327. * converts from ll to a given SRS, not from a given SRS to ll
  328. * thus PROJ 5+ itself uses an inverse transformation in the
  329. * first step of the pipeline for proj_create_crs_to_crs()
  330. * if info_trans->def is not NULL, this pipeline definition will be
  331. * used to create a transformation object
  332. * PROJ 4:
  333. * info_in->pj must not be null
  334. * if info_out->pj is null, create info_out as ll equivalent
  335. * else do nothing, info_trans is not used
  336. *
  337. * \param info_in pointer to pj_info struct for input co-ordinate system
  338. * \param info_out pointer to pj_info struct for output co-ordinate system
  339. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  340. *
  341. * \return 1 on success, -1 on failure
  342. **/
  343. int GPJ_init_transform(const struct pj_info *info_in,
  344. const struct pj_info *info_out,
  345. struct pj_info *info_trans)
  346. {
  347. if (info_in->pj == NULL)
  348. G_fatal_error(_("Input coordinate system is NULL"));
  349. if (info_in->def == NULL)
  350. G_fatal_error(_("Input coordinate system definition is NULL"));
  351. #ifdef HAVE_PROJ_H
  352. #if PROJ_VERSION_MAJOR >= 6
  353. /* PROJ6+: enforce axis order easting, northing
  354. * +axis=enu (works with proj-4.8+) */
  355. info_trans->pj = NULL;
  356. info_trans->meters = 1.;
  357. info_trans->zone = 0;
  358. sprintf(info_trans->proj, "pipeline");
  359. /* user-provided pipeline */
  360. if (info_trans->def) {
  361. const char *projstr;
  362. /* info_in->pj, info_in->proj, info_out->pj, info_out->proj
  363. * must be set */
  364. if (!info_in->pj || !info_in->proj[0] ||
  365. !info_out->pj ||info_out->proj[0]) {
  366. G_warning(_("A custom pipeline requires input and output projection info"));
  367. return -1;
  368. }
  369. /* create a pj from user-defined transformation pipeline */
  370. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  371. if (info_trans->pj == NULL) {
  372. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  373. return -1;
  374. }
  375. projstr = proj_as_proj_string(NULL, info_trans->pj, PJ_PROJ_5, NULL);
  376. if (projstr == NULL) {
  377. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  378. return -1;
  379. }
  380. else {
  381. /* make sure axis order is easting, northing
  382. * proj_normalize_for_visualization() does not work here
  383. * because source and target CRS are unknown to PROJ
  384. * remove any "+step +proj=axisswap +order=2,1" ?
  385. * */
  386. info_trans->def = G_store(projstr);
  387. if (strstr(info_trans->def, "axisswap")) {
  388. G_warning(_("The transformation pipeline contains an '%s' step. "
  389. "Remove this step if easting and northing are swapped in the output."),
  390. "axisswap");
  391. }
  392. G_debug(1, "proj_create() pipeline: %s", info_trans->def);
  393. /* the user-provided PROJ pipeline is supposed to do
  394. * all the needed unit conversions */
  395. /* ugly hack */
  396. ((struct pj_info *)info_in)->meters = 1;
  397. ((struct pj_info *)info_out)->meters = 1;
  398. }
  399. }
  400. /* if no output CRS is defined,
  401. * assume info_out to be ll equivalent of info_in */
  402. else if (info_out->pj == NULL) {
  403. const char *projstr = NULL;
  404. char *indef = NULL;
  405. /* get PROJ-style definition */
  406. indef = G_store(info_in->def);
  407. G_debug(1, "ll equivalent definition: %s", indef);
  408. /* what about axis order?
  409. * is it always enu?
  410. * probably yes, as long as there is no +proj=axisswap step */
  411. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s",
  412. indef);
  413. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  414. if (info_trans->pj == NULL) {
  415. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  416. G_free(indef);
  417. return -1;
  418. }
  419. projstr = proj_as_proj_string(NULL, info_trans->pj, PJ_PROJ_5, NULL);
  420. if (projstr == NULL) {
  421. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  422. G_free(indef);
  423. return -1;
  424. }
  425. G_free(indef);
  426. }
  427. /* input and output CRS are available */
  428. else if (info_in->def && info_out->pj && info_out->def) {
  429. char *indef = NULL, *outdef = NULL;
  430. char *insrid = NULL, *outsrid = NULL;
  431. PJ *in_pj, *out_pj;
  432. PJ_OBJ_LIST *op_list;
  433. PJ_OPERATION_FACTORY_CONTEXT *operation_ctx;
  434. PJ_AREA *pj_area = NULL;
  435. double xmin, xmax, ymin, ymax;
  436. int op_count = 0, op_count_area = 0;
  437. /* get pj_area */
  438. /* do it here because get_pj_area() will use
  439. * the PROJ definition for simple transformation to the
  440. * ll equivalent and we need to do unit conversion */
  441. if (get_pj_area(info_in, &xmin, &xmax, &ymin, &ymax)) {
  442. pj_area = proj_area_create();
  443. proj_area_set_bbox(pj_area, xmin, ymin, xmax, ymax);
  444. }
  445. G_debug(1, "source proj string: %s", info_in->def);
  446. G_debug(1, "source type: %s", get_pj_type_string(info_in->pj));
  447. /* PROJ6+: EPSG must be uppercase EPSG */
  448. if (info_in->srid) {
  449. if (strncmp(info_in->srid, "epsg", 4) == 0) {
  450. insrid = G_store_upper(info_in->srid);
  451. G_free(info_in->srid);
  452. ((struct pj_info *)info_in)->srid = insrid;
  453. insrid = NULL;
  454. }
  455. }
  456. in_pj = get_pj_object(info_in, &indef);
  457. if (in_pj == NULL || indef == NULL) {
  458. G_warning(_("Input CRS not available for '%s'"), info_in->def);
  459. return -1;
  460. }
  461. G_debug(1, "Input CRS definition: %s", indef);
  462. G_debug(1, "target proj string: %s", info_out->def);
  463. G_debug(1, "target type: %s", get_pj_type_string(info_out->pj));
  464. /* PROJ6+: EPSG must be uppercase EPSG */
  465. if (info_out->srid) {
  466. if (strncmp(info_out->srid, "epsg", 4) == 0) {
  467. outsrid = G_store_upper(info_out->srid);
  468. G_free(info_out->srid);
  469. ((struct pj_info *)info_out)->srid = outsrid;
  470. outsrid = NULL;
  471. }
  472. }
  473. out_pj = get_pj_object(info_out, &outdef);
  474. if (out_pj == NULL || outdef == NULL) {
  475. G_warning(_("Output CRS not available for '%s'"), info_out->def);
  476. return -1;
  477. }
  478. G_debug(1, "Output CRS definition: %s", outdef);
  479. /* check number of operations */
  480. operation_ctx = proj_create_operation_factory_context(PJ_DEFAULT_CTX, NULL);
  481. /* proj_create_operations() works only if both source_crs
  482. * and target_crs are found in the proj db
  483. * if any is not found, proj can not get a list of operations
  484. * and we have to take care of datumshift manually */
  485. /* list all operations irrespecitve of area and
  486. * grid availability */
  487. op_list = proj_create_operations(PJ_DEFAULT_CTX,
  488. in_pj,
  489. out_pj,
  490. operation_ctx);
  491. proj_operation_factory_context_destroy(operation_ctx);
  492. op_count = 0;
  493. if (op_list)
  494. op_count = proj_list_get_count(op_list);
  495. if (op_count > 1) {
  496. int i;
  497. G_important_message(_("Found %d possible transformations"), op_count);
  498. for (i = 0; i < op_count; i++) {
  499. const char *area_of_use, *projstr;
  500. double e, w, s, n;
  501. PJ_PROJ_INFO pj_info;
  502. PJ *op, *op_norm;
  503. op = proj_list_get(PJ_DEFAULT_CTX, op_list, i);
  504. op_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, op);
  505. if (!op_norm) {
  506. G_warning(_("proj_normalize_for_visualization() failed for operation %d"),
  507. i + 1);
  508. }
  509. else {
  510. proj_destroy(op);
  511. op = op_norm;
  512. }
  513. pj_info = proj_pj_info(op);
  514. proj_get_area_of_use(NULL, op, &w, &s, &e, &n, &area_of_use);
  515. G_important_message("************************");
  516. G_important_message(_("Operation %d:"), i + 1);
  517. if (pj_info.description) {
  518. G_important_message(_("Description: %s"),
  519. pj_info.description);
  520. }
  521. if (area_of_use) {
  522. G_important_message(" ");
  523. G_important_message(_("Area of use: %s"),
  524. area_of_use);
  525. }
  526. if (pj_info.accuracy > 0) {
  527. G_important_message(" ");
  528. G_important_message(_("Accuracy within area of use: %g m"),
  529. pj_info.accuracy);
  530. }
  531. #if PROJ_VERSION_NUM >= 6020000
  532. const char *str = proj_get_remarks(op);
  533. if (str && *str) {
  534. G_important_message(" ");
  535. G_important_message(_("Remarks: %s"), str);
  536. }
  537. str = proj_get_scope(op);
  538. if (str && *str) {
  539. G_important_message(" ");
  540. G_important_message(_("Scope: %s"), str);
  541. }
  542. #endif
  543. projstr = proj_as_proj_string(NULL, op,
  544. PJ_PROJ_5, NULL);
  545. if (projstr) {
  546. G_important_message(" ");
  547. G_important_message(_("PROJ string:"));
  548. G_important_message("%s", projstr);
  549. }
  550. proj_destroy(op);
  551. }
  552. G_important_message("************************");
  553. G_important_message(_("See also output of:"));
  554. G_important_message("projinfo -o PROJ -s \"%s\" -t \"%s\"",
  555. indef, outdef);
  556. G_important_message(_("Please provide the appropriate PROJ string with the %s option"),
  557. "pipeline");
  558. G_important_message("************************");
  559. }
  560. if (op_list)
  561. proj_list_destroy(op_list);
  562. /* follwing code copied from proj_create_crs_to_crs_from_pj()
  563. * in proj src/4D_api.cpp
  564. * but using PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT */
  565. /* now use the current region as area of interest */
  566. operation_ctx = proj_create_operation_factory_context(PJ_DEFAULT_CTX, NULL);
  567. proj_operation_factory_context_set_area_of_interest(PJ_DEFAULT_CTX,
  568. operation_ctx,
  569. xmin, ymin,
  570. xmax, ymax);
  571. proj_operation_factory_context_set_spatial_criterion(PJ_DEFAULT_CTX,
  572. operation_ctx,
  573. PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT);
  574. proj_operation_factory_context_set_grid_availability_use(PJ_DEFAULT_CTX,
  575. operation_ctx,
  576. PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
  577. /* The operations are sorted with the most relevant ones first:
  578. * by descending area (intersection of the transformation area
  579. * with the area of interest, or intersection of the
  580. * transformation with the area of use of the CRS),
  581. * and by increasing accuracy.
  582. * Operations with unknown accuracy are sorted last,
  583. * whatever their area.
  584. */
  585. op_list = proj_create_operations(PJ_DEFAULT_CTX,
  586. in_pj,
  587. out_pj,
  588. operation_ctx);
  589. proj_operation_factory_context_destroy(operation_ctx);
  590. op_count_area = 0;
  591. if (op_list)
  592. op_count_area = proj_list_get_count(op_list);
  593. if (op_count_area == 0) {
  594. /* no operations */
  595. info_trans->pj = NULL;
  596. }
  597. else if (op_count_area == 1) {
  598. info_trans->pj = proj_list_get(PJ_DEFAULT_CTX, op_list, 0);
  599. }
  600. else { /* op_count_area > 1 */
  601. /* can't use pj_create_prepared_operations()
  602. * this is a PROJ-internal function
  603. * trust the sorting of PROJ and use the first one */
  604. info_trans->pj = proj_list_get(PJ_DEFAULT_CTX, op_list, 0);
  605. }
  606. if (op_list)
  607. proj_list_destroy(op_list);
  608. /* try proj_create_crs_to_crs() */
  609. G_debug(1, "trying %s to %s", indef, outdef);
  610. /* proj_create_crs_to_crs() does not work because it calls
  611. * proj_create_crs_to_crs_from_pj() which calls
  612. * proj_operation_factory_context_set_spatial_criterion()
  613. * with PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION
  614. * instead of
  615. * PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT
  616. *
  617. * fixed in PROJ master, probably available with PROJ 7.3.x */
  618. /*
  619. info_trans->pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
  620. indef,
  621. outdef,
  622. pj_area);
  623. */
  624. if (in_pj)
  625. proj_destroy(in_pj);
  626. if (out_pj)
  627. proj_destroy(out_pj);
  628. if (info_trans->pj) {
  629. const char *projstr;
  630. PJ *pj_norm = NULL;
  631. G_debug(1, "proj_create_crs_to_crs() succeeded with PROJ%d",
  632. PROJ_VERSION_MAJOR);
  633. projstr = proj_as_proj_string(NULL, info_trans->pj,
  634. PJ_PROJ_5, NULL);
  635. info_trans->def = G_store(projstr);
  636. if (projstr) {
  637. /* make sure axis order is easting, northing
  638. * proj_normalize_for_visualization() requires
  639. * source and target CRS
  640. * -> does not work with ll equivalent of input:
  641. * no target CRS in +proj=pipeline +step +inv %s */
  642. pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, info_trans->pj);
  643. if (!pj_norm) {
  644. G_warning(_("proj_normalize_for_visualization() failed for '%s'"),
  645. info_trans->def);
  646. }
  647. else {
  648. projstr = proj_as_proj_string(NULL, pj_norm,
  649. PJ_PROJ_5, NULL);
  650. if (projstr && *projstr) {
  651. proj_destroy(info_trans->pj);
  652. info_trans->pj = pj_norm;
  653. info_trans->def = G_store(projstr);
  654. }
  655. else {
  656. proj_destroy(pj_norm);
  657. G_warning(_("No PROJ definition for normalized version of '%s'"),
  658. info_trans->def);
  659. }
  660. }
  661. G_important_message(_("Selected PROJ pipeline:"));
  662. G_important_message(_("%s"), info_trans->def);
  663. G_important_message("************************");
  664. }
  665. else {
  666. proj_destroy(info_trans->pj);
  667. info_trans->pj = NULL;
  668. }
  669. }
  670. if (pj_area)
  671. proj_area_destroy(pj_area);
  672. if (insrid)
  673. G_free(insrid);
  674. if (outsrid)
  675. G_free(outsrid);
  676. G_free(indef);
  677. G_free(outdef);
  678. }
  679. if (info_trans->pj == NULL) {
  680. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  681. return -1;
  682. }
  683. #else /* PROJ 5 */
  684. info_trans->pj = NULL;
  685. info_trans->meters = 1.;
  686. info_trans->zone = 0;
  687. sprintf(info_trans->proj, "pipeline");
  688. /* user-provided pipeline */
  689. if (info_trans->def) {
  690. /* create a pj from user-defined transformation pipeline */
  691. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  692. if (info_trans->pj == NULL) {
  693. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  694. return -1;
  695. }
  696. }
  697. /* if no output CRS is defined,
  698. * assume info_out to be ll equivalent of info_in */
  699. else if (info_out->pj == NULL) {
  700. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s",
  701. info_in->def);
  702. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  703. if (info_trans->pj == NULL) {
  704. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  705. return -1;
  706. }
  707. }
  708. else if (info_in->def && info_out->pj && info_out->def) {
  709. char *indef = NULL, *outdef = NULL;
  710. char *insrid = NULL, *outsrid = NULL;
  711. /* PROJ5: EPSG must be lowercase epsg */
  712. if (info_in->srid) {
  713. if (strncmp(info_in->srid, "EPSG", 4) == 0)
  714. insrid = G_store_lower(info_in->srid);
  715. else
  716. insrid = G_store(info_in->srid);
  717. }
  718. if (info_out->pj && info_out->srid) {
  719. if (strncmp(info_out->srid, "EPSG", 4) == 0)
  720. outsrid = G_store_lower(info_out->srid);
  721. else
  722. outsrid = G_store(info_out->srid);
  723. }
  724. info_trans->pj = NULL;
  725. if (insrid && outsrid) {
  726. G_asprintf(&indef, "%s", insrid);
  727. G_asprintf(&outdef, "%s", outsrid);
  728. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv +init=%s +step +init=%s",
  729. indef, outdef);
  730. /* try proj_create_crs_to_crs() */
  731. info_trans->pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
  732. indef,
  733. outdef,
  734. NULL);
  735. }
  736. if (info_trans->pj) {
  737. G_debug(1, "proj_create_crs_to_crs() succeeded with PROJ5");
  738. }
  739. else {
  740. if (indef) {
  741. G_free(indef);
  742. indef = NULL;
  743. }
  744. if (insrid) {
  745. G_asprintf(&indef, "+init=%s", insrid);
  746. }
  747. else {
  748. G_asprintf(&indef, "%s", info_in->def);
  749. }
  750. if (outdef) {
  751. G_free(outdef);
  752. outdef = NULL;
  753. }
  754. if (outsrid) {
  755. G_asprintf(&outdef, "+init=%s", outsrid);
  756. }
  757. else {
  758. G_asprintf(&outdef, "%s", info_out->def);
  759. }
  760. /* try proj_create() with +proj=pipeline +step +inv %s +step %s" */
  761. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s +step %s",
  762. indef, outdef);
  763. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  764. }
  765. if (insrid)
  766. G_free(insrid);
  767. if (outsrid)
  768. G_free(outsrid);
  769. G_free(indef);
  770. G_free(outdef);
  771. }
  772. if (info_trans->pj == NULL) {
  773. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  774. return -1;
  775. }
  776. #endif
  777. #else /* PROJ 4 */
  778. if (info_out->pj == NULL) {
  779. if (GPJ_get_equivalent_latlong(info_out, info_in) < 0) {
  780. G_warning(_("Unable to create latlong equivalent for '%s'"),
  781. info_in->def);
  782. return -1;
  783. }
  784. }
  785. #endif
  786. return 1;
  787. }
  788. /* TODO: rename pj_ to GPJ_ to avoid symbol clash with PROJ lib */
  789. /**
  790. * \brief Re-project a point between two co-ordinate systems using a
  791. * transformation object prepared with GPJ_prepare_pj()
  792. *
  793. * This function takes pointers to three pj_info structures as arguments,
  794. * and projects a point between the input and output co-ordinate system.
  795. * The pj_info structure info_trans must have been initialized with
  796. * GPJ_init_transform().
  797. * The direction determines if a point is projected from input CRS to
  798. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  799. * The easting, northing, and height of the point are contained in the
  800. * pointers passed to the function; these will be overwritten by the
  801. * coordinates of the transformed point.
  802. *
  803. * \param info_in pointer to pj_info struct for input co-ordinate system
  804. * \param info_out pointer to pj_info struct for output co-ordinate system
  805. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  806. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  807. * \param x Pointer to a double containing easting or longitude
  808. * \param y Pointer to a double containing northing or latitude
  809. * \param z Pointer to a double containing height, or NULL
  810. *
  811. * \return Return value from PROJ proj_trans() function
  812. **/
  813. int GPJ_transform(const struct pj_info *info_in,
  814. const struct pj_info *info_out,
  815. const struct pj_info *info_trans, int dir,
  816. double *x, double *y, double *z)
  817. {
  818. int ok = 0;
  819. #ifdef HAVE_PROJ_H
  820. /* PROJ 5+ variant */
  821. int in_is_ll, out_is_ll, in_deg2rad, out_rad2deg;
  822. PJ_COORD c;
  823. if (info_in->pj == NULL)
  824. G_fatal_error(_("No input projection"));
  825. if (info_trans->pj == NULL)
  826. G_fatal_error(_("No transformation object"));
  827. in_deg2rad = out_rad2deg = 1;
  828. if (dir == PJ_FWD) {
  829. /* info_in -> info_out */
  830. METERS_in = info_in->meters;
  831. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  832. #if PROJ_VERSION_MAJOR >= 6
  833. /* PROJ 6+: conversion to radians is not always needed:
  834. * if proj_angular_input(info_trans->pj, dir) == 1
  835. * -> convert from degrees to radians */
  836. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  837. in_deg2rad = 0;
  838. }
  839. #endif
  840. if (info_out->pj) {
  841. METERS_out = info_out->meters;
  842. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  843. #if PROJ_VERSION_MAJOR >= 6
  844. /* PROJ 6+: conversion to radians is not always needed:
  845. * if proj_angular_input(info_trans->pj, dir) == 1
  846. * -> convert from degrees to radians */
  847. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  848. out_rad2deg = 0;
  849. }
  850. #endif
  851. }
  852. else {
  853. METERS_out = 1.0;
  854. out_is_ll = 1;
  855. }
  856. }
  857. else {
  858. /* info_out -> info_in */
  859. METERS_out = info_in->meters;
  860. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  861. #if PROJ_VERSION_MAJOR >= 6
  862. /* PROJ 6+: conversion to radians is not always needed:
  863. * if proj_angular_input(info_trans->pj, dir) == 1
  864. * -> convert from degrees to radians */
  865. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  866. out_rad2deg = 0;
  867. }
  868. #endif
  869. if (info_out->pj) {
  870. METERS_in = info_out->meters;
  871. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  872. #if PROJ_VERSION_MAJOR >= 6
  873. /* PROJ 6+: conversion to radians is not always needed:
  874. * if proj_angular_input(info_trans->pj, dir) == 1
  875. * -> convert from degrees to radians */
  876. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  877. in_deg2rad = 0;
  878. }
  879. #endif
  880. }
  881. else {
  882. METERS_in = 1.0;
  883. in_is_ll = 1;
  884. }
  885. }
  886. /* prepare */
  887. if (in_is_ll) {
  888. if (in_deg2rad) {
  889. /* convert degrees to radians */
  890. c.lpzt.lam = (*x) / RAD_TO_DEG;
  891. c.lpzt.phi = (*y) / RAD_TO_DEG;
  892. }
  893. else {
  894. c.lpzt.lam = (*x);
  895. c.lpzt.phi = (*y);
  896. }
  897. c.lpzt.z = 0;
  898. if (z)
  899. c.lpzt.z = *z;
  900. c.lpzt.t = 0;
  901. }
  902. else {
  903. /* convert to meters */
  904. c.xyzt.x = *x * METERS_in;
  905. c.xyzt.y = *y * METERS_in;
  906. c.xyzt.z = 0;
  907. if (z)
  908. c.xyzt.z = *z;
  909. c.xyzt.t = 0;
  910. }
  911. G_debug(1, "c.xyzt.x: %g", c.xyzt.x);
  912. G_debug(1, "c.xyzt.y: %g", c.xyzt.y);
  913. G_debug(1, "c.xyzt.z: %g", c.xyzt.z);
  914. /* transform */
  915. c = proj_trans(info_trans->pj, dir, c);
  916. ok = proj_errno(info_trans->pj);
  917. if (ok < 0) {
  918. G_warning(_("proj_trans() failed: %s"), proj_errno_string(ok));
  919. return ok;
  920. }
  921. /* output */
  922. if (out_is_ll) {
  923. /* convert to degrees */
  924. if (out_rad2deg) {
  925. /* convert radians to degrees */
  926. *x = c.lp.lam * RAD_TO_DEG;
  927. *y = c.lp.phi * RAD_TO_DEG;
  928. }
  929. else {
  930. *x = c.lp.lam;
  931. *y = c.lp.phi;
  932. }
  933. if (z)
  934. *z = c.lpzt.z;
  935. }
  936. else {
  937. /* convert to map units */
  938. *x = c.xyzt.x / METERS_out;
  939. *y = c.xyzt.y / METERS_out;
  940. if (z)
  941. *z = c.xyzt.z;
  942. }
  943. #else
  944. /* PROJ 4 variant */
  945. double u, v;
  946. double h = 0.0;
  947. const struct pj_info *p_in, *p_out;
  948. if (info_out == NULL)
  949. G_fatal_error(_("No output projection"));
  950. if (dir == PJ_FWD) {
  951. p_in = info_in;
  952. p_out = info_out;
  953. }
  954. else {
  955. p_in = info_out;
  956. p_out = info_in;
  957. }
  958. METERS_in = p_in->meters;
  959. METERS_out = p_out->meters;
  960. if (z)
  961. h = *z;
  962. if (strncmp(p_in->proj, "ll", 2) == 0) {
  963. u = (*x) / RAD_TO_DEG;
  964. v = (*y) / RAD_TO_DEG;
  965. }
  966. else {
  967. u = *x * METERS_in;
  968. v = *y * METERS_in;
  969. }
  970. ok = pj_transform(p_in->pj, p_out->pj, 1, 0, &u, &v, &h);
  971. if (ok < 0) {
  972. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  973. return ok;
  974. }
  975. if (strncmp(p_out->proj, "ll", 2) == 0) {
  976. *x = u * RAD_TO_DEG;
  977. *y = v * RAD_TO_DEG;
  978. }
  979. else {
  980. *x = u / METERS_out;
  981. *y = v / METERS_out;
  982. }
  983. if (z)
  984. *z = h;
  985. #endif
  986. return ok;
  987. }
  988. /**
  989. * \brief Re-project an array of points between two co-ordinate systems
  990. * using a transformation object prepared with GPJ_prepare_pj()
  991. *
  992. * This function takes pointers to three pj_info structures as arguments,
  993. * and projects an array of pointd between the input and output
  994. * co-ordinate system. The pj_info structure info_trans must have been
  995. * initialized with GPJ_init_transform().
  996. * The direction determines if a point is projected from input CRS to
  997. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  998. * The easting, northing, and height of the point are contained in the
  999. * pointers passed to the function; these will be overwritten by the
  1000. * coordinates of the transformed point.
  1001. *
  1002. * \param info_in pointer to pj_info struct for input co-ordinate system
  1003. * \param info_out pointer to pj_info struct for output co-ordinate system
  1004. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  1005. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  1006. * \param x pointer to an array of type double containing easting or longitude
  1007. * \param y pointer to an array of type double containing northing or latitude
  1008. * \param z pointer to an array of type double containing height, or NULL
  1009. * \param n number of points in the arrays to be transformed
  1010. *
  1011. * \return Return value from PROJ proj_trans() function
  1012. **/
  1013. int GPJ_transform_array(const struct pj_info *info_in,
  1014. const struct pj_info *info_out,
  1015. const struct pj_info *info_trans, int dir,
  1016. double *x, double *y, double *z, int n)
  1017. {
  1018. int ok;
  1019. int i;
  1020. int has_z = 1;
  1021. #ifdef HAVE_PROJ_H
  1022. /* PROJ 5+ variant */
  1023. int in_is_ll, out_is_ll, in_deg2rad, out_rad2deg;
  1024. PJ_COORD c;
  1025. if (info_trans->pj == NULL)
  1026. G_fatal_error(_("No transformation object"));
  1027. in_deg2rad = out_rad2deg = 1;
  1028. if (dir == PJ_FWD) {
  1029. /* info_in -> info_out */
  1030. METERS_in = info_in->meters;
  1031. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  1032. #if PROJ_VERSION_MAJOR >= 6
  1033. /* PROJ 6+: conversion to radians is not always needed:
  1034. * if proj_angular_input(info_trans->pj, dir) == 1
  1035. * -> convert from degrees to radians */
  1036. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  1037. in_deg2rad = 0;
  1038. }
  1039. #endif
  1040. if (info_out->pj) {
  1041. METERS_out = info_out->meters;
  1042. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  1043. #if PROJ_VERSION_MAJOR >= 6
  1044. /* PROJ 6+: conversion to radians is not always needed:
  1045. * if proj_angular_input(info_trans->pj, dir) == 1
  1046. * -> convert from degrees to radians */
  1047. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  1048. out_rad2deg = 0;
  1049. }
  1050. #endif
  1051. }
  1052. else {
  1053. METERS_out = 1.0;
  1054. out_is_ll = 1;
  1055. }
  1056. }
  1057. else {
  1058. /* info_out -> info_in */
  1059. METERS_out = info_in->meters;
  1060. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  1061. #if PROJ_VERSION_MAJOR >= 6
  1062. /* PROJ 6+: conversion to radians is not always needed:
  1063. * if proj_angular_input(info_trans->pj, dir) == 1
  1064. * -> convert from degrees to radians */
  1065. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  1066. out_rad2deg = 0;
  1067. }
  1068. #endif
  1069. if (info_out->pj) {
  1070. METERS_in = info_out->meters;
  1071. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  1072. #if PROJ_VERSION_MAJOR >= 6
  1073. /* PROJ 6+: conversion to degrees is not always needed:
  1074. * if proj_angular_output(info_trans->pj, dir) == 1
  1075. * -> convert from degrees to radians */
  1076. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  1077. in_deg2rad = 0;
  1078. }
  1079. #endif
  1080. }
  1081. else {
  1082. METERS_in = 1.0;
  1083. in_is_ll = 1;
  1084. }
  1085. }
  1086. if (z == NULL) {
  1087. z = G_malloc(sizeof(double) * n);
  1088. /* they say memset is only guaranteed for chars ;-( */
  1089. for (i = 0; i < n; i++)
  1090. z[i] = 0.0;
  1091. has_z = 0;
  1092. }
  1093. ok = 0;
  1094. if (in_is_ll) {
  1095. c.lpzt.t = 0;
  1096. if (out_is_ll) {
  1097. /* what is more costly ?
  1098. * calling proj_trans for each point
  1099. * or having three loops over all points ?
  1100. * proj_trans_array() itself calls proj_trans() in a loop
  1101. * -> one loop over all points is better than
  1102. * three loops over all points
  1103. */
  1104. for (i = 0; i < n; i++) {
  1105. if (in_deg2rad) {
  1106. /* convert degrees to radians */
  1107. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1108. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1109. }
  1110. else {
  1111. c.lpzt.lam = (*x);
  1112. c.lpzt.phi = (*y);
  1113. }
  1114. c.lpzt.z = z[i];
  1115. c = proj_trans(info_trans->pj, dir, c);
  1116. if ((ok = proj_errno(info_trans->pj)) < 0)
  1117. break;
  1118. if (out_rad2deg) {
  1119. /* convert radians to degrees */
  1120. x[i] = c.lp.lam * RAD_TO_DEG;
  1121. y[i] = c.lp.phi * RAD_TO_DEG;
  1122. }
  1123. else {
  1124. x[i] = c.lp.lam;
  1125. y[i] = c.lp.phi;
  1126. }
  1127. }
  1128. }
  1129. else {
  1130. for (i = 0; i < n; i++) {
  1131. if (in_deg2rad) {
  1132. /* convert degrees to radians */
  1133. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1134. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1135. }
  1136. else {
  1137. c.lpzt.lam = (*x);
  1138. c.lpzt.phi = (*y);
  1139. }
  1140. c.lpzt.z = z[i];
  1141. c = proj_trans(info_trans->pj, dir, c);
  1142. if ((ok = proj_errno(info_trans->pj)) < 0)
  1143. break;
  1144. /* convert to map units */
  1145. x[i] = c.xy.x / METERS_out;
  1146. y[i] = c.xy.y / METERS_out;
  1147. }
  1148. }
  1149. }
  1150. else {
  1151. c.xyzt.t = 0;
  1152. if (out_is_ll) {
  1153. for (i = 0; i < n; i++) {
  1154. /* convert to meters */
  1155. c.xyzt.x = x[i] * METERS_in;
  1156. c.xyzt.y = y[i] * METERS_in;
  1157. c.xyzt.z = z[i];
  1158. c = proj_trans(info_trans->pj, dir, c);
  1159. if ((ok = proj_errno(info_trans->pj)) < 0)
  1160. break;
  1161. if (out_rad2deg) {
  1162. /* convert radians to degrees */
  1163. x[i] = c.lp.lam * RAD_TO_DEG;
  1164. y[i] = c.lp.phi * RAD_TO_DEG;
  1165. }
  1166. else {
  1167. x[i] = c.lp.lam;
  1168. y[i] = c.lp.phi;
  1169. }
  1170. }
  1171. }
  1172. else {
  1173. for (i = 0; i < n; i++) {
  1174. /* convert to meters */
  1175. c.xyzt.x = x[i] * METERS_in;
  1176. c.xyzt.y = y[i] * METERS_in;
  1177. c.xyzt.z = z[i];
  1178. c = proj_trans(info_trans->pj, dir, c);
  1179. if ((ok = proj_errno(info_trans->pj)) < 0)
  1180. break;
  1181. /* convert to map units */
  1182. x[i] = c.xy.x / METERS_out;
  1183. y[i] = c.xy.y / METERS_out;
  1184. }
  1185. }
  1186. }
  1187. if (!has_z)
  1188. G_free(z);
  1189. if (ok < 0) {
  1190. G_warning(_("proj_trans() failed: %s"), proj_errno_string(ok));
  1191. }
  1192. #else
  1193. /* PROJ 4 variant */
  1194. const struct pj_info *p_in, *p_out;
  1195. if (dir == PJ_FWD) {
  1196. p_in = info_in;
  1197. p_out = info_out;
  1198. }
  1199. else {
  1200. p_in = info_out;
  1201. p_out = info_in;
  1202. }
  1203. METERS_in = p_in->meters;
  1204. METERS_out = p_out->meters;
  1205. if (z == NULL) {
  1206. z = G_malloc(sizeof(double) * n);
  1207. /* they say memset is only guaranteed for chars ;-( */
  1208. for (i = 0; i < n; ++i)
  1209. z[i] = 0.0;
  1210. has_z = 0;
  1211. }
  1212. if (strncmp(p_in->proj, "ll", 2) == 0) {
  1213. if (strncmp(p_out->proj, "ll", 2) == 0) {
  1214. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  1215. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1216. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  1217. }
  1218. else {
  1219. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  1220. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1221. DIVIDE_LOOP(x, y, n, METERS_out);
  1222. }
  1223. }
  1224. else {
  1225. if (strncmp(p_out->proj, "ll", 2) == 0) {
  1226. MULTIPLY_LOOP(x, y, n, METERS_in);
  1227. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1228. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  1229. }
  1230. else {
  1231. MULTIPLY_LOOP(x, y, n, METERS_in);
  1232. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1233. DIVIDE_LOOP(x, y, n, METERS_out);
  1234. }
  1235. }
  1236. if (!has_z)
  1237. G_free(z);
  1238. if (ok < 0)
  1239. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1240. #endif
  1241. return ok;
  1242. }
  1243. /*
  1244. * old API, to be deleted
  1245. */
  1246. /**
  1247. * \brief Re-project a point between two co-ordinate systems
  1248. *
  1249. * This function takes pointers to two pj_info structures as arguments,
  1250. * and projects a point between the co-ordinate systems represented by them.
  1251. * The easting and northing of the point are contained in two pointers passed
  1252. * to the function; these will be overwritten by the co-ordinates of the
  1253. * re-projected point.
  1254. *
  1255. * \param x Pointer to a double containing easting or longitude
  1256. * \param y Pointer to a double containing northing or latitude
  1257. * \param info_in pointer to pj_info struct for input co-ordinate system
  1258. * \param info_out pointer to pj_info struct for output co-ordinate system
  1259. *
  1260. * \return Return value from PROJ proj_trans() function
  1261. **/
  1262. int pj_do_proj(double *x, double *y,
  1263. const struct pj_info *info_in, const struct pj_info *info_out)
  1264. {
  1265. int ok;
  1266. #ifdef HAVE_PROJ_H
  1267. struct pj_info info_trans;
  1268. PJ_COORD c;
  1269. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  1270. return -1;
  1271. }
  1272. METERS_in = info_in->meters;
  1273. METERS_out = info_out->meters;
  1274. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1275. /* convert to radians */
  1276. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1277. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1278. c.lpzt.z = 0;
  1279. c.lpzt.t = 0;
  1280. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1281. ok = proj_errno(info_trans.pj);
  1282. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1283. /* convert to degrees */
  1284. *x = c.lp.lam * RAD_TO_DEG;
  1285. *y = c.lp.phi * RAD_TO_DEG;
  1286. }
  1287. else {
  1288. /* convert to map units */
  1289. *x = c.xy.x / METERS_out;
  1290. *y = c.xy.y / METERS_out;
  1291. }
  1292. }
  1293. else {
  1294. /* convert to meters */
  1295. c.xyzt.x = *x * METERS_in;
  1296. c.xyzt.y = *y * METERS_in;
  1297. c.xyzt.z = 0;
  1298. c.xyzt.t = 0;
  1299. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1300. ok = proj_errno(info_trans.pj);
  1301. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1302. /* convert to degrees */
  1303. *x = c.lp.lam * RAD_TO_DEG;
  1304. *y = c.lp.phi * RAD_TO_DEG;
  1305. }
  1306. else {
  1307. /* convert to map units */
  1308. *x = c.xy.x / METERS_out;
  1309. *y = c.xy.y / METERS_out;
  1310. }
  1311. }
  1312. proj_destroy(info_trans.pj);
  1313. if (ok < 0) {
  1314. G_warning(_("proj_trans() failed: %d"), ok);
  1315. }
  1316. #else
  1317. double u, v;
  1318. double h = 0.0;
  1319. METERS_in = info_in->meters;
  1320. METERS_out = info_out->meters;
  1321. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1322. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1323. u = (*x) / RAD_TO_DEG;
  1324. v = (*y) / RAD_TO_DEG;
  1325. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1326. *x = u * RAD_TO_DEG;
  1327. *y = v * RAD_TO_DEG;
  1328. }
  1329. else {
  1330. u = (*x) / RAD_TO_DEG;
  1331. v = (*y) / RAD_TO_DEG;
  1332. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1333. *x = u / METERS_out;
  1334. *y = v / METERS_out;
  1335. }
  1336. }
  1337. else {
  1338. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1339. u = *x * METERS_in;
  1340. v = *y * METERS_in;
  1341. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1342. *x = u * RAD_TO_DEG;
  1343. *y = v * RAD_TO_DEG;
  1344. }
  1345. else {
  1346. u = *x * METERS_in;
  1347. v = *y * METERS_in;
  1348. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1349. *x = u / METERS_out;
  1350. *y = v / METERS_out;
  1351. }
  1352. }
  1353. if (ok < 0) {
  1354. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1355. }
  1356. #endif
  1357. return ok;
  1358. }
  1359. /**
  1360. * \brief Re-project an array of points between two co-ordinate systems with
  1361. * optional ellipsoidal height conversion
  1362. *
  1363. * This function takes pointers to two pj_info structures as arguments,
  1364. * and projects an array of points between the co-ordinate systems
  1365. * represented by them. Pointers to the three arrays of easting, northing,
  1366. * and ellipsoidal height of the point (this one may be NULL) are passed
  1367. * to the function; these will be overwritten by the co-ordinates of the
  1368. * re-projected points.
  1369. *
  1370. * \param count Number of points in the arrays to be transformed
  1371. * \param x Pointer to an array of type double containing easting or longitude
  1372. * \param y Pointer to an array of type double containing northing or latitude
  1373. * \param h Pointer to an array of type double containing ellipsoidal height.
  1374. * May be null in which case a two-dimensional re-projection will be
  1375. * done
  1376. * \param info_in pointer to pj_info struct for input co-ordinate system
  1377. * \param info_out pointer to pj_info struct for output co-ordinate system
  1378. *
  1379. * \return Return value from PROJ proj_trans() function
  1380. **/
  1381. int pj_do_transform(int count, double *x, double *y, double *h,
  1382. const struct pj_info *info_in, const struct pj_info *info_out)
  1383. {
  1384. int ok;
  1385. int i;
  1386. int has_h = 1;
  1387. #ifdef HAVE_PROJ_H
  1388. struct pj_info info_trans;
  1389. PJ_COORD c;
  1390. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  1391. return -1;
  1392. }
  1393. METERS_in = info_in->meters;
  1394. METERS_out = info_out->meters;
  1395. if (h == NULL) {
  1396. h = G_malloc(sizeof *h * count);
  1397. /* they say memset is only guaranteed for chars ;-( */
  1398. for (i = 0; i < count; ++i)
  1399. h[i] = 0.0;
  1400. has_h = 0;
  1401. }
  1402. ok = 0;
  1403. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1404. c.lpzt.t = 0;
  1405. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1406. for (i = 0; i < count; i++) {
  1407. /* convert to radians */
  1408. c.lpzt.lam = x[i] / RAD_TO_DEG;
  1409. c.lpzt.phi = y[i] / RAD_TO_DEG;
  1410. c.lpzt.z = h[i];
  1411. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1412. if ((ok = proj_errno(info_trans.pj)) < 0)
  1413. break;
  1414. /* convert to degrees */
  1415. x[i] = c.lp.lam * RAD_TO_DEG;
  1416. y[i] = c.lp.phi * RAD_TO_DEG;
  1417. }
  1418. }
  1419. else {
  1420. for (i = 0; i < count; i++) {
  1421. /* convert to radians */
  1422. c.lpzt.lam = x[i] / RAD_TO_DEG;
  1423. c.lpzt.phi = y[i] / RAD_TO_DEG;
  1424. c.lpzt.z = h[i];
  1425. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1426. if ((ok = proj_errno(info_trans.pj)) < 0)
  1427. break;
  1428. /* convert to map units */
  1429. x[i] = c.xy.x / METERS_out;
  1430. y[i] = c.xy.y / METERS_out;
  1431. }
  1432. }
  1433. }
  1434. else {
  1435. c.xyzt.t = 0;
  1436. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1437. for (i = 0; i < count; i++) {
  1438. /* convert to meters */
  1439. c.xyzt.x = x[i] * METERS_in;
  1440. c.xyzt.y = y[i] * METERS_in;
  1441. c.xyzt.z = h[i];
  1442. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1443. if ((ok = proj_errno(info_trans.pj)) < 0)
  1444. break;
  1445. /* convert to degrees */
  1446. x[i] = c.lp.lam * RAD_TO_DEG;
  1447. y[i] = c.lp.phi * RAD_TO_DEG;
  1448. }
  1449. }
  1450. else {
  1451. for (i = 0; i < count; i++) {
  1452. /* convert to meters */
  1453. c.xyzt.x = x[i] * METERS_in;
  1454. c.xyzt.y = y[i] * METERS_in;
  1455. c.xyzt.z = h[i];
  1456. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1457. if ((ok = proj_errno(info_trans.pj)) < 0)
  1458. break;
  1459. /* convert to map units */
  1460. x[i] = c.xy.x / METERS_out;
  1461. y[i] = c.xy.y / METERS_out;
  1462. }
  1463. }
  1464. }
  1465. if (!has_h)
  1466. G_free(h);
  1467. proj_destroy(info_trans.pj);
  1468. if (ok < 0) {
  1469. G_warning(_("proj_trans() failed: %d"), ok);
  1470. }
  1471. #else
  1472. METERS_in = info_in->meters;
  1473. METERS_out = info_out->meters;
  1474. if (h == NULL) {
  1475. h = G_malloc(sizeof *h * count);
  1476. /* they say memset is only guaranteed for chars ;-( */
  1477. for (i = 0; i < count; ++i)
  1478. h[i] = 0.0;
  1479. has_h = 0;
  1480. }
  1481. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1482. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1483. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  1484. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1485. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  1486. }
  1487. else {
  1488. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  1489. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1490. DIVIDE_LOOP(x, y, count, METERS_out);
  1491. }
  1492. }
  1493. else {
  1494. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1495. MULTIPLY_LOOP(x, y, count, METERS_in);
  1496. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1497. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  1498. }
  1499. else {
  1500. MULTIPLY_LOOP(x, y, count, METERS_in);
  1501. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1502. DIVIDE_LOOP(x, y, count, METERS_out);
  1503. }
  1504. }
  1505. if (!has_h)
  1506. G_free(h);
  1507. if (ok < 0) {
  1508. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1509. }
  1510. #endif
  1511. return ok;
  1512. }