diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2019-11-04 22:35:57 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-11-04 22:35:57 +0100 |
| commit | 34dc695402ba5d10248ea47bec3ab88ed950eccb (patch) | |
| tree | 56bfb7962cca13095a85a93af4e372ffac2e0be2 | |
| parent | 1bee3d54b05d2f6bd406749126ff7d6ac26e7013 (diff) | |
| parent | 67e987ed84e19dd0ce46bdc529e8a73010af2c66 (diff) | |
| download | PROJ-34dc695402ba5d10248ea47bec3ab88ed950eccb.tar.gz PROJ-34dc695402ba5d10248ea47bec3ab88ed950eccb.zip | |
Merge pull request #1710 from rouault/geoid_model
Add support for GEOIDMODEL
| -rw-r--r-- | data/projjson.schema.json | 11 | ||||
| -rw-r--r-- | data/sql/commit.sql | 16 | ||||
| -rw-r--r-- | data/sql/customizations.sql | 28 | ||||
| -rw-r--r-- | data/sql/grid_alternatives.sql | 77 | ||||
| -rw-r--r-- | data/sql/proj_db_table_defs.sql | 17 | ||||
| -rw-r--r-- | docs/source/usage/projjson.rst | 10 | ||||
| -rw-r--r-- | include/proj/internal/io_internal.hpp | 1 | ||||
| -rw-r--r-- | include/proj/io.hpp | 4 | ||||
| -rw-r--r-- | schemas/v0.2/projjson.schema.json | 981 | ||||
| -rw-r--r-- | scripts/reference_exported_symbols.txt | 1 | ||||
| -rw-r--r-- | src/iso19111/c_api.cpp | 79 | ||||
| -rw-r--r-- | src/iso19111/coordinateoperation.cpp | 143 | ||||
| -rw-r--r-- | src/iso19111/crs.cpp | 31 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 22 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 129 | ||||
| -rw-r--r-- | src/iso19111/static.cpp | 1 | ||||
| -rw-r--r-- | src/proj_experimental.h | 13 | ||||
| -rw-r--r-- | test/cli/testprojinfo_out.dist | 16 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 109 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 121 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 130 |
21 files changed, 1843 insertions, 97 deletions
diff --git a/data/projjson.schema.json b/data/projjson.schema.json index 60fca6df..e71e5031 100644 --- a/data/projjson.schema.json +++ b/data/projjson.schema.json @@ -1,5 +1,5 @@ { - "$id": "https://proj.org/schemas/v0.1/projjson.schema.json", + "$id": "https://proj.org/schemas/v0.2/projjson.schema.json", "$schema": "http://json-schema.org/draft-07/schema#", "description": "Schema for PROJJSON", "$comment": "This file exists both in data/ and in schemas/vXXX/. Keep both in sync. And if changing the value of $id, change PROJJSON_CURRENT_VERSION accordingly in io.cpp", @@ -932,6 +932,15 @@ }, "datum_ensemble": { "$ref": "#/definitions/datum_ensemble" }, "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "geoid_model": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "id": { "$ref": "#/definitions/id" } + }, + "required" : [ "name" ], + "additionalProperties": false + }, "$schema" : {}, "scope": {}, "area": {}, diff --git a/data/sql/commit.sql b/data/sql/commit.sql index eb49828a..a708df0f 100644 --- a/data/sql/commit.sql +++ b/data/sql/commit.sql @@ -29,6 +29,22 @@ FOR EACH ROW BEGIN WHERE (SELECT 1 FROM object_view LIMIT 1) = 0; SELECT RAISE(ABORT, 'corrupt definition of authority_list') WHERE (SELECT 1 FROM authority_list LIMIT 1) = 0; + + -- check geoid_model table + SELECT RAISE(ABORT, 'missing GEOID99 in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID99'); + SELECT RAISE(ABORT, 'missing GEOID03 in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID03'); + SELECT RAISE(ABORT, 'missing GEOID06 in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID06'); + SELECT RAISE(ABORT, 'missing GEOID09 in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID09'); + SELECT RAISE(ABORT, 'missing GEOID12A in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID12A'); + SELECT RAISE(ABORT, 'missing GEOID12B in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID12B'); + SELECT RAISE(ABORT, 'missing GEOID18 in geoid_model') + WHERE NOT EXISTS(SELECT 1 FROM geoid_model WHERE name = 'GEOID18'); END; INSERT INTO dummy DEFAULT VALUES; DROP TRIGGER final_checks; diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql index 99891dc1..6301bda9 100644 --- a/data/sql/customizations.sql +++ b/data/sql/customizations.sql @@ -83,15 +83,19 @@ INSERT INTO "helmert_transformation" VALUES('PROJ','WGS84_TO_WGS84_G1674','WGS 8 INSERT INTO "helmert_transformation" VALUES('PROJ','WGS84_TO_WGS84_G1762','WGS 84 to WGS 84 (G1762)','','Accuracy 2m','EPSG','9603','Geocentric translations (geog2D domain)','EPSG','4326','EPSG','9057','EPSG','1262',2.0,0,0,0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'',0); INSERT INTO "helmert_transformation" VALUES('PROJ','WGS84_TO_WGS84_TRANSIT','WGS 84 to WGS 84 (Transit)','','Accuracy 2m','EPSG','9603','Geocentric translations (geog2D domain)','EPSG','4326','EPSG','8888','EPSG','1262',2.0,0,0,0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'',0); ---- Remove a few supersessions --- - --- TODO: remove this once https://github.com/OSGeo/proj-datumgrid/issues/55 is dealt with --- The following supersessions are for geoid2012, replaced by geoid2018 --- but we don't have geoid2018 yet as PROJ grids, so remove for now those --- supersessions --- INSERT INTO "supersession" VALUES('grid_transformation','EPSG','6326','grid_transformation','EPSG','9229','EPSG'); --- INSERT INTO "supersession" VALUES('grid_transformation','EPSG','7646','grid_transformation','EPSG','9230','EPSG'); --- INSERT INTO "supersession" VALUES('grid_transformation','EPSG','7647','grid_transformation','EPSG','9231','EPSG'); -DELETE FROM supersession WHERE superseded_table_name = 'grid_transformation' AND superseded_auth_name = 'EPSG' AND superseded_code = '6326'; -DELETE FROM supersession WHERE superseded_table_name = 'grid_transformation' AND superseded_auth_name = 'EPSG' AND superseded_code = '7646'; -DELETE FROM supersession WHERE superseded_table_name = 'grid_transformation' AND superseded_auth_name = 'EPSG' AND superseded_code = '7647'; +---- Geoid models ----- + +INSERT INTO "geoid_model" SELECT 'GEOID99', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'g1999%' AND deprecated = 0; + +INSERT INTO "geoid_model" SELECT 'GEOID03', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'geoid03%' AND deprecated = 0; + +INSERT INTO "geoid_model" SELECT 'GEOID06', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'geoid06%' AND deprecated = 0; + +INSERT INTO "geoid_model" SELECT 'GEOID09', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'geoid09%' AND deprecated = 0; + +-- Geoid12A and Geoid12B are identical +INSERT INTO "geoid_model" SELECT 'GEOID12A', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'g2012b%' AND deprecated = 0; + +INSERT INTO "geoid_model" SELECT 'GEOID12B', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'g2012b%' AND deprecated = 0; + +INSERT INTO "geoid_model" SELECT 'GEOID18', auth_name, code FROM grid_transformation WHERE auth_name = 'EPSG' AND grid_name LIKE 'g2018%' AND deprecated = 0; diff --git a/data/sql/grid_alternatives.sql b/data/sql/grid_alternatives.sql index eb752e44..ab81a2fa 100644 --- a/data/sql/grid_alternatives.sql +++ b/data/sql/grid_alternatives.sql @@ -361,6 +361,74 @@ INSERT INTO grid_alternatives(original_grid_name, NULL, NULL, NULL, NULL); ---------------------------- +-- US GEOID99 height models +---------------------------- + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u01.bin', 'g1999u01.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u02.bin', 'g1999u02.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u03.bin', 'g1999u03.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u04.bin', 'g1999u04.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u05.bin', 'g1999u05.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u06.bin', 'g1999u06.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u07.bin', 'g1999u07.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g1999u08.bin', 'g1999u08.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +-- Not mapped: +-- g1999a01.gtx to g1999a04.gtx : Alaska +-- g1999h01.gtx : Hawaii +-- g1999p01.gtx : Puerto Rico + +---------------------------- +-- US GEOID03 height models +---------------------------- + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('geoid03_conus.bin', 'geoid03_conus.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +-- Not mapped: +-- g2003a01.gtx to g2003a04.gtx : Alaska +-- g2003h01.gtx : Hawaii +---g2003p01.gtx : Puerto Rico + +---------------------------- +-- US GEOID06 height models +---------------------------- + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('geoid06_ak.bin', 'geoid06_ak.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +---------------------------- +-- US GEOID09 height models +---------------------------- + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('geoid09_ak.bin', 'geoid09_ak.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('geoid09_conus.bin', 'geoid09_conus.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +-- Not mapped: +-- g2009h01.gtx : Hawaii +-- g2009g01.gtx : Guam and Northern Mariana Islands. +-- g2009s01.gtx : American Samoa. +-- g2009p01.gtx : Puerto Rico / U.S. Vigin Islands. + +---------------------------- -- US GEOID12B height models ---------------------------- @@ -444,6 +512,15 @@ INSERT INTO grid_alternatives(original_grid_name, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); +---------------------------- +-- US GEOID18 height models +---------------------------- + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g2018u0.bin', 'g2018u0.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); + +INSERT INTO grid_alternatives(original_grid_name, proj_grid_name, proj_grid_format, proj_method, inverse_direction, package_name, url, direct_download, open_license, directory) + VALUES ('g2018p0.bin', 'g2018p0.gtx', 'GTX', 'vgridshift', 1, 'proj-datumgrid-north-america', NULL, NULL, NULL, NULL); ---------------------------- -- French vertical grids diff --git a/data/sql/proj_db_table_defs.sql b/data/sql/proj_db_table_defs.sql index 8a02acf4..a97e75a8 100644 --- a/data/sql/proj_db_table_defs.sql +++ b/data/sql/proj_db_table_defs.sql @@ -1365,6 +1365,23 @@ FOR EACH ROW BEGIN END; + +CREATE TABLE geoid_model( + name TEXT NOT NULL, + operation_auth_name TEXT NOT NULL, + operation_code TEXT NOT NULL, + CONSTRAINT pk_geoid_model PRIMARY KEY (name, operation_auth_name, operation_code) + -- CONSTRATINT fk_geoid_model_operation FOREIGN KEY (operation_auth_name, operation_code) REFERENCES coordinate_operation(auth_name, code) +); + +CREATE TRIGGER geoid_model_insert_trigger +BEFORE INSERT ON geoid_model +FOR EACH ROW BEGIN + SELECT RAISE(ABORT, 'insert on geoid_model violates constraint: (operation_auth_name, operation_code) must already exist in coordinate_operation_with_conversion_view') + WHERE NOT EXISTS (SELECT 1 FROM coordinate_operation_with_conversion_view covwv WHERE covwv.auth_name = NEW.operation_auth_name AND covwv.code = NEW.operation_code); +END; + + CREATE TABLE alias_name( table_name TEXT NOT NULL CHECK (table_name IN ( 'unit_of_measure', 'celestial_body', 'ellipsoid', diff --git a/docs/source/usage/projjson.rst b/docs/source/usage/projjson.rst index 09123711..c7183185 100644 --- a/docs/source/usage/projjson.rst +++ b/docs/source/usage/projjson.rst @@ -13,13 +13,19 @@ the same as WKT2:2019. PROJJSON is available as input and output of PROJ since PROJ 6.2. -The current version is 0.1. +The current version is 0.2. Schema ------ A JSON schema of its grammar is available at -https://proj.org/schemas/v0.1/projjson.schema.json +https://proj.org/schemas/v0.2/projjson.schema.json + +History +------- + +* v0.2: addition of geoid_model in VerticalCRS object. +* v0.1: initial version for PROJ 6.2 Content ------- diff --git a/include/proj/internal/io_internal.hpp b/include/proj/internal/io_internal.hpp index 781dfa6c..1e2508cc 100644 --- a/include/proj/internal/io_internal.hpp +++ b/include/proj/internal/io_internal.hpp @@ -134,6 +134,7 @@ class WKTConstants { static const std::string BASEPARAMCRS; static const std::string BASETIMECRS; static const std::string VERSION; + static const std::string GEOIDMODEL; // WKT2-2019 // WKT2 alternate (longer or shorter) static const std::string GEODETICCRS; diff --git a/include/proj/io.hpp b/include/proj/io.hpp index f8ee1c97..17f0fea5 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -1118,6 +1118,10 @@ class PROJ_GCC_DLL AuthorityFactory { getPreferredHubGeodeticReferenceFrames( const std::string &geodeticReferenceFrameCode) const; + PROJ_INTERNAL std::vector<operation::CoordinateOperationNNPtr> + getTransformationsForGeoid(const std::string &geoidName, + bool usePROJAlternativeGridNames) const; + //! @endcond protected: diff --git a/schemas/v0.2/projjson.schema.json b/schemas/v0.2/projjson.schema.json new file mode 100644 index 00000000..e71e5031 --- /dev/null +++ b/schemas/v0.2/projjson.schema.json @@ -0,0 +1,981 @@ +{ + "$id": "https://proj.org/schemas/v0.2/projjson.schema.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Schema for PROJJSON", + "$comment": "This file exists both in data/ and in schemas/vXXX/. Keep both in sync. And if changing the value of $id, change PROJJSON_CURRENT_VERSION accordingly in io.cpp", + + "oneOf": [ + { "$ref": "#/definitions/crs" }, + { "$ref": "#/definitions/datum" }, + { "$ref": "#/definitions/datum_ensemble" }, + { "$ref": "#/definitions/ellipsoid" }, + { "$ref": "#/definitions/prime_meridian" }, + { "$ref": "#/definitions/single_operation" }, + { "$ref": "#/definitions/concatenated_operation" } + ], + + "definitions": { + + "abridged_transformation": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["AbridgedTransformation"] }, + "name": { "type": "string" }, + "method": { "$ref": "#/definitions/method" }, + "parameters": { + "type": "array", + "items": { "$ref": "#/definitions/parameter_value" } + }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "method", "parameters" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "axis": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["Axis"] }, + "name": { "type": "string" }, + "abbreviation": { "type": "string" }, + "direction": { "type": "string", + "enum": [ "north", + "northNorthEast", + "northEast", + "eastNorthEast", + "east", + "eastSouthEast", + "southEast", + "southSouthEast", + "south", + "southSouthWest", + "southWest", + "westSouthWest", + "west", + "westNorthWest", + "northWest", + "northNorthWest", + "up", + "down", + "geocentricX", + "geocentricY", + "geocentricZ", + "columnPositive", + "columnNegative", + "rowPositive", + "rowNegative", + "displayRight", + "displayLeft", + "displayUp", + "displayDown", + "forward", + "aft", + "port", + "starboard", + "clockwise", + "counterClockwise", + "towards", + "awayFrom", + "future", + "past", + "unspecified" ] }, + "unit": { "$ref": "#/definitions/unit" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "abbreviation", "direction" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "bbox": { + "type": "object", + "properties": { + "east_longitude": { "type": "number" }, + "west_longitude": { "type": "number" }, + "south_latitude": { "type": "number" }, + "north_latitude": { "type": "number" } + }, + "required" : [ "east_longitude", "west_longitude", + "south_latitude", "north_latitude" ], + "additionalProperties": false + }, + + "bound_crs": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["BoundCRS"] }, + "source_crs": { "$ref": "#/definitions/crs" }, + "target_crs": { "$ref": "#/definitions/crs" }, + "transformation": { "$ref": "#/definitions/abridged_transformation" } + }, + "required" : [ "source_crs", "target_crs", "transformation" ], + "additionalProperties": false + }, + + "compound_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["CompoundCRS"] }, + "name": { "type": "string" }, + "components": { + "type": "array", + "items": { "$ref": "#/definitions/crs" } + }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "components" ], + "additionalProperties": false + }, + + "concatenated_operation": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["ConcatenatedOperation"] }, + "name": { "type": "string" }, + "source_crs": { "$ref": "#/definitions/crs" }, + "target_crs": { "$ref": "#/definitions/crs" }, + "steps": { + "type": "array", + "items": { "$ref": "#/definitions/single_operation" } + }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "source_crs", "target_crs", "steps" ], + "additionalProperties": false + }, + + "conversion": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["Conversion"] }, + "name": { "type": "string" }, + "method": { "$ref": "#/definitions/method" }, + "parameters": { + "type": "array", + "items": { "$ref": "#/definitions/parameter_value" } + }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "method" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "coordinate_system": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["CoordinateSystem"] }, + "name": { "type": "string" }, + "subtype": { "type": "string", + "enum": ["Cartesian", + "spherical", + "ellipsoidal", + "vertical", + "ordinal", + "parametric", + "TemporalDateTime", + "TemporalCount", + "TemporalMeasure"] }, + "axis": { + "type": "array", + "items": { "$ref": "#/definitions/axis" } + }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "subtype", "axis" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "crs": { + "oneOf": [ + { "$ref": "#/definitions/bound_crs" }, + { "$ref": "#/definitions/compound_crs" }, + { "$ref": "#/definitions/derived_engineering_crs" }, + { "$ref": "#/definitions/derived_geodetic_crs" }, + { "$ref": "#/definitions/derived_parametric_crs" }, + { "$ref": "#/definitions/derived_projected_crs" }, + { "$ref": "#/definitions/derived_temporal_crs" }, + { "$ref": "#/definitions/derived_vertical_crs" }, + { "$ref": "#/definitions/engineering_crs" }, + { "$ref": "#/definitions/geodetic_crs" }, + { "$ref": "#/definitions/parametric_crs" }, + { "$ref": "#/definitions/projected_crs" }, + { "$ref": "#/definitions/temporal_crs" }, + { "$ref": "#/definitions/vertical_crs" } + ] + }, + + "datum": { + "oneOf": [ + { "$ref": "#/definitions/geodetic_reference_frame" }, + { "$ref": "#/definitions/vertical_reference_frame" }, + { "$ref": "#/definitions/dynamic_geodetic_reference_frame" }, + { "$ref": "#/definitions/dynamic_vertical_reference_frame" }, + { "$ref": "#/definitions/temporal_datum" }, + { "$ref": "#/definitions/parametric_datum" }, + { "$ref": "#/definitions/engineering_datum" } + ] + }, + + "datum_ensemble": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["DatumEnsemble"] }, + "name": { "type": "string" }, + "members": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + } + }, + "ellipsoid": { "$ref": "#/definitions/ellipsoid" }, + "accuracy": { "type": "string" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "members", "accuracy" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "derived_engineering_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedEngineeringCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/engineering_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "derived_geodetic_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedGeodeticCRS", + "DerivedGeographicCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/geodetic_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "derived_parametric_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedParametricCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/parametric_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "derived_projected_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedProjectedCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/projected_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "derived_temporal_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedTemporalCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/temporal_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "derived_vertical_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["DerivedVerticalCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/vertical_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "dynamic_geodetic_reference_frame": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/geodetic_reference_frame" }], + "properties": { + "type": { "type": "string", "enum": ["DynamicGeodeticReferenceFrame"] }, + "name": {}, + "anchor": {}, + "ellipsoid": {}, + "prime_meridian": {}, + "frame_reference_epoch": { "type": "number" }, + "deformation_model": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "ellipsoid", "frame_reference_epoch" ], + "additionalProperties": false + }, + + "dynamic_vertical_reference_frame": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/vertical_reference_frame" }], + "properties": { + "type": { "type": "string", "enum": ["DynamicVerticalReferenceFrame"] }, + "name": {}, + "anchor": {}, + "frame_reference_epoch": { "type": "number" }, + "deformation_model": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "frame_reference_epoch" ], + "additionalProperties": false + }, + + "ellipsoid": { + "type": "object", + "oneOf":[ + { + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["Ellipsoid"] }, + "name": { "type": "string" }, + "semi_major_axis": { "$ref": "#/definitions/value_in_metre_or_value_and_unit" }, + "semi_minor_axis": { "$ref": "#/definitions/value_in_metre_or_value_and_unit" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "semi_major_axis", "semi_minor_axis" ], + "additionalProperties": false + }, + { + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["Ellipsoid"] }, + "name": { "type": "string" }, + "semi_major_axis": { "$ref": "#/definitions/value_in_metre_or_value_and_unit" }, + "inverse_flattening": { "type": "number" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "semi_major_axis", "inverse_flattening" ], + "additionalProperties": false + }, + { + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["Ellipsoid"] }, + "name": { "type": "string" }, + "radius": { "$ref": "#/definitions/value_in_metre_or_value_and_unit" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "radius" ], + "additionalProperties": false + } + ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ] + }, + + "engineering_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["EngineeringCRS"] }, + "name": { "type": "string" }, + "datum": { "$ref": "#/definitions/engineering_datum" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "datum" ], + "additionalProperties": false + }, + + "engineering_datum": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["EngineeringDatum"] }, + "name": { "type": "string" }, + "anchor": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name" ], + "additionalProperties": false + }, + + "geodetic_crs": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["GeodeticCRS", "GeographicCRS"] }, + "name": { "type": "string" }, + "datum": { + "oneOf": [ + { "$ref": "#/definitions/geodetic_reference_frame" }, + { "$ref": "#/definitions/dynamic_geodetic_reference_frame" } + ] + }, + "datum_ensemble": { "$ref": "#/definitions/datum_ensemble" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name" ], + "description": "One and only one of datum and datum_ensemble must be provided", + "allOf": [ + { "$ref": "#/definitions/object_usage" }, + { "$ref": "#/definitions/one_and_only_one_of_datum_or_datum_ensemble" } + ], + "additionalProperties": false + }, + + "geodetic_reference_frame": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["GeodeticReferenceFrame"] }, + "name": { "type": "string" }, + "anchor": { "type": "string" }, + "ellipsoid": { "$ref": "#/definitions/ellipsoid" }, + "prime_meridian": { "$ref": "#/definitions/prime_meridian" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "ellipsoid" ], + "additionalProperties": false + }, + + "id": { + "type": "object", + "properties": { + "authority": { "type": "string" }, + "code": { + "oneOf": [ { "type": "string" }, { "type": "integer" } ] + } + }, + "required" : [ "authority", "code" ], + "additionalProperties": false + }, + + "ids": { + "type": "array", + "items": { "$ref": "#/definitions/id" } + }, + + "method": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["OperationMethod"]}, + "name": { "type": "string" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "id_ids_mutually_exclusive": { + "not": { + "type": "object", + "required": [ "id", "ids" ] + } + }, + + "one_and_only_one_of_datum_or_datum_ensemble": { + "allOf": [ + { + "not": { + "type": "object", + "required": [ "datum", "datum_ensemble" ] + } + }, + { + "oneOf": [ + { "type": "object", "required": ["datum"] }, + { "type": "object", "required": ["datum_ensemble"] } + ] + } + ] + }, + + "object_usage": { + "anyOf": [ + { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "scope": { "type": "string" }, + "area": { "type": "string" }, + "bbox": { "$ref": "#/definitions/bbox" }, + "remarks": { "type": "string" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ] + }, + { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "usages": { "$ref": "#/definitions/usages" }, + "remarks": { "type": "string" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ] + } + ] + }, + + "parameter_value": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["ParameterValue"] }, + "name": { "type": "string" }, + "value": { + "oneOf": [ + { "type": "string" }, + { "type": "number" } + ] + }, + "unit": { "$ref": "#/definitions/unit" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name", "value" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "parametric_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["ParametricCRS"] }, + "name": { "type": "string" }, + "datum": { "$ref": "#/definitions/parametric_datum" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "datum" ], + "additionalProperties": false + }, + + "parametric_datum": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["ParametricDatum"] }, + "name": { "type": "string" }, + "anchor": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name" ], + "additionalProperties": false + }, + + "prime_meridian": { + "type": "object", + "properties": { + "$schema" : { "type": "string" }, + "type": { "type": "string", "enum": ["PrimeMeridian"] }, + "name": { "type": "string" }, + "longitude": { "$ref": "#/definitions/value_and_unit" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "name" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + }, + + "single_operation": { + "oneOf": [ + { "$ref": "#/definitions/conversion" }, + { "$ref": "#/definitions/transformation" } + ] + }, + + "projected_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", + "enum": ["ProjectedCRS"] }, + "name": { "type": "string" }, + "base_crs": { "$ref": "#/definitions/geodetic_crs" }, + "conversion": { "$ref": "#/definitions/conversion" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "base_crs", "conversion", "coordinate_system" ], + "additionalProperties": false + }, + + "temporal_crs": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["TemporalCRS"] }, + "name": { "type": "string" }, + "datum": { "$ref": "#/definitions/temporal_datum" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "datum" ], + "additionalProperties": false + }, + + "temporal_datum": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["TemporalDatum"] }, + "name": { "type": "string" }, + "calendar": { "type": "string" }, + "time_origin": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "calendar" ], + "additionalProperties": false + }, + + "transformation": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["Transformation"] }, + "name": { "type": "string" }, + "source_crs": { "$ref": "#/definitions/crs" }, + "target_crs": { "$ref": "#/definitions/crs" }, + "interpolation_crs": { "$ref": "#/definitions/crs" }, + "method": { "$ref": "#/definitions/method" }, + "parameters": { + "type": "array", + "items": { "$ref": "#/definitions/parameter_value" } + }, + "accuracy": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name", "source_crs", "target_crs", "method", "parameters" ], + "additionalProperties": false + }, + + "unit": { + "oneOf": [ + { + "type": "string", + "enum": ["metre", "degree", "unity"] + }, + { + "type": "object", + "properties": { + "type": { "type": "string", + "enum": ["LinearUnit", "AngularUnit", "ScaleUnit", + "TimeUnit", "ParametricUnit", "Unit"] }, + "name": { "type": "string" }, + "conversion_factor": { "type": "number" }, + "id": { "$ref": "#/definitions/id" }, + "ids": { "$ref": "#/definitions/ids" } + }, + "required" : [ "type", "name" ], + "allOf": [ + { "$ref": "#/definitions/id_ids_mutually_exclusive" } + ], + "additionalProperties": false + } + ] + }, + + "usages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "scope": { "type": "string" }, + "area": { "type": "string" }, + "bbox": { "$ref": "#/definitions/bbox" } + }, + "additionalProperties": false + } + }, + + "value_and_unit": { + "type": "object", + "properties": { + "value": { "type": "number" }, + "unit": { "$ref": "#/definitions/unit" } + }, + "required" : [ "value", "unit" ], + "additionalProperties": false + }, + + "value_in_metre_or_value_and_unit": { + "oneOf": [ + { "type": "number" }, + { "$ref": "#/definitions/value_and_unit" } + ] + }, + + "vertical_crs": { + "type": "object", + "properties": { + "type": { "type": "string", "enum": ["VerticalCRS"] }, + "name": { "type": "string" }, + "datum": { + "oneOf": [ + { "$ref": "#/definitions/vertical_reference_frame" }, + { "$ref": "#/definitions/dynamic_vertical_reference_frame" } + ] + }, + "datum_ensemble": { "$ref": "#/definitions/datum_ensemble" }, + "coordinate_system": { "$ref": "#/definitions/coordinate_system" }, + "geoid_model": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "id": { "$ref": "#/definitions/id" } + }, + "required" : [ "name" ], + "additionalProperties": false + }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name"], + "description": "One and only one of datum and datum_ensemble must be provided", + "allOf": [ + { "$ref": "#/definitions/object_usage" }, + { "$ref": "#/definitions/one_and_only_one_of_datum_or_datum_ensemble" } + ], + "additionalProperties": false + }, + + "vertical_reference_frame": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/object_usage" }], + "properties": { + "type": { "type": "string", "enum": ["VerticalReferenceFrame"] }, + "name": { "type": "string" }, + "anchor": { "type": "string" }, + "$schema" : {}, + "scope": {}, + "area": {}, + "bbox": {}, + "usages": {}, + "remarks": {}, + "id": {}, "ids": {} + }, + "required" : [ "name" ], + "additionalProperties": false + } + + } +} diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index cb568566..67d4837f 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -899,6 +899,7 @@ proj_create_operations proj_create_projected_crs proj_create_transformation proj_create_vertical_crs +proj_create_vertical_crs_ex proj_crs_alter_cs_angular_unit proj_crs_alter_cs_linear_unit proj_crs_alter_geodetic_crs diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 337de313..f3badb51 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -2615,13 +2615,19 @@ int proj_coordoperation_get_method_info(PJ_CONTEXT *ctx, // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -static PropertyMap createPropertyMapName(const char *c_name) { +static PropertyMap createPropertyMapName(const char *c_name, + const char *auth_name = nullptr, + const char *code = nullptr) { std::string name(c_name ? c_name : "unnamed"); PropertyMap properties; if (ends_with(name, " (deprecated)")) { name.resize(name.size() - strlen(" (deprecated)")); properties.set(common::IdentifiedObject::DEPRECATED_KEY, true); } + if (auth_name && code) { + properties.set(metadata::Identifier::CODESPACE_KEY, auth_name); + properties.set(metadata::Identifier::CODE_KEY, code); + } return properties.set(common::IdentifiedObject::NAME_KEY, name); } @@ -2946,15 +2952,76 @@ PJ *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, const char *linear_units, double linear_units_conv) { + return proj_create_vertical_crs_ex( + ctx, crs_name, datum_name, nullptr, nullptr, linear_units, + linear_units_conv, nullptr, nullptr, nullptr, nullptr, nullptr); +} + +// --------------------------------------------------------------------------- + +/** \brief Create a VerticalCRS + * + * The returned object must be unreferenced with proj_destroy() after + * use. + * It should be used by at most one thread at a time. + * + * This is an extented (_ex) version of proj_create_vertical_crs() that adds + * the capability of defining a geoid model. + * + * @param ctx PROJ context, or NULL for default context + * @param crs_name Name of the GeographicCRS. Or NULL + * @param datum_name Name of the VerticalReferenceFrame. Or NULL + * @param datum_auth_name Authority name of the VerticalReferenceFrame. Or NULL + * @param datum_code Code of the VerticalReferenceFrame. Or NULL + * @param linear_units Name of the linear units. Or NULL for Metre + * @param linear_units_conv Conversion factor from the linear unit to metre. Or + * 0 for Metre if linear_units == NULL. Otherwise should be not NULL + * @param geoid_model_name Geoid model name, or NULL. Can be a name from the + * geoid_model name or a string "PROJ foo.gtx" + * @param geoid_model_auth_name Authority name of the transformation for + * the geoid model. or NULL + * @param geoid_model_code Code of the transformation for + * the geoid model. or NULL + * @param geoid_geog_crs Geographic CRS for the geoid transformation, or NULL. + * @param options should be set to NULL for now + * @return Object of type VerticalCRS that must be unreferenced with + * proj_destroy(), or NULL in case of error. + */ +PJ *proj_create_vertical_crs_ex( + PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name, + const char *datum_auth_name, const char *datum_code, + const char *linear_units, double linear_units_conv, + const char *geoid_model_name, const char *geoid_model_auth_name, + const char *geoid_model_code, const PJ *geoid_geog_crs, + const char *const *options) { SANITIZE_CTX(ctx); + (void)options; try { const UnitOfMeasure linearUnit( createLinearUnit(linear_units, linear_units_conv)); - auto datum = - VerticalReferenceFrame::create(createPropertyMapName(datum_name)); - auto vertCRS = VerticalCRS::create( - createPropertyMapName(crs_name), datum, - cs::VerticalCS::createGravityRelatedHeight(linearUnit)); + auto datum = VerticalReferenceFrame::create( + createPropertyMapName(datum_name, datum_auth_name, datum_code)); + auto props = createPropertyMapName(crs_name); + auto cs = cs::VerticalCS::createGravityRelatedHeight(linearUnit); + if (geoid_model_name) { + auto propsModel = createPropertyMapName( + geoid_model_name, geoid_model_auth_name, geoid_model_code); + const auto vertCRSWithoutGeoid = + VerticalCRS::create(props, datum, cs); + const auto interpCRS = + geoid_geog_crs && std::dynamic_pointer_cast<GeographicCRS>( + geoid_geog_crs->iso_obj) + ? std::dynamic_pointer_cast<CRS>(geoid_geog_crs->iso_obj) + : nullptr; + const auto model(Transformation::create( + propsModel, vertCRSWithoutGeoid, GeographicCRS::EPSG_4979, + interpCRS, + OperationMethod::create(PropertyMap(), + std::vector<OperationParameterNNPtr>()), + {}, {})); + props.set("GEOID_MODEL", model); + } + auto vertCRS = VerticalCRS::create(props, datum, cs); return pj_obj_create(ctx, vertCRS); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); diff --git a/src/iso19111/coordinateoperation.cpp b/src/iso19111/coordinateoperation.cpp index 0983c471..c51b4d3d 100644 --- a/src/iso19111/coordinateoperation.cpp +++ b/src/iso19111/coordinateoperation.cpp @@ -10480,6 +10480,12 @@ struct CoordinateOperationFactory::Private { std::vector<CoordinateOperationNNPtr> &res); static std::vector<CoordinateOperationNNPtr> + createOperationsGeogToVertFromGeoid(const crs::CRSNNPtr &sourceCRS, + const crs::CRSNNPtr &targetCRS, + const crs::VerticalCRS *vertDst, + Context &context); + + static std::vector<CoordinateOperationNNPtr> createOperationsGeogToVertWithIntermediateVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, const crs::VerticalCRS *vertDst, Context &context); @@ -12813,6 +12819,18 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( ENTER_FUNCTION(); + if (geogSrc && vertDst) { + res = createOperationsGeogToVertFromGeoid(sourceCRS, targetCRS, vertDst, + context); + } else if (geogDst && vertSrc) { + res = applyInverse(createOperationsGeogToVertFromGeoid( + targetCRS, sourceCRS, vertSrc, context)); + } + + if (!res.empty()) { + return true; + } + res = findOpsInRegistryDirect(sourceCRS, targetCRS, context); // If we get at least a result with perfect accuracy, do not @@ -12936,6 +12954,116 @@ findCandidateVertCRSForDatum(const io::AuthorityFactoryPtr &authFactory, // --------------------------------------------------------------------------- +std::vector<CoordinateOperationNNPtr> +CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid( + const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::VerticalCRS *vertDst, Private::Context &context) { + + ENTER_FUNCTION(); + + const auto useTransf = [&targetCRS, &context, + vertDst](const CoordinateOperationNNPtr &op) { + const auto targetOp = + dynamic_cast<const crs::VerticalCRS *>(op->targetCRS().get()); + assert(targetOp); + if (targetOp->_isEquivalentTo( + vertDst, util::IComparable::Criterion::EQUIVALENT)) { + return op; + } + std::vector<CoordinateOperationNNPtr> tmp; + createOperationsVertToVert(NN_NO_CHECK(op->targetCRS()), targetCRS, + context, targetOp, vertDst, tmp); + assert(!tmp.empty()); + auto ret = ConcatenatedOperation::createComputeMetadata( + {op, tmp.front()}, !allowEmptyIntersection); + return ret; + }; + + const auto getProjGeoidTransformation = [&sourceCRS, &targetCRS, &vertDst]( + const CoordinateOperationNNPtr &model, + const std::string &projFilename) { + + const auto getNameVertCRSMetre = [](const std::string &name) { + if (name.empty()) + return std::string("unnamed"); + auto ret(name); + bool haveOriginalUnit = false; + if (name.back() == ')') { + const auto pos = ret.rfind(" ("); + if (pos != std::string::npos) { + haveOriginalUnit = true; + ret = ret.substr(0, pos); + } + } + const auto pos = ret.rfind(" depth"); + if (pos != std::string::npos) { + ret = ret.substr(0, pos) + " height"; + } + if (!haveOriginalUnit) { + ret += " (metre)"; + } + return ret; + }; + + const auto &axis = vertDst->coordinateSystem()->axisList()[0]; + const auto geogSrcCRS = + dynamic_cast<crs::GeographicCRS *>(model->interpolationCRS().get()) + ? NN_NO_CHECK(model->interpolationCRS()) + : sourceCRS; + const auto vertCRSMetre = + axis->unit() == common::UnitOfMeasure::METRE && + axis->direction() == cs::AxisDirection::UP + ? targetCRS + : util::nn_static_pointer_cast<crs::CRS>( + crs::VerticalCRS::create( + util::PropertyMap().set( + common::IdentifiedObject::NAME_KEY, + getNameVertCRSMetre(targetCRS->nameStr())), + vertDst->datum(), vertDst->datumEnsemble(), + cs::VerticalCS::createGravityRelatedHeight( + common::UnitOfMeasure::METRE))); + const auto properties = util::PropertyMap().set( + common::IdentifiedObject::NAME_KEY, + buildOpName("Transformation", vertCRSMetre, geogSrcCRS)); + return Transformation::createGravityRelatedHeightToGeographic3D( + properties, vertCRSMetre, geogSrcCRS, nullptr, projFilename, {}); + }; + + std::vector<CoordinateOperationNNPtr> res; + const auto &authFactory = context.context->getAuthorityFactory(); + if (authFactory) { + const auto &models = vertDst->geoidModel(); + for (const auto &model : models) { + const auto &modelName = model->nameStr(); + const auto transformations = + starts_with(modelName, "PROJ ") + ? std::vector< + CoordinateOperationNNPtr>{getProjGeoidTransformation( + model, modelName.substr(strlen("PROJ ")))} + : authFactory->getTransformationsForGeoid( + modelName, + context.context->getUsePROJAlternativeGridNames()); + for (const auto &transf : transformations) { + if (dynamic_cast<crs::GeographicCRS *>( + transf->sourceCRS().get()) && + dynamic_cast<crs::VerticalCRS *>( + transf->targetCRS().get())) { + res.push_back(useTransf(transf)); + } else if (dynamic_cast<crs::GeographicCRS *>( + transf->targetCRS().get()) && + dynamic_cast<crs::VerticalCRS *>( + transf->sourceCRS().get())) { + res.push_back(useTransf(transf->inverse())); + } + } + } + } + + return res; +} + +// --------------------------------------------------------------------------- + std::vector<CoordinateOperationNNPtr> CoordinateOperationFactory::Private:: createOperationsGeogToVertWithIntermediateVert( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, @@ -14261,16 +14389,25 @@ getResolvedCRS(const crs::CRSNNPtr &crs, } else { auto outCrs = tryToIdentifyByName( io::AuthorityFactory::ObjectType::COMPOUND_CRS); + const auto &components = compoundCrs->componentReferenceSystems(); if (outCrs.get() != crs.get()) { - return outCrs; + bool hasGeoid = false; + if (components.size() == 2) { + auto vertCRS = + dynamic_cast<crs::VerticalCRS *>(components[1].get()); + if (vertCRS && !vertCRS->geoidModel().empty()) { + hasGeoid = true; + } + } + if (!hasGeoid) { + return outCrs; + } } if (approxExtent || !extentOut) { // If we still did not get a reliable extent, then try to // resolve the components of the compoundCRS, and take the // intersection of their extent. extentOut = metadata::ExtentPtr(); - const auto &components = - compoundCrs->componentReferenceSystems(); for (const auto &component : components) { metadata::ExtentPtr componentExtent; getResolvedCRS(component, context, componentExtent); diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp index 26725e24..b9b38c80 100644 --- a/src/iso19111/crs.cpp +++ b/src/iso19111/crs.cpp @@ -2478,6 +2478,14 @@ void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const { cs->_exportToWKT(formatter); formatter->setOutputAxis(oldAxisOutputRule); + if (isWKT2 && formatter->use2019Keywords() && !d->geoidModel.empty()) { + const auto &model = d->geoidModel[0]; + formatter->startNode(io::WKTConstants::GEOIDMODEL, false); + formatter->addQuotedString(model->nameStr()); + model->formatID(formatter); + formatter->endNode(); + } + ObjectUsage::baseExportToWKT(formatter); formatter->endNode(); } @@ -2539,6 +2547,15 @@ void VerticalCRS::_exportToJSON( formatter->setOmitTypeInImmediateChild(); coordinateSystem()->_exportToJSON(formatter); + if (!d->geoidModel.empty()) { + const auto &model = d->geoidModel[0]; + writer.AddObjKey("geoid_model"); + auto objectContext2(formatter->MakeObjectContext(nullptr, false)); + writer.AddObjKey("name"); + writer.Add(model->nameStr()); + model->formatID(formatter); + } + ObjectUsage::baseExportToJSON(formatter); } //! @endcond @@ -2572,7 +2589,8 @@ void VerticalCRS::addLinearUnitConvert( * cs::VerticalCS. * * @param properties See \ref general_properties. - * At minimum the name should be defined. + * At minimum the name should be defined. The GEOID_MODEL property can be set + * to a TransformationNNPtr object. * @param datumIn The datum of the CRS. * @param csIn a VerticalCS. * @return new VerticalCRS. @@ -2592,7 +2610,8 @@ VerticalCRS::create(const util::PropertyMap &properties, * One and only one of datum or datumEnsemble should be set to a non-null value. * * @param properties See \ref general_properties. - * At minimum the name should be defined. + * At minimum the name should be defined. The GEOID_MODEL property can be set + * to a TransformationNNPtr object. * @param datumIn The datum of the CRS, or nullptr * @param datumEnsembleIn The datum ensemble of the CRS, or nullptr. * @param csIn a VerticalCS. @@ -2607,6 +2626,14 @@ VerticalCRS::create(const util::PropertyMap &properties, csIn)); crs->assignSelf(crs); crs->setProperties(properties); + const auto geoidModelPtr = properties.get("GEOID_MODEL"); + if (geoidModelPtr) { + auto transf = util::nn_dynamic_pointer_cast<operation::Transformation>( + *geoidModelPtr); + if (transf) { + crs->d->geoidModel.emplace_back(NN_NO_CHECK(transf)); + } + } return crs; } diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 307c3a07..d9917996 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -5380,6 +5380,28 @@ AuthorityFactory::getPreferredHubGeodeticReferenceFrames( // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress +std::vector<operation::CoordinateOperationNNPtr> +AuthorityFactory::getTransformationsForGeoid( + const std::string &geoidName, bool usePROJAlternativeGridNames) const { + std::vector<operation::CoordinateOperationNNPtr> res; + + const std::string sql("SELECT operation_auth_name, operation_code FROM " + "geoid_model WHERE name = ?"); + auto sqlRes = d->run(sql, {geoidName}); + for (const auto &row : sqlRes) { + const auto &auth_name = row[0]; + const auto &code = row[1]; + res.emplace_back(d->createFactory(auth_name)->createCoordinateOperation( + code, usePROJAlternativeGridNames)); + } + + return res; +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress FactoryException::FactoryException(const char *message) : Exception(message) {} // --------------------------------------------------------------------------- diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 50ad5a87..645bec0b 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -89,7 +89,7 @@ static const std::string emptyString{}; // If changing that value, change it in data/projjson.schema.json as well #define PROJJSON_CURRENT_VERSION \ - "https://proj.org/schemas/v0.1/projjson.schema.json" + "https://proj.org/schemas/v0.2/projjson.schema.json" //! @endcond #if 0 @@ -3873,28 +3873,110 @@ CRSNNPtr WKTParser::Private::buildVerticalCRS(const WKTNodeNNPtr &node) { ThrowNotExpectedCSType("vertical"); } + auto &props = buildProperties(node); + + // Deal with Lidar WKT1 VertCRS that embeds geoid model in CRS name, + // following conventions from + // https://pubs.usgs.gov/tm/11b4/pdf/tm11-B4.pdf + // page 9 + if (ci_equal(nodeValue, WKTConstants::VERT_CS)) { + std::string name; + if (props.getStringValue(IdentifiedObject::NAME_KEY, name)) { + std::string geoidName; + for (const char *prefix : + {"NAVD88 - ", "NAVD88 via ", "NAVD88 height - ", + "NAVD88 height (ftUS) - "}) { + if (starts_with(name, prefix)) { + geoidName = name.substr(strlen(prefix)); + auto pos = geoidName.find_first_of(" ("); + if (pos != std::string::npos) { + geoidName.resize(pos); + } + break; + } + } + if (!geoidName.empty()) { + const auto &axis = verticalCS->axisList()[0]; + const auto &dir = axis->direction(); + if (dir == cs::AxisDirection::UP) { + if (axis->unit() == common::UnitOfMeasure::METRE) { + props.set(IdentifiedObject::NAME_KEY, "NAVD88 height"); + props.set(Identifier::CODE_KEY, 5703); + props.set(Identifier::CODESPACE_KEY, Identifier::EPSG); + } else if (axis->unit().name() == "US survey foot") { + props.set(IdentifiedObject::NAME_KEY, + "NAVD88 height (ftUS)"); + props.set(Identifier::CODE_KEY, 6360); + props.set(Identifier::CODESPACE_KEY, Identifier::EPSG); + } + } + PropertyMap propsModel; + propsModel.set(IdentifiedObject::NAME_KEY, toupper(geoidName)); + PropertyMap propsDatum; + propsDatum.set(IdentifiedObject::NAME_KEY, + "North American Vertical Datum 1988"); + propsDatum.set(Identifier::CODE_KEY, 5103); + propsDatum.set(Identifier::CODESPACE_KEY, Identifier::EPSG); + datum = + VerticalReferenceFrame::create(propsDatum).as_nullable(); + const auto dummyCRS = + VerticalCRS::create(PropertyMap(), datum, datumEnsemble, + NN_NO_CHECK(verticalCS)); + const auto model(Transformation::create( + propsModel, dummyCRS, dummyCRS, nullptr, + OperationMethod::create( + PropertyMap(), std::vector<OperationParameterNNPtr>()), + {}, {})); + props.set("GEOID_MODEL", model); + } + } + } + + auto &geoidModelNode = nodeP->lookForChild(WKTConstants::GEOIDMODEL); + if (!isNull(geoidModelNode)) { + auto &propsModel = buildProperties(geoidModelNode); + const auto dummyCRS = VerticalCRS::create( + PropertyMap(), datum, datumEnsemble, NN_NO_CHECK(verticalCS)); + const auto model(Transformation::create( + propsModel, dummyCRS, dummyCRS, nullptr, + OperationMethod::create(PropertyMap(), + std::vector<OperationParameterNNPtr>()), + {}, {})); + props.set("GEOID_MODEL", model); + } + auto crs = nn_static_pointer_cast<CRS>(VerticalCRS::create( - buildProperties(node), datum, datumEnsemble, NN_NO_CHECK(verticalCS))); + props, datum, datumEnsemble, NN_NO_CHECK(verticalCS))); if (!isNull(datumNode)) { auto &extensionNode = datumNode->lookForChild(WKTConstants::EXTENSION); const auto &extensionChildren = extensionNode->GP()->children(); if (extensionChildren.size() == 2) { if (ci_equal(stripQuotes(extensionChildren[0]), "PROJ4_GRIDS")) { - std::string transformationName(crs->nameStr()); - if (!ends_with(transformationName, " height")) { - transformationName += " height"; + const auto gridName(stripQuotes(extensionChildren[1])); + // This is the expansion of EPSG:5703 by old GDAL versions. + // See + // https://trac.osgeo.org/metacrs/changeset?reponame=&new=2281%40geotiff%2Ftrunk%2Flibgeotiff%2Fcsv%2Fvertcs.override.csv&old=1893%40geotiff%2Ftrunk%2Flibgeotiff%2Fcsv%2Fvertcs.override.csv + // It is unlikely that the user really explictly wants this. + if (gridName != "g2003conus.gtx,g2003alaska.gtx," + "g2003h01.gtx,g2003p01.gtx" && + gridName != "g2012a_conus.gtx,g2012a_alaska.gtx," + "g2012a_guam.gtx,g2012a_hawaii.gtx," + "g2012a_puertorico.gtx,g2012a_samoa.gtx") { + std::string transformationName(crs->nameStr()); + if (!ends_with(transformationName, " height")) { + transformationName += " height"; + } + transformationName += " to WGS84 ellipsoidal height"; + auto transformation = Transformation:: + createGravityRelatedHeightToGeographic3D( + PropertyMap().set(IdentifiedObject::NAME_KEY, + transformationName), + crs, GeographicCRS::EPSG_4979, nullptr, gridName, + std::vector<PositionalAccuracyNNPtr>()); + return nn_static_pointer_cast<CRS>(BoundCRS::create( + crs, GeographicCRS::EPSG_4979, transformation)); } - transformationName += " to WGS84 ellipsoidal height"; - auto transformation = - Transformation::createGravityRelatedHeightToGeographic3D( - PropertyMap().set(IdentifiedObject::NAME_KEY, - transformationName), - crs, GeographicCRS::EPSG_4979, nullptr, - stripQuotes(extensionChildren[1]), - std::vector<PositionalAccuracyNNPtr>()); - return nn_static_pointer_cast<CRS>(BoundCRS::create( - crs, GeographicCRS::EPSG_4979, transformation)); } } } @@ -5040,7 +5122,22 @@ VerticalCRSNNPtr JSONParser::buildVerticalCRS(const json &j) { if (!verticalCS) { throw ParsingException("expected a vertical CS"); } - return VerticalCRS::create(buildProperties(j), datum, datumEnsemble, + + auto props = buildProperties(j); + if (j.contains("geoid_model")) { + auto geoidModelJ = getObject(j, "geoid_model"); + auto propsModel = buildProperties(geoidModelJ); + const auto dummyCRS = VerticalCRS::create( + PropertyMap(), datum, datumEnsemble, NN_NO_CHECK(verticalCS)); + const auto model(Transformation::create( + propsModel, dummyCRS, dummyCRS, nullptr, + OperationMethod::create(PropertyMap(), + std::vector<OperationParameterNNPtr>()), + {}, {})); + props.set("GEOID_MODEL", model); + } + + return VerticalCRS::create(props, datum, datumEnsemble, NN_NO_CHECK(verticalCS)); } diff --git a/src/iso19111/static.cpp b/src/iso19111/static.cpp index 824047f0..0ed08c95 100644 --- a/src/iso19111/static.cpp +++ b/src/iso19111/static.cpp @@ -274,6 +274,7 @@ DEFINE_WKT_CONSTANT(BASEENGCRS); DEFINE_WKT_CONSTANT(BASEPARAMCRS); DEFINE_WKT_CONSTANT(BASETIMECRS); DEFINE_WKT_CONSTANT(VERSION); +DEFINE_WKT_CONSTANT(GEOIDMODEL); DEFINE_WKT_CONSTANT(GEODETICCRS); DEFINE_WKT_CONSTANT(GEODETICDATUM); diff --git a/src/proj_experimental.h b/src/proj_experimental.h index c6d5bc45..0886ba28 100644 --- a/src/proj_experimental.h +++ b/src/proj_experimental.h @@ -248,6 +248,19 @@ PJ PROJ_DLL *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *linear_units, double linear_units_conv); +PJ PROJ_DLL *proj_create_vertical_crs_ex(PJ_CONTEXT *ctx, + const char *crs_name, + const char *datum_name, + const char *datum_auth_name, + const char* datum_code, + const char *linear_units, + double linear_units_conv, + const char* geoid_model_name, + const char* geoid_model_auth_name, + const char* geoid_model_code, + const PJ* geoid_geog_crs, + const char *const *options); + PJ PROJ_DLL *proj_create_compound_crs(PJ_CONTEXT *ctx, const char *crs_name, PJ* horiz_crs, diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index c1d9f3e0..2875626b 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -137,7 +137,7 @@ GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.25722 PROJJSON: { - "$schema": "https://proj.org/schemas/v0.1/projjson.schema.json", + "$schema": "https://proj.org/schemas/v0.2/projjson.schema.json", "type": "GeographicCRS", "name": "WGS 84", "datum": { @@ -947,14 +947,14 @@ COORDINATEOPERATION["RH2000 height to SWEREF99", ID["PROJ","EPSG_5613_TO_EPSG_4977"]] Testing NAD83(2011) + NAVD88 height -> NAD83(2011) : projinfo -s EPSG:6349 -t EPSG:6319 --spatial-test intersects -o PROJ -Candidate operations found: 3 +Candidate operations found: 2 ------------------------------------- Operation n°1: -unknown id, Inverse of NAD83(2011) to NAVD88 height (1), 0.02 m, USA - CONUS - onshore +unknown id, Inverse of NAD83(2011) to NAVD88 height (3), 0.015 m, USA - CONUS - onshore PROJ string: -+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=g2012bu0.gtx +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 ++proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=g2018u0.gtx +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 ------------------------------------- Operation n°2: @@ -964,14 +964,6 @@ unknown id, Inverse of NAD83(2011) to NAVD88 height (2), 0.02 m, USA - Alaska PROJ string: +proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=vgridshift +grids=g2012ba0.gtx +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 -------------------------------------- -Operation n°3: - -unknown id, Inverse of NAD83(2011) to NAVD88 height (3), 0.015 m, USA - CONUS - onshore - -PROJ string: -+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +inv +proj=vgridshift +grids=g2018u0.bin +multiplier=1 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 - Testing NGF IGN69 height to RGF93: projinfo -s EPSG:5720 -t EPSG:4965 -o PROJ Candidate operations found: 2 ------------------------------------- diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index b8310ce5..bdadc8b8 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -3602,7 +3602,7 @@ TEST_F(CApi, proj_as_projjson) { EXPECT_EQ(std::string(projjson), "{\n" " \"$schema\": " - "\"https://proj.org/schemas/v0.1/projjson.schema.json\",\n" + "\"https://proj.org/schemas/v0.2/projjson.schema.json\",\n" " \"type\": \"Ellipsoid\",\n" " \"name\": \"WGS 84\",\n" " \"semi_major_axis\": 6378137,\n" @@ -4215,4 +4215,111 @@ TEST_F( EXPECT_NEAR(outcoord.xyzt.z, -32.5823, 1e-3); } +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_create_vertical_crs_ex) { + + // NAD83(2011) / UTM zone 11N + auto horiz_crs = proj_create_from_database(m_ctxt, "EPSG", "6340", + PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper_horiz_crs(horiz_crs); + ASSERT_NE(horiz_crs, nullptr); + + auto vert_crs = proj_create_vertical_crs_ex( + m_ctxt, "myVertCRS (ftUS)", "myVertDatum", nullptr, nullptr, + "US survey foot", 0.304800609601219, "PROJ @foo.gtx", nullptr, nullptr, + nullptr, nullptr); + ObjectKeeper keeper_vert_crs(vert_crs); + ASSERT_NE(vert_crs, nullptr); + + auto compound = + proj_create_compound_crs(m_ctxt, "Compound", horiz_crs, vert_crs); + ObjectKeeper keeper_compound(compound); + ASSERT_NE(compound, nullptr); + + // NAD83(2011) 3D + PJ *geog_crs = proj_create(m_ctxt, "EPSG:6319"); + ObjectKeeper keeper_geog_crs(geog_crs); + ASSERT_NE(geog_crs, nullptr); + + auto P = proj_create_crs_to_crs_from_pj(m_ctxt, compound, geog_crs, nullptr, + nullptr); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + + auto name = proj_get_name(P); + ASSERT_TRUE(name != nullptr); + EXPECT_EQ(name, + std::string("Inverse of UTM zone 11N + " + "Transformation from myVertCRS (ftUS) to myVertCRS + " + "Transformation from myVertCRS to NAD83(2011)")); + + auto proj_5 = proj_as_proj_string(m_ctxt, P, PJ_PROJ_5, nullptr); + ASSERT_NE(proj_5, nullptr); + EXPECT_EQ(std::string(proj_5), + "+proj=pipeline " + "+step +inv +proj=utm +zone=11 +ellps=GRS80 " + "+step +proj=unitconvert +z_in=us-ft +z_out=m " + "+step +proj=vgridshift +grids=@foo.gtx +multiplier=1 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_create_vertical_crs_ex_with_geog_crs) { + + // NAD83(2011) / UTM zone 11N + auto horiz_crs = proj_create_from_database(m_ctxt, "EPSG", "6340", + PJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper_horiz_crs(horiz_crs); + ASSERT_NE(horiz_crs, nullptr); + + // WGS84 + PJ *wgs84 = proj_create(m_ctxt, "EPSG:4979"); + ObjectKeeper keeper_wgs84(wgs84); + ASSERT_NE(wgs84, nullptr); + + auto vert_crs = proj_create_vertical_crs_ex( + m_ctxt, "myVertCRS", "myVertDatum", nullptr, nullptr, "US survey foot", + 0.304800609601219, "PROJ @foo.gtx", nullptr, nullptr, wgs84, nullptr); + ObjectKeeper keeper_vert_crs(vert_crs); + ASSERT_NE(vert_crs, nullptr); + + auto compound = + proj_create_compound_crs(m_ctxt, "Compound", horiz_crs, vert_crs); + ObjectKeeper keeper_compound(compound); + ASSERT_NE(compound, nullptr); + + // NAD83(2011) 3D + PJ *geog_crs = proj_create(m_ctxt, "EPSG:6319"); + ObjectKeeper keeper_geog_crs(geog_crs); + ASSERT_NE(geog_crs, nullptr); + + auto P = proj_create_crs_to_crs_from_pj(m_ctxt, compound, geog_crs, nullptr, + nullptr); + ObjectKeeper keeper_P(P); + ASSERT_NE(P, nullptr); + + auto name = proj_get_name(P); + ASSERT_TRUE(name != nullptr); + EXPECT_EQ( + name, + std::string("Inverse of UTM zone 11N + " + "Ballpark geographic offset from NAD83(2011) to WGS 84 + " + "Transformation from myVertCRS to myVertCRS (metre) + " + "Transformation from myVertCRS (metre) to WGS 84 + " + "Ballpark geographic offset from WGS 84 to NAD83(2011)")); + + auto proj_5 = proj_as_proj_string(m_ctxt, P, PJ_PROJ_5, nullptr); + ASSERT_NE(proj_5, nullptr); + EXPECT_EQ(std::string(proj_5), + "+proj=pipeline " + "+step +inv +proj=utm +zone=11 +ellps=GRS80 " + "+step +proj=unitconvert +z_in=us-ft +z_out=m " + "+step +proj=vgridshift +grids=@foo.gtx +multiplier=1 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + } // namespace diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index 8aff0908..a71b63bb 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -1994,16 +1994,21 @@ TEST(wkt_parse, vertcrs_VRF_WKT2) { // --------------------------------------------------------------------------- TEST(wkt_parse, vertcrs_with_GEOIDMODEL) { - auto wkt = "VERTCRS[\"CGVD2013\"," - " VRF[\"Canadian Geodetic Vertical Datum of 2013\"]," - " CS[vertical,1]," - " AXIS[\"gravity-related height (H)\",up]," - " LENGTHUNIT[\"metre\",1.0]," - " GEOIDMODEL[\"CGG2013\",ID[\"EPSG\",6648]]]"; + auto wkt = "VERTCRS[\"CGVD2013\",\n" + " VDATUM[\"Canadian Geodetic Vertical Datum of 2013\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " GEOIDMODEL[\"CGG2013\",\n" + " ID[\"EPSG\",6648]]]"; auto obj = WKTParser().createFromWKT(wkt); auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj); ASSERT_TRUE(crs != nullptr); + EXPECT_EQ( + crs->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get()), + wkt); } // --------------------------------------------------------------------------- @@ -2060,6 +2065,71 @@ TEST(wkt_parse, vertcrs_WKT1_GDAL_minimum) { // --------------------------------------------------------------------------- +TEST(wkt_parse, vertcrs_WKT1_LAS_ftUS) { + auto wkt = "VERT_CS[\"NAVD88 - Geoid03 (Feet)\"," + " VERT_DATUM[\"unknown\",2005]," + " UNIT[\"US survey foot\",0.3048006096012192," + " AUTHORITY[\"EPSG\",\"9003\"]]," + " AXIS[\"Up\",UP]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), "NAVD88 height (ftUS)"); + ASSERT_EQ(crs->identifiers().size(), 1U); + EXPECT_EQ(crs->identifiers()[0]->code(), "6360"); + EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG"); + + const auto &geoidModel = crs->geoidModel(); + ASSERT_TRUE(!geoidModel.empty()); + EXPECT_EQ(geoidModel[0]->nameStr(), "GEOID03"); + + auto datum = crs->datum(); + EXPECT_EQ(datum->nameStr(), "North American Vertical Datum 1988"); + ASSERT_EQ(datum->identifiers().size(), 1U); + EXPECT_EQ(datum->identifiers()[0]->code(), "5103"); + EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG"); + + const auto &axis = crs->coordinateSystem()->axisList()[0]; + EXPECT_EQ(axis->direction(), AxisDirection::UP); + EXPECT_EQ(axis->unit().name(), "US survey foot"); + EXPECT_NEAR(axis->unit().conversionToSI(), 0.3048006096012192, 1e-16); +} + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, vertcrs_WKT1_LAS_metre) { + auto wkt = "VERT_CS[\"NAVD88 via Geoid09\"," + " VERT_DATUM[\"unknown\",2005]," + " UNIT[\"metre\",1.0," + " AUTHORITY[\"EPSG\",\"9001\"]]," + " AXIS[\"Up\",UP]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast<VerticalCRS>(obj); + ASSERT_TRUE(crs != nullptr); + EXPECT_EQ(crs->nameStr(), "NAVD88 height"); + ASSERT_EQ(crs->identifiers().size(), 1U); + EXPECT_EQ(crs->identifiers()[0]->code(), "5703"); + EXPECT_EQ(*(crs->identifiers()[0]->codeSpace()), "EPSG"); + + const auto &geoidModel = crs->geoidModel(); + ASSERT_TRUE(!geoidModel.empty()); + EXPECT_EQ(geoidModel[0]->nameStr(), "GEOID09"); + + auto datum = crs->datum(); + EXPECT_EQ(datum->nameStr(), "North American Vertical Datum 1988"); + ASSERT_EQ(datum->identifiers().size(), 1U); + EXPECT_EQ(datum->identifiers()[0]->code(), "5103"); + EXPECT_EQ(*(datum->identifiers()[0]->codeSpace()), "EPSG"); + + const auto &axis = crs->coordinateSystem()->axisList()[0]; + EXPECT_EQ(axis->direction(), AxisDirection::UP); + EXPECT_EQ(axis->unit(), UnitOfMeasure::METRE); +} + +// --------------------------------------------------------------------------- + TEST(wkt_parse, dynamic_vertical_reference_frame) { auto obj = WKTParser().createFromWKT( "VERTCRS[\"RH2000\"," @@ -10842,6 +10912,45 @@ TEST(json_import, vertical_crs_with_datum_ensemble) { // --------------------------------------------------------------------------- +TEST(json_import, vertical_crs_with_geoid_model) { + auto json = "{\n" + " \"$schema\": \"foo\",\n" + " \"type\": \"VerticalCRS\",\n" + " \"name\": \"CGVD2013\",\n" + " \"datum\": {\n" + " \"type\": \"VerticalReferenceFrame\",\n" + " \"name\": \"Canadian Geodetic Vertical Datum of 2013\"\n" + " },\n" + " \"coordinate_system\": {\n" + " \"subtype\": \"vertical\",\n" + " \"axis\": [\n" + " {\n" + " \"name\": \"Gravity-related height\",\n" + " \"abbreviation\": \"H\",\n" + " \"direction\": \"up\",\n" + " \"unit\": \"metre\"\n" + " }\n" + " ]\n" + " },\n" + " \"geoid_model\": {\n" + " \"name\": \"CGG2013\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 6648\n" + " }\n" + " }\n" + "}"; + + // No database + auto obj = createFromUserInput(json, nullptr); + auto vcrs = nn_dynamic_pointer_cast<VerticalCRS>(obj); + ASSERT_TRUE(vcrs != nullptr); + EXPECT_EQ(vcrs->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), + json); +} + +// --------------------------------------------------------------------------- + TEST(json_import, parametric_crs) { auto json = "{\n" " \"$schema\": \"foo\",\n" diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 8df785b1..07b0daea 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -7072,17 +7072,13 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { "+proj=pipeline " "+step +proj=axisswap +order=2,1 " "+step +proj=unitconvert +xy_in=deg +xy_out=rad " - // Inv here since the grid is not known - "+step +inv +proj=vgridshift +grids=geoid09_conus.bin " + "+step +proj=vgridshift +grids=geoid09_conus.gtx " "+multiplier=1 " "+step +proj=unitconvert +xy_in=rad +xy_out=deg " "+step +proj=axisswap +order=2,1"); } - // CompoundCRS to Geog3DCRS, with same vertical unit, and with - // ellipsoid height <--> vertical height correction that requires a - // horizontal adjustment before and after (which is empty in practice here - // as NAD83 to NAD83(2011) is no-op) + // NAD83 + NAVD88 height --> WGS 84 { auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); @@ -7105,10 +7101,10 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { ASSERT_GE(list.size(), 2U); EXPECT_EQ(list[0]->nameStr(), - "NAD83 to NAD83(2011) (1) + " - "Inverse of NAD83(2011) to NAVD88 height (1) + " - "Inverse of NAD83 to NAD83(2011) (1) + " - "NAD83 to WGS 84 (1)"); + "NAD83 to WGS 84 (1) + " + "Inverse of NAD83(NSRS2007) to WGS 84 (1) + " + "Inverse of NAD83(NSRS2007) to NAVD88 height (1) + " + "NAD83(NSRS2007) to WGS 84 (1)"); EXPECT_EQ(list[0]->exportToPROJString( PROJStringFormatter::create( PROJStringFormatter::Convention::PROJ_5, @@ -7117,30 +7113,10 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { "+proj=pipeline " "+step +proj=axisswap +order=2,1 " "+step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=vgridshift +grids=g2012bu0.gtx " + "+step +proj=vgridshift +grids=geoid09_conus.gtx " "+multiplier=1 " "+step +proj=unitconvert +xy_in=rad +xy_out=deg " "+step +proj=axisswap +order=2,1"); - - // Shows vertical step, and then horizontal step - EXPECT_EQ(list[1]->nameStr(), - "NAD83 to NAD83(2011) (1) + " - "Inverse of NAD83(2011) to NAVD88 height (1) + " - "Inverse of NAD83 to NAD83(2011) (1) + " - "NAD83 to WGS 84 (18)"); - EXPECT_EQ(list[1]->exportToPROJString( - PROJStringFormatter::create( - PROJStringFormatter::Convention::PROJ_5, - authFactory->databaseContext()) - .get()), - "+proj=pipeline " - "+step +proj=axisswap +order=2,1 " - "+step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=vgridshift +grids=g2012bu0.gtx " - "+multiplier=1 " - "+step +proj=hgridshift +grids=FL " - "+step +proj=unitconvert +xy_in=rad +xy_out=deg " - "+step +proj=axisswap +order=2,1"); } // Another variation, but post horizontal adjustment is in two steps @@ -7166,7 +7142,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { ASSERT_GE(list.size(), 2U); EXPECT_EQ(list[0]->nameStr(), - "Inverse of NAD83(2011) to NAVD88 height (1) + " + "Inverse of NAD83(2011) to NAVD88 height (3) + " "Inverse of NAD83 to NAD83(2011) (1) + " "NAD83 to WGS 84 (1)"); EXPECT_EQ(list[0]->exportToPROJString( @@ -7177,14 +7153,14 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { "+proj=pipeline " "+step +proj=axisswap +order=2,1 " "+step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=vgridshift +grids=g2012bu0.gtx " + "+step +proj=vgridshift +grids=g2018u0.gtx " "+multiplier=1 " "+step +proj=unitconvert +xy_in=rad +xy_out=deg " "+step +proj=axisswap +order=2,1"); // Shows vertical step, and then horizontal step EXPECT_EQ(list[1]->nameStr(), - "Inverse of NAD83(2011) to NAVD88 height (1) + " + "Inverse of NAD83(2011) to NAVD88 height (3) + " "Inverse of NAD83 to NAD83(2011) (1) + " "NAD83 to WGS 84 (18)"); EXPECT_EQ(list[1]->exportToPROJString( @@ -7195,7 +7171,7 @@ TEST(operation, compoundCRS_to_geogCRS_3D_context) { "+proj=pipeline " "+step +proj=axisswap +order=2,1 " "+step +proj=unitconvert +xy_in=deg +xy_out=rad " - "+step +proj=vgridshift +grids=g2012bu0.gtx " + "+step +proj=vgridshift +grids=g2018u0.gtx " "+multiplier=1 " "+step +proj=hgridshift +grids=FL " "+step +proj=unitconvert +xy_in=rad +xy_out=deg " @@ -7227,7 +7203,7 @@ TEST(operation, compoundCRS_to_geogCRS_2D_promote_to_3D_context) { ctxt); // The checked value is not that important, but in case this changes, // likely due to a EPSG upgrade, worth checking - ASSERT_EQ(listCompoundToGeog2D.size(), 469U); + ASSERT_EQ(listCompoundToGeog2D.size(), 467U); auto listGeog2DToCompound = CoordinateOperationFactory::create()->createOperations(dst, nnSrc, @@ -7623,6 +7599,88 @@ TEST( // --------------------------------------------------------------------------- +TEST(operation, compoundCRS_of_vertCRS_with_geoid_model_to_geogCRS) { + auto authFactory = + AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto ctxt = CoordinateOperationContext::create(authFactory, nullptr, 0.0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto wkt = + "COMPOUNDCRS[\"NAD83 / Pennsylvania South + NAVD88 height\",\n" + " PROJCRS[\"NAD83 / Pennsylvania South\",\n" + " BASEGEOGCRS[\"NAD83\",\n" + " DATUM[\"North American Datum 1983\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]],\n" + " CONVERSION[\"SPCS83 Pennsylvania South zone (meters)\",\n" + " METHOD[\"Lambert Conic Conformal (2SP)\",\n" + " ID[\"EPSG\",9802]],\n" + " PARAMETER[\"Latitude of false origin\",39.3333333333333,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8821]],\n" + " PARAMETER[\"Longitude of false origin\",-77.75,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8822]],\n" + " PARAMETER[\"Latitude of 1st standard " + "parallel\",40.9666666666667,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8823]],\n" + " PARAMETER[\"Latitude of 2nd standard " + "parallel\",39.9333333333333,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8824]],\n" + " PARAMETER[\"Easting at false origin\",600000,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8826]],\n" + " PARAMETER[\"Northing at false origin\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8827]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"easting (X)\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"northing (Y)\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " VERTCRS[\"NAVD88 height\",\n" + " VDATUM[\"North American Vertical Datum 1988\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " GEOIDMODEL[\"GEOID12B\"]]]"; + auto srcObj = + createFromUserInput(wkt, authFactory->databaseContext(), false); + auto src = nn_dynamic_pointer_cast<CRS>(srcObj); + ASSERT_TRUE(src != nullptr); + auto dst = authFactory->createCoordinateReferenceSystem("4269"); // NAD83 + + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src), dst, ctxt); + ASSERT_TRUE(!list.empty()); + EXPECT_EQ(list[0]->nameStr(), + "Inverse of SPCS83 Pennsylvania South zone (meters) + " + "Ballpark geographic offset from NAD83 to NAD83(2011) + " + "Inverse of NAD83(2011) to NAVD88 height (1) + " + "Ballpark geographic offset from NAD83(2011) to NAD83"); + auto op_proj = + list[0]->exportToPROJString(PROJStringFormatter::create().get()); + EXPECT_EQ(op_proj, + "+proj=pipeline " + "+step +inv +proj=lcc +lat_0=39.3333333333333 +lon_0=-77.75 " + "+lat_1=40.9666666666667 +lat_2=39.9333333333333 +x_0=600000 " + "+y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=g2012bu0.gtx +multiplier=1 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + TEST(operation, compoundCRS_from_WKT2_to_geogCRS_3D_context) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); |
