123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /*
- ****************************************************************************
- *
- * MODULE: g.proj
- * AUTHOR(S): Paul Kelly - paul-grass@stjohnspoint.co.uk
- * Frank Warmerdam
- * Radim Blazek
- * PURPOSE: Provides a means of reporting the contents of GRASS
- * projection information files and creating
- * new projection information files.
- * COPYRIGHT: (C) 2001-2007 by the GRASS Development Team
- *
- * This program is free software under the GNU General Public
- * License (>=v2). Read the file COPYING that comes with GRASS
- * for details.
- *
- *****************************************************************************/
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
- #include <grass/gis.h>
- #include <grass/gprojects.h>
- #include <grass/glocale.h>
- #include <grass/config.h>
- #ifdef HAVE_OGR
- # include <gdal.h>
- # include <ogr_api.h>
- # include <cpl_csv.h>
- #endif
- #include "local_proto.h"
- static void set_default_region(void);
- /**
- * \brief Read projection and region information from current location
- *
- * Reads projection and region information from current location and
- * stores in global structs cellhd, projinfo and projunits.
- **/
- void input_currloc(void)
- {
- G_get_default_window(&cellhd);
- if (cellhd.proj != PROJECTION_XY) {
- projsrid = G_get_projsrid();
- projwkt = G_get_projwkt();
- projinfo = G_get_projinfo();
- projunits = G_get_projunits();
- /* projepsg = G_get_projepsg(); */
- }
- return;
- }
- /**
- * \brief Populates global cellhd with "default" region settings
- **/
- static void set_default_region(void)
- {
- /* If importing projection there will be no region information, so
- * set some default values */
- cellhd.rows = 1;
- cellhd.rows3 = 1;
- cellhd.cols = 1;
- cellhd.cols3 = 1;
- cellhd.depths = 1.;
- cellhd.north = 1.;
- cellhd.ns_res = 1.;
- cellhd.ns_res3 = 1.;
- cellhd.south = 0.;
- cellhd.west = 0.;
- cellhd.ew_res = 1.;
- cellhd.ew_res3 = 1.;
- cellhd.east = 1.;
- cellhd.top = 1.;
- cellhd.tb_res = 1.;
- cellhd.bottom = 0.;
- return;
- }
- #ifdef HAVE_OGR
- static void set_gdal_region(GDALDatasetH);
- static void set_authnamecode(OGRSpatialReferenceH);
- /**
- * \brief Read projection information in WKT format from stdin or a file
- *
- * Reads projection information from a file or stdin and stores in global
- * structs projinfo and projunits.
- * Populates global cellhd with default region information.
- *
- * \param wktfile File to read WKT co-ordinate system description from; -
- * for stdin
- *
- * \return 2 if a projected or lat/long co-ordinate system has been
- * defined; 1 if an unreferenced XY co-ordinate system has
- * been defined
- **/
- int input_wkt(char *wktfile)
- {
- FILE *infd;
- char buff[8192], *tmpwkt;
- OGRSpatialReferenceH hSRS;
- char *papszOptions[3];
- int ret;
- if (strcmp(wktfile, "-") == 0)
- infd = stdin;
- else
- infd = fopen(wktfile, "r");
- if (infd) {
- size_t wktlen;
- wktlen = fread(buff, 1, sizeof(buff), infd);
- if (wktlen == sizeof(buff))
- G_fatal_error(_("Input WKT definition is too long"));
- if (ferror(infd))
- G_fatal_error(_("Error reading WKT definition"));
- else
- fclose(infd);
- /* terminate WKT string */
- buff[wktlen] = '\0';
- /* Get rid of newlines */
- G_squeeze(buff);
- }
- else
- G_fatal_error(_("Unable to open file '%s' for reading"), wktfile);
- projwkt = G_store(buff);
- #if PROJ_VERSION_MAJOR >= 6
- /* validate input WKT */
- {
- PROJ_STRING_LIST wkt_warnings, wkt_grammar_errors;
- PJ *obj;
- wkt_warnings = NULL;
- wkt_grammar_errors = NULL;
- /* no strict validation */
- obj = proj_create_from_wkt(NULL, buff, NULL, &wkt_warnings,
- &wkt_grammar_errors);
- if (wkt_warnings) {
- int i;
- G_warning(_("WKT validation warnings:"));
- for (i = 0; wkt_warnings[i]; i++)
- G_warning("%s", wkt_warnings[i]);
- proj_string_list_destroy(wkt_warnings);
- }
- if (wkt_grammar_errors) {
- int i;
- G_warning(_("WKT validation grammar errors:"));
- for (i = 0; wkt_grammar_errors[i]; i++)
- G_warning("%s", wkt_grammar_errors[i]);
- proj_string_list_destroy(wkt_grammar_errors);
- }
- proj_destroy(obj);
- }
- #endif
- /* get GRASS proj info + units */
- /* NOTE: GPJ_wkt_to_grass() converts any WKT version to WKT1 */
- ret = GPJ_wkt_to_grass(&cellhd, &projinfo, &projunits, buff, 0);
- if (ret < 2)
- G_fatal_error(_("WKT not recognized: %s"), buff);
- set_default_region();
- /* find authname and authcode */
- hSRS = OSRNewSpatialReference(buff);
- /* get clean WKT definition */
- #if GDAL_VERSION_MAJOR >= 3
- papszOptions[0] = G_store("MULTILINE=YES");
- papszOptions[1] = G_store("FORMAT=WKT2");
- papszOptions[2] = NULL;
- OSRExportToWktEx(hSRS, &tmpwkt, (const char **)papszOptions);
- G_free(papszOptions[0]);
- G_free(papszOptions[1]);
- #else
- OSRExportToPrettyWkt(hSRS, &tmpwkt, FALSE);
- #endif
- projwkt = G_store(tmpwkt);
- CPLFree(tmpwkt);
- set_authnamecode(hSRS);
- OSRDestroySpatialReference(hSRS);
- return ret;
- }
- /**
- * \brief Read projection information in PROJ.4 format from a string
- * or stdin
- *
- * Reads projection information from a string or stdin and stores in global
- * structs projinfo and projunits.
- * Populates global cellhd with default region information.
- *
- * \param proj4params String representation of PROJ.4 co-ordinate system
- * description, or - to read it from stdin
- *
- * \return 2 if a projected or lat/long co-ordinate system has been
- * defined; 1 if an unreferenced XY co-ordinate system has
- * been defined
- **/
- int input_proj4(char *proj4params)
- {
- FILE *infd;
- char buff[8000];
- char *proj4string;
- OGRSpatialReferenceH hSRS;
- int ret = 0;
- /* TEST: use PROJ proj_create(), convert to WKT,
- * OSRImportFromWkt */
- if (strcmp(proj4params, "-") == 0) {
- infd = stdin;
- fgets(buff, sizeof(buff), infd);
- }
- else
- strcpy(buff, proj4params);
- #if PROJ_VERSION_MAJOR >= 6
- if (!strstr(buff, "+type=crs"))
- G_asprintf(&proj4string, "%s +no_defs +type=crs", buff);
- else
- G_asprintf(&proj4string, "%s +no_defs", buff);
- #else
- G_asprintf(&proj4string, "%s +no_defs", buff);
- #endif
- /* Set finder function for locating OGR csv co-ordinate system tables */
- /* SetCSVFilenameHook(GPJ_set_csv_loc); */
- hSRS = OSRNewSpatialReference(NULL);
- if (OSRImportFromProj4(hSRS, proj4string) != OGRERR_NONE)
- G_fatal_error(_("Can't parse PROJ.4-style parameter string"));
- G_free(proj4string);
- ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
- /* authority name and code not available in PROJ definitions */
- OSRDestroySpatialReference(hSRS);
- set_default_region();
- return ret;
- }
- /**
- * \brief Read projection information corresponding to a spatial
- * reference id (srid)
- *
- * Determines projection information corresponding to a srid
- * composed of authority name and code and stores in global structs
- * projinfo and projunits. Populates global cellhd with default region
- * information.
- *
- * Examples: "EPSG:4326", "urn:ogc:def:crs:EPSG::4326"
- *
- * \param srid spatial reference id
- *
- * \return 2 if a projected or lat/long co-ordinate system has been
- * defined; 1 if an unreferenced XY co-ordinate system has
- * been defined
- **/
- int input_srid(char *srid)
- {
- #if PROJ_VERSION_MAJOR >= 6
- OGRSpatialReferenceH hSRS;
- int ret = 0;
- char *papszOptions[3];
- PJ *obj;
- const char *tmpwkt;
- /* GDAL alternative: OSRSetFromUserInput() */
- obj = proj_create(NULL, srid);
- if (!obj)
- G_fatal_error(_("SRID <%s> not recognized by PROJ"), srid);
- tmpwkt = proj_as_wkt(NULL, obj, PJ_WKT2_LATEST, NULL);
- hSRS = OSRNewSpatialReference(tmpwkt);
- if (!hSRS)
- G_fatal_error(_("WKT for SRID <%s> not recognized by GDAL"), srid);
- projsrid = G_store(srid);
- /* WKT */
- papszOptions[0] = G_store("MULTILINE=YES");
- papszOptions[1] = G_store("FORMAT=WKT2");
- papszOptions[2] = NULL;
- if (OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions) != OGRERR_NONE)
- G_warning(_("Unable to convert srid to WKT"));
- G_free(papszOptions[0]);
- G_free(papszOptions[1]);
- /* GRASS proj info + units */
- ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
- proj_destroy(obj);
- OSRDestroySpatialReference(hSRS);
- set_default_region();
- return ret;
- #else
- G_fatal_error(_("Input srid requires GDAL 3+ and PROJ 6+"));
- return 0;
- #endif
- }
- /**
- * \brief Read projection information corresponding to an EPSG co-ordinate
- * system number
- *
- * Determines projection information corresponding to an EPSG co-ordinate
- * system number and stores in global structs projinfo and projunits.
- * Populates global cellhd with default region information.
- *
- * \param epsg_num EPSG number for co-ordinate system
- *
- * \return 2 if a projected or lat/long co-ordinate system has been
- * defined; 1 if an unreferenced XY co-ordinate system has
- * been defined
- **/
- int input_epsg(int epsg_num)
- {
- OGRSpatialReferenceH hSRS;
- char epsgstr[100];
- int ret = 0;
- char *papszOptions[3];
- /* Set finder function for locating OGR csv co-ordinate system tables */
- /* SetCSVFilenameHook(GPJ_set_csv_loc); */
- hSRS = OSRNewSpatialReference(NULL);
- if (OSRImportFromEPSG(hSRS, epsg_num) != OGRERR_NONE)
- G_fatal_error(_("Unable to translate EPSG code"));
- /* GRASS proj info + units */
- ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
- /* EPSG code */
- sprintf(epsgstr, "%d", epsg_num);
- projepsg = G_create_key_value();
- G_set_key_value("epsg", epsgstr, projepsg);
- /* srid as AUTHORITY:CODE */
- G_asprintf(&projsrid, "EPSG:%s", epsgstr);
- #if GDAL_VERSION_MAJOR >= 3
- /* WKT */
- papszOptions[0] = G_store("MULTILINE=YES");
- papszOptions[1] = G_store("FORMAT=WKT2");
- papszOptions[2] = NULL;
- if (OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions) != OGRERR_NONE)
- G_warning(_("Unable to convert EPSG code to WKT"));
- G_free(papszOptions[0]);
- G_free(papszOptions[1]);
- #endif
- OSRDestroySpatialReference(hSRS);
- set_default_region();
- return ret;
- }
- /**
- * \brief Read projection and region information associated with a
- * georeferenced file
- *
- * Reads projection information associated with a georeferenced file, if
- * available. Attempts are made to open the file with OGR and GDAL in turn.
- * (GDAL conventionally supports raster formats, and OGR vector formats.)
- *
- * If successful, projection and region information are read from the file
- * using GDAL/OGR functions and stored in global structs cellhd, projinfo
- * and projunits.
- *
- * \param geofile Path to georeferenced file
- *
- * \return 2 if a projected or lat/long co-ordinate system has been
- * defined; 1 if an unreferenced XY co-ordinate system has
- * been defined
- **/
- int input_georef(char *geofile)
- {
- /* GDAL >= 3 */
- #if GDAL_VERSION_MAJOR >= 3
- GDALDatasetH hDS = NULL;
- OGRSpatialReferenceH hSRS = NULL;
- int ret = 0;
- GDALAllRegister();
- /* Try opening file as vector first because it doesn't output a
- * (potentially confusing) error message if it can't open the file */
- /* Try opening as vector */
- G_debug(1, "Trying to open <%s> as vector...", geofile);
- if ((hDS = GDALOpenEx(geofile, GDAL_OF_VECTOR, NULL, NULL, NULL))
- && GDALDatasetGetLayerCount(hDS) > 0) {
- OGRLayerH ogr_layer;
- ogr_layer = GDALDatasetGetLayer(hDS, 0);
- hSRS = OGR_L_GetSpatialRef(ogr_layer);
- if (hSRS)
- set_default_region();
- }
- else {
- /* Try opening as raster */
- G_debug(1, "Trying to open <%s> as raster...", geofile);
- if ((hDS = GDALOpen(geofile, GA_ReadOnly))) {
- char **sds;
- /* does the dataset include subdatasets? */
- sds = GDALGetMetadata(hDS, "SUBDATASETS");
- if (sds && *sds) {
- G_warning(_("Input dataset <%s> contains subdatasets. "
- "Please select a subdataset."), geofile);
- }
- else {
- hSRS = GDALGetSpatialRef(hDS);
- if (hSRS)
- set_gdal_region(hDS);
- }
- }
- else {
- int namelen;
- namelen = strlen(geofile);
- if (namelen > 4 && G_strcasecmp(geofile + (namelen - 4), ".prj") == 0) {
- G_warning(_("<%s> is not a GDAL dataset, trying to open it as ESRI WKT"),
- geofile);
- return input_wkt(geofile);
- }
- else {
- G_fatal_error(_("Unable to read georeferenced file <%s> using "
- "GDAL library"), geofile);
- }
- }
- }
- if (hSRS) {
- char **papszOptions = NULL;
- ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
- if (cellhd.proj == PROJECTION_XY)
- G_warning(_("Read of file %s was successful, but it did not contain "
- "projection information. 'XY (unprojected)' will be used"),
- geofile);
- papszOptions = G_calloc(3, sizeof(char *));
- papszOptions[0] = G_store("MULTILINE=YES");
- papszOptions[1] = G_store("FORMAT=WKT2");
- OSRExportToWktEx(hSRS, &projwkt, (const char **)papszOptions);
- G_free(papszOptions[0]);
- G_free(papszOptions[1]);
- G_free(papszOptions);
- set_authnamecode(hSRS);
- }
- if (hDS)
- GDALClose(hDS);
- return ret;
- /* GDAL < 3 */
- #else
- OGRDataSourceH ogr_ds;
- OGRSpatialReferenceH hSRS;
- int ret = 0;
- /* Try opening file with OGR first because it doesn't output a
- * (potentially confusing) error message if it can't open the file */
- G_debug(1, "Trying to open <%s> with OGR...", geofile);
- OGRRegisterAll();
- ogr_ds = NULL;
- hSRS = NULL;
- /* Try opening with OGR */
- if ((ogr_ds = OGROpen(geofile, FALSE, NULL))
- && (OGR_DS_GetLayerCount(ogr_ds) > 0)) {
- OGRLayerH ogr_layer;
- G_debug(1, "...succeeded.");
- /* Get the first layer */
- ogr_layer = OGR_DS_GetLayer(ogr_ds, 0);
- hSRS = OGR_L_GetSpatialRef(ogr_layer);
- ret = GPJ_osr_to_grass(&cellhd, &projinfo, &projunits, hSRS, 0);
- OSRExportToWkt(hSRS, &projwkt);
- set_default_region();
- }
- else {
- /* Try opening with GDAL */
- GDALDatasetH gdal_ds;
- G_debug(1, "Trying to open with GDAL...");
- GDALAllRegister();
- if ((gdal_ds = GDALOpen(geofile, GA_ReadOnly))) {
- char *wktstring;
- G_debug(1, "...succeeded.");
- /* TODO: change for GDAL 3+ */
- wktstring = (char *)GDALGetProjectionRef(gdal_ds);
- projwkt = G_store(wktstring);
- ret =
- GPJ_wkt_to_grass(&cellhd, &projinfo, &projunits, projwkt,
- 0);
- set_gdal_region(gdal_ds);
- hSRS = OSRNewSpatialReference(projwkt);
- GDALClose(gdal_ds);
- }
- else {
- int namelen;
- namelen = strlen(geofile);
- if (namelen > 4 && G_strcasecmp(geofile + (namelen - 4), ".prj") == 0) {
- G_warning(_("<%s> is not a GDAL dataset, trying to open it as ESRI WKT"),
- geofile);
- return input_wkt(geofile);
- }
- else {
- G_fatal_error(_("Unable to read georeferenced file <%s> using "
- "GDAL library"), geofile);
- }
- }
- }
- if (cellhd.proj == PROJECTION_XY)
- G_warning(_("Read of file %s was successful, but it did not contain "
- "projection information. 'XY (unprojected)' will be used"),
- geofile);
- set_authnamecode(hSRS);
- if (ogr_ds)
- OGR_DS_Destroy(ogr_ds);
- else
- OSRDestroySpatialReference(hSRS);
- return ret;
- #endif
- }
- /**
- * \brief Populates global cellhd with region settings based on
- * georeferencing information in a GDAL dataset
- *
- * \param hDS GDAL dataset object to retrieve georeferencing information from
- **/
- static void set_gdal_region(GDALDatasetH hDS)
- {
- double adfGeoTransform[6];
- /* Populate with initial values in case we can't set everything */
- set_default_region();
- /* Code below originally from r.in.gdal */
- cellhd.rows = GDALGetRasterYSize(hDS);
- cellhd.cols = GDALGetRasterXSize(hDS);
- cellhd.rows3 = cellhd.rows;
- cellhd.cols3 = cellhd.cols;
- if (GDALGetGeoTransform(hDS, adfGeoTransform) == CE_None
- && adfGeoTransform[5] < 0.0) {
- if (adfGeoTransform[2] != 0.0 || adfGeoTransform[4] != 0.0) {
- /* Map is rotated. Calculation of north/south extents and
- * resolution more complicated.
- * TODO: do it anyway */
- return;
- }
- cellhd.north = adfGeoTransform[3];
- cellhd.ns_res = fabs(adfGeoTransform[5]);
- cellhd.south = cellhd.north - cellhd.ns_res * cellhd.rows;
- cellhd.west = adfGeoTransform[0];
- cellhd.ew_res = adfGeoTransform[1];
- cellhd.east = cellhd.west + cellhd.cols * cellhd.ew_res;
- cellhd.ns_res3 = cellhd.ns_res;
- cellhd.ew_res3 = cellhd.ew_res;
- }
- else {
- cellhd.north = cellhd.rows;
- cellhd.east = cellhd.cols;
- }
- return;
- }
- void set_authnamecode(OGRSpatialReferenceH hSRS)
- {
- const char *authkey, *authname, *authcode;
- if (!hSRS)
- return;
- authkey = NULL;
- if (OSRIsProjected(hSRS))
- authkey = "PROJCS";
- else if (OSRIsGeographic(hSRS))
- authkey = "GEOGCS";
- if (authkey) {
- authname = OSRGetAuthorityName(hSRS, authkey);
- if (authname && *authname) {
- authcode = OSRGetAuthorityCode(hSRS, authkey);
- if (authcode && *authcode) {
- G_asprintf(&projsrid, "%s:%s", authname, authcode);
- /* for backwards compatibility; remove ? */
- if (strcmp(authname, "EPSG") == 0) {
- projepsg = G_create_key_value();
- G_set_key_value("epsg", authcode, projepsg);
- }
- }
- }
- }
- }
- #endif /* HAVE_OGR */
|