input.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. /*
  2. ****************************************************************************
  3. *
  4. * MODULE: g.proj
  5. * AUTHOR(S): Paul Kelly - paul-grass@stjohnspoint.co.uk
  6. * Frank Warmerdam
  7. * Radim Blazek
  8. * PURPOSE: Provides a means of reporting the contents of GRASS
  9. * projection information files and creating
  10. * new projection information files.
  11. * COPYRIGHT: (C) 2001-2007 by the GRASS Development Team
  12. *
  13. * This program is free software under the GNU General Public
  14. * License (>=v2). Read the file COPYING that comes with GRASS
  15. * for details.
  16. *
  17. *****************************************************************************/
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <math.h>
  21. #include <grass/gis.h>
  22. #include <grass/gprojects.h>
  23. #include <grass/glocale.h>
  24. #include <grass/config.h>
  25. #ifdef HAVE_OGR
  26. # include <gdal.h>
  27. # include <ogr_api.h>
  28. # include <cpl_csv.h>
  29. #endif
  30. #include "local_proto.h"
  31. static void set_default_region(void);
  32. /**
  33. * \brief Read projection and region information from current location
  34. *
  35. * Reads projection and region information from current location and
  36. * stores in global structs cellhd, projinfo and projunits.
  37. **/
  38. void input_currloc(void)
  39. {
  40. G_get_default_window(&cellhd);
  41. if (cellhd.proj != PROJECTION_XY) {
  42. projsrid = G_get_projsrid();
  43. projwkt = G_get_projwkt();
  44. projinfo = G_get_projinfo();
  45. projunits = G_get_projunits();
  46. /* projepsg = G_get_projepsg(); */
  47. }
  48. return;
  49. }
  50. /**
  51. * \brief Populates global cellhd with "default" region settings
  52. **/
  53. static void set_default_region(void)
  54. {
  55. /* If importing projection there will be no region information, so
  56. * set some default values */
  57. cellhd.rows = 1;
  58. cellhd.rows3 = 1;
  59. cellhd.cols = 1;
  60. cellhd.cols3 = 1;
  61. cellhd.depths = 1.;
  62. cellhd.north = 1.;
  63. cellhd.ns_res = 1.;
  64. cellhd.ns_res3 = 1.;
  65. cellhd.south = 0.;
  66. cellhd.west = 0.;
  67. cellhd.ew_res = 1.;
  68. cellhd.ew_res3 = 1.;
  69. cellhd.east = 1.;
  70. cellhd.top = 1.;
  71. cellhd.tb_res = 1.;
  72. cellhd.bottom = 0.;
  73. return;
  74. }
  75. #ifdef HAVE_OGR
  76. static void set_gdal_region(GDALDatasetH);
  77. static void set_authnamecode(OGRSpatialReferenceH);
  78. /**
  79. * \brief Read projection information in WKT format from stdin or a file
  80. *
  81. * Reads projection information from a file or stdin and stores in global
  82. * structs projinfo and projunits.
  83. * Populates global cellhd with default region information.
  84. *
  85. * \param wktfile File to read WKT co-ordinate system description from; -
  86. * for stdin
  87. *
  88. * \return 2 if a projected or lat/long co-ordinate system has been
  89. * defined; 1 if an unreferenced XY co-ordinate system has
  90. * been defined
  91. **/
  92. int input_wkt(char *wktfile)
  93. {
  94. FILE *infd;
  95. char buff[8192], *tmpwkt;
  96. OGRSpatialReferenceH hSRS;
  97. char *papszOptions[3];
  98. int ret;
  99. if (strcmp(wktfile, "-") == 0)
  100. infd = stdin;
  101. else
  102. infd = fopen(wktfile, "r");
  103. if (infd) {
  104. size_t wktlen;
  105. wktlen = fread(buff, 1, sizeof(buff), infd);
  106. if (wktlen == sizeof(buff))
  107. G_fatal_error(_("Input WKT definition is too long"));
  108. if (ferror(infd))
  109. G_fatal_error(_("Error reading WKT definition"));
  110. else
  111. fclose(infd);
  112. /* terminate WKT string */
  113. buff[wktlen] = '\0';
  114. /* Get rid of newlines */
  115. G_squeeze(buff);
  116. }
  117. else
  118. G_fatal_error(_("Unable to open file '%s' for reading"), wktfile);
  119. projwkt = G_store(buff);
  120. #if PROJ_VERSION_MAJOR >= 6
  121. /* validate input WKT */
  122. {
  123. PROJ_STRING_LIST wkt_warnings, wkt_grammar_errors;
  124. PJ *obj;
  125. wkt_warnings = NULL;
  126. wkt_grammar_errors = NULL;
  127. /* no strict validation */
  128. obj = proj_create_from_wkt(NULL, buff, NULL, &wkt_warnings,
  129. &wkt_grammar_errors);
  130. if (wkt_warnings) {
  131. int i;
  132. G_warning(_("WKT validation warnings:"));
  133. for (i = 0; wkt_warnings[i]; i++)
  134. G_warning("%s", wkt_warnings[i]);
  135. proj_string_list_destroy(wkt_warnings);
  136. }
  137. if (wkt_grammar_errors) {
  138. int i;
  139. G_warning(_("WKT validation grammar errors:"));
  140. for (i = 0; wkt_grammar_errors[i]; i++)
  141. G_warning("%s", wkt_grammar_errors[i]);
  142. proj_string_list_destroy(wkt_grammar_errors);
  143. }
  144. proj_destroy(obj);
  145. }
  146. #endif
  147. /* get GRASS proj info + units */
  148. /* NOTE: GPJ_wkt_to_grass() converts any WKT version to WKT1 */
  149. ret = GPJ_wkt_to_grass(&cellhd, &projinfo, &projunits, buff, 0);
  150. if (ret < 2)
  151. G_fatal_error(_("WKT not recognized: %s"), buff);
  152. set_default_region();
  153. /* find authname and authcode */
  154. hSRS = OSRNewSpatialReference(buff);
  155. /* get clean WKT definition */
  156. #if GDAL_VERSION_MAJOR >= 3
  157. papszOptions[0] = G_store("MULTILINE=YES");
  158. papszOptions[1] = G_store("FORMAT=WKT2");
  159. papszOptions[2] = NULL;
  160. OSRExportToWktEx(hSRS, &tmpwkt, (const char **)papszOptions);
  161. G_free(papszOptions[0]);
  162. G_free(papszOptions[1]);
  163. #else
  164. OSRExportToPrettyWkt(hSRS, &tmpwkt, FALSE);
  165. #endif
  166. projwkt = G_store(tmpwkt);
  167. CPLFree(tmpwkt);
  168. set_authnamecode(hSRS);
  169. OSRDestroySpatialReference(hSRS);
  170. return ret;
  171. }
  172. /**
  173. * \brief Read projection information in PROJ.4 format from a string
  174. * or stdin
  175. *
  176. * Reads projection information from a string or stdin and stores in global
  177. * structs projinfo and projunits.
  178. * Populates global cellhd with default region information.
  179. *
  180. * \param proj4params String representation of PROJ.4 co-ordinate system
  181. * description, or - to read it from stdin
  182. *
  183. * \return 2 if a projected or lat/long co-ordinate system has been
  184. * defined; 1 if an unreferenced XY co-ordinate system has
  185. * been defined
  186. **/
  187. int input_proj4(char *proj4params)
  188. {
  189. FILE *infd;
  190. char buff[8000];
  191. char *proj4string;
  192. OGRSpatialReferenceH hSRS;
  193. int ret = 0;
  194. /* TEST: use PROJ proj_create(), convert to WKT,
  195. * OSRImportFromWkt */
  196. if (strcmp(proj4params, "-") == 0) {
  197. infd = stdin;
  198. fgets(buff, sizeof(buff), infd);
  199. }
  200. else
  201. strcpy(buff, proj4params);
  202. #if PROJ_VERSION_MAJOR >= 6
  203. if (!strstr(buff, "+type=crs"))
  204. G_asprintf(&proj4string, "%s +no_defs +type=crs", buff);
  205. else
  206. G_asprintf(&proj4string, "%s +no_defs", buff);
  207. #else
  208. G_asprintf(&proj4string, "%s +no_defs", buff);
  209. #endif
  210. /* Set finder function for locating OGR csv co-ordinate system tables */
  211. /* SetCSVFilenameHook(GPJ_set_csv_loc); */
  212. hSRS = OSRNewSpatialReference(NULL);
  213. if (OSRImportFromProj4(hSRS, proj4string) != OGRERR_NONE)
  214. G_fatal_error(_("Can't parse PROJ.4-style parameter string"));
  215. G_free(proj4string);
  216. ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
  217. /* authority name and code not available in PROJ definitions */
  218. OSRDestroySpatialReference(hSRS);
  219. set_default_region();
  220. return ret;
  221. }
  222. /**
  223. * \brief Read projection information corresponding to a spatial
  224. * reference id (srid)
  225. *
  226. * Determines projection information corresponding to a srid
  227. * composed of authority name and code and stores in global structs
  228. * projinfo and projunits. Populates global cellhd with default region
  229. * information.
  230. *
  231. * Examples: "EPSG:4326", "urn:ogc:def:crs:EPSG::4326"
  232. *
  233. * \param srid spatial reference id
  234. *
  235. * \return 2 if a projected or lat/long co-ordinate system has been
  236. * defined; 1 if an unreferenced XY co-ordinate system has
  237. * been defined
  238. **/
  239. int input_srid(char *srid)
  240. {
  241. #if PROJ_VERSION_MAJOR >= 6
  242. OGRSpatialReferenceH hSRS;
  243. int ret = 0;
  244. char *papszOptions[3];
  245. PJ *obj;
  246. const char *tmpwkt;
  247. /* GDAL alternative: OSRSetFromUserInput() */
  248. obj = proj_create(NULL, srid);
  249. if (!obj)
  250. G_fatal_error(_("SRID <%s> not recognized by PROJ"), srid);
  251. tmpwkt = proj_as_wkt(NULL, obj, PJ_WKT2_LATEST, NULL);
  252. hSRS = OSRNewSpatialReference(tmpwkt);
  253. if (!hSRS)
  254. G_fatal_error(_("WKT for SRID <%s> not recognized by GDAL"), srid);
  255. projsrid = G_store(srid);
  256. /* WKT */
  257. papszOptions[0] = G_store("MULTILINE=YES");
  258. papszOptions[1] = G_store("FORMAT=WKT2");
  259. papszOptions[2] = NULL;
  260. if (OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions) != OGRERR_NONE)
  261. G_warning(_("Unable to convert srid to WKT"));
  262. G_free(papszOptions[0]);
  263. G_free(papszOptions[1]);
  264. /* GRASS proj info + units */
  265. ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
  266. proj_destroy(obj);
  267. OSRDestroySpatialReference(hSRS);
  268. set_default_region();
  269. return ret;
  270. #else
  271. G_fatal_error(_("Input srid requires GDAL 3+ and PROJ 6+"));
  272. return 0;
  273. #endif
  274. }
  275. /**
  276. * \brief Read projection information corresponding to an EPSG co-ordinate
  277. * system number
  278. *
  279. * Determines projection information corresponding to an EPSG co-ordinate
  280. * system number and stores in global structs projinfo and projunits.
  281. * Populates global cellhd with default region information.
  282. *
  283. * \param epsg_num EPSG number for co-ordinate system
  284. *
  285. * \return 2 if a projected or lat/long co-ordinate system has been
  286. * defined; 1 if an unreferenced XY co-ordinate system has
  287. * been defined
  288. **/
  289. int input_epsg(int epsg_num)
  290. {
  291. OGRSpatialReferenceH hSRS;
  292. char epsgstr[100];
  293. int ret = 0;
  294. char *papszOptions[3];
  295. /* Set finder function for locating OGR csv co-ordinate system tables */
  296. /* SetCSVFilenameHook(GPJ_set_csv_loc); */
  297. hSRS = OSRNewSpatialReference(NULL);
  298. if (OSRImportFromEPSG(hSRS, epsg_num) != OGRERR_NONE)
  299. G_fatal_error(_("Unable to translate EPSG code"));
  300. /* GRASS proj info + units */
  301. ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
  302. /* EPSG code */
  303. sprintf(epsgstr, "%d", epsg_num);
  304. projepsg = G_create_key_value();
  305. G_set_key_value("epsg", epsgstr, projepsg);
  306. /* srid as AUTHORITY:CODE */
  307. G_asprintf(&projsrid, "EPSG:%s", epsgstr);
  308. #if GDAL_VERSION_MAJOR >= 3
  309. /* WKT */
  310. papszOptions[0] = G_store("MULTILINE=YES");
  311. papszOptions[1] = G_store("FORMAT=WKT2");
  312. papszOptions[2] = NULL;
  313. if (OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions) != OGRERR_NONE)
  314. G_warning(_("Unable to convert EPSG code to WKT"));
  315. G_free(papszOptions[0]);
  316. G_free(papszOptions[1]);
  317. #endif
  318. OSRDestroySpatialReference(hSRS);
  319. set_default_region();
  320. return ret;
  321. }
  322. /**
  323. * \brief Read projection and region information associated with a
  324. * georeferenced file
  325. *
  326. * Reads projection information associated with a georeferenced file, if
  327. * available. Attempts are made to open the file with OGR and GDAL in turn.
  328. * (GDAL conventionally supports raster formats, and OGR vector formats.)
  329. *
  330. * If successful, projection and region information are read from the file
  331. * using GDAL/OGR functions and stored in global structs cellhd, projinfo
  332. * and projunits.
  333. *
  334. * \param geofile Path to georeferenced file
  335. *
  336. * \return 2 if a projected or lat/long co-ordinate system has been
  337. * defined; 1 if an unreferenced XY co-ordinate system has
  338. * been defined
  339. **/
  340. int input_georef(char *geofile)
  341. {
  342. /* GDAL >= 3 */
  343. #if GDAL_VERSION_MAJOR >= 3
  344. GDALDatasetH hDS = NULL;
  345. OGRSpatialReferenceH hSRS = NULL;
  346. int ret = 0;
  347. GDALAllRegister();
  348. /* Try opening file as vector first because it doesn't output a
  349. * (potentially confusing) error message if it can't open the file */
  350. /* Try opening as vector */
  351. G_debug(1, "Trying to open <%s> as vector...", geofile);
  352. if ((hDS = GDALOpenEx(geofile, GDAL_OF_VECTOR, NULL, NULL, NULL))
  353. && GDALDatasetGetLayerCount(hDS) > 0) {
  354. OGRLayerH ogr_layer;
  355. ogr_layer = GDALDatasetGetLayer(hDS, 0);
  356. hSRS = OGR_L_GetSpatialRef(ogr_layer);
  357. if (hSRS)
  358. set_default_region();
  359. }
  360. else {
  361. /* Try opening as raster */
  362. G_debug(1, "Trying to open <%s> as raster...", geofile);
  363. if ((hDS = GDALOpen(geofile, GA_ReadOnly))) {
  364. char **sds;
  365. /* does the dataset include subdatasets? */
  366. sds = GDALGetMetadata(hDS, "SUBDATASETS");
  367. if (sds && *sds) {
  368. G_warning(_("Input dataset <%s> contains subdatasets. "
  369. "Please select a subdataset."), geofile);
  370. }
  371. else {
  372. hSRS = GDALGetSpatialRef(hDS);
  373. if (hSRS)
  374. set_gdal_region(hDS);
  375. }
  376. }
  377. else {
  378. int namelen;
  379. namelen = strlen(geofile);
  380. if (namelen > 4 && G_strcasecmp(geofile + (namelen - 4), ".prj") == 0) {
  381. G_warning(_("<%s> is not a GDAL dataset, trying to open it as ESRI WKT"),
  382. geofile);
  383. return input_wkt(geofile);
  384. }
  385. else {
  386. G_fatal_error(_("Unable to read georeferenced file <%s> using "
  387. "GDAL library"), geofile);
  388. }
  389. }
  390. }
  391. if (hSRS) {
  392. char **papszOptions = NULL;
  393. ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
  394. if (cellhd.proj == PROJECTION_XY)
  395. G_warning(_("Read of file %s was successful, but it did not contain "
  396. "projection information. 'XY (unprojected)' will be used"),
  397. geofile);
  398. papszOptions = G_calloc(3, sizeof(char *));
  399. papszOptions[0] = G_store("MULTILINE=YES");
  400. papszOptions[1] = G_store("FORMAT=WKT2");
  401. OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions);
  402. G_free(papszOptions[0]);
  403. G_free(papszOptions[1]);
  404. G_free(papszOptions);
  405. set_authnamecode(hSRS);
  406. }
  407. if (hDS)
  408. GDALClose(hDS);
  409. return ret;
  410. /* GDAL < 3 */
  411. #else
  412. OGRDataSourceH ogr_ds;
  413. OGRSpatialReferenceH hSRS;
  414. int ret = 0;
  415. /* Try opening file with OGR first because it doesn't output a
  416. * (potentially confusing) error message if it can't open the file */
  417. G_debug(1, "Trying to open <%s> with OGR...", geofile);
  418. OGRRegisterAll();
  419. ogr_ds = NULL;
  420. hSRS = NULL;
  421. /* Try opening with OGR */
  422. if ((ogr_ds = OGROpen(geofile, FALSE, NULL))
  423. && (OGR_DS_GetLayerCount(ogr_ds) > 0)) {
  424. OGRLayerH ogr_layer;
  425. G_debug(1, "...succeeded.");
  426. /* Get the first layer */
  427. ogr_layer = OGR_DS_GetLayer(ogr_ds, 0);
  428. hSRS = OGR_L_GetSpatialRef(ogr_layer);
  429. ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
  430. OSRExportToWkt(hSRS, &projwkt);
  431. set_default_region();
  432. }
  433. else {
  434. /* Try opening with GDAL */
  435. GDALDatasetH gdal_ds;
  436. G_debug(1, "Trying to open with GDAL...");
  437. GDALAllRegister();
  438. if ((gdal_ds = GDALOpen(geofile, GA_ReadOnly))) {
  439. char *wktstring;
  440. G_debug(1, "...succeeded.");
  441. /* TODO: change for GDAL 3+ */
  442. wktstring = (char *)GDALGetProjectionRef(gdal_ds);
  443. projwkt = G_store(wktstring);
  444. ret =
  445. GPJ_wkt_to_grass(&cellhd, &projinfo, &projunits, projwkt,
  446. 0);
  447. set_gdal_region(gdal_ds);
  448. hSRS = OSRNewSpatialReference(projwkt);
  449. GDALClose(gdal_ds);
  450. }
  451. else {
  452. int namelen;
  453. namelen = strlen(geofile);
  454. if (namelen > 4 && G_strcasecmp(geofile + (namelen - 4), ".prj") == 0) {
  455. G_warning(_("<%s> is not a GDAL dataset, trying to open it as ESRI WKT"),
  456. geofile);
  457. return input_wkt(geofile);
  458. }
  459. else {
  460. G_fatal_error(_("Unable to read georeferenced file <%s> using "
  461. "GDAL library"), geofile);
  462. }
  463. }
  464. }
  465. if (cellhd.proj == PROJECTION_XY)
  466. G_warning(_("Read of file %s was successful, but it did not contain "
  467. "projection information. 'XY (unprojected)' will be used"),
  468. geofile);
  469. set_authnamecode(hSRS);
  470. if (ogr_ds)
  471. OGR_DS_Destroy(ogr_ds);
  472. else
  473. OSRDestroySpatialReference(hSRS);
  474. return ret;
  475. #endif
  476. }
  477. /**
  478. * \brief Populates global cellhd with region settings based on
  479. * georeferencing information in a GDAL dataset
  480. *
  481. * \param hDS GDAL dataset object to retrieve georeferencing information from
  482. **/
  483. static void set_gdal_region(GDALDatasetH hDS)
  484. {
  485. double adfGeoTransform[6];
  486. /* Populate with initial values in case we can't set everything */
  487. set_default_region();
  488. /* Code below originally from r.in.gdal */
  489. cellhd.rows = GDALGetRasterYSize(hDS);
  490. cellhd.cols = GDALGetRasterXSize(hDS);
  491. cellhd.rows3 = cellhd.rows;
  492. cellhd.cols3 = cellhd.cols;
  493. if (GDALGetGeoTransform(hDS, adfGeoTransform) == CE_None
  494. && adfGeoTransform[5] < 0.0) {
  495. if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) {
  496. /* Map is rotated. Calculation of north/south extents and
  497. * resolution more complicated.
  498. * TODO: do it anyway */
  499. return;
  500. }
  501. cellhd.north = adfGeoTransform[3];
  502. cellhd.ns_res = fabs(adfGeoTransform[5]);
  503. cellhd.south = cellhd.north - cellhd.ns_res * cellhd.rows;
  504. cellhd.west = adfGeoTransform[0];
  505. cellhd.ew_res = adfGeoTransform[1];
  506. cellhd.east = cellhd.west + cellhd.cols * cellhd.ew_res;
  507. cellhd.ns_res3 = cellhd.ns_res;
  508. cellhd.ew_res3 = cellhd.ew_res;
  509. }
  510. else {
  511. cellhd.north = cellhd.rows;
  512. cellhd.east = cellhd.cols;
  513. }
  514. return;
  515. }
  516. void set_authnamecode(OGRSpatialReferenceH hSRS)
  517. {
  518. const char *authkey, *authname, *authcode;
  519. if (!hSRS)
  520. return;
  521. authkey = NULL;
  522. if (OSRIsProjected(hSRS))
  523. authkey = "PROJCS";
  524. else if (OSRIsGeographic(hSRS))
  525. authkey = "GEOGCS";
  526. if (authkey) {
  527. authname = OSRGetAuthorityName(hSRS, authkey);
  528. if (authname && *authname) {
  529. authcode = OSRGetAuthorityCode(hSRS, authkey);
  530. if (authcode && *authcode) {
  531. G_asprintf(&projsrid, "%s:%s", authname, authcode);
  532. /* for backwards compatibility; remove ? */
  533. if (strcmp(authname, "EPSG") == 0) {
  534. projepsg = G_create_key_value();
  535. G_set_key_value("epsg", authcode, projepsg);
  536. }
  537. }
  538. }
  539. }
  540. }
  541. #endif /* HAVE_OGR */