make_loc.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /*!
  2. * \file lib/gis/make_loc.c
  3. *
  4. * \brief GIS Library - Functions to create a new location
  5. *
  6. * Creates a new location automatically given a "Cell_head", PROJ_INFO
  7. * and PROJ_UNITS information.
  8. *
  9. * (C) 2000-2013 by the GRASS Development Team
  10. *
  11. * This program is free software under the GNU General Public License
  12. * (>=v2). Read the file COPYING that comes with GRASS for details.
  13. *
  14. * \author Frank Warmerdam
  15. */
  16. #include <grass/gis.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <errno.h>
  20. #include <unistd.h>
  21. #include <sys/stat.h>
  22. #include <math.h>
  23. #include <grass/glocale.h>
  24. /*!
  25. * \brief Create a new location
  26. *
  27. * This function creates a new location in the current database,
  28. * initializes the projection, default window and current window.
  29. *
  30. * \param location_name Name of the new location. Should not include
  31. * the full path, the location will be created within
  32. * the current database.
  33. * \param wind default window setting for the new location.
  34. * All fields should be set in this
  35. * structure, and care should be taken to ensure that
  36. * the proj/zone fields match the definition in the
  37. * proj_info parameter(see G_set_cellhd_from_projinfo()).
  38. *
  39. * \param proj_info projection definition suitable to write to the
  40. * PROJ_INFO file, or NULL for PROJECTION_XY.
  41. *
  42. * \param proj_units projection units suitable to write to the PROJ_UNITS
  43. * file, or NULL.
  44. *
  45. * \return 0 on success
  46. * \return -1 to indicate a system error (check errno).
  47. * \return -2 failed to create projection file (currently not used)
  48. * \return -3 illegal name
  49. */
  50. int G_make_location(const char *location_name,
  51. struct Cell_head *wind,
  52. const struct Key_Value *proj_info,
  53. const struct Key_Value *proj_units)
  54. {
  55. char path[GPATH_MAX];
  56. /* check if location name is legal */
  57. if (G_legal_filename(location_name) != 1)
  58. return -3;
  59. /* Try to create the location directory, under the gisdbase. */
  60. sprintf(path, "%s/%s", G_gisdbase(), location_name);
  61. if (G_mkdir(path) != 0)
  62. return -1;
  63. /* Make the PERMANENT mapset. */
  64. sprintf(path, "%s/%s/%s", G_gisdbase(), location_name, "PERMANENT");
  65. if (G_mkdir(path) != 0) {
  66. return -1;
  67. }
  68. /* make these the new current location and mapset */
  69. G_setenv_nogisrc("LOCATION_NAME", location_name);
  70. G_setenv_nogisrc("MAPSET", "PERMANENT");
  71. /* Create the default, and current window files */
  72. G_put_element_window(wind, "", "DEFAULT_WIND");
  73. G_put_element_window(wind, "", "WIND");
  74. /* Write out the PROJ_INFO, and PROJ_UNITS if available. */
  75. if (proj_info != NULL) {
  76. G_file_name(path, "", "PROJ_INFO", "PERMANENT");
  77. G_write_key_value_file(path, proj_info);
  78. }
  79. if (proj_units != NULL) {
  80. G_file_name(path, "", "PROJ_UNITS", "PERMANENT");
  81. G_write_key_value_file(path, proj_units);
  82. }
  83. return 0;
  84. }
  85. /*!
  86. * \brief Create a new location
  87. *
  88. * This function creates a new location in the current database,
  89. * initializes the projection, default window and current window,
  90. * and sets the EPSG code if present
  91. *
  92. * \param location_name Name of the new location. Should not include
  93. * the full path, the location will be created within
  94. * the current database.
  95. * \param wind default window setting for the new location.
  96. * All fields should be set in this
  97. * structure, and care should be taken to ensure that
  98. * the proj/zone fields match the definition in the
  99. * proj_info parameter(see G_set_cellhd_from_projinfo()).
  100. *
  101. * \param proj_info projection definition suitable to write to the
  102. * PROJ_INFO file, or NULL for PROJECTION_XY.
  103. *
  104. * \param proj_units projection units suitable to write to the PROJ_UNITS
  105. * file, or NULL.
  106. *
  107. * \param proj_epsg EPSG code suitable to write to the PROJ_EPSG
  108. * file, or NULL.
  109. *
  110. * \return 0 on success
  111. * \return -1 to indicate a system error (check errno).
  112. * \return -2 failed to create projection file (currently not used)
  113. * \return -3 illegal name
  114. */
  115. int G_make_location_epsg(const char *location_name,
  116. struct Cell_head *wind,
  117. const struct Key_Value *proj_info,
  118. const struct Key_Value *proj_units,
  119. const struct Key_Value *proj_epsg)
  120. {
  121. int ret;
  122. ret = G_make_location(location_name, wind, proj_info, proj_units);
  123. if (ret != 0)
  124. return ret;
  125. /* Write out the PROJ_EPSG if available. */
  126. if (proj_epsg != NULL) {
  127. char path[GPATH_MAX];
  128. G_file_name(path, "", "PROJ_EPSG", "PERMANENT");
  129. G_write_key_value_file(path, proj_epsg);
  130. }
  131. return 0;
  132. }
  133. /*!
  134. * \brief Create a new location
  135. *
  136. * This function creates a new location in the current database,
  137. * initializes the projection, default window and current window,
  138. * and sets WKT, srid, and EPSG code if present
  139. *
  140. * \param location_name Name of the new location. Should not include
  141. * the full path, the location will be created within
  142. * the current database.
  143. * \param wind default window setting for the new location.
  144. * All fields should be set in this
  145. * structure, and care should be taken to ensure that
  146. * the proj/zone fields match the definition in the
  147. * proj_info parameter(see G_set_cellhd_from_projinfo()).
  148. *
  149. * \param proj_info projection definition suitable to write to the
  150. * PROJ_INFO file, or NULL for PROJECTION_XY.
  151. *
  152. * \param proj_units projection units suitable to write to the PROJ_UNITS
  153. * file, or NULL.
  154. *
  155. * \param proj_epsg EPSG code suitable to write to the PROJ_EPSG
  156. * file, or NULL.
  157. *
  158. * \param proj_wkt WKT definition suitable to write to the PROJ_WKT
  159. * file, or NULL.
  160. *
  161. * \param proj_srid Spatial reference ID suitable to write to the PROJ_SRID
  162. * file, or NULL.
  163. *
  164. * \return 0 on success
  165. * \return -1 to indicate a system error (check errno).
  166. * \return -2 failed to create projection file (currently not used)
  167. * \return -3 illegal name
  168. */
  169. int G_make_location_crs(const char *location_name,
  170. struct Cell_head *wind,
  171. const struct Key_Value *proj_info,
  172. const struct Key_Value *proj_units,
  173. const char *proj_srid,
  174. const char *proj_wkt)
  175. {
  176. int ret;
  177. ret = G_make_location(location_name, wind, proj_info, proj_units);
  178. if (ret != 0)
  179. return ret;
  180. /* Write out PROJ_SRID if srid is available. */
  181. if (proj_srid != NULL) {
  182. G_write_projsrid(location_name, proj_srid);
  183. }
  184. /* Write out PROJ_WKT if WKT is available. */
  185. if (proj_wkt != NULL) {
  186. G_write_projwkt(location_name, proj_wkt);
  187. }
  188. return 0;
  189. }
  190. /*!
  191. * \brief Compare projections including units
  192. *
  193. * \param proj_info1 projection info to compare
  194. * \param proj_units1 projection units to compare
  195. * \param proj_info2 projection info to compare
  196. * \param proj_units2 projection units to compare
  197. * \return -1 if not the same projection
  198. * \return -2 if linear unit translation to meters fails
  199. * \return -3 if not the same datum,
  200. * \return -4 if not the same ellipsoid,
  201. * \return -5 if UTM zone differs
  202. * \return -6 if UTM hemisphere differs,
  203. * \return -7 if false easting differs
  204. * \return -8 if false northing differs,
  205. * \return -9 if center longitude differs,
  206. * \return -10 if center latitude differs,
  207. * \return -11 if standard parallels differ,
  208. * \return 1 if projections match.
  209. */
  210. int G_compare_projections(const struct Key_Value *proj_info1,
  211. const struct Key_Value *proj_units1,
  212. const struct Key_Value *proj_info2,
  213. const struct Key_Value *proj_units2)
  214. {
  215. const char *proj1, *proj2;
  216. if (proj_info1 == NULL && proj_info2 == NULL)
  217. return TRUE;
  218. /* -------------------------------------------------------------------- */
  219. /* Are they both in the same projection? */
  220. /* -------------------------------------------------------------------- */
  221. /* prevent seg fault in G_find_key_value */
  222. if (proj_info1 == NULL || proj_info2 == NULL)
  223. return -1;
  224. proj1 = G_find_key_value("proj", proj_info1);
  225. proj2 = G_find_key_value("proj", proj_info2);
  226. if (proj1 == NULL || proj2 == NULL || strcmp(proj1, proj2))
  227. return -1;
  228. /* -------------------------------------------------------------------- */
  229. /* Verify that the linear unit translation to meters is OK. */
  230. /* -------------------------------------------------------------------- */
  231. /* prevent seg fault in G_find_key_value */
  232. if (proj_units1 == NULL && proj_units2 == NULL)
  233. return 1;
  234. if (proj_units1 == NULL || proj_units2 == NULL)
  235. return -2;
  236. {
  237. double a1 = 0, a2 = 0;
  238. if (G_find_key_value("meters", proj_units1) != NULL)
  239. a1 = atof(G_find_key_value("meters", proj_units1));
  240. if (G_find_key_value("meters", proj_units2) != NULL)
  241. a2 = atof(G_find_key_value("meters", proj_units2));
  242. if (a1 && a2 && (fabs(a2 - a1) > 0.000001))
  243. return -2;
  244. }
  245. /* compare unit name only if there is no to meter conversion factor */
  246. if (G_find_key_value("meters", proj_units1) == NULL ||
  247. G_find_key_value("meters", proj_units2) == NULL) {
  248. const char *u_1 = NULL, *u_2 = NULL;
  249. u_1 = G_find_key_value("unit", proj_units1);
  250. u_2 = G_find_key_value("unit", proj_units2);
  251. if ((u_1 && !u_2) || (!u_1 && u_2))
  252. return -2;
  253. /* the unit name can be arbitrary: the following can be the same
  254. * us-ft (proj.4 keyword)
  255. * U.S. Surveyor's Foot (proj.4 name)
  256. * US survey foot (WKT)
  257. * Foot_US (WKT)
  258. */
  259. if (u_1 && u_2 && G_strcasecmp(u_1, u_2))
  260. return -2;
  261. }
  262. /* -------------------------------------------------------------------- */
  263. /* Do they both have the same datum? */
  264. /* -------------------------------------------------------------------- */
  265. {
  266. const char *d_1 = NULL, *d_2 = NULL;
  267. d_1 = G_find_key_value("datum", proj_info1);
  268. d_2 = G_find_key_value("datum", proj_info2);
  269. if ((d_1 && !d_2) || (!d_1 && d_2))
  270. return -3;
  271. if (d_1 && d_2 && strcmp(d_1, d_2)) {
  272. /* different datum short names can mean the same datum,
  273. * see lib/gis/datum.table */
  274. G_debug(1, "Different datum names");
  275. }
  276. }
  277. /* -------------------------------------------------------------------- */
  278. /* Do they both have the same ellipsoid? */
  279. /* -------------------------------------------------------------------- */
  280. {
  281. const char *e_1 = NULL, *e_2 = NULL;
  282. e_1 = G_find_key_value("ellps", proj_info1);
  283. e_2 = G_find_key_value("ellps", proj_info2);
  284. if (e_1 && e_2 && strcmp(e_1, e_2))
  285. return -4;
  286. if (e_1 == NULL || e_2 == NULL) {
  287. double a1 = 0, a2 = 0;
  288. double es1 = 0, es2 = 0;
  289. /* it may happen that one proj_info has ellps,
  290. * while the other has a, es: translate ellps to a, es */
  291. if (e_1)
  292. G_get_ellipsoid_by_name(e_1, &a1, &es1);
  293. else {
  294. if (G_find_key_value("a", proj_info1) != NULL)
  295. a1 = atof(G_find_key_value("a", proj_info1));
  296. if (G_find_key_value("es", proj_info1) != NULL)
  297. es1 = atof(G_find_key_value("es", proj_info1));
  298. }
  299. if (e_2)
  300. G_get_ellipsoid_by_name(e_2, &a2, &es2);
  301. else {
  302. if (G_find_key_value("a", proj_info2) != NULL)
  303. a2 = atof(G_find_key_value("a", proj_info2));
  304. if (G_find_key_value("es", proj_info2) != NULL)
  305. es2 = atof(G_find_key_value("es", proj_info2));
  306. }
  307. /* it should be an error if a = 0 */
  308. if ((a1 == 0 && a2 != 0) || (a1 != 0 && a2 == 0))
  309. return -4;
  310. if (a1 && a2 && (fabs(a2 - a1) > 0.000001))
  311. return -4;
  312. if ((es1 == 0 && es2 != 0) || (es1 != 0 && es2 == 0))
  313. return -4;
  314. if (es1 && es2 && (fabs(es2 - es1) > 0.000001))
  315. return -4;
  316. }
  317. }
  318. /* -------------------------------------------------------------------- */
  319. /* Zone check specially for UTM */
  320. /* -------------------------------------------------------------------- */
  321. if (!strcmp(proj1, "utm") && !strcmp(proj2, "utm")
  322. && atof(G_find_key_value("zone", proj_info1))
  323. != atof(G_find_key_value("zone", proj_info2)))
  324. return -5;
  325. /* -------------------------------------------------------------------- */
  326. /* Hemisphere check specially for UTM */
  327. /* -------------------------------------------------------------------- */
  328. if (!strcmp(proj1, "utm") && !strcmp(proj2, "utm")
  329. && !!G_find_key_value("south", proj_info1)
  330. != !!G_find_key_value("south", proj_info2))
  331. return -6;
  332. /* -------------------------------------------------------------------- */
  333. /* Do they both have the same false easting? */
  334. /* -------------------------------------------------------------------- */
  335. {
  336. const char *x_0_1 = NULL, *x_0_2 = NULL;
  337. x_0_1 = G_find_key_value("x_0", proj_info1);
  338. x_0_2 = G_find_key_value("x_0", proj_info2);
  339. if ((x_0_1 && !x_0_2) || (!x_0_1 && x_0_2))
  340. return -7;
  341. if (x_0_1 && x_0_2 && (fabs(atof(x_0_1) - atof(x_0_2)) > 0.000001))
  342. return -7;
  343. }
  344. /* -------------------------------------------------------------------- */
  345. /* Do they both have the same false northing? */
  346. /* -------------------------------------------------------------------- */
  347. {
  348. const char *y_0_1 = NULL, *y_0_2 = NULL;
  349. y_0_1 = G_find_key_value("y_0", proj_info1);
  350. y_0_2 = G_find_key_value("y_0", proj_info2);
  351. if ((y_0_1 && !y_0_2) || (!y_0_1 && y_0_2))
  352. return -8;
  353. if (y_0_1 && y_0_2 && (fabs(atof(y_0_1) - atof(y_0_2)) > 0.000001))
  354. return -8;
  355. }
  356. /* -------------------------------------------------------------------- */
  357. /* Do they have the same center longitude? */
  358. /* -------------------------------------------------------------------- */
  359. {
  360. const char *l_1 = NULL, *l_2 = NULL;
  361. l_1 = G_find_key_value("lon_0", proj_info1);
  362. l_2 = G_find_key_value("lon_0", proj_info2);
  363. if ((l_1 && !l_2) || (!l_1 && l_2))
  364. return -9;
  365. if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001))
  366. return -9;
  367. /* -------------------------------------------------------------------- */
  368. /* Do they have the same center latitude? */
  369. /* -------------------------------------------------------------------- */
  370. l_1 = G_find_key_value("lat_0", proj_info1);
  371. l_2 = G_find_key_value("lat_0", proj_info2);
  372. if ((l_1 && !l_2) || (!l_1 && l_2))
  373. return -10;
  374. if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001))
  375. return -10;
  376. /* -------------------------------------------------------------------- */
  377. /* Do they have the same standard parallels? */
  378. /* -------------------------------------------------------------------- */
  379. l_1 = G_find_key_value("lat_1", proj_info1);
  380. l_2 = G_find_key_value("lat_1", proj_info2);
  381. if ((l_1 && !l_2) || (!l_1 && l_2))
  382. return -11;
  383. if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001)) {
  384. /* lat_1 differ */
  385. /* check for swapped lat_1, lat_2 */
  386. l_2 = G_find_key_value("lat_2", proj_info2);
  387. if (!l_2)
  388. return -11;
  389. if (fabs(atof(l_1) - atof(l_2)) > 0.000001) {
  390. return -11;
  391. }
  392. }
  393. l_1 = G_find_key_value("lat_2", proj_info1);
  394. l_2 = G_find_key_value("lat_2", proj_info2);
  395. if ((l_1 && !l_2) || (!l_1 && l_2))
  396. return -11;
  397. if (l_1 && l_2 && (fabs(atof(l_1) - atof(l_2)) > 0.000001)) {
  398. /* lat_2 differ */
  399. /* check for swapped lat_1, lat_2 */
  400. l_2 = G_find_key_value("lat_1", proj_info2);
  401. if (!l_2)
  402. return -11;
  403. if (fabs(atof(l_1) - atof(l_2)) > 0.000001) {
  404. return -11;
  405. }
  406. }
  407. }
  408. /* towgs84 ? */
  409. /* -------------------------------------------------------------------- */
  410. /* Add more details in later. */
  411. /* -------------------------------------------------------------------- */
  412. return 1;
  413. }
  414. /*!
  415. \brief Write WKT definition to file
  416. Any WKT string and version recognized by PROJ is supported.
  417. \param location_name name of the location to write the WKT definition
  418. \param wktstring pointer to WKT string
  419. \return 0 success
  420. \return -1 error writing
  421. */
  422. int G_write_projwkt(const char *location_name, const char *wktstring)
  423. {
  424. FILE *fp;
  425. char path[GPATH_MAX];
  426. int err, n;
  427. if (!wktstring)
  428. return 0;
  429. if (location_name && *location_name)
  430. sprintf(path, "%s/%s/%s/%s", G_gisdbase(), location_name, "PERMANENT", WKT_FILE);
  431. else
  432. G_file_name(path, "", WKT_FILE, "PERMANENT");
  433. fp = fopen(path, "w");
  434. if (!fp)
  435. G_fatal_error(_("Unable to open output file <%s>: %s"), path, strerror(errno));
  436. err = 0;
  437. n = strlen(wktstring);
  438. if (wktstring[n - 1] != '\n') {
  439. if (n != fprintf(fp, "%s\n", wktstring))
  440. err = -1;
  441. }
  442. else {
  443. if (n != fprintf(fp, "%s", wktstring))
  444. err = -1;
  445. }
  446. if (fclose(fp) != 0)
  447. G_fatal_error(_("Error closing output file <%s>: %s"), path, strerror(errno));
  448. return err;
  449. }
  450. /*!
  451. \brief Write srid (spatial reference id) to file
  452. A srid consists of an authority name and code and must be known to
  453. PROJ.
  454. \param location_name name of the location to write the srid
  455. \param sridstring pointer to srid string
  456. \return 0 success
  457. \return -1 error writing
  458. */
  459. int G_write_projsrid(const char *location_name, const char *sridstring)
  460. {
  461. FILE *fp;
  462. char path[GPATH_MAX];
  463. int err, n;
  464. if (!sridstring)
  465. return 0;
  466. if (location_name && *location_name)
  467. sprintf(path, "%s/%s/%s/%s", G_gisdbase(), location_name, "PERMANENT", SRID_FILE);
  468. else
  469. G_file_name(path, "", SRID_FILE, "PERMANENT");
  470. fp = fopen(path, "w");
  471. if (!fp)
  472. G_fatal_error(_("Unable to open output file <%s>: %s"), path, strerror(errno));
  473. err = 0;
  474. n = strlen(sridstring);
  475. if (sridstring[n - 1] != '\n') {
  476. if (n != fprintf(fp, "%s\n", sridstring))
  477. err = -1;
  478. }
  479. else {
  480. if (n != fprintf(fp, "%s", sridstring))
  481. err = -1;
  482. }
  483. if (fclose(fp) != 0)
  484. G_fatal_error(_("Error closing output file <%s>: %s"), path, strerror(errno));
  485. return err;
  486. }