diff --git a/data/sql/commit.sql b/data/sql/commit.sql index e76135e768..9e8f777bb2 100644 --- a/data/sql/commit.sql +++ b/data/sql/commit.sql @@ -73,7 +73,11 @@ FOR EACH ROW BEGIN AND g1.source_crs_code = g2.source_crs_code AND g1.target_crs_auth_name = g2.target_crs_auth_name AND g1.target_crs_code = g2.target_crs_code - WHERE g1.auth_name = 'PROJ' AND g1.code NOT LIKE '%_RESTRICTED_TO_VERTCRS%' AND g2.auth_name = 'EPSG' AND g2.deprecated = 0) + WHERE g1.auth_name = 'PROJ' AND g1.code NOT LIKE '%_RESTRICTED_TO_VERTCRS%' AND g2.auth_name = 'EPSG' AND g2.deprecated = 0 AND ( + (g1.interpolation_crs_auth_name IS NULL AND g2.interpolation_crs_auth_name IS NULL) OR + (g1.interpolation_crs_auth_name IS NOT NULL AND g2.interpolation_crs_auth_name IS NOT NULL AND + g1.interpolation_crs_auth_name = g2.interpolation_crs_auth_name AND + g1.interpolation_crs_code = g2.interpolation_crs_code))) OR EXISTS (SELECT 1 FROM grid_transformation g1 JOIN grid_transformation g2 ON g1.source_crs_auth_name = g2.target_crs_auth_name diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql index b209b636a5..77426a1ca3 100644 --- a/data/sql/customizations.sql +++ b/data/sql/customizations.sql @@ -188,6 +188,19 @@ INSERT INTO usage SELECT scope_code FROM usage WHERE object_table_name = 'grid_transformation' AND object_auth_name = 'EPSG' AND object_code = '9123'; +-- Duplicate EPSG:10115, 10116, 10117 with NAD83(CSRS)v7 as interpolation CRS +INSERT INTO "grid_transformation" VALUES +('PROJ','EPSG_10115_WITH_NAD83CSRSV7_INTERPOLATION','CGVD28 height to CGVD2013a(2010) height (1)','Equivalent to HT2_2010v70 hybrid geoid model minus CGG2013a geoid model (i.e. CT code 9986 minus CT 9247 = CT 9987 minus CT 10109). Specifies NAD83(CSRS)v7 (code 8255) as interpolation CRS.','EPSG','1112','Vertical Offset by Grid Interpolation (NRCan byn)','EPSG','5713','EPSG','9245',0.05,'EPSG','8732','Vertical offset file','HT2_2010v70_CGG2013a.byn',NULL,NULL,NULL,NULL,'EPSG','8255','NR-Can HT2 2010',0); + +INSERT INTO "usage" VALUES('PROJ','USAGE_EPSG_10115_WITH_NAD83CSRSV7_INTERPOLATION','grid_transformation','PROJ','EPSG_10115_WITH_NAD83CSRSV7_INTERPOLATION','EPSG','1061','EPSG','1133'); + +INSERT INTO "grid_transformation" VALUES('PROJ','EPSG_10116_WITH_NAD83CSRSV7_INTERPOLATION','CGVD2013a(2010) height to CGVD2013a(2002) height (1)',' Specifies NAD83(CSRS)v7 (code 8255) as interpolation CRS. Equivalent to CGVD28 to CGVD2013a(2002) minus CGVD28 to CGVD2013a(2010) (i.e. CT code 10114 minus CT 10115).','EPSG','1113','Vertical Offset by velocity grid (NRCan byn)','EPSG','9245','EPSG','20034',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can',0); +INSERT INTO "usage" VALUES('PROJ','USAGE_EPSG_10116_WITH_NAD83CSRSV7_INTERPOLATION','grid_transformation','PROJ','EPSG_10116_WITH_NAD83CSRSV7_INTERPOLATION','EPSG','1061','EPSG','1026'); + +INSERT INTO "grid_transformation" VALUES('PROJ','EPSG_10117_WITH_NAD83CSRSV7_INTERPOLATION','CGVD2013a(2010) height to CGVD2013a(1997) height (1)','Specifies NAD83(CSRS)v7 (code 8255) as interpolation CRS. Equivalent to CGVD28 to CGVD2013a(1997) minus CGVD28 to CGVD2013a(2010) (i.e. CT code 10113 minus CT 10115).','EPSG','1113','Vertical Offset by velocity grid (NRCan byn)','EPSG','9245','EPSG','20035',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can',0); +INSERT INTO "usage" VALUES('PROJ','USAGE_EPSG_10117_WITH_NAD83CSRSV7_INTERPOLATION','grid_transformation','PROJ','EPSG_10117_WITH_NAD83CSRSV7_INTERPOLATION','EPSG','1061','EPSG','1026'); + + -- Define the allowed authorities, and their precedence, when researching a -- coordinate operation diff --git a/data/sql/grid_transformation.sql b/data/sql/grid_transformation.sql index 6548902e31..6cc61fabef 100644 --- a/data/sql/grid_transformation.sql +++ b/data/sql/grid_transformation.sql @@ -1440,6 +1440,28 @@ INSERT INTO "grid_transformation" VALUES('EPSG','10114','CGVD28 height to CGVD20 INSERT INTO "usage" VALUES('EPSG','18322','grid_transformation','EPSG','10114','EPSG','1061','EPSG','1133'); INSERT INTO "grid_transformation" VALUES('EPSG','10115','CGVD28 height to CGVD2013a(2010) height (1)','Equivalent to HT2_2010v70 hybrid geoid model minus CGG2013a geoid model (i.e. CT code 9986 minus CT 9247 = CT 9987 minus CT 10109). Specifies NAD83(CSRS)v6 as interpolation CRS, but NAD83(CSRS)v7 (code 8255) may equally be used.','EPSG','1112','Vertical Offset by Grid Interpolation (NRCan byn)','EPSG','5713','EPSG','9245',0.05,'EPSG','8732','Vertical offset file','HT2_2010v70_CGG2013a.byn',NULL,NULL,NULL,NULL,'EPSG','8252','NR-Can HT2 2010',0); INSERT INTO "usage" VALUES('EPSG','18323','grid_transformation','EPSG','10115','EPSG','1061','EPSG','1133'); +INSERT INTO "grid_transformation" VALUES('EPSG','10116','CGVD2013a(2010) height to CGVD2013a(2002) height (1)','Interpolation CRS = NAD83(CSRS) 2010, i.e. may be any one of NAD83(CSRS)v6, v7 or v8 (CRS codes 8252, 8255 or 10414). Equivalent to CGVD28 to CGVD2013a(2002) minus CGVD28 to CGVD2013a(2010) (i.e. CT code 10114 minus CT 10115).','EPSG','1113','Vertical Offset by velocity grid (NRCan byn)','EPSG','9245','EPSG','20034',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8252','EPSG-Can',0); +INSERT INTO "usage" VALUES('EPSG','18325','grid_transformation','EPSG','10116','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10117','CGVD2013a(2010) height to CGVD2013a(1997) height (1)','Interpolation CRS = NAD83(CSRS) 2010, i.e. may be any one of NAD83(CSRS)v6, v7 or v8 (CRS codes 8252, 8255 or 10414). Equivalent to CGVD28 to CGVD2013a(1997) minus CGVD28 to CGVD2013a(2010) (i.e. CT code 10113 minus CT 10115).','EPSG','1113','Vertical Offset by velocity grid (NRCan byn)','EPSG','9245','EPSG','20035',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8252','EPSG-Can',0); +INSERT INTO "usage" VALUES('EPSG','18352','grid_transformation','EPSG','10117','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10118','CGVD2013a(2002) height to CGVD2013a(1997) height (1)','Equivalent to CGVD28 to CGVD2013a(1997) minus CGVD28 to CGVD2013a(2002) (i.e. CT code 10113 minus CT 10114).','EPSG','1113','Vertical Offset by velocity grid (NRCan byn)','EPSG','20034','EPSG','20035',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8246','EPSG-Can',0); +INSERT INTO "usage" VALUES('EPSG','18328','grid_transformation','EPSG','10118','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10119','NAD83(CSRS)v4 to NAD83(CSRS)v2 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8244','EPSG','8235',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8246','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18201','grid_transformation','EPSG','10119','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10120','NAD83(CSRS)v4 to NAD83(CSRS)v3 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8244','EPSG','8239',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8246','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18210','grid_transformation','EPSG','10120','EPSG','1061','EPSG','1274'); +INSERT INTO "grid_transformation" VALUES('EPSG','10121','NAD83(CSRS)v6 to NAD83(CSRS)v2 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8251','EPSG','8235',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8252','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18203','grid_transformation','EPSG','10121','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10122','NAD83(CSRS)v6 to NAD83(CSRS)v3 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8251','EPSG','8239',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8252','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18351','grid_transformation','EPSG','10122','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10123','NAD83(CSRS)v6 to NAD83(CSRS)v4 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8251','EPSG','8244',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8252','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18259','grid_transformation','EPSG','10123','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10124','NAD83(CSRS)v7 to NAD83(CSRS)v2 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8254','EPSG','8235',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18206','grid_transformation','EPSG','10124','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10125','NAD83(CSRS)v7 to NAD83(CSRS)v3 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8254','EPSG','8239',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18207','grid_transformation','EPSG','10125','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10126','NAD83(CSRS)v7 to NAD83(CSRS)v4 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','8254','EPSG','8244',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','18350','grid_transformation','EPSG','10126','EPSG','1061','EPSG','1026'); INSERT INTO "grid_transformation" VALUES('EPSG','10128','NAD83(CSRS)v4 to NAD83(CSRS)v4 + CGVD2013a(2002) height (1)','Reversible alternative to NAD83(CSRS)v4 to CGVD2013a(2002) height (1) (code 10110).','EPSG','1090','Geog3D to Geog2D+GravityRelatedHeight (NRCan byn)','EPSG','8244','EPSG','20037',0.05,'EPSG','8666','Geoid (height correction) model file','CGG2013an83.byn',NULL,NULL,NULL,NULL,'EPSG','8246','NR-Can CGG2013a 2002',0); INSERT INTO "usage" VALUES('EPSG','18287','grid_transformation','EPSG','10128','EPSG','1061','EPSG','1270'); INSERT INTO "grid_transformation" VALUES('EPSG','10129','NAD83(CSRS)v3 to NAD83(CSRS)v3 + CGVD2013a(1997) height (1)','Reversible alternative to NAD83(CSRS)v3 to CGVD2013a(1997) height (1) (code 10111).','EPSG','1090','Geog3D to Geog2D+GravityRelatedHeight (NRCan byn)','EPSG','8239','EPSG','20038',0.05,'EPSG','8666','Geoid (height correction) model file','CGG2013an83.byn',NULL,NULL,NULL,NULL,'EPSG','8240','NR-Can CGG2013a 1997',0); @@ -1546,6 +1568,12 @@ INSERT INTO "grid_transformation" VALUES('EPSG','10417','NAD83(CSRS)v8 to CGVD20 INSERT INTO "usage" VALUES('EPSG','20252','grid_transformation','EPSG','10417','EPSG','1061','EPSG','1133'); INSERT INTO "grid_transformation" VALUES('EPSG','10418','NAD83(CSRS)v8 to CGVD28 height (1)','Valid at epoch 2010.0. Hybrid geoid model, grid derived at epoch 1997.0 through NAD83(CSRS)v3 (CT code 9983) and then modified to include correction for propagation of height between 1997.0 and 2010.0 derived from the Canada velocity grid v7.0.','EPSG','1060','Geographic3D to GravityRelatedHeight (NRCan byn)','EPSG','10413','EPSG','5713',0.05,'EPSG','8666','Geoid (height correction) model file','HT2_2010v70.byn',NULL,NULL,NULL,NULL,NULL,NULL,'NRC-Can CGG2000 2010',0); INSERT INTO "usage" VALUES('EPSG','20137','grid_transformation','EPSG','10418','EPSG','1289','EPSG','1133'); +INSERT INTO "grid_transformation" VALUES('EPSG','10421','NAD83(CSRS)v8 to NAD83(CSRS)v2 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','10413','EPSG','8235',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','10414','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','20188','grid_transformation','EPSG','10421','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10422','NAD83(CSRS)v8 to NAD83(CSRS)v3 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','10413','EPSG','8239',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','10414','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','20189','grid_transformation','EPSG','10422','EPSG','1061','EPSG','1026'); +INSERT INTO "grid_transformation" VALUES('EPSG','10423','NAD83(CSRS)v8 to NAD83(CSRS)v4 (1)','','EPSG','1114','Geographic3D Offset by velocity grid (NRCan byn)','EPSG','10413','EPSG','8244',0.05,'EPSG','1050','Point motion velocity grid file','NAD83v70VG.gvb',NULL,NULL,NULL,NULL,'EPSG','8255','EPSG-Can cvg70',0); +INSERT INTO "usage" VALUES('EPSG','20190','grid_transformation','EPSG','10423','EPSG','1061','EPSG','1026'); INSERT INTO "grid_transformation" VALUES('EPSG','10469','ETRS89 to COV23-IRF (1)','In conjunction with the COV23-TM map projection (code 10470) applied to COV23-IRF (code 10468), emulates the COV23 Snake projection. Applied to ETRS89 (as realized through the OSNet v2009) defines COV23-IRF hence is errorless. ','EPSG','9615','NTv2','EPSG','4258','EPSG','10468',0.0,'EPSG','8656','Latitude and longitude difference file','TN15-ETRS89-to-COV23-IRF.gsb',NULL,NULL,NULL,NULL,NULL,NULL,'CCC-Gbr COV23 OSNet2009',0); INSERT INTO "usage" VALUES('EPSG','20323','grid_transformation','EPSG','10469','EPSG','4743','EPSG','1141'); INSERT INTO "grid_transformation" VALUES('EPSG','15486','CH1903 to CH1903+ (1)','For improved accuracy (0.01m) use CHENyx06 interpolation programme FINELTRA. File CHENyx06 replaced by CHENyx06a; there is a small area at the border of the data where some more real data has been introduced. swisstopo consider the change insignificant.','EPSG','9615','NTv2','EPSG','4149','EPSG','4150',0.2,'EPSG','8656','Latitude and longitude difference file','CHENyx06a.gsb',NULL,NULL,NULL,NULL,NULL,NULL,'BfL-Che',0); diff --git a/data/sql/metadata.sql b/data/sql/metadata.sql index e26b4c380a..97c77a265f 100644 --- a/data/sql/metadata.sql +++ b/data/sql/metadata.sql @@ -7,7 +7,7 @@ -- DATABASE_LAYOUT_VERSION_MINOR constants in src/iso19111/factory.cpp must be -- updated as well. INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MAJOR', 1); -INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MINOR', 2); +INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MINOR', 3); INSERT INTO "metadata" VALUES('EPSG.VERSION', 'v10.094'); INSERT INTO "metadata" VALUES('EPSG.DATE', '2023-08-08'); diff --git a/data/sql/nrcan.sql b/data/sql/nrcan.sql new file mode 100644 index 0000000000..a500f2e4fb --- /dev/null +++ b/data/sql/nrcan.sql @@ -0,0 +1,105 @@ +--- This file has been generated by scripts/build_nrcan.py. DO NOT EDIT ! + +INSERT INTO "grid_transformation" VALUES('NRCAN','HT2_1997_NAD83CSRSV7','NAD83(CSRS)v7 to CGVD28 height',NULL,'EPSG','1060','Geographic3D to GravityRelatedHeight (NRCan byn)','EPSG','8254','EPSG','5713',0.05,'EPSG','8666','Geoid (height correction) model file','HT2_1997.byn',NULL,NULL,NULL,NULL,NULL,NULL,'NRC-Can CGG2000',0); +INSERT INTO "usage" VALUES('NRCAN','USAGE_HT2_1997_NAD83CSRSV7','grid_transformation','NRCAN','HT2_1997_NAD83CSRSV7','EPSG','1289','EPSG','1133'); +INSERT INTO "grid_transformation" VALUES('NRCAN','HT2_2002_NAD83CSRSV7','NAD83(CSRS)v7 to CGVD28 height',NULL,'EPSG','1060','Geographic3D to GravityRelatedHeight (NRCan byn)','EPSG','8254','EPSG','5713',0.05,'EPSG','8666','Geoid (height correction) model file','HT2_2002v70.byn',NULL,NULL,NULL,NULL,NULL,NULL,'NRC-Can CGG2000 2002',0); +INSERT INTO "usage" VALUES('NRCAN','USAGE_HT2_2002_NAD83CSRSV7','grid_transformation','NRCAN','HT2_2002_NAD83CSRSV7','EPSG','1289','EPSG','1133'); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM1_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 1", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 1", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -53, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 1 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland - onshore east of 54\u00b030''W.", "bbox": {"south_latitude": 46.56, "west_longitude": -54.5, "north_latitude": 49.89, "east_longitude": -52.54}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM2_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 2", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 2", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -56, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 2 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland and Labrador between 57\u00b030''W and 54\u00b030''W.", "bbox": {"south_latitude": 46.81, "west_longitude": -57.5, "north_latitude": 54.71, "east_longitude": -54.49}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM3_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 3", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 3", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -58.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 3 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland west of 57\u00b030''W.", "bbox": {"south_latitude": 47.5, "west_longitude": -59.48, "north_latitude": 50.54, "east_longitude": -57.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM4_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 4", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 4", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -61.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 4 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador between 63\u00b0W and 60\u00b0W.", "bbox": {"south_latitude": 52, "west_longitude": -63, "north_latitude": 58.92, "east_longitude": -60}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM5_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 5", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 5", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -64.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 5 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - 66\u00b0W to 63\u00b0W.", "bbox": {"south_latitude": 51.58, "west_longitude": -66, "north_latitude": 60.52, "east_longitude": -63}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM6_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 6", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 6", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -67.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 6 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - west of 66\u00b0W.", "bbox": {"south_latitude": 52.05, "west_longitude": -67.81, "north_latitude": 55.34, "east_longitude": -66}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM7_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 7", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -70.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 7 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Quebec - between 72\u00b0W and 69\u00b0W.", "bbox": {"south_latitude": 45.01, "west_longitude": -72, "north_latitude": 61.8, "east_longitude": -69}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM8_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 8", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -73.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 8 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - east of 75\u00b0W.", "bbox": {"south_latitude": 44.98, "west_longitude": -75, "north_latitude": 45.65, "east_longitude": -74.35}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM9_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 9", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -76.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 9 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 78\u00b0W and 75\u00b0W.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 46.25, "east_longitude": -75}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM10_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 10", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -79.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 10 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 81\u00b0W and 78\u00b0W: south of 46\u00b0N in area to west of 80\u00b015''W, south of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W, entire province between 79\u00b030''W and 78\u00b0W.", "bbox": {"south_latitude": 42.26, "west_longitude": -81, "north_latitude": 47.33, "east_longitude": -77.99}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM11_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 11", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -82.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 11 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - south of 46\u00b0N and west of 81\u00b0W.", "bbox": {"south_latitude": 41.67, "west_longitude": -83.6, "north_latitude": 46, "east_longitude": -81}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM12_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 12", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 12 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 82\u00b030''W and 79\u00b030''W: north of 46\u00b0N in area between 82\u00b030''W and 80\u00b015''W, north of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W.", "bbox": {"south_latitude": 46, "west_longitude": -82.5, "north_latitude": 55.21, "east_longitude": -79.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM13_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 13", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -84, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 13 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 85\u00b030''W and 82\u00b030''W and north of 46\u00b0N.", "bbox": {"south_latitude": 46, "west_longitude": -85.5, "north_latitude": 55.59, "east_longitude": -82.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM14_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 14", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 14 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 88\u00b030''W and 85\u00b030''W.", "bbox": {"south_latitude": 47.17, "west_longitude": -88.5, "north_latitude": 56.7, "east_longitude": -85.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM15_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 15", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -90, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 15 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 91\u00b030''W and 88\u00b030''W.", "bbox": {"south_latitude": 47.97, "west_longitude": -91.5, "north_latitude": 56.9, "east_longitude": -88.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM16_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 16", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 16 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 94\u00b030''W and 91\u00b030''W.", "bbox": {"south_latitude": 48.06, "west_longitude": -94.5, "north_latitude": 55.2, "east_longitude": -91.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM17_HT2_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 17", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -96, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_1997", "id": {"authority": "NRCAN", "code": "HT2_1997_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 17 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - west of 94\u00b030''W.", "bbox": {"south_latitude": 48.69, "west_longitude": -95.16, "north_latitude": 53.24, "east_longitude": -94.5}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM1_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 1", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 1", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -53, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 1 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland - onshore east of 54\u00b030''W.", "bbox": {"south_latitude": 46.56, "west_longitude": -54.5, "north_latitude": 49.89, "east_longitude": -52.54}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM2_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 2", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 2", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -56, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 2 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland and Labrador between 57\u00b030''W and 54\u00b030''W.", "bbox": {"south_latitude": 46.81, "west_longitude": -57.5, "north_latitude": 54.71, "east_longitude": -54.49}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM3_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 3", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 3", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -58.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 3 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland west of 57\u00b030''W.", "bbox": {"south_latitude": 47.5, "west_longitude": -59.48, "north_latitude": 50.54, "east_longitude": -57.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM4_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 4", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 4", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -61.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 4 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador between 63\u00b0W and 60\u00b0W.", "bbox": {"south_latitude": 52, "west_longitude": -63, "north_latitude": 58.92, "east_longitude": -60}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM5_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 5", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 5", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -64.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 5 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - 66\u00b0W to 63\u00b0W.", "bbox": {"south_latitude": 51.58, "west_longitude": -66, "north_latitude": 60.52, "east_longitude": -63}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM6_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 6", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 6", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -67.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 6 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - west of 66\u00b0W.", "bbox": {"south_latitude": 52.05, "west_longitude": -67.81, "north_latitude": 55.34, "east_longitude": -66}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM7_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 7", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -70.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 7 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Quebec - between 72\u00b0W and 69\u00b0W.", "bbox": {"south_latitude": 45.01, "west_longitude": -72, "north_latitude": 61.8, "east_longitude": -69}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM8_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 8", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -73.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 8 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - east of 75\u00b0W.", "bbox": {"south_latitude": 44.98, "west_longitude": -75, "north_latitude": 45.65, "east_longitude": -74.35}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM9_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 9", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -76.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 9 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 78\u00b0W and 75\u00b0W.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 46.25, "east_longitude": -75}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM10_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 10", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -79.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 10 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 81\u00b0W and 78\u00b0W: south of 46\u00b0N in area to west of 80\u00b015''W, south of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W, entire province between 79\u00b030''W and 78\u00b0W.", "bbox": {"south_latitude": 42.26, "west_longitude": -81, "north_latitude": 47.33, "east_longitude": -77.99}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM11_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 11", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -82.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 11 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - south of 46\u00b0N and west of 81\u00b0W.", "bbox": {"south_latitude": 41.67, "west_longitude": -83.6, "north_latitude": 46, "east_longitude": -81}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM12_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 12", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 12 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 82\u00b030''W and 79\u00b030''W: north of 46\u00b0N in area between 82\u00b030''W and 80\u00b015''W, north of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W.", "bbox": {"south_latitude": 46, "west_longitude": -82.5, "north_latitude": 55.21, "east_longitude": -79.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM13_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 13", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -84, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 13 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 85\u00b030''W and 82\u00b030''W and north of 46\u00b0N.", "bbox": {"south_latitude": 46, "west_longitude": -85.5, "north_latitude": 55.59, "east_longitude": -82.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM14_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 14", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 14 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 88\u00b030''W and 85\u00b030''W.", "bbox": {"south_latitude": 47.17, "west_longitude": -88.5, "north_latitude": 56.7, "east_longitude": -85.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM15_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 15", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -90, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 15 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 91\u00b030''W and 88\u00b030''W.", "bbox": {"south_latitude": 47.97, "west_longitude": -91.5, "north_latitude": 56.9, "east_longitude": -88.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM16_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 16", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 16 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 94\u00b030''W and 91\u00b030''W.", "bbox": {"south_latitude": 48.06, "west_longitude": -94.5, "north_latitude": 55.2, "east_longitude": -91.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM17_HT2_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 17", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -96, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2002", "id": {"authority": "NRCAN", "code": "HT2_2002_NAD83CSRSV7"}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 17 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - west of 94\u00b030''W.", "bbox": {"south_latitude": 48.69, "west_longitude": -95.16, "north_latitude": 53.24, "east_longitude": -94.5}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM1_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 1", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 1", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -53, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 1 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland - onshore east of 54\u00b030''W.", "bbox": {"south_latitude": 46.56, "west_longitude": -54.5, "north_latitude": 49.89, "east_longitude": -52.54}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM2_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 2", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 2", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -56, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 2 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland and Labrador between 57\u00b030''W and 54\u00b030''W.", "bbox": {"south_latitude": 46.81, "west_longitude": -57.5, "north_latitude": 54.71, "east_longitude": -54.49}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM3_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 3", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 3", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -58.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 3 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Newfoundland west of 57\u00b030''W.", "bbox": {"south_latitude": 47.5, "west_longitude": -59.48, "north_latitude": 50.54, "east_longitude": -57.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM4_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 4", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 4", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -61.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 4 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador between 63\u00b0W and 60\u00b0W.", "bbox": {"south_latitude": 52, "west_longitude": -63, "north_latitude": 58.92, "east_longitude": -60}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM5_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 5", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 5", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -64.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 5 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - 66\u00b0W to 63\u00b0W.", "bbox": {"south_latitude": 51.58, "west_longitude": -66, "north_latitude": 60.52, "east_longitude": -63}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM6_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 6", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 6", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -67.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 6 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Labrador - west of 66\u00b0W.", "bbox": {"south_latitude": 52.05, "west_longitude": -67.81, "north_latitude": 55.34, "east_longitude": -66}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM7_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 7", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -70.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 7 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Quebec - between 72\u00b0W and 69\u00b0W.", "bbox": {"south_latitude": 45.01, "west_longitude": -72, "north_latitude": 61.8, "east_longitude": -69}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM8_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 8", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -73.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 8 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - east of 75\u00b0W.", "bbox": {"south_latitude": 44.98, "west_longitude": -75, "north_latitude": 45.65, "east_longitude": -74.35}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM9_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 9", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -76.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 9 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 78\u00b0W and 75\u00b0W.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 46.25, "east_longitude": -75}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM10_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 10", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -79.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 10 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 81\u00b0W and 78\u00b0W: south of 46\u00b0N in area to west of 80\u00b015''W, south of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W, entire province between 79\u00b030''W and 78\u00b0W.", "bbox": {"south_latitude": 42.26, "west_longitude": -81, "north_latitude": 47.33, "east_longitude": -77.99}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM11_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 11", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -82.5, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 11 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - south of 46\u00b0N and west of 81\u00b0W.", "bbox": {"south_latitude": 41.67, "west_longitude": -83.6, "north_latitude": 46, "east_longitude": -81}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM12_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 12", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 12 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 82\u00b030''W and 79\u00b030''W: north of 46\u00b0N in area between 82\u00b030''W and 80\u00b015''W, north of 47\u00b0N in area between 80\u00b015''W and 79\u00b030''W.", "bbox": {"south_latitude": 46, "west_longitude": -82.5, "north_latitude": 55.21, "east_longitude": -79.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM13_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 13", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -84, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 13 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 85\u00b030''W and 82\u00b030''W and north of 46\u00b0N.", "bbox": {"south_latitude": 46, "west_longitude": -85.5, "north_latitude": 55.59, "east_longitude": -82.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM14_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 14", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 14 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 88\u00b030''W and 85\u00b030''W.", "bbox": {"south_latitude": 47.17, "west_longitude": -88.5, "north_latitude": 56.7, "east_longitude": -85.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM15_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 15", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -90, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 15 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 91\u00b030''W and 88\u00b030''W.", "bbox": {"south_latitude": 47.97, "west_longitude": -91.5, "north_latitude": 56.9, "east_longitude": -88.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM16_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 16", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 16 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - between 94\u00b030''W and 91\u00b030''W.", "bbox": {"south_latitude": 48.06, "west_longitude": -94.5, "north_latitude": 55.2, "east_longitude": -91.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM17_HT2_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / MTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "MTM zone 17", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -96, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9999, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 304800, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD28 height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 1928"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "geoid_model": {"name": "HT2_2010", "id": {"authority": "EPSG", "code": 9987}}, "id": {"authority": "EPSG", "code": 5713}}], "name": "NAD83(CSRS)v7 / MTM zone 17 + CGVD28 height", "scope": "Engineering survey, topographic mapping.", "area": "Canada - Ontario - west of 94\u00b030''W.", "bbox": {"south_latitude": 48.69, "west_longitude": -95.16, "north_latitude": 53.24, "east_longitude": -94.5}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM7_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 7N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -141, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 7 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada west of 138\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Yukon.", "bbox": {"south_latitude": 52.05, "west_longitude": -141.01, "north_latitude": 72.53, "east_longitude": -138}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM8_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 8N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -135, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 8 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM9_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 9N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -129, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 9 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM10_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 10N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -123, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 10 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 126\u00b0W and 120\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.13, "west_longitude": -126, "north_latitude": 81.8, "east_longitude": -120}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM11_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 11N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -117, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 11 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 120\u00b0W and 114\u00b0W onshore and offshore - Alberta, British Columbia, Northwest Territories, Nunavut.", "bbox": {"south_latitude": 48.99, "west_longitude": -120, "north_latitude": 83.5, "east_longitude": -114}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM12_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 12N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -111, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 12 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 114\u00b0W and 108\u00b0W onshore and offshore - Alberta, Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -114, "north_latitude": 84, "east_longitude": -108}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM13_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 13N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -105, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 13 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 108\u00b0W and 102\u00b0W onshore and offshore - Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -108, "north_latitude": 84, "east_longitude": -102}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM14_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 14N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -99, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 14 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 102\u00b0W and 96\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -102, "north_latitude": 84, "east_longitude": -96}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM15_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 15N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 15 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 96\u00b0W and 90\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 48.03, "west_longitude": -96, "north_latitude": 84, "east_longitude": -90}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM16_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 16N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 16 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 90\u00b0W and 84\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 46.11, "west_longitude": -90, "north_latitude": 84, "east_longitude": -84}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM17_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 17N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 17 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 84\u00b0W and 78\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 41.67, "west_longitude": -84, "north_latitude": 84, "east_longitude": -78}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM18_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 18", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 18N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -75, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 18 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 78\u00b0W and 72\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 84, "east_longitude": -72}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM19_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 19", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 19N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -69, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 19 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 72\u00b0W and 66\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Quebec.", "bbox": {"south_latitude": 40.8, "west_longitude": -72, "north_latitude": 84, "east_longitude": -66}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM20_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 20", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 20N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -63, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 20 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 66\u00b0W and 60\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Prince Edward Island, Quebec.", "bbox": {"south_latitude": 40.04, "west_longitude": -66, "north_latitude": 84, "east_longitude": -60}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM21_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 21", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 21N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -57, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 21 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 60\u00b0W and 54\u00b0W - Newfoundland and Labrador; Nunavut; Quebec.", "bbox": {"south_latitude": 38.56, "west_longitude": -60, "north_latitude": 84, "east_longitude": -54}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM22_CGVD2013_1997', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 22", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 22N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -51, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(1997) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20035}}], "name": "NAD83(CSRS)v7 / UTM zone 22 + CGVD2013a(1997) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 54\u00b0W and 48\u00b0W onshore and offshore - Newfoundland and Labrador.", "bbox": {"south_latitude": 39.5, "west_longitude": -54, "north_latitude": 57.65, "east_longitude": -47.99}}', 1997.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM7_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 7N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -141, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 7 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada west of 138\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Yukon.", "bbox": {"south_latitude": 52.05, "west_longitude": -141.01, "north_latitude": 72.53, "east_longitude": -138}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM8_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 8N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -135, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 8 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM9_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 9N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -129, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 9 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM10_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 10N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -123, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 10 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 126\u00b0W and 120\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.13, "west_longitude": -126, "north_latitude": 81.8, "east_longitude": -120}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM11_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 11N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -117, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 11 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 120\u00b0W and 114\u00b0W onshore and offshore - Alberta, British Columbia, Northwest Territories, Nunavut.", "bbox": {"south_latitude": 48.99, "west_longitude": -120, "north_latitude": 83.5, "east_longitude": -114}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM12_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 12N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -111, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 12 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 114\u00b0W and 108\u00b0W onshore and offshore - Alberta, Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -114, "north_latitude": 84, "east_longitude": -108}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM13_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 13N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -105, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 13 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 108\u00b0W and 102\u00b0W onshore and offshore - Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -108, "north_latitude": 84, "east_longitude": -102}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM14_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 14N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -99, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 14 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 102\u00b0W and 96\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -102, "north_latitude": 84, "east_longitude": -96}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM15_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 15N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 15 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 96\u00b0W and 90\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 48.03, "west_longitude": -96, "north_latitude": 84, "east_longitude": -90}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM16_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 16N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 16 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 90\u00b0W and 84\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 46.11, "west_longitude": -90, "north_latitude": 84, "east_longitude": -84}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM17_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 17N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 17 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 84\u00b0W and 78\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 41.67, "west_longitude": -84, "north_latitude": 84, "east_longitude": -78}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM18_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 18", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 18N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -75, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 18 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 78\u00b0W and 72\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 84, "east_longitude": -72}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM19_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 19", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 19N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -69, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 19 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 72\u00b0W and 66\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Quebec.", "bbox": {"south_latitude": 40.8, "west_longitude": -72, "north_latitude": 84, "east_longitude": -66}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM20_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 20", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 20N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -63, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 20 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 66\u00b0W and 60\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Prince Edward Island, Quebec.", "bbox": {"south_latitude": 40.04, "west_longitude": -66, "north_latitude": 84, "east_longitude": -60}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM21_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 21", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 21N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -57, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 21 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 60\u00b0W and 54\u00b0W - Newfoundland and Labrador; Nunavut; Quebec.", "bbox": {"south_latitude": 38.56, "west_longitude": -60, "north_latitude": 84, "east_longitude": -54}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM22_CGVD2013_2002', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 22", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 22N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -51, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2002) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 20034}}], "name": "NAD83(CSRS)v7 / UTM zone 22 + CGVD2013a(2002) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 54\u00b0W and 48\u00b0W onshore and offshore - Newfoundland and Labrador.", "bbox": {"south_latitude": 39.5, "west_longitude": -54, "north_latitude": 57.65, "east_longitude": -47.99}}', 2002.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM7_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 7", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 7N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -141, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 7 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada west of 138\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Yukon.", "bbox": {"south_latitude": 52.05, "west_longitude": -141.01, "north_latitude": 72.53, "east_longitude": -138}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM8_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 8", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 8N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -135, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 8 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM9_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 9", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 9N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -129, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 9 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 138\u00b0W and 132\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.06, "west_longitude": -138, "north_latitude": 79.42, "east_longitude": -132}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM10_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 10", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 10N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -123, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 10 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 126\u00b0W and 120\u00b0W, onshore and offshore south of 84\u00b0N - British Columbia, Northwest Territories, Yukon.", "bbox": {"south_latitude": 48.13, "west_longitude": -126, "north_latitude": 81.8, "east_longitude": -120}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM11_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 11", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 11N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -117, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 11 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 120\u00b0W and 114\u00b0W onshore and offshore - Alberta, British Columbia, Northwest Territories, Nunavut.", "bbox": {"south_latitude": 48.99, "west_longitude": -120, "north_latitude": 83.5, "east_longitude": -114}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM12_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 12", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 12N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -111, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 12 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 114\u00b0W and 108\u00b0W onshore and offshore - Alberta, Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -114, "north_latitude": 84, "east_longitude": -108}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM13_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 13", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 13N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -105, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 13 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 108\u00b0W and 102\u00b0W onshore and offshore - Northwest Territories, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -108, "north_latitude": 84, "east_longitude": -102}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM14_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 14", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 14N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -99, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 14 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 102\u00b0W and 96\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Saskatchewan.", "bbox": {"south_latitude": 48.99, "west_longitude": -102, "north_latitude": 84, "east_longitude": -96}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM15_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 15", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 15N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -93, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 15 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 96\u00b0W and 90\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 48.03, "west_longitude": -96, "north_latitude": 84, "east_longitude": -90}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM16_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 16", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 16N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -87, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 16 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 90\u00b0W and 84\u00b0W, onshore and offshore south of 84\u00b0N - Manitoba, Nunavut, Ontario.", "bbox": {"south_latitude": 46.11, "west_longitude": -90, "north_latitude": 84, "east_longitude": -84}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM17_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 17", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 17N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -81, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 17 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 84\u00b0W and 78\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 41.67, "west_longitude": -84, "north_latitude": 84, "east_longitude": -78}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM18_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 18", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 18N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -75, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 18 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 78\u00b0W and 72\u00b0W, onshore and offshore south of 84\u00b0N - Nunavut, Ontario and Quebec.", "bbox": {"south_latitude": 43.63, "west_longitude": -78, "north_latitude": 84, "east_longitude": -72}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM19_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 19", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 19N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -69, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 19 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 72\u00b0W and 66\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Quebec.", "bbox": {"south_latitude": 40.8, "west_longitude": -72, "north_latitude": 84, "east_longitude": -66}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM20_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 20", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 20N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -63, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 20 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 66\u00b0W and 60\u00b0W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Prince Edward Island, Quebec.", "bbox": {"south_latitude": 40.04, "west_longitude": -66, "north_latitude": 84, "east_longitude": -60}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM21_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 21", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 21N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -57, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 21 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 60\u00b0W and 54\u00b0W - Newfoundland and Labrador; Nunavut; Quebec.", "bbox": {"south_latitude": 38.56, "west_longitude": -60, "north_latitude": 84, "east_longitude": -54}}', 2010.0, 0); +INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM22_CGVD2013_2010', NULL, NULL, NULL, '{"type": "CompoundCRS", "components": [{"type": "ProjectedCRS", "name": "NAD83(CSRS)v7 / UTM zone 22", "base_crs": {"name": "NAD83(CSRS)v7", "datum": {"type": "GeodeticReferenceFrame", "name": "North American Datum of 1983 (CSRS) version 7", "ellipsoid": {"name": "GRS 1980", "semi_major_axis": 6378137, "inverse_flattening": 298.257222101}}, "coordinate_system": {"subtype": "ellipsoidal", "axis": [{"name": "Geodetic latitude", "abbreviation": "Lat", "direction": "north", "unit": "degree"}, {"name": "Geodetic longitude", "abbreviation": "Lon", "direction": "east", "unit": "degree"}]}}, "conversion": {"name": "UTM zone 22N", "method": {"name": "Transverse Mercator", "id": {"authority": "EPSG", "code": 9807}}, "parameters": [{"name": "Latitude of natural origin", "value": 0, "unit": "degree", "id": {"authority": "EPSG", "code": 8801}}, {"name": "Longitude of natural origin", "value": -51, "unit": "degree", "id": {"authority": "EPSG", "code": 8802}}, {"name": "Scale factor at natural origin", "value": 0.9996, "unit": "unity", "id": {"authority": "EPSG", "code": 8805}}, {"name": "False easting", "value": 500000, "unit": "metre", "id": {"authority": "EPSG", "code": 8806}}, {"name": "False northing", "value": 0, "unit": "metre", "id": {"authority": "EPSG", "code": 8807}}]}, "coordinate_system": {"subtype": "Cartesian", "axis": [{"name": "Easting", "abbreviation": "E(X)", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N(Y)", "direction": "north", "unit": "metre"}]}}, {"type": "VerticalCRS", "name": "CGVD2013a(2010) height", "datum": {"type": "VerticalReferenceFrame", "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010"}, "coordinate_system": {"subtype": "vertical", "axis": [{"name": "Gravity-related height", "abbreviation": "H", "direction": "up", "unit": "metre"}]}, "id": {"authority": "EPSG", "code": 9245}}], "name": "NAD83(CSRS)v7 / UTM zone 22 + CGVD2013a(2010) height", "scope": "Engineering survey, topographic mapping.", "area": "Canada between 54\u00b0W and 48\u00b0W onshore and offshore - Newfoundland and Labrador.", "bbox": {"south_latitude": 39.5, "west_longitude": -54, "north_latitude": 57.65, "east_longitude": -47.99}}', 2010.0, 0); diff --git a/data/sql/proj_db_table_defs.sql b/data/sql/proj_db_table_defs.sql index b377199edd..dd24fd7e9a 100644 --- a/data/sql/proj_db_table_defs.sql +++ b/data/sql/proj_db_table_defs.sql @@ -777,6 +777,34 @@ FOR EACH ROW BEGIN WHERE EXISTS (SELECT 1 FROM vertical_crs WHERE vertical_crs.auth_name = NEW.vertical_crs_auth_name AND vertical_crs.code = NEW.vertical_crs_code AND vertical_crs.deprecated != 0) AND NEW.deprecated = 0; END; +CREATE TABLE coordinate_metadata( + auth_name TEXT NOT NULL CHECK (length(auth_name) >= 1), + code INTEGER_OR_TEXT NOT NULL CHECK (length(code) >= 1), + description TEXT, + crs_auth_name TEXT, + crs_code INTEGER_OR_TEXT, + crs_text_definition TEXT, -- WKT string or PROJJSON string. Mutually exclusive with (crs_auth_name, crs_code) + coordinate_epoch DOUBLE, -- may be NULL + deprecated BOOLEAN NOT NULL CHECK (deprecated IN (0, 1)), + CONSTRAINT pk_coordinate_metadata PRIMARY KEY (auth_name, code) +) WITHOUT ROWID; + +CREATE TRIGGER coordinate_metadata_insert_trigger +BEFORE INSERT ON coordinate_metadata +FOR EACH ROW BEGIN + SELECT RAISE(ABORT, 'insert on coordinate_metadata violates constraint: (crs_auth_name, crs_code) must already exist in crs_view') + WHERE NOT EXISTS ( + SELECT 1 FROM crs_view WHERE + NEW.crs_auth_name IS NOT NULL AND + crs_view.auth_name = NEW.crs_auth_name AND + crs_view.code = NEW.crs_code + UNION ALL SELECT 1 WHERE NEW.crs_auth_name IS NULL); + SELECT RAISE(ABORT, 'insert on coordinate_metadata violates constraint: (crs_auth_name, crs_code) and crs_text_definition are mutually exclusive') + WHERE NEW.crs_auth_name IS NOT NULL AND NEW.crs_text_definition IS NOT NULL; + SELECT RAISE(ABORT, 'insert on coordinate_metadata violates constraint: one of (crs_auth_name, crs_code) or crs_text_definition must be set') + WHERE NEW.crs_auth_name IS NULL AND NEW.crs_text_definition IS NULL; +END; + CREATE TABLE coordinate_operation_method( auth_name TEXT NOT NULL CHECK (length(auth_name) >= 1), code INTEGER_OR_TEXT NOT NULL CHECK (length(code) >= 1), diff --git a/data/sql_filelist.cmake b/data/sql_filelist.cmake index 8a1c066d96..b88451ecdb 100644 --- a/data/sql_filelist.cmake +++ b/data/sql_filelist.cmake @@ -35,6 +35,7 @@ set(SQL_FILES "${SQL_DIR}/ignf.sql" "${SQL_DIR}/nkg.sql" "${SQL_DIR}/iau.sql" + "${SQL_DIR}/nrcan.sql" "${SQL_DIR}/grid_alternatives.sql" "${SQL_DIR}/grid_alternatives_generated_noaa.sql" "${SQL_DIR}/nadcon5_concatenated_operations.sql" diff --git a/docs/source/apps/cs2cs.rst b/docs/source/apps/cs2cs.rst index ece14b4860..4e6221dcbb 100644 --- a/docs/source/apps/cs2cs.rst +++ b/docs/source/apps/cs2cs.rst @@ -15,6 +15,7 @@ Synopsis | [[--area ] | [--bbox ]] | [--authority ] [--3d] | [--accuracy ] [--only-best[=yes|=no]] [--no-ballpark] + | [--s_epoch {epoch}] [--t_epoch {epoch}] | ([*+opt[=arg]* ...] [+to *+opt[=arg]* ...] | {source_crs} {target_crs}) | file ... @@ -224,6 +225,20 @@ The following control parameters can appear in any order: transformations. Starting with PROJ 9.1, both CRS need to be 3D for vertical transformation to possibly happen. +.. option:: --s_epoch + + .. versionadded:: 9.4 + + Epoch of coordinates in the source CRS, as decimal year. + Only applies to a dynamic CRS. + +.. option:: --t_epoch + + .. versionadded:: 9.4 + + Epoch of coordinates in the target CRS, as decimal year. + Only applies to a dynamic CRS. + .. only:: man The *+opt* run-line arguments are associated with cartographic diff --git a/docs/source/apps/projinfo.rst b/docs/source/apps/projinfo.rst index eee0985fbd..eb64774da7 100644 --- a/docs/source/apps/projinfo.rst +++ b/docs/source/apps/projinfo.rst @@ -34,7 +34,8 @@ Synopsis | --searchpaths | --remote-data | | --list-crs [list-crs-filter] | | --dump-db-structure [{object_definition} | {object_reference}] | - | {object_definition} | {object_reference} | (-s {srs_def} -t {srs_def}) + | {object_definition} | {object_reference} | + | [--s_epoch {epoch}] -t {srs_def} [--t_epoch {epoch}]) | where {object_definition} or {srs_def} is one of the possibilities accepted @@ -54,6 +55,8 @@ Synopsis e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)": "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031" (*added in 6.2*) + - Extension of OGC URN for CoordinateMetadata. + e.g. "urn:ogc:def:CoordinateMetadata:NRCAN::NAD83_CSRS_1997_MTM11_HT2_1997" - a OGC URN combining references for concatenated operations (e.g. "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618") - a PROJJSON string. The jsonschema is at https://proj.org/schemas/v0.4/projjson.schema.json (*added in 6.2*) @@ -356,6 +359,20 @@ The following control parameters can appear in any order: Display information regarding if :ref:`network` is enabled, and the related URL. +.. option:: --s_epoch + + .. versionadded:: 9.4 + + Epoch of coordinates in the source CRS, as decimal year. + Only applies to a dynamic CRS. + +.. option:: --t_epoch + + .. versionadded:: 9.4 + + Epoch of coordinates in the target CRS, as decimal year. + Only applies to a dynamic CRS. + Examples ******** diff --git a/docs/source/development/reference/functions.rst b/docs/source/development/reference/functions.rst index 533a9942b0..8adb5c0279 100644 --- a/docs/source/development/reference/functions.rst +++ b/docs/source/development/reference/functions.rst @@ -139,8 +139,13 @@ paragraph for more details. - more generally any string accepted by :c:func:`proj_create` representing a CRS - Starting with PROJ 9.2, source_crs or target_crs can be a CoordinateMetadata - with an associated coordinate epoch (but only one of them, not both). + Starting with PROJ 9.2, source_crs (exclusively) or target_crs can be a CoordinateMetadata + with an associated coordinate epoch. + + Starting with PROJ 9.4, both source_crs and target_crs can be a CoordinateMetadata + with an associated coordinate epoch, to perform changes of coordinate epochs. + Note however than this is in practice limited to use of velocity grids inside + the same dynamic CRS. An "area of use" can be specified in area. When it is supplied, the more accurate transformation between two given systems can be chosen. @@ -185,8 +190,13 @@ paragraph for more details. This is the same as :c:func:`proj_create_crs_to_crs` except that the source and target CRS are passed as PJ* objects which must be of the CRS variety. - Starting with PROJ 9.2, source_crs or target_crs can be a CoordinateMetadata - with an associated coordinate epoch (but only one of them, not both). + Starting with PROJ 9.2, source_crs (exclusively) or target_crs can be a CoordinateMetadata + with an associated coordinate epoch. + + Starting with PROJ 9.4, both source_crs and target_crs can be a CoordinateMetadata + with an associated coordinate epoch, to perform changes of coordinate epochs. + Note however than this is in practice limited to use of velocity grids inside + the same dynamic CRS. :param `options`: a list of NUL terminated options, or NULL. diff --git a/include/proj/coordinateoperation.hpp b/include/proj/coordinateoperation.hpp index 158df31d20..dc7b43a862 100644 --- a/include/proj/coordinateoperation.hpp +++ b/include/proj/coordinateoperation.hpp @@ -389,6 +389,7 @@ class PROJ_GCC_DLL GeneralParameterValue : public util::BaseObject, friend class Conversion; friend class SingleOperation; + friend class PointMotionOperation; PROJ_INTERNAL virtual void _exportToWKT(io::WKTFormatter *formatter, const MethodMapping *mapping) const = 0; // throw(io::FormattingException) @@ -1765,8 +1766,61 @@ class PROJ_GCC_DLL PointMotionOperation : public SingleOperation { PROJ_DLL ~PointMotionOperation() override; //! @endcond + PROJ_DLL const crs::CRSNNPtr &sourceCRS() PROJ_PURE_DECL; + + PROJ_DLL CoordinateOperationNNPtr inverse() const override; + + PROJ_DLL static PointMotionOperationNNPtr + create(const util::PropertyMap &properties, const crs::CRSNNPtr &crsIn, + const OperationMethodNNPtr &methodIn, + const std::vector &values, + const std::vector + &accuracies); // throw InvalidOperation + + PROJ_DLL static PointMotionOperationNNPtr + create(const util::PropertyMap &propertiesOperation, + const crs::CRSNNPtr &crsIn, + const util::PropertyMap &propertiesOperationMethod, + const std::vector ¶meters, + const std::vector &values, + const std::vector + &accuracies); // throw InvalidOperation + + PROJ_DLL PointMotionOperationNNPtr substitutePROJAlternativeGridNames( + io::DatabaseContextNNPtr databaseContext) const; + + PROJ_PRIVATE : + //! @cond Doxygen_Suppress + PROJ_INTERNAL PointMotionOperationNNPtr + shallowClone() const; + + PROJ_INTERNAL PointMotionOperationNNPtr + cloneWithEpochs(const common::DataEpoch &sourceEpoch, + const common::DataEpoch &targetEpoch) const; + + PROJ_INTERNAL void _exportToPROJString(io::PROJStringFormatter *formatter) + const override; // throw(FormattingException) + + PROJ_INTERNAL void _exportToWKT(io::WKTFormatter *formatter) + const override; // throw(io::FormattingException) + + PROJ_INTERNAL void _exportToJSON(io::JSONFormatter *formatter) + const override; // throw(FormattingException) + + //! @endcond + + protected: + PROJ_INTERNAL PointMotionOperation( + const crs::CRSNNPtr &crsIn, const OperationMethodNNPtr &methodIn, + const std::vector &values, + const std::vector &accuracies); + PROJ_INTERNAL PointMotionOperation(const PointMotionOperation &other); + INLINED_MAKE_SHARED + + PROJ_INTERNAL CoordinateOperationNNPtr _shallowClone() const override; + private: - PointMotionOperation(const PointMotionOperation &) = delete; + PointMotionOperation &operator=(const PointMotionOperation &) = delete; }; // --------------------------------------------------------------------------- @@ -2049,6 +2103,11 @@ class PROJ_GCC_DLL CoordinateOperationFactory { const coordinates::CoordinateMetadataNNPtr &targetCoordinateMetadata, const CoordinateOperationContextNNPtr &context) const; + PROJ_DLL std::vector createOperations( + const coordinates::CoordinateMetadataNNPtr &sourceCoordinateMetadata, + const coordinates::CoordinateMetadataNNPtr &targetCoordinateMetadata, + const CoordinateOperationContextNNPtr &context) const; + PROJ_DLL static CoordinateOperationFactoryNNPtr create(); protected: diff --git a/include/proj/coordinates.hpp b/include/proj/coordinates.hpp index 87ca479e3a..d5350c06c5 100644 --- a/include/proj/coordinates.hpp +++ b/include/proj/coordinates.hpp @@ -74,6 +74,13 @@ class PROJ_GCC_DLL CoordinateMetadata : public util::BaseObject, PROJ_DLL static CoordinateMetadataNNPtr create(const crs::CRSNNPtr &crsIn); PROJ_DLL static CoordinateMetadataNNPtr create(const crs::CRSNNPtr &crsIn, double coordinateEpochAsDecimalYear); + PROJ_DLL static CoordinateMetadataNNPtr + create(const crs::CRSNNPtr &crsIn, double coordinateEpochAsDecimalYear, + const io::DatabaseContextPtr &dbContext); + + PROJ_DLL CoordinateMetadataNNPtr + promoteTo3D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const; PROJ_PRIVATE : //! @cond Doxygen_Suppress diff --git a/include/proj/internal/internal.hpp b/include/proj/internal/internal.hpp index 6a7b912573..6d6bedff0f 100644 --- a/include/proj/internal/internal.hpp +++ b/include/proj/internal/internal.hpp @@ -181,6 +181,9 @@ std::string concat(const std::string &, const std::string &, const char *) = delete; std::string concat(const std::string &, const std::string &, const std::string &) = delete; + +double getRoundedEpochInDecimalYear(double year); + } // namespace internal NS_PROJ_END diff --git a/include/proj/internal/io_internal.hpp b/include/proj/internal/io_internal.hpp index 4973f6870f..96d939fe84 100644 --- a/include/proj/internal/io_internal.hpp +++ b/include/proj/internal/io_internal.hpp @@ -137,12 +137,13 @@ class WKTConstants { static const std::string BASEPARAMCRS; static const std::string BASETIMECRS; static const std::string VERSION; - static const std::string GEOIDMODEL; // WKT2-2019 - static const std::string COORDINATEMETADATA; // WKT2-2019 - static const std::string EPOCH; // WKT2-2019 - static const std::string AXISMINVALUE; // WKT2-2019 - static const std::string AXISMAXVALUE; // WKT2-2019 - static const std::string RANGEMEANING; // WKT2-2019 + static const std::string GEOIDMODEL; // WKT2-2019 + static const std::string COORDINATEMETADATA; // WKT2-2019 + static const std::string EPOCH; // WKT2-2019 + static const std::string AXISMINVALUE; // WKT2-2019 + static const std::string AXISMAXVALUE; // WKT2-2019 + static const std::string RANGEMEANING; // WKT2-2019 + static const std::string POINTMOTIONOPERATION; // 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 26ac8612f4..1ca6501fe7 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -118,6 +118,14 @@ using CompoundCRSPtr = std::shared_ptr; using CompoundCRSNNPtr = util::nn; } // namespace crs +namespace coordinates { +class CoordinateMetadata; +/** Shared pointer of CoordinateMetadata */ +using CoordinateMetadataPtr = std::shared_ptr; +/** Non-null shared pointer of CoordinateMetadata */ +using CoordinateMetadataNNPtr = util::nn; +} // namespace coordinates + namespace operation { class Conversion; using ConversionPtr = std::shared_ptr; @@ -126,6 +134,10 @@ using ConversionNNPtr = util::nn; class CoordinateOperation; using CoordinateOperationPtr = std::shared_ptr; using CoordinateOperationNNPtr = util::nn; + +class PointMotionOperation; +using PointMotionOperationPtr = std::shared_ptr; +using PointMotionOperationNNPtr = util::nn; } // namespace operation /** osgeo.proj.io namespace. @@ -1034,6 +1046,9 @@ class PROJ_GCC_DLL AuthorityFactory { PROJ_DLL crs::CRSNNPtr createCoordinateReferenceSystem(const std::string &code) const; + PROJ_DLL coordinates::CoordinateMetadataNNPtr + createCoordinateMetadata(const std::string &code) const; + PROJ_DLL operation::CoordinateOperationNNPtr createCoordinateOperation(const std::string &code, bool usePROJAlternativeGridNames) const; @@ -1244,6 +1259,11 @@ class PROJ_GCC_DLL AuthorityFactory { const std::string &datum_code, const std::string &geodetic_crs_type) const; + PROJ_INTERNAL std::list + createGeodeticCRSFromDatum(const datum::GeodeticReferenceFrameNNPtr &datum, + const std::string &preferredAuthName, + const std::string &geodetic_crs_type) const; + PROJ_INTERNAL std::list createVerticalCRSFromDatum(const std::string &datum_auth_name, const std::string &datum_code) const; @@ -1287,6 +1307,10 @@ class PROJ_GCC_DLL AuthorityFactory { bool approximateMatch = true, size_t limitResultCount = 0) const; + PROJ_FOR_TEST std::vector + getPointMotionOperationsFor(const crs::GeodeticCRSNNPtr &crs, + bool usePROJAlternativeGridNames) const; + //! @endcond protected: diff --git a/scripts/build_db.py b/scripts/build_db.py index 1109ca4f58..4166e59410 100755 --- a/scripts/build_db.py +++ b/scripts/build_db.py @@ -692,7 +692,7 @@ def fill_helmert_transformation(proj_db_cursor): '?,?,?, ?, ?,?,?, ?,?, ?,?, ?, ?,?,?,?,?, ?,?,?,?,?, ?,?,?, ?,?,?,?,?, ?,?,?,?,?, ?,?,?, ?,?,?, ?,?,?,?,?, ?,?)', arg) def fill_grid_transformation(proj_db_cursor): - proj_db_cursor.execute("SELECT coord_op_code, coord_op_name, coord_op_method_code, coord_op_method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, epsg_coordoperation.deprecated, epsg_coordoperation.remarks FROM epsg.epsg_coordoperation LEFT JOIN epsg.epsg_coordoperationmethod USING (coord_op_method_code) WHERE coord_op_type IN ('transformation', 'point motion operation') AND (coord_op_method_name LIKE 'Geographic3D to%' OR coord_op_method_name LIKE 'Geog3D to%' OR coord_op_method_name LIKE 'Point motion by grid%' OR coord_op_method_name LIKE 'Vertical Offset by Grid Interpolation%' OR coord_op_method_name IN ('NADCON', 'NADCON5 (2D)', 'NADCON5 (3D)', 'NTv1', 'NTv2', 'VERTCON', 'Geocentric translation by Grid Interpolation (IGN)'))") + proj_db_cursor.execute("SELECT coord_op_code, coord_op_name, coord_op_method_code, coord_op_method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, epsg_coordoperation.deprecated, epsg_coordoperation.remarks FROM epsg.epsg_coordoperation LEFT JOIN epsg.epsg_coordoperationmethod USING (coord_op_method_code) WHERE coord_op_type IN ('transformation', 'point motion operation') AND (coord_op_method_name LIKE 'Geographic3D to%' OR coord_op_method_name LIKE 'Geog3D to%' OR coord_op_method_name LIKE 'Point motion by grid%' OR coord_op_method_name LIKE 'Vertical Offset by Grid Interpolation%' OR coord_op_method_name IN ('NADCON', 'NADCON5 (2D)', 'NADCON5 (3D)', 'NTv1', 'NTv2', 'VERTCON', 'Geocentric translation by Grid Interpolation (IGN)', 'Geographic3D Offset by velocity grid (NRCan byn)', 'Vertical Offset by velocity grid (NRCan byn)'))") for (code, name, method_code, method_name, source_crs_code, target_crs_code, coord_op_accuracy, coord_tfm_version, deprecated, remarks) in proj_db_cursor.fetchall(): expected_order = 1 max_n_params = 3 if method_name == 'Geocentric translation by Grid Interpolation (IGN)' else 2 @@ -775,12 +775,14 @@ def fill_grid_transformation(proj_db_cursor): # 1105: Geog3D to Geog2D+GravityRelatedHeight (ITAL2005) # 1110: Geog3D to Geog2D+Depth (Gravsoft) # 1112: Vertical Offset by Grid Interpolation (NRCan byn) + # 1113: Vertical Offset by velocity grid (NRCan byn) + # 1114: Geographic3D Offset by velocity grid (NRCan byn) # 1115: Geog3D to Geog2D+Depth (txt) # 1118: Geog3D to Geog2D+GravityRelatedHeight (ISG) # 1122: Geog3D to Geog2D+Depth (gtx) # WARNING: update Transformation::isGeographic3DToGravityRelatedHeight() # in src/iso19111/operation/singleoperation.cpp if adding new methods - elif method_code in (1071, 1080, 1081, 1083, 1084, 1085, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1100, 1101, 1103, 1105, 1110, 1112, 1115, 1118, 1122) and n_params == 2: + elif method_code in (1071, 1080, 1081, 1083, 1084, 1085, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1100, 1101, 1103, 1105, 1110, 1112, 1113, 1114, 1115, 1118, 1122) and n_params == 2: assert param_code[1] == 1048, (code, method_code, param_code[1]) interpolation_crs_auth_name = EPSG_AUTHORITY interpolation_crs_code = str(int(param_value[1])) # needed to avoid codes like XXXX.0 diff --git a/scripts/build_nrcan.py b/scripts/build_nrcan.py new file mode 100755 index 0000000000..b813fb5814 --- /dev/null +++ b/scripts/build_nrcan.py @@ -0,0 +1,876 @@ +#!/usr/bin/env python +############################################################################### +# $Id$ +# +# Project: PROJ +# Purpose: Build NRCan specific definitions +# Author: Even Rouault +# +############################################################################### +# Copyright (c) 2023, Even Rouault +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +############################################################################### + +import json +import os + + +def MTM_NAD83CSRSv7(zone): + longitude = { + 1: -53, + 2: -56, + 3: -58.5, + 4: -61.5, + 5: -64.5, + 6: -67.5, + 7: -70.5, + 8: -73.5, + 9: -76.5, + 10: -79.5, + 11: -82.5, + 12: -81, + 13: -84, + 14: -87, + 15: -90, + 16: -93, + 17: -96 + } + return { + "type": "ProjectedCRS", + "name": "NAD83(CSRS)v7 / MTM zone " + str(zone), + "base_crs": { + "name": "NAD83(CSRS)v7", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "North American Datum of 1983 (CSRS) version 7", + "ellipsoid": { + "name": "GRS 1980", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257222101 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + } + }, + "conversion": { + "name": "MTM zone " + str(zone), + "method": { + "name": "Transverse Mercator", + "id": { + "authority": "EPSG", + "code": 9807 + } + }, + "parameters": [ + { + "name": "Latitude of natural origin", + "value": 0, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8801 + } + }, + { + "name": "Longitude of natural origin", + "value": longitude[zone], + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8802 + } + }, + { + "name": "Scale factor at natural origin", + "value": 0.9999, + "unit": "unity", + "id": { + "authority": "EPSG", + "code": 8805 + } + }, + { + "name": "False easting", + "value": 304800, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8806 + } + }, + { + "name": "False northing", + "value": 0, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8807 + } + } + ] + }, + "coordinate_system": { + "subtype": "Cartesian", + "axis": [ + { + "name": "Easting", + "abbreviation": "E(X)", + "direction": "east", + "unit": "metre" + }, + { + "name": "Northing", + "abbreviation": "N(Y)", + "direction": "north", + "unit": "metre" + } + ] + } + } + + +def UTM_NAD83CSRSv7(zone): + return { + "type": "ProjectedCRS", + "name": "NAD83(CSRS)v7 / UTM zone " + str(zone), + "base_crs": { + "name": "NAD83(CSRS)v7", + "datum": { + "type": "GeodeticReferenceFrame", + "name": "North American Datum of 1983 (CSRS) version 7", + "ellipsoid": { + "name": "GRS 1980", + "semi_major_axis": 6378137, + "inverse_flattening": 298.257222101 + } + }, + "coordinate_system": { + "subtype": "ellipsoidal", + "axis": [ + { + "name": "Geodetic latitude", + "abbreviation": "Lat", + "direction": "north", + "unit": "degree" + }, + { + "name": "Geodetic longitude", + "abbreviation": "Lon", + "direction": "east", + "unit": "degree" + } + ] + } + }, + "conversion": { + "name": "UTM zone " + str(zone) + "N", + "method": { + "name": "Transverse Mercator", + "id": { + "authority": "EPSG", + "code": 9807 + } + }, + "parameters": [ + { + "name": "Latitude of natural origin", + "value": 0, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8801 + } + }, + { + "name": "Longitude of natural origin", + "value": -183 + zone * 6, + "unit": "degree", + "id": { + "authority": "EPSG", + "code": 8802 + } + }, + { + "name": "Scale factor at natural origin", + "value": 0.9996, + "unit": "unity", + "id": { + "authority": "EPSG", + "code": 8805 + } + }, + { + "name": "False easting", + "value": 500000, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8806 + } + }, + { + "name": "False northing", + "value": 0, + "unit": "metre", + "id": { + "authority": "EPSG", + "code": 8807 + } + } + ] + + }, + "coordinate_system": { + "subtype": "Cartesian", + "axis": [ + { + "name": "Easting", + "abbreviation": "E(X)", + "direction": "east", + "unit": "metre" + }, + { + "name": "Northing", + "abbreviation": "N(Y)", + "direction": "north", + "unit": "metre" + } + ] + } + } + + +def vert_crs_CGVD28(geoid_model_name, geoid_model_authority, geoid_model_code): + return { + "type": "VerticalCRS", + "name": "CGVD28 height", + "datum": { + "type": "VerticalReferenceFrame", + "name": "Canadian Geodetic Vertical Datum of 1928" + }, + "coordinate_system": { + "subtype": "vertical", + "axis": [ + { + "name": "Gravity-related height", + "abbreviation": "H", + "direction": "up", + "unit": "metre" + } + ] + }, + "geoid_model": { + "name": geoid_model_name, + "id": { + "authority": geoid_model_authority, + "code": geoid_model_code + } + }, + "id": { + "authority": "EPSG", + "code": 5713 + } + } + + +def vert_crs_CGVD28_HT2_1997(): + return vert_crs_CGVD28("HT2_1997", "NRCAN", "HT2_1997_NAD83CSRSV7") + + +def vert_crs_CGVD28_HT2_2002(): + return vert_crs_CGVD28("HT2_2002", "NRCAN", "HT2_2002_NAD83CSRSV7") + + +def vert_crs_CGVD28_HT2_2010(): + return vert_crs_CGVD28("HT2_2010", "EPSG", 9987) + + +def vert_crs_CGVD2013a_1997(): + return { + "type": "VerticalCRS", + "name": "CGVD2013a(1997) height", + "datum": { + "type": "VerticalReferenceFrame", + "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 1997" + }, + "coordinate_system": { + "subtype": "vertical", + "axis": [ + { + "name": "Gravity-related height", + "abbreviation": "H", + "direction": "up", + "unit": "metre" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 20035 + } + } + + +def vert_crs_CGVD2013a_2002(): + return { + "type": "VerticalCRS", + "name": "CGVD2013a(2002) height", + "datum": { + "type": "VerticalReferenceFrame", + "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2002" + }, + "coordinate_system": { + "subtype": "vertical", + "axis": [ + { + "name": "Gravity-related height", + "abbreviation": "H", + "direction": "up", + "unit": "metre" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 20034 + } + } + + +def vert_crs_CGVD2013a_2010(): + return { + "type": "VerticalCRS", + "name": "CGVD2013a(2010) height", + "datum": { + "type": "VerticalReferenceFrame", + "name": "Canadian Geodetic Vertical Datum of 2013 (CGG2013a) epoch 2010" + }, + "coordinate_system": { + "subtype": "vertical", + "axis": [ + { + "name": "Gravity-related height", + "abbreviation": "H", + "direction": "up", + "unit": "metre" + } + ] + }, + "id": { + "authority": "EPSG", + "code": 9245 + } + } + + +usages_MTM = { + 1: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Newfoundland - onshore east of 54°30'W.", + "bbox": { + "south_latitude": 46.56, + "west_longitude": -54.5, + "north_latitude": 49.89, + "east_longitude": -52.54 + }, + }, + 2: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Newfoundland and Labrador between 57°30'W and 54°30'W.", + "bbox": { + "south_latitude": 46.81, + "west_longitude": -57.5, + "north_latitude": 54.71, + "east_longitude": -54.49 + }, + + }, + 3: + { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Newfoundland west of 57°30'W.", + "bbox": { + "south_latitude": 47.5, + "west_longitude": -59.48, + "north_latitude": 50.54, + "east_longitude": -57.5 + }, + }, + 4: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Labrador between 63°W and 60°W.", + "bbox": { + "south_latitude": 52, + "west_longitude": -63, + "north_latitude": 58.92, + "east_longitude": -60 + }, + }, + 5: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Labrador - 66°W to 63°W.", + "bbox": { + "south_latitude": 51.58, + "west_longitude": -66, + "north_latitude": 60.52, + "east_longitude": -63 + }, + }, + 6: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Labrador - west of 66°W.", + "bbox": { + "south_latitude": 52.05, + "west_longitude": -67.81, + "north_latitude": 55.34, + "east_longitude": -66 + }, + }, + 7: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Quebec - between 72°W and 69°W.", + "bbox": { + "south_latitude": 45.01, + "west_longitude": -72, + "north_latitude": 61.8, + "east_longitude": -69 + }, + + }, + 8: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - east of 75°W.", + "bbox": { + "south_latitude": 44.98, + "west_longitude": -75, + "north_latitude": 45.65, + "east_longitude": -74.35 + }, + }, + 9: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 78°W and 75°W.", + "bbox": { + "south_latitude": 43.63, + "west_longitude": -78, + "north_latitude": 46.25, + "east_longitude": -75 + }, + }, + 10: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 81°W and 78°W: south of 46°N in area to west of 80°15'W, south of 47°N in area between 80°15'W and 79°30'W, entire province between 79°30'W and 78°W.", + "bbox": { + "south_latitude": 42.26, + "west_longitude": -81, + "north_latitude": 47.33, + "east_longitude": -77.99 + }, + }, + 11: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - south of 46°N and west of 81°W.", + "bbox": { + "south_latitude": 41.67, + "west_longitude": -83.6, + "north_latitude": 46, + "east_longitude": -81 + }, + }, + 12: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 82°30'W and 79°30'W: north of 46°N in area between 82°30'W and 80°15'W, north of 47°N in area between 80°15'W and 79°30'W.", + "bbox": { + "south_latitude": 46, + "west_longitude": -82.5, + "north_latitude": 55.21, + "east_longitude": -79.5 + }, + }, + 13: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 85°30'W and 82°30'W and north of 46°N.", + "bbox": { + "south_latitude": 46, + "west_longitude": -85.5, + "north_latitude": 55.59, + "east_longitude": -82.5 + }, + }, + 14: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 88°30'W and 85°30'W.", + "bbox": { + "south_latitude": 47.17, + "west_longitude": -88.5, + "north_latitude": 56.7, + "east_longitude": -85.5 + }, + + }, + 15: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 91°30'W and 88°30'W.", + "bbox": { + "south_latitude": 47.97, + "west_longitude": -91.5, + "north_latitude": 56.9, + "east_longitude": -88.5 + }, + }, + 16: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - between 94°30'W and 91°30'W.", + "bbox": { + "south_latitude": 48.06, + "west_longitude": -94.5, + "north_latitude": 55.2, + "east_longitude": -91.5 + }, + }, + 17: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada - Ontario - west of 94°30'W.", + "bbox": { + "south_latitude": 48.69, + "west_longitude": -95.16, + "north_latitude": 53.24, + "east_longitude": -94.5 + }, + }, +} + +usages_UTM = { + 7: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada west of 138°W, onshore and offshore south of 84°N - British Columbia, Yukon.", + "bbox": { + "south_latitude": 52.05, + "west_longitude": -141.01, + "north_latitude": 72.53, + "east_longitude": -138 + }, + }, + 8: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 138°W and 132°W, onshore and offshore south of 84°N - British Columbia, Northwest Territories, Yukon.", + "bbox": { + "south_latitude": 48.06, + "west_longitude": -138, + "north_latitude": 79.42, + "east_longitude": -132 + }, + }, + 9: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 138°W and 132°W, onshore and offshore south of 84°N - British Columbia, Northwest Territories, Yukon.", + "bbox": { + "south_latitude": 48.06, + "west_longitude": -138, + "north_latitude": 79.42, + "east_longitude": -132 + }, + }, + 10: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 126°W and 120°W, onshore and offshore south of 84°N - British Columbia, Northwest Territories, Yukon.", + "bbox": { + "south_latitude": 48.13, + "west_longitude": -126, + "north_latitude": 81.8, + "east_longitude": -120 + }, + }, + 11: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 120°W and 114°W onshore and offshore - Alberta, British Columbia, Northwest Territories, Nunavut.", + "bbox": { + "south_latitude": 48.99, + "west_longitude": -120, + "north_latitude": 83.5, + "east_longitude": -114 + }, + }, + 12: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 114°W and 108°W onshore and offshore - Alberta, Northwest Territories, Nunavut, Saskatchewan.", + "bbox": { + "south_latitude": 48.99, + "west_longitude": -114, + "north_latitude": 84, + "east_longitude": -108 + }, + }, + 13: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 108°W and 102°W onshore and offshore - Northwest Territories, Nunavut, Saskatchewan.", + "bbox": { + "south_latitude": 48.99, + "west_longitude": -108, + "north_latitude": 84, + "east_longitude": -102 + }, + }, + 14: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 102°W and 96°W, onshore and offshore south of 84°N - Manitoba, Nunavut, Saskatchewan.", + "bbox": { + "south_latitude": 48.99, + "west_longitude": -102, + "north_latitude": 84, + "east_longitude": -96 + }, + }, + 15: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 96°W and 90°W, onshore and offshore south of 84°N - Manitoba, Nunavut, Ontario.", + "bbox": { + "south_latitude": 48.03, + "west_longitude": -96, + "north_latitude": 84, + "east_longitude": -90 + }, + }, + 16: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 90°W and 84°W, onshore and offshore south of 84°N - Manitoba, Nunavut, Ontario.", + "bbox": { + "south_latitude": 46.11, + "west_longitude": -90, + "north_latitude": 84, + "east_longitude": -84 + }, + }, + 17: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 84°W and 78°W, onshore and offshore south of 84°N - Nunavut, Ontario and Quebec.", + "bbox": { + "south_latitude": 41.67, + "west_longitude": -84, + "north_latitude": 84, + "east_longitude": -78 + }, + }, + 18: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 78°W and 72°W, onshore and offshore south of 84°N - Nunavut, Ontario and Quebec.", + "bbox": { + "south_latitude": 43.63, + "west_longitude": -78, + "north_latitude": 84, + "east_longitude": -72 + }, + }, + 19: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 72°W and 66°W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Quebec.", + "bbox": { + "south_latitude": 40.8, + "west_longitude": -72, + "north_latitude": 84, + "east_longitude": -66 + }, + }, + 20: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 66°W and 60°W onshore and offshore - New Brunswick, Labrador, Nova Scotia, Nunavut, Prince Edward Island, Quebec.", + "bbox": { + "south_latitude": 40.04, + "west_longitude": -66, + "north_latitude": 84, + "east_longitude": -60 + }, + }, + 21: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 60°W and 54°W - Newfoundland and Labrador; Nunavut; Quebec.", + "bbox": { + "south_latitude": 38.56, + "west_longitude": -60, + "north_latitude": 84, + "east_longitude": -54 + }, + }, + 22: { + "scope": "Engineering survey, topographic mapping.", + "area": "Canada between 54°W and 48°W onshore and offshore - Newfoundland and Labrador.", + "bbox": { + "south_latitude": 39.5, + "west_longitude": -54, + "north_latitude": 57.65, + "east_longitude": -47.99 + }, + } +} + + +def compound_crs_MTM_HT_1997(zone): + j = { + "type": "CompoundCRS", + "components": [ + MTM_NAD83CSRSv7(zone), + vert_crs_CGVD28_HT2_1997() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_MTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +def compound_crs_MTM_HT_2002(zone): + j = { + "type": "CompoundCRS", + "components": [ + MTM_NAD83CSRSv7(zone), + vert_crs_CGVD28_HT2_2002() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_MTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +def compound_crs_MTM_HT_2010(zone): + j = { + "type": "CompoundCRS", + "components": [ + MTM_NAD83CSRSv7(zone), + vert_crs_CGVD28_HT2_2010() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_MTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +def compound_crs_UTM_CGVD2013_1997(zone): + j = { + "type": "CompoundCRS", + "components": [ + UTM_NAD83CSRSv7(zone), + vert_crs_CGVD2013a_1997() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_UTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +def compound_crs_UTM_CGVD2013_2002(zone): + j = { + "type": "CompoundCRS", + "components": [ + UTM_NAD83CSRSv7(zone), + vert_crs_CGVD2013a_2002() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_UTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +def compound_crs_UTM_CGVD2013_2010(zone): + j = { + "type": "CompoundCRS", + "components": [ + UTM_NAD83CSRSv7(zone), + vert_crs_CGVD2013a_2010() + ] + } + j["name"] = j["components"][0]["name"] + " + " + j["components"][1]["name"] + usage = usages_UTM[zone] + for key in usage: + j[key] = usage[key] + return j + + +script_dir_name = os.path.dirname(os.path.realpath(__file__)) +sql_dir_name = os.path.join(os.path.dirname(script_dir_name), 'data', 'sql') + +all_sql = [] +all_sql.append("""INSERT INTO "grid_transformation" VALUES('NRCAN','HT2_1997_NAD83CSRSV7','NAD83(CSRS)v7 to CGVD28 height',NULL,'EPSG','1060','Geographic3D to GravityRelatedHeight (NRCan byn)','EPSG','8254','EPSG','5713',0.05,'EPSG','8666','Geoid (height correction) model file','HT2_1997.byn',NULL,NULL,NULL,NULL,NULL,NULL,'NRC-Can CGG2000',0);""") +all_sql.append("""INSERT INTO "usage" VALUES('NRCAN','USAGE_HT2_1997_NAD83CSRSV7','grid_transformation','NRCAN','HT2_1997_NAD83CSRSV7','EPSG','1289','EPSG','1133');""") + +all_sql.append("""INSERT INTO "grid_transformation" VALUES('NRCAN','HT2_2002_NAD83CSRSV7','NAD83(CSRS)v7 to CGVD28 height',NULL,'EPSG','1060','Geographic3D to GravityRelatedHeight (NRCan byn)','EPSG','8254','EPSG','5713',0.05,'EPSG','8666','Geoid (height correction) model file','HT2_2002v70.byn',NULL,NULL,NULL,NULL,NULL,NULL,'NRC-Can CGG2000 2002',0);""") +all_sql.append("""INSERT INTO "usage" VALUES('NRCAN','USAGE_HT2_2002_NAD83CSRSV7','grid_transformation','NRCAN','HT2_2002_NAD83CSRSV7','EPSG','1289','EPSG','1133');""") + +for zone in range(1, 17+1): + projjson = json.dumps(compound_crs_MTM_HT_1997(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_MTM{zone}_HT2_1997', NULL, NULL, NULL, '{projjson}', 1997.0, 0);""") + +for zone in range(1, 17+1): + projjson = json.dumps(compound_crs_MTM_HT_2002(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_MTM{zone}_HT2_2002', NULL, NULL, NULL, '{projjson}', 2002.0, 0);""") + +for zone in range(1, 17+1): + projjson = json.dumps(compound_crs_MTM_HT_2010(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_MTM{zone}_HT2_2010', NULL, NULL, NULL, '{projjson}', 2010.0, 0);""") + +for zone in range(7, 22+1): + projjson = json.dumps( + compound_crs_UTM_CGVD2013_1997(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_1997_UTM{zone}_CGVD2013_1997', NULL, NULL, NULL, '{projjson}', 1997.0, 0);""") + +for zone in range(7, 22+1): + projjson = json.dumps( + compound_crs_UTM_CGVD2013_2002(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2002_UTM{zone}_CGVD2013_2002', NULL, NULL, NULL, '{projjson}', 2002.0, 0);""") + +for zone in range(7, 22+1): + projjson = json.dumps( + compound_crs_UTM_CGVD2013_2010(zone)).replace("'", "''") + all_sql.append( + f"""INSERT INTO coordinate_metadata VALUES('NRCAN', 'NAD83_CSRS_2010_UTM{zone}_CGVD2013_2010', NULL, NULL, NULL, '{projjson}', 2010.0, 0);""") + +f = open(os.path.join(sql_dir_name, 'nrcan') + '.sql', 'wb') +f.write("--- This file has been generated by scripts/build_nrcan.py. DO NOT EDIT !\n\n".encode('UTF-8')) +for sql in all_sql: + f.write((sql + '\n').encode('UTF-8')) +f.close() diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt index f9e94e1c58..fc6733b1ed 100644 --- a/scripts/reference_exported_symbols.txt +++ b/scripts/reference_exported_symbols.txt @@ -97,7 +97,9 @@ osgeo::proj::coordinates::CoordinateMetadata::coordinateEpoch() const osgeo::proj::coordinates::CoordinateMetadata::~CoordinateMetadata() osgeo::proj::coordinates::CoordinateMetadata::create(dropbox::oxygen::nn > const&) osgeo::proj::coordinates::CoordinateMetadata::create(dropbox::oxygen::nn > const&, double) +osgeo::proj::coordinates::CoordinateMetadata::create(dropbox::oxygen::nn > const&, double, std::shared_ptr const&) osgeo::proj::coordinates::CoordinateMetadata::crs() const +osgeo::proj::coordinates::CoordinateMetadata::promoteTo3D(std::string const&, std::shared_ptr const&) const osgeo::proj::crs::BoundCRS::baseCRS() const osgeo::proj::crs::BoundCRS::baseCRSWithCanonicalBoundCRS() const osgeo::proj::crs::BoundCRS::~BoundCRS() @@ -346,6 +348,7 @@ osgeo::proj::io::AuthorityFactory::~AuthorityFactory() osgeo::proj::io::AuthorityFactory::CelestialBodyInfo::CelestialBodyInfo() osgeo::proj::io::AuthorityFactory::createCompoundCRS(std::string const&) const osgeo::proj::io::AuthorityFactory::createConversion(std::string const&) const +osgeo::proj::io::AuthorityFactory::createCoordinateMetadata(std::string const&) const osgeo::proj::io::AuthorityFactory::createCoordinateOperation(std::string const&, bool) const osgeo::proj::io::AuthorityFactory::createCoordinateReferenceSystem(std::string const&) const osgeo::proj::io::AuthorityFactory::createCoordinateSystem(std::string const&) const @@ -376,6 +379,7 @@ osgeo::proj::io::AuthorityFactory::getCRSInfoList() const osgeo::proj::io::AuthorityFactory::getDescriptionText(std::string const&) const osgeo::proj::io::AuthorityFactory::getGeoidModels(std::string const&) const osgeo::proj::io::AuthorityFactory::getOfficialNameFromAlias(std::string const&, std::string const&, std::string const&, bool, std::string&, std::string&, std::string&) const +osgeo::proj::io::AuthorityFactory::getPointMotionOperationsFor(dropbox::oxygen::nn > const&, bool) const osgeo::proj::io::AuthorityFactory::getUnitList() const osgeo::proj::io::AuthorityFactory::identifyBodyFromSemiMajorAxis(double, double) const osgeo::proj::io::AuthorityFactory::listAreaOfUseFromName(std::string const&, bool) const @@ -662,6 +666,7 @@ osgeo::proj::operation::CoordinateOperation::coordinateTransformer(pj_ctx*) cons osgeo::proj::operation::CoordinateOperationFactory::~CoordinateOperationFactory() osgeo::proj::operation::CoordinateOperationFactory::create() osgeo::proj::operation::CoordinateOperationFactory::createOperation(dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&) const +osgeo::proj::operation::CoordinateOperationFactory::createOperations(dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > > const&) const osgeo::proj::operation::CoordinateOperationFactory::createOperations(dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > > const&) const osgeo::proj::operation::CoordinateOperationFactory::createOperations(dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > > const&) const osgeo::proj::operation::CoordinateOperationFactory::createOperations(dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > > const&) const @@ -713,7 +718,12 @@ osgeo::proj::operation::ParameterValue::stringValue() const osgeo::proj::operation::ParameterValue::type() const osgeo::proj::operation::ParameterValue::value() const osgeo::proj::operation::ParameterValue::valueFile() const +osgeo::proj::operation::PointMotionOperation::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, dropbox::oxygen::nn > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&) +osgeo::proj::operation::PointMotionOperation::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn > const&, osgeo::proj::util::PropertyMap const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&, std::vector >, std::allocator > > > const&) +osgeo::proj::operation::PointMotionOperation::inverse() const osgeo::proj::operation::PointMotionOperation::~PointMotionOperation() +osgeo::proj::operation::PointMotionOperation::sourceCRS() const +osgeo::proj::operation::PointMotionOperation::substitutePROJAlternativeGridNames(dropbox::oxygen::nn >) const osgeo::proj::operation::SingleOperation::createPROJBased(osgeo::proj::util::PropertyMap const&, std::string const&, std::shared_ptr const&, std::shared_ptr const&, std::vector >, std::allocator > > > const&) osgeo::proj::operation::SingleOperation::gridsNeeded(std::shared_ptr const&, bool) const osgeo::proj::operation::SingleOperation::method() const @@ -867,6 +877,7 @@ proj_context_use_proj4_init_rules proj_convert_conversion_to_other_method proj_coord proj_coord_error() +proj_coordinate_metadata_create proj_coordinate_metadata_get_epoch proj_coordoperation_create_inverse proj_coordoperation_get_accuracy @@ -991,6 +1002,7 @@ proj_crs_get_datum_forced proj_crs_get_geodetic_crs proj_crs_get_horizontal_datum proj_crs_get_sub_crs +proj_crs_has_point_motion_operation proj_crs_info_list_destroy proj_crs_is_derived proj_crs_promote_to_3D diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp index d4485fbe5b..8d76828e7f 100644 --- a/src/apps/cs2cs.cpp +++ b/src/apps/cs2cs.cpp @@ -82,6 +82,7 @@ static const char *usage = " [--authority {name}] [--3d]\n" " [--accuracy {accuracy}] [--only-best[=yes|=no]] " "[--no-ballpark]\n" + " [--s_epoch {epoch}] [--t_epoch {epoch}]\n" " [+opt[=arg] ...] [+to +opt[=arg] ...] [file ...]\n"; static double (*informat)(const char *, @@ -424,6 +425,8 @@ int main(int argc, char **argv) { bool onlyBestSet = false; bool errorIfBestTransformationNotAvailable = false; bool promoteTo3D = false; + std::string sourceEpoch; + std::string targetEpoch; /* process run line arguments */ while (--argc > 0) { /* collect run line arguments */ @@ -493,6 +496,22 @@ int main(int argc, char **argv) { errorIfBestTransformationNotAvailable = false; } else if (strcmp(*argv, "--3d") == 0) { promoteTo3D = true; + } else if (strcmp(*argv, "--s_epoch") == 0) { + ++argv; + --argc; + if (argc == 0) { + emess(1, "missing argument for --s_epoch"); + std::exit(1); + } + sourceEpoch = *argv; + } else if (strcmp(*argv, "--t_epoch") == 0) { + ++argv; + --argc; + if (argc == 0) { + emess(1, "missing argument for --t_epoch"); + std::exit(1); + } + targetEpoch = *argv; } else if (**argv == '-') { for (arg = *argv;;) { switch (*++arg) { @@ -873,6 +892,42 @@ int main(int argc, char **argv) { } } + if (!sourceEpoch.empty()) { + PJ *srcMetadata = nullptr; + double sourceEpochDbl; + try { + sourceEpochDbl = c_locale_stod(sourceEpoch); + } catch (const std::exception &e) { + sourceEpochDbl = 0; + emess(3, e.what()); + } + srcMetadata = + proj_coordinate_metadata_create(nullptr, src, sourceEpochDbl); + if (!srcMetadata) { + emess(3, "cannot instantiate source coordinate system"); + } + proj_destroy(src); + src = srcMetadata; + } + + if (!targetEpoch.empty()) { + PJ *dstMetadata = nullptr; + double targetEpochDbl; + try { + targetEpochDbl = c_locale_stod(targetEpoch); + } catch (const std::exception &e) { + targetEpochDbl = 0; + emess(3, e.what()); + } + dstMetadata = + proj_coordinate_metadata_create(nullptr, dst, targetEpochDbl); + if (!dstMetadata) { + emess(3, "cannot instantiate target coordinate system"); + } + proj_destroy(dst); + dst = dstMetadata; + } + std::string authorityOption; /* keep this variable in this outer scope ! */ std::string accuracyOption; /* keep this variable in this outer scope ! */ std::vector options; diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index d076237518..f1d5ed401f 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -119,8 +119,10 @@ struct OutputOptions { << " --dump-db-structure [{object_definition} | " "{object_reference}] |" << std::endl - << " {object_definition} | {object_reference} | " - "(-s {srs_def} -t {srs_def})" + << " {object_definition} | {object_reference} |" + << std::endl + << " (-s {srs_def} [--s_epoch {epoch}] " + "-t {srs_def} [--t_epoch {epoch}])" << std::endl; std::cerr << std::endl; std::cerr << "-o: formats is a comma separated combination of: " @@ -256,8 +258,8 @@ static ExtentPtr makeBboxFilter(DatabaseContextPtr dbContext, static BaseObjectNNPtr buildObject( DatabaseContextPtr dbContext, const std::string &user_string, - const std::string &kind, const std::string &context, - bool buildBoundCRSToWGS84, + const std::string &epoch, const std::string &kind, + const std::string &context, bool buildBoundCRSToWGS84, CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS, bool promoteTo3D, bool normalizeAxisOrder, bool quiet) { BaseObjectPtr obj; @@ -404,6 +406,11 @@ static BaseObjectNNPtr buildObject( auto crs = std::dynamic_pointer_cast(obj); if (crs) { obj = crs->promoteTo3D(std::string(), dbContext).as_nullable(); + } else { + auto cm = std::dynamic_pointer_cast(obj); + if (cm) { + obj = cm->promoteTo3D(std::string(), dbContext).as_nullable(); + } } } @@ -414,6 +421,19 @@ static BaseObjectNNPtr buildObject( } } + if (!epoch.empty()) { + auto crs = std::dynamic_pointer_cast(obj); + if (crs) { + obj = CoordinateMetadata::create(NN_NO_CHECK(crs), std::stod(epoch), + dbContext) + .as_nullable(); + } else { + std::cerr << context << ": applying epoch to a non-CRS object" + << std::endl; + std::exit(1); + } + } + return NN_NO_CHECK(obj); } @@ -822,7 +842,8 @@ static bool is3DCRS(const CRSPtr &crs) { static void outputOperations( DatabaseContextPtr dbContext, const std::string &sourceCRSStr, - const std::string &targetCRSStr, const ExtentPtr &bboxFilter, + const std::string &sourceEpoch, const std::string &targetCRSStr, + const std::string &targetEpoch, const ExtentPtr &bboxFilter, CoordinateOperationContext::SpatialCriterion spatialCriterion, bool spatialCriterionExplicitlySpecified, CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse, @@ -832,10 +853,10 @@ static void outputOperations( const std::string &authority, bool usePROJGridAlternatives, bool showSuperseded, bool promoteTo3D, bool normalizeAxisOrder, double minimumAccuracy, const OutputOptions &outputOpt, bool summary) { - auto sourceObj = - buildObject(dbContext, sourceCRSStr, "crs", "source CRS", false, - CoordinateOperationContext::IntermediateCRSUse::NEVER, - promoteTo3D, normalizeAxisOrder, outputOpt.quiet); + auto sourceObj = buildObject( + dbContext, sourceCRSStr, sourceEpoch, "crs", "source CRS", false, + CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, + normalizeAxisOrder, outputOpt.quiet); auto sourceCRS = nn_dynamic_pointer_cast(sourceObj); CoordinateMetadataPtr sourceCoordinateMetadata; if (!sourceCRS) { @@ -853,10 +874,10 @@ static void outputOperations( } } - auto targetObj = - buildObject(dbContext, targetCRSStr, "crs", "target CRS", false, - CoordinateOperationContext::IntermediateCRSUse::NEVER, - promoteTo3D, normalizeAxisOrder, outputOpt.quiet); + auto targetObj = buildObject( + dbContext, targetCRSStr, targetEpoch, "crs", "target CRS", false, + CoordinateOperationContext::IntermediateCRSUse::NEVER, promoteTo3D, + normalizeAxisOrder, outputOpt.quiet); auto targetCRS = nn_dynamic_pointer_cast(targetObj); CoordinateMetadataPtr targetCoordinateMetadata; if (!targetCRS) { @@ -874,14 +895,6 @@ static void outputOperations( } } - if (sourceCoordinateMetadata != nullptr && - targetCoordinateMetadata != nullptr) { - std::cerr << "CoordinateMetadata with epoch to CoordinateMetadata " - "with epoch not supported currently." - << std::endl; - std::exit(1); - } - // TODO: handle promotion of CoordinateMetadata if (sourceCRS && targetCRS && dbContext && !promoteTo3D) { // Auto-promote source/target CRS if it is specified by its name, @@ -922,6 +935,12 @@ static void outputOperations( const auto createOperations = [&]() { if (sourceCoordinateMetadata) { + if (targetCoordinateMetadata) { + return CoordinateOperationFactory::create() + ->createOperations( + NN_NO_CHECK(sourceCoordinateMetadata), + NN_NO_CHECK(targetCoordinateMetadata), ctxt); + } return CoordinateOperationFactory::create()->createOperations( NN_NO_CHECK(sourceCoordinateMetadata), NN_NO_CHECK(targetCRS), ctxt); @@ -1020,7 +1039,9 @@ int main(int argc, char **argv) { std::string user_string; bool user_string_specified = false; std::string sourceCRSStr; + std::string sourceEpoch; std::string targetCRSStr; + std::string targetEpoch; bool outputSwitchSpecified = false; OutputOptions outputOpt; std::string objectKind; @@ -1193,9 +1214,15 @@ int main(int argc, char **argv) { } else if ((arg == "-s" || arg == "--source-crs") && i + 1 < argc) { i++; sourceCRSStr = argv[i]; + } else if (arg == "--s_epoch" && i + 1 < argc) { + i++; + sourceEpoch = argv[i]; } else if ((arg == "-t" || arg == "--target-crs") && i + 1 < argc) { i++; targetCRSStr = argv[i]; + } else if (arg == "--t_epoch" && i + 1 < argc) { + i++; + targetEpoch = argv[i]; } else if (arg == "-q" || arg == "--quiet") { outputOpt.quiet = true; } else if (arg == "--c-ify") { @@ -1574,10 +1601,10 @@ int main(int argc, char **argv) { if (!user_string.empty()) { try { - auto obj(buildObject(dbContext, user_string, objectKind, - "input string", buildBoundCRSToWGS84, - allowUseIntermediateCRS, promoteTo3D, - normalizeAxisOrder, outputOpt.quiet)); + auto obj(buildObject( + dbContext, user_string, std::string(), objectKind, + "input string", buildBoundCRSToWGS84, allowUseIntermediateCRS, + promoteTo3D, normalizeAxisOrder, outputOpt.quiet)); if (guessDialect) { auto dialect = WKTParser().guessDialect(user_string); std::cout << "Guessed WKT dialect: "; @@ -1671,8 +1698,8 @@ int main(int argc, char **argv) { } else { auto bboxFilter = makeBboxFilter(dbContext, bboxStr, area, true); try { - outputOperations(dbContext, sourceCRSStr, targetCRSStr, bboxFilter, - spatialCriterion, + outputOperations(dbContext, sourceCRSStr, sourceEpoch, targetCRSStr, + targetEpoch, bboxFilter, spatialCriterion, spatialCriterionExplicitlySpecified, crsExtentUse, gridAvailabilityUse, allowUseIntermediateCRS, pivots, authority, usePROJGridAlternatives, diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp index 64da697f8f..59ea970f96 100644 --- a/src/iso19111/c_api.cpp +++ b/src/iso19111/c_api.cpp @@ -208,13 +208,15 @@ PJ *pj_obj_create(PJ_CONTEXT *ctx, const BaseObjectNNPtr &objIn) { pj->iso_obj = objIn; pj->iso_obj_is_coordinate_operation = true; auto sourceEpoch = coordop->sourceCoordinateEpoch(); + auto targetEpoch = coordop->targetCoordinateEpoch(); if (sourceEpoch.has_value()) { - pj->hasCoordinateEpoch = true; - pj->coordinateEpoch = - sourceEpoch->coordinateEpoch().convertToUnit( - common::UnitOfMeasure::YEAR); + if (!targetEpoch.has_value()) { + pj->hasCoordinateEpoch = true; + pj->coordinateEpoch = + sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR); + } } else { - auto targetEpoch = coordop->targetCoordinateEpoch(); if (targetEpoch.has_value()) { pj->hasCoordinateEpoch = true; pj->coordinateEpoch = @@ -4155,18 +4157,44 @@ PJ *proj_crs_promote_to_3D(PJ_CONTEXT *ctx, const char *crs_3D_name, } auto cpp_2D_crs = dynamic_cast(crs_2D->iso_obj.get()); if (!cpp_2D_crs) { - proj_log_error(ctx, __FUNCTION__, "crs_2D is not a CRS"); - return nullptr; - } - try { - auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); - return pj_obj_create( - ctx, cpp_2D_crs->promoteTo3D(crs_3D_name ? std::string(crs_3D_name) - : cpp_2D_crs->nameStr(), - dbContext)); - } catch (const std::exception &e) { - proj_log_error(ctx, __FUNCTION__, e.what()); - return nullptr; + auto coordinateMetadata = + dynamic_cast(crs_2D->iso_obj.get()); + if (!coordinateMetadata) { + proj_log_error(ctx, __FUNCTION__, + "crs_2D is not a CRS or a CoordinateMetadata"); + return nullptr; + } + + try { + auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + auto crs = coordinateMetadata->crs(); + auto crs_3D = crs->promoteTo3D( + crs_3D_name ? std::string(crs_3D_name) : crs->nameStr(), + dbContext); + if (coordinateMetadata->coordinateEpoch().has_value()) { + return pj_obj_create( + ctx, CoordinateMetadata::create( + crs_3D, + coordinateMetadata->coordinateEpochAsDecimalYear(), + dbContext)); + } else { + return pj_obj_create(ctx, CoordinateMetadata::create(crs_3D)); + } + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + return nullptr; + } + } else { + try { + auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + return pj_obj_create(ctx, cpp_2D_crs->promoteTo3D( + crs_3D_name ? std::string(crs_3D_name) + : cpp_2D_crs->nameStr(), + dbContext)); + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + return nullptr; + } } } @@ -8440,22 +8468,19 @@ proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs, } } - if (sourceCoordinateMetadata != nullptr && - targetCoordinateMetadata != nullptr) { - proj_log_error(ctx, __FUNCTION__, - "CoordinateMetadata with epoch to CoordinateMetadata " - "with epoch not supported currently"); - return nullptr; - } - try { auto factory = CoordinateOperationFactory::create(); std::vector objects; auto ops = sourceCoordinateMetadata != nullptr - ? factory->createOperations( - NN_NO_CHECK(sourceCoordinateMetadata), - NN_NO_CHECK(targetCRS), - operationContext->operationContext) + ? (targetCoordinateMetadata != nullptr + ? factory->createOperations( + NN_NO_CHECK(sourceCoordinateMetadata), + NN_NO_CHECK(targetCoordinateMetadata), + operationContext->operationContext) + : factory->createOperations( + NN_NO_CHECK(sourceCoordinateMetadata), + NN_NO_CHECK(targetCRS), + operationContext->operationContext)) : targetCoordinateMetadata != nullptr ? factory->createOperations( NN_NO_CHECK(sourceCRS), @@ -9541,6 +9566,36 @@ proj_get_geoid_models_from_database(PJ_CONTEXT *ctx, const char *auth_name, // --------------------------------------------------------------------------- +/** \brief Instanciate a CoordinateMetadata object + * + * @since 9.4 + */ + +PJ *proj_coordinate_metadata_create(PJ_CONTEXT *ctx, const PJ *crs, + double epoch) { + SANITIZE_CTX(ctx); + if (!crs) { + proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE); + proj_log_error(ctx, __FUNCTION__, "missing required input"); + return nullptr; + } + auto crsCast = std::dynamic_pointer_cast(crs->iso_obj); + if (!crsCast) { + proj_log_error(ctx, __FUNCTION__, "Object is not a CRS"); + return nullptr; + } + try { + auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); + return pj_obj_create(ctx, CoordinateMetadata::create( + NN_NO_CHECK(crsCast), epoch, dbContext)); + } catch (const std::exception &e) { + proj_log_debug(ctx, __FUNCTION__, e.what()); + return nullptr; + } +} + +// --------------------------------------------------------------------------- + /** \brief Return the coordinate epoch associated with a CoordinateMetadata. * * It may return a NaN value if there is no associated coordinate epoch. @@ -9567,3 +9622,35 @@ double proj_coordinate_metadata_get_epoch(PJ_CONTEXT *ctx, const PJ *obj) { } // --------------------------------------------------------------------------- + +/** \brief Return whether a CRS has an associated PointMotionOperation + * + * @since 9.4 + */ +int proj_crs_has_point_motion_operation(PJ_CONTEXT *ctx, const PJ *crs) { + SANITIZE_CTX(ctx); + if (!crs) { + proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE); + proj_log_error(ctx, __FUNCTION__, "missing required input"); + return false; + } + auto l_crs = dynamic_cast(crs->iso_obj.get()); + if (!l_crs) { + proj_log_error(ctx, __FUNCTION__, "Object is not a CRS"); + return false; + } + auto geodeticCRS = l_crs->extractGeodeticCRS(); + if (!geodeticCRS) + return false; + try { + auto factory = + AuthorityFactory::create(getDBcontext(ctx), std::string()); + return !factory + ->getPointMotionOperationsFor(NN_NO_CHECK(geodeticCRS), + false) + .empty(); + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + } + return false; +} diff --git a/src/iso19111/coordinates.cpp b/src/iso19111/coordinates.cpp index 1986cd1660..a51763d88e 100644 --- a/src/iso19111/coordinates.cpp +++ b/src/iso19111/coordinates.cpp @@ -115,9 +115,41 @@ CoordinateMetadataNNPtr CoordinateMetadata::create(const crs::CRSNNPtr &crsIn) { CoordinateMetadataNNPtr CoordinateMetadata::create(const crs::CRSNNPtr &crsIn, double coordinateEpochIn) { + return create(crsIn, coordinateEpochIn, nullptr); +} + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a CoordinateMetadata from a dynamic CRS and an associated + * coordinate epoch. + * + * @param crsIn a dynamic CRS + * @param coordinateEpochIn coordinate epoch expressed in decimal year. + * @param dbContext Database context (may be null) + * @return new CoordinateMetadata. + * @throw util::Exception if crsIn is a static CRS. + */ +CoordinateMetadataNNPtr +CoordinateMetadata::create(const crs::CRSNNPtr &crsIn, double coordinateEpochIn, + const io::DatabaseContextPtr &dbContext) { + if (!crsIn->isDynamic(/*considerWGS84AsDynamic=*/true)) { - throw util::Exception( - "Coordinate epoch should not be provided for a static CRS"); + bool ok = false; + if (dbContext) { + auto geodCrs = crsIn->extractGeodeticCRS(); + if (geodCrs) { + auto factory = io::AuthorityFactory::create( + NN_NO_CHECK(dbContext), std::string()); + ok = !factory + ->getPointMotionOperationsFor(NN_NO_CHECK(geodCrs), + false) + .empty(); + } + } + if (!ok) { + throw util::Exception( + "Coordinate epoch should not be provided for a static CRS"); + } } auto coordinateMetadata( @@ -150,17 +182,6 @@ CoordinateMetadata::coordinateEpoch() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- -// Avoid rounding issues due to year -> second (SI unit) -> year roundtrips -static double getRoundedEpochInDecimalYear(double year) { - // Try to see if the value is close to xxxx.yyy decimal year. - if (std::fabs(1000 * year - std::round(1000 * year)) <= 1e-3) { - year = std::round(1000 * year) / 1000.0; - } - return year; -} - -// --------------------------------------------------------------------------- - /** \brief Get the coordinate epoch associated with this CoordinateMetadata * object, as decimal year. * @@ -178,6 +199,31 @@ double CoordinateMetadata::coordinateEpochAsDecimalYear() PROJ_PURE_DEFN { // --------------------------------------------------------------------------- +/** \brief Return a variant of this CoordinateMetadata "promoted" to a 3D one, + * if not already the case. + * + * @param newName Name of the new underlying CRS. If empty, nameStr() will be + * used. + * @param dbContext Database context to look for potentially already registered + * 3D CRS. May be nullptr. + * @return a new CoordinateMetadata object promoted to 3D, or the current one if + * already 3D or not applicable. + */ +CoordinateMetadataNNPtr +CoordinateMetadata::promoteTo3D(const std::string &newName, + const io::DatabaseContextPtr &dbContext) const { + auto crs = d->crs_->promoteTo3D(newName, dbContext); + auto coordinateMetadata( + d->coordinateEpoch_.has_value() + ? CoordinateMetadata::nn_make_shared( + crs, coordinateEpochAsDecimalYear()) + : CoordinateMetadata::nn_make_shared(crs)); + coordinateMetadata->assignSelf(coordinateMetadata); + return coordinateMetadata; +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress void CoordinateMetadata::_exportToWKT(io::WKTFormatter *formatter) const { if (formatter->version() != io::WKTFormatter::Version::WKT2 || diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp index 86e9e929ce..91b5b8468d 100644 --- a/src/iso19111/datum.cpp +++ b/src/iso19111/datum.cpp @@ -120,17 +120,6 @@ void Datum::Private::exportAnchorDefinition(io::WKTFormatter *formatter) const { // --------------------------------------------------------------------------- -// Avoid rounding issues due to year -> second (SI unit) -> year roundtrips -static double getRoundedEpochInDecimalYear(double year) { - // Try to see if the value is close to xxxx.yyy decimal year. - if (std::fabs(1000 * year - std::round(1000 * year)) <= 1e-3) { - year = std::round(1000 * year) / 1000.0; - } - return year; -} - -// --------------------------------------------------------------------------- - void Datum::Private::exportAnchorEpoch(io::WKTFormatter *formatter) const { if (anchorEpoch->has_value()) { formatter->startNode(io::WKTConstants::ANCHOREPOCH, false); diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 6473f1caab..7b722aa20a 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -32,6 +32,7 @@ #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" +#include "proj/coordinates.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" @@ -118,7 +119,7 @@ namespace io { constexpr int DATABASE_LAYOUT_VERSION_MAJOR = 1; // If the code depends on the new additions, then DATABASE_LAYOUT_VERSION_MINOR // must be incremented. -constexpr int DATABASE_LAYOUT_VERSION_MINOR = 2; +constexpr int DATABASE_LAYOUT_VERSION_MINOR = 3; constexpr size_t N_MAX_PARAMS = 7; @@ -5666,6 +5667,61 @@ AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, // --------------------------------------------------------------------------- +/** \brief Returns a coordinates::CoordinateMetadata from the specified code. + * + * @param code Object code allocated by authority. + * @return object. + * @throw NoSuchAuthorityCodeException + * @throw FactoryException + * @since 9.4 + */ + +coordinates::CoordinateMetadataNNPtr +AuthorityFactory::createCoordinateMetadata(const std::string &code) const { + auto res = d->runWithCodeParam( + "SELECT crs_auth_name, crs_code, crs_text_definition, coordinate_epoch " + "FROM coordinate_metadata WHERE auth_name = ? AND code = ?", + code); + if (res.empty()) { + throw NoSuchAuthorityCodeException("coordinate_metadata not found", + d->authority(), code); + } + try { + const auto &row = res.front(); + const auto &crs_auth_name = row[0]; + const auto &crs_code = row[1]; + const auto &crs_text_definition = row[2]; + const auto &coordinate_epoch = row[3]; + + auto l_context = d->context(); + DatabaseContext::Private::RecursionDetector detector(l_context); + auto crs = + !crs_auth_name.empty() + ? d->createFactory(crs_auth_name) + ->createCoordinateReferenceSystem(crs_code) + .as_nullable() + : util::nn_dynamic_pointer_cast( + createFromUserInput(crs_text_definition, l_context)); + if (!crs) { + throw FactoryException( + std::string("cannot build CoordinateMetadata ") + + d->authority() + ":" + code + ": cannot build CRS"); + } + if (coordinate_epoch.empty()) { + return coordinates::CoordinateMetadata::create(NN_NO_CHECK(crs)); + } else { + return coordinates::CoordinateMetadata::create( + NN_NO_CHECK(crs), c_locale_stod(coordinate_epoch), + l_context.as_nullable()); + } + } catch (const std::exception &ex) { + throw buildFactoryException("CoordinateMetadata", d->authority(), code, + ex); + } +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress static util::PropertyMap createMapNameEPSGCode(const std::string &name, @@ -6082,6 +6138,26 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( accuracies.emplace_back( metadata::PositionalAccuracy::create(accuracy)); } + + // A bit fragile to detect the operation type with the method name, + // but not worth changing the database model + if (starts_with(method_name, "Point motion")) { + if (!sourceCRS->isEquivalentTo(targetCRS.get())) { + throw operation::InvalidOperation( + "source_crs and target_crs should be the same for a " + "PointMotionOperation"); + } + + auto pmo = operation::PointMotionOperation::create( + props, sourceCRS, propsMethod, parameters, values, + accuracies); + if (usePROJAlternativeGridNames) { + return pmo->substitutePROJAlternativeGridNames( + d->context()); + } + return pmo; + } + auto transf = operation::Transformation::create( props, sourceCRS, targetCRS, interpolationCRS, propsMethod, parameters, values, accuracies); @@ -9072,6 +9148,54 @@ std::list AuthorityFactory::createGeodeticCRSFromDatum( // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +std::list AuthorityFactory::createGeodeticCRSFromDatum( + const datum::GeodeticReferenceFrameNNPtr &datum, + const std::string &preferredAuthName, + const std::string &geodetic_crs_type) const { + std::list candidates; + const auto &ids = datum->identifiers(); + const auto &datumName = datum->nameStr(); + if (!ids.empty()) { + for (const auto &id : ids) { + const auto &authName = *(id->codeSpace()); + const auto &code = id->code(); + if (!authName.empty()) { + const auto tmpFactory = + (preferredAuthName == authName) + ? create(databaseContext(), authName) + : NN_NO_CHECK(d->getSharedFromThis()); + auto l_candidates = tmpFactory->createGeodeticCRSFromDatum( + authName, code, geodetic_crs_type); + for (const auto &candidate : l_candidates) { + candidates.emplace_back(candidate); + } + } + } + } else if (datumName != "unknown" && datumName != "unnamed") { + auto matches = createObjectsFromName( + datumName, + {io::AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, false, + 2); + if (matches.size() == 1) { + const auto &match = matches.front(); + if (datum->_isEquivalentTo(match.get(), + util::IComparable::Criterion::EQUIVALENT, + databaseContext().as_nullable()) && + !match->identifiers().empty()) { + return createGeodeticCRSFromDatum( + util::nn_static_pointer_cast( + match), + preferredAuthName, geodetic_crs_type); + } + } + } + return candidates; +} +//! @endcond + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress std::list AuthorityFactory::createVerticalCRSFromDatum( const std::string &datum_auth_name, const std::string &datum_code) const { @@ -9567,6 +9691,55 @@ AuthorityFactory::getTransformationsForGeoid( return res; } + +// --------------------------------------------------------------------------- + +std::vector +AuthorityFactory::getPointMotionOperationsFor( + const crs::GeodeticCRSNNPtr &crs, bool usePROJAlternativeGridNames) const { + std::vector res; + const auto crsList = + createGeodeticCRSFromDatum(crs->datumNonNull(d->context()), + /* preferredAuthName = */ std::string(), + /* geodetic_crs_type = */ std::string()); + if (crsList.empty()) + return res; + std::string sql("SELECT auth_name, code FROM coordinate_operation_view " + "WHERE source_crs_auth_name = target_crs_auth_name AND " + "source_crs_code = target_crs_code AND deprecated = 0 AND " + "("); + bool addOr = false; + ListOfParams params; + for (const auto &candidateCrs : crsList) { + if (addOr) + sql += " OR "; + addOr = true; + sql += "(source_crs_auth_name = ? AND source_crs_code = ?)"; + const auto &ids = candidateCrs->identifiers(); + params.emplace_back(*(ids[0]->codeSpace())); + params.emplace_back(ids[0]->code()); + } + sql += ")"; + if (d->hasAuthorityRestriction()) { + sql += " AND auth_name = ?"; + params.emplace_back(d->authority()); + } + + auto sqlRes = d->run(sql, params); + for (const auto &row : sqlRes) { + const auto &auth_name = row[0]; + const auto &code = row[1]; + auto pmo = + util::nn_dynamic_pointer_cast( + d->createFactory(auth_name)->createCoordinateOperation( + code, usePROJAlternativeGridNames)); + if (pmo) { + res.emplace_back(NN_NO_CHECK(pmo)); + } + } + return res; +} + //! @endcond // --------------------------------------------------------------------------- diff --git a/src/iso19111/internal.cpp b/src/iso19111/internal.cpp index 2ebf60dd8a..c6ba11c73b 100644 --- a/src/iso19111/internal.cpp +++ b/src/iso19111/internal.cpp @@ -32,6 +32,7 @@ #include "proj/internal/internal.hpp" +#include #include #include #ifdef _MSC_VER @@ -395,6 +396,17 @@ std::string concat(const char *a, const std::string &b, const char *c) { // --------------------------------------------------------------------------- +// Avoid rounding issues due to year -> second (SI unit) -> year roundtrips +double getRoundedEpochInDecimalYear(double year) { + // Try to see if the value is close to xxxx.yyy decimal year. + if (std::fabs(1000 * year - std::round(1000 * year)) <= 1e-3) { + year = std::round(1000 * year) / 1000.0; + } + return year; +} + +// --------------------------------------------------------------------------- + } // namespace internal NS_PROJ_END diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 721addf972..be2d10f036 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -1479,6 +1479,9 @@ struct WKTParser::Private { TransformationNNPtr buildCoordinateOperation(const WKTNodeNNPtr &node); + PointMotionOperationNNPtr + buildPointMotionOperation(const WKTNodeNNPtr &node); + ConcatenatedOperationNNPtr buildConcatenatedOperation(const WKTNodeNNPtr &node); @@ -3622,6 +3625,47 @@ WKTParser::Private::buildCoordinateOperation(const WKTNodeNNPtr &node) { // --------------------------------------------------------------------------- +PointMotionOperationNNPtr +WKTParser::Private::buildPointMotionOperation(const WKTNodeNNPtr &node) { + const auto *nodeP = node->GP(); + auto &methodNode = nodeP->lookForChild(WKTConstants::METHOD); + if (isNull(methodNode)) { + ThrowMissing(WKTConstants::METHOD); + } + if (methodNode->GP()->childrenSize() == 0) { + ThrowNotEnoughChildren(WKTConstants::METHOD); + } + + auto &sourceCRSNode = nodeP->lookForChild(WKTConstants::SOURCECRS); + if (sourceCRSNode->GP()->childrenSize() != 1) { + ThrowMissing(WKTConstants::SOURCECRS); + } + auto sourceCRS = buildCRS(sourceCRSNode->GP()->children()[0]); + if (!sourceCRS) { + throw ParsingException("Invalid content in SOURCECRS node"); + } + + std::vector parameters; + std::vector values; + auto defaultLinearUnit = UnitOfMeasure::NONE; + auto defaultAngularUnit = UnitOfMeasure::NONE; + consumeParameters(node, false, parameters, values, defaultLinearUnit, + defaultAngularUnit); + + std::vector accuracies; + auto &accuracyNode = nodeP->lookForChild(WKTConstants::OPERATIONACCURACY); + if (/*!isNull(accuracyNode) && */ accuracyNode->GP()->childrenSize() == 1) { + accuracies.push_back(PositionalAccuracy::create( + stripQuotes(accuracyNode->GP()->children()[0]))); + } + + return PointMotionOperation::create( + buildProperties(node), NN_NO_CHECK(sourceCRS), + buildProperties(methodNode), parameters, values, accuracies); +} + +// --------------------------------------------------------------------------- + ConcatenatedOperationNNPtr WKTParser::Private::buildConcatenatedOperation(const WKTNodeNNPtr &node) { @@ -5363,13 +5407,14 @@ WKTParser::Private::buildCoordinateMetadata(const WKTNodeNNPtr &node) { if (epochChildren.empty()) { ThrowMissing(WKTConstants::EPOCH); } + double coordinateEpoch; try { - const double coordinateEpoch = asDouble(epochChildren[0]); - return CoordinateMetadata::create(NN_NO_CHECK(crs), - coordinateEpoch); + coordinateEpoch = asDouble(epochChildren[0]); } catch (const std::exception &) { throw ParsingException("Invalid EPOCH node"); } + return CoordinateMetadata::create(NN_NO_CHECK(crs), coordinateEpoch, + dbContext_); } return CoordinateMetadata::create(NN_NO_CHECK(crs)); @@ -5601,6 +5646,11 @@ BaseObjectNNPtr WKTParser::Private::build(const WKTNodeNNPtr &node) { buildConcatenatedOperation(node)); } + if (ci_equal(name, WKTConstants::POINTMOTIONOPERATION)) { + return util::nn_static_pointer_cast( + buildPointMotionOperation(node)); + } + if (ci_equal(name, WKTConstants::ID) || ci_equal(name, WKTConstants::AUTHORITY)) { return util::nn_static_pointer_cast( @@ -5658,6 +5708,7 @@ class JSONParser { CompoundCRSNNPtr buildCompoundCRS(const json &j); BoundCRSNNPtr buildBoundCRS(const json &j); TransformationNNPtr buildTransformation(const json &j); + PointMotionOperationNNPtr buildPointMotionOperation(const json &j); ConcatenatedOperationNNPtr buildConcatenatedOperation(const json &j); CoordinateMetadataNNPtr buildCoordinateMetadata(const json &j); @@ -6217,6 +6268,9 @@ BaseObjectNNPtr JSONParser::create(const json &j) if (type == "Transformation") { return buildTransformation(j); } + if (type == "PointMotionOperation") { + return buildPointMotionOperation(j); + } if (type == "ConcatenatedOperation") { return buildConcatenatedOperation(j); } @@ -6581,6 +6635,43 @@ TransformationNNPtr JSONParser::buildTransformation(const json &j) { // --------------------------------------------------------------------------- +PointMotionOperationNNPtr JSONParser::buildPointMotionOperation(const json &j) { + + auto sourceCRS = buildCRS(getObject(j, "source_crs")); + auto methodJ = getObject(j, "method"); + auto parametersJ = getArray(j, "parameters"); + std::vector parameters; + std::vector values; + for (const auto ¶m : parametersJ) { + if (!param.is_object()) { + throw ParsingException( + "Unexpected type for a \"parameters\" child"); + } + parameters.emplace_back( + OperationParameter::create(buildProperties(param))); + if (param.contains("value")) { + auto v = param["value"]; + if (v.is_string()) { + values.emplace_back( + ParameterValue::createFilename(v.get())); + continue; + } + } + values.emplace_back(ParameterValue::create(getMeasure(param))); + } + std::vector accuracies; + if (j.contains("accuracy")) { + accuracies.push_back( + PositionalAccuracy::create(getString(j, "accuracy"))); + } + + return PointMotionOperation::create(buildProperties(j), sourceCRS, + buildProperties(methodJ), parameters, + values, accuracies); +} + +// --------------------------------------------------------------------------- + ConcatenatedOperationNNPtr JSONParser::buildConcatenatedOperation(const json &j) { @@ -6625,8 +6716,8 @@ CoordinateMetadataNNPtr JSONParser::buildCoordinateMetadata(const json &j) { if (j.contains("coordinateEpoch")) { auto jCoordinateEpoch = j["coordinateEpoch"]; if (jCoordinateEpoch.is_number()) { - return CoordinateMetadata::create(crs, - jCoordinateEpoch.get()); + return CoordinateMetadata::create( + crs, jCoordinateEpoch.get(), dbContext_); } throw ParsingException( "Unexpected type for value of \"coordinateEpoch\""); @@ -7209,6 +7300,10 @@ static BaseObjectNNPtr createFromURNPart(const DatabaseContextPtr &dbContext, if (type == "meridian") { return factory->createPrimeMeridian(code); } + // Extension of OGC URN syntax to CoordinateMetadata + if (type == "coordinateMetadata") { + return factory->createCoordinateMetadata(code); + } throw ParsingException(concat("unhandled object type: ", type)); } catch (...) { if (version.empty()) { @@ -7238,11 +7333,53 @@ static BaseObjectNNPtr createFromURNPart(const DatabaseContextPtr &dbContext, static BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules, - PJ_CONTEXT *ctx) { + PJ_CONTEXT *ctx, + bool ignoreCoordinateEpoch) { std::size_t idxFirstCharNotSpace = text.find_first_not_of(" \t\r\n"); if (idxFirstCharNotSpace > 0 && idxFirstCharNotSpace != std::string::npos) { return createFromUserInput(text.substr(idxFirstCharNotSpace), dbContext, - usePROJ4InitRules, ctx); + usePROJ4InitRules, ctx, + ignoreCoordinateEpoch); + } + + // Parse strings like "ITRF2014 @ 2025.0" + const auto posAt = text.find('@'); + if (!ignoreCoordinateEpoch && posAt != std::string::npos) { + + // Try first as if belonged to the name + try { + return createFromUserInput(text, dbContext, usePROJ4InitRules, ctx, + /* ignoreCoordinateEpoch = */ true); + } catch (...) { + } + + std::string leftPart = text.substr(0, posAt); + while (!leftPart.empty() && leftPart.back() == ' ') + leftPart.resize(leftPart.size() - 1); + const auto nonSpacePos = text.find_first_not_of(' ', posAt + 1); + if (nonSpacePos != std::string::npos) { + auto obj = + createFromUserInput(leftPart, dbContext, usePROJ4InitRules, ctx, + /* ignoreCoordinateEpoch = */ true); + auto crs = nn_dynamic_pointer_cast(obj); + if (crs) { + double epoch; + try { + epoch = c_locale_stod(text.substr(nonSpacePos)); + } catch (const std::exception &) { + throw ParsingException("non-numeric value after @"); + } + try { + return CoordinateMetadata::create(NN_NO_CHECK(crs), epoch, + dbContext); + } catch (const std::exception &e) { + throw ParsingException( + std::string( + "CoordinateMetadata::create() failed with: ") + + e.what()); + } + } + } } if (!text.empty() && text[0] == '{') { @@ -7771,36 +7908,6 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, } } - // Parse strings like "ITRF2014 @ 2025.0" - const auto posAt = text.find('@'); - if (posAt != std::string::npos) { - std::string leftPart = text.substr(0, posAt); - while (!leftPart.empty() && leftPart.back() == ' ') - leftPart.resize(leftPart.size() - 1); - const auto nonSpacePos = text.find_first_not_of(' ', posAt + 1); - if (nonSpacePos != std::string::npos) { - auto obj = createFromUserInput(leftPart, dbContext, - usePROJ4InitRules, ctx); - auto crs = nn_dynamic_pointer_cast(obj); - if (crs) { - double epoch; - try { - epoch = c_locale_stod(text.substr(nonSpacePos)); - } catch (const std::exception &) { - throw ParsingException("non-numeric value after @"); - } - try { - return CoordinateMetadata::create(NN_NO_CHECK(crs), epoch); - } catch (const std::exception &e) { - throw ParsingException( - std::string( - "CoordinateMetadata::create() failed with: ") + - e.what()); - } - } - } - } - throw ParsingException("unrecognized format / unknown name"); } //! @endcond @@ -7828,6 +7935,9 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)" * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031" * + *
  • Extension of OGC URN for CoordinateMetadata. + * e.g. + * "urn:ogc:def:coordinateMetadata:NRCAN::NAD83_CSRS_1997_MTM11_HT2_1997"
  • *
  • OGC URN combining references for concatenated operations * e.g. * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"
  • @@ -7861,7 +7971,8 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, BaseObjectNNPtr createFromUserInput(const std::string &text, const DatabaseContextPtr &dbContext, bool usePROJ4InitRules) { - return createFromUserInput(text, dbContext, usePROJ4InitRules, nullptr); + return createFromUserInput(text, dbContext, usePROJ4InitRules, nullptr, + /* ignoreCoordinateEpoch = */ false); } // --------------------------------------------------------------------------- @@ -7886,6 +7997,9 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, * e.g. for Projected 3D CRS "UTM zone 31N / WGS 84 (3D)" * "urn:ogc:def:crs,crs:EPSG::4979,cs:PROJ::ENh,coordinateOperation:EPSG::16031" * + *
  • Extension of OGC URN for CoordinateMetadata. + * e.g. + * "urn:ogc:def:coordinateMetadata:NRCAN::NAD83_CSRS_1997_MTM11_HT2_1997"
  • *
  • OGC URN combining references for concatenated operations * e.g. * "urn:ogc:def:coordinateOperation,coordinateOperation:EPSG::3895,coordinateOperation:EPSG::1618"
  • @@ -7914,7 +8028,8 @@ BaseObjectNNPtr createFromUserInput(const std::string &text, PJ_CONTEXT *ctx) { } } catch (const std::exception &) { } - return createFromUserInput(text, dbContext, false, ctx); + return createFromUserInput(text, dbContext, false, ctx, + /* ignoreCoordinateEpoch = */ false); } // --------------------------------------------------------------------------- @@ -8075,7 +8190,8 @@ WKTParser::guessDialect(const std::string &inputWkt) noexcept { &WKTConstants::DYNAMIC, &WKTConstants::FRAMEEPOCH, &WKTConstants::MODEL, &WKTConstants::VELOCITYGRID, &WKTConstants::ENSEMBLE, &WKTConstants::DERIVEDPROJCRS, &WKTConstants::BASEPROJCRS, - &WKTConstants::GEOGRAPHICCRS, &WKTConstants::TRF, &WKTConstants::VRF}; + &WKTConstants::GEOGRAPHICCRS, &WKTConstants::TRF, &WKTConstants::VRF, + &WKTConstants::POINTMOTIONOPERATION}; for (const auto &pointerKeyword : wkt2_2019_only_keywords) { auto pos = ci_find(wkt, *pointerKeyword); diff --git a/src/iso19111/operation/concatenatedoperation.cpp b/src/iso19111/operation/concatenatedoperation.cpp index ea089ab75c..b3164e67c8 100644 --- a/src/iso19111/operation/concatenatedoperation.cpp +++ b/src/iso19111/operation/concatenatedoperation.cpp @@ -706,6 +706,8 @@ CoordinateOperationNNPtr ConcatenatedOperation::inverse() const { create(properties, inversedOperations, coordinateOperationAccuracies()); op->d->computedName_ = d->computedName_; op->setHasBallparkTransformation(hasBallparkTransformation()); + op->setSourceCoordinateEpoch(targetCoordinateEpoch()); + op->setTargetCoordinateEpoch(sourceCoordinateEpoch()); return op; } @@ -838,9 +840,33 @@ CoordinateOperationNNPtr ConcatenatedOperation::_shallowClone() const { void ConcatenatedOperation::_exportToPROJString( io::PROJStringFormatter *formatter) const // throw(FormattingException) { + double sourceYear = + sourceCoordinateEpoch().has_value() + ? getRoundedEpochInDecimalYear( + sourceCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)) + : 0; + double targetYear = + targetCoordinateEpoch().has_value() + ? getRoundedEpochInDecimalYear( + targetCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)) + : 0; + if (sourceYear > 0 && targetYear == 0) + targetYear = sourceYear; + else if (targetYear > 0 && sourceYear == 0) + sourceYear = targetYear; + if (sourceYear > 0) { + formatter->addStep("set"); + formatter->addParam("v_4", sourceYear); + } for (const auto &operation : operations()) { operation->_exportToPROJString(formatter); } + if (targetYear > 0) { + formatter->addStep("set"); + formatter->addParam("v_4", targetYear); + } } // --------------------------------------------------------------------------- diff --git a/src/iso19111/operation/coordinateoperationfactory.cpp b/src/iso19111/operation/coordinateoperationfactory.cpp index 0b21343ca9..7808be957b 100644 --- a/src/iso19111/operation/coordinateoperationfactory.cpp +++ b/src/iso19111/operation/coordinateoperationfactory.cpp @@ -109,6 +109,17 @@ namespace operation { static std::string objectAsStr(const common::IdentifiedObject *obj) { std::string ret(obj->nameStr()); const auto &ids = obj->identifiers(); + if (const auto *geogCRS = dynamic_cast(obj)) { + if (geogCRS->coordinateSystem()->axisList().size() == 3U) + ret += " (geographic3D)"; + else + ret += " (geographic2D)"; + } + if (const auto *geodCRS = dynamic_cast(obj)) { + if (geodCRS->isGeocentric()) { + ret += " (geocentric)"; + } + } if (!ids.empty()) { ret += " ("; ret += (*ids[0]->codeSpace()) + ":" + ids[0]->code(); @@ -561,7 +572,10 @@ struct CoordinateOperationFactory::Private { static std::vector createOperations(const crs::CRSNNPtr &sourceCRS, - const crs::CRSNNPtr &targetCRS, Context &context); + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, + Context &context); private: static constexpr bool disallowEmptyIntersection = true; @@ -590,7 +604,10 @@ struct CoordinateOperationFactory::Private { std::vector &res); static bool createOperationsFromDatabase( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, @@ -605,7 +622,10 @@ struct CoordinateOperationFactory::Private { static std::vector createOperationsGeogToVertWithIntermediateVert( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, const crs::VerticalCRS *vertDst, Context &context); static std::vector @@ -614,7 +634,10 @@ struct CoordinateOperationFactory::Private { Context &context); static void createOperationsFromDatabaseWithVertCRS( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, @@ -624,10 +647,13 @@ struct CoordinateOperationFactory::Private { const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, - std::vector &res); + std::vector &res, bool forceBallpark); static void createOperationsFromSphericalPlanetocentric( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeodeticCRS *geodSrc, std::vector &res); @@ -638,7 +664,10 @@ struct CoordinateOperationFactory::Private { std::vector &res); static void createOperationsDerivedTo( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::DerivedCRS *derivedSrc, std::vector &res); @@ -661,7 +690,10 @@ struct CoordinateOperationFactory::Private { std::vector &res); static void createOperationsVertToGeog( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::VerticalCRS *vertSrc, const crs::GeographicCRS *geogDst, std::vector &res); @@ -679,18 +711,28 @@ struct CoordinateOperationFactory::Private { std::vector &res); static void createOperationsCompoundToGeog( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::GeographicCRS *geogDst, std::vector &res); - static void createOperationsToGeod( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, - Private::Context &context, const crs::GeodeticCRS *geodDst, - std::vector &res); + static void + createOperationsToGeod(const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, + Private::Context &context, + const crs::GeodeticCRS *geodDst, + std::vector &res); static void createOperationsCompoundToCompound( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::CompoundCRS *compoundDst, std::vector &res); @@ -705,11 +747,14 @@ struct CoordinateOperationFactory::Private { std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeographicCRS *geogSrc, - const crs::GeographicCRS *geogDst); + const crs::GeographicCRS *geogDst, bool forceBallpark); static void createOperationsWithDatumPivot( std::vector &res, - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, Context &context); @@ -1893,53 +1938,16 @@ CoordinateOperationFactory::Private::findOpsInRegistryDirectTo( // --------------------------------------------------------------------------- -static std::vector +static std::list findCandidateGeodCRSForDatum(const io::AuthorityFactoryPtr &authFactory, const crs::GeodeticCRS *crs, - const datum::GeodeticReferenceFrame *datum) { - std::vector candidates; - assert(datum); - const auto &ids = datum->identifiers(); - const auto &datumName = datum->nameStr(); - if (!ids.empty()) { - for (const auto &id : ids) { - const auto &authName = *(id->codeSpace()); - const auto &code = id->code(); - if (!authName.empty()) { - const auto crsIds = crs->identifiers(); - const auto tmpFactory = - (crsIds.size() == 1 && - *(crsIds.front()->codeSpace()) == authName) - ? io::AuthorityFactory::create( - authFactory->databaseContext(), authName) - .as_nullable() - : authFactory; - auto l_candidates = tmpFactory->createGeodeticCRSFromDatum( - authName, code, std::string()); - for (const auto &candidate : l_candidates) { - candidates.emplace_back(candidate); - } - } - } - } else if (datumName != "unknown" && datumName != "unnamed") { - auto matches = authFactory->createObjectsFromName( - datumName, - {io::AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, false, - 2); - if (matches.size() == 1) { - const auto &match = matches.front(); - if (datum->_isEquivalentTo( - match.get(), util::IComparable::Criterion::EQUIVALENT, - authFactory->databaseContext().as_nullable()) && - !match->identifiers().empty()) { - return findCandidateGeodCRSForDatum( - authFactory, crs, - dynamic_cast( - match.get())); - } - } - } - return candidates; + const datum::GeodeticReferenceFrameNNPtr &datum) { + std::string preferredAuthName; + const auto crsIds = crs->identifiers(); + if (crsIds.size() == 1) + preferredAuthName = *(crsIds.front()->codeSpace()); + return authFactory->createGeodeticCRSFromDatum(datum, preferredAuthName, + std::string()); } // --------------------------------------------------------------------------- @@ -1985,7 +1993,7 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate( if (geodSrc) { const auto dbContext = authFactory->databaseContext().as_nullable(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( - authFactory, geodSrc, geodSrc->datumNonNull(dbContext).get())); + authFactory, geodSrc, geodSrc->datumNonNull(dbContext))); std::vector res; for (const auto &candidateSrcGeod : candidatesSrcGeod) { if (candidateSrcGeod->coordinateSystem()->axisList().size() == @@ -2010,7 +2018,9 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate( useCreateBetweenGeodeticCRSWithDatumBasedIntermediates); if (!opsWithIntermediate.empty()) { const auto opsFirst = createOperations( - sourceCRS, candidateSrcGeod, context); + sourceCRS, util::optional(), + candidateSrcGeod, + util::optional(), context); for (const auto &opFirst : opsFirst) { for (const auto &opSecond : opsWithIntermediate) { try { @@ -2140,17 +2150,16 @@ CoordinateOperationFactory::Private::findsOpsInRegistryWithIntermediate( // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -static TransformationNNPtr -createBallparkGeographicOffset(const crs::CRSNNPtr &sourceCRS, - const crs::CRSNNPtr &targetCRS, - const io::DatabaseContextPtr &dbContext) { +static TransformationNNPtr createBallparkGeographicOffset( + const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const io::DatabaseContextPtr &dbContext, bool forceBallpark) { const crs::GeographicCRS *geogSrc = dynamic_cast(sourceCRS.get()); const crs::GeographicCRS *geogDst = dynamic_cast(targetCRS.get()); const bool isSameDatum = - geogSrc && geogDst && + !forceBallpark && geogSrc && geogDst && isSameGeodeticDatum(geogSrc->datumNonNull(dbContext), geogDst->datumNonNull(dbContext), dbContext); @@ -2484,9 +2493,12 @@ createGeodToGeodPROJBased(const crs::CRSNNPtr &geodSrc, util::nn_dynamic_pointer_cast(geodSrc), util::nn_dynamic_pointer_cast(geodDst)); - auto properties = util::PropertyMap().set( - common::IdentifiedObject::NAME_KEY, - buildTransfName(geodSrc->nameStr(), geodDst->nameStr())); + auto properties = + util::PropertyMap() + .set(common::IdentifiedObject::NAME_KEY, + buildTransfName(geodSrc->nameStr(), geodDst->nameStr())) + .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, + metadata::Extent::WORLD); return createPROJBased(properties, exportable, geodSrc, geodDst, nullptr, {}, false); } @@ -2747,7 +2759,8 @@ std::vector CoordinateOperationFactory::Private::createOperationsGeogToGeog( std::vector &res, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, - const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst) { + const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, + bool forceBallpark) { assert(sourceCRS.get() == geogSrc); assert(targetCRS.get() == geogDst); @@ -2782,6 +2795,7 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog( authFactory ? authFactory->databaseContext().as_nullable() : nullptr; const bool sameDatum = + !forceBallpark && isSameGeodeticDatum(geogSrc->datumNonNull(dbContext), geogDst->datumNonNull(dbContext), dbContext); @@ -2877,8 +2891,8 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog( metadata::Extent::WORLD), datum, dstCS)); - steps.emplace_back( - createBallparkGeographicOffset(sourceCRS, interm_crs, dbContext)); + steps.emplace_back(createBallparkGeographicOffset( + sourceCRS, interm_crs, dbContext, forceBallpark)); steps.emplace_back(Transformation::createLongitudeRotation( util::PropertyMap() @@ -2913,10 +2927,10 @@ CoordinateOperationFactory::Private::createOperationsGeogToGeog( metadata::Extent::WORLD), sourceCRS, interm_crs, offset_pm)); steps.emplace_back(createBallparkGeographicOffset( - interm_crs, targetCRS, dbContext)); + interm_crs, targetCRS, dbContext, forceBallpark)); } else { steps.emplace_back(createBallparkGeographicOffset( - sourceCRS, targetCRS, dbContext)); + sourceCRS, targetCRS, dbContext, forceBallpark)); } } @@ -2949,17 +2963,19 @@ static bool hasIdentifiers(const CoordinateOperationNNPtr &op) { void CoordinateOperationFactory::Private::setCRSs( CoordinateOperation *co, const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS) { - co->setCRSs(sourceCRS, targetCRS, nullptr); + const auto &interpolationCRS = co->interpolationCRS(); + co->setCRSs(sourceCRS, targetCRS, interpolationCRS); auto invCO = dynamic_cast(co); if (invCO) { - invCO->forwardOperation()->setCRSs(targetCRS, sourceCRS, nullptr); + invCO->forwardOperation()->setCRSs(targetCRS, sourceCRS, + interpolationCRS); } auto transf = dynamic_cast(co); if (transf) { transf->inverseAsTransformation()->setCRSs(targetCRS, sourceCRS, - nullptr); + interpolationCRS); } auto concat = dynamic_cast(co); @@ -3012,8 +3028,11 @@ static bool hasResultSetOnlyResultsWithPROJStep( void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( std::vector &res, const crs::CRSNNPtr &sourceCRS, - const crs::CRSNNPtr &targetCRS, const crs::GeodeticCRS *geodSrc, - const crs::GeodeticCRS *geodDst, Private::Context &context) { + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, + const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, + Private::Context &context) { #ifdef TRACE_CREATE_OPERATIONS ENTER_BLOCK("createOperationsWithDatumPivot(" + @@ -3040,11 +3059,9 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( const auto &dbContext = authFactory->databaseContext(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( - authFactory, geodSrc, - geodSrc->datumNonNull(dbContext.as_nullable()).get())); + authFactory, geodSrc, geodSrc->datumNonNull(dbContext.as_nullable()))); const auto candidatesDstGeod(findCandidateGeodCRSForDatum( - authFactory, geodDst, - geodDst->datumNonNull(dbContext.as_nullable()).get())); + authFactory, geodDst, geodDst->datumNonNull(dbContext.as_nullable()))); const bool sourceAndTargetAre3D = geodSrc->coordinateSystem()->axisList().size() == 3 && @@ -3056,125 +3073,176 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( bool isNullFirst, bool useOnlyDirectRegistryOp) { bool resNonEmptyBeforeFiltering; + + // Deal with potential epoch change + std::vector opsEpochChangeSrc; + std::vector opsEpochChangeDst; + if (sourceEpoch.has_value() && targetEpoch.has_value() && + !sourceEpoch->coordinateEpoch()._isEquivalentTo( + targetEpoch->coordinateEpoch())) { + const auto pmoSrc = + context.context->getAuthorityFactory() + ->getPointMotionOperationsFor( + NN_NO_CHECK( + util::nn_dynamic_pointer_cast( + candidateSrcGeod)), + true); + if (!pmoSrc.empty()) { + opsEpochChangeSrc = + createOperations(candidateSrcGeod, sourceEpoch, + candidateSrcGeod, targetEpoch, context); + } else { + const auto pmoDst = + context.context->getAuthorityFactory() + ->getPointMotionOperationsFor( + NN_NO_CHECK( + util::nn_dynamic_pointer_cast( + candidateDstGeod)), + true); + if (!pmoDst.empty()) { + opsEpochChangeDst = createOperations( + candidateDstGeod, sourceEpoch, candidateDstGeod, + targetEpoch, context); + } + } + } + const auto opsSecond = useOnlyDirectRegistryOp ? findOpsInRegistryDirect(candidateSrcGeod, candidateDstGeod, context, resNonEmptyBeforeFiltering) - : createOperations(candidateSrcGeod, candidateDstGeod, context); + : createOperations(candidateSrcGeod, targetEpoch, + candidateDstGeod, targetEpoch, context); const auto opsThird = createOperations( sourceAndTargetAre3D ? candidateDstGeod->promoteTo3D(std::string(), dbContext) : candidateDstGeod, - targetCRS, context); + targetEpoch, targetCRS, targetEpoch, context); assert(!opsThird.empty()); const CoordinateOperationNNPtr &opThird(opsThird[0]); - for (auto &opSecond : opsSecond) { - // Check that it is not a transformation synthetized by - // ourselves - if (!hasIdentifiers(opSecond)) { - continue; - } - // And even if it is a referenced transformation, check that - // it is not a trivial one - auto so = dynamic_cast(opSecond.get()); - if (so && isAxisOrderReversal(so->method()->getEPSGCode())) { - continue; - } + const auto nIters = std::max( + 1, std::max(opsEpochChangeSrc.size(), opsEpochChangeDst.size())); + for (size_t iEpochChange = 0; iEpochChange < nIters; ++iEpochChange) { + for (auto &opSecond : opsSecond) { + // Check that it is not a transformation synthetized by + // ourselves + if (!hasIdentifiers(opSecond)) { + continue; + } + // And even if it is a referenced transformation, check that + // it is not a trivial one + auto so = dynamic_cast(opSecond.get()); + if (so && isAxisOrderReversal(so->method()->getEPSGCode())) { + continue; + } - std::vector subOps; - const bool isNullThird = isNullTransformation(opThird->nameStr()); - CoordinateOperationNNPtr opSecondCloned( - (isNullFirst || isNullThird || sourceAndTargetAre3D) - ? opSecond->shallowClone() - : opSecond); - if (isNullFirst || isNullThird) { - if (opSecondCloned->identifiers().size() == 1 && - (*opSecondCloned->identifiers()[0]->codeSpace()) - .find("DERIVED_FROM") == std::string::npos) { - { - util::PropertyMap map; - addModifiedIdentifier(map, opSecondCloned.get(), false, - true); - opSecondCloned->setProperties(map); - } - auto invCO = dynamic_cast( - opSecondCloned.get()); - if (invCO) { - auto invCOForward = invCO->forwardOperation().get(); - if (invCOForward->identifiers().size() == 1 && - (*invCOForward->identifiers()[0]->codeSpace()) - .find("DERIVED_FROM") == - std::string::npos) { + std::vector subOps; + const bool isNullThird = + isNullTransformation(opThird->nameStr()); + CoordinateOperationNNPtr opSecondCloned( + (isNullFirst || isNullThird || sourceAndTargetAre3D) + ? opSecond->shallowClone() + : opSecond); + if (isNullFirst || isNullThird) { + if (opSecondCloned->identifiers().size() == 1 && + (*opSecondCloned->identifiers()[0]->codeSpace()) + .find("DERIVED_FROM") == std::string::npos) { + { util::PropertyMap map; - addModifiedIdentifier(map, invCOForward, false, - true); - invCOForward->setProperties(map); + addModifiedIdentifier(map, opSecondCloned.get(), + false, true); + opSecondCloned->setProperties(map); + } + auto invCO = dynamic_cast( + opSecondCloned.get()); + if (invCO) { + auto invCOForward = invCO->forwardOperation().get(); + if (invCOForward->identifiers().size() == 1 && + (*invCOForward->identifiers()[0]->codeSpace()) + .find("DERIVED_FROM") == + std::string::npos) { + util::PropertyMap map; + addModifiedIdentifier(map, invCOForward, false, + true); + invCOForward->setProperties(map); + } } } } - } - if (sourceAndTargetAre3D) { - - // Force Helmert operations to use the 3D domain, even if the - // ones we found in EPSG are advertized for the 2D domain. - auto concat = - dynamic_cast(opSecondCloned.get()); - if (concat) { - std::vector newSteps; - for (const auto &step : concat->operations()) { - auto newStep = step->shallowClone(); - setCRSs(newStep.get(), - newStep->sourceCRS()->promoteTo3D(std::string(), - dbContext), - newStep->targetCRS()->promoteTo3D(std::string(), - dbContext)); - newSteps.emplace_back(newStep); + if (sourceAndTargetAre3D) { + + // Force Helmert operations to use the 3D domain, even if + // the ones we found in EPSG are advertized for the 2D + // domain. + auto concat = dynamic_cast( + opSecondCloned.get()); + if (concat) { + std::vector newSteps; + for (const auto &step : concat->operations()) { + auto newStep = step->shallowClone(); + setCRSs(newStep.get(), + newStep->sourceCRS()->promoteTo3D( + std::string(), dbContext), + newStep->targetCRS()->promoteTo3D( + std::string(), dbContext)); + newSteps.emplace_back(newStep); + } + opSecondCloned = + ConcatenatedOperation::createComputeMetadata( + newSteps, disallowEmptyIntersection); + } else { + setCRSs(opSecondCloned.get(), + opSecondCloned->sourceCRS()->promoteTo3D( + std::string(), dbContext), + opSecondCloned->targetCRS()->promoteTo3D( + std::string(), dbContext)); } - opSecondCloned = - ConcatenatedOperation::createComputeMetadata( - newSteps, disallowEmptyIntersection); - } else { - setCRSs(opSecondCloned.get(), - opSecondCloned->sourceCRS()->promoteTo3D( - std::string(), dbContext), - opSecondCloned->targetCRS()->promoteTo3D( - std::string(), dbContext)); } - } - if (isNullFirst) { - auto oldTarget(NN_CHECK_ASSERT(opSecondCloned->targetCRS())); - setCRSs(opSecondCloned.get(), sourceCRS, oldTarget); - } else { - subOps.emplace_back(opFirst); - } - if (isNullThird) { - auto oldSource(NN_CHECK_ASSERT(opSecondCloned->sourceCRS())); - setCRSs(opSecondCloned.get(), oldSource, targetCRS); - subOps.emplace_back(opSecondCloned); - } else { + if (!isNullFirst) { + subOps.emplace_back(opFirst); + } + if (!opsEpochChangeSrc.empty()) { + subOps.emplace_back(opsEpochChangeSrc[iEpochChange]); + } subOps.emplace_back(opSecondCloned); - subOps.emplace_back(opThird); - } + if (!opsEpochChangeDst.empty()) { + subOps.emplace_back(opsEpochChangeDst[iEpochChange]); + } + if (!isNullThird) { + subOps.emplace_back(opThird); + } + + subOps[0] = subOps[0]->shallowClone(); + if (subOps[0]->targetCRS()) + setCRSs(subOps[0].get(), sourceCRS, + NN_NO_CHECK(subOps[0]->targetCRS())); + subOps.back() = subOps.back()->shallowClone(); + if (subOps[0]->sourceCRS()) + setCRSs(subOps.back().get(), + NN_NO_CHECK(subOps.back()->sourceCRS()), targetCRS); + #ifdef TRACE_CREATE_OPERATIONS - std::string debugStr; - for (const auto &op : subOps) { - if (!debugStr.empty()) { - debugStr += " + "; + std::string debugStr; + for (const auto &op : subOps) { + if (!debugStr.empty()) { + debugStr += " + "; + } + debugStr += objectAsStr(op.get()); + debugStr += " ("; + debugStr += objectAsStr(op->sourceCRS().get()); + debugStr += "->"; + debugStr += objectAsStr(op->targetCRS().get()); + debugStr += ")"; } - debugStr += objectAsStr(op.get()); - debugStr += " ("; - debugStr += objectAsStr(op->sourceCRS().get()); - debugStr += "->"; - debugStr += objectAsStr(op->targetCRS().get()); - debugStr += ")"; - } - logTrace("transformation " + debugStr); + logTrace("transformation " + debugStr); #endif - try { - res.emplace_back(ConcatenatedOperation::createComputeMetadata( - subOps, disallowEmptyIntersection)); - } catch (const InvalidOperationEmptyIntersection &) { + try { + res.emplace_back( + ConcatenatedOperation::createComputeMetadata( + subOps, disallowEmptyIntersection)); + } catch (const InvalidOperationEmptyIntersection &) { + } } } }; @@ -3195,12 +3263,16 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( // If RGF93GEO is returned before then we go through WGS84 and use // instead a Helmert transformation. // - // Actually, in the general case, we do the lookup in 2 passes with the 2 + // Actually, in the general case, we do the lookup in 3 passes with the 2 // above steps in each pass: // - one first pass where we only consider direct transformations (no // other intermediate CRS) // - a second pass where we allow transformation through another - // intermediate CRS. + // intermediate CRS, but we make sure the candidate geodetic CRS are of + // the same type + // - a third where we allow transformation through another + // intermediate CRS, where the candidate geodetic CRS are of different + // type. // ... but when transforming between 2 IGNF CRS, we do just one single pass // by allowing directly all transformation. There is no strong reason for // that particular case, except that otherwise we'd get different results @@ -3216,25 +3288,49 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( const auto &ids = crs->identifiers(); return !ids.empty() && *(ids.front()->codeSpace()) == "IGNF"; }; - const int nIters = (isIGNF(sourceCRS) && isIGNF(targetCRS)) ? 1 : 2; + const int nIters = (isIGNF(sourceCRS) && isIGNF(targetCRS)) ? 1 : 3; + + const auto getType = [](const crs::GeodeticCRSNNPtr &crs) { + if (auto geogCRS = + dynamic_cast(crs.get())) { + if (geogCRS->coordinateSystem()->axisList().size() == 3) + return 1; + return 0; + } + return 2; + }; + for (int iter = 0; iter < nIters; ++iter) { - const bool useOnlyDirectRegistryOp = (iter == 0 && nIters == 2); + const bool useOnlyDirectRegistryOp = (iter == 0 && nIters == 3); for (const auto &candidateSrcGeod : candidatesSrcGeod) { if (candidateSrcGeod->nameStr() == sourceCRS->nameStr()) { + const auto typeSource = + (iter >= 1) ? getType(candidateSrcGeod) : -1; auto sourceSrcGeodModified(sourceAndTargetAre3D ? candidateSrcGeod->promoteTo3D( std::string(), dbContext) : candidateSrcGeod); for (const auto &candidateDstGeod : candidatesDstGeod) { if (candidateDstGeod->nameStr() == targetCRS->nameStr()) { + if (iter == 1) { + if (typeSource != getType(candidateDstGeod)) { + continue; + } + } else if (iter == 2) { + if (typeSource == getType(candidateDstGeod)) { + continue; + } + } #ifdef TRACE_CREATE_OPERATIONS - ENTER_BLOCK("try " + objectAsStr(sourceCRS.get()) + - "->" + objectAsStr(candidateSrcGeod.get()) + - "->" + objectAsStr(candidateDstGeod.get()) + - "->" + objectAsStr(targetCRS.get()) + ")"); + ENTER_BLOCK("iter=" + toString(iter) + ", try " + + objectAsStr(sourceCRS.get()) + "->" + + objectAsStr(candidateSrcGeod.get()) + "->" + + objectAsStr(candidateDstGeod.get()) + "->" + + objectAsStr(targetCRS.get()) + ")"); #endif const auto opsFirst = createOperations( - sourceCRS, sourceSrcGeodModified, context); + sourceCRS, sourceEpoch, sourceSrcGeodModified, + sourceEpoch, context); assert(!opsFirst.empty()); const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); @@ -3263,7 +3359,8 @@ void CoordinateOperationFactory::Private::createOperationsWithDatumPivot( ? candidateSrcGeod->promoteTo3D(std::string(), dbContext) : candidateSrcGeod); const auto opsFirst = - createOperations(sourceCRS, sourceSrcGeodModified, context); + createOperations(sourceCRS, sourceEpoch, sourceSrcGeodModified, + sourceEpoch, context); assert(!opsFirst.empty()); const bool isNullFirst = isNullTransformation(opsFirst[0]->nameStr()); @@ -3331,7 +3428,10 @@ bool CoordinateOperationFactory::Private::hasPerfectAccuracyResult( std::vector CoordinateOperationFactory::Private::createOperations( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context) { #ifdef TRACE_CREATE_OPERATIONS @@ -3400,25 +3500,27 @@ CoordinateOperationFactory::Private::createOperations( !derivedDst->baseCRS()->_isEquivalentTo( sourceCRS.get(), util::IComparable::Criterion::EQUIVALENT))) { - if (createOperationsFromDatabase(sourceCRS, targetCRS, context, geodSrc, - geodDst, geogSrc, geogDst, vertSrc, - vertDst, res)) { + if (createOperationsFromDatabase( + sourceCRS, sourceEpoch, targetCRS, targetEpoch, context, + geodSrc, geodDst, geogSrc, geogDst, vertSrc, vertDst, res)) { return res; } } if (geodSrc && geodSrc->isSphericalPlanetocentric()) { - createOperationsFromSphericalPlanetocentric(sourceCRS, targetCRS, + createOperationsFromSphericalPlanetocentric(sourceCRS, sourceEpoch, + targetCRS, targetEpoch, context, geodSrc, res); return res; } else if (geodDst && geodDst->isSphericalPlanetocentric()) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } // Special case if both CRS are geodetic if (geodSrc && geodDst && !derivedSrc && !derivedDst) { createOperationsGeodToGeod(sourceCRS, targetCRS, context, geodSrc, - geodDst, res); + geodDst, res, /*forceBallpark=*/false); return res; } @@ -3435,8 +3537,8 @@ CoordinateOperationFactory::Private::createOperations( auto geodDstBase = util::nn_dynamic_pointer_cast( boundDst->baseCRS()); if (geodDstBase && geodDstBase->isSphericalPlanetocentric()) { - return applyInverse( - createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations( + targetCRS, targetEpoch, sourceCRS, sourceEpoch, context)); } } @@ -3444,14 +3546,15 @@ CoordinateOperationFactory::Private::createOperations( // deriving conversion, with transforms from its baseCRS to the // targetCRS if (derivedSrc) { - createOperationsDerivedTo(sourceCRS, targetCRS, context, derivedSrc, - res); + createOperationsDerivedTo(sourceCRS, sourceEpoch, targetCRS, + targetEpoch, context, derivedSrc, res); return res; } // reverse of previous case if (derivedDst) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } // Order of comparison between the geogDst vs geodDst is important @@ -3460,13 +3563,15 @@ CoordinateOperationFactory::Private::createOperations( geogDst, res); return res; } else if (boundSrc && geodDst) { - createOperationsToGeod(sourceCRS, targetCRS, context, geodDst, res); + createOperationsToGeod(sourceCRS, sourceEpoch, targetCRS, targetEpoch, + context, geodDst, res); return res; } // reverse of previous case if (geodSrc && boundDst) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } // vertCRS (as boundCRS with transformation to target vertCRS) to @@ -3479,7 +3584,8 @@ CoordinateOperationFactory::Private::createOperations( // reverse of previous case if (boundDst && vertSrc) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } if (vertSrc && vertDst) { @@ -3491,14 +3597,15 @@ CoordinateOperationFactory::Private::createOperations( // A bit odd case as we are comparing apples to oranges, but in case // the vertical unit differ, do something useful. if (vertSrc && geogDst) { - createOperationsVertToGeog(sourceCRS, targetCRS, context, vertSrc, - geogDst, res); + createOperationsVertToGeog(sourceCRS, sourceEpoch, targetCRS, + targetEpoch, context, vertSrc, geogDst, res); return res; } // reverse of previous case if (vertDst && geogSrc) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } // boundCRS to boundCRS @@ -3511,23 +3618,27 @@ CoordinateOperationFactory::Private::createOperations( auto compoundSrc = dynamic_cast(sourceCRS.get()); // Order of comparison between the geogDst vs geodDst is important if (compoundSrc && geogDst) { - createOperationsCompoundToGeog(sourceCRS, targetCRS, context, - compoundSrc, geogDst, res); + createOperationsCompoundToGeog(sourceCRS, sourceEpoch, targetCRS, + targetEpoch, context, compoundSrc, + geogDst, res); return res; } else if (compoundSrc && geodDst) { - createOperationsToGeod(sourceCRS, targetCRS, context, geodDst, res); + createOperationsToGeod(sourceCRS, sourceEpoch, targetCRS, targetEpoch, + context, geodDst, res); return res; } // reverse of previous cases auto compoundDst = dynamic_cast(targetCRS.get()); if (geodSrc && compoundDst) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } if (compoundSrc && compoundDst) { - createOperationsCompoundToCompound(sourceCRS, targetCRS, context, - compoundSrc, compoundDst, res); + createOperationsCompoundToCompound(sourceCRS, sourceEpoch, targetCRS, + targetEpoch, context, compoundSrc, + compoundDst, res); return res; } @@ -3542,7 +3653,8 @@ CoordinateOperationFactory::Private::createOperations( // reverse of previous case if (boundDst && compoundSrc) { - return applyInverse(createOperations(targetCRS, sourceCRS, context)); + return applyInverse(createOperations(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context)); } return res; @@ -3602,7 +3714,10 @@ void CoordinateOperationFactory::Private::createOperationsFromProj4Ext( // --------------------------------------------------------------------------- bool CoordinateOperationFactory::Private::createOperationsFromDatabase( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeodeticCRS *geodSrc, const crs::GeodeticCRS *geodDst, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, @@ -3612,9 +3727,9 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( ENTER_FUNCTION(); if (geogSrc && vertDst) { - createOperationsFromDatabase(targetCRS, sourceCRS, context, geodDst, - geodSrc, geogDst, geogSrc, vertDst, - vertSrc, res); + createOperationsFromDatabase(targetCRS, targetEpoch, sourceCRS, + sourceEpoch, context, geodDst, geodSrc, + geogDst, geogSrc, vertDst, vertSrc, res); res = applyInverse(res); } else if (geogDst && vertSrc) { res = applyInverse(createOperationsGeogToVertFromGeoid( @@ -3629,6 +3744,71 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( return true; } + // Use PointMotionOperations if appropriate and available + if (geodSrc && geodDst && sourceEpoch.has_value() && + targetEpoch.has_value() && + !sourceEpoch->coordinateEpoch()._isEquivalentTo( + targetEpoch->coordinateEpoch())) { + const auto pmoSrc = + context.context->getAuthorityFactory()->getPointMotionOperationsFor( + NN_NO_CHECK( + util::nn_dynamic_pointer_cast(sourceCRS)), + true); + if (!pmoSrc.empty()) { + const auto pmoDst = + context.context->getAuthorityFactory() + ->getPointMotionOperationsFor( + NN_NO_CHECK( + util::nn_dynamic_pointer_cast( + targetCRS)), + true); + if (pmoDst.size() == pmoSrc.size()) { + bool ok = true; + for (size_t i = 0; i < pmoSrc.size(); ++i) { + if (pmoSrc[i]->_isEquivalentTo(pmoDst[i].get())) { + auto pmo = pmoSrc[i]->cloneWithEpochs(*sourceEpoch, + *targetEpoch); + std::vector ops; + if (!pmo->sourceCRS()->_isEquivalentTo( + sourceCRS.get(), + util::IComparable::Criterion::EQUIVALENT)) { + auto tmp = createOperations(sourceCRS, sourceEpoch, + pmo->sourceCRS(), + sourceEpoch, context); + assert(!tmp.empty()); + ops.emplace_back(tmp.front()); + } + ops.emplace_back(pmo); + // pmo->sourceCRS() == pmo->targetCRS() by definition + if (!pmo->sourceCRS()->_isEquivalentTo( + targetCRS.get(), + util::IComparable::Criterion::EQUIVALENT)) { + auto tmp = createOperations(pmo->sourceCRS(), + targetEpoch, targetCRS, + targetEpoch, context); + assert(!tmp.empty()); + ops.emplace_back(tmp.front()); + } + res.emplace_back( + ConcatenatedOperation::createComputeMetadata( + ops, disallowEmptyIntersection)); + } else { + ok = false; + break; + } + } + if (ok) { + std::vector resTmp; + createOperationsGeodToGeod(sourceCRS, targetCRS, context, + geodSrc, geodDst, resTmp, + /*forceBallpark=*/true); + res.insert(res.end(), resTmp.begin(), resTmp.end()); + return true; + } + } + } + } + bool resFindDirectNonEmptyBeforeFiltering = false; res = findOpsInRegistryDirect(sourceCRS, targetCRS, context, resFindDirectNonEmptyBeforeFiltering); @@ -3674,9 +3854,9 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( } } if (res.empty()) { - createOperationsFromDatabaseWithVertCRS(sourceCRS, targetCRS, - context, geogSrc, geogDst, - vertSrc, vertDst, res); + createOperationsFromDatabaseWithVertCRS( + sourceCRS, sourceEpoch, targetCRS, targetEpoch, context, + geogSrc, geogDst, vertSrc, vertDst, res); } } else if (geodSrc && geodDst) { @@ -3699,7 +3879,8 @@ bool CoordinateOperationFactory::Private::createOperationsFromDatabase( // GeographicCRS, // but transformations are only available between their // corresponding geocentric CRS. - createOperationsWithDatumPivot(res, sourceCRS, targetCRS, geodSrc, + createOperationsWithDatumPivot(res, sourceCRS, sourceEpoch, + targetCRS, targetEpoch, geodSrc, geodDst, context); doFilterAndCheckPerfectOp = !res.empty(); } @@ -3851,7 +4032,7 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid( opsUnitConvert, tmpCRSWithSrcZ, NN_NO_CHECK(op->sourceCRS()), context, dynamic_cast(tmpCRSWithSrcZ.get()), - opSourceCRSGeog); + opSourceCRSGeog, /*forceBallpark=*/false); assert(opsUnitConvert.size() == 1); opPtr = opsUnitConvert.front().as_nullable(); } @@ -3999,8 +4180,19 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid( const auto &models = vertDst->geoidModel(); for (const auto &model : models) { const auto &modelName = model->nameStr(); + const auto &modelIds = model->identifiers(); const auto transformations = - starts_with(modelName, "PROJ ") + !modelIds.empty() + ? std::vector< + CoordinateOperationNNPtr>{io::AuthorityFactory::create( + authFactory + ->databaseContext(), + *(modelIds[0] + ->codeSpace())) + ->createCoordinateOperation( + modelIds[0]->code(), + true)} + : starts_with(modelName, "PROJ ") ? std::vector< CoordinateOperationNNPtr>{getProjGeoidTransformation( model, modelName.substr(strlen("PROJ ")))} @@ -4030,7 +4222,10 @@ CoordinateOperationFactory::Private::createOperationsGeogToVertFromGeoid( std::vector CoordinateOperationFactory::Private:: createOperationsGeogToVertWithIntermediateVert( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, const crs::VerticalCRS *vertDst, Private::Context &context) { ENTER_FUNCTION(); @@ -4056,10 +4251,11 @@ std::vector CoordinateOperationFactory::Private:: auto candidatesVert = findCandidateVertCRSForDatum( authFactory, vertDst->datumNonNull(dbContext).get()); for (const auto &candidateVert : candidatesVert) { - auto resTmp = createOperations(sourceCRS, candidateVert, context); + auto resTmp = createOperations(sourceCRS, sourceEpoch, candidateVert, + sourceEpoch, context); if (!resTmp.empty()) { - const auto opsSecond = - createOperations(candidateVert, targetCRS, context); + const auto opsSecond = createOperations( + candidateVert, sourceEpoch, targetCRS, targetEpoch, context); if (!opsSecond.empty()) { // The transformation from candidateVert to targetCRS should // be just a unit change typically, so take only the first one, @@ -4141,7 +4337,7 @@ std::vector CoordinateOperationFactory::Private:: NN_NO_CHECK(op->sourceCRS()), context, dynamic_cast( tmpCRSWithSrcZ.get()), - tmpCRS); + tmpCRS, /*forceBallpark=*/false); assert(opsUnitConvert.size() == 1); auto concat = ConcatenatedOperation::createComputeMetadata( {opsUnitConvert.front(), op}, disallowEmptyIntersection); @@ -4159,7 +4355,10 @@ std::vector CoordinateOperationFactory::Private:: void CoordinateOperationFactory::Private:: createOperationsFromDatabaseWithVertCRS( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeographicCRS *geogSrc, const crs::GeographicCRS *geogDst, const crs::VerticalCRS *vertSrc, const crs::VerticalCRS *vertDst, @@ -4171,12 +4370,12 @@ void CoordinateOperationFactory::Private:: !context.inCreateOperationsGeogToVertWithIntermediateVert && geogSrc && vertDst) { res = createOperationsGeogToVertWithIntermediateVert( - sourceCRS, targetCRS, vertDst, context); + sourceCRS, sourceEpoch, targetCRS, targetEpoch, vertDst, context); } else if (res.empty() && !context.inCreateOperationsGeogToVertWithIntermediateVert && geogDst && vertSrc) { res = applyInverse(createOperationsGeogToVertWithIntermediateVert( - targetCRS, sourceCRS, vertSrc, context)); + targetCRS, targetEpoch, sourceCRS, sourceEpoch, vertSrc, context)); } // NAD83 only exists in 2D version in EPSG, so if it has been @@ -4192,7 +4391,7 @@ void CoordinateOperationFactory::Private:: const auto &dbContext = authFactory->databaseContext(); const auto candidatesSrcGeod(findCandidateGeodCRSForDatum( authFactory, geogSrcIn, - geogSrcIn->datumNonNull(dbContext).get())); + geogSrcIn->datumNonNull(dbContext))); for (const auto &candidate : candidatesSrcGeod) { auto geogCandidate = util::nn_dynamic_pointer_cast( @@ -4239,8 +4438,8 @@ void CoordinateOperationFactory::Private:: void CoordinateOperationFactory::Private::createOperationsGeodToGeod( const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, Private::Context &context, const crs::GeodeticCRS *geodSrc, - const crs::GeodeticCRS *geodDst, - std::vector &res) { + const crs::GeodeticCRS *geodDst, std::vector &res, + bool forceBallpark) { ENTER_FUNCTION(); @@ -4275,7 +4474,7 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( if (geogSrc && geogDst) { createOperationsGeogToGeog(res, sourceCRS, targetCRS, context, geogSrc, - geogDst); + geogDst, forceBallpark); return; } @@ -4300,8 +4499,14 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( // Same datum ? if (IsSameDatum()) { - res.emplace_back( - Conversion::createGeographicGeocentric(sourceCRS, targetCRS)); + if (forceBallpark) { + auto op = createGeodToGeodPROJBased(sourceCRS, targetCRS); + op->setHasBallparkTransformation(true); + res.emplace_back(op); + } else { + res.emplace_back(Conversion::createGeographicGeocentric( + sourceCRS, targetCRS)); + } } else if (isSrcGeocentric && geogDst) { #if 0 // The below logic was used between PROJ >= 6.0 and < 9.2 @@ -4342,7 +4547,9 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( common::UnitOfMeasure::METRE))); auto opFirst = Conversion::createGeographicGeocentric(sourceCRS, interm_crs); - auto opsSecond = createOperations(interm_crs, targetCRS, context); + auto opsSecond = createOperations( + interm_crs, util::optional(), targetCRS, + util::optional(), context); for (const auto &opSecond : opsSecond) { try { res.emplace_back( @@ -4356,7 +4563,7 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( // Apply previous case in reverse way std::vector resTmp; createOperationsGeodToGeod(targetCRS, sourceCRS, context, geodDst, - geodSrc, resTmp); + geodSrc, resTmp, forceBallpark); resTmp = applyInverse(resTmp); res.insert(res.end(), resTmp.begin(), resTmp.end()); } @@ -4365,9 +4572,10 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( } if (isSrcGeocentric && isTargetGeocentric) { - if (sourceCRS->_isEquivalentTo( - targetCRS.get(), util::IComparable::Criterion::EQUIVALENT) || - IsSameDatum()) { + if (!forceBallpark && + (sourceCRS->_isEquivalentTo( + targetCRS.get(), util::IComparable::Criterion::EQUIVALENT) || + IsSameDatum())) { std::string name(NULL_GEOCENTRIC_TRANSLATION); name += " from "; name += sourceCRS->nameStr(); @@ -4396,7 +4604,10 @@ void CoordinateOperationFactory::Private::createOperationsGeodToGeod( void CoordinateOperationFactory::Private:: createOperationsFromSphericalPlanetocentric( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeodeticCRS *geodSrc, std::vector &res) { @@ -4435,7 +4646,8 @@ void CoordinateOperationFactory::Private:: auto opFirst = Conversion::createGeographicGeocentricLatitude(sourceCRS, interm_crs); - auto opsSecond = createOperations(interm_crs, targetCRS, context); + auto opsSecond = createOperations(interm_crs, sourceEpoch, targetCRS, + targetEpoch, context); for (const auto &opSecond : opsSecond) { try { res.emplace_back(ConcatenatedOperation::createComputeMetadata( @@ -4483,7 +4695,9 @@ void CoordinateOperationFactory::Private:: auto opFirst = Conversion::createGeographicGeocentricLatitude(geodSrcBase, intermGeog); setCRSs(opFirst.get(), sourceCRS, intermBoundCRS); - auto opsSecond = createOperations(intermBoundCRS, targetCRS, context); + auto opsSecond = createOperations( + intermBoundCRS, util::optional(), targetCRS, + util::optional(), context); for (const auto &opSecond : opsSecond) { try { auto opSecondClone = opSecond->shallowClone(); @@ -4501,7 +4715,10 @@ void CoordinateOperationFactory::Private:: // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsDerivedTo( - const crs::CRSNNPtr & /*sourceCRS*/, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr & /*sourceCRS*/, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::DerivedCRS *derivedSrc, std::vector &res) { @@ -4515,8 +4732,8 @@ void CoordinateOperationFactory::Private::createOperationsDerivedTo( res.emplace_back(opFirst); return; } - auto opsSecond = - createOperations(derivedSrc->baseCRS(), targetCRS, context); + auto opsSecond = createOperations(derivedSrc->baseCRS(), sourceEpoch, + targetCRS, targetEpoch, context); for (const auto &opSecond : opsSecond) { try { res.emplace_back(ConcatenatedOperation::createComputeMetadata( @@ -4568,7 +4785,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( if (geogCRSOfBaseOfBoundSrcDatum->_isEquivalentTo( geogDstDatum.get(), util::IComparable::Criterion::EQUIVALENT, dbContext)) { - res = createOperations(boundSrc->baseCRS(), targetCRS, context); + res = createOperations( + boundSrc->baseCRS(), util::optional(), + targetCRS, util::optional(), context); return; } } @@ -4585,9 +4804,11 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( if (!geogCRSOfBaseOfBoundSrc->_isEquivalentTo( boundSrc->transformation()->sourceCRS().get(), util::IComparable::Criterion::EQUIVALENT)) { - auto opsIntermediate = createOperations( - NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), - boundSrc->transformation()->sourceCRS(), context); + auto opsIntermediate = + createOperations(NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), + util::optional(), + boundSrc->transformation()->sourceCRS(), + util::optional(), context); assert(!opsIntermediate.empty()); opIntermediate = opsIntermediate.front(); } @@ -4610,7 +4831,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( return; } auto opsFirst = createOperations( - boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); + boundSrc->baseCRS(), util::optional(), + NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), + util::optional(), context); if (!opsFirst.empty()) { for (const auto &opFirst : opsFirst) { try { @@ -4636,8 +4859,12 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( geogDstDatum.get(), util::IComparable::Criterion::EQUIVALENT)) { auto opsFirst = createOperations( - boundSrc->baseCRS(), NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), context); - auto opsLast = createOperations(hubSrc, targetCRS, context); + boundSrc->baseCRS(), util::optional(), + NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), + util::optional(), context); + auto opsLast = createOperations( + hubSrc, util::optional(), targetCRS, + util::optional(), context); if (!opsFirst.empty() && !opsLast.empty()) { CoordinateOperationPtr opIntermediate; if (!geogCRSOfBaseOfBoundSrc->_isEquivalentTo( @@ -4645,7 +4872,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( util::IComparable::Criterion::EQUIVALENT)) { auto opsIntermediate = createOperations( NN_NO_CHECK(geogCRSOfBaseOfBoundSrc), - boundSrc->transformation()->sourceCRS(), context); + util::optional(), + boundSrc->transformation()->sourceCRS(), + util::optional(), context); assert(!opsIntermediate.empty()); opIntermediate = opsIntermediate.front(); } @@ -4699,7 +4928,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( return; } else { auto opsFirst = createOperations( - boundSrc->baseCRS(), nnGeogCRSOfBaseOfBoundSrc, context); + boundSrc->baseCRS(), util::optional(), + nnGeogCRSOfBaseOfBoundSrc, util::optional(), + context); auto transf = boundSrc->transformation()->shallowClone(); transf->setProperties(util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, @@ -4730,8 +4961,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( if (dynamic_cast(transfSrc.get()) && !boundSrc->baseCRS()->_isEquivalentTo( transfSrc.get(), util::IComparable::Criterion::EQUIVALENT)) { - auto opsFirst = - createOperations(boundSrc->baseCRS(), transfSrc, context); + auto opsFirst = createOperations( + boundSrc->baseCRS(), util::optional(), + transfSrc, util::optional(), context); for (const auto &opFirst : opsFirst) { try { res.emplace_back( @@ -4752,8 +4984,12 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( geogCRSOfBaseOfBoundSrc) { // This one should go to the above 'Is it: boundCRS to a geogCRS // that is the same as the hubCRS ?' case - auto opsFirst = createOperations(sourceCRS, hubSrc, context); - auto opsLast = createOperations(hubSrc, targetCRS, context); + auto opsFirst = createOperations( + sourceCRS, util::optional(), hubSrc, + util::optional(), context); + auto opsLast = createOperations( + hubSrc, util::optional(), targetCRS, + util::optional(), context); if (!opsFirst.empty() && !opsLast.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { @@ -4788,7 +5024,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( // GeographicCRS if (vertCRSOfBaseOfBoundSrc && hubSrcGeog && dynamic_cast(hubSrcGeog) == nullptr) { - auto opsFirst = createOperations(sourceCRS, hubSrc, context); + auto opsFirst = createOperations( + sourceCRS, util::optional(), hubSrc, + util::optional(), context); if (context.skipHorizontalTransformation) { if (!opsFirst.empty()) { const auto &hubAxisList = @@ -4845,7 +5083,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( } return; } else { - auto opsSecond = createOperations(hubSrc, targetCRS, context); + auto opsSecond = createOperations( + hubSrc, util::optional(), targetCRS, + util::optional(), context); if (!opsFirst.empty() && !opsSecond.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsSecond) { @@ -4874,7 +5114,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog( } } - res = createOperations(boundSrc->baseCRS(), targetCRS, context); + res = createOperations(boundSrc->baseCRS(), + util::optional(), targetCRS, + util::optional(), context); } // --------------------------------------------------------------------------- @@ -4898,7 +5140,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToVert( return; } - res = createOperations(boundSrc->baseCRS(), targetCRS, context); + res = createOperations(boundSrc->baseCRS(), + util::optional(), targetCRS, + util::optional(), context); } // --------------------------------------------------------------------------- @@ -4991,7 +5235,10 @@ void CoordinateOperationFactory::Private::createOperationsVertToVert( // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsVertToGeog( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::VerticalCRS *vertSrc, const crs::GeographicCRS *geogDst, std::vector &res) { @@ -5016,7 +5263,7 @@ void CoordinateOperationFactory::Private::createOperationsVertToGeog( NN_NO_CHECK( util::nn_dynamic_pointer_cast( match)), - targetCRS, context); + sourceEpoch, targetCRS, targetEpoch, context); res.insert(res.end(), resTmp.begin(), resTmp.end()); return; } @@ -5101,8 +5348,12 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( if (hubSrcGeog && hubDstGeog && hubSrcGeog->_isEquivalentTo(hubDstGeog, util::IComparable::Criterion::EQUIVALENT)) { - auto opsFirst = createOperations(sourceCRS, hubSrc, context); - auto opsLast = createOperations(hubSrc, targetCRS, context); + auto opsFirst = createOperations( + sourceCRS, util::optional(), hubSrc, + util::optional(), context); + auto opsLast = createOperations( + hubSrc, util::optional(), targetCRS, + util::optional(), context); for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { try { @@ -5141,8 +5392,10 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( boundSrc->transformation()->_isEquivalentTo( boundDst->transformation().get(), util::IComparable::Criterion::EQUIVALENT))) { - res = createOperations(boundSrc->baseCRS(), boundDst->baseCRS(), - context); + res = createOperations( + boundSrc->baseCRS(), util::optional(), + boundDst->baseCRS(), util::optional(), + context); return; } } @@ -5154,8 +5407,12 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( hubSrcGeog->_isEquivalentTo(hubDstGeog, util::IComparable::Criterion::EQUIVALENT) && vertCRSOfBaseOfBoundSrc && vertCRSOfBaseOfBoundDst) { - auto opsFirst = createOperations(sourceCRS, hubSrc, context); - auto opsLast = createOperations(hubSrc, targetCRS, context); + auto opsFirst = createOperations( + sourceCRS, util::optional(), hubSrc, + util::optional(), context); + auto opsLast = createOperations( + hubSrc, util::optional(), targetCRS, + util::optional(), context); if (!opsFirst.empty() && !opsLast.empty()) { for (const auto &opFirst : opsFirst) { for (const auto &opLast : opsLast) { @@ -5173,7 +5430,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToBound( } } - res = createOperations(boundSrc->baseCRS(), boundDst->baseCRS(), context); + res = createOperations( + boundSrc->baseCRS(), util::optional(), + boundDst->baseCRS(), util::optional(), context); } // --------------------------------------------------------------------------- @@ -5321,7 +5580,10 @@ getInterpolationGeogCRS(const CoordinateOperationNNPtr &verticalTransform, // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::GeographicCRS *geogDst, std::vector &res) { @@ -5332,6 +5594,10 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( const auto &componentsSrc = compoundSrc->componentReferenceSystems(); if (!componentsSrc.empty()) { + const auto dbContext = + authFactory ? authFactory->databaseContext().as_nullable() + : nullptr; + if (componentsSrc.size() == 2) { auto derivedHSrc = dynamic_cast(componentsSrc[0].get()); @@ -5345,10 +5611,12 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( auto intermCompound = crs::CompoundCRS::create(properties, intermComponents); auto opsFirst = - createOperations(sourceCRS, intermCompound, context); + createOperations(sourceCRS, sourceEpoch, intermCompound, + sourceEpoch, context); assert(!opsFirst.empty()); auto opsLast = - createOperations(intermCompound, targetCRS, context); + createOperations(intermCompound, sourceEpoch, targetCRS, + targetEpoch, context); for (const auto &opLast : opsLast) { try { res.emplace_back( @@ -5361,6 +5629,82 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( return; } + auto geogSrc = dynamic_cast( + componentsSrc[0].get()); + // Sorry for this hack... aimed at transforming + // "NAD83(CSRS)v7 + CGVD2013a(1997) height @ 1997" to "NAD83(CSRS)v7 + // @ 1997" to "NAD83(CSRS)v3 + CGVD2013a(1997) height" to + // "NAD83(CSRS)v3" OR "NAD83(CSRS)v7 + CGVD2013a(2002) height @ + // 2002" to "NAD83(CSRS)v7 @ 2002" to "NAD83(CSRS)v4 + + // CGVD2013a(2002) height" to "NAD83(CSRS)v4" + if (dbContext && geogSrc && geogSrc->nameStr() == "NAD83(CSRS)v7" && + sourceEpoch.has_value() && + geogDst->coordinateSystem()->axisList().size() == 3U && + geogDst->nameStr() == geogSrc->nameStr() && + targetEpoch.has_value() && + sourceEpoch->coordinateEpoch()._isEquivalentTo( + targetEpoch->coordinateEpoch())) { + const bool is1997 = + std::abs(sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR) - + 1997) < 1e-10; + const bool is2002 = + std::abs(sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR) - + 2002) < 1e-10; + try { + auto authFactoryEPSG = io::AuthorityFactory::create( + authFactory->databaseContext(), "EPSG"); + if (geogSrc->_isEquivalentTo( + authFactoryEPSG + ->createCoordinateReferenceSystem("8255") + .get(), + util::IComparable::Criterion:: + EQUIVALENT) && // NAD83(CSRS)v7 + // 2D + geogDst->_isEquivalentTo( + authFactoryEPSG + ->createCoordinateReferenceSystem("8254") + .get(), + util::IComparable::Criterion:: + EQUIVALENT) && // NAD83(CSRS)v7 + // 3D + ((is1997 && componentsSrc[1]->nameStr() == + "CGVD2013a(1997) height") || + (is2002 && componentsSrc[1]->nameStr() == + "CGVD2013a(2002) height"))) { + const auto newGeogCRS_2D( + authFactoryEPSG->createCoordinateReferenceSystem( + is1997 ? "8240" : // NAD83(CSRS)v3 2D + "8246" // NAD83(CSRS)v4 2D + )); + const auto newGeogCRS_3D( + authFactoryEPSG->createCoordinateReferenceSystem( + is1997 ? "8239" : // NAD83(CSRS)v3 3D + "8244" // NAD83(CSRS)v4 3D + )); + std::vector intermComponents{ + newGeogCRS_2D, componentsSrc[1]}; + auto properties = util::PropertyMap().set( + common::IdentifiedObject::NAME_KEY, + intermComponents[0]->nameStr() + " + " + + intermComponents[1]->nameStr()); + auto newCompound = crs::CompoundCRS::create( + properties, intermComponents); + auto ops = createOperations(newCompound, sourceEpoch, + newGeogCRS_3D, sourceEpoch, + context); + for (const auto &op : ops) { + auto opClone = op->shallowClone(); + setCRSs(opClone.get(), sourceCRS, targetCRS); + res.emplace_back(opClone); + } + return; + } + } catch (const std::exception &) { + } + } + auto boundSrc = dynamic_cast(componentsSrc[0].get()); if (boundSrc) { @@ -5379,10 +5723,12 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( auto intermCompound = crs::CompoundCRS::create(properties, intermComponents); auto opsFirst = - createOperations(sourceCRS, intermCompound, context); + createOperations(sourceCRS, sourceEpoch, intermCompound, + sourceEpoch, context); assert(!opsFirst.empty()); auto opsLast = - createOperations(intermCompound, targetCRS, context); + createOperations(intermCompound, sourceEpoch, targetCRS, + targetEpoch, context); for (const auto &opLast : opsLast) { try { res.emplace_back( @@ -5397,10 +5743,6 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( } } - const auto dbContext = - authFactory ? authFactory->databaseContext().as_nullable() - : nullptr; - // Deal with "+proj=something +geoidgrids +nadgrids/+towgs84" to // another CRS whose datum is not the same as the horizontal datum // of the source @@ -5428,7 +5770,8 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( util::PropertyMap().set( common::IdentifiedObject::NAME_KEY, std::string()), {comp0Bound->baseCRS(), componentsSrc[1]}), - op1Dest, context); + util::optional(), op1Dest, + util::optional(), context); const auto op2Dest = comp0Bound->hubCRS()->promoteTo3D(std::string(), dbContext); @@ -5439,9 +5782,12 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( NN_NO_CHECK(comp0Geog), comp0Bound->hubCRS(), comp0Bound->transformation()) ->promoteTo3D(std::string(), dbContext), - op2Dest, context); + util::optional(), op2Dest, + util::optional(), context); - const auto ops3 = createOperations(op2Dest, targetCRS, context); + const auto ops3 = createOperations( + op2Dest, util::optional(), targetCRS, + util::optional(), context); for (const auto &op1 : ops1) { auto op1Clone = op1->shallowClone(); @@ -5471,7 +5817,8 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( const auto dstSingle = dynamic_cast(targetCRS.get()); if (dstSingle && dstSingle->coordinateSystem()->axisList().size() == 2) { - auto tmp = createOperations(componentsSrc[0], targetCRS, context); + auto tmp = createOperations(componentsSrc[0], sourceEpoch, + targetCRS, targetEpoch, context); for (const auto &op : tmp) { auto opClone = op->shallowClone(); setCRSs(opClone.get(), sourceCRS, targetCRS); @@ -5483,8 +5830,8 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( std::vector horizTransforms; auto srcGeogCRS = componentsSrc[0]->extractGeographicCRS(); if (srcGeogCRS) { - horizTransforms = - createOperations(componentsSrc[0], targetCRS, context); + horizTransforms = createOperations(componentsSrc[0], sourceEpoch, + targetCRS, targetEpoch, context); } std::vector verticalTransforms; @@ -5507,8 +5854,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( SetSkipHorizontalTransform setSkipHorizontalTransform(context); verticalTransforms = createOperations( - componentsSrc[1], - targetCRS->promoteTo3D(std::string(), dbContext), context); + componentsSrc[1], util::optional(), + targetCRS->promoteTo3D(std::string(), dbContext), + util::optional(), context); bool foundRegisteredTransformWithAllGridsAvailable = false; const auto gridAvailabilityUse = context.context->getGridAvailabilityUse(); @@ -5552,8 +5900,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( : cs::VerticalCS::createGravityRelatedHeight( common::UnitOfMeasure::METRE) ->axisList()[0]); - auto verticalTransformsTmp = - createOperations(componentsSrc[1], geogCRSTmp, context); + auto verticalTransformsTmp = createOperations( + componentsSrc[1], util::optional(), + geogCRSTmp, util::optional(), context); bool foundRegisteredTransform = false; foundRegisteredTransformWithAllGridsAvailable = false; for (const auto &op : verticalTransformsTmp) { @@ -5632,8 +5981,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( // Do the sourceCRS to interpolation CRS in 2D only // to avoid altering the orthometric elevation srcToInterpOps = createOperations( - componentsSrc[0], NN_NO_CHECK(interpolationGeogCRS), - context); + componentsSrc[0], util::optional(), + NN_NO_CHECK(interpolationGeogCRS), + util::optional(), context); // e.g when doing COMPOUND_CRS[ // NAD83(CRS)+TOWGS84[0,0,0], @@ -5677,8 +6027,10 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( createGravityRelatedHeight( common::UnitOfMeasure::METRE) ->axisList()[0]); - interpToTargetOps = - createOperations(interp3D, targetCRS, context); + interpToTargetOps = createOperations( + interp3D, util::optional(), + targetCRS, util::optional(), + context); }; if (!key.empty()) { @@ -5816,7 +6168,10 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToGeog( // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsToGeod( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::GeodeticCRS *geodDst, std::vector &res) { @@ -5829,10 +6184,10 @@ void CoordinateOperationFactory::Private::createOperationsToGeod( .set(common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, metadata::Extent::WORLD), geodDst->datum(), geodDst->datumEnsemble(), cs)); - auto sourceToGeog3DOps = - createOperations(sourceCRS, intermGeog3DCRS, context); - auto geog3DToTargetOps = - createOperations(intermGeog3DCRS, targetCRS, context); + auto sourceToGeog3DOps = createOperations( + sourceCRS, sourceEpoch, intermGeog3DCRS, sourceEpoch, context); + auto geog3DToTargetOps = createOperations(intermGeog3DCRS, targetEpoch, + targetCRS, targetEpoch, context); if (!geog3DToTargetOps.empty()) { for (const auto &op : sourceToGeog3DOps) { auto newOp = op->shallowClone(); @@ -5850,7 +6205,10 @@ void CoordinateOperationFactory::Private::createOperationsToGeod( // --------------------------------------------------------------------------- void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( - const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS, + const crs::CRSNNPtr &sourceCRS, + const util::optional &sourceEpoch, + const crs::CRSNNPtr &targetCRS, + const util::optional &targetEpoch, Private::Context &context, const crs::CompoundCRS *compoundSrc, const crs::CompoundCRS *compoundDst, std::vector &res) { @@ -5866,6 +6224,50 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( return; } + // Use PointMotionOperations if appropriate and available + const auto &authFactory = context.context->getAuthorityFactory(); + if (authFactory && sourceEpoch.has_value() && targetEpoch.has_value() && + !sourceEpoch->coordinateEpoch()._isEquivalentTo( + targetEpoch->coordinateEpoch()) && + srcGeog->_isEquivalentTo(dstGeog.get(), + util::IComparable::Criterion::EQUIVALENT)) { + const auto pmoSrc = authFactory->getPointMotionOperationsFor( + NN_NO_CHECK(srcGeog), true); + if (!pmoSrc.empty()) { + auto geog3D = srcGeog->promoteTo3D( + std::string(), authFactory->databaseContext().as_nullable()); + auto opsFirst = createOperations(sourceCRS, sourceEpoch, geog3D, + sourceEpoch, context); + auto pmoOps = createOperations(geog3D, sourceEpoch, geog3D, + targetEpoch, context); + auto opsLast = createOperations(geog3D, targetEpoch, targetCRS, + targetEpoch, context); + for (const auto &opFirst : opsFirst) { + if (!opFirst->hasBallparkTransformation()) { + for (const auto &opMiddle : pmoOps) { + if (!opMiddle->hasBallparkTransformation()) { + for (const auto &opLast : opsLast) { + if (!opLast->hasBallparkTransformation()) { + try { + res.emplace_back( + ConcatenatedOperation:: + createComputeMetadata( + {opFirst, opMiddle, opLast}, + disallowEmptyIntersection)); + } catch (const std::exception &) { + } + } + } + } + } + } + } + if (!res.empty()) { + return; + } + } + } + // Deal with "+proj=something +geoidgrids +nadgrids/+towgs84" to // "+proj=something +geoidgrids +nadgrids/+towgs84", using WGS 84 as an // intermediate. @@ -5884,14 +6286,15 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( util::IComparable::Criterion::EQUIVALENT) && !comp1SrcBound->isEquivalentTo( comp1DstBound, util::IComparable::Criterion::EQUIVALENT)) { - const auto &authFactory = context.context->getAuthorityFactory(); auto dbContext = authFactory ? authFactory->databaseContext().as_nullable() : nullptr; auto hub3D = comp0SrcBound->hubCRS()->promoteTo3D(std::string(), dbContext); - const auto ops1 = createOperations(sourceCRS, hub3D, context); - const auto ops2 = createOperations(hub3D, targetCRS, context); + const auto ops1 = createOperations(sourceCRS, sourceEpoch, hub3D, + sourceEpoch, context); + const auto ops2 = createOperations(hub3D, targetEpoch, targetCRS, + targetEpoch, context); for (const auto &op1 : ops1) { for (const auto &op2 : ops2) { try { @@ -5907,30 +6310,74 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( } std::vector verticalTransforms; - if (componentsSrc.size() >= 2 && componentsSrc[1]->extractVerticalCRS() && - componentsDst[1]->extractVerticalCRS()) { - if (!componentsSrc[1]->_isEquivalentTo(componentsDst[1].get())) { - verticalTransforms = - createOperations(componentsSrc[1], componentsDst[1], context); + bool bTryThroughIntermediateGeogCRS = false; + if (componentsSrc.size() >= 2) { + const auto vertSrc = componentsSrc[1]->extractVerticalCRS(); + const auto vertDst = componentsDst[1]->extractVerticalCRS(); + if (vertSrc && vertDst && + !componentsSrc[1]->_isEquivalentTo(componentsDst[1].get())) { + if ((!vertSrc->geoidModel().empty() || + !vertDst->geoidModel().empty()) && + // To be able to use "CGVD28 height to + // CGVD2013a(1997|2002|2010)" single grid + !(vertSrc->nameStr() == "CGVD28 height" && + !vertSrc->geoidModel().empty() && + vertSrc->geoidModel().front()->nameStr() == "HT2_1997" && + vertDst->nameStr() == "CGVD2013a(1997) height" && + vertDst->geoidModel().empty()) && + !(vertSrc->nameStr() == "CGVD28 height" && + !vertSrc->geoidModel().empty() && + vertSrc->geoidModel().front()->nameStr() == "HT2_2002" && + vertDst->nameStr() == "CGVD2013a(2002) height" && + vertDst->geoidModel().empty()) && + !(vertSrc->nameStr() == "CGVD28 height" && + !vertSrc->geoidModel().empty() && + vertSrc->geoidModel().front()->nameStr() == "HT2_2010" && + vertDst->nameStr() == "CGVD2013a(2010) height" && + vertDst->geoidModel().empty()) && + !(vertDst->nameStr() == "CGVD28 height" && + !vertDst->geoidModel().empty() && + vertDst->geoidModel().front()->nameStr() == "HT2_1997" && + vertSrc->nameStr() == "CGVD2013a(1997) height" && + vertSrc->geoidModel().empty()) && + !(vertDst->nameStr() == "CGVD28 height" && + !vertDst->geoidModel().empty() && + vertDst->geoidModel().front()->nameStr() == "HT2_2002" && + vertSrc->nameStr() == "CGVD2013a(2002) height" && + vertSrc->geoidModel().empty()) && + !(vertDst->nameStr() == "CGVD28 height" && + !vertDst->geoidModel().empty() && + vertDst->geoidModel().front()->nameStr() == "HT2_2010" && + vertSrc->nameStr() == "CGVD2013a(2010) height" && + vertSrc->geoidModel().empty())) { + // If we have a geoid model, force using through it + bTryThroughIntermediateGeogCRS = true; + } else { + verticalTransforms = + createOperations(componentsSrc[1], sourceEpoch, + componentsDst[1], targetEpoch, context); + // If we didn't find a non-ballpark transformation between + // the 2 vertical CRS, then try through intermediate geographic + // CRS + bTryThroughIntermediateGeogCRS = + (verticalTransforms.size() == 1 && + verticalTransforms.front()->hasBallparkTransformation()); + } } } - // If we didn't find a non-ballpark transformation between - // the 2 vertical CRS, then try through intermediate geographic CRS - if (verticalTransforms.size() == 1 && - verticalTransforms.front()->hasBallparkTransformation()) { - const auto &authFactory = context.context->getAuthorityFactory(); + if (bTryThroughIntermediateGeogCRS) { auto dbContext = authFactory ? authFactory->databaseContext().as_nullable() : nullptr; const auto createWithIntermediateCRS = - [&sourceCRS, &targetCRS, &context, + [&sourceCRS, &sourceEpoch, &targetCRS, &targetEpoch, &context, &res](const crs::CRSNNPtr &intermCRS) { - const auto ops1 = - createOperations(sourceCRS, intermCRS, context); - const auto ops2 = - createOperations(intermCRS, targetCRS, context); + const auto ops1 = createOperations( + sourceCRS, sourceEpoch, intermCRS, sourceEpoch, context); + const auto ops2 = createOperations( + intermCRS, targetEpoch, targetCRS, targetEpoch, context); for (const auto &op1 : ops1) { for (const auto &op2 : ops2) { try { @@ -5984,10 +6431,10 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( intermGeogSrcIsSameAsIntermGeogDst ? intermGeogSrc : dstGeog->promoteTo3D(std::string(), dbContext); - const auto opsSrcToGeog = - createOperations(sourceCRS, intermGeogSrc, context); - const auto opsGeogToTarget = - createOperations(intermGeogDst, targetCRS, context); + const auto opsSrcToGeog = createOperations( + sourceCRS, sourceEpoch, intermGeogSrc, sourceEpoch, context); + const auto opsGeogToTarget = createOperations( + intermGeogDst, targetEpoch, targetCRS, targetEpoch, context); // We preferably accept using an intermediate if the operations // to it do not include ballpark operations, but if they do include @@ -6024,7 +6471,8 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( size_t bestStepCount = 0; if (hasNonTrivialSrcTransf && hasNonTrivialTargetTransf) { const auto opsGeogSrcToGeogDst = - createOperations(intermGeogSrc, intermGeogDst, context); + createOperations(intermGeogSrc, sourceEpoch, intermGeogDst, + targetEpoch, context); // In first pass, exclude (horizontal) ballpark operations, but // accept them in second pass. for (int pass = 0; pass <= 1 && res.empty(); pass++) { @@ -6156,8 +6604,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( intermGeogSrc->identifiers().empty() && !intermGeogDst->identifiers().empty() && hasNonTrivialTargetTransf) { - const auto opsSrcToIntermGeog = - createOperations(sourceCRS, intermGeogDst, context); + const auto opsSrcToIntermGeog = createOperations( + sourceCRS, util::optional(), intermGeogDst, + util::optional(), context); if (hasNonTrivialTransf(opsSrcToIntermGeog)) { createOpsInTwoSteps(opsSrcToIntermGeog, opsGeogToTarget); } @@ -6167,8 +6616,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( intermGeogDst->identifiers().empty() && !intermGeogSrc->identifiers().empty() && hasNonTrivialSrcTransf) { - const auto opsIntermGeogToDst = - createOperations(intermGeogSrc, targetCRS, context); + const auto opsIntermGeogToDst = createOperations( + intermGeogSrc, util::optional(), targetCRS, + util::optional(), context); if (hasNonTrivialTransf(opsIntermGeogToDst)) { createOpsInTwoSteps(opsSrcToGeog, opsIntermGeogToDst); } @@ -6206,10 +6656,52 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( compSrc0BoundCrs->hubCRS())); } } - auto opSrcCRSToGeogCRS = - createOperations(componentsSrc[0], interpolationGeogCRS, context); - auto opGeogCRStoDstCRS = - createOperations(interpolationGeogCRS, componentsDst[0], context); + + // Hack for + // NAD83_CSRS_1997_xxxx_HT2_1997 to NAD83_CSRS_1997_yyyy_CGVD2013_1997 + // NAD83_CSRS_2002_xxxx_HT2_2002 to NAD83_CSRS_2002_yyyy_CGVD2013_2002 + if (sourceEpoch.has_value() && targetEpoch.has_value() && + sourceEpoch->coordinateEpoch()._isEquivalentTo( + targetEpoch->coordinateEpoch()) && + srcGeog->_isEquivalentTo( + dstGeog.get(), util::IComparable::Criterion::EQUIVALENT) && + srcGeog->nameStr() == "NAD83(CSRS)v7") { + const bool is1997 = + std::abs(sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR) - + 1997) < 1e-10; + const bool is2002 = + std::abs(sourceEpoch->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR) - + 2002) < 1e-10; + try { + auto authFactoryEPSG = io::AuthorityFactory::create( + authFactory->databaseContext(), "EPSG"); + auto nad83CSRSv7 = authFactoryEPSG->createGeographicCRS("8255"); + if (srcGeog->_isEquivalentTo(nad83CSRSv7.get(), + util::IComparable::Criterion:: + EQUIVALENT) && // NAD83(CSRS)v7 + // 2D + ((is1997 && + verticalTransform->nameStr().find( + "CGVD28 height to CGVD2013a(1997) height (1)") != + std::string::npos) || + (is2002 && + verticalTransform->nameStr().find( + "CGVD28 height to CGVD2013a(2002) height (1)") != + std::string::npos))) { + interpolationGeogCRS = nad83CSRSv7; + } + } catch (const std::exception &) { + } + } + + const auto opSrcCRSToGeogCRS = createOperations( + componentsSrc[0], util::optional(), + interpolationGeogCRS, util::optional(), context); + const auto opGeogCRStoDstCRS = createOperations( + interpolationGeogCRS, util::optional(), + componentsDst[0], util::optional(), context); for (const auto &opSrc : opSrcCRSToGeogCRS) { for (const auto &opDst : opGeogCRStoDstCRS) { @@ -6246,8 +6738,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( std::vector opsHoriz; createOperationsFromDatabase( - componentsSrc[0], componentsDst[0], context, srcGeog.get(), - dstGeog.get(), srcGeog.get(), dstGeog.get(), + componentsSrc[0], util::optional(), + componentsDst[0], util::optional(), context, + srcGeog.get(), dstGeog.get(), srcGeog.get(), dstGeog.get(), /*vertSrc=*/nullptr, /*vertDst=*/nullptr, opsHoriz); for (const auto &opHoriz : opsHoriz) { res.emplace_back(createHorizNullVerticalPROJBased( @@ -6257,8 +6750,9 @@ void CoordinateOperationFactory::Private::createOperationsCompoundToCompound( } if (verticalTransforms.empty()) { - auto resTmp = - createOperations(componentsSrc[0], componentsDst[0], context); + auto resTmp = createOperations( + componentsSrc[0], util::optional(), + componentsDst[0], util::optional(), context); for (const auto &op : resTmp) { auto opClone = op->shallowClone(); setCRSs(opClone.get(), sourceCRS, targetCRS); @@ -6316,7 +6810,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound( auto dstNew = crs::CompoundCRS::create( properties, {NN_NO_CHECK(compDst0BoundCrs), componentsDst[1]}); - auto tmpRes = createOperations(srcNew, dstNew, context); + auto tmpRes = createOperations( + srcNew, util::optional(), dstNew, + util::optional(), context); for (const auto &op : tmpRes) { auto opClone = op->shallowClone(); setCRSs(opClone.get(), sourceCRS, targetCRS); @@ -6357,10 +6853,14 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound( metadata::Extent::WORLD), boundSrcHubAsGeogCRS->datum(), boundSrcHubAsGeogCRS->datumEnsemble(), cs)); - auto sourceToGeog3DOps = - createOperations(sourceCRS, intermGeog3DCRS, context); - auto geog3DToTargetOps = - createOperations(intermGeog3DCRS, targetCRS, context); + auto sourceToGeog3DOps = createOperations( + sourceCRS, util::optional(), + intermGeog3DCRS, util::optional(), + context); + auto geog3DToTargetOps = createOperations( + intermGeog3DCRS, util::optional(), + targetCRS, util::optional(), + context); for (const auto &opSrc : sourceToGeog3DOps) { for (const auto &opDst : geog3DToTargetOps) { if (opSrc->targetCRS() && opDst->sourceCRS() && @@ -6374,7 +6874,10 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound( // So create an adapter operation... auto intermOps = createOperations( NN_NO_CHECK(opSrc->targetCRS()), - NN_NO_CHECK(opDst->sourceCRS()), context); + util::optional(), + NN_NO_CHECK(opDst->sourceCRS()), + util::optional(), + context); if (!intermOps.empty()) { res.emplace_back( ConcatenatedOperation:: @@ -6417,9 +6920,12 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound( comp0Geog->datumNonNull(dbContext).get(), util::IComparable::Criterion::EQUIVALENT)) { const auto ops1 = - createOperations(sourceCRS, boundSrc->hubCRS(), context); - const auto ops2 = - createOperations(boundSrc->hubCRS(), targetCRS, context); + createOperations(sourceCRS, util::optional(), + boundSrc->hubCRS(), + util::optional(), context); + const auto ops2 = createOperations( + boundSrc->hubCRS(), util::optional(), + targetCRS, util::optional(), context); for (const auto &op1 : ops1) { for (const auto &op2 : ops2) { try { @@ -6438,7 +6944,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToCompound( // There might be better things to do, but for now just ignore the // transformation of the bound CRS - res = createOperations(boundSrc->baseCRS(), targetCRS, context); + res = createOperations(boundSrc->baseCRS(), + util::optional(), targetCRS, + util::optional(), context); } //! @endcond @@ -6504,8 +7012,10 @@ CoordinateOperationFactory::createOperations( } auto resFiltered = filterAndSort( - Private::createOperations(l_resolvedSourceCRS, l_resolvedTargetCRS, - contextPrivate), + Private::createOperations( + l_resolvedSourceCRS, context->getSourceCoordinateEpoch(), + l_resolvedTargetCRS, context->getTargetCoordinateEpoch(), + contextPrivate), context, sourceCRSExtent, targetCRSExtent); if (context->getSourceCoordinateEpoch().has_value() || context->getTargetCoordinateEpoch().has_value()) { @@ -6570,6 +7080,36 @@ CoordinateOperationFactory::createOperations( // --------------------------------------------------------------------------- +/** \brief Find a list of CoordinateOperation from a source coordinate metadata + * to a target coordinate metadata. + * + * Both source_crs and target_crs can be a CoordinateMetadata + * with an associated coordinate epoch, to perform changes of coordinate epochs. + * Note however than this is in practice limited to use of velocity grids inside + * the same dynamic CRS. + * + * @param sourceCoordinateMetadata source CoordinateMetadata. + * @param targetCoordinateMetadata target CoordinateMetadata. + * @param context Search context. + * @return a list + * @since 9.4 + */ +std::vector +CoordinateOperationFactory::createOperations( + const coordinates::CoordinateMetadataNNPtr &sourceCoordinateMetadata, + const coordinates::CoordinateMetadataNNPtr &targetCoordinateMetadata, + const CoordinateOperationContextNNPtr &context) const { + auto newContext = context->clone(); + newContext->setSourceCoordinateEpoch( + sourceCoordinateMetadata->coordinateEpoch()); + newContext->setTargetCoordinateEpoch( + targetCoordinateMetadata->coordinateEpoch()); + return createOperations(sourceCoordinateMetadata->crs(), + targetCoordinateMetadata->crs(), newContext); +} + +// --------------------------------------------------------------------------- + /** \brief Instantiate a CoordinateOperationFactory. */ CoordinateOperationFactoryNNPtr CoordinateOperationFactory::create() { diff --git a/src/iso19111/operation/parammappings.cpp b/src/iso19111/operation/parammappings.cpp index 30f32dd935..1f1515ad2a 100644 --- a/src/iso19111/operation/parammappings.cpp +++ b/src/iso19111/operation/parammappings.cpp @@ -1006,6 +1006,8 @@ const struct MethodNameCode methodNameCodes[] = { METHOD_NAME_CODE(NADCON5_3D), METHOD_NAME_CODE(VERTCON), METHOD_NAME_CODE(GEOCENTRIC_TRANSLATION_BY_GRID_INTERPOLATION_IGN), + // PointMotionOperation + METHOD_NAME_CODE(POINT_MOTION_BY_GRID_CANADA_NTV2_VEL), }; const MethodNameCode *getMethodNameCodes(size_t &nElts) { @@ -1089,6 +1091,8 @@ const struct ParamNameCode paramNameCodes[] = { PARAM_NAME_CODE(INCLINATION_IN_LONGITUDE), PARAM_NAME_CODE(EPSG_CODE_FOR_HORIZONTAL_CRS), PARAM_NAME_CODE(EPSG_CODE_FOR_INTERPOLATION_CRS), + // Parameters of point motion operations + PARAM_NAME_CODE(POINT_MOTION_VELOCITY_GRID_FILE), }; const ParamNameCode *getParamNameCodes(size_t &nElts) { @@ -1385,6 +1389,14 @@ static const ParamMapping paramVerticalOffsetFile = { static const ParamMapping *const paramsVERTCON[] = {¶mVerticalOffsetFile, nullptr}; +static const ParamMapping paramPointMotiionVelocityGridFile = { + EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, nullptr, + common::UnitOfMeasure::Type::NONE, nullptr}; + +static const ParamMapping *const paramsPointMotionOperationByVelocityGrid[] = { + ¶mPointMotiionVelocityGridFile, nullptr}; + static const ParamMapping paramSouthPoleLatGRIB = { PROJ_WKT2_NAME_PARAMETER_SOUTH_POLE_LATITUDE_GRIB_CONVENTION, 0, nullptr, common::UnitOfMeasure::Type::ANGULAR, nullptr}; @@ -1572,6 +1584,10 @@ static const MethodMapping otherMethodMappings[] = { nullptr, paramsVERTCON}, {EPSG_NAME_METHOD_VERTCON_OLDNAME, EPSG_CODE_METHOD_VERTCON, nullptr, nullptr, nullptr, paramsVERTCON}, + + {EPSG_NAME_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL, + EPSG_CODE_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL, nullptr, nullptr, + nullptr, paramsPointMotionOperationByVelocityGrid}, }; const MethodMapping *getOtherMethodMappings(size_t &nElts) { diff --git a/src/iso19111/operation/singleoperation.cpp b/src/iso19111/operation/singleoperation.cpp index 89ae015150..07f288b0d2 100644 --- a/src/iso19111/operation/singleoperation.cpp +++ b/src/iso19111/operation/singleoperation.cpp @@ -1963,6 +1963,62 @@ _getGeocentricTranslationFilename(const SingleOperation *op, // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +static const std::string & +_getGeographic3DOffsetByVelocityGridFilename(const SingleOperation *op, + bool allowInverse) { + + const auto &l_method = op->method(); + const auto &methodName = l_method->nameStr(); + if (l_method->getEPSGCode() == + EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN || + (allowInverse && + ci_equal( + methodName, + INVERSE_OF + + EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN))) { + const auto &fileParameter = op->parameterValue( + EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + return fileParameter->valueFile(); + } + } + return nullString; +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress +static const std::string & +_getVerticalOffsetByVelocityGridFilename(const SingleOperation *op, + bool allowInverse) { + + const auto &l_method = op->method(); + const auto &methodName = l_method->nameStr(); + if (l_method->getEPSGCode() == + EPSG_CODE_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN || + (allowInverse && + ci_equal( + methodName, + INVERSE_OF + + EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN))) { + const auto &fileParameter = op->parameterValue( + EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + return fileParameter->valueFile(); + } + } + return nullString; +} +//! @endcond + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress static const std::string & _getHeightToGeographic3DFilename(const SingleOperation *op, bool allowInverse) { @@ -2082,7 +2138,7 @@ const std::string &Transformation::getHeightToGeographic3DFilename() const { //! @cond Doxygen_Suppress static util::PropertyMap -createSimilarPropertiesTransformation(TransformationNNPtr obj) { +createSimilarPropertiesOperation(const CoordinateOperationNNPtr &obj) { util::PropertyMap map; // The domain(s) are unchanged @@ -2159,12 +2215,20 @@ createSimilarPropertiesMethod(common::IdentifiedObjectNNPtr obj) { // --------------------------------------------------------------------------- -static bool isRegularVerticalGridMethod(int methodEPSGCode) { +static bool isRegularVerticalGridMethod(int methodEPSGCode, + bool &reverseOffsetSign) { + if (methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NRCAN_BYN) { + // NRCAN vertical shift grids use a reverse convention from other + // grids: the value in the grid is the value to subtract from the + // source vertical CRS to get the target value. + reverseOffsetSign = true; + return true; + } + reverseOffsetSign = false; return methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NZLVD || methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_BEV_AT || methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_GTX || - methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_PL_TXT || - methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_NRCAN_BYN; + methodEPSGCode == EPSG_CODE_METHOD_VERTICALGRID_PL_TXT; } // --------------------------------------------------------------------------- @@ -2257,7 +2321,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( } else { return Transformation::create( - createSimilarPropertiesTransformation(self), l_sourceCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, l_targetCRS, l_interpolationCRS, methodProperties, parameters, values, l_accuracies); } @@ -2269,7 +2333,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( l_accuracies) ->inverseAsTransformation(); } else { - return createNTv1(createSimilarPropertiesTransformation(self), + return createNTv1(createSimilarPropertiesOperation(self), l_sourceCRS, l_targetCRS, projFilename, l_accuracies); } @@ -2282,7 +2346,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( ->inverseAsTransformation(); } else { return Transformation::createNTv2( - createSimilarPropertiesTransformation(self), l_sourceCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, l_targetCRS, projFilename, l_accuracies); } } else if (projGridFormat == "CTable2") { @@ -2304,7 +2368,7 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( } else { return Transformation::create( - createSimilarPropertiesTransformation(self), l_sourceCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, l_targetCRS, l_interpolationCRS, methodProperties, parameters, values, l_accuracies); } @@ -2361,8 +2425,8 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( #endif { return Transformation::create( - createSimilarPropertiesTransformation(self), - l_sourceCRS, l_targetCRS, l_interpolationCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, + l_targetCRS, l_interpolationCRS, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename(projFilename)}, coordinateOperationAccuracies()); @@ -2402,7 +2466,85 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( std::vector{createOpParamNameEPSGCode( EPSG_CODE_PARAMETER_GEOCENTRIC_TRANSLATION_FILE)}; return Transformation::create( - createSimilarPropertiesTransformation(self), l_sourceCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, + l_targetCRS, l_interpolationCRS, + createSimilarPropertiesMethod(method()), parameters, + {ParameterValue::createFilename(projFilename)}, + coordinateOperationAccuracies()); + } + } + + const auto &geographic3DOffsetByVelocityGridFilename = + _getGeographic3DOffsetByVelocityGridFilename(this, false); + if (!geographic3DOffsetByVelocityGridFilename.empty()) { + if (databaseContext->lookForGridAlternative( + geographic3DOffsetByVelocityGridFilename, projFilename, + projGridFormat, inverseDirection)) { + + if (inverseDirection) { + throw util::UnsupportedOperationException( + "Inverse direction for " + "Geographic3DOFffsetByVelocityGrid not supported"); + } + + if (geographic3DOffsetByVelocityGridFilename == projFilename) { + return self; + } + + const auto l_sourceCRSNull = sourceCRS(); + const auto l_targetCRSNull = targetCRS(); + if (l_sourceCRSNull == nullptr) { + throw util::UnsupportedOperationException("Missing sourceCRS"); + } + if (l_targetCRSNull == nullptr) { + throw util::UnsupportedOperationException("Missing targetCRS"); + } + auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull); + auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull); + auto parameters = + std::vector{createOpParamNameEPSGCode( + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)}; + return Transformation::create( + createSimilarPropertiesOperation(self), l_sourceCRS, + l_targetCRS, l_interpolationCRS, + createSimilarPropertiesMethod(method()), parameters, + {ParameterValue::createFilename(projFilename)}, + coordinateOperationAccuracies()); + } + } + + const auto &verticalOffsetByVelocityGridFilename = + _getVerticalOffsetByVelocityGridFilename(this, false); + if (!verticalOffsetByVelocityGridFilename.empty()) { + if (databaseContext->lookForGridAlternative( + verticalOffsetByVelocityGridFilename, projFilename, + projGridFormat, inverseDirection)) { + + if (inverseDirection) { + throw util::UnsupportedOperationException( + "Inverse direction for " + "VerticalOffsetByVelocityGrid not supported"); + } + + if (verticalOffsetByVelocityGridFilename == projFilename) { + return self; + } + + const auto l_sourceCRSNull = sourceCRS(); + const auto l_targetCRSNull = targetCRS(); + if (l_sourceCRSNull == nullptr) { + throw util::UnsupportedOperationException("Missing sourceCRS"); + } + if (l_targetCRSNull == nullptr) { + throw util::UnsupportedOperationException("Missing targetCRS"); + } + auto l_sourceCRS = NN_NO_CHECK(l_sourceCRSNull); + auto l_targetCRS = NN_NO_CHECK(l_targetCRSNull); + auto parameters = + std::vector{createOpParamNameEPSGCode( + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)}; + return Transformation::create( + createSimilarPropertiesOperation(self), l_sourceCRS, l_targetCRS, l_interpolationCRS, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename(projFilename)}, @@ -2410,8 +2552,9 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( } } + bool reverseOffsetSign = false; if (methodEPSGCode == EPSG_CODE_METHOD_VERTCON || - isRegularVerticalGridMethod(methodEPSGCode)) { + isRegularVerticalGridMethod(methodEPSGCode, reverseOffsetSign)) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); @@ -2458,8 +2601,8 @@ TransformationNNPtr SingleOperation::substitutePROJAlternativeGridNames( ->inverseAsTransformation(); } else { return Transformation::create( - createSimilarPropertiesTransformation(self), - l_sourceCRS, l_targetCRS, l_interpolationCRS, + createSimilarPropertiesOperation(self), l_sourceCRS, + l_targetCRS, l_interpolationCRS, createSimilarPropertiesMethod(method()), parameters, {ParameterValue::createFilename(projFilename)}, coordinateOperationAccuracies()); @@ -3837,6 +3980,203 @@ bool SingleOperation::exportToPROJStringGeneric( return true; } + const auto &geographic3DOffsetByVelocityGridFilename = + _getGeographic3DOffsetByVelocityGridFilename(this, true); + if (!geographic3DOffsetByVelocityGridFilename.empty()) { + auto sourceCRSGeog = + dynamic_cast(sourceCRS().get()); + if (!sourceCRSGeog) { + throw io::FormattingException( + concat("Can apply ", methodName, " only to GeographicCRS")); + } + + auto targetCRSGeog = + dynamic_cast(targetCRS().get()); + if (!targetCRSGeog) { + throw io::FormattingException( + concat("Can apply ", methodName, " only to GeographicCRS")); + } + + const auto &interpCRS = interpolationCRS(); + if (!interpCRS) { + throw io::FormattingException( + "InterpolationCRS required " + "for" + " " EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN); + } + const bool interpIsSrc = interpCRS->_isEquivalentTo( + sourceCRS()->demoteTo2D(std::string(), nullptr).get(), + util::IComparable::Criterion::EQUIVALENT); + const bool interpIsTarget = interpCRS->_isEquivalentTo( + targetCRS()->demoteTo2D(std::string(), nullptr).get(), + util::IComparable::Criterion::EQUIVALENT); + if (!interpIsSrc && !interpIsTarget) { + throw io::FormattingException( + "For" + " " EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN + ", interpolation CRS should be the source or target CRS"); + } + + formatter->startInversion(); + sourceCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); + formatter->stopInversion(); + + if (isMethodInverseOf) { + formatter->startInversion(); + } + + const bool addPushPopV3 = + ((sourceCRSGeog && + sourceCRSGeog->coordinateSystem()->axisList().size() == 2) || + (targetCRSGeog && + targetCRSGeog->coordinateSystem()->axisList().size() == 2)); + + if (addPushPopV3) { + formatter->addStep("push"); + formatter->addParam("v_3"); + } + + formatter->addStep("cart"); + sourceCRSGeog->ellipsoid()->_exportToPROJString(formatter); + + formatter->addStep("deformation"); + auto srcName = sourceCRS()->nameStr(); + auto dstName = targetCRS()->nameStr(); + const struct { + const char *name; + double epoch; + } realizationEpochs[] = { + {"NAD83(CSRS)v2", 1997.0}, {"NAD83(CSRS)v3", 1997.0}, + {"NAD83(CSRS)v4", 2002.0}, {"NAD83(CSRS)v5", 2006.0}, + {"NAD83(CSRS)v6", 2010.0}, {"NAD83(CSRS)v7", 2010.0}, + {"NAD83(CSRS)v8", 2010.0}, + }; + double sourceYear = 0.0; + double targetYear = 0.0; + for (const auto &iter : realizationEpochs) { + if (iter.name == srcName) + sourceYear = iter.epoch; + if (iter.name == dstName) + targetYear = iter.epoch; + } + if (sourceYear == 0.0) { + throw io::FormattingException( + "For" + " " EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN + ", missing epoch for source CRS"); + } + if (targetYear == 0.0) { + throw io::FormattingException( + "For" + " " EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN + ", missing epoch for target CRS"); + } + formatter->addParam("dt", targetYear - sourceYear); + formatter->addParam("grids", geographic3DOffsetByVelocityGridFilename); + (interpIsTarget ? targetCRSGeog : sourceCRSGeog) + ->ellipsoid() + ->_exportToPROJString(formatter); + + formatter->startInversion(); + formatter->addStep("cart"); + targetCRSGeog->ellipsoid()->_exportToPROJString(formatter); + formatter->stopInversion(); + + if (addPushPopV3) { + formatter->addStep("pop"); + formatter->addParam("v_3"); + } + + if (isMethodInverseOf) { + formatter->stopInversion(); + } + + targetCRSGeog->addAngularUnitConvertAndAxisSwap(formatter); + + return true; + } + + const auto &verticalOffsetByVelocityGridFilename = + _getVerticalOffsetByVelocityGridFilename(this, true); + if (!verticalOffsetByVelocityGridFilename.empty()) { + + const auto &interpCRS = interpolationCRS(); + if (!interpCRS) { + throw io::FormattingException( + "InterpolationCRS required " + "for" + " " EPSG_NAME_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN); + } + + auto interpCRSGeog = + dynamic_cast(interpCRS.get()); + if (!interpCRSGeog) { + throw io::FormattingException( + concat("Can apply ", methodName, + " only to a GeographicCRS interpolation CRS")); + } + + if (isMethodInverseOf) { + formatter->startInversion(); + } + formatter->addStep("push"); + formatter->addParam("v_1"); + formatter->addParam("v_2"); + + formatter->addStep("cart"); + interpCRSGeog->ellipsoid()->_exportToPROJString(formatter); + + formatter->addStep("deformation"); + auto srcName = sourceCRS()->nameStr(); + auto dstName = targetCRS()->nameStr(); + const struct { + const char *name; + double epoch; + } realizationEpochs[] = { + {"CGVD2013a(1997) height", 1997.0}, + {"CGVD2013a(2002) height", 2002.0}, + {"CGVD2013a(2010) height", 2010.0}, + }; + double sourceYear = 0.0; + double targetYear = 0.0; + for (const auto &iter : realizationEpochs) { + if (iter.name == srcName) + sourceYear = iter.epoch; + if (iter.name == dstName) + targetYear = iter.epoch; + } + if (sourceYear == 0.0) { + throw io::FormattingException( + "For" + " " EPSG_NAME_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN + ", missing epoch for source CRS"); + } + if (targetYear == 0.0) { + throw io::FormattingException( + "For" + " " EPSG_NAME_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN + ", missing epoch for target CRS"); + } + formatter->addParam("dt", targetYear - sourceYear); + formatter->addParam("grids", verticalOffsetByVelocityGridFilename); + interpCRSGeog->ellipsoid()->_exportToPROJString(formatter); + + formatter->startInversion(); + formatter->addStep("cart"); + interpCRSGeog->ellipsoid()->_exportToPROJString(formatter); + formatter->stopInversion(); + + formatter->addStep("pop"); + formatter->addParam("v_1"); + formatter->addParam("v_2"); + + if (isMethodInverseOf) { + formatter->stopInversion(); + } + + return true; + } + const auto &heightFilename = _getHeightToGeographic3DFilename(this, true); if (!heightFilename.empty()) { auto l_targetCRS = targetCRS(); @@ -3963,7 +4303,8 @@ bool SingleOperation::exportToPROJStringGeneric( } } - if (isRegularVerticalGridMethod(methodEPSGCode)) { + bool reverseOffsetSign = false; + if (isRegularVerticalGridMethod(methodEPSGCode, reverseOffsetSign)) { auto fileParameter = parameterValue(EPSG_NAME_PARAMETER_VERTICAL_OFFSET_FILE, EPSG_CODE_PARAMETER_VERTICAL_OFFSET_FILE); @@ -3971,7 +4312,7 @@ bool SingleOperation::exportToPROJStringGeneric( fileParameter->type() == ParameterValue::Type::FILENAME) { formatter->addStep("vgridshift"); formatter->addParam("grids", fileParameter->valueFile()); - formatter->addParam("multiplier", 1.0); + formatter->addParam("multiplier", reverseOffsetSign ? -1.0 : 1.0); return true; } } @@ -4125,6 +4466,455 @@ PointMotionOperation::~PointMotionOperation() = default; // --------------------------------------------------------------------------- +/** \brief Instantiate a point motion operation from a vector of + * GeneralParameterValue. + * + * @param properties See \ref general_properties. At minimum the name should be + * defined. + * @param crsIn Source and target CRS. + * @param methodIn Operation method. + * @param values Vector of GeneralOperationParameterNNPtr. + * @param accuracies Vector of positional accuracy (might be empty). + * @return new PointMotionOperation. + * @throws InvalidOperation + */ +PointMotionOperationNNPtr PointMotionOperation::create( + const util::PropertyMap &properties, const crs::CRSNNPtr &crsIn, + const OperationMethodNNPtr &methodIn, + const std::vector &values, + const std::vector &accuracies) { + if (methodIn->parameters().size() != values.size()) { + throw InvalidOperation( + "Inconsistent number of parameters and parameter values"); + } + auto pmo = PointMotionOperation::nn_make_shared( + crsIn, methodIn, values, accuracies); + pmo->assignSelf(pmo); + pmo->setProperties(properties); + + const std::string l_name = pmo->nameStr(); + auto pos = l_name.find(" from epoch "); + if (pos != std::string::npos) { + pos += strlen(" from epoch "); + const auto pos2 = l_name.find(" to epoch ", pos); + if (pos2 != std::string::npos) { + const double sourceYear = std::stod(l_name.substr(pos, pos2 - pos)); + const double targetYear = + std::stod(l_name.substr(pos2 + strlen(" to epoch "))); + pmo->setSourceCoordinateEpoch( + util::optional(common::DataEpoch( + common::Measure(sourceYear, common::UnitOfMeasure::YEAR)))); + pmo->setTargetCoordinateEpoch( + util::optional(common::DataEpoch( + common::Measure(targetYear, common::UnitOfMeasure::YEAR)))); + } + } + + return pmo; +} + +// --------------------------------------------------------------------------- + +/** \brief Instantiate a point motion operation and its OperationMethod. + * + * @param propertiesOperation The \ref general_properties of the + * PointMotionOperation. + * At minimum the name should be defined. + * @param crsIn Source and target CRS. + * @param propertiesOperationMethod The \ref general_properties of the + * OperationMethod. + * At minimum the name should be defined. + * @param parameters Vector of parameters of the operation method. + * @param values Vector of ParameterValueNNPtr. Constraint: + * values.size() == parameters.size() + * @param accuracies Vector of positional accuracy (might be empty). + * @return new PointMotionOperation. + * @throws InvalidOperation + */ +PointMotionOperationNNPtr PointMotionOperation::create( + const util::PropertyMap &propertiesOperation, const crs::CRSNNPtr &crsIn, + const util::PropertyMap &propertiesOperationMethod, + const std::vector ¶meters, + const std::vector &values, + const std::vector + &accuracies) // throw InvalidOperation +{ + OperationMethodNNPtr op( + OperationMethod::create(propertiesOperationMethod, parameters)); + + if (parameters.size() != values.size()) { + throw InvalidOperation( + "Inconsistent number of parameters and parameter values"); + } + std::vector generalParameterValues; + generalParameterValues.reserve(values.size()); + for (size_t i = 0; i < values.size(); i++) { + generalParameterValues.push_back( + OperationParameterValue::create(parameters[i], values[i])); + } + return create(propertiesOperation, crsIn, op, generalParameterValues, + accuracies); +} + +// --------------------------------------------------------------------------- + +PointMotionOperation::PointMotionOperation( + const crs::CRSNNPtr &crsIn, const OperationMethodNNPtr &methodIn, + const std::vector &values, + const std::vector &accuracies) + : SingleOperation(methodIn) { + setParameterValues(values); + setCRSs(crsIn, crsIn, nullptr); + setAccuracies(accuracies); +} + +// --------------------------------------------------------------------------- + +PointMotionOperation::PointMotionOperation(const PointMotionOperation &other) + : CoordinateOperation(other), SingleOperation(other) {} + +// --------------------------------------------------------------------------- + +CoordinateOperationNNPtr PointMotionOperation::inverse() const { + auto inverse = shallowClone(); + if (sourceCoordinateEpoch().has_value()) { + // Switch source and target epochs + inverse->setSourceCoordinateEpoch(targetCoordinateEpoch()); + inverse->setTargetCoordinateEpoch(sourceCoordinateEpoch()); + + auto l_name = inverse->nameStr(); + auto pos = l_name.find(" from epoch "); + if (pos != std::string::npos) + l_name.resize(pos); + + const double sourceYear = getRoundedEpochInDecimalYear( + inverse->sourceCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + const double targetYear = getRoundedEpochInDecimalYear( + inverse->targetCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + + l_name += " from epoch "; + l_name += toString(sourceYear); + l_name += " to epoch "; + l_name += toString(targetYear); + util::PropertyMap newProperties; + newProperties.set(IdentifiedObject::NAME_KEY, l_name); + inverse->setProperties(newProperties); + } + return inverse; +} + +// --------------------------------------------------------------------------- + +/** \brief Return an equivalent transformation to the current one, but using + * PROJ alternative grid names. + */ +PointMotionOperationNNPtr +PointMotionOperation::substitutePROJAlternativeGridNames( + io::DatabaseContextNNPtr databaseContext) const { + auto self = NN_NO_CHECK(std::dynamic_pointer_cast( + shared_from_this().as_nullable())); + + const auto &l_method = method(); + const int methodEPSGCode = l_method->getEPSGCode(); + + std::string filename; + if (methodEPSGCode == + EPSG_CODE_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL) { + const auto &fileParameter = + parameterValue(EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + filename = fileParameter->valueFile(); + } + } + + std::string projFilename; + std::string projGridFormat; + bool inverseDirection = false; + if (!filename.empty() && + databaseContext->lookForGridAlternative( + filename, projFilename, projGridFormat, inverseDirection)) { + + if (filename == projFilename) { + return self; + } + + auto parameters = + std::vector{createOpParamNameEPSGCode( + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE)}; + return PointMotionOperation::create( + createSimilarPropertiesOperation(self), sourceCRS(), + createSimilarPropertiesMethod(method()), parameters, + {ParameterValue::createFilename(projFilename)}, + coordinateOperationAccuracies()); + } + + return self; +} + +// --------------------------------------------------------------------------- + +/** \brief Return the source crs::CRS of the operation. + * + * @return the source CRS. + */ +const crs::CRSNNPtr &PointMotionOperation::sourceCRS() PROJ_PURE_DEFN { + return CoordinateOperation::getPrivate()->strongRef_->sourceCRS_; +} + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress + +PointMotionOperationNNPtr PointMotionOperation::shallowClone() const { + auto pmo = + PointMotionOperation::nn_make_shared(*this); + pmo->assignSelf(pmo); + pmo->setCRSs(this, false); + return pmo; +} + +CoordinateOperationNNPtr PointMotionOperation::_shallowClone() const { + return util::nn_static_pointer_cast(shallowClone()); +} + +// --------------------------------------------------------------------------- + +PointMotionOperationNNPtr PointMotionOperation::cloneWithEpochs( + const common::DataEpoch &sourceEpoch, + const common::DataEpoch &targetEpoch) const { + auto pmo = + PointMotionOperation::nn_make_shared(*this); + + pmo->assignSelf(pmo); + pmo->setCRSs(this, false); + + pmo->setSourceCoordinateEpoch( + util::optional(sourceEpoch)); + pmo->setTargetCoordinateEpoch( + util::optional(targetEpoch)); + + const double sourceYear = getRoundedEpochInDecimalYear( + sourceEpoch.coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + const double targetYear = getRoundedEpochInDecimalYear( + targetEpoch.coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + + auto l_name = nameStr(); + l_name += " from epoch "; + l_name += toString(sourceYear); + l_name += " to epoch "; + l_name += toString(targetYear); + util::PropertyMap newProperties; + newProperties.set(IdentifiedObject::NAME_KEY, l_name); + pmo->setProperties(newProperties); + + return pmo; +} + +// --------------------------------------------------------------------------- + +void PointMotionOperation::_exportToWKT(io::WKTFormatter *formatter) const { + if (formatter->version() != io::WKTFormatter::Version::WKT2 || + !formatter->use2019Keywords()) { + throw io::FormattingException( + "Transformation can only be exported to WKT2:2019"); + } + + formatter->startNode(io::WKTConstants::POINTMOTIONOPERATION, + !identifiers().empty()); + + formatter->addQuotedString(nameStr()); + + const auto &version = operationVersion(); + if (version.has_value()) { + formatter->startNode(io::WKTConstants::VERSION, false); + formatter->addQuotedString(*version); + formatter->endNode(); + } + + auto l_sourceCRS = sourceCRS(); + const bool canExportCRSId = + !(formatter->idOnTopLevelOnly() && formatter->topLevelHasId()); + + const bool hasDomains = !domains().empty(); + if (hasDomains) { + formatter->pushDisableUsage(); + } + + formatter->startNode(io::WKTConstants::SOURCECRS, false); + if (canExportCRSId && !l_sourceCRS->identifiers().empty()) { + // fake that top node has no id, so that the sourceCRS id is + // considered + formatter->pushHasId(false); + l_sourceCRS->_exportToWKT(formatter); + formatter->popHasId(); + } else { + l_sourceCRS->_exportToWKT(formatter); + } + formatter->endNode(); + + if (hasDomains) { + formatter->popDisableUsage(); + } + + const auto &l_method = method(); + l_method->_exportToWKT(formatter); + + for (const auto ¶mValue : parameterValues()) { + paramValue->_exportToWKT(formatter, nullptr); + } + + if (!coordinateOperationAccuracies().empty()) { + formatter->startNode(io::WKTConstants::OPERATIONACCURACY, false); + formatter->add(coordinateOperationAccuracies()[0]->value()); + formatter->endNode(); + } + + ObjectUsage::baseExportToWKT(formatter); + formatter->endNode(); +} + +// --------------------------------------------------------------------------- + +void PointMotionOperation::_exportToPROJString( + io::PROJStringFormatter *formatter) const // throw(FormattingException) +{ + if (formatter->convention() == + io::PROJStringFormatter::Convention::PROJ_4) { + throw io::FormattingException( + "PointMotionOperation cannot be exported as a PROJ.4 string"); + } + + const int methodEPSGCode = method()->getEPSGCode(); + if (methodEPSGCode == + EPSG_CODE_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL) { + if (!sourceCoordinateEpoch().has_value()) { + throw io::FormattingException( + "CoordinateOperationNNPtr::_exportToPROJString() unimplemented " + "when source coordinate epoch is missing"); + } + if (!targetCoordinateEpoch().has_value()) { + throw io::FormattingException( + "CoordinateOperationNNPtr::_exportToPROJString() unimplemented " + "when target coordinate epoch is missing"); + } + + auto l_sourceCRS = dynamic_cast(sourceCRS().get()); + + if (!l_sourceCRS->isGeocentric()) { + formatter->startInversion(); + l_sourceCRS->_exportToPROJString(formatter); + formatter->stopInversion(); + + formatter->addStep("cart"); + l_sourceCRS->ellipsoid()->_exportToPROJString(formatter); + } else { + formatter->startInversion(); + l_sourceCRS->addGeocentricUnitConversionIntoPROJString(formatter); + formatter->stopInversion(); + } + + const double sourceYear = getRoundedEpochInDecimalYear( + sourceCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + const double targetYear = getRoundedEpochInDecimalYear( + targetCoordinateEpoch()->coordinateEpoch().convertToUnit( + common::UnitOfMeasure::YEAR)); + + formatter->addStep("set"); + formatter->addParam("v_4", sourceYear); + formatter->addParam("omit_fwd"); + + formatter->addStep("deformation"); + formatter->addParam("dt", targetYear - sourceYear); + const auto &fileParameter = + parameterValue(EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE, + EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE); + if (fileParameter && + fileParameter->type() == ParameterValue::Type::FILENAME) { + formatter->addParam("grids", fileParameter->valueFile()); + } else { + throw io::FormattingException( + "CoordinateOperationNNPtr::_exportToPROJString(): missing " + "velocity grid file parameter"); + } + l_sourceCRS->ellipsoid()->_exportToPROJString(formatter); + + formatter->addStep("set"); + formatter->addParam("v_4", targetYear); + formatter->addParam("omit_inv"); + + if (!l_sourceCRS->isGeocentric()) { + formatter->startInversion(); + formatter->addStep("cart"); + l_sourceCRS->ellipsoid()->_exportToPROJString(formatter); + formatter->stopInversion(); + + l_sourceCRS->_exportToPROJString(formatter); + } else { + l_sourceCRS->addGeocentricUnitConversionIntoPROJString(formatter); + } + + } else { + throw io::FormattingException( + "CoordinateOperationNNPtr::_exportToPROJString() unimplemented for " + "this method"); + } +} + +// --------------------------------------------------------------------------- + +void PointMotionOperation::_exportToJSON( + io::JSONFormatter *formatter) const // throw(FormattingException) +{ + auto writer = formatter->writer(); + auto objectContext(formatter->MakeObjectContext("PointMotionOperation", + !identifiers().empty())); + + writer->AddObjKey("name"); + auto l_name = nameStr(); + if (l_name.empty()) { + writer->Add("unnamed"); + } else { + writer->Add(l_name); + } + + writer->AddObjKey("source_crs"); + formatter->setAllowIDInImmediateChild(); + sourceCRS()->_exportToJSON(formatter); + + writer->AddObjKey("method"); + formatter->setOmitTypeInImmediateChild(); + formatter->setAllowIDInImmediateChild(); + method()->_exportToJSON(formatter); + + writer->AddObjKey("parameters"); + { + auto parametersContext(writer->MakeArrayContext(false)); + for (const auto &genOpParamvalue : parameterValues()) { + formatter->setAllowIDInImmediateChild(); + formatter->setOmitTypeInImmediateChild(); + genOpParamvalue->_exportToJSON(formatter); + } + } + + if (!coordinateOperationAccuracies().empty()) { + writer->AddObjKey("accuracy"); + writer->Add(coordinateOperationAccuracies()[0]->value()); + } + + ObjectUsage::baseExportToJSON(formatter); +} + +//! @endcond + +// --------------------------------------------------------------------------- + } // namespace operation NS_PROJ_END diff --git a/src/iso19111/operation/transformation.cpp b/src/iso19111/operation/transformation.cpp index d660e269d8..22569c5a5f 100644 --- a/src/iso19111/operation/transformation.cpp +++ b/src/iso19111/operation/transformation.cpp @@ -1851,7 +1851,7 @@ void Transformation::_exportToPROJString( return; } - throw io::FormattingException("Unimplemented"); + throw io::FormattingException("Unimplemented " + nameStr()); } } // namespace operation diff --git a/src/iso19111/static.cpp b/src/iso19111/static.cpp index c9a029e934..3c4c8ddced 100644 --- a/src/iso19111/static.cpp +++ b/src/iso19111/static.cpp @@ -284,6 +284,7 @@ DEFINE_WKT_CONSTANT(EPOCH); DEFINE_WKT_CONSTANT(AXISMINVALUE); DEFINE_WKT_CONSTANT(AXISMAXVALUE); DEFINE_WKT_CONSTANT(RANGEMEANING); +DEFINE_WKT_CONSTANT(POINTMOTIONOPERATION); DEFINE_WKT_CONSTANT(GEODETICCRS); DEFINE_WKT_CONSTANT(GEODETICDATUM); diff --git a/src/proj.h b/src/proj.h index 20b24ddc18..cef28e3ccc 100644 --- a/src/proj.h +++ b/src/proj.h @@ -1395,6 +1395,9 @@ PJ PROJ_DLL *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs); PJ PROJ_DLL *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs); +int PROJ_DLL proj_crs_has_point_motion_operation(PJ_CONTEXT *ctx, + const PJ *crs); + int PROJ_DLL proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx, const PJ *datum_ensemble); @@ -1491,6 +1494,9 @@ PJ PROJ_DLL *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation, int i_step); +PJ PROJ_DLL *proj_coordinate_metadata_create(PJ_CONTEXT *ctx, const PJ *crs, + double epoch); + double PROJ_DLL proj_coordinate_metadata_get_epoch(PJ_CONTEXT *ctx, const PJ *obj); diff --git a/src/proj_constants.h b/src/proj_constants.h index 9b93aef360..02b416a4ba 100644 --- a/src/proj_constants.h +++ b/src/proj_constants.h @@ -581,6 +581,27 @@ /* ------------------------------------------------------------------------ */ +#define EPSG_NAME_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL \ + "Point motion by grid (Canada NTv2_Vel)" +#define EPSG_CODE_METHOD_POINT_MOTION_BY_GRID_CANADA_NTV2_VEL 1070 + +#define EPSG_CODE_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE 1050 +#define EPSG_NAME_PARAMETER_POINT_MOTION_VELOCITY_GRID_FILE \ + "Point motion velocity grid file" + +/* ------------------------------------------------------------------------ */ +#define EPSG_NAME_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN \ + "Geographic3D Offset by velocity grid (NRCan byn)" +#define EPSG_CODE_METHOD_GEOGRAPHIC3D_OFFSET_BY_VELOCITY_GRID_NRCAN 1114 + +/* ------------------------------------------------------------------------ */ + +#define EPSG_NAME_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN \ + "Vertical Offset by velocity grid (NRCan byn)" +#define EPSG_CODE_METHOD_VERTICAL_OFFSET_BY_VELOCITY_GRID_NRCAN 1113 + +/* ------------------------------------------------------------------------ */ + #define PROJ_WKT2_NAME_METHOD_HEIGHT_TO_GEOG3D \ "GravityRelatedHeight to Geographic3D" diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo index 509099e66f..77a33b2e62 100755 --- a/test/cli/testprojinfo +++ b/test/cli/testprojinfo @@ -390,6 +390,9 @@ echo 'Testing EPSG:9945 -o PROJ -q' >> ${OUT} $EXE EPSG:9945 -o PROJ -q >> ${OUT} echo "" >>${OUT} +echo 'Testing -s "NAD83(CSRS)v7" --s_epoch 1997 -t "NAD83(CSRS)v7" --t_epoch 2010 --summary --hide-ballpark' >> ${OUT} +$EXE -s "NAD83(CSRS)v7" --s_epoch 1997 -t "NAD83(CSRS)v7" --t_epoch 2010 --summary --hide-ballpark >> ${OUT} +echo "" >>${OUT} # do 'diff' with distribution results echo "diff ${OUT} with testprojinfo_out.dist" diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index 27852870d8..a22c9196e7 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -1593,7 +1593,7 @@ CREATE TABLE unit_of_measure( Testing projinfo --dump-db-structure --output-id HOBU:XXXX EPSG:4326 | tail -n 4 INSERT INTO metadata VALUES('DATABASE.LAYOUT.VERSION.MAJOR',1); -INSERT INTO metadata VALUES('DATABASE.LAYOUT.VERSION.MINOR',2); +INSERT INTO metadata VALUES('DATABASE.LAYOUT.VERSION.MINOR',3); INSERT INTO geodetic_crs VALUES('HOBU','XXXX','WGS 84','','geographic 2D','EPSG','6422','EPSG','6326',NULL,0); INSERT INTO usage VALUES('HOBU','USAGE_GEODETIC_CRS_XXXX','geodetic_crs','HOBU','XXXX','EPSG','1262','EPSG','1183'); @@ -1697,3 +1697,7 @@ EPSG:2154 "RGF93 v1 / Lambert-93" Testing EPSG:9945 -o PROJ -q +proj=tmerc +lat_0=0 +lon_0=21 +k=0.9999 +x_0=500000 +y_0=-4000000 +ellps=bessel +towgs84=521.748,229.489,590.921,4.029,4.488,-15.521,-9.78 +units=m +no_defs +type=crs +Testing -s "NAD83(CSRS)v7" --s_epoch 1997 -t "NAD83(CSRS)v7" --t_epoch 2010 --summary --hide-ballpark +Candidate operations found: 1 +unknown id, Null geographic offset from NAD83(CSRS)v7 (geog2D) to NAD83(CSRS)v7 (geog3D) + Canada velocity grid v7 from epoch 1997 to epoch 2010 + Null geographic offset from NAD83(CSRS)v7 (geog3D) to NAD83(CSRS)v7 (geog2D), 0.01 m, Canada - onshore and offshore - Alberta; British Columbia; Manitoba; New Brunswick; Newfoundland and Labrador; Northwest Territories; Nova Scotia; Nunavut; Ontario; Prince Edward Island; Quebec; Saskatchewan; Yukon. + diff --git a/test/cli/testvarious b/test/cli/testvarious index ce50615ec4..c2aad17756 100755 --- a/test/cli/testvarious +++ b/test/cli/testvarious @@ -1332,6 +1332,20 @@ $EXE -d 3 EPSG:3912+EPSG:5779 EPSG:3794+EPSG:8690 -E >>${OUT} 2>&1 <> ${OUT} +echo "Test --s_epoch" >> ${OUT} +# +$EXE -d 8 --s_epoch 2022 "ITRF2014" "GDA2020" -E >>${OUT} 2>&1 <> ${OUT} +echo "Test --t_epoch" >> ${OUT} +# +$EXE -d 8 --t_epoch 2022 "GDA2020" "ITRF2014" -E >>${OUT} 2>&1 <coordinateEpochAsDecimalYear(), 2023.5, 1e-10); } + +// --------------------------------------------------------------------------- + +TEST(coordinateMetadata, crs_with_point_motion_operation_and_promote_to_3D) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + { + // "NAD83(CSRS)v7" + auto crs = factory->createCoordinateReferenceSystem("8255"); + EXPECT_THROW(CoordinateMetadata::create(crs, 2023.5), Exception); + EXPECT_NO_THROW(CoordinateMetadata::create(crs, 2023.5, dbContext)); + auto cm = CoordinateMetadata::create(crs, 2023.5, dbContext) + ->promoteTo3D(std::string(), dbContext); + EXPECT_TRUE(cm->crs()->isEquivalentTo( + crs->promoteTo3D(std::string(), dbContext).get())); + EXPECT_TRUE(cm->coordinateEpoch().has_value()); + EXPECT_NEAR(cm->coordinateEpochAsDecimalYear(), 2023.5, 1e-10); + } + { + auto crs = factory->createCoordinateReferenceSystem("4267"); + EXPECT_THROW(CoordinateMetadata::create(crs, 2023.5, dbContext), + Exception); + auto cm = CoordinateMetadata::create(crs)->promoteTo3D(std::string(), + dbContext); + EXPECT_TRUE(cm->crs()->isEquivalentTo( + crs->promoteTo3D(std::string(), dbContext).get())); + EXPECT_TRUE(!cm->coordinateEpoch().has_value()); + } +} diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp index 1b261945e7..f2a4d96371 100644 --- a/test/unit/test_factory.cpp +++ b/test/unit/test_factory.cpp @@ -32,6 +32,7 @@ #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" +#include "proj/coordinates.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" @@ -50,6 +51,7 @@ #endif using namespace osgeo::proj::common; +using namespace osgeo::proj::coordinates; using namespace osgeo::proj::crs; using namespace osgeo::proj::cs; using namespace osgeo::proj::datum; @@ -1368,6 +1370,58 @@ TEST(factory, AuthorityFactory_createCoordinateOperation_conversion) { // --------------------------------------------------------------------------- +TEST(factory, + AuthorityFactory_createCoordinateOperation_point_motion_operation) { + auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto op = factory->createCoordinateOperation("9483", false); + auto pmo = nn_dynamic_pointer_cast(op); + ASSERT_TRUE(pmo != nullptr); + auto expected = + "POINTMOTIONOPERATION[\"Canada velocity grid v7\",\n" + " VERSION[\"NRC-Can cvg7.0\"],\n" + " SOURCECRS[\n" + " GEOGCRS[\"NAD83(CSRS)v7\",\n" + " DATUM[\"North American Datum of 1983 (CSRS) version 7\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,3],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"ellipsoidal height (h)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",8254]]],\n" + " METHOD[\"Point motion by grid (Canada NTv2_Vel)\",\n" + " ID[\"EPSG\",1070]],\n" + " PARAMETERFILE[\"Point motion velocity grid " + "file\",\"NAD83v70VG.gvb\"],\n" + " OPERATIONACCURACY[0.01],\n" + " USAGE[\n" + " SCOPE[\"Change of coordinate epoch for points referenced to " + "NAD83(CSRS)v7.\"],\n" + " AREA[\"Canada - onshore and offshore - Alberta; British " + "Columbia; Manitoba; New Brunswick; Newfoundland and Labrador; " + "Northwest Territories; Nova Scotia; Nunavut; Ontario; Prince Edward " + "Island; Quebec; Saskatchewan; Yukon.\"],\n" + " BBOX[38.21,-141.01,86.46,-40.73]],\n" + " ID[\"EPSG\",9483],\n" + " REMARK[\"File initially published with name cvg70.cvb, later " + "renamed to NAD83v70VG.gvb with no change of content.\"]]"; + + EXPECT_EQ( + pmo->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get()), + expected); +} + +// --------------------------------------------------------------------------- + TEST(factory, AuthorityFactory_getAuthorityCodes) { auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); { @@ -3038,6 +3092,88 @@ TEST_F(FactoryWithTmpDatabase, custom_projected_crs) { // --------------------------------------------------------------------------- +TEST_F(FactoryWithTmpDatabase, CoordinateMetadata) { + createStructure(); + populateWithFakeEPSG(); + + ASSERT_TRUE(execute("INSERT INTO coordinate_metadata " + "VALUES('TEST_NS','TEST','my desc','EPSG',4326," + "NULL,2020.1,0);")) + << last_error(); + + const std::string wkt = + "GEOGCRS[\"WGS 84\",\n" + " ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n" + " MEMBER[\"World Geodetic System 1984 (Transit)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G730)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G873)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1150)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1674)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G1762)\"],\n" + " MEMBER[\"World Geodetic System 1984 (G2139)\"],\n" + " ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ENSEMBLEACCURACY[2.0]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " USAGE[\n" + " SCOPE[\"Horizontal component of 3D system.\"],\n" + " AREA[\"World.\"],\n" + " BBOX[-90,-180,90,180]],\n" + " ID[\"EPSG\",4326]]"; + ASSERT_TRUE(execute("INSERT INTO coordinate_metadata " + "VALUES('TEST_NS','TEST2','my desc',NULL,NULL," + "'" + + wkt + "',2021.1,0);")) + << last_error(); + + ASSERT_TRUE(execute("INSERT INTO coordinate_metadata " + "VALUES('TEST_NS','TEST_NO_EPOCH','my desc'," + "'EPSG',4326,NULL,NULL,0);")) + << last_error(); + + auto dbContext = DatabaseContext::create(m_ctxt); + auto factoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + auto crs_4326 = factoryEPSG->createCoordinateReferenceSystem("4326"); + auto factory = AuthorityFactory::create(dbContext, "TEST_NS"); + { + auto cm = factory->createCoordinateMetadata("TEST"); + EXPECT_TRUE(cm->crs()->isEquivalentTo(crs_4326.get())); + EXPECT_TRUE(cm->coordinateEpoch().has_value()); + EXPECT_NEAR(cm->coordinateEpochAsDecimalYear(), 2020.1, 1e-10); + } + { + auto cm = factory->createCoordinateMetadata("TEST2"); + EXPECT_TRUE(cm->crs()->isEquivalentTo( + crs_4326.get(), IComparable::Criterion::EQUIVALENT)); + EXPECT_TRUE(cm->coordinateEpoch().has_value()); + EXPECT_NEAR(cm->coordinateEpochAsDecimalYear(), 2021.1, 1e-10); + } + { + auto cm = factory->createCoordinateMetadata("TEST_NO_EPOCH"); + EXPECT_TRUE(cm->crs()->isEquivalentTo(crs_4326.get())); + EXPECT_FALSE(cm->coordinateEpoch().has_value()); + } + { + auto obj = createFromUserInput( + "urn:ogc:def:coordinateMetadata:TEST_NS::TEST", dbContext, true); + auto cm = dynamic_cast(obj.get()); + ASSERT_TRUE(cm != nullptr); + EXPECT_TRUE(cm->crs()->isEquivalentTo(crs_4326.get())); + EXPECT_TRUE(cm->coordinateEpoch().has_value()); + EXPECT_NEAR(cm->coordinateEpochAsDecimalYear(), 2020.1, 1e-10); + } +} + +// --------------------------------------------------------------------------- + TEST(factory, attachExtraDatabases_none) { auto ctxt = DatabaseContext::create(std::string(), {}); auto factory = AuthorityFactory::create(ctxt, "EPSG"); @@ -4652,4 +4788,16 @@ TEST(factory, ogc_crs) { // --------------------------------------------------------------------------- +TEST(factory, getPointMotionOperationsFor) { + auto ctxt = DatabaseContext::create(); + auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + // "NAD83(CSRS)v7" + auto crs = factory->createGeodeticCRS("8255"); + auto opList = factory->getPointMotionOperationsFor(crs, false); + ASSERT_TRUE(!opList.empty()); + EXPECT_EQ(opList.front()->identifiers().front()->code(), "9483"); +} + +// --------------------------------------------------------------------------- + } // namespace diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index f878017d8c..36bb1a39e0 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -12689,6 +12689,14 @@ TEST(io, createFromUserInput) { ASSERT_TRUE(coordinateMetadata != nullptr); EXPECT_EQ(coordinateMetadata->coordinateEpochAsDecimalYear(), 2025.1); } + + { + auto obj = createFromUserInput("EPSG:9000 @ 2025.1", dbContext); + auto coordinateMetadata = + nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(coordinateMetadata != nullptr); + EXPECT_EQ(coordinateMetadata->coordinateEpochAsDecimalYear(), 2025.1); + } } // --------------------------------------------------------------------------- @@ -16883,3 +16891,129 @@ TEST(io, EXTENSION_PROJ4) { EXPECT_EQ(crs3->exportToPROJString(PROJStringFormatter::create().get()), "+proj=utm +datum=NAD27 +zone=11 +over +type=crs"); } + +// --------------------------------------------------------------------------- + +TEST(wkt_parse, PointMotionOperation) { + auto wkt = + "POINTMOTIONOPERATION[\"Canada velocity grid v7\",\n" + " SOURCECRS[\n" + " GEOGCRS[\"NAD83(CSRS)v7\",\n" + " DATUM[\"North American Datum of 1983 (CSRS) version 7\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,3],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"ellipsoidal height (h)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",8254]]],\n" + " METHOD[\"Point motion by grid (Canada NTv2_Vel)\",\n" + " ID[\"EPSG\",1070]],\n" + " PARAMETERFILE[\"Point motion velocity grid file\",\"foo.tif\"],\n" + " OPERATIONACCURACY[0.01],\n" + " USAGE[\n" + " SCOPE[\"scope\"],\n" + " AREA[\"area\"],\n" + " BBOX[38.21,-141.01,86.46,-40.73]],\n" + " ID[\"DERIVED_FROM(EPSG)\",9483],\n" + " REMARK[\"remark.\"]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto pmo = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(pmo != nullptr); + EXPECT_EQ( + pmo->exportToWKT( + WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get()), + wkt); +} + +// --------------------------------------------------------------------------- + +TEST(json_import, PointMotionOperation) { + auto json = + "{\n" + " \"$schema\": \"foo\",\n" + " \"type\": \"PointMotionOperation\",\n" + " \"name\": \"Canada velocity grid v7\",\n" + " \"source_crs\": {\n" + " \"type\": \"GeographicCRS\",\n" + " \"name\": \"NAD83(CSRS)v7\",\n" + " \"datum\": {\n" + " \"type\": \"GeodeticReferenceFrame\",\n" + " \"name\": \"North American Datum of 1983 (CSRS) version 7\",\n" + " \"ellipsoid\": {\n" + " \"name\": \"GRS 1980\",\n" + " \"semi_major_axis\": 6378137,\n" + " \"inverse_flattening\": 298.257222101\n" + " }\n" + " },\n" + " \"coordinate_system\": {\n" + " \"subtype\": \"ellipsoidal\",\n" + " \"axis\": [\n" + " {\n" + " \"name\": \"Geodetic latitude\",\n" + " \"abbreviation\": \"Lat\",\n" + " \"direction\": \"north\",\n" + " \"unit\": \"degree\"\n" + " },\n" + " {\n" + " \"name\": \"Geodetic longitude\",\n" + " \"abbreviation\": \"Lon\",\n" + " \"direction\": \"east\",\n" + " \"unit\": \"degree\"\n" + " },\n" + " {\n" + " \"name\": \"Ellipsoidal height\",\n" + " \"abbreviation\": \"h\",\n" + " \"direction\": \"up\",\n" + " \"unit\": \"metre\"\n" + " }\n" + " ]\n" + " },\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 8254\n" + " }\n" + " },\n" + " \"method\": {\n" + " \"name\": \"Point motion by grid (Canada NTv2_Vel)\",\n" + " \"id\": {\n" + " \"authority\": \"EPSG\",\n" + " \"code\": 1070\n" + " }\n" + " },\n" + " \"parameters\": [\n" + " {\n" + " \"name\": \"Point motion velocity grid file\",\n" + " \"value\": \"foo.tif\"\n" + " }\n" + " ],\n" + " \"accuracy\": \"0.01\",\n" + " \"scope\": \"scope\",\n" + " \"area\": \"area\",\n" + " \"bbox\": {\n" + " \"south_latitude\": 38.21,\n" + " \"west_longitude\": -141.01,\n" + " \"north_latitude\": 86.46,\n" + " \"east_longitude\": -40.73\n" + " },\n" + " \"id\": {\n" + " \"authority\": \"DERIVED_FROM(EPSG)\",\n" + " \"code\": 9483\n" + " },\n" + " \"remarks\": \"remark.\"\n" + "}"; + auto obj = createFromUserInput(json, nullptr); + auto pmo = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(pmo != nullptr); + EXPECT_EQ(pmo->exportToJSON(&(JSONFormatter::create()->setSchema("foo"))), + json); +} diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 98fbd2c490..59f6a745e1 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -5777,3 +5777,69 @@ TEST(operation, export_of_boundCRS_with_proj_string_method) { "+step +proj=axisswap +order=2,1 " "+step +proj=unitconvert +xy_in=rad +xy_out=deg"); } + +// --------------------------------------------------------------------------- + +TEST(operation, PointMotionOperation_with_epochs) { + auto wkt = + "POINTMOTIONOPERATION[\"Canada velocity grid v7 from epoch 2010 to " + "epoch 2002\",\n" + " SOURCECRS[\n" + " GEOGCRS[\"NAD83(CSRS)v7\",\n" + " DATUM[\"North American Datum of 1983 (CSRS) version 7\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,3],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"ellipsoidal height (h)\",up,\n" + " ORDER[3],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " ID[\"EPSG\",8254]]],\n" + " METHOD[\"Point motion by grid (Canada NTv2_Vel)\",\n" + " ID[\"EPSG\",1070]],\n" + " PARAMETERFILE[\"Point motion velocity grid " + "file\",\"ca_nrc_NAD83v70VG.tif\"],\n" + " OPERATIONACCURACY[0.01],\n" + " ID[\"DERIVED_FROM(EPSG)\",9483],\n" + " REMARK[\"File initially published with name cvg70.cvb, later " + "renamed to NAD83v70VG.gvb with no change of content.\"]]"; + + auto obj = WKTParser().createFromWKT(wkt); + auto pmo = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(pmo != nullptr); + EXPECT_EQ(pmo->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_fwd " + "+step +proj=deformation +dt=-8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); + + EXPECT_EQ(pmo->inverse()->nameStr(), + "Canada velocity grid v7 from epoch 2002 to epoch 2010"); + EXPECT_EQ( + pmo->inverse()->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); +} diff --git a/test/unit/test_operationfactory.cpp b/test/unit/test_operationfactory.cpp index 9b67342a63..8e093f2c0d 100644 --- a/test/unit/test_operationfactory.cpp +++ b/test/unit/test_operationfactory.cpp @@ -37,6 +37,7 @@ #include "proj/common.hpp" #include "proj/coordinateoperation.hpp" +#include "proj/coordinates.hpp" #include "proj/coordinatesystem.hpp" #include "proj/crs.hpp" #include "proj/datum.hpp" @@ -52,6 +53,7 @@ #include using namespace osgeo::proj::common; +using namespace osgeo::proj::coordinates; using namespace osgeo::proj::crs; using namespace osgeo::proj::cs; using namespace osgeo::proj::datum; @@ -7216,6 +7218,77 @@ TEST(operation, // --------------------------------------------------------------------------- +TEST(operation, compoundCRS_of_vertCRS_with_geoid_model_by_id_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(CSRS) / MTM zone 7 + CGVD28 height\",\n" + " PROJCRS[\"NAD83(CSRS) / MTM zone 7\",\n" + " BASEGEOGCRS[\"NAD83(CSRS)\",\n" + " DATUM[\"North American Datum of 1983 (CSRS)\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]],\n" + " CONVERSION[\"MTM zone 7\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",-70.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9999,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",304800,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"easting (E(X))\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1]],\n" + " AXIS[\"northing (N(Y))\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " VERTCRS[\"CGVD28 height\",\n" + " VDATUM[\"Canadian Geodetic Vertical Datum of 1928\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " GEOIDMODEL[\"HT2_2002v70\",\n" + " ID[\"EPSG\",9985]],\n" + " ID[\"EPSG\",5713]]]"; + auto srcObj = + createFromUserInput(wkt, authFactory->databaseContext(), false); + auto src = nn_dynamic_pointer_cast(srcObj); + ASSERT_TRUE(src != nullptr); + auto dst = + authFactory->createCoordinateReferenceSystem("4955")->promoteTo3D( + std::string(), authFactory->databaseContext()); // NAD83(CSRS) 3d + + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src), dst, ctxt); + ASSERT_TRUE(!list.empty()); + EXPECT_EQ(list[0]->nameStr(), + "Inverse of MTM zone 7 + " + "Ballpark geographic offset from NAD83(CSRS) to NAD83(CSRS)v4 + " + "Inverse of NAD83(CSRS)v4 to CGVD28 height (1) + " + "Ballpark geographic offset from NAD83(CSRS)v4 to NAD83(CSRS)"); +} + +// --------------------------------------------------------------------------- + TEST(operation, compoundCRS_of_bound_horizCRS_and_bound_vertCRS_to_geogCRS) { auto authFactory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); @@ -9307,3 +9380,666 @@ TEST(operation, "+step +proj=affine +xoff=20 " "+step +proj=axisswap +order=2,1"); } + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_point_motion_operation_geog2D) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // "NAD83(CSRS)v7" + auto crs = factory->createCoordinateReferenceSystem("8255"); + auto crs_2002 = CoordinateMetadata::create(crs, 2002.0, dbContext); + auto crs_2010 = CoordinateMetadata::create(crs, 2010.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2002, crs_2010, ctxt); + ASSERT_EQ(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2002 " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1 " + "+step +proj=set +v_4=2010"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_point_motion_operation_geog3D) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // "NAD83(CSRS)v7" + auto crs = factory->createCoordinateReferenceSystem("8254"); + auto crs_2002 = CoordinateMetadata::create(crs, 2002.0, dbContext); + auto crs_2010 = CoordinateMetadata::create(crs, 2010.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2002, crs_2010, ctxt); + ASSERT_EQ(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_point_motion_operation_geocentric) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // "NAD83(CSRS)v7" + auto crs = factory->createCoordinateReferenceSystem("8253"); + auto crs_2002 = CoordinateMetadata::create(crs, 2002.0, dbContext); + auto crs_2010 = CoordinateMetadata::create(crs, 2010.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2002, crs_2010, ctxt); + ASSERT_EQ(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2002 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +proj=set +v_4=2010"); + EXPECT_EQ(list[0]->inverse()->exportToPROJString( + PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2010 " + "+step +proj=set +v_4=2010 +omit_fwd " + "+step +proj=deformation +dt=-8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_inv " + "+step +proj=set +v_4=2002"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_point_motion_operation_geocentric_to_geog3D) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // "NAD83(CSRS)v7" + auto crs_geocentric = factory->createCoordinateReferenceSystem("8253"); + auto crs_2002 = + CoordinateMetadata::create(crs_geocentric, 2002.0, dbContext); + auto crs_geog3d = factory->createCoordinateReferenceSystem("8254"); + auto crs_2010 = CoordinateMetadata::create(crs_geog3d, 2010.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2002, crs_2010, ctxt); + ASSERT_EQ(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2002 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=8 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1 " + "+step +proj=set +v_4=2010"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); + EXPECT_EQ(list[1]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_point_motion_operation_NAD83_CSRS_v7_TO_ITRF2014) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // "NAD83(CSRS)v7" + auto sourceCRS = factory->createCoordinateReferenceSystem("8254"); + auto crs_2002 = CoordinateMetadata::create(sourceCRS, 2002.0, dbContext); + // ITRF2014 + auto targetCRS = factory->createCoordinateReferenceSystem("7912"); + auto crs_2005 = CoordinateMetadata::create(targetCRS, 2005.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2002, crs_2005, ctxt); + ASSERT_GE(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2002 " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_fwd " + "+step +proj=deformation +dt=3 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2005 +omit_inv " + "+step +inv +proj=helmert " + "+x=1.0053 +y=-1.90921 +z=-0.54157 +rx=-0.02678138 " + "+ry=0.00042027 +rz=-0.01093206 +s=0.00036891 +dx=0.00079 +dy=-0.0006 " + "+dz=-0.00144 +drx=-6.667e-05 +dry=0.00075744 +drz=5.133e-05 " + "+ds=-7.201e-05 +t_epoch=2010 +convention=position_vector " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1 " + "+step +proj=set +v_4=2005"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_point_motion_operation_ITRF2014_to_NAD83_CSRS_v7) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + // ITRF2014 + auto sourceCRS = factory->createCoordinateReferenceSystem("7912"); + auto crs_2005 = CoordinateMetadata::create(sourceCRS, 2005.0, dbContext); + // "NAD83(CSRS)v7" + auto targetCRS = factory->createCoordinateReferenceSystem("8254"); + auto crs_2002 = CoordinateMetadata::create(targetCRS, 2002.0, dbContext); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + crs_2005, crs_2002, ctxt); + ASSERT_GE(list.size(), 2U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=2005 " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=helmert +x=1.0053 +y=-1.90921 +z=-0.54157 +rx=-0.02678138 " + "+ry=0.00042027 +rz=-0.01093206 +s=0.00036891 +dx=0.00079 +dy=-0.0006 " + "+dz=-0.00144 +drx=-6.667e-05 +dry=0.00075744 +drz=5.133e-05 " + "+ds=-7.201e-05 +t_epoch=2010 +convention=position_vector " + "+step +proj=set +v_4=2005 +omit_fwd " + "+step +proj=deformation +dt=-3 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1 " + "+step +proj=set +v_4=2002"); + EXPECT_TRUE(list[1]->hasBallparkTransformation()); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_compound_to_compound_with_point_motion_operation) { + auto dbContext = DatabaseContext::create(); + auto factoryNRCAN = AuthorityFactory::create(dbContext, "NRCAN"); + auto sourceCM = + factoryNRCAN->createCoordinateMetadata("NAD83_CSRS_1997_MTM7_HT2_1997"); + auto targetCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_2010_UTM19_CGVD2013_2010"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCM, targetCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=1997 " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_HT2_1997.tif +multiplier=1 " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=1997 +omit_fwd " + "+step +proj=deformation +dt=13 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2010 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +inv +proj=vgridshift +grids=ca_nrc_CGG2013an83.tif " + "+multiplier=1 " + "+step +proj=utm +zone=19 +ellps=GRS80 " + "+step +proj=set +v_4=2010"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_compound_to_compound_with_epoch_HT2_1997_CGG2013a) { + auto dbContext = DatabaseContext::create(); + auto factoryNRCAN = AuthorityFactory::create(dbContext, "NRCAN"); + auto sourceCM = + factoryNRCAN->createCoordinateMetadata("NAD83_CSRS_1997_MTM7_HT2_1997"); + auto targetCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_1997_UTM19_CGVD2013_1997"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + { + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCM, targetCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_HT2_1997_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=utm +zone=19 +ellps=GRS80"); + } + { + auto list = CoordinateOperationFactory::create()->createOperations( + targetCM, sourceCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=utm +zone=19 +ellps=GRS80 " + "+step +inv +proj=vgridshift " + "+grids=ca_nrc_HT2_1997_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_compound_to_compound_with_epoch_HT2_2002_CGG2013a) { + auto dbContext = DatabaseContext::create(); + auto factoryNRCAN = AuthorityFactory::create(dbContext, "NRCAN"); + auto sourceCM = + factoryNRCAN->createCoordinateMetadata("NAD83_CSRS_2002_MTM7_HT2_2002"); + auto targetCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_2002_UTM19_CGVD2013_2002"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + { + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCM, targetCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_HT2_2002v70_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=utm +zone=19 +ellps=GRS80"); + } + { + auto list = CoordinateOperationFactory::create()->createOperations( + targetCM, sourceCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=utm +zone=19 +ellps=GRS80 " + "+step +inv +proj=vgridshift " + "+grids=ca_nrc_HT2_2002v70_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80"); + } +} + +// --------------------------------------------------------------------------- + +TEST(operation, + createOperation_compound_to_compound_with_epoch_HT2_2010_CGG2013a) { + auto dbContext = DatabaseContext::create(); + auto factoryNRCAN = AuthorityFactory::create(dbContext, "NRCAN"); + auto sourceCM = + factoryNRCAN->createCoordinateMetadata("NAD83_CSRS_2010_MTM7_HT2_2010"); + auto targetCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_2010_UTM19_CGVD2013_2010"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + { + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCM, targetCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_HT2_2010v70_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=utm +zone=19 +ellps=GRS80"); + } + { + auto list = CoordinateOperationFactory::create()->createOperations( + targetCM, sourceCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ( + list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=utm +zone=19 +ellps=GRS80 " + "+step +inv +proj=vgridshift " + "+grids=ca_nrc_HT2_2010v70_CGG2013a.tif " + "+multiplier=-1 " + "+step +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80"); + } +} + +// --------------------------------------------------------------------------- + +TEST( + operation, + createOperation_compound_to_compound_with_Geographic3D_Offset_by_velocity_grid) { + auto dbContext = DatabaseContext::create(); + auto wkt = + "COMPOUNDCRS[\"NAD83(CSRS)v3 / MTM zone 7 + CGVD28 height\",\n" + " PROJCRS[\"NAD83(CSRS)v3 / MTM zone 7\",\n" + " BASEGEOGCRS[\"NAD83(CSRS)v3\",\n" + " DATUM[\"North American Datum of 1983 (CSRS) version 3\",\n" + " ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",8240]],\n" + " CONVERSION[\"MTM zone 7\",\n" + " METHOD[\"Transverse Mercator\",\n" + " ID[\"EPSG\",9807]],\n" + " PARAMETER[\"Latitude of natural origin\",0,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8801]],\n" + " PARAMETER[\"Longitude of natural origin\",-70.5,\n" + " ANGLEUNIT[\"degree\",0.0174532925199433],\n" + " ID[\"EPSG\",8802]],\n" + " PARAMETER[\"Scale factor at natural origin\",0.9999,\n" + " SCALEUNIT[\"unity\",1],\n" + " ID[\"EPSG\",8805]],\n" + " PARAMETER[\"False easting\",304800,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8806]],\n" + " PARAMETER[\"False northing\",0,\n" + " LENGTHUNIT[\"metre\",1],\n" + " ID[\"EPSG\",8807]]],\n" + " CS[Cartesian,2],\n" + " AXIS[\"easting (E(X))\",east,\n" + " ORDER[1],\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]],\n" + " AXIS[\"northing (N(Y))\",north,\n" + " ORDER[2],\n" + " LENGTHUNIT[\"metre\",1,\n" + " ID[\"EPSG\",9001]]]],\n" + " VERTCRS[\"CGVD28 height\",\n" + " VDATUM[\"Canadian Geodetic Vertical Datum of 1928\"],\n" + " CS[vertical,1],\n" + " AXIS[\"gravity-related height (H)\",up,\n" + " LENGTHUNIT[\"metre\",1]],\n" + " GEOIDMODEL[\"HT2_1997\",\n" + " ID[\"EPSG\",9983]],\n" + " ID[\"EPSG\",5713]]]"; + auto objSrc = WKTParser().createFromWKT(wkt); + auto src = nn_dynamic_pointer_cast(objSrc); + ASSERT_TRUE(src != nullptr); + + auto objDest = createFromUserInput( + "NAD83(CSRS)v7 / UTM zone 19 + CGVD2013a(2010) height", dbContext); + auto dst = nn_dynamic_pointer_cast(objDest); + ASSERT_TRUE(dst != nullptr); + + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src), NN_NO_CHECK(dst), ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + // Very similar output pipeline as + // createOperation_compound_to_compound_with_point_motion_operation + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +inv +proj=tmerc +lat_0=0 +lon_0=-70.5 +k=0.9999 " + "+x_0=304800 +y_0=0 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_HT2_1997.tif +multiplier=1 " + "+step +proj=cart +ellps=GRS80 " + "+step +inv +proj=deformation +dt=-13 " + "+grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +inv +proj=vgridshift +grids=ca_nrc_CGG2013an83.tif " + "+multiplier=1 " + "+step +proj=utm +zone=19 +ellps=GRS80"); +} + +// --------------------------------------------------------------------------- + +TEST( + operation, + createOperation_compound_to_compound_with_point_motion_operation_special_case_CGVD2013a) { + auto dbContext = DatabaseContext::create(); + auto factoryNRCAN = AuthorityFactory::create(dbContext, "NRCAN"); + auto sourceCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_1997_UTM17_CGVD2013_1997"); + auto targetCM = factoryNRCAN->createCoordinateMetadata( + "NAD83_CSRS_2002_UTM17_CGVD2013_2002"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCM, targetCM, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=set +v_4=1997 " + "+step +inv +proj=utm +zone=17 +ellps=GRS80 " + "+step +proj=vgridshift +grids=ca_nrc_CGG2013an83.tif " + "+multiplier=1 " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=set +v_4=1997 +omit_fwd " + "+step +proj=deformation +dt=5 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +proj=set +v_4=2002 +omit_inv " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +inv +proj=vgridshift +grids=ca_nrc_CGG2013an83.tif " + "+multiplier=1 " + "+step +proj=utm +zone=17 +ellps=GRS80 " + "+step +proj=set +v_4=2002"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_Geographic3D_Offset_by_velocity_grid) { + auto dbContext = DatabaseContext::create(); + auto factoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + auto sourceCRS = + factoryEPSG->createCoordinateReferenceSystem("8254"); // NAD83(CSRS)v7 + auto targetCRS = + factoryEPSG->createCoordinateReferenceSystem("8239"); // NAD83(CSRS)v3 + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCRS, targetCRS, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +z_in=m +xy_out=rad +z_out=m " + "+step +proj=cart +ellps=GRS80 " + "+step +proj=deformation +dt=-13 +grids=ca_nrc_NAD83v70VG.tif " + "+ellps=GRS80 " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=unitconvert +xy_in=rad +z_in=m +xy_out=deg +z_out=m " + "+step +proj=axisswap +order=2,1"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_test_createOperationsWithDatumPivot_iter_1) { + // Test + // CoordinateOperationFactory::Private::createOperationsWithDatumPivot() + // iter=1, ie getType(candidateSrcGeod) == getType(candidateDstGeod) + auto dbContext = DatabaseContext::create(); + auto factoryEPSG = AuthorityFactory::create(dbContext, "EPSG"); + // NAD83(CSRS)v2 (2D) + auto sourceCRS = factoryEPSG->createCoordinateReferenceSystem("8237"); + // NAD83(CSRS)v3 (2D) + auto targetCRS = factoryEPSG->createCoordinateReferenceSystem("8240"); + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + // Do *NOT* set SpatialCriterion::PARTIAL_INTERSECTION, otherwise we'd + // get the direct operations + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + sourceCRS, targetCRS, ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_STREQ(list[0]->nameStr().c_str(), + "Inverse of NAD83(CSRS)v8 to NAD83(CSRS)v2 (1) + " + "NAD83(CSRS)v8 to NAD83(CSRS)v3 (1)"); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=noop"); +} + +// --------------------------------------------------------------------------- + +TEST(operation, createOperation_Vrtical_Offset_by_velocity_grid) { + auto dbContext = DatabaseContext::create(); + + auto objSrc = createFromUserInput("NAD83(CSRS)v7 + CGVD2013a(2002) height", + dbContext); + auto src = nn_dynamic_pointer_cast(objSrc); + ASSERT_TRUE(src != nullptr); + + auto objDest = createFromUserInput("NAD83(CSRS)v7 + CGVD2013a(2010) height", + dbContext); + auto dst = nn_dynamic_pointer_cast(objDest); + ASSERT_TRUE(dst != nullptr); + + auto ctxt = CoordinateOperationContext::create( + AuthorityFactory::create(dbContext, std::string()), nullptr, 0); + ctxt->setSpatialCriterion( + CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + ctxt->setGridAvailabilityUse( + CoordinateOperationContext::GridAvailabilityUse:: + IGNORE_GRID_AVAILABILITY); + auto list = CoordinateOperationFactory::create()->createOperations( + NN_NO_CHECK(src), NN_NO_CHECK(dst), ctxt); + ASSERT_GE(list.size(), 1U); + EXPECT_FALSE(list[0]->hasBallparkTransformation()); + EXPECT_EQ(list[0]->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +proj=push +v_1 +v_2 " + "+step +proj=cart +ellps=GRS80 " + "+step +inv +proj=deformation +dt=-8 " + "+grids=ca_nrc_NAD83v70VG.tif +ellps=GRS80 " + "+step +inv +proj=cart +ellps=GRS80 " + "+step +proj=pop +v_1 +v_2 " + "+step +proj=unitconvert +xy_in=rad +xy_out=deg " + "+step +proj=axisswap +order=2,1"); +}