convert.c 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130
  1. /*!
  2. \file lib/proj/convert.c
  3. \brief GProj Library - Functions for manipulating co-ordinate
  4. system representations
  5. (C) 2003-2018 by the GRASS Development Team
  6. This program is free software under the GNU General Public License
  7. (>=v2). Read the file COPYING that comes with GRASS for details.
  8. \author Paul Kelly, Frank Warmerdam, Markus Metz
  9. */
  10. #include <grass/config.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <math.h>
  15. #include <grass/gis.h>
  16. #include <grass/gprojects.h>
  17. #include <grass/glocale.h>
  18. #ifdef HAVE_OGR
  19. #include <cpl_csv.h>
  20. #include "local_proto.h"
  21. /* GRASS relative location of OGR co-ordinate system lookup tables */
  22. #define CSVDIR "/etc/proj/ogr_csv"
  23. static void DatumNameMassage(char **);
  24. #endif
  25. /* from proj-5.0.0/src/pj_units.c */
  26. struct gpj_units {
  27. char *id; /* units keyword */
  28. char *to_meter; /* multiply by value to get meters */
  29. char *name; /* comments */
  30. double factor; /* to_meter factor in actual numbers */
  31. };
  32. struct gpj_units
  33. gpj_units[] = {
  34. {"km", "1000.", "Kilometer", 1000.0},
  35. {"m", "1.", "Meter", 1.0},
  36. {"dm", "1/10", "Decimeter", 0.1},
  37. {"cm", "1/100", "Centimeter", 0.01},
  38. {"mm", "1/1000", "Millimeter", 0.001},
  39. {"kmi", "1852.0", "International Nautical Mile", 1852.0},
  40. {"in", "0.0254", "International Inch", 0.0254},
  41. {"ft", "0.3048", "International Foot", 0.3048},
  42. {"yd", "0.9144", "International Yard", 0.9144},
  43. {"mi", "1609.344", "International Statute Mile", 1609.344},
  44. {"fath", "1.8288", "International Fathom", 1.8288},
  45. {"ch", "20.1168", "International Chain", 20.1168},
  46. {"link", "0.201168", "International Link", 0.201168},
  47. {"us-in", "1./39.37", "U.S. Surveyor's Inch", 0.0254},
  48. {"us-ft", "0.304800609601219", "U.S. Surveyor's Foot", 0.304800609601219},
  49. {"us-yd", "0.914401828803658", "U.S. Surveyor's Yard", 0.914401828803658},
  50. {"us-ch", "20.11684023368047", "U.S. Surveyor's Chain", 20.11684023368047},
  51. {"us-mi", "1609.347218694437", "U.S. Surveyor's Statute Mile", 1609.347218694437},
  52. {"ind-yd", "0.91439523", "Indian Yard", 0.91439523},
  53. {"ind-ft", "0.30479841", "Indian Foot", 0.30479841},
  54. {"ind-ch", "20.11669506", "Indian Chain", 20.11669506},
  55. {NULL, NULL, NULL, 0.0}
  56. };
  57. static char *grass_to_wkt(const struct Key_Value *proj_info,
  58. const struct Key_Value *proj_units,
  59. const struct Key_Value *proj_epsg,
  60. int esri_style, int prettify)
  61. {
  62. #ifdef HAVE_OGR
  63. OGRSpatialReferenceH hSRS;
  64. char *wkt, *local_wkt;
  65. hSRS = GPJ_grass_to_osr2(proj_info, proj_units, proj_epsg);
  66. if (hSRS == NULL)
  67. return NULL;
  68. if (esri_style)
  69. OSRMorphToESRI(hSRS);
  70. if (prettify)
  71. OSRExportToPrettyWkt(hSRS, &wkt, 0);
  72. else
  73. OSRExportToWkt(hSRS, &wkt);
  74. local_wkt = G_store(wkt);
  75. CPLFree(wkt);
  76. OSRDestroySpatialReference(hSRS);
  77. return local_wkt;
  78. #else
  79. G_warning(_("GRASS is not compiled with OGR support"));
  80. return NULL;
  81. #endif
  82. }
  83. /*!
  84. * \brief Converts a GRASS co-ordinate system representation to WKT style.
  85. *
  86. * Takes a GRASS co-ordinate system as specified by two sets of
  87. * key/value pairs derived from the PROJ_INFO and PROJ_UNITS files,
  88. * and converts it to the 'Well Known Text' format.
  89. *
  90. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  91. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  92. * \param esri_style boolean Output ESRI-style WKT (Use OSRMorphToESRI()
  93. * function provided by OGR library)
  94. * \param prettify boolean Use linebreaks and indents to 'prettify' output
  95. * WKT string (Use OSRExportToPrettyWkt() function in OGR)
  96. *
  97. * \return Pointer to a string containing the co-ordinate system in
  98. * WKT format
  99. * \return NULL on error
  100. */
  101. char *GPJ_grass_to_wkt(const struct Key_Value *proj_info,
  102. const struct Key_Value *proj_units,
  103. int esri_style, int prettify)
  104. {
  105. return grass_to_wkt(proj_info, proj_units, NULL, esri_style, prettify);
  106. }
  107. /*!
  108. * \brief Converts a GRASS co-ordinate system representation to WKT
  109. * style. EPSG code is preferred if available.
  110. *
  111. * Takes a GRASS co-ordinate system as specified key/value pairs
  112. * derived from the PROJ_EPSG file. TOWGS84 parameter is scanned
  113. * from PROJ_INFO file and appended to co-ordinate system definition
  114. * imported from EPSG code by GDAL library. PROJ_UNITS file is
  115. * ignored. The function converts it to the 'Well Known Text' format.
  116. *
  117. * \todo Merge with GPJ_grass_to_wkt() in GRASS 8.
  118. *
  119. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  120. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  121. * \param proj_epsg Set of GRASS PROJ_EPSG key/value pairs
  122. * \param esri_style boolean Output ESRI-style WKT (Use OSRMorphToESRI()
  123. * function provided by OGR library)
  124. * \param prettify boolean Use linebreaks and indents to 'prettify' output
  125. * WKT string (Use OSRExportToPrettyWkt() function in OGR)
  126. *
  127. * \return Pointer to a string containing the co-ordinate system in
  128. * WKT format
  129. * \return NULL on error
  130. */
  131. char *GPJ_grass_to_wkt2(const struct Key_Value *proj_info,
  132. const struct Key_Value *proj_units,
  133. const struct Key_Value *proj_epsg,
  134. int esri_style, int prettify)
  135. {
  136. return grass_to_wkt(proj_info, proj_units, proj_epsg, esri_style, prettify);
  137. }
  138. #ifdef HAVE_OGR
  139. /*!
  140. * \brief Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object.
  141. *
  142. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  143. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  144. *
  145. * \return OGRSpatialReferenceH object representing the co-ordinate system
  146. * defined by proj_info and proj_units or NULL if it fails
  147. */
  148. OGRSpatialReferenceH GPJ_grass_to_osr(const struct Key_Value * proj_info,
  149. const struct Key_Value * proj_units)
  150. {
  151. struct pj_info pjinfo;
  152. char *proj4, *proj4mod, *wkt, *modwkt, *startmod, *lastpart;
  153. OGRSpatialReferenceH hSRS, hSRS2;
  154. OGRErr errcode;
  155. struct gpj_datum dstruct;
  156. struct gpj_ellps estruct;
  157. size_t len;
  158. const char *ellpskv, *unit, *unfact;
  159. char *ellps, *ellpslong, *datum, *params, *towgs84, *datumlongname,
  160. *start, *end;
  161. const char *sysname, *osrunit, *osrunfact;
  162. double a, es, rf;
  163. int haveparams = 0;
  164. if ((proj_info == NULL) || (proj_units == NULL))
  165. return NULL;
  166. hSRS = OSRNewSpatialReference(NULL);
  167. /* create PROJ structure from GRASS key/value pairs */
  168. if (pj_get_kv(&pjinfo, proj_info, proj_units) < 0) {
  169. G_warning(_("Unable parse GRASS PROJ_INFO file"));
  170. return NULL;
  171. }
  172. /* fetch the PROJ definition */
  173. /* TODO: get the PROJ definition as used by pj_get_kv() */
  174. if ((proj4 = pjinfo.def) == NULL) {
  175. G_warning(_("Unable get PROJ.4-style parameter string"));
  176. return NULL;
  177. }
  178. #ifdef HAVE_PROJ_H
  179. proj_destroy(pjinfo.pj);
  180. #else
  181. pj_free(pjinfo.pj);
  182. #endif
  183. unit = G_find_key_value("unit", proj_units);
  184. unfact = G_find_key_value("meters", proj_units);
  185. if (unfact != NULL && (strcmp(pjinfo.proj, "ll") != 0))
  186. G_asprintf(&proj4mod, "%s +to_meter=%s", proj4, unfact);
  187. else
  188. proj4mod = G_store(proj4);
  189. /* create GDAL OSR from proj string */
  190. if ((errcode = OSRImportFromProj4(hSRS, proj4mod)) != OGRERR_NONE) {
  191. G_warning(_("OGR can't parse PROJ.4-style parameter string: "
  192. "%s (OGR Error code was %d)"), proj4mod, errcode);
  193. return NULL;
  194. }
  195. G_free(proj4mod);
  196. /* this messes up PROJCS versus GEOGCS!
  197. sysname = G_find_key_value("name", proj_info);
  198. if (sysname)
  199. OSRSetProjCS(hSRS, sysname);
  200. */
  201. if ((errcode = OSRExportToWkt(hSRS, &wkt)) != OGRERR_NONE) {
  202. G_warning(_("OGR can't get WKT-style parameter string "
  203. "(OGR Error code was %d)"), errcode);
  204. return NULL;
  205. }
  206. ellpskv = G_find_key_value("ellps", proj_info);
  207. GPJ__get_ellipsoid_params(proj_info, &a, &es, &rf);
  208. haveparams = GPJ__get_datum_params(proj_info, &datum, &params);
  209. if (ellpskv != NULL)
  210. ellps = G_store(ellpskv);
  211. else
  212. ellps = NULL;
  213. if ((datum == NULL) || (GPJ_get_datum_by_name(datum, &dstruct) < 0)) {
  214. datumlongname = G_store("unknown");
  215. if (ellps == NULL)
  216. ellps = G_store("unnamed");
  217. }
  218. else {
  219. datumlongname = G_store(dstruct.longname);
  220. if (ellps == NULL)
  221. ellps = G_store(dstruct.ellps);
  222. GPJ_free_datum(&dstruct);
  223. }
  224. G_debug(3, "GPJ_grass_to_osr: datum: <%s>", datum);
  225. G_free(datum);
  226. if (GPJ_get_ellipsoid_by_name(ellps, &estruct) > 0) {
  227. ellpslong = G_store(estruct.longname);
  228. DatumNameMassage(&ellpslong);
  229. GPJ_free_ellps(&estruct);
  230. }
  231. else
  232. ellpslong = G_store(ellps);
  233. startmod = strstr(wkt, "GEOGCS");
  234. lastpart = strstr(wkt, "PRIMEM");
  235. len = strlen(wkt) - strlen(startmod);
  236. wkt[len] = '\0';
  237. if (haveparams == 2) {
  238. /* Only put datum params into the WKT if they were specifically
  239. * specified in PROJ_INFO */
  240. char *paramkey, *paramvalue;
  241. paramkey = strtok(params, "=");
  242. paramvalue = params + strlen(paramkey) + 1;
  243. if (G_strcasecmp(paramkey, "towgs84") == 0)
  244. G_asprintf(&towgs84, ",TOWGS84[%s]", paramvalue);
  245. else
  246. towgs84 = G_store("");
  247. G_free(params);
  248. }
  249. else
  250. towgs84 = G_store("");
  251. sysname = OSRGetAttrValue(hSRS, "PROJCS", 0);
  252. if (sysname == NULL) {
  253. /* Not a projected co-ordinate system */
  254. start = G_store("");
  255. end = G_store("");
  256. }
  257. else {
  258. if ((strcmp(sysname, "unnamed") == 0) &&
  259. (G_find_key_value("name", proj_info) != NULL))
  260. G_asprintf(&start, "PROJCS[\"%s\",",
  261. G_find_key_value("name", proj_info));
  262. else
  263. start = G_store(wkt);
  264. osrunit = OSRGetAttrValue(hSRS, "UNIT", 0);
  265. osrunfact = OSRGetAttrValue(hSRS, "UNIT", 1);
  266. if ((unfact == NULL) || (G_strcasecmp(osrunit, "unknown") != 0))
  267. end = G_store("");
  268. else {
  269. char *buff;
  270. double unfactf = atof(unfact);
  271. G_asprintf(&buff, ",UNIT[\"%s\",", osrunit);
  272. startmod = strstr(lastpart, buff);
  273. len = strlen(lastpart) - strlen(startmod);
  274. lastpart[len] = '\0';
  275. G_free(buff);
  276. if (unit == NULL)
  277. unit = "unknown";
  278. G_asprintf(&end, ",UNIT[\"%s\",%.16g]]", unit, unfactf);
  279. }
  280. }
  281. OSRDestroySpatialReference(hSRS);
  282. G_asprintf(&modwkt,
  283. "%sGEOGCS[\"%s\",DATUM[\"%s\",SPHEROID[\"%s\",%.16g,%.16g]%s],%s%s",
  284. start, ellps, datumlongname, ellpslong, a, rf, towgs84,
  285. lastpart, end);
  286. hSRS2 = OSRNewSpatialReference(modwkt);
  287. G_free(modwkt);
  288. CPLFree(wkt);
  289. G_free(start);
  290. G_free(ellps);
  291. G_free(datumlongname);
  292. G_free(ellpslong);
  293. G_free(towgs84);
  294. G_free(end);
  295. return hSRS2;
  296. }
  297. /*!
  298. * \brief Converts a GRASS co-ordinate system to an
  299. * OGRSpatialReferenceH object. EPSG code is preferred if available.
  300. *
  301. * The co-ordinate system definition is imported from EPSG (by GDAL)
  302. * definition if available. TOWGS84 parameter is scanned from
  303. * PROJ_INFO file and appended to co-ordinate system definition. If
  304. * EPSG code is not available, PROJ_INFO file is used as
  305. * GPJ_grass_to_osr() does.
  306. * \todo Merge with GPJ_grass_to_osr() in GRASS 8.
  307. *
  308. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  309. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  310. * \param proj_epsg Set of GRASS PROJ_EPSG key/value pairs
  311. *
  312. * \return OGRSpatialReferenceH object representing the co-ordinate system
  313. * defined by proj_info and proj_units or NULL if it fails
  314. */
  315. OGRSpatialReferenceH GPJ_grass_to_osr2(const struct Key_Value * proj_info,
  316. const struct Key_Value * proj_units,
  317. const struct Key_Value * proj_epsg)
  318. {
  319. int epsgcode = 0;
  320. if (proj_epsg) {
  321. const char *epsgstr = G_find_key_value("epsg", proj_epsg);
  322. if (epsgstr)
  323. epsgcode = atoi(epsgstr);
  324. }
  325. if (epsgcode) {
  326. const char *towgs84;
  327. OGRSpatialReferenceH hSRS;
  328. hSRS = OSRNewSpatialReference(NULL);
  329. OSRImportFromEPSG(hSRS, epsgcode);
  330. /* take +towgs84 from projinfo file if defined */
  331. towgs84 = G_find_key_value("towgs84", proj_info);
  332. if (towgs84) {
  333. char **tokens;
  334. int i;
  335. double df[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
  336. tokens = G_tokenize(towgs84, ",");
  337. for (i = 0; i < G_number_of_tokens(tokens); i++)
  338. df[i] = atof(tokens[i]);
  339. G_free_tokens(tokens);
  340. OSRSetTOWGS84(hSRS, df[0], df[1], df[2], df[3], df[4], df[5], df[6]);
  341. }
  342. return hSRS;
  343. }
  344. return GPJ_grass_to_osr(proj_info, proj_units);
  345. }
  346. /*!
  347. * \brief Converts an OGRSpatialReferenceH object to a GRASS co-ordinate system.
  348. *
  349. * \param cellhd Pointer to a GRASS Cell_head structure that will have its
  350. * projection-related members populated with appropriate values
  351. * \param projinfo Pointer to a pointer which will have a GRASS Key_Value
  352. * structure allocated containing a set of GRASS PROJ_INFO values
  353. * \param projunits Pointer to a pointer which will have a GRASS Key_Value
  354. * structure allocated containing a set of GRASS PROJ_UNITS values
  355. * \param hSRS OGRSpatialReferenceH object containing the co-ordinate
  356. * system to be converted
  357. * \param datumtrans Index number of datum parameter set to use, 0 to leave
  358. * unspecified
  359. *
  360. * \return 2 if a projected or lat/long co-ordinate system has been
  361. * defined
  362. * \return 1 if an unreferenced XY co-ordinate system has
  363. * been defined
  364. */
  365. int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
  366. struct Key_Value **projunits, OGRSpatialReferenceH hSRS1,
  367. int datumtrans)
  368. {
  369. struct Key_Value *temp_projinfo, *temp_projinfo_ext;
  370. char *pszProj4 = NULL, *pszRemaining;
  371. char *pszProj = NULL;
  372. const char *pszProjCS = NULL;
  373. char *datum = NULL;
  374. char *proj4_unit = NULL;
  375. struct gpj_datum dstruct;
  376. const char *ograttr;
  377. OGRSpatialReferenceH hSRS;
  378. int use_proj_extension;
  379. *projinfo = NULL;
  380. *projunits = NULL;
  381. hSRS = hSRS1;
  382. if (hSRS == NULL)
  383. goto default_to_xy;
  384. /* Set finder function for locating OGR csv co-ordinate system tables */
  385. /* SetCSVFilenameHook(GPJ_set_csv_loc); */
  386. /* Hopefully this doesn't do any harm if it wasn't in ESRI format
  387. * to start with... */
  388. OSRMorphFromESRI(hSRS);
  389. *projinfo = G_create_key_value();
  390. use_proj_extension = 0;
  391. /* use proj4 definition from EXTENSION attribute if existing */
  392. ograttr = OSRGetAttrValue(hSRS, "EXTENSION", 0);
  393. if (ograttr && *ograttr && strcmp(ograttr, "PROJ4") == 0) {
  394. ograttr = OSRGetAttrValue(hSRS, "EXTENSION", 1);
  395. G_debug(3, "proj4 extension:");
  396. G_debug(3, "%s", ograttr);
  397. if (ograttr && *ograttr) {
  398. char *proj4ext;
  399. OGRSpatialReferenceH hSRS2;
  400. hSRS2 = OSRNewSpatialReference(NULL);
  401. proj4ext = G_store(ograttr);
  402. /* test */
  403. if (OSRImportFromProj4(hSRS2, proj4ext) != OGRERR_NONE) {
  404. G_warning(_("Updating spatial reference with embedded proj4 definition failed. "
  405. "Proj4 definition: <%s>"), proj4ext);
  406. OSRDestroySpatialReference(hSRS2);
  407. }
  408. else {
  409. /* use new OGR spatial reference defined with embedded proj4 string */
  410. /* TODO: replace warning with important_message once confirmed working */
  411. G_warning(_("Updating spatial reference with embedded proj4 definition"));
  412. /* -------------------------------------------------------------------- */
  413. /* Derive the user name for the coordinate system. */
  414. /* -------------------------------------------------------------------- */
  415. pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0);
  416. if (!pszProjCS)
  417. pszProjCS = OSRGetAttrValue(hSRS, "GEOGCS", 0);
  418. if (pszProjCS) {
  419. G_set_key_value("name", pszProjCS, *projinfo);
  420. }
  421. else if (pszProj) {
  422. char path[4095];
  423. char name[80];
  424. /* use name of the projection as name for the coordinate system */
  425. sprintf(path, "%s/etc/proj/projections", G_gisbase());
  426. if (G_lookup_key_value_from_file(path, pszProj, name, sizeof(name)) >
  427. 0)
  428. G_set_key_value("name", name, *projinfo);
  429. else
  430. G_set_key_value("name", pszProj, *projinfo);
  431. }
  432. /* the original hSRS1 is left as is, ok? */
  433. hSRS = hSRS2;
  434. use_proj_extension = 1;
  435. }
  436. G_free(proj4ext);
  437. }
  438. }
  439. /* -------------------------------------------------------------------- */
  440. /* Set cellhd for well known coordinate systems. */
  441. /* -------------------------------------------------------------------- */
  442. if (!OSRIsGeographic(hSRS) && !OSRIsProjected(hSRS))
  443. goto default_to_xy;
  444. if (cellhd) {
  445. int bNorth;
  446. if (OSRIsGeographic(hSRS)) {
  447. cellhd->proj = PROJECTION_LL;
  448. cellhd->zone = 0;
  449. }
  450. else if (OSRGetUTMZone(hSRS, &bNorth) != 0) {
  451. cellhd->proj = PROJECTION_UTM;
  452. cellhd->zone = OSRGetUTMZone(hSRS, &bNorth);
  453. if (!bNorth)
  454. cellhd->zone *= -1;
  455. }
  456. else {
  457. cellhd->proj = PROJECTION_OTHER;
  458. cellhd->zone = 0;
  459. }
  460. }
  461. /* -------------------------------------------------------------------- */
  462. /* Get the coordinate system definition in PROJ.4 format. */
  463. /* -------------------------------------------------------------------- */
  464. if (OSRExportToProj4(hSRS, &pszProj4) != OGRERR_NONE)
  465. goto default_to_xy;
  466. /* -------------------------------------------------------------------- */
  467. /* Parse the PROJ.4 string into key/value pairs. Do a bit of */
  468. /* extra work to "GRASSify" the result. */
  469. /* -------------------------------------------------------------------- */
  470. temp_projinfo = G_create_key_value();
  471. temp_projinfo_ext = G_create_key_value();
  472. /* Create "local" copy of proj4 string so we can modify and free it
  473. * using GRASS functions */
  474. pszRemaining = G_store(pszProj4);
  475. CPLFree(pszProj4);
  476. pszProj4 = pszRemaining;
  477. while ((pszRemaining = strstr(pszRemaining, "+")) != NULL) {
  478. char *pszToken, *pszValue;
  479. pszRemaining++;
  480. /* Advance pszRemaining to end of this token[=value] pair */
  481. pszToken = pszRemaining;
  482. while (*pszRemaining != ' ' && *pszRemaining != '\0')
  483. pszRemaining++;
  484. if (*pszRemaining == ' ') {
  485. *pszRemaining = '\0';
  486. pszRemaining++;
  487. }
  488. /* parse token, and value */
  489. if (strstr(pszToken, "=") != NULL) {
  490. pszValue = strstr(pszToken, "=");
  491. *pszValue = '\0';
  492. pszValue++;
  493. }
  494. else
  495. pszValue = "defined";
  496. /* projection name */
  497. if (G_strcasecmp(pszToken, "proj") == 0) {
  498. /* The ll projection is known as longlat in PROJ.4 */
  499. if (G_strcasecmp(pszValue, "longlat") == 0)
  500. pszValue = "ll";
  501. pszProj = pszValue;
  502. }
  503. /* Ellipsoid and datum handled separately below */
  504. if (G_strcasecmp(pszToken, "ellps") == 0
  505. || G_strcasecmp(pszToken, "a") == 0
  506. || G_strcasecmp(pszToken, "b") == 0
  507. || G_strcasecmp(pszToken, "es") == 0
  508. || G_strcasecmp(pszToken, "rf") == 0
  509. || G_strcasecmp(pszToken, "datum") == 0) {
  510. G_set_key_value(pszToken, pszValue, temp_projinfo_ext);
  511. continue;
  512. }
  513. /* We will handle units separately */
  514. if (G_strcasecmp(pszToken, "to_meter") == 0)
  515. continue;
  516. if (G_strcasecmp(pszToken, "units") == 0) {
  517. proj4_unit = G_store(pszValue);
  518. continue;
  519. }
  520. G_set_key_value(pszToken, pszValue, temp_projinfo);
  521. }
  522. if (!pszProj)
  523. G_warning(_("No projection name! Projection parameters likely to be meaningless."));
  524. /* -------------------------------------------------------------------- */
  525. /* Derive the user name for the coordinate system. */
  526. /* -------------------------------------------------------------------- */
  527. if (!G_find_key_value("name", *projinfo)) {
  528. pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0);
  529. if (!pszProjCS)
  530. pszProjCS = OSRGetAttrValue(hSRS, "GEOGCS", 0);
  531. if (pszProjCS) {
  532. G_set_key_value("name", pszProjCS, *projinfo);
  533. }
  534. else if (pszProj) {
  535. char path[4095];
  536. char name[80];
  537. /* use name of the projection as name for the coordinate system */
  538. sprintf(path, "%s/etc/proj/projections", G_gisbase());
  539. if (G_lookup_key_value_from_file(path, pszProj, name, sizeof(name)) >
  540. 0)
  541. G_set_key_value("name", name, *projinfo);
  542. else
  543. G_set_key_value("name", pszProj, *projinfo);
  544. }
  545. }
  546. /* -------------------------------------------------------------------- */
  547. /* Find the GRASS datum name and choose parameters either */
  548. /* interactively or not. */
  549. /* -------------------------------------------------------------------- */
  550. {
  551. const char *pszDatumNameConst;
  552. struct datum_list *list, *listhead;
  553. char *dum1, *dum2, *pszDatumName;
  554. int paramspresent =
  555. GPJ__get_datum_params(temp_projinfo, &dum1, &dum2);
  556. if (!use_proj_extension)
  557. pszDatumNameConst = OSRGetAttrValue(hSRS, "DATUM", 0);
  558. else
  559. pszDatumNameConst = G_find_key_value("datum", temp_projinfo_ext);
  560. if (pszDatumNameConst) {
  561. /* Need to make a new copy of the string so we don't mess
  562. * around with the memory inside the OGRSpatialReferenceH? */
  563. pszDatumName = G_store(pszDatumNameConst);
  564. DatumNameMassage(&pszDatumName);
  565. G_debug(3, "GPJ_osr_to_grass: pszDatumNameConst: <%s>", pszDatumName);
  566. list = listhead = read_datum_table();
  567. while (list != NULL) {
  568. if (G_strcasecmp(pszDatumName, list->longname) == 0) {
  569. datum = G_store(list->name);
  570. break;
  571. }
  572. list = list->next;
  573. }
  574. free_datum_list(listhead);
  575. if (datum == NULL) {
  576. if (paramspresent < 2)
  577. /* Only give warning if no parameters present */
  578. G_debug(1, "Datum <%s> not recognised by GRASS and no parameters found",
  579. pszDatumName);
  580. }
  581. else {
  582. G_set_key_value("datum", datum, *projinfo);
  583. if (paramspresent < 2) {
  584. /* If no datum parameters were imported from the OSR
  585. * object then we should use the set specified by datumtrans */
  586. char *params, *chosenparams = NULL;
  587. int paramsets;
  588. paramsets =
  589. GPJ_get_default_datum_params_by_name(datum, &params);
  590. if (paramsets < 0)
  591. G_debug(1, "Datum <%s> apparently recognised by GRASS but no parameters found. "
  592. "You may want to look into this.", datum);
  593. else if (datumtrans > paramsets) {
  594. G_debug(1, "Invalid transformation number %d; valid range is 1 to %d. "
  595. "Leaving datum transform parameters unspecified.",
  596. datumtrans, paramsets);
  597. datumtrans = 0;
  598. }
  599. if (paramsets > 0) {
  600. struct gpj_datum_transform_list *tlist, *old;
  601. tlist = GPJ_get_datum_transform_by_name(datum);
  602. if (tlist != NULL) {
  603. do {
  604. if (tlist->count == datumtrans)
  605. chosenparams = G_store(tlist->params);
  606. old = tlist;
  607. tlist = tlist->next;
  608. GPJ_free_datum_transform(old);
  609. } while (tlist != NULL);
  610. }
  611. }
  612. if (chosenparams != NULL) {
  613. char *paramkey, *paramvalue;
  614. paramkey = strtok(chosenparams, "=");
  615. paramvalue = chosenparams + strlen(paramkey) + 1;
  616. G_set_key_value(paramkey, paramvalue, *projinfo);
  617. G_free(chosenparams);
  618. }
  619. if (paramsets > 0)
  620. G_free(params);
  621. }
  622. }
  623. G_free(pszDatumName);
  624. }
  625. }
  626. /* -------------------------------------------------------------------- */
  627. /* Determine an appropriate GRASS ellipsoid name if possible, or */
  628. /* else just put a and es values into PROJ_INFO */
  629. /* -------------------------------------------------------------------- */
  630. if ((datum != NULL) && (GPJ_get_datum_by_name(datum, &dstruct) > 0)) {
  631. /* Use ellps name associated with datum */
  632. G_set_key_value("ellps", dstruct.ellps, *projinfo);
  633. GPJ_free_datum(&dstruct);
  634. G_free(datum);
  635. }
  636. else if (!use_proj_extension) {
  637. /* If we can't determine the ellipsoid from the datum, derive it
  638. * directly from "SPHEROID" parameters in WKT */
  639. const char *pszSemiMajor = OSRGetAttrValue(hSRS, "SPHEROID", 1);
  640. const char *pszInvFlat = OSRGetAttrValue(hSRS, "SPHEROID", 2);
  641. if (pszSemiMajor != NULL && pszInvFlat != NULL) {
  642. char *ellps = NULL;
  643. struct ellps_list *list, *listhead;
  644. double a = atof(pszSemiMajor), invflat = atof(pszInvFlat), flat;
  645. double es;
  646. /* Allow for incorrect WKT describing a sphere where InvFlat
  647. * is given as 0 rather than inf */
  648. if (invflat > 0)
  649. flat = 1 / invflat;
  650. else
  651. flat = 0;
  652. es = flat * (2.0 - flat);
  653. list = listhead = read_ellipsoid_table(0);
  654. while (list != NULL) {
  655. /* Try and match a and es against GRASS defined ellipsoids;
  656. * accept first one that matches. These numbers were found
  657. * by trial and error and could be fine-tuned, or possibly
  658. * a direct comparison of IEEE floating point values used. */
  659. if ((a == list->a || fabs(a - list->a) < 0.1 || fabs(1 - a / list->a) < 0.0000001) &&
  660. ((es == 0 && list->es == 0) ||
  661. /* Special case for sphere */
  662. (invflat == list->rf || fabs(invflat - list->rf) < 0.0000001))) {
  663. ellps = G_store(list->name);
  664. break;
  665. }
  666. list = list->next;
  667. }
  668. if (listhead != NULL)
  669. free_ellps_list(listhead);
  670. if (ellps == NULL) {
  671. /* If we weren't able to find a matching ellps name, set
  672. * a and es values directly from WKT-derived data */
  673. char es_str[100];
  674. G_set_key_value("a", (char *)pszSemiMajor, *projinfo);
  675. sprintf(es_str, "%.16g", es);
  676. G_set_key_value("es", es_str, *projinfo);
  677. }
  678. else {
  679. /* else specify the GRASS ellps name for readability */
  680. G_set_key_value("ellps", ellps, *projinfo);
  681. G_free(ellps);
  682. }
  683. }
  684. }
  685. else if (use_proj_extension) {
  686. double a, es, rf;
  687. if (GPJ__get_ellipsoid_params(temp_projinfo_ext, &a, &es, &rf)) {
  688. char parmstr[100];
  689. sprintf(parmstr, "%.16g", a);
  690. G_set_key_value("a", parmstr, *projinfo);
  691. sprintf(parmstr, "%.16g", es);
  692. G_set_key_value("es", parmstr, *projinfo);
  693. }
  694. }
  695. /* -------------------------------------------------------------------- */
  696. /* Finally append the detailed projection parameters to the end */
  697. /* -------------------------------------------------------------------- */
  698. {
  699. int i;
  700. for (i = 0; i < temp_projinfo->nitems; i++)
  701. G_set_key_value(temp_projinfo->key[i],
  702. temp_projinfo->value[i], *projinfo);
  703. G_free_key_value(temp_projinfo);
  704. }
  705. G_free_key_value(temp_projinfo_ext);
  706. G_free(pszProj4);
  707. /* -------------------------------------------------------------------- */
  708. /* Set the linear units. */
  709. /* -------------------------------------------------------------------- */
  710. *projunits = G_create_key_value();
  711. if (OSRIsGeographic(hSRS)) {
  712. /* We assume degrees ... someday we will be wrong! */
  713. G_set_key_value("unit", "degree", *projunits);
  714. G_set_key_value("units", "degrees", *projunits);
  715. G_set_key_value("meters", "1.0", *projunits);
  716. }
  717. else {
  718. char szFormatBuf[256];
  719. char *pszUnitsName = NULL;
  720. double dfToMeters;
  721. char *pszUnitsPlural, *pszStringEnd;
  722. dfToMeters = OSRGetLinearUnits(hSRS, &pszUnitsName);
  723. /* the unit name can be arbitrary: the following can be the same
  724. * us-ft (proj.4 keyword)
  725. * U.S. Surveyor's Foot (proj.4 name)
  726. * US survey foot (WKT)
  727. * Foot_US (WKT)
  728. */
  729. /* Workaround for the most obvious case when unit name is unknown */
  730. if ((G_strcasecmp(pszUnitsName, "unknown") == 0) &&
  731. (dfToMeters == 1.))
  732. G_asprintf(&pszUnitsName, "meter");
  733. if ((G_strcasecmp(pszUnitsName, "metre") == 0))
  734. G_asprintf(&pszUnitsName, "meter");
  735. if ((G_strcasecmp(pszUnitsName, "kilometre") == 0))
  736. G_asprintf(&pszUnitsName, "kilometer");
  737. if (dfToMeters != 1. && proj4_unit) {
  738. int i;
  739. i = 0;
  740. while (gpj_units[i].id != NULL) {
  741. if (strcmp(proj4_unit, gpj_units[i].id) == 0) {
  742. G_asprintf(&pszUnitsName, "%s", gpj_units[i].name);
  743. break;
  744. }
  745. i++;
  746. }
  747. }
  748. G_set_key_value("unit", pszUnitsName, *projunits);
  749. /* Attempt at plural formation (WKT format doesn't store plural
  750. * form of unit name) */
  751. pszUnitsPlural = G_malloc(strlen(pszUnitsName) + 3);
  752. strcpy(pszUnitsPlural, pszUnitsName);
  753. pszStringEnd = pszUnitsPlural + strlen(pszUnitsPlural) - 4;
  754. if (G_strcasecmp(pszStringEnd, "foot") == 0) {
  755. /* Special case for foot - change two o's to e's */
  756. pszStringEnd[1] = 'e';
  757. pszStringEnd[2] = 'e';
  758. }
  759. else if (G_strcasecmp(pszStringEnd, "inch") == 0) {
  760. /* Special case for inch - add es */
  761. pszStringEnd[4] = 'e';
  762. pszStringEnd[5] = 's';
  763. pszStringEnd[6] = '\0';
  764. }
  765. else {
  766. /* For anything else add an s at the end */
  767. pszStringEnd[4] = 's';
  768. pszStringEnd[5] = '\0';
  769. }
  770. G_set_key_value("units", pszUnitsPlural, *projunits);
  771. G_free(pszUnitsPlural);
  772. sprintf(szFormatBuf, "%.16g", dfToMeters);
  773. G_set_key_value("meters", szFormatBuf, *projunits);
  774. }
  775. if (hSRS != hSRS1)
  776. OSRDestroySpatialReference(hSRS);
  777. return 2;
  778. /* -------------------------------------------------------------------- */
  779. /* Fallback to returning an ungeoreferenced definition. */
  780. /* -------------------------------------------------------------------- */
  781. default_to_xy:
  782. if (cellhd != NULL) {
  783. cellhd->proj = PROJECTION_XY;
  784. cellhd->zone = 0;
  785. }
  786. if (*projinfo)
  787. G_free_key_value(*projinfo);
  788. *projinfo = NULL;
  789. *projunits = NULL;
  790. if (hSRS != NULL && hSRS != hSRS1)
  791. OSRDestroySpatialReference(hSRS);
  792. return 1;
  793. }
  794. #endif
  795. /*!
  796. * \brief Converts a WKT projection description to a GRASS co-ordinate system.
  797. *
  798. * \param cellhd Pointer to a GRASS Cell_head structure that will have its
  799. * projection-related members populated with appropriate values
  800. * \param projinfo Pointer to a pointer which will have a GRASS Key_Value
  801. * structure allocated containing a set of GRASS PROJ_INFO values
  802. * \param projunits Pointer to a pointer which will have a GRASS Key_Value
  803. * structure allocated containing a set of GRASS PROJ_UNITS values
  804. * \param wkt Well-known Text (WKT) description of the co-ordinate
  805. * system to be converted
  806. * \param datumtrans Index number of datum parameter set to use, 0 to leave
  807. * unspecified
  808. *
  809. * \return 2 if a projected or lat/long co-ordinate system has been
  810. * defined
  811. * \return 1 if an unreferenced XY co-ordinate system has
  812. * been defined
  813. * \return -1 on error
  814. */
  815. int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
  816. struct Key_Value **projunits, const char *wkt,
  817. int datumtrans)
  818. {
  819. #ifdef HAVE_OGR
  820. int retval;
  821. if (wkt == NULL)
  822. retval =
  823. GPJ_osr_to_grass(cellhd, projinfo, projunits, NULL, datumtrans);
  824. else {
  825. OGRSpatialReferenceH hSRS;
  826. /* Set finder function for locating OGR csv co-ordinate system tables */
  827. /* SetCSVFilenameHook(GPJ_set_csv_loc); */
  828. hSRS = OSRNewSpatialReference(wkt);
  829. retval =
  830. GPJ_osr_to_grass(cellhd, projinfo, projunits, hSRS, datumtrans);
  831. OSRDestroySpatialReference(hSRS);
  832. }
  833. return retval;
  834. #else
  835. return -1;
  836. #endif
  837. }
  838. #ifdef HAVE_OGR
  839. /* GPJ_set_csv_loc()
  840. * 'finder function' for use with OGR SetCSVFilenameHook() function */
  841. const char *GPJ_set_csv_loc(const char *name)
  842. {
  843. const char *gisbase = G_gisbase();
  844. static char *buf = NULL;
  845. if (buf != NULL)
  846. G_free(buf);
  847. G_asprintf(&buf, "%s%s/%s", gisbase, CSVDIR, name);
  848. return buf;
  849. }
  850. /* The list below is only for files that use a non-standard name for a
  851. * datum that is already supported in GRASS. The number of entries must be even;
  852. * they are all in pairs. The first one in the pair is the non-standard name;
  853. * the second is the GRASS/GDAL name. If a name appears more than once (as for
  854. * European_Terrestrial_Reference_System_1989) then it means there was more
  855. * than one non-standard name for it that needs to be accounted for.
  856. *
  857. * N.B. The order of these pairs is different from that in
  858. * ogr/ogrfromepsg.cpp in the GDAL source tree! GRASS uses the EPSG
  859. * names in its WKT representation except WGS_1984 and WGS_1972 as
  860. * these shortened versions seem to be standard.
  861. * Below order:
  862. * the equivalent name comes first in the pair, and
  863. * the EPSG name (as used in the GRASS datum.table file) comes second.
  864. *
  865. * The datum parameters are stored in
  866. * ../gis/datum.table # 3 parameters
  867. * ../gis/datumtransform.table # 7 parameters (requires entry in datum.table)
  868. *
  869. * Hint: use GDAL's "testepsg" to identify the canonical name, e.g.
  870. * testepsg epsg:4674
  871. */
  872. static const char *papszDatumEquiv[] = {
  873. "Militar_Geographische_Institute",
  874. "Militar_Geographische_Institut",
  875. "World_Geodetic_System_1984",
  876. "WGS_1984",
  877. "World_Geodetic_System_1972",
  878. "WGS_1972",
  879. "European_Terrestrial_Reference_System_89",
  880. "European_Terrestrial_Reference_System_1989",
  881. "European_Reference_System_1989",
  882. "European_Terrestrial_Reference_System_1989",
  883. "ETRS_1989",
  884. "European_Terrestrial_Reference_System_1989",
  885. "ETRS89",
  886. "European_Terrestrial_Reference_System_1989",
  887. "ETRF_1989",
  888. "European_Terrestrial_Reference_System_1989",
  889. "NZGD_2000",
  890. "New_Zealand_Geodetic_Datum_2000",
  891. "Monte_Mario_Rome",
  892. "Monte_Mario",
  893. "MONTROME",
  894. "Monte_Mario",
  895. "Campo_Inchauspe_1969",
  896. "Campo_Inchauspe",
  897. "S_JTSK",
  898. "System_Jednotne_Trigonometricke_Site_Katastralni",
  899. "S_JTSK_Ferro",
  900. "Militar_Geographische_Institut",
  901. "Potsdam_Datum_83",
  902. "Deutsches_Hauptdreiecksnetz",
  903. "Rauenberg_Datum_83",
  904. "Deutsches_Hauptdreiecksnetz",
  905. "South_American_1969",
  906. "South_American_Datum_1969",
  907. "International_Terrestrial_Reference_Frame_1992",
  908. "ITRF92",
  909. "ITRF_1992",
  910. "ITRF92",
  911. NULL
  912. };
  913. /************************************************************************/
  914. /* OGREPSGDatumNameMassage() */
  915. /* */
  916. /* Massage an EPSG datum name into WMT format. Also transform */
  917. /* specific exception cases into WKT versions. */
  918. /************************************************************************/
  919. static void DatumNameMassage(char **ppszDatum)
  920. {
  921. int i, j;
  922. char *pszDatum = *ppszDatum;
  923. G_debug(3, "DatumNameMassage: Raw string found <%s>", (char *)pszDatum);
  924. /* -------------------------------------------------------------------- */
  925. /* Translate non-alphanumeric values to underscores. */
  926. /* -------------------------------------------------------------------- */
  927. for (i = 0; pszDatum[i] != '\0'; i++) {
  928. if (!(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
  929. && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
  930. && !(pszDatum[i] >= '0' && pszDatum[i] <= '9')) {
  931. pszDatum[i] = '_';
  932. }
  933. }
  934. /* -------------------------------------------------------------------- */
  935. /* Remove repeated and trailing underscores. */
  936. /* -------------------------------------------------------------------- */
  937. for (i = 1, j = 0; pszDatum[i] != '\0'; i++) {
  938. if (pszDatum[j] == '_' && pszDatum[i] == '_')
  939. continue;
  940. pszDatum[++j] = pszDatum[i];
  941. }
  942. if (pszDatum[j] == '_')
  943. pszDatum[j] = '\0';
  944. else
  945. pszDatum[j + 1] = '\0';
  946. /* -------------------------------------------------------------------- */
  947. /* Search for datum equivalences. Specific massaged names get */
  948. /* mapped to OpenGIS specified names. */
  949. /* -------------------------------------------------------------------- */
  950. G_debug(3, "DatumNameMassage: Search for datum equivalences of <%s>", (char *)pszDatum);
  951. for (i = 0; papszDatumEquiv[i] != NULL; i += 2) {
  952. if (EQUAL(*ppszDatum, papszDatumEquiv[i])) {
  953. G_free(*ppszDatum);
  954. *ppszDatum = G_store(papszDatumEquiv[i + 1]);
  955. break;
  956. }
  957. }
  958. }
  959. #endif /* HAVE_OGR */