convert.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. /**
  2. \file convert.c
  3. \brief GProj library - Functions for manipulating co-ordinate system representations
  4. \author Paul Kelly, Frank Warmerdam
  5. (C) 2003-2008 by the GRASS Development Team
  6. This program is free software under the GNU General Public
  7. License (>=v2). Read the file COPYING that comes with GRASS
  8. for details.
  9. **/
  10. #include <grass/config.h>
  11. #ifdef HAVE_OGR
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <math.h>
  16. #include <grass/gis.h>
  17. #include <grass/gprojects.h>
  18. #include <grass/glocale.h>
  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/ogr_csv"
  23. static void DatumNameMassage(char **);
  24. /**
  25. * \brief Converts a GRASS co-ordinate system representation to WKT style.
  26. *
  27. * Takes a GRASS co-ordinate system as specified by two sets of key/value
  28. * pairs derived from the PROJ_INFO and PROJ_UNITS files, and converts it
  29. * to the 'Well Known Text' format popularised by proprietary GIS
  30. *
  31. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  32. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  33. * \param esri_style boolean Output ESRI-style WKT (Use OSRMorphToESRI()
  34. * function provided by OGR library)
  35. * \param prettify boolean Use linebreaks and indents to 'prettify' output
  36. * WKT string (Use OSRExportToPrettyWkt() function in OGR)
  37. *
  38. * \return Pointer to a string containing the co-ordinate system in WKT
  39. * format
  40. **/
  41. char *GPJ_grass_to_wkt(struct Key_Value *proj_info,
  42. struct Key_Value *proj_units,
  43. int esri_style, int prettify)
  44. {
  45. OGRSpatialReferenceH hSRS;
  46. char *wkt;
  47. hSRS = GPJ_grass_to_osr(proj_info, proj_units);
  48. if (hSRS == NULL)
  49. return NULL;
  50. if (esri_style)
  51. OSRMorphToESRI(hSRS);
  52. if (prettify)
  53. OSRExportToPrettyWkt(hSRS, &wkt, 0);
  54. else
  55. OSRExportToWkt(hSRS, &wkt);
  56. OSRDestroySpatialReference(hSRS);
  57. return wkt;
  58. }
  59. /**
  60. * \brief Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object.
  61. *
  62. * \param proj_info Set of GRASS PROJ_INFO key/value pairs
  63. * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
  64. *
  65. * \return OGRSpatialReferenceH object representing the co-ordinate system
  66. * defined by proj_info and proj_units or NULL if it fails
  67. **/
  68. OGRSpatialReferenceH GPJ_grass_to_osr(struct Key_Value * proj_info,
  69. struct Key_Value * proj_units)
  70. {
  71. struct pj_info pjinfo;
  72. char *proj4, *proj4mod, *wkt, *modwkt, *startmod, *lastpart;
  73. OGRSpatialReferenceH hSRS, hSRS2;
  74. OGRErr errcode;
  75. struct gpj_datum dstruct;
  76. struct gpj_ellps estruct;
  77. size_t len;
  78. const char *ellpskv, *unit, *unfact;
  79. char *ellps, *ellpslong, *datum, *params, *towgs84, *datumlongname,
  80. *start, *end;
  81. const char *sysname, *osrunit, *osrunfact;
  82. double a, es, rf;
  83. int haveparams = 0;
  84. if ((proj_info == NULL) || (proj_units == NULL))
  85. return NULL;
  86. hSRS = OSRNewSpatialReference(NULL);
  87. if (pj_get_kv(&pjinfo, proj_info, proj_units) < 0) {
  88. G_warning(_("Unable parse GRASS PROJ_INFO file"));
  89. return NULL;
  90. }
  91. if ((proj4 = pj_get_def(pjinfo.pj, 0)) == NULL) {
  92. G_warning(_("Unable get PROJ.4-style parameter string"));
  93. return NULL;
  94. }
  95. pj_free(pjinfo.pj);
  96. unit = G_find_key_value("unit", proj_units);
  97. unfact = G_find_key_value("meters", proj_units);
  98. if (unfact != NULL && (strcmp(pjinfo.proj, "ll") != 0))
  99. G_asprintf(&proj4mod, "%s +to_meter=%s", proj4, unfact);
  100. else
  101. proj4mod = G_store(proj4);
  102. pj_dalloc(proj4);
  103. if ((errcode = OSRImportFromProj4(hSRS, proj4mod)) != OGRERR_NONE) {
  104. G_warning(_("OGR can't parse PROJ.4-style parameter string: "
  105. "%s (OGR Error code was %d)"), proj4mod, errcode);
  106. return NULL;
  107. }
  108. G_free(proj4mod);
  109. if ((errcode = OSRExportToWkt(hSRS, &wkt)) != OGRERR_NONE) {
  110. G_warning(_("OGR can't get WKT-style parameter string "
  111. "(OGR Error code was %d)"), errcode);
  112. return NULL;
  113. }
  114. ellpskv = G_find_key_value("ellps", proj_info);
  115. GPJ__get_ellipsoid_params(proj_info, &a, &es, &rf);
  116. haveparams = GPJ__get_datum_params(proj_info, &datum, &params);
  117. if(ellpskv != NULL)
  118. ellps = G_store(ellpskv);
  119. else
  120. ellps = NULL;
  121. if ((datum == NULL) || (GPJ_get_datum_by_name(datum, &dstruct) < 0)) {
  122. datumlongname = G_store("unknown");
  123. if (ellps == NULL)
  124. ellps = G_store("unnamed");
  125. }
  126. else {
  127. datumlongname = G_store(dstruct.longname);
  128. if (ellps == NULL)
  129. ellps = G_store(dstruct.ellps);
  130. GPJ_free_datum(&dstruct);
  131. }
  132. G_free(datum);
  133. if (GPJ_get_ellipsoid_by_name(ellps, &estruct) > 0) {
  134. ellpslong = G_store(estruct.longname);
  135. DatumNameMassage(&ellpslong);
  136. GPJ_free_ellps(&estruct);
  137. }
  138. else
  139. ellpslong = G_store(ellps);
  140. startmod = strstr(wkt, "GEOGCS");
  141. lastpart = strstr(wkt, "PRIMEM");
  142. len = strlen(wkt) - strlen(startmod);
  143. wkt[len] = '\0';
  144. if (haveparams == 2) {
  145. /* Only put datum params into the WKT if they were specifically
  146. * specified in PROJ_INFO */
  147. char *paramkey, *paramvalue;
  148. paramkey = strtok(params, "=");
  149. paramvalue = params + strlen(paramkey) + 1;
  150. if (G_strcasecmp(paramkey, "towgs84") == 0)
  151. G_asprintf(&towgs84, ",TOWGS84[%s]", paramvalue);
  152. else
  153. towgs84 = G_store("");
  154. G_free(params);
  155. }
  156. else
  157. towgs84 = G_store("");
  158. sysname = OSRGetAttrValue(hSRS, "PROJCS", 0);
  159. if (sysname == NULL) {
  160. /* Not a projected co-ordinate system */
  161. start = G_store("");
  162. end = G_store("");
  163. }
  164. else {
  165. if ((strcmp(sysname, "unnamed") == 0) &&
  166. (G_find_key_value("name", proj_info) != NULL))
  167. G_asprintf(&start, "PROJCS[\"%s\",",
  168. G_find_key_value("name", proj_info));
  169. else
  170. start = G_store(wkt);
  171. osrunit = OSRGetAttrValue(hSRS, "UNIT", 0);
  172. osrunfact = OSRGetAttrValue(hSRS, "UNIT", 1);
  173. if ((unfact == NULL) || (G_strcasecmp(osrunit, "unknown") != 0))
  174. end = G_store("");
  175. else {
  176. char *buff;
  177. double unfactf = atof(unfact);
  178. G_asprintf(&buff, ",UNIT[\"%s\",", osrunit);
  179. startmod = strstr(lastpart, buff);
  180. len = strlen(lastpart) - strlen(startmod);
  181. lastpart[len] = '\0';
  182. G_free(buff);
  183. if (unit == NULL)
  184. unit = "unknown";
  185. G_asprintf(&end, ",UNIT[\"%s\",%.16g]]", unit, unfactf);
  186. }
  187. }
  188. OSRDestroySpatialReference(hSRS);
  189. G_asprintf(&modwkt,
  190. "%sGEOGCS[\"%s\",DATUM[\"%s\",SPHEROID[\"%s\",%.16g,%.16g]%s],%s%s",
  191. start, ellps, datumlongname, ellpslong, a, rf, towgs84,
  192. lastpart, end);
  193. hSRS2 = OSRNewSpatialReference(modwkt);
  194. G_free(modwkt);
  195. CPLFree(wkt);
  196. G_free(start);
  197. G_free(ellps);
  198. G_free(datumlongname);
  199. G_free(ellpslong);
  200. G_free(towgs84);
  201. G_free(end);
  202. return hSRS2;
  203. }
  204. /**
  205. * \brief Converts an OGRSpatialReferenceH object to a GRASS co-ordinate system.
  206. *
  207. * \param cellhd Pointer to a GRASS Cell_head structure that will have its
  208. * projection-related members populated with appropriate values
  209. * \param projinfo Pointer to a pointer which will have a GRASS Key_Value
  210. * structure allocated containing a set of GRASS PROJ_INFO values
  211. * \param projunits Pointer to a pointer which will have a GRASS Key_Value
  212. * structure allocated containing a set of GRASS PROJ_UNITS values
  213. * \param hSRS OGRSpatialReferenceH object containing the co-ordinate
  214. * system to be converted
  215. * \param datumtrans Index number of datum parameter set to use, 0 to leave
  216. * unspecifed
  217. *
  218. * \return 2 if a projected or lat/long co-ordinate system has been
  219. * defined; 1 if an unreferenced XY co-ordinate system has
  220. * been defined
  221. **/
  222. int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
  223. struct Key_Value **projunits, OGRSpatialReferenceH hSRS,
  224. int datumtrans)
  225. {
  226. struct Key_Value *temp_projinfo;
  227. char *pszProj4 = NULL, *pszRemaining;
  228. char *pszProj = NULL;
  229. char *datum = NULL;
  230. struct gpj_datum dstruct;
  231. if (hSRS == NULL)
  232. goto default_to_xy;
  233. /* Set finder function for locating OGR csv co-ordinate system tables */
  234. SetCSVFilenameHook(GPJ_set_csv_loc);
  235. /* Hopefully this doesn't do any harm if it wasn't in ESRI format
  236. * to start with... */
  237. OSRMorphFromESRI(hSRS);
  238. /* -------------------------------------------------------------------- */
  239. /* Set cellhd for well known coordinate systems. */
  240. /* -------------------------------------------------------------------- */
  241. if (!OSRIsGeographic(hSRS) && !OSRIsProjected(hSRS))
  242. goto default_to_xy;
  243. if (cellhd) {
  244. int bNorth;
  245. if (OSRIsGeographic(hSRS)) {
  246. cellhd->proj = PROJECTION_LL;
  247. cellhd->zone = 0;
  248. }
  249. else if (OSRGetUTMZone(hSRS, &bNorth) != 0) {
  250. cellhd->proj = PROJECTION_UTM;
  251. cellhd->zone = OSRGetUTMZone(hSRS, &bNorth);
  252. if (!bNorth)
  253. cellhd->zone *= -1;
  254. }
  255. else {
  256. cellhd->proj = PROJECTION_OTHER;
  257. cellhd->zone = 0;
  258. }
  259. }
  260. /* -------------------------------------------------------------------- */
  261. /* Get the coordinate system definition in PROJ.4 format. */
  262. /* -------------------------------------------------------------------- */
  263. if (OSRExportToProj4(hSRS, &pszProj4) != OGRERR_NONE)
  264. goto default_to_xy;
  265. /* -------------------------------------------------------------------- */
  266. /* Parse the PROJ.4 string into key/value pairs. Do a bit of */
  267. /* extra work to "GRASSify" the result. */
  268. /* -------------------------------------------------------------------- */
  269. temp_projinfo = G_create_key_value();
  270. pszRemaining = pszProj4;
  271. while ((pszRemaining = strstr(pszRemaining, "+")) != NULL) {
  272. char *pszToken, *pszValue;
  273. pszRemaining++;
  274. /* Advance pszRemaining to end of this token[=value] pair */
  275. pszToken = pszRemaining;
  276. while (*pszRemaining != ' ' && *pszRemaining != '\0')
  277. pszRemaining++;
  278. if (*pszRemaining == ' ') {
  279. *pszRemaining = '\0';
  280. pszRemaining++;
  281. }
  282. /* parse token, and value */
  283. if (strstr(pszToken, "=") != NULL) {
  284. pszValue = strstr(pszToken, "=");
  285. *pszValue = '\0';
  286. pszValue++;
  287. }
  288. else
  289. pszValue = "defined";
  290. if (G_strcasecmp(pszToken, "proj") == 0) {
  291. /* The ll projection is known as longlat in PROJ.4 */
  292. if (G_strcasecmp(pszValue, "longlat") == 0)
  293. pszValue = "ll";
  294. pszProj = pszValue;
  295. continue;
  296. }
  297. /* Ellipsoid and datum handled separately below */
  298. if (G_strcasecmp(pszToken, "ellps") == 0
  299. || G_strcasecmp(pszToken, "a") == 0
  300. || G_strcasecmp(pszToken, "b") == 0
  301. || G_strcasecmp(pszToken, "es") == 0
  302. || G_strcasecmp(pszToken, "rf") == 0
  303. || G_strcasecmp(pszToken, "datum") == 0)
  304. continue;
  305. /* We will handle units separately */
  306. if (G_strcasecmp(pszToken, "to_meter") == 0
  307. || G_strcasecmp(pszToken, "units") == 0)
  308. continue;
  309. G_set_key_value(pszToken, pszValue, temp_projinfo);
  310. }
  311. *projinfo = G_create_key_value();
  312. /* -------------------------------------------------------------------- */
  313. /* Derive the user name for the projection. */
  314. /* -------------------------------------------------------------------- */
  315. if (pszProj) {
  316. char path[4095];
  317. char name[80];
  318. sprintf(path, "%s/etc/projections", G_gisbase());
  319. if (G_lookup_key_value_from_file(path, pszProj, name, sizeof(name)) >
  320. 0)
  321. G_set_key_value("name", name, *projinfo);
  322. else
  323. G_set_key_value("name", pszProj, *projinfo);
  324. G_set_key_value("proj", pszProj, *projinfo);
  325. }
  326. else
  327. G_warning(_("No projection name! Projection parameters likely to be meaningless."));
  328. /* -------------------------------------------------------------------- */
  329. /* Find the GRASS datum name and choose parameters either */
  330. /* interactively or not. */
  331. /* -------------------------------------------------------------------- */
  332. {
  333. const char *pszDatumNameConst = OSRGetAttrValue(hSRS, "DATUM", 0);
  334. struct datum_list *list, *listhead;
  335. char *dum1, *dum2, *pszDatumName;
  336. int paramspresent =
  337. GPJ__get_datum_params(temp_projinfo, &dum1, &dum2);
  338. if (pszDatumNameConst) {
  339. /* Need to make a new copy of the string so we don't mess
  340. * around with the memory inside the OGRSpatialReferenceH? */
  341. pszDatumName = G_store(pszDatumNameConst);
  342. DatumNameMassage(&pszDatumName);
  343. list = listhead = read_datum_table();
  344. while (list != NULL) {
  345. if (G_strcasecmp(pszDatumName, list->longname) == 0) {
  346. datum = G_store(list->name);
  347. break;
  348. }
  349. list = list->next;
  350. }
  351. free_datum_list(listhead);
  352. if (datum == NULL) {
  353. if (paramspresent < 2)
  354. /* Only give warning if no parameters present */
  355. G_warning(_("Datum <%s> not recognised by GRASS and no parameters found"),
  356. pszDatumName);
  357. }
  358. else {
  359. G_set_key_value("datum", datum, *projinfo);
  360. if (paramspresent < 2) {
  361. /* If no datum parameters were imported from the OSR
  362. * object then we should use the set specified by datumtrans */
  363. char *params, *chosenparams = NULL;
  364. int paramsets;
  365. paramsets =
  366. GPJ_get_default_datum_params_by_name(datum, &params);
  367. if (paramsets < 0)
  368. G_warning(_("Datum <%s> apparently recognised by GRASS but no parameters found. "
  369. "You may want to look into this."), datum);
  370. else if (datumtrans > paramsets) {
  371. G_warning(_("Invalid transformation number %d; valid range is 1 to %d. "
  372. "Leaving datum transform parameters unspecified."),
  373. datumtrans, paramsets);
  374. datumtrans = 0;
  375. }
  376. if (paramsets > 0) {
  377. struct gpj_datum_transform_list *list, *old;
  378. list = GPJ_get_datum_transform_by_name(datum);
  379. if (list != NULL) {
  380. do {
  381. if (list->count == datumtrans)
  382. chosenparams = G_store(list->params);
  383. old = list;
  384. list = list->next;
  385. GPJ_free_datum_transform(old);
  386. } while (list != NULL);
  387. }
  388. }
  389. if (chosenparams != NULL) {
  390. char *paramkey, *paramvalue;
  391. paramkey = strtok(chosenparams, "=");
  392. paramvalue = chosenparams + strlen(paramkey) + 1;
  393. G_set_key_value(paramkey, paramvalue, *projinfo);
  394. G_free(chosenparams);
  395. }
  396. if (paramsets > 0)
  397. G_free(params);
  398. }
  399. }
  400. G_free(pszDatumName);
  401. }
  402. }
  403. /* -------------------------------------------------------------------- */
  404. /* Determine an appropriate GRASS ellipsoid name if possible, or */
  405. /* else just put a and es values into PROJ_INFO */
  406. /* -------------------------------------------------------------------- */
  407. if ((datum != NULL) && (GPJ_get_datum_by_name(datum, &dstruct) > 0)) {
  408. /* Use ellps name associated with datum */
  409. G_set_key_value("ellps", dstruct.ellps, *projinfo);
  410. GPJ_free_datum(&dstruct);
  411. G_free(datum);
  412. }
  413. else {
  414. /* If we can't determine the ellipsoid from the datum, derive it
  415. * directly from "SPHEROID" parameters in WKT */
  416. const char *pszSemiMajor = OSRGetAttrValue(hSRS, "SPHEROID", 1);
  417. const char *pszInvFlat = OSRGetAttrValue(hSRS, "SPHEROID", 2);
  418. if (pszSemiMajor != NULL && pszInvFlat != NULL) {
  419. char *ellps = NULL;
  420. struct ellps_list *list, *listhead;
  421. double a = atof(pszSemiMajor), invflat = atof(pszInvFlat), flat;
  422. double es;
  423. /* Allow for incorrect WKT describing a sphere where InvFlat
  424. * is given as 0 rather than inf */
  425. if (invflat > 0)
  426. flat = 1 / invflat;
  427. else
  428. flat = 0;
  429. es = flat * (2.0 - flat);
  430. list = listhead = read_ellipsoid_table(0);
  431. while (list != NULL) {
  432. /* Try and match a and es against GRASS defined ellipsoids;
  433. * accept first one that matches. These numbers were found
  434. * by trial and error and could be fine-tuned, or possibly
  435. * a direct comparison of IEEE floating point values used. */
  436. if ((a == list->a || fabs(a - list->a) < 0.1 || fabs(1 - a / list->a) < 0.0000001) && ((es == 0 && list->es == 0) || /* Special case for sphere */
  437. (invflat == list->rf || fabs(invflat - list->rf) < 0.0000001))) {
  438. ellps = G_store(list->name);
  439. break;
  440. }
  441. list = list->next;
  442. }
  443. if (listhead != NULL)
  444. free_ellps_list(listhead);
  445. if (ellps == NULL) {
  446. /* If we weren't able to find a matching ellps name, set
  447. * a and es values directly from WKT-derived data */
  448. char es_str[100];
  449. G_set_key_value("a", (char *)pszSemiMajor, *projinfo);
  450. sprintf(es_str, "%.16g", es);
  451. G_set_key_value("es", es_str, *projinfo);
  452. }
  453. else {
  454. /* else specify the GRASS ellps name for readability */
  455. G_set_key_value("ellps", ellps, *projinfo);
  456. G_free(ellps);
  457. }
  458. }
  459. }
  460. /* -------------------------------------------------------------------- */
  461. /* Finally append the detailed projection parameters to the end */
  462. /* -------------------------------------------------------------------- */
  463. {
  464. int i;
  465. for (i = 0; i < temp_projinfo->nitems; i++)
  466. G_set_key_value(temp_projinfo->key[i],
  467. temp_projinfo->value[i], *projinfo);
  468. G_free_key_value(temp_projinfo);
  469. }
  470. free(pszProj4); /* hopefully the same as CPLFree()! */
  471. /* -------------------------------------------------------------------- */
  472. /* Set the linear units. */
  473. /* -------------------------------------------------------------------- */
  474. *projunits = G_create_key_value();
  475. if (OSRIsGeographic(hSRS)) {
  476. /* We assume degrees ... someday we will be wrong! */
  477. G_set_key_value("unit", "degree", *projunits);
  478. G_set_key_value("units", "degrees", *projunits);
  479. G_set_key_value("meters", "1.0", *projunits);
  480. }
  481. else {
  482. char szFormatBuf[256];
  483. char *pszUnitsName = NULL;
  484. double dfToMeters;
  485. char *pszUnitsPlural, *pszStringEnd;
  486. dfToMeters = OSRGetLinearUnits(hSRS, &pszUnitsName);
  487. /* Workaround for the most obvious case when unit name is unknown */
  488. if ((G_strcasecmp(pszUnitsName, "unknown") == 0) &&
  489. (dfToMeters == 1.))
  490. G_asprintf(&pszUnitsName, "meter");
  491. G_set_key_value("unit", pszUnitsName, *projunits);
  492. /* Attempt at plural formation (WKT format doesn't store plural
  493. * form of unit name) */
  494. pszUnitsPlural = G_malloc(strlen(pszUnitsName) + 3);
  495. strcpy(pszUnitsPlural, pszUnitsName);
  496. pszStringEnd = pszUnitsPlural + strlen(pszUnitsPlural) - 4;
  497. if (G_strcasecmp(pszStringEnd, "foot") == 0) {
  498. /* Special case for foot - change two o's to e's */
  499. pszStringEnd[1] = 'e';
  500. pszStringEnd[2] = 'e';
  501. }
  502. else if (G_strcasecmp(pszStringEnd, "inch") == 0) {
  503. /* Special case for inch - add es */
  504. pszStringEnd[4] = 'e';
  505. pszStringEnd[5] = 's';
  506. pszStringEnd[6] = '\0';
  507. }
  508. else {
  509. /* For anything else add an s at the end */
  510. pszStringEnd[4] = 's';
  511. pszStringEnd[5] = '\0';
  512. }
  513. G_set_key_value("units", pszUnitsPlural, *projunits);
  514. G_free(pszUnitsPlural);
  515. sprintf(szFormatBuf, "%.16g", dfToMeters);
  516. G_set_key_value("meters", szFormatBuf, *projunits);
  517. }
  518. return 2;
  519. /* -------------------------------------------------------------------- */
  520. /* Fallback to returning an ungeoreferenced definition. */
  521. /* -------------------------------------------------------------------- */
  522. default_to_xy:
  523. if (cellhd != NULL) {
  524. cellhd->proj = PROJECTION_XY;
  525. cellhd->zone = 0;
  526. }
  527. *projinfo = NULL;
  528. *projunits = NULL;
  529. return 1;
  530. }
  531. /**
  532. * \brief Converts a WKT projection description to a GRASS co-ordinate system.
  533. *
  534. * \param cellhd Pointer to a GRASS Cell_head structure that will have its
  535. * projection-related members populated with appropriate values
  536. * \param projinfo Pointer to a pointer which will have a GRASS Key_Value
  537. * structure allocated containing a set of GRASS PROJ_INFO values
  538. * \param projunits Pointer to a pointer which will have a GRASS Key_Value
  539. * structure allocated containing a set of GRASS PROJ_UNITS values
  540. * \param wkt Well-known Text (WKT) description of the co-ordinate
  541. * system to be converted
  542. * \param datumtrans Index number of datum parameter set to use, 0 to leave
  543. * unspecifed
  544. *
  545. * \return 2 if a projected or lat/long co-ordinate system has been
  546. * defined; 1 if an unreferenced XY co-ordinate system has
  547. * been defined
  548. **/
  549. int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
  550. struct Key_Value **projunits, const char *wkt,
  551. int datumtrans)
  552. {
  553. int retval;
  554. if (wkt == NULL)
  555. retval =
  556. GPJ_osr_to_grass(cellhd, projinfo, projunits, NULL, datumtrans);
  557. else {
  558. OGRSpatialReferenceH hSRS;
  559. /* Set finder function for locating OGR csv co-ordinate system tables */
  560. SetCSVFilenameHook(GPJ_set_csv_loc);
  561. hSRS = OSRNewSpatialReference(wkt);
  562. retval =
  563. GPJ_osr_to_grass(cellhd, projinfo, projunits, hSRS, datumtrans);
  564. OSRDestroySpatialReference(hSRS);
  565. }
  566. return retval;
  567. }
  568. /* GPJ_set_csv_loc()
  569. * 'finder function' for use with OGR SetCSVFilenameHook() function */
  570. const char *GPJ_set_csv_loc(const char *name)
  571. {
  572. const char *gisbase = G_gisbase();
  573. static char *buf = NULL;
  574. if (buf != NULL)
  575. G_free(buf);
  576. G_asprintf(&buf, "%s%s/%s", gisbase, CSVDIR, name);
  577. return buf;
  578. }
  579. /* The list below is only for files that use a non-standard name for a
  580. * datum that is already supported in GRASS. The number of entries must be even;
  581. * they are all in pairs. The first one in the pair is the non-standard name;
  582. * the second is the GRASS name. If a name appears more than once (as for
  583. * European_Terrestrial_Reference_System_1989) then it means there was more
  584. * than one non-standard name for it that needs to be accounted for.
  585. *
  586. * N.B. The order of these pairs is different from that in
  587. * ogr/ogrfromepsg.cpp in the GDAL source tree! GRASS uses the EPSG
  588. * names in its WKT representation except WGS_1984 and WGS_1972 as
  589. * these shortened versions seem to be standard
  590. */
  591. static const char *papszDatumEquiv[] = {
  592. "Militar_Geographische_Institute",
  593. "Militar_Geographische_Institut",
  594. "World_Geodetic_System_1984",
  595. "WGS_1984",
  596. "World_Geodetic_System_1972",
  597. "WGS_1972",
  598. "European_Terrestrial_Reference_System_89",
  599. "European_Terrestrial_Reference_System_1989",
  600. "European_Reference_System_1989",
  601. "European_Terrestrial_Reference_System_1989",
  602. "ETRS_1989",
  603. "European_Terrestrial_Reference_System_1989",
  604. "ETRF_1989",
  605. "European_Terrestrial_Reference_System_1989",
  606. "NZGD_2000",
  607. "New_Zealand_Geodetic_Datum_2000",
  608. "Monte_Mario_Rome",
  609. "Monte_Mario",
  610. "MONTROME",
  611. "Monte_Mario",
  612. "Campo_Inchauspe_1969",
  613. "Campo_Inchauspe",
  614. "S_JTSK_Ferro",
  615. "Militar_Geographische_Institut",
  616. NULL
  617. };
  618. /************************************************************************/
  619. /* OGREPSGDatumNameMassage() */
  620. /* */
  621. /* Massage an EPSG datum name into WMT format. Also transform */
  622. /* specific exception cases into WKT versions. */
  623. /************************************************************************/
  624. static void DatumNameMassage(char **ppszDatum)
  625. {
  626. int i, j;
  627. char *pszDatum = *ppszDatum;
  628. /* -------------------------------------------------------------------- */
  629. /* Translate non-alphanumeric values to underscores. */
  630. /* -------------------------------------------------------------------- */
  631. for (i = 0; pszDatum[i] != '\0'; i++) {
  632. if (!(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z')
  633. && !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z')
  634. && !(pszDatum[i] >= '0' && pszDatum[i] <= '9')) {
  635. pszDatum[i] = '_';
  636. }
  637. }
  638. /* -------------------------------------------------------------------- */
  639. /* Remove repeated and trailing underscores. */
  640. /* -------------------------------------------------------------------- */
  641. for (i = 1, j = 0; pszDatum[i] != '\0'; i++) {
  642. if (pszDatum[j] == '_' && pszDatum[i] == '_')
  643. continue;
  644. pszDatum[++j] = pszDatum[i];
  645. }
  646. if (pszDatum[j] == '_')
  647. pszDatum[j] = '\0';
  648. else
  649. pszDatum[j + 1] = '\0';
  650. /* -------------------------------------------------------------------- */
  651. /* Search for datum equivalences. Specific massaged names get */
  652. /* mapped to OpenGIS specified names. */
  653. /* -------------------------------------------------------------------- */
  654. for (i = 0; papszDatumEquiv[i] != NULL; i += 2) {
  655. if (EQUAL(*ppszDatum, papszDatumEquiv[i])) {
  656. CPLFree(*ppszDatum);
  657. *ppszDatum = CPLStrdup(papszDatumEquiv[i + 1]);
  658. break;
  659. }
  660. }
  661. }
  662. #endif /* HAVE_OGR */