convert.c 27 KB

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