diff --git a/python/core/qgscoordinatereferencesystem.sip b/python/core/qgscoordinatereferencesystem.sip index e5b1ce46f13..f5439975a34 100644 --- a/python/core/qgscoordinatereferencesystem.sip +++ b/python/core/qgscoordinatereferencesystem.sip @@ -1,6 +1,146 @@ /** \ingroup core - * Class for storing a coordinate reference system (CRS) + * This class represents a coordinate reference system (CRS). + * + * Coordinate reference system object defines a specific map projection, as well as transformations + * between different coordinate reference systems. There are various ways how a CRS can be defined: + * using well-known text (WKT), PROJ.4 string or combination of authority and code (e.g. EPSG:4326). + * QGIS comes with its internal database of coordinate reference systems (stored in SQLite) that + * allows lookups of CRS and seamless conversions between the various definitions. + * + * Most commonly one comes across two types of coordinate systems: + * + * 1. **Geographic coordinate systems** - based on a geodetic datum, normally with coordinates being + * latitude/longitude in degrees. The most common one is World Geodetic System 84 (WGS84). + * 2. **Projected coordinate systems** - based on a geodetic datum with coordinates projected to a plane, + * typically using meters or feet as units. Common projected coordinate systems are Universal + * Transverse Mercator or Albers Equal Area. + * + * Internally QGIS uses proj4 library for all the math behind coordinate transformations, so in case + * of any troubles with projections it is best to examine the PROJ.4 representation within the object, + * as that is the representation that will be ultimately used. + * + * Methods that allow inspection of CRS instances include isValid(), authid(), description(), + * toWkt(), toProj4(), mapUnits() and others. + * Creation of CRS instances is further described in \ref crs_construct_and_copy section below. + * Transformations between coordinate reference systems are done using QgsCoordinateTransform class. + * + * For example, the following code will create and inspect "British national grid" CRS: + * + * ~~~{.py} + * crs = QgsCoordinateReferenceSystem("EPSG:27700") + * if crs.isValid(): + * print("CRS Description: {}".format(crs.description())) + * print("CRS PROJ.4 text: {}".format(crs.toProj4())) + * else: + * print("Invalid CRS!") + * ~~~ + * + * This will produce the following output: + * + * ~~~ + * CRS Description: OSGB 1936 / British National Grid + * CRS PROJ.4 text: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 [output trimmed] + * ~~~ + * + * CRS Definition Formats + * ====================== + * + * This section gives an overview of various supported CRS definition formats: + * + * 1. **Authority and Code.** Also referred to as OGC WMS format within QGIS as they have been widely + * used in OGC standards. These are encoded as `:`, for example `EPSG:4326` refers + * to WGS84 system. EPSG is the most commonly used authority that covers a wide range + * of coordinate systems around the world. + * + * An extended variant of this format is OGC URN. Syntax of URN for CRS definition is + * `urn:ogc:def:crs::[]:`. This class can also parse URNs (versions + * are currently ignored). For example, WGS84 may be encoded as `urn:ogc:def:crs:OGC:1.3:CRS84`. + * + * QGIS adds support for "USER" authority that refers to IDs used internally in QGIS. This variant + * is best avoided or used with caution as the IDs are not permanent and they refer to different CRS + * on different machines or user profiles. + * + * See authid() and createFromOgcWmsCrs() methods. + * + * 2. **PROJ.4 string.** This is a string consisting of a series of key/value pairs in the following + * format: `+param1=value1 +param2=value2 [...]`. This is the format natively used by the + * underlying proj4 library. For example, the definition of WGS84 looks like this: + * + * +proj=longlat +datum=WGS84 +no_defs + * + * See toProj4() and createFromProj4() methods. + * + * 3. **Well-known text (WKT).** Defined by Open Geospatial Consortium (OGC), this is another common + * format to define CRS. For WGS84 the OGC WKT definition is the following: + * + * GEOGCS["WGS 84", + * DATUM["WGS_1984", + * SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]], + * AUTHORITY["EPSG","6326"]], + * PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]], + * UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]], + * AUTHORITY["EPSG","4326"]] + * + * See toWkt() and createFromWkt() methods. + * + * CRS Database and Custom CRS + * =========================== + * + * The database of CRS shipped with QGIS is stored in a SQLite database (see QgsApplication::srsDbFilePath()) + * and it is based on the data files maintained by GDAL project (a variety of .csv and .wkt files). + * + * Sometimes it happens that users need to use a CRS definition that is not well known + * or that has been only created with a specific purpose (and thus its definition is not + * available in our database of CRS). Whenever a new CRS definition is seen, it will + * be added to the local databse (in user's home directory, see QgsApplication::qgisUserDbFilePath()). + * QGIS also features a GUI for management of local custom CRS definitions. + * + * There are therefore two databases: one for shipped CRS definitions and one for custom CRS definitions. + * Custom CRS have internal IDs (accessible with srsid()) greater or equal to \ref USER_CRS_START_ID. + * The local CRS databases should never be accessed directly with SQLite functions, instead + * you should use QgsCoordinateReferenceSystem API for CRS lookups and for managements of custom CRS. + * + * Validation + * ========== + * + * In some cases (most prominently when loading a map layer), QGIS will try to ensure + * that the given map layer CRS is valid using validate() call. If not, a custom + * validation function will be called - such function may for example show a GUI + * for manual CRS selection. The validation function is configured using setCustomSrsValidation(). + * If validation fails or no validation function is set, the default CRS is assigned + * (WGS84). QGIS application registers its validation function that will act according + * to user's settings (either show CRS selector dialog or use project/custom CRS). + * + * Object Construction and Copying {#crs_construct_and_copy} + * =============================== + * + * The easiest way of creating CRS instances is to use QgsCoordinateReferenceSystem(const QString&) + * constructor that automatically recognizes definition format from the given string. + * + * Creation of CRS object involves some queries in a local SQLite database, which may + * be potentially expensive. It is therefore recommended to use QgsCRSCache methods + * that return possibly cached CRS objects instead of constructing new instances that + * involve the lookup overhead. + * + * Since QGIS 2.16 QgsCoordinateReferenceSystem objects are implicitly shared. + * + * The following table summarizes equivalents for non-cached and cached CRS lookup: + * + * Definition | Non-cached | Cached + * ----------- | --------------------- | ------------------------------ + * Auth + Code | createFromOgcWmsCrs() | QgsCRSCache::crsByOgcWmsCrs() + * PROJ.4 | createFromProj4() | QgsCRSCache::crsByProj4() + * WKT | createFromWkt() | QgsCRSCache::crsByWkt() + * + * Caveats + * ======= + * + * There are two different flavours of WKT: one is defined by OGC, the other is the standard + * used by ESRI. They look very similar, but they are not the same. QGIS is able to consume + * both flavours. + * + * \see QgsCoordinateTransform, QgsCRSCache */ class QgsCoordinateReferenceSystem { @@ -11,115 +151,137 @@ class QgsCoordinateReferenceSystem // typedef void (*CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem*); + //! Enumeration of types of IDs accepted in createFromId() method enum CrsType { - InternalCrsId, - PostgisCrsId, - EpsgCrsId // deprecated + InternalCrsId, //!< Internal ID used by QGIS in the local SQLite database + PostgisCrsId, //!< SRID used in PostGIS + EpsgCrsId //!< EPSG code }; - //! Default constructor + /** Constructs an invalid CRS object */ QgsCoordinateReferenceSystem(); ~QgsCoordinateReferenceSystem(); /*! - * Constructs a CRS object from a string definition as defined in the createFromString - * member function (by default a WKT definition). + * Constructs a CRS object from a string definition using createFromString() + * + * It supports the following formats: + * - "EPSG:" - handled with createFromOgcWms() + * - "POSTGIS:" - handled with createFromSrid() + * - "INTERNAL:" - handled with createFromSrsId() + * - "PROJ4:" - handled with createFromProj4() + * - "WKT:" - handled with createFromWkt() + * + * If no prefix is specified, WKT definition is assumed. * @param theDefinition A String containing a coordinate reference system definition. + * @see createFromString() */ + // TODO QGIS 3: remove "POSTGIS" and "INTERNAL", allow PROJ4 without the prefix explicit QgsCoordinateReferenceSystem( const QString& theDefinition ); - /** Use this constructor when you want to create a CRS object using - * a postgis SRID, an EpsgCrsId id or a QGIS CRS_ID. - * @note We encourage you to use EpsgCrsId, WKT or Proj4 to describe CRS's in your code - * wherever possible. QGIS CRS_IDs are not guaranteed to be permanent / involatile. - * @param theId The ID no valid for the chosen coordinate system id type - * @param theType One of the types described in QgsCoordinateReferenceSystem::CrsType + /** Constructor a CRS object using a postgis SRID, an EPSG code or an internal QGIS CRS ID. + * @note We encourage you to use EPSG code, WKT or Proj4 to describe CRS's in your code + * wherever possible. Internal QGIS CRS IDs are not guaranteed to be permanent / involatile. + * @param theId The ID valid for the chosen CRS ID type + * @param theType One of the types described in CrsType */ + // TODO QGIS 3: remove theType and always use EPSG code QgsCoordinateReferenceSystem( const long theId, CrsType theType = PostgisCrsId ); // Misc helper functions ----------------------- + /** + * Sets this CRS by lookup of the given ID in the CRS database. + * @note We encourage you to use EPSG code, WKT or Proj4 to describe CRS's in your code + * wherever possible. Internal QGIS CRS IDs are not guaranteed to be permanent / involatile. + * @return True on success else false + */ + // TODO QGIS 3: remove theType and always use EPSG code, rename to createFromEpsg bool createFromId( const long theId, CrsType theType = PostgisCrsId ); /** * Sets this CRS to the given OGC WMS-format Coordinate Reference Systems. - * @returns false if not given an valid label + * + * Accepts both ":" format and OGC URN "urn:ogc:def:crs::[]:". + * It also recognizes "QGIS", "USER", "CUSTOM" authorities, which all have the same meaning + * and refer to QGIS internal CRS IDs. * @note this method is expensive. Consider using QgsCRSCache::crsByOgcWmsCrs() instead. + * @return True on success else false */ - bool createFromOgcWmsCrs( QString theCrs ); + // TODO QGIS 3: remove "QGIS" and "CUSTOM", only support "USER" (also returned by authid()) + bool createFromOgcWmsCrs( const QString& theCrs ); - /** Set up this CRS by fetching the appropriate information from the - * sqlite backend. First the system level read only srs.db will be checked - * and then the users ~/.qgis/qgis.db database will be checked for a match. - * @note Any members will be overwritten during this process. + /** Sets this CRS by lookup of the given PostGIS SRID in the CRS database. * @param theSrid The postgis SRID for the desired spatial reference system. + * @return True on success else false */ + // TODO QGIS 3: remove unless really necessary - let's use EPSG codes instead bool createFromSrid( const long theSrid ); - /** Set up this CRS using a WKT spatial ref sys definition. - * The WKT will be converted to a proj4 string using OGR helper - * functions. After this the SRS databases will be searched for matches. - * First the system level read only srs.db will be checked - * and then the users ~/.qgis/qgis.db database will be checked for a match. - * @note Any members will be overwritten during this process. - * @note SRID and EpsgCrsId may be blank if no match can be found on SRS db. + /** Sets this CRS using a WKT definition. + * + * If EPSG code of the WKT definition can be determined, it is extracted + * and createFromOgcWmsCrs() is used to initialize the object. + * Otherwise the WKT will be converted to a proj4 string and createFromProj4() + * set up the object. + * @note Some members may be left blank if no match can be found in CRS database. + * @note this method is expensive. Consider using QgsCRSCache::crsByWkt() instead. * @param theWkt The WKT for the desired spatial reference system. - * @return bool TRUE if success else false + * @return True on success else false */ bool createFromWkt( const QString &theWkt ); - /** Set up this CRS by fetching the appropriate information from the - * sqlite backend. If the srsid is < 100000, only the system srs.db - * will be checked. If the srsid > 100000 the srs will be retrieved from - * the ~/.qgis/qgis.db - * @note Any members will be overwritten during this process. - * @param theSrsId The QGIS SrsId for the desired spatial reference system. - * @return bool TRUE if success else false + /** Sets this CRS by lookup of internal QGIS CRS ID in the CRS database. + * + * If the srsid is < USER_CRS_START_ID, system CRS database is used, otherwise + * user's local CRS database from home directory is used. * @note this method is expensive. Consider using QgsCRSCache::crsBySrsId() instead. + * @param theSrsId The internal QGIS CRS ID for the desired spatial reference system. + * @return True on success else false */ bool createFromSrsId( const long theSrsId ); - /** Set up this CRS by passing it a proj4 style formatted string. + /** Sets this CRS by passing it a PROJ.4 style formatted string. + * * The string will be parsed and the projection and ellipsoid * members set and the remainder of the proj4 string will be stored * in the parameters member. The reason for this is so that we * can easily present the user with 'natural language' representation - * of the projection and ellipsoid by looking them up in the srs.bs sqlite - * database. Also having the ellipse and proj elements stripped out - * is helpful to speed up globbing queries (see below). + * of the projection and ellipsoid by looking them up in the srs.db sqlite + * database. * - * We try to match the proj string to and srsid using the following logic: + * We try to match the proj4 string to internal QGIS CRS ID using the following logic: * - * - perform a whole text search on CRS name (if not null). The CRS name will - * have been set if this method has been delegated to from createFromWkt. - * - if the above does not match perform a whole text search on proj4 string (if not null) - * - if none of the above match convert the proj4 string to an OGR CRS - * then check if its a geocs or a proj cs (using ogr isGeographic) - * then sequentially walk through the database (first users qgis.db srs tbl then - * system srs.db tbl), converting each entry into an ogr srs and using isSame - * or isSameGeocs (essentially calling the == overloaded operator). We'll try to - * be smart about this and first parse out the proj and ellpse strings and only - * check for a match in entities that have the same ellps and proj entries so - * that it doesn't munch yer cpu so much. + * - perform a whole text search on proj4 string (if not null) + * - if not match is found, split proj4 into individual parameters and try to find + * a match where the parameters are in a different order + * - if none of the above match, use findMatchingProj() * - * @param theProjString A proj4 format string - * @return bool TRUE if success else false + * @note Some members may be left blank if no match can be found in CRS database. * @note this method is expensive. Consider using QgsCRSCache::crsByProj4() instead. + * @param theProjString A proj4 format string + * @return True on success else false */ bool createFromProj4( const QString &theProjString ); - /** Set up this CRS from a string definition, by default a WKT definition. Otherwise - * the string defines a authority, followed by a colon, followed by the definition. - * The authority can be one of "epsg", "postgis", "internal" for integer definitions, - * and "wkt" or "proj4" for string definitions. The implementation of each authority - * uses the corresponding createFrom... function. + /** Set up this CRS from a string definition. + * + * It supports the following formats: + * - "EPSG:" - handled with createFromOgcWms() + * - "POSTGIS:" - handled with createFromSrid() + * - "INTERNAL:" - handled with createFromSrsId() + * - "PROJ4:" - handled with createFromProj4() + * - "WKT:" - handled with createFromWkt() + * + * If no prefix is specified, WKT definition is assumed. * @param theDefinition A String containing a coordinate reference system definition. + * @return True on success else false */ bool createFromString( const QString &theDefinition ); - /** Set up this CRS from a various text formats. + /** Set up this CRS from various text formats. * * Valid formats: WKT string, "EPSG:n", "EPSGA:n", "AUTO:proj_id,unit_id,lon0,lat0", * "urn:ogc:def:crs:EPSG::n", PROJ.4 string, filename (with WKT, XML or PROJ.4 string), @@ -131,7 +293,9 @@ class QgsCoordinateReferenceSystem * @note this function generates a WKT string using OSRSetFromUserInput() and * passes it to createFromWkt() function. * @param theDefinition A String containing a coordinate reference system definition. + * @return True on success else false */ + // TODO QGIS3: rename to createFromStringOGR so it is clear it's similar to createFromString, just different backend bool createFromUserInput( const QString &theDefinition ); /** Make sure that ESRI WKT import is done properly. @@ -147,7 +311,7 @@ class QgsCoordinateReferenceSystem /** Returns whether this CRS is correctly initialized and usable */ bool isValid() const; - /** Perform some validation on this CRS. If the sts doesn't validate the + /** Perform some validation on this CRS. If the CRS doesn't validate the * default behaviour settings for layers with unknown CRS will be * consulted and acted on accordingly. By hell or high water this * method will do its best to make sure that this CRS is valid - even @@ -155,49 +319,37 @@ class QgsCoordinateReferenceSystem * * @note It is not usually necessary to use this function, unless you * are trying to force this CRS to be valid. + * @see setCustomSrsValidation(), customSrsValidation() */ void validate(); - /** This is a globbing function to try to find a record in the database - * that matches a CRS defined only by a proj4string. The goal is to - * learn what the tbl_srs.srs_id value is for the CRS. Internally - * the source CRS is converted to an OGR srs object using the proj4string call - * and then every record in the database that matches projection and ellipsoid - * will be converted to an OGR srs in turn and compared to the source CRS. - * There are some gotchas with using ogr isSame() srs comparison, but - * its more effective than using straight string comparison of proj4params. + /** Walks the CRS databases (both system and user database) trying to match + * stored PROJ.4 string to a database entry in order to fill in further + * pieces of information about CRS. * @note The ellipsoid and projection acronyms must be set as well as the proj4string! - * @return long the SrsId of the matched CRS + * @return long the SrsId of the matched CRS, zero if no match was found */ + // TODO QGIS 3: seems completely obsolete now (only compares proj4 - already done in createFromProj4) long findMatchingProj(); /** Overloaded == operator used to compare to CRS's. - * Internally it will delegate to the equals method described below + * + * Internally it will use authid() for comparison. */ bool operator==( const QgsCoordinateReferenceSystem &theSrs ) const; /** Overloaded != operator used to compare to CRS's. - * Returns opposite bool value to operator == + * + * Returns opposite bool value to operator == */ bool operator!=( const QgsCoordinateReferenceSystem &theSrs ) const; - /** Restores state from the given Dom node. + /** Restores state from the given DOM node. * @param theNode The node from which state will be restored * @return bool True on success, False on failure */ bool readXML( const QDomNode & theNode ); + /** Stores state to the given Dom node in the given document. - * Below is an example of the generated tag. - \code{.xml} - - +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs - 2585 - 4326 - 4326 - WGS 84 - longlat - WGS84 - - \endcode * @param theNode The node in which state will be restored * @param theDoc The document in which state will be stored * @return bool True on success, False on failure @@ -218,20 +370,25 @@ class QgsCoordinateReferenceSystem // Accessors ----------------------------------- - /** Returns the SrsId, if available. - * @return the internal sqlite3 srs.db primary key for this srs - * @see postgisSrid() + /** Returns the internal CRS ID, if available. + * @return the internal sqlite3 srs.db primary key for this CRS */ long srsid() const; /** Returns PostGIS SRID for the CRS. * @return the PostGIS spatial_ref_sys identifier for this CRS (defaults to 0) */ + // TODO QGIS 3: remove unless really necessary - let's use EPSG codes instead long postgisSrid() const; - /** Returns the authority identifier for the CRS, which includes both the authority (eg EPSG) - * and the CRS number (eg 4326). This is the best method to use when showing a very short CRS - * identifier to a user, eg "EPSG:4326". + /** Returns the authority identifier for the CRS. + * + * The identifier includes both the authority (eg EPSG) and the CRS number (eg 4326). + * This is the best method to use when showing a very short CRS identifier to a user, + * eg "EPSG:4326". + * + * If CRS object is a custom CRS (not found in database), the method will return + * internal QGIS CRS ID with "QGIS" authority, for example "QGIS:100005" * @returns the authority identifier for this CRS * @see description() */ @@ -267,6 +424,7 @@ class QgsCoordinateReferenceSystem QString toWkt() const; /** Returns a Proj4 string representation of this CRS. + * * If proj and ellps keys are found in the parameters, * they will be stripped out and the projection and ellipsoid acronyms will be * overridden with these. @@ -276,9 +434,10 @@ class QgsCoordinateReferenceSystem */ QString toProj4() const; - /** Returns whether the CRS is a geographic CRS. + /** Returns whether the CRS is a geographic CRS (using lat/lon coordinates) * @returns true if CRS is geographic, or false if it is a projected CRS */ + //TODO QGIS 3.0 - rename to isGeographic bool geographicFlag() const; /** Returns whether axis is inverted (eg. for WMS 1.3) for the CRS. @@ -299,9 +458,11 @@ class QgsCoordinateReferenceSystem /** Get user hint for validation */ QString validationHint(); + /** Update proj.4 parameters in our database from proj.4 * @returns number of updated CRS on success and * negative number of failed updates in case of errors. + * @note This is used internally and should not be necessary to call in client code */ static int syncDb(); diff --git a/src/core/qgscoordinatereferencesystem.cpp b/src/core/qgscoordinatereferencesystem.cpp index b93befdfd4c..3e1414334a9 100644 --- a/src/core/qgscoordinatereferencesystem.cpp +++ b/src/core/qgscoordinatereferencesystem.cpp @@ -520,21 +520,13 @@ bool QgsCoordinateReferenceSystem::createFromProj4( const QString &theProj4Strin QgsDebugMsg( "proj string supplied has no +a argument" ); } - /* - * We try to match the proj string to and srsid using the following logic: - * - * - perform a whole text search on srs name (if not null). The srs name will - * have been set if this method has been delegated to from createFromWkt. - * Normally we wouldnt expect this to work, but its worth trying first - * as its quicker than methods below.. - */ long mySrsId = 0; QgsCoordinateReferenceSystem::RecordMap myRecord; /* - * - if the above does not match perform a whole text search on proj4 string (if not null) + * We try to match the proj string to and srsid using the following logic: + * - perform a whole text search on proj4 string (if not null) */ - // QgsDebugMsg( "wholetext match on name failed, trying proj4string match" ); myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( myProj4String ) + " order by deprecated" ); if ( myRecord.empty() ) { @@ -999,15 +991,7 @@ void QgsCoordinateReferenceSystem::setMapUnits() } } -/* -* check if srs is a geocs or a proj cs (using ogr isGeographic) -* then sequentially walk through the database (first users qgis.db srs tbl then -* system srs.db tbl), converting each entry into an ogr srs and using isSame -* or isSameGeocs (essentially calling the == overloaded operator). We'll try to -* be smart about this and first parse out the proj and ellpse strings and only -* check for a match in entities that have the same ellps and proj entries so -* that it doesnt munch yer cpu so much. -*/ + long QgsCoordinateReferenceSystem::findMatchingProj() { if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull() diff --git a/src/core/qgscoordinatereferencesystem.h b/src/core/qgscoordinatereferencesystem.h index a63dc979cf7..9db5e0517d5 100644 --- a/src/core/qgscoordinatereferencesystem.h +++ b/src/core/qgscoordinatereferencesystem.h @@ -46,38 +46,189 @@ class QgsCoordinateReferenceSystem; typedef void ( *CUSTOM_CRS_VALIDATION )( QgsCoordinateReferenceSystem& ); /** \ingroup core - * Class for storing a coordinate reference system (CRS) - * \note Since QGIS 2.16 QgsCoordinateReferenceSystem objects are implicitly shared. + * This class represents a coordinate reference system (CRS). + * + * Coordinate reference system object defines a specific map projection, as well as transformations + * between different coordinate reference systems. There are various ways how a CRS can be defined: + * using well-known text (WKT), PROJ.4 string or combination of authority and code (e.g. EPSG:4326). + * QGIS comes with its internal database of coordinate reference systems (stored in SQLite) that + * allows lookups of CRS and seamless conversions between the various definitions. + * + * Most commonly one comes across two types of coordinate systems: + * + * 1. **Geographic coordinate systems** - based on a geodetic datum, normally with coordinates being + * latitude/longitude in degrees. The most common one is World Geodetic System 84 (WGS84). + * 2. **Projected coordinate systems** - based on a geodetic datum with coordinates projected to a plane, + * typically using meters or feet as units. Common projected coordinate systems are Universal + * Transverse Mercator or Albers Equal Area. + * + * Internally QGIS uses proj4 library for all the math behind coordinate transformations, so in case + * of any troubles with projections it is best to examine the PROJ.4 representation within the object, + * as that is the representation that will be ultimately used. + * + * Methods that allow inspection of CRS instances include isValid(), authid(), description(), + * toWkt(), toProj4(), mapUnits() and others. + * Creation of CRS instances is further described in \ref crs_construct_and_copy section below. + * Transformations between coordinate reference systems are done using QgsCoordinateTransform class. + * + * For example, the following code will create and inspect "British national grid" CRS: + * + * ~~~{.py} + * crs = QgsCoordinateReferenceSystem("EPSG:27700") + * if crs.isValid(): + * print("CRS Description: {}".format(crs.description())) + * print("CRS PROJ.4 text: {}".format(crs.toProj4())) + * else: + * print("Invalid CRS!") + * ~~~ + * + * This will produce the following output: + * + * ~~~ + * CRS Description: OSGB 1936 / British National Grid + * CRS PROJ.4 text: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 [output trimmed] + * ~~~ + * + * CRS Definition Formats + * ====================== + * + * This section gives an overview of various supported CRS definition formats: + * + * 1. **Authority and Code.** Also referred to as OGC WMS format within QGIS as they have been widely + * used in OGC standards. These are encoded as `:`, for example `EPSG:4326` refers + * to WGS84 system. EPSG is the most commonly used authority that covers a wide range + * of coordinate systems around the world. + * + * An extended variant of this format is OGC URN. Syntax of URN for CRS definition is + * `urn:ogc:def:crs::[]:`. This class can also parse URNs (versions + * are currently ignored). For example, WGS84 may be encoded as `urn:ogc:def:crs:OGC:1.3:CRS84`. + * + * QGIS adds support for "USER" authority that refers to IDs used internally in QGIS. This variant + * is best avoided or used with caution as the IDs are not permanent and they refer to different CRS + * on different machines or user profiles. + * + * See authid() and createFromOgcWmsCrs() methods. + * + * 2. **PROJ.4 string.** This is a string consisting of a series of key/value pairs in the following + * format: `+param1=value1 +param2=value2 [...]`. This is the format natively used by the + * underlying proj4 library. For example, the definition of WGS84 looks like this: + * + * +proj=longlat +datum=WGS84 +no_defs + * + * See toProj4() and createFromProj4() methods. + * + * 3. **Well-known text (WKT).** Defined by Open Geospatial Consortium (OGC), this is another common + * format to define CRS. For WGS84 the OGC WKT definition is the following: + * + * GEOGCS["WGS 84", + * DATUM["WGS_1984", + * SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]], + * AUTHORITY["EPSG","6326"]], + * PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]], + * UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]], + * AUTHORITY["EPSG","4326"]] + * + * See toWkt() and createFromWkt() methods. + * + * CRS Database and Custom CRS + * =========================== + * + * The database of CRS shipped with QGIS is stored in a SQLite database (see QgsApplication::srsDbFilePath()) + * and it is based on the data files maintained by GDAL project (a variety of .csv and .wkt files). + * + * Sometimes it happens that users need to use a CRS definition that is not well known + * or that has been only created with a specific purpose (and thus its definition is not + * available in our database of CRS). Whenever a new CRS definition is seen, it will + * be added to the local databse (in user's home directory, see QgsApplication::qgisUserDbFilePath()). + * QGIS also features a GUI for management of local custom CRS definitions. + * + * There are therefore two databases: one for shipped CRS definitions and one for custom CRS definitions. + * Custom CRS have internal IDs (accessible with srsid()) greater or equal to \ref USER_CRS_START_ID. + * The local CRS databases should never be accessed directly with SQLite functions, instead + * you should use QgsCoordinateReferenceSystem API for CRS lookups and for managements of custom CRS. + * + * Validation + * ========== + * + * In some cases (most prominently when loading a map layer), QGIS will try to ensure + * that the given map layer CRS is valid using validate() call. If not, a custom + * validation function will be called - such function may for example show a GUI + * for manual CRS selection. The validation function is configured using setCustomSrsValidation(). + * If validation fails or no validation function is set, the default CRS is assigned + * (WGS84). QGIS application registers its validation function that will act according + * to user's settings (either show CRS selector dialog or use project/custom CRS). + * + * Object Construction and Copying {#crs_construct_and_copy} + * =============================== + * + * The easiest way of creating CRS instances is to use QgsCoordinateReferenceSystem(const QString&) + * constructor that automatically recognizes definition format from the given string. + * + * Creation of CRS object involves some queries in a local SQLite database, which may + * be potentially expensive. It is therefore recommended to use QgsCRSCache methods + * that return possibly cached CRS objects instead of constructing new instances that + * involve the lookup overhead. + * + * Since QGIS 2.16 QgsCoordinateReferenceSystem objects are implicitly shared. + * + * The following table summarizes equivalents for non-cached and cached CRS lookup: + * + * Definition | Non-cached | Cached + * ----------- | --------------------- | ------------------------------ + * Auth + Code | createFromOgcWmsCrs() | QgsCRSCache::crsByOgcWmsCrs() + * PROJ.4 | createFromProj4() | QgsCRSCache::crsByProj4() + * WKT | createFromWkt() | QgsCRSCache::crsByWkt() + * + * Caveats + * ======= + * + * There are two different flavours of WKT: one is defined by OGC, the other is the standard + * used by ESRI. They look very similar, but they are not the same. QGIS is able to consume + * both flavours. + * + * \see QgsCoordinateTransform, QgsCRSCache */ class CORE_EXPORT QgsCoordinateReferenceSystem { public: + //! Enumeration of types of IDs accepted in createFromId() method enum CrsType { - InternalCrsId, - PostgisCrsId, - EpsgCrsId // deprecated + InternalCrsId, //!< Internal ID used by QGIS in the local SQLite database + PostgisCrsId, //!< SRID used in PostGIS + EpsgCrsId //!< EPSG code }; + /** Constructs an invalid CRS object */ QgsCoordinateReferenceSystem(); ~QgsCoordinateReferenceSystem(); /*! - * Constructs a CRS object from a string definition as defined in the createFromString - * member function (by default a WKT definition). + * Constructs a CRS object from a string definition using createFromString() + * + * It supports the following formats: + * - "EPSG:" - handled with createFromOgcWms() + * - "POSTGIS:" - handled with createFromSrid() + * - "INTERNAL:" - handled with createFromSrsId() + * - "PROJ4:" - handled with createFromProj4() + * - "WKT:" - handled with createFromWkt() + * + * If no prefix is specified, WKT definition is assumed. * @param theDefinition A String containing a coordinate reference system definition. + * @see createFromString() */ + // TODO QGIS 3: remove "POSTGIS" and "INTERNAL", allow PROJ4 without the prefix explicit QgsCoordinateReferenceSystem( const QString& theDefinition ); - /** Use this constructor when you want to create a CRS object using - * a postgis SRID, an EpsgCrsId id or a QGIS CRS_ID. - * @note We encourage you to use EpsgCrsId, WKT or Proj4 to describe CRS's in your code - * wherever possible. QGIS CRS_IDs are not guaranteed to be permanent / involatile. - * @param theId The ID no valid for the chosen coordinate system id type - * @param theType One of the types described in QgsCoordinateReferenceSystem::CrsType + /** Constructor a CRS object using a postgis SRID, an EPSG code or an internal QGIS CRS ID. + * @note We encourage you to use EPSG code, WKT or Proj4 to describe CRS's in your code + * wherever possible. Internal QGIS CRS IDs are not guaranteed to be permanent / involatile. + * @param theId The ID valid for the chosen CRS ID type + * @param theType One of the types described in CrsType */ + // TODO QGIS 3: remove theType and always use EPSG code QgsCoordinateReferenceSystem( const long theId, CrsType theType = PostgisCrsId ); //! Copy constructor @@ -88,86 +239,96 @@ class CORE_EXPORT QgsCoordinateReferenceSystem // Misc helper functions ----------------------- + /** + * Sets this CRS by lookup of the given ID in the CRS database. + * @note We encourage you to use EPSG code, WKT or Proj4 to describe CRS's in your code + * wherever possible. Internal QGIS CRS IDs are not guaranteed to be permanent / involatile. + * @return True on success else false + */ + // TODO QGIS 3: remove theType and always use EPSG code, rename to createFromEpsg bool createFromId( const long theId, CrsType theType = PostgisCrsId ); /** * Sets this CRS to the given OGC WMS-format Coordinate Reference Systems. - * @returns false if not given an valid label + * + * Accepts both ":" format and OGC URN "urn:ogc:def:crs::[]:". + * It also recognizes "QGIS", "USER", "CUSTOM" authorities, which all have the same meaning + * and refer to QGIS internal CRS IDs. * @note this method is expensive. Consider using QgsCRSCache::crsByOgcWmsCrs() instead. + * @return True on success else false */ + // TODO QGIS 3: remove "QGIS" and "CUSTOM", only support "USER" (also returned by authid()) bool createFromOgcWmsCrs( const QString& theCrs ); - /** Set up this CRS by fetching the appropriate information from the - * sqlite backend. First the system level read only srs.db will be checked - * and then the users ~/.qgis/qgis.db database will be checked for a match. - * @note Any members will be overwritten during this process. + /** Sets this CRS by lookup of the given PostGIS SRID in the CRS database. * @param theSrid The postgis SRID for the desired spatial reference system. + * @return True on success else false */ + // TODO QGIS 3: remove unless really necessary - let's use EPSG codes instead bool createFromSrid( const long theSrid ); - /** Set up this CRS using a WKT spatial ref sys definition. - * The WKT will be converted to a proj4 string using OGR helper - * functions. After this the SRS databases will be searched for matches. - * First the system level read only srs.db will be checked - * and then the users ~/.qgis/qgis.db database will be checked for a match. - * @note Any members will be overwritten during this process. - * @note SRID and EpsgCrsId may be blank if no match can be found on SRS db. - * @param theWkt The WKT for the desired spatial reference system. - * @return bool TRUE if success else false + /** Sets this CRS using a WKT definition. + * + * If EPSG code of the WKT definition can be determined, it is extracted + * and createFromOgcWmsCrs() is used to initialize the object. + * Otherwise the WKT will be converted to a proj4 string and createFromProj4() + * set up the object. + * @note Some members may be left blank if no match can be found in CRS database. * @note this method is expensive. Consider using QgsCRSCache::crsByWkt() instead. + * @param theWkt The WKT for the desired spatial reference system. + * @return True on success else false */ bool createFromWkt( const QString &theWkt ); - /** Set up this CRS by fetching the appropriate information from the - * sqlite backend. If the srsid is < 100000, only the system srs.db - * will be checked. If the srsid > 100000 the srs will be retrieved from - * the ~/.qgis/qgis.db - * @note Any members will be overwritten during this process. - * @param theSrsId The QGIS SrsId for the desired spatial reference system. - * @return bool TRUE if success else false + /** Sets this CRS by lookup of internal QGIS CRS ID in the CRS database. + * + * If the srsid is < USER_CRS_START_ID, system CRS database is used, otherwise + * user's local CRS database from home directory is used. * @note this method is expensive. Consider using QgsCRSCache::crsBySrsId() instead. + * @param theSrsId The internal QGIS CRS ID for the desired spatial reference system. + * @return True on success else false */ bool createFromSrsId( const long theSrsId ); - /** Set up this CRS by passing it a proj4 style formatted string. + /** Sets this CRS by passing it a PROJ.4 style formatted string. + * * The string will be parsed and the projection and ellipsoid * members set and the remainder of the proj4 string will be stored * in the parameters member. The reason for this is so that we * can easily present the user with 'natural language' representation - * of the projection and ellipsoid by looking them up in the srs.bs sqlite - * database. Also having the ellipse and proj elements stripped out - * is helpful to speed up globbing queries (see below). + * of the projection and ellipsoid by looking them up in the srs.db sqlite + * database. * - * We try to match the proj string to and srsid using the following logic: + * We try to match the proj4 string to internal QGIS CRS ID using the following logic: * - * - perform a whole text search on CRS name (if not null). The CRS name will - * have been set if this method has been delegated to from createFromWkt. - * - if the above does not match perform a whole text search on proj4 string (if not null) - * - if none of the above match convert the proj4 string to an OGR CRS - * then check if its a geocs or a proj cs (using ogr isGeographic) - * then sequentially walk through the database (first users qgis.db srs tbl then - * system srs.db tbl), converting each entry into an ogr srs and using isSame - * or isSameGeocs (essentially calling the == overloaded operator). We'll try to - * be smart about this and first parse out the proj and ellpse strings and only - * check for a match in entities that have the same ellps and proj entries so - * that it doesn't munch yer cpu so much. + * - perform a whole text search on proj4 string (if not null) + * - if not match is found, split proj4 into individual parameters and try to find + * a match where the parameters are in a different order + * - if none of the above match, use findMatchingProj() * - * @param theProjString A proj4 format string - * @return bool TRUE if success else false + * @note Some members may be left blank if no match can be found in CRS database. * @note this method is expensive. Consider using QgsCRSCache::crsByProj4() instead. + * @param theProjString A proj4 format string + * @return True on success else false */ bool createFromProj4( const QString &theProjString ); - /** Set up this CRS from a string definition, by default a WKT definition. Otherwise - * the string defines a authority, followed by a colon, followed by the definition. - * The authority can be one of "epsg", "postgis", "internal" for integer definitions, - * and "wkt" or "proj4" for string definitions. The implementation of each authority - * uses the corresponding createFrom... function. + /** Set up this CRS from a string definition. + * + * It supports the following formats: + * - "EPSG:" - handled with createFromOgcWms() + * - "POSTGIS:" - handled with createFromSrid() + * - "INTERNAL:" - handled with createFromSrsId() + * - "PROJ4:" - handled with createFromProj4() + * - "WKT:" - handled with createFromWkt() + * + * If no prefix is specified, WKT definition is assumed. * @param theDefinition A String containing a coordinate reference system definition. + * @return True on success else false */ bool createFromString( const QString &theDefinition ); - /** Set up this CRS from a various text formats. + /** Set up this CRS from various text formats. * * Valid formats: WKT string, "EPSG:n", "EPSGA:n", "AUTO:proj_id,unit_id,lon0,lat0", * "urn:ogc:def:crs:EPSG::n", PROJ.4 string, filename (with WKT, XML or PROJ.4 string), @@ -179,7 +340,9 @@ class CORE_EXPORT QgsCoordinateReferenceSystem * @note this function generates a WKT string using OSRSetFromUserInput() and * passes it to createFromWkt() function. * @param theDefinition A String containing a coordinate reference system definition. + * @return True on success else false */ + // TODO QGIS3: rename to createFromStringOGR so it is clear it's similar to createFromString, just different backend bool createFromUserInput( const QString &theDefinition ); /** Make sure that ESRI WKT import is done properly. @@ -195,7 +358,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem /** Returns whether this CRS is correctly initialized and usable */ bool isValid() const; - /** Perform some validation on this CRS. If the sts doesn't validate the + /** Perform some validation on this CRS. If the CRS doesn't validate the * default behaviour settings for layers with unknown CRS will be * consulted and acted on accordingly. By hell or high water this * method will do its best to make sure that this CRS is valid - even @@ -203,49 +366,37 @@ class CORE_EXPORT QgsCoordinateReferenceSystem * * @note It is not usually necessary to use this function, unless you * are trying to force this CRS to be valid. + * @see setCustomSrsValidation(), customSrsValidation() */ void validate(); - /** This is a globbing function to try to find a record in the database - * that matches a CRS defined only by a proj4string. The goal is to - * learn what the tbl_srs.srs_id value is for the CRS. Internally - * the source CRS is converted to an OGR srs object using the proj4string call - * and then every record in the database that matches projection and ellipsoid - * will be converted to an OGR srs in turn and compared to the source CRS. - * There are some gotchas with using ogr isSame() srs comparison, but - * its more effective than using straight string comparison of proj4params. + /** Walks the CRS databases (both system and user database) trying to match + * stored PROJ.4 string to a database entry in order to fill in further + * pieces of information about CRS. * @note The ellipsoid and projection acronyms must be set as well as the proj4string! - * @return long the SrsId of the matched CRS + * @return long the SrsId of the matched CRS, zero if no match was found */ + // TODO QGIS 3: seems completely obsolete now (only compares proj4 - already done in createFromProj4) long findMatchingProj(); /** Overloaded == operator used to compare to CRS's. - * Internally it will delegate to the equals method described below + * + * Internally it will use authid() for comparison. */ bool operator==( const QgsCoordinateReferenceSystem &theSrs ) const; /** Overloaded != operator used to compare to CRS's. - * Returns opposite bool value to operator == + * + * Returns opposite bool value to operator == */ bool operator!=( const QgsCoordinateReferenceSystem &theSrs ) const; - /** Restores state from the given Dom node. + /** Restores state from the given DOM node. * @param theNode The node from which state will be restored * @return bool True on success, False on failure */ bool readXML( const QDomNode & theNode ); + /** Stores state to the given Dom node in the given document. - * Below is an example of the generated tag. - \code{.xml} - - +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs - 2585 - 4326 - 4326 - WGS 84 - longlat - WGS84 - - \endcode * @param theNode The node in which state will be restored * @param theDoc The document in which state will be stored * @return bool True on success, False on failure @@ -266,20 +417,25 @@ class CORE_EXPORT QgsCoordinateReferenceSystem // Accessors ----------------------------------- - /** Returns the SrsId, if available. - * @return the internal sqlite3 srs.db primary key for this srs - * @see postgisSrid() + /** Returns the internal CRS ID, if available. + * @return the internal sqlite3 srs.db primary key for this CRS */ long srsid() const; /** Returns PostGIS SRID for the CRS. * @return the PostGIS spatial_ref_sys identifier for this CRS (defaults to 0) */ + // TODO QGIS 3: remove unless really necessary - let's use EPSG codes instead long postgisSrid() const; - /** Returns the authority identifier for the CRS, which includes both the authority (eg EPSG) - * and the CRS number (eg 4326). This is the best method to use when showing a very short CRS - * identifier to a user, eg "EPSG:4326". + /** Returns the authority identifier for the CRS. + * + * The identifier includes both the authority (eg EPSG) and the CRS number (eg 4326). + * This is the best method to use when showing a very short CRS identifier to a user, + * eg "EPSG:4326". + * + * If CRS object is a custom CRS (not found in database), the method will return + * internal QGIS CRS ID with "QGIS" authority, for example "QGIS:100005" * @returns the authority identifier for this CRS * @see description() */ @@ -315,6 +471,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem QString toWkt() const; /** Returns a Proj4 string representation of this CRS. + * * If proj and ellps keys are found in the parameters, * they will be stripped out and the projection and ellipsoid acronyms will be * overridden with these. @@ -324,7 +481,7 @@ class CORE_EXPORT QgsCoordinateReferenceSystem */ QString toProj4() const; - /** Returns whether the CRS is a geographic CRS. + /** Returns whether the CRS is a geographic CRS (using lat/lon coordinates) * @returns true if CRS is geographic, or false if it is a projected CRS */ //TODO QGIS 3.0 - rename to isGeographic @@ -348,9 +505,11 @@ class CORE_EXPORT QgsCoordinateReferenceSystem /** Get user hint for validation */ QString validationHint(); + /** Update proj.4 parameters in our database from proj.4 * @returns number of updated CRS on success and * negative number of failed updates in case of errors. + * @note This is used internally and should not be necessary to call in client code */ static int syncDb(); @@ -441,8 +600,8 @@ class CORE_EXPORT QgsCoordinateReferenceSystem */ RecordMap getRecord( const QString& theSql ); - // Open SQLite db and show message if cannot be opened - // returns the same code as sqlite3_open + //! Open SQLite db and show message if cannot be opened + //! @return the same code as sqlite3_open static int openDb( const QString& path, sqlite3 **db, bool readonly = true ); //! Work out the projection units and set the appropriate local variable @@ -454,14 +613,20 @@ class CORE_EXPORT QgsCoordinateReferenceSystem //! Helper for sql-safe value quoting static QString quotedValue( QString value ); + //! Initialize the CRS object by looking up CRS database in path given in db argument, + //! using first CRS entry where expression = 'value' bool loadFromDb( const QString& db, const QString& expression, const QString& value ); + //! Load CRS definitions from GDAL .csv files. Used by syncDb() static bool loadIDs( QHash &wkts ); + //! Load CRS definitions from GDAL .wkt files. Used by syncDb() static bool loadWkts( QHash &wkts, const char *filename ); + //! Update datum shift definitions from GDAL data. Used by syncDb() static bool syncDatumTransform( const QString& dbPath ); QExplicitlySharedDataPointer d; + //! Function for CRS validation. May be null. static CUSTOM_CRS_VALIDATION mCustomSrsValidation; };