do_proj.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682
  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 *str;
  500. const char *area_of_use, *projstr;
  501. double e, w, s, n;
  502. PJ_PROJ_INFO pj_info;
  503. PJ *op, *op_norm;
  504. op = proj_list_get(PJ_DEFAULT_CTX, op_list, i);
  505. op_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, op);
  506. if (!op_norm) {
  507. G_warning(_("proj_normalize_for_visualization() failed for operation %d"),
  508. i + 1);
  509. }
  510. else {
  511. proj_destroy(op);
  512. op = op_norm;
  513. }
  514. pj_info = proj_pj_info(op);
  515. proj_get_area_of_use(NULL, op, &w, &s, &e, &n, &area_of_use);
  516. G_important_message("************************");
  517. G_important_message(_("Operation %d:"), i + 1);
  518. if (pj_info.description) {
  519. G_important_message(_("Description: %s"),
  520. pj_info.description);
  521. }
  522. if (area_of_use) {
  523. G_important_message(" ");
  524. G_important_message(_("Area of use: %s"),
  525. area_of_use);
  526. }
  527. if (pj_info.accuracy > 0) {
  528. G_important_message(" ");
  529. G_important_message(_("Accuracy within area of use: %g m"),
  530. pj_info.accuracy);
  531. }
  532. #if PROJ_VERSION_NUM >= 6020000
  533. str = proj_get_remarks(op);
  534. if (str && *str) {
  535. G_important_message(" ");
  536. G_important_message(_("Remarks: %s"), str);
  537. }
  538. str = proj_get_scope(op);
  539. if (str && *str) {
  540. G_important_message(" ");
  541. G_important_message(_("Scope: %s"), str);
  542. }
  543. #endif
  544. projstr = proj_as_proj_string(NULL, op,
  545. PJ_PROJ_5, NULL);
  546. if (projstr) {
  547. G_important_message(" ");
  548. G_important_message(_("PROJ string:"));
  549. G_important_message("%s", projstr);
  550. }
  551. proj_destroy(op);
  552. }
  553. G_important_message("************************");
  554. G_important_message(_("See also output of:"));
  555. G_important_message("projinfo -o PROJ -s \"%s\" -t \"%s\"",
  556. indef, outdef);
  557. G_important_message(_("Please provide the appropriate PROJ string with the %s option"),
  558. "pipeline");
  559. G_important_message("************************");
  560. }
  561. if (op_list)
  562. proj_list_destroy(op_list);
  563. /* follwing code copied from proj_create_crs_to_crs_from_pj()
  564. * in proj src/4D_api.cpp
  565. * but using PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT */
  566. /* now use the current region as area of interest */
  567. operation_ctx = proj_create_operation_factory_context(PJ_DEFAULT_CTX, NULL);
  568. proj_operation_factory_context_set_area_of_interest(PJ_DEFAULT_CTX,
  569. operation_ctx,
  570. xmin, ymin,
  571. xmax, ymax);
  572. proj_operation_factory_context_set_spatial_criterion(PJ_DEFAULT_CTX,
  573. operation_ctx,
  574. PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT);
  575. proj_operation_factory_context_set_grid_availability_use(PJ_DEFAULT_CTX,
  576. operation_ctx,
  577. PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID);
  578. /* The operations are sorted with the most relevant ones first:
  579. * by descending area (intersection of the transformation area
  580. * with the area of interest, or intersection of the
  581. * transformation with the area of use of the CRS),
  582. * and by increasing accuracy.
  583. * Operations with unknown accuracy are sorted last,
  584. * whatever their area.
  585. */
  586. op_list = proj_create_operations(PJ_DEFAULT_CTX,
  587. in_pj,
  588. out_pj,
  589. operation_ctx);
  590. proj_operation_factory_context_destroy(operation_ctx);
  591. op_count_area = 0;
  592. if (op_list)
  593. op_count_area = proj_list_get_count(op_list);
  594. if (op_count_area == 0) {
  595. /* no operations */
  596. info_trans->pj = NULL;
  597. }
  598. else if (op_count_area == 1) {
  599. info_trans->pj = proj_list_get(PJ_DEFAULT_CTX, op_list, 0);
  600. }
  601. else { /* op_count_area > 1 */
  602. /* can't use pj_create_prepared_operations()
  603. * this is a PROJ-internal function
  604. * trust the sorting of PROJ and use the first one */
  605. info_trans->pj = proj_list_get(PJ_DEFAULT_CTX, op_list, 0);
  606. }
  607. if (op_list)
  608. proj_list_destroy(op_list);
  609. /* try proj_create_crs_to_crs() */
  610. G_debug(1, "trying %s to %s", indef, outdef);
  611. /* proj_create_crs_to_crs() does not work because it calls
  612. * proj_create_crs_to_crs_from_pj() which calls
  613. * proj_operation_factory_context_set_spatial_criterion()
  614. * with PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION
  615. * instead of
  616. * PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT
  617. *
  618. * fixed in PROJ master, probably available with PROJ 7.3.x */
  619. /*
  620. info_trans->pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
  621. indef,
  622. outdef,
  623. pj_area);
  624. */
  625. if (in_pj)
  626. proj_destroy(in_pj);
  627. if (out_pj)
  628. proj_destroy(out_pj);
  629. if (info_trans->pj) {
  630. const char *projstr;
  631. PJ *pj_norm = NULL;
  632. G_debug(1, "proj_create_crs_to_crs() succeeded with PROJ%d",
  633. PROJ_VERSION_MAJOR);
  634. projstr = proj_as_proj_string(NULL, info_trans->pj,
  635. PJ_PROJ_5, NULL);
  636. info_trans->def = G_store(projstr);
  637. if (projstr) {
  638. /* make sure axis order is easting, northing
  639. * proj_normalize_for_visualization() requires
  640. * source and target CRS
  641. * -> does not work with ll equivalent of input:
  642. * no target CRS in +proj=pipeline +step +inv %s */
  643. pj_norm = proj_normalize_for_visualization(PJ_DEFAULT_CTX, info_trans->pj);
  644. if (!pj_norm) {
  645. G_warning(_("proj_normalize_for_visualization() failed for '%s'"),
  646. info_trans->def);
  647. }
  648. else {
  649. projstr = proj_as_proj_string(NULL, pj_norm,
  650. PJ_PROJ_5, NULL);
  651. if (projstr && *projstr) {
  652. proj_destroy(info_trans->pj);
  653. info_trans->pj = pj_norm;
  654. info_trans->def = G_store(projstr);
  655. }
  656. else {
  657. proj_destroy(pj_norm);
  658. G_warning(_("No PROJ definition for normalized version of '%s'"),
  659. info_trans->def);
  660. }
  661. }
  662. G_important_message(_("Selected PROJ pipeline:"));
  663. G_important_message(_("%s"), info_trans->def);
  664. G_important_message("************************");
  665. }
  666. else {
  667. proj_destroy(info_trans->pj);
  668. info_trans->pj = NULL;
  669. }
  670. }
  671. if (pj_area)
  672. proj_area_destroy(pj_area);
  673. if (insrid)
  674. G_free(insrid);
  675. if (outsrid)
  676. G_free(outsrid);
  677. G_free(indef);
  678. G_free(outdef);
  679. }
  680. if (info_trans->pj == NULL) {
  681. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  682. return -1;
  683. }
  684. #else /* PROJ 5 */
  685. info_trans->pj = NULL;
  686. info_trans->meters = 1.;
  687. info_trans->zone = 0;
  688. sprintf(info_trans->proj, "pipeline");
  689. /* user-provided pipeline */
  690. if (info_trans->def) {
  691. /* create a pj from user-defined transformation pipeline */
  692. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  693. if (info_trans->pj == NULL) {
  694. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  695. return -1;
  696. }
  697. }
  698. /* if no output CRS is defined,
  699. * assume info_out to be ll equivalent of info_in */
  700. else if (info_out->pj == NULL) {
  701. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s",
  702. info_in->def);
  703. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  704. if (info_trans->pj == NULL) {
  705. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  706. return -1;
  707. }
  708. }
  709. else if (info_in->def && info_out->pj && info_out->def) {
  710. char *indef = NULL, *outdef = NULL;
  711. char *insrid = NULL, *outsrid = NULL;
  712. /* PROJ5: EPSG must be lowercase epsg */
  713. if (info_in->srid) {
  714. if (strncmp(info_in->srid, "EPSG", 4) == 0)
  715. insrid = G_store_lower(info_in->srid);
  716. else
  717. insrid = G_store(info_in->srid);
  718. }
  719. if (info_out->pj && info_out->srid) {
  720. if (strncmp(info_out->srid, "EPSG", 4) == 0)
  721. outsrid = G_store_lower(info_out->srid);
  722. else
  723. outsrid = G_store(info_out->srid);
  724. }
  725. info_trans->pj = NULL;
  726. if (insrid && outsrid) {
  727. G_asprintf(&indef, "%s", insrid);
  728. G_asprintf(&outdef, "%s", outsrid);
  729. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv +init=%s +step +init=%s",
  730. indef, outdef);
  731. /* try proj_create_crs_to_crs() */
  732. info_trans->pj = proj_create_crs_to_crs(PJ_DEFAULT_CTX,
  733. indef,
  734. outdef,
  735. NULL);
  736. }
  737. if (info_trans->pj) {
  738. G_debug(1, "proj_create_crs_to_crs() succeeded with PROJ5");
  739. }
  740. else {
  741. if (indef) {
  742. G_free(indef);
  743. indef = NULL;
  744. }
  745. if (insrid) {
  746. G_asprintf(&indef, "+init=%s", insrid);
  747. }
  748. else {
  749. G_asprintf(&indef, "%s", info_in->def);
  750. }
  751. if (outdef) {
  752. G_free(outdef);
  753. outdef = NULL;
  754. }
  755. if (outsrid) {
  756. G_asprintf(&outdef, "+init=%s", outsrid);
  757. }
  758. else {
  759. G_asprintf(&outdef, "%s", info_out->def);
  760. }
  761. /* try proj_create() with +proj=pipeline +step +inv %s +step %s" */
  762. G_asprintf(&(info_trans->def), "+proj=pipeline +step +inv %s +step %s",
  763. indef, outdef);
  764. info_trans->pj = proj_create(PJ_DEFAULT_CTX, info_trans->def);
  765. }
  766. if (insrid)
  767. G_free(insrid);
  768. if (outsrid)
  769. G_free(outsrid);
  770. G_free(indef);
  771. G_free(outdef);
  772. }
  773. if (info_trans->pj == NULL) {
  774. G_warning(_("proj_create() failed for '%s'"), info_trans->def);
  775. return -1;
  776. }
  777. #endif
  778. #else /* PROJ 4 */
  779. if (info_out->pj == NULL) {
  780. if (GPJ_get_equivalent_latlong(info_out, info_in) < 0) {
  781. G_warning(_("Unable to create latlong equivalent for '%s'"),
  782. info_in->def);
  783. return -1;
  784. }
  785. }
  786. #endif
  787. return 1;
  788. }
  789. /* TODO: rename pj_ to GPJ_ to avoid symbol clash with PROJ lib */
  790. /**
  791. * \brief Re-project a point between two co-ordinate systems using a
  792. * transformation object prepared with GPJ_prepare_pj()
  793. *
  794. * This function takes pointers to three pj_info structures as arguments,
  795. * and projects a point between the input and output co-ordinate system.
  796. * The pj_info structure info_trans must have been initialized with
  797. * GPJ_init_transform().
  798. * The direction determines if a point is projected from input CRS to
  799. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  800. * The easting, northing, and height of the point are contained in the
  801. * pointers passed to the function; these will be overwritten by the
  802. * coordinates of the transformed point.
  803. *
  804. * \param info_in pointer to pj_info struct for input co-ordinate system
  805. * \param info_out pointer to pj_info struct for output co-ordinate system
  806. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  807. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  808. * \param x Pointer to a double containing easting or longitude
  809. * \param y Pointer to a double containing northing or latitude
  810. * \param z Pointer to a double containing height, or NULL
  811. *
  812. * \return Return value from PROJ proj_trans() function
  813. **/
  814. int GPJ_transform(const struct pj_info *info_in,
  815. const struct pj_info *info_out,
  816. const struct pj_info *info_trans, int dir,
  817. double *x, double *y, double *z)
  818. {
  819. int ok = 0;
  820. #ifdef HAVE_PROJ_H
  821. /* PROJ 5+ variant */
  822. int in_is_ll, out_is_ll, in_deg2rad, out_rad2deg;
  823. PJ_COORD c;
  824. if (info_in->pj == NULL)
  825. G_fatal_error(_("No input projection"));
  826. if (info_trans->pj == NULL)
  827. G_fatal_error(_("No transformation object"));
  828. in_deg2rad = out_rad2deg = 1;
  829. if (dir == PJ_FWD) {
  830. /* info_in -> info_out */
  831. METERS_in = info_in->meters;
  832. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  833. #if PROJ_VERSION_MAJOR >= 6
  834. /* PROJ 6+: conversion to radians is not always needed:
  835. * if proj_angular_input(info_trans->pj, dir) == 1
  836. * -> convert from degrees to radians */
  837. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  838. in_deg2rad = 0;
  839. }
  840. #endif
  841. if (info_out->pj) {
  842. METERS_out = info_out->meters;
  843. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  844. #if PROJ_VERSION_MAJOR >= 6
  845. /* PROJ 6+: conversion to radians is not always needed:
  846. * if proj_angular_input(info_trans->pj, dir) == 1
  847. * -> convert from degrees to radians */
  848. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  849. out_rad2deg = 0;
  850. }
  851. #endif
  852. }
  853. else {
  854. METERS_out = 1.0;
  855. out_is_ll = 1;
  856. }
  857. }
  858. else {
  859. /* info_out -> info_in */
  860. METERS_out = info_in->meters;
  861. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  862. #if PROJ_VERSION_MAJOR >= 6
  863. /* PROJ 6+: conversion to radians is not always needed:
  864. * if proj_angular_input(info_trans->pj, dir) == 1
  865. * -> convert from degrees to radians */
  866. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  867. out_rad2deg = 0;
  868. }
  869. #endif
  870. if (info_out->pj) {
  871. METERS_in = info_out->meters;
  872. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  873. #if PROJ_VERSION_MAJOR >= 6
  874. /* PROJ 6+: conversion to radians is not always needed:
  875. * if proj_angular_input(info_trans->pj, dir) == 1
  876. * -> convert from degrees to radians */
  877. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  878. in_deg2rad = 0;
  879. }
  880. #endif
  881. }
  882. else {
  883. METERS_in = 1.0;
  884. in_is_ll = 1;
  885. }
  886. }
  887. /* prepare */
  888. if (in_is_ll) {
  889. if (in_deg2rad) {
  890. /* convert degrees to radians */
  891. c.lpzt.lam = (*x) / RAD_TO_DEG;
  892. c.lpzt.phi = (*y) / RAD_TO_DEG;
  893. }
  894. else {
  895. c.lpzt.lam = (*x);
  896. c.lpzt.phi = (*y);
  897. }
  898. c.lpzt.z = 0;
  899. if (z)
  900. c.lpzt.z = *z;
  901. c.lpzt.t = 0;
  902. }
  903. else {
  904. /* convert to meters */
  905. c.xyzt.x = *x * METERS_in;
  906. c.xyzt.y = *y * METERS_in;
  907. c.xyzt.z = 0;
  908. if (z)
  909. c.xyzt.z = *z;
  910. c.xyzt.t = 0;
  911. }
  912. G_debug(1, "c.xyzt.x: %g", c.xyzt.x);
  913. G_debug(1, "c.xyzt.y: %g", c.xyzt.y);
  914. G_debug(1, "c.xyzt.z: %g", c.xyzt.z);
  915. /* transform */
  916. c = proj_trans(info_trans->pj, dir, c);
  917. ok = proj_errno(info_trans->pj);
  918. if (ok < 0) {
  919. G_warning(_("proj_trans() failed: %s"), proj_errno_string(ok));
  920. return ok;
  921. }
  922. /* output */
  923. if (out_is_ll) {
  924. /* convert to degrees */
  925. if (out_rad2deg) {
  926. /* convert radians to degrees */
  927. *x = c.lp.lam * RAD_TO_DEG;
  928. *y = c.lp.phi * RAD_TO_DEG;
  929. }
  930. else {
  931. *x = c.lp.lam;
  932. *y = c.lp.phi;
  933. }
  934. if (z)
  935. *z = c.lpzt.z;
  936. }
  937. else {
  938. /* convert to map units */
  939. *x = c.xyzt.x / METERS_out;
  940. *y = c.xyzt.y / METERS_out;
  941. if (z)
  942. *z = c.xyzt.z;
  943. }
  944. #else
  945. /* PROJ 4 variant */
  946. double u, v;
  947. double h = 0.0;
  948. const struct pj_info *p_in, *p_out;
  949. if (info_out == NULL)
  950. G_fatal_error(_("No output projection"));
  951. if (dir == PJ_FWD) {
  952. p_in = info_in;
  953. p_out = info_out;
  954. }
  955. else {
  956. p_in = info_out;
  957. p_out = info_in;
  958. }
  959. METERS_in = p_in->meters;
  960. METERS_out = p_out->meters;
  961. if (z)
  962. h = *z;
  963. if (strncmp(p_in->proj, "ll", 2) == 0) {
  964. u = (*x) / RAD_TO_DEG;
  965. v = (*y) / RAD_TO_DEG;
  966. }
  967. else {
  968. u = *x * METERS_in;
  969. v = *y * METERS_in;
  970. }
  971. ok = pj_transform(p_in->pj, p_out->pj, 1, 0, &u, &v, &h);
  972. if (ok < 0) {
  973. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  974. return ok;
  975. }
  976. if (strncmp(p_out->proj, "ll", 2) == 0) {
  977. *x = u * RAD_TO_DEG;
  978. *y = v * RAD_TO_DEG;
  979. }
  980. else {
  981. *x = u / METERS_out;
  982. *y = v / METERS_out;
  983. }
  984. if (z)
  985. *z = h;
  986. #endif
  987. return ok;
  988. }
  989. /**
  990. * \brief Re-project an array of points between two co-ordinate systems
  991. * using a transformation object prepared with GPJ_prepare_pj()
  992. *
  993. * This function takes pointers to three pj_info structures as arguments,
  994. * and projects an array of pointd between the input and output
  995. * co-ordinate system. The pj_info structure info_trans must have been
  996. * initialized with GPJ_init_transform().
  997. * The direction determines if a point is projected from input CRS to
  998. * output CRS (PJ_FWD) or from output CRS to input CRS (PJ_INV).
  999. * The easting, northing, and height of the point are contained in the
  1000. * pointers passed to the function; these will be overwritten by the
  1001. * coordinates of the transformed point.
  1002. *
  1003. * \param info_in pointer to pj_info struct for input co-ordinate system
  1004. * \param info_out pointer to pj_info struct for output co-ordinate system
  1005. * \param info_trans pointer to pj_info struct for a transformation object (PROJ 5+)
  1006. * \param dir direction of the transformation (PJ_FWD or PJ_INV)
  1007. * \param x pointer to an array of type double containing easting or longitude
  1008. * \param y pointer to an array of type double containing northing or latitude
  1009. * \param z pointer to an array of type double containing height, or NULL
  1010. * \param n number of points in the arrays to be transformed
  1011. *
  1012. * \return Return value from PROJ proj_trans() function
  1013. **/
  1014. int GPJ_transform_array(const struct pj_info *info_in,
  1015. const struct pj_info *info_out,
  1016. const struct pj_info *info_trans, int dir,
  1017. double *x, double *y, double *z, int n)
  1018. {
  1019. int ok;
  1020. int i;
  1021. int has_z = 1;
  1022. #ifdef HAVE_PROJ_H
  1023. /* PROJ 5+ variant */
  1024. int in_is_ll, out_is_ll, in_deg2rad, out_rad2deg;
  1025. PJ_COORD c;
  1026. if (info_trans->pj == NULL)
  1027. G_fatal_error(_("No transformation object"));
  1028. in_deg2rad = out_rad2deg = 1;
  1029. if (dir == PJ_FWD) {
  1030. /* info_in -> info_out */
  1031. METERS_in = info_in->meters;
  1032. in_is_ll = !strncmp(info_in->proj, "ll", 2);
  1033. #if PROJ_VERSION_MAJOR >= 6
  1034. /* PROJ 6+: conversion to radians is not always needed:
  1035. * if proj_angular_input(info_trans->pj, dir) == 1
  1036. * -> convert from degrees to radians */
  1037. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  1038. in_deg2rad = 0;
  1039. }
  1040. #endif
  1041. if (info_out->pj) {
  1042. METERS_out = info_out->meters;
  1043. out_is_ll = !strncmp(info_out->proj, "ll", 2);
  1044. #if PROJ_VERSION_MAJOR >= 6
  1045. /* PROJ 6+: conversion to radians is not always needed:
  1046. * if proj_angular_input(info_trans->pj, dir) == 1
  1047. * -> convert from degrees to radians */
  1048. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  1049. out_rad2deg = 0;
  1050. }
  1051. #endif
  1052. }
  1053. else {
  1054. METERS_out = 1.0;
  1055. out_is_ll = 1;
  1056. }
  1057. }
  1058. else {
  1059. /* info_out -> info_in */
  1060. METERS_out = info_in->meters;
  1061. out_is_ll = !strncmp(info_in->proj, "ll", 2);
  1062. #if PROJ_VERSION_MAJOR >= 6
  1063. /* PROJ 6+: conversion to radians is not always needed:
  1064. * if proj_angular_input(info_trans->pj, dir) == 1
  1065. * -> convert from degrees to radians */
  1066. if (out_is_ll && proj_angular_output(info_trans->pj, dir) == 0) {
  1067. out_rad2deg = 0;
  1068. }
  1069. #endif
  1070. if (info_out->pj) {
  1071. METERS_in = info_out->meters;
  1072. in_is_ll = !strncmp(info_out->proj, "ll", 2);
  1073. #if PROJ_VERSION_MAJOR >= 6
  1074. /* PROJ 6+: conversion to degrees is not always needed:
  1075. * if proj_angular_output(info_trans->pj, dir) == 1
  1076. * -> convert from degrees to radians */
  1077. if (in_is_ll && proj_angular_input(info_trans->pj, dir) == 0) {
  1078. in_deg2rad = 0;
  1079. }
  1080. #endif
  1081. }
  1082. else {
  1083. METERS_in = 1.0;
  1084. in_is_ll = 1;
  1085. }
  1086. }
  1087. if (z == NULL) {
  1088. z = G_malloc(sizeof(double) * n);
  1089. /* they say memset is only guaranteed for chars ;-( */
  1090. for (i = 0; i < n; i++)
  1091. z[i] = 0.0;
  1092. has_z = 0;
  1093. }
  1094. ok = 0;
  1095. if (in_is_ll) {
  1096. c.lpzt.t = 0;
  1097. if (out_is_ll) {
  1098. /* what is more costly ?
  1099. * calling proj_trans for each point
  1100. * or having three loops over all points ?
  1101. * proj_trans_array() itself calls proj_trans() in a loop
  1102. * -> one loop over all points is better than
  1103. * three loops over all points
  1104. */
  1105. for (i = 0; i < n; i++) {
  1106. if (in_deg2rad) {
  1107. /* convert degrees to radians */
  1108. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1109. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1110. }
  1111. else {
  1112. c.lpzt.lam = (*x);
  1113. c.lpzt.phi = (*y);
  1114. }
  1115. c.lpzt.z = z[i];
  1116. c = proj_trans(info_trans->pj, dir, c);
  1117. if ((ok = proj_errno(info_trans->pj)) < 0)
  1118. break;
  1119. if (out_rad2deg) {
  1120. /* convert radians to degrees */
  1121. x[i] = c.lp.lam * RAD_TO_DEG;
  1122. y[i] = c.lp.phi * RAD_TO_DEG;
  1123. }
  1124. else {
  1125. x[i] = c.lp.lam;
  1126. y[i] = c.lp.phi;
  1127. }
  1128. }
  1129. }
  1130. else {
  1131. for (i = 0; i < n; i++) {
  1132. if (in_deg2rad) {
  1133. /* convert degrees to radians */
  1134. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1135. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1136. }
  1137. else {
  1138. c.lpzt.lam = (*x);
  1139. c.lpzt.phi = (*y);
  1140. }
  1141. c.lpzt.z = z[i];
  1142. c = proj_trans(info_trans->pj, dir, c);
  1143. if ((ok = proj_errno(info_trans->pj)) < 0)
  1144. break;
  1145. /* convert to map units */
  1146. x[i] = c.xy.x / METERS_out;
  1147. y[i] = c.xy.y / METERS_out;
  1148. }
  1149. }
  1150. }
  1151. else {
  1152. c.xyzt.t = 0;
  1153. if (out_is_ll) {
  1154. for (i = 0; i < n; i++) {
  1155. /* convert to meters */
  1156. c.xyzt.x = x[i] * METERS_in;
  1157. c.xyzt.y = y[i] * METERS_in;
  1158. c.xyzt.z = z[i];
  1159. c = proj_trans(info_trans->pj, dir, c);
  1160. if ((ok = proj_errno(info_trans->pj)) < 0)
  1161. break;
  1162. if (out_rad2deg) {
  1163. /* convert radians to degrees */
  1164. x[i] = c.lp.lam * RAD_TO_DEG;
  1165. y[i] = c.lp.phi * RAD_TO_DEG;
  1166. }
  1167. else {
  1168. x[i] = c.lp.lam;
  1169. y[i] = c.lp.phi;
  1170. }
  1171. }
  1172. }
  1173. else {
  1174. for (i = 0; i < n; i++) {
  1175. /* convert to meters */
  1176. c.xyzt.x = x[i] * METERS_in;
  1177. c.xyzt.y = y[i] * METERS_in;
  1178. c.xyzt.z = z[i];
  1179. c = proj_trans(info_trans->pj, dir, c);
  1180. if ((ok = proj_errno(info_trans->pj)) < 0)
  1181. break;
  1182. /* convert to map units */
  1183. x[i] = c.xy.x / METERS_out;
  1184. y[i] = c.xy.y / METERS_out;
  1185. }
  1186. }
  1187. }
  1188. if (!has_z)
  1189. G_free(z);
  1190. if (ok < 0) {
  1191. G_warning(_("proj_trans() failed: %s"), proj_errno_string(ok));
  1192. }
  1193. #else
  1194. /* PROJ 4 variant */
  1195. const struct pj_info *p_in, *p_out;
  1196. if (dir == PJ_FWD) {
  1197. p_in = info_in;
  1198. p_out = info_out;
  1199. }
  1200. else {
  1201. p_in = info_out;
  1202. p_out = info_in;
  1203. }
  1204. METERS_in = p_in->meters;
  1205. METERS_out = p_out->meters;
  1206. if (z == NULL) {
  1207. z = G_malloc(sizeof(double) * n);
  1208. /* they say memset is only guaranteed for chars ;-( */
  1209. for (i = 0; i < n; ++i)
  1210. z[i] = 0.0;
  1211. has_z = 0;
  1212. }
  1213. if (strncmp(p_in->proj, "ll", 2) == 0) {
  1214. if (strncmp(p_out->proj, "ll", 2) == 0) {
  1215. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  1216. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1217. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  1218. }
  1219. else {
  1220. DIVIDE_LOOP(x, y, n, RAD_TO_DEG);
  1221. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1222. DIVIDE_LOOP(x, y, n, METERS_out);
  1223. }
  1224. }
  1225. else {
  1226. if (strncmp(p_out->proj, "ll", 2) == 0) {
  1227. MULTIPLY_LOOP(x, y, n, METERS_in);
  1228. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1229. MULTIPLY_LOOP(x, y, n, RAD_TO_DEG);
  1230. }
  1231. else {
  1232. MULTIPLY_LOOP(x, y, n, METERS_in);
  1233. ok = pj_transform(info_in->pj, info_out->pj, n, 1, x, y, z);
  1234. DIVIDE_LOOP(x, y, n, METERS_out);
  1235. }
  1236. }
  1237. if (!has_z)
  1238. G_free(z);
  1239. if (ok < 0)
  1240. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1241. #endif
  1242. return ok;
  1243. }
  1244. /*
  1245. * old API, to be deleted
  1246. */
  1247. /**
  1248. * \brief Re-project a point between two co-ordinate systems
  1249. *
  1250. * This function takes pointers to two pj_info structures as arguments,
  1251. * and projects a point between the co-ordinate systems represented by them.
  1252. * The easting and northing of the point are contained in two pointers passed
  1253. * to the function; these will be overwritten by the co-ordinates of the
  1254. * re-projected point.
  1255. *
  1256. * \param x Pointer to a double containing easting or longitude
  1257. * \param y Pointer to a double containing northing or latitude
  1258. * \param info_in pointer to pj_info struct for input co-ordinate system
  1259. * \param info_out pointer to pj_info struct for output co-ordinate system
  1260. *
  1261. * \return Return value from PROJ proj_trans() function
  1262. **/
  1263. int pj_do_proj(double *x, double *y,
  1264. const struct pj_info *info_in, const struct pj_info *info_out)
  1265. {
  1266. int ok;
  1267. #ifdef HAVE_PROJ_H
  1268. struct pj_info info_trans;
  1269. PJ_COORD c;
  1270. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  1271. return -1;
  1272. }
  1273. METERS_in = info_in->meters;
  1274. METERS_out = info_out->meters;
  1275. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1276. /* convert to radians */
  1277. c.lpzt.lam = (*x) / RAD_TO_DEG;
  1278. c.lpzt.phi = (*y) / RAD_TO_DEG;
  1279. c.lpzt.z = 0;
  1280. c.lpzt.t = 0;
  1281. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1282. ok = proj_errno(info_trans.pj);
  1283. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1284. /* convert to degrees */
  1285. *x = c.lp.lam * RAD_TO_DEG;
  1286. *y = c.lp.phi * RAD_TO_DEG;
  1287. }
  1288. else {
  1289. /* convert to map units */
  1290. *x = c.xy.x / METERS_out;
  1291. *y = c.xy.y / METERS_out;
  1292. }
  1293. }
  1294. else {
  1295. /* convert to meters */
  1296. c.xyzt.x = *x * METERS_in;
  1297. c.xyzt.y = *y * METERS_in;
  1298. c.xyzt.z = 0;
  1299. c.xyzt.t = 0;
  1300. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1301. ok = proj_errno(info_trans.pj);
  1302. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1303. /* convert to degrees */
  1304. *x = c.lp.lam * RAD_TO_DEG;
  1305. *y = c.lp.phi * RAD_TO_DEG;
  1306. }
  1307. else {
  1308. /* convert to map units */
  1309. *x = c.xy.x / METERS_out;
  1310. *y = c.xy.y / METERS_out;
  1311. }
  1312. }
  1313. proj_destroy(info_trans.pj);
  1314. if (ok < 0) {
  1315. G_warning(_("proj_trans() failed: %d"), ok);
  1316. }
  1317. #else
  1318. double u, v;
  1319. double h = 0.0;
  1320. METERS_in = info_in->meters;
  1321. METERS_out = info_out->meters;
  1322. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1323. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1324. u = (*x) / RAD_TO_DEG;
  1325. v = (*y) / RAD_TO_DEG;
  1326. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1327. *x = u * RAD_TO_DEG;
  1328. *y = v * RAD_TO_DEG;
  1329. }
  1330. else {
  1331. u = (*x) / RAD_TO_DEG;
  1332. v = (*y) / RAD_TO_DEG;
  1333. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1334. *x = u / METERS_out;
  1335. *y = v / METERS_out;
  1336. }
  1337. }
  1338. else {
  1339. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1340. u = *x * METERS_in;
  1341. v = *y * METERS_in;
  1342. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1343. *x = u * RAD_TO_DEG;
  1344. *y = v * RAD_TO_DEG;
  1345. }
  1346. else {
  1347. u = *x * METERS_in;
  1348. v = *y * METERS_in;
  1349. ok = pj_transform(info_in->pj, info_out->pj, 1, 0, &u, &v, &h);
  1350. *x = u / METERS_out;
  1351. *y = v / METERS_out;
  1352. }
  1353. }
  1354. if (ok < 0) {
  1355. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1356. }
  1357. #endif
  1358. return ok;
  1359. }
  1360. /**
  1361. * \brief Re-project an array of points between two co-ordinate systems with
  1362. * optional ellipsoidal height conversion
  1363. *
  1364. * This function takes pointers to two pj_info structures as arguments,
  1365. * and projects an array of points between the co-ordinate systems
  1366. * represented by them. Pointers to the three arrays of easting, northing,
  1367. * and ellipsoidal height of the point (this one may be NULL) are passed
  1368. * to the function; these will be overwritten by the co-ordinates of the
  1369. * re-projected points.
  1370. *
  1371. * \param count Number of points in the arrays to be transformed
  1372. * \param x Pointer to an array of type double containing easting or longitude
  1373. * \param y Pointer to an array of type double containing northing or latitude
  1374. * \param h Pointer to an array of type double containing ellipsoidal height.
  1375. * May be null in which case a two-dimensional re-projection will be
  1376. * done
  1377. * \param info_in pointer to pj_info struct for input co-ordinate system
  1378. * \param info_out pointer to pj_info struct for output co-ordinate system
  1379. *
  1380. * \return Return value from PROJ proj_trans() function
  1381. **/
  1382. int pj_do_transform(int count, double *x, double *y, double *h,
  1383. const struct pj_info *info_in, const struct pj_info *info_out)
  1384. {
  1385. int ok;
  1386. int i;
  1387. int has_h = 1;
  1388. #ifdef HAVE_PROJ_H
  1389. struct pj_info info_trans;
  1390. PJ_COORD c;
  1391. if (GPJ_init_transform(info_in, info_out, &info_trans) < 0) {
  1392. return -1;
  1393. }
  1394. METERS_in = info_in->meters;
  1395. METERS_out = info_out->meters;
  1396. if (h == NULL) {
  1397. h = G_malloc(sizeof *h * count);
  1398. /* they say memset is only guaranteed for chars ;-( */
  1399. for (i = 0; i < count; ++i)
  1400. h[i] = 0.0;
  1401. has_h = 0;
  1402. }
  1403. ok = 0;
  1404. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1405. c.lpzt.t = 0;
  1406. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1407. for (i = 0; i < count; i++) {
  1408. /* convert to radians */
  1409. c.lpzt.lam = x[i] / RAD_TO_DEG;
  1410. c.lpzt.phi = y[i] / RAD_TO_DEG;
  1411. c.lpzt.z = h[i];
  1412. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1413. if ((ok = proj_errno(info_trans.pj)) < 0)
  1414. break;
  1415. /* convert to degrees */
  1416. x[i] = c.lp.lam * RAD_TO_DEG;
  1417. y[i] = c.lp.phi * RAD_TO_DEG;
  1418. }
  1419. }
  1420. else {
  1421. for (i = 0; i < count; i++) {
  1422. /* convert to radians */
  1423. c.lpzt.lam = x[i] / RAD_TO_DEG;
  1424. c.lpzt.phi = y[i] / RAD_TO_DEG;
  1425. c.lpzt.z = h[i];
  1426. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1427. if ((ok = proj_errno(info_trans.pj)) < 0)
  1428. break;
  1429. /* convert to map units */
  1430. x[i] = c.xy.x / METERS_out;
  1431. y[i] = c.xy.y / METERS_out;
  1432. }
  1433. }
  1434. }
  1435. else {
  1436. c.xyzt.t = 0;
  1437. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1438. for (i = 0; i < count; i++) {
  1439. /* convert to meters */
  1440. c.xyzt.x = x[i] * METERS_in;
  1441. c.xyzt.y = y[i] * METERS_in;
  1442. c.xyzt.z = h[i];
  1443. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1444. if ((ok = proj_errno(info_trans.pj)) < 0)
  1445. break;
  1446. /* convert to degrees */
  1447. x[i] = c.lp.lam * RAD_TO_DEG;
  1448. y[i] = c.lp.phi * RAD_TO_DEG;
  1449. }
  1450. }
  1451. else {
  1452. for (i = 0; i < count; i++) {
  1453. /* convert to meters */
  1454. c.xyzt.x = x[i] * METERS_in;
  1455. c.xyzt.y = y[i] * METERS_in;
  1456. c.xyzt.z = h[i];
  1457. c = proj_trans(info_trans.pj, PJ_FWD, c);
  1458. if ((ok = proj_errno(info_trans.pj)) < 0)
  1459. break;
  1460. /* convert to map units */
  1461. x[i] = c.xy.x / METERS_out;
  1462. y[i] = c.xy.y / METERS_out;
  1463. }
  1464. }
  1465. }
  1466. if (!has_h)
  1467. G_free(h);
  1468. proj_destroy(info_trans.pj);
  1469. if (ok < 0) {
  1470. G_warning(_("proj_trans() failed: %d"), ok);
  1471. }
  1472. #else
  1473. METERS_in = info_in->meters;
  1474. METERS_out = info_out->meters;
  1475. if (h == NULL) {
  1476. h = G_malloc(sizeof *h * count);
  1477. /* they say memset is only guaranteed for chars ;-( */
  1478. for (i = 0; i < count; ++i)
  1479. h[i] = 0.0;
  1480. has_h = 0;
  1481. }
  1482. if (strncmp(info_in->proj, "ll", 2) == 0) {
  1483. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1484. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  1485. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1486. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  1487. }
  1488. else {
  1489. DIVIDE_LOOP(x, y, count, RAD_TO_DEG);
  1490. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1491. DIVIDE_LOOP(x, y, count, METERS_out);
  1492. }
  1493. }
  1494. else {
  1495. if (strncmp(info_out->proj, "ll", 2) == 0) {
  1496. MULTIPLY_LOOP(x, y, count, METERS_in);
  1497. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1498. MULTIPLY_LOOP(x, y, count, RAD_TO_DEG);
  1499. }
  1500. else {
  1501. MULTIPLY_LOOP(x, y, count, METERS_in);
  1502. ok = pj_transform(info_in->pj, info_out->pj, count, 1, x, y, h);
  1503. DIVIDE_LOOP(x, y, count, METERS_out);
  1504. }
  1505. }
  1506. if (!has_h)
  1507. G_free(h);
  1508. if (ok < 0) {
  1509. G_warning(_("pj_transform() failed: %s"), pj_strerrno(ok));
  1510. }
  1511. #endif
  1512. return ok;
  1513. }