aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/ProjTest.cmake2
-rw-r--r--data/sql/alias_name.sql27
-rw-r--r--data/sql/conversion.sql50
-rw-r--r--data/sql/extent.sql29
-rw-r--r--data/sql/metadata.sql4
-rw-r--r--data/sql/projected_crs.sql54
-rw-r--r--docs/source/install.rst24
-rw-r--r--include/proj/crs.hpp2
-rw-r--r--include/proj/internal/Makefile.am1
-rw-r--r--include/proj/internal/crs_internal.hpp41
-rw-r--r--src/CMakeLists.txt20
-rw-r--r--src/Makefile.am12
-rw-r--r--src/apps/geod.cpp3
-rw-r--r--src/apps/proj.cpp3
-rw-r--r--src/bin_geod.cmake35
-rw-r--r--src/bin_gie.cmake6
-rw-r--r--src/bin_proj.cmake35
-rw-r--r--src/check_md5sum.cmake7
-rw-r--r--src/generate_wkt_parser.cmake18
-rw-r--r--src/iso19111/c_api.cpp7
-rw-r--r--src/iso19111/crs.cpp247
-rw-r--r--src/iso19111/datum.cpp48
-rw-r--r--src/iso19111/io.cpp47
-rw-r--r--src/iso19111/operation/concatenatedoperation.cpp36
-rw-r--r--src/iso19111/operation/coordinateoperationfactory.cpp7
-rw-r--r--src/iso19111/operation/parammappings.cpp5
-rw-r--r--src/iso19111/operation/singleoperation.cpp15
-rw-r--r--src/lib_proj.cmake54
-rw-r--r--test/cli/CMakeLists.txt2
-rw-r--r--test/cli/Makefile.am8
-rwxr-xr-xtest/cli/testinvproj55
-rw-r--r--test/cli/testinvproj_out.dist1
-rwxr-xr-xtest/cli/testproj2
-rwxr-xr-xtest/fuzzers/build.sh16
-rw-r--r--test/unit/test_crs.cpp88
-rw-r--r--test/unit/test_io.cpp31
-rw-r--r--test/unit/test_operation.cpp17
37 files changed, 933 insertions, 126 deletions
diff --git a/cmake/ProjTest.cmake b/cmake/ProjTest.cmake
index 542c7440..c632d539 100644
--- a/cmake/ProjTest.cmake
+++ b/cmake/ProjTest.cmake
@@ -39,7 +39,7 @@ endfunction()
# Create user writable directory for tests
add_custom_target(create_tmp_user_writable_dir ALL
- COMMAND ${CMAKE_COMMAND} -E make_directory {PROJ_BINARY_DIR}/tmp_user_writable_dir)
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJ_BINARY_DIR}/tmp_user_writable_dir)
function(proj_add_gie_network_dependent_test TESTNAME TESTCASE)
diff --git a/data/sql/alias_name.sql b/data/sql/alias_name.sql
index 5d58135c..0fc8b6e7 100644
--- a/data/sql/alias_name.sql
+++ b/data/sql/alias_name.sql
@@ -7413,6 +7413,22 @@ INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','8252','NAD83(CSRS) 2010',
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','8253','NAD83(CSRS) 2010','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','8254','NAD83(CSRS) 2010','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','8255','NAD83(CSRS) 2010','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9831','UA_UCS_2000 / LCS_01','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9832','UA_UCS_2000 / LCS_05','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9833','UA_UCS_2000 / LCS_07','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9834','UA_UCS_2000 / LCS_12','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9836','UA_UCS_2000 / LCS_18','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9835','UA_UCS_2000 / LCS_14','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9837','UA_UCS_2000 / LCS_21','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9838','UA_UCS_2000 / LCS_23','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9839','UA_UCS_2000 / LCS_26','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9840','UA_UCS_2000 / LCS_35','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9841','UA_UCS_2000 / LCS_44','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9851','UA_UCS_2000 / LCS_46','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9852','UA_UCS_2000 / LCS_48','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9853','UA_UCS_2000 / LCS_51','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9856','UA_UCS_2000 / LCS_59','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9854','UA_UCS_2000 / LCS_53','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','9331','777','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','9331','KSA-GRF17 - XYZ','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','9332','778','EPSG');
@@ -7421,3 +7437,14 @@ INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','9333','779','EPSG');
INSERT INTO "alias_name" VALUES('geodetic_crs','EPSG','9333','KSA-GRF17 - LatLon','EPSG');
INSERT INTO "alias_name" VALUES('vertical_crs','EPSG','9335','780','EPSG');
INSERT INTO "alias_name" VALUES('vertical_crs','EPSG','9335','KSA-VRF14 - OHt','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9858','UA_UCS_2000 / LCS_63','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9857','UA_UCS_2000 / LCS_61','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9859','UA_UCS_2000 / LCS_65','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9865','UA_UCS_2000 / LCS_85','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9863','UA_UCS_2000 / LCS_74','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9862','UA_UCS_2000 / LCS_73','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9861','UA_UCS_2000 / LCS_71','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9821','UA_UCS_2000 / LCS_32','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9860','UA_UCS_2000 / LCS_68','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9855','UA_UCS_2000 / LCS_56','EPSG');
+INSERT INTO "alias_name" VALUES('projected_crs','EPSG','9864','UA_UCS_2000 / LCS_80','EPSG');
diff --git a/data/sql/conversion.sql b/data/sql/conversion.sql
index 422d240e..206299d9 100644
--- a/data/sql/conversion.sql
+++ b/data/sql/conversion.sql
@@ -1750,6 +1750,56 @@ INSERT INTO "conversion" VALUES('EPSG','9760','ECML14_NB-TM','In conjunction wit
INSERT INTO "usage" VALUES('EPSG','16497','conversion','EPSG','9760','EPSG','4621','EPSG','1141');
INSERT INTO "conversion" VALUES('EPSG','9765','EWR2-TM','In conjunction with transformation ETRS89 to EWR2-IRF (1) (code 9764), emulates the EWR2 Snake projection.','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',51.57,'EPSG','9110','EPSG','8802','Longitude of natural origin',-0.54,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',192519.9715,'EPSG','9001','EPSG','8807','False northing',146942.6806,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
INSERT INTO "usage" VALUES('EPSG','16511','conversion','EPSG','9765','EPSG','4622','EPSG','1141');
+INSERT INTO "conversion" VALUES('EPSG','9796','Local coordinate system of Kyiv','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',30.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16909','conversion','EPSG','9796','EPSG','4650','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9797','Local coordinate system of Crimea region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',34.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16876','conversion','EPSG','9797','EPSG','4648','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9798','Local coordinate system of Vinnytsia region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',28.4,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16877','conversion','EPSG','9798','EPSG','4643','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9799','Local coordinate system of Volyn region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',24.5,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16878','conversion','EPSG','9799','EPSG','4644','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9800','Local coordinate system of Dnipropetrovsk region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',35.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16879','conversion','EPSG','9800','EPSG','4627','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9801','Local coordinate system of Donetsk region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',37.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16880','conversion','EPSG','9801','EPSG','4628','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9802','Local coordinate system of Zhytomyr region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',28.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16881','conversion','EPSG','9802','EPSG','4647','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9803','Local coordinate system of Zakarpattia region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',23.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16882','conversion','EPSG','9803','EPSG','4645','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9804','Local coordinate system of Zaporizhzhia region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',36.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16883','conversion','EPSG','9804','EPSG','4646','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9805','Local coordinate system of Ivano-Frankivsk region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',24.45,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16884','conversion','EPSG','9805','EPSG','4629','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9806','Local coordinate system of Kirovohrad region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',32.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16885','conversion','EPSG','9806','EPSG','4634','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9807','Local coordinate system of Luhansk region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',39.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16886','conversion','EPSG','9807','EPSG','4635','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9808','Local coordinate system of Lviv region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',24.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16887','conversion','EPSG','9808','EPSG','4636','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9809','Local coordinate system of Mykolaiv region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',31.5,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16888','conversion','EPSG','9809','EPSG','4637','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9810','Local coordinate system of Odessa region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',30.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16889','conversion','EPSG','9810','EPSG','4638','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9811','Local coordinate system of Poltava region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',33.5,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16890','conversion','EPSG','9811','EPSG','4639','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9812','Local coordinate system of Rivne and Khmelnytsky regions','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',27.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16908','conversion','EPSG','9812','EPSG','4651','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9813','Local coordinate system of Sumy region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',34.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16895','conversion','EPSG','9813','EPSG','4641','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9814','Local coordinate system of Ternopil region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',25.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16896','conversion','EPSG','9814','EPSG','4642','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9815','Local coordinate system of Kharkiv region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',36.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16897','conversion','EPSG','9815','EPSG','4630','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9816','Local coordinate system of Kherson region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',33.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16898','conversion','EPSG','9816','EPSG','4631','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9817','Local coordinate system of Cherkasy region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9110','EPSG','8802','Longitude of natural origin',31.3,'EPSG','9110','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16899','conversion','EPSG','9817','EPSG','4624','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9818','Local coordinate system of Chernivtsi region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',26.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16900','conversion','EPSG','9818','EPSG','4626','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9819','Local coordinate system of Chernihiv region','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',32.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16901','conversion','EPSG','9819','EPSG','4625','EPSG','1207');
+INSERT INTO "conversion" VALUES('EPSG','9820','Local coordinate system of Sevastopol city','','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',33.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',1.0,'EPSG','9201','EPSG','8806','False easting',300000.0,'EPSG','9001','EPSG','8807','False northing',0.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16902','conversion','EPSG','9820','EPSG','4649','EPSG','1207');
INSERT INTO "conversion" VALUES('EPSG','9872','Papua New Guinea Map Grid 1994 zone 57','Grid convergence uses opposite sign convention to UTM.','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',159.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',0.9996,'EPSG','9201','EPSG','8806','False easting',500000.0,'EPSG','9001','EPSG','8807','False northing',10000000.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
INSERT INTO "usage" VALUES('EPSG','16917','conversion','EPSG','9872','EPSG','4653','EPSG','1092');
INSERT INTO "conversion" VALUES('EPSG','9873','Papua New Guinea Map Grid 1994 zone 58','Grid convergence uses opposite sign convention to UTM.','EPSG','9807','Transverse Mercator','EPSG','8801','Latitude of natural origin',0.0,'EPSG','9102','EPSG','8802','Longitude of natural origin',165.0,'EPSG','9102','EPSG','8805','Scale factor at natural origin',0.9996,'EPSG','9201','EPSG','8806','False easting',500000.0,'EPSG','9001','EPSG','8807','False northing',10000000.0,'EPSG','9001',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,0);
diff --git a/data/sql/extent.sql b/data/sql/extent.sql
index ec9ddb85..73657051 100644
--- a/data/sql/extent.sql
+++ b/data/sql/extent.sql
@@ -3598,5 +3598,34 @@ INSERT INTO "extent" VALUES('EPSG','4619','Italy - 6°22''E to 18°40''E and nor
INSERT INTO "extent" VALUES('EPSG','4620','UK - Tweedmouth to Aberdeen','United Kingdom (UK) - on or related to the complex of rail routes in the East of Scotland, incorporating the route from Tweedbank through the Borders to Edinburgh; the line from Edinburgh to Aberdeen; routes via Kirkaldy and Cowdenbeath; and routes via Leuchars and Perth to Dundee. ',55.55,57.2,-3.55,-1.95,0);
INSERT INTO "extent" VALUES('EPSG','4621','UK - Newcastle to Ashington','United Kingdom (UK) - on or related to rail routes from Newcastle Central to Ashington via Benton North Junction, and the section from Bedlington to Morpeth.',54.85,55.3,-1.9,-1.3,0);
INSERT INTO "extent" VALUES('EPSG','4622','UK - Oxford to Bedford','United Kingdom (UK) - on or related to East West Rail (Phase 2) routes from Oxford to Bicester, Bletchley and Bedford, and from Claydon Junction to Aylesbury and Princes Risborough.',51.7,52.24,-1.43,-0.36,0);
+INSERT INTO "extent" VALUES('EPSG','4623','Ukraine - Kyiv city','Ukraine - Kyiv (Kiev) city.',50.21,50.59,30.23,30.83,0);
+INSERT INTO "extent" VALUES('EPSG','4624','Ukraine - Cherkasy oblast','Ukraine - Cherkasy region (oblast).',48.45,50.23,29.6,32.89,0);
+INSERT INTO "extent" VALUES('EPSG','4625','Ukraine - Chernihiv oblast','Ukraine - Chernihiv region (oblast).',50.34,52.38,30.48,33.51,0);
+INSERT INTO "extent" VALUES('EPSG','4626','Ukraine - Chernivtsi oblast','Ukraine - Chernivtsi region (oblast).',47.72,48.68,24.9,27.53,0);
+INSERT INTO "extent" VALUES('EPSG','4627','Ukraine - Dnipropetrovsk oblast','Ukraine - Dnipropetrovsk region (oblast).',47.45,49.19,32.95,36.94,0);
+INSERT INTO "extent" VALUES('EPSG','4628','Ukraine - Donetsk oblast','Ukraine - Donetsk region (oblast).',46.86,49.23,36.54,39.09,0);
+INSERT INTO "extent" VALUES('EPSG','4629','Ukraine - Ivano-Frankivsk oblast','Ukraine - Ivano-Frankivsk region (oblast).',47.72,49.56,23.53,25.65,0);
+INSERT INTO "extent" VALUES('EPSG','4630','Ukraine - Kharkiv oblast','Ukraine - Kharkiv region (oblast).',48.52,50.46,34.84,38.1,0);
+INSERT INTO "extent" VALUES('EPSG','4631','Ukraine - Kherson oblast','Ukraine - Kherson region (oblast).',45.75,47.6,31.51,35.1,0);
+INSERT INTO "extent" VALUES('EPSG','4632','Ukraine - Khmelnytskyi oblast','Ukraine - Khmelnytskyi region (oblast).',48.45,50.59,26.13,27.9,0);
+INSERT INTO "extent" VALUES('EPSG','4633','Ukraine - Kyiv oblast','Ukraine - Kyiv (Kiev) region (oblast).',49.17,51.55,29.26,32.16,0);
+INSERT INTO "extent" VALUES('EPSG','4634','Ukraine - Kirovohrad oblast','Ukraine - Kirovohrad region (oblast).',47.74,49.25,29.74,33.9,0);
+INSERT INTO "extent" VALUES('EPSG','4635','Ukraine - Luhansk oblast','Ukraine - Luhansk region (oblast).',47.82,50.09,37.83,40.23,0);
+INSERT INTO "extent" VALUES('EPSG','4636','Ukraine - Lviv oblast','Ukraine - Lviv region (oblast).',48.71,50.65,22.64,25.42,0);
+INSERT INTO "extent" VALUES('EPSG','4637','Ukraine - Mykolaiv oblast','Ukraine - Mykolaiv region (oblast).',46.36,48.23,30.2,33.18,0);
+INSERT INTO "extent" VALUES('EPSG','4638','Ukraine - Odessa oblast','Ukraine - Odessa region (oblast).',45.2,48.23,28.21,31.29,0);
+INSERT INTO "extent" VALUES('EPSG','4639','Ukraine - Poltava oblast','Ukraine - Poltava region (oblast).',48.74,50.55,32.08,35.48,0);
+INSERT INTO "extent" VALUES('EPSG','4640','Ukraine - Rivne oblast','Ukraine - Rivne region (oblast).',50.0,51.95,25.08,27.73,0);
+INSERT INTO "extent" VALUES('EPSG','4641','Ukraine - Sumy oblast','Ukraine - Sumy region (oblast).',50.1,52.37,32.93,35.67,0);
+INSERT INTO "extent" VALUES('EPSG','4642','Ukraine - Ternopil oblast','Ukraine - Ternopil region (oblast).',48.5,50.27,24.71,26.44,0);
+INSERT INTO "extent" VALUES('EPSG','4643','Ukraine - Vinnytsia oblast','Ukraine - Vinnytsia region (oblast).',48.06,49.89,27.37,30.02,0);
+INSERT INTO "extent" VALUES('EPSG','4644','Ukraine - Volyn oblast','Ukraine - Volyn region (oblast).',50.28,51.97,23.6,26.1,0);
+INSERT INTO "extent" VALUES('EPSG','4645','Ukraine - Zakarpattia oblast','Ukraine - Zakarpattia (Zakarpatska) region (Transcarpathia oblast).',47.89,49.1,22.13,24.62,0);
+INSERT INTO "extent" VALUES('EPSG','4646','Ukraine - Zaporizhzhia oblast','Ukraine - Zaporizhzhia region (oblast).',46.07,48.14,34.17,37.24,0);
+INSERT INTO "extent" VALUES('EPSG','4647','Ukraine - Zhytomyr oblast','Ukraine - Zhytomyr region (oblast).',49.58,51.68,27.19,29.73,0);
+INSERT INTO "extent" VALUES('EPSG','4648','Ukraine - Crimea','Ukraine - Crimea autonomous region.',44.38,46.24,32.47,36.65,0);
+INSERT INTO "extent" VALUES('EPSG','4649','Ukraine - Sevastopol','Ukraine - Sevastopol (Sebastopol) city.',44.38,44.84,33.37,33.92,0);
+INSERT INTO "extent" VALUES('EPSG','4650','Ukraine - Kyiv city and oblast','Ukraine - Kyiv (Kiev) city and Kyiv region (oblast).',49.17,51.55,29.26,32.16,0);
+INSERT INTO "extent" VALUES('EPSG','4651','Ukraine - Rivne and Khmelnytskyi oblasts','Ukraine - Rivne and Khmelnytskyi regions (oblasts).',48.45,51.95,25.08,27.9,0);
INSERT INTO "extent" VALUES('EPSG','4653','Papua New Guinea - 156°E to 162°E','Papua New Guinea - between 156°E and 162°E, onshore and offshore.',-14.26,-1.11,156.0,162.0,0);
INSERT INTO "extent" VALUES('EPSG','4654','Papua New Guinea - east of 162°E','Papua New Guinea - east of 162°E to EEZ limit.',-4.35,-2.34,162.0,162.8,0);
diff --git a/data/sql/metadata.sql b/data/sql/metadata.sql
index f3987969..8230e7da 100644
--- a/data/sql/metadata.sql
+++ b/data/sql/metadata.sql
@@ -9,8 +9,8 @@
INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MAJOR', 1);
INSERT INTO "metadata" VALUES('DATABASE.LAYOUT.VERSION.MINOR', 2);
-INSERT INTO "metadata" VALUES('EPSG.VERSION', 'v10.036');
-INSERT INTO "metadata" VALUES('EPSG.DATE', '2021-09-23');
+INSERT INTO "metadata" VALUES('EPSG.VERSION', 'v10.037');
+INSERT INTO "metadata" VALUES('EPSG.DATE', '2021-10-07');
-- The value of ${PROJ_VERSION} is substituted at build time by the actual
-- value.
diff --git a/data/sql/projected_crs.sql b/data/sql/projected_crs.sql
index 8671ff27..52ff05b2 100644
--- a/data/sql/projected_crs.sql
+++ b/data/sql/projected_crs.sql
@@ -7335,6 +7335,8 @@ INSERT INTO "projected_crs" VALUES('EPSG','9793','RGF93 v2 / Lambert-93',NULL,'E
INSERT INTO "usage" VALUES('EPSG','16614','projected_crs','EPSG','9793','EPSG','1096','EPSG','1142');
INSERT INTO "projected_crs" VALUES('EPSG','9794','RGF93 v2b / Lambert-93',NULL,'EPSG','4499','EPSG','9782','EPSG','18085',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16615','projected_crs','EPSG','9794','EPSG','1096','EPSG','1142');
+INSERT INTO "projected_crs" VALUES('EPSG','9821','UCS-2000 / LCS-32 Kyiv region',NULL,'EPSG','4531','EPSG','5561','EPSG','9796',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16867','projected_crs','EPSG','9821','EPSG','4633','EPSG','1207');
INSERT INTO "projected_crs" VALUES('EPSG','9822','RGF93 v2 / CC42',NULL,'EPSG','4499','EPSG','9777','EPSG','18101',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16616','projected_crs','EPSG','9822','EPSG','3545','EPSG','1054');
INSERT INTO "projected_crs" VALUES('EPSG','9823','RGF93 v2 / CC43',NULL,'EPSG','4499','EPSG','9777','EPSG','18102',NULL,0);
@@ -7353,6 +7355,28 @@ INSERT INTO "projected_crs" VALUES('EPSG','9829','RGF93 v2 / CC49',NULL,'EPSG','
INSERT INTO "usage" VALUES('EPSG','16623','projected_crs','EPSG','9829','EPSG','3552','EPSG','1054');
INSERT INTO "projected_crs" VALUES('EPSG','9830','RGF93 v2 / CC50',NULL,'EPSG','4499','EPSG','9777','EPSG','18109',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16624','projected_crs','EPSG','9830','EPSG','3553','EPSG','1054');
+INSERT INTO "projected_crs" VALUES('EPSG','9831','UCS-2000 / LCS-01 Crimea',NULL,'EPSG','4531','EPSG','5561','EPSG','9797',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16766','projected_crs','EPSG','9831','EPSG','4648','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9832','UCS-2000 / LCS-05 Vinnytsia',NULL,'EPSG','4531','EPSG','5561','EPSG','9798',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16767','projected_crs','EPSG','9832','EPSG','4643','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9833','UCS-2000 / LCS-07 Volyn',NULL,'EPSG','4531','EPSG','5561','EPSG','9799',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16768','projected_crs','EPSG','9833','EPSG','4644','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9834','UCS-2000 / LCS-12 Dnipropetrovsk',NULL,'EPSG','4531','EPSG','5561','EPSG','9800',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16769','projected_crs','EPSG','9834','EPSG','4627','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9835','UCS-2000 / LCS-14 Donetsk',NULL,'EPSG','4531','EPSG','5561','EPSG','9801',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16772','projected_crs','EPSG','9835','EPSG','4628','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9836','UCS-2000 / LCS-18 Zhytomyr',NULL,'EPSG','4531','EPSG','5561','EPSG','9802',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16771','projected_crs','EPSG','9836','EPSG','4647','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9837','UCS-2000 / LCS-21 Zakarpattia',NULL,'EPSG','4531','EPSG','5561','EPSG','9803',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16773','projected_crs','EPSG','9837','EPSG','4645','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9838','UCS-2000 / LCS-23 Zaporizhzhia',NULL,'EPSG','4531','EPSG','5561','EPSG','9804',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16774','projected_crs','EPSG','9838','EPSG','4646','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9839','UCS-2000 / LCS-26 Ivano-Frankivsk',NULL,'EPSG','4531','EPSG','5561','EPSG','9805',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16775','projected_crs','EPSG','9839','EPSG','4629','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9840','UCS-2000 / LCS-35 Kirovohrad',NULL,'EPSG','4531','EPSG','5561','EPSG','9806',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16776','projected_crs','EPSG','9840','EPSG','4634','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9841','UCS-2000 / LCS-44 Luhansk',NULL,'EPSG','4531','EPSG','5561','EPSG','9807',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16777','projected_crs','EPSG','9841','EPSG','4635','EPSG','1207');
INSERT INTO "projected_crs" VALUES('EPSG','9842','RGF93 v2b / CC42',NULL,'EPSG','4499','EPSG','9782','EPSG','18101',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16625','projected_crs','EPSG','9842','EPSG','3545','EPSG','1054');
INSERT INTO "projected_crs" VALUES('EPSG','9843','RGF93 v2b / CC43',NULL,'EPSG','4499','EPSG','9782','EPSG','18102',NULL,0);
@@ -7371,6 +7395,36 @@ INSERT INTO "projected_crs" VALUES('EPSG','9849','RGF93 v2b / CC49',NULL,'EPSG',
INSERT INTO "usage" VALUES('EPSG','16632','projected_crs','EPSG','9849','EPSG','3552','EPSG','1054');
INSERT INTO "projected_crs" VALUES('EPSG','9850','RGF93 v2b / CC50',NULL,'EPSG','4499','EPSG','9782','EPSG','18109',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16633','projected_crs','EPSG','9850','EPSG','3553','EPSG','1054');
+INSERT INTO "projected_crs" VALUES('EPSG','9851','UCS-2000 / LCS-46 Lviv',NULL,'EPSG','4531','EPSG','5561','EPSG','9808',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16778','projected_crs','EPSG','9851','EPSG','4636','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9852','UCS-2000 / LCS-48 Mykolaiv',NULL,'EPSG','4531','EPSG','5561','EPSG','9809',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16779','projected_crs','EPSG','9852','EPSG','4637','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9853','UCS-2000 / LCS-51 Odessa',NULL,'EPSG','4531','EPSG','5561','EPSG','9810',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16780','projected_crs','EPSG','9853','EPSG','4638','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9854','UCS-2000 / LCS-53 Poltava',NULL,'EPSG','4531','EPSG','5561','EPSG','9811',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16786','projected_crs','EPSG','9854','EPSG','4639','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9855','UCS-2000 / LCS-56 Rivne',NULL,'EPSG','4531','EPSG','5561','EPSG','9812',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16869','projected_crs','EPSG','9855','EPSG','4640','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9856','UCS-2000 / LCS-59 Sumy',NULL,'EPSG','4531','EPSG','5561','EPSG','9813',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16785','projected_crs','EPSG','9856','EPSG','4641','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9857','UCS-2000 / LCS-61 Ternopil',NULL,'EPSG','4531','EPSG','5561','EPSG','9814',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16789','projected_crs','EPSG','9857','EPSG','4642','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9858','UCS-2000 / LCS-63 Kharkiv',NULL,'EPSG','4531','EPSG','5561','EPSG','9815',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16788','projected_crs','EPSG','9858','EPSG','4630','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9859','UCS-2000 / LCS-65 Kherson',NULL,'EPSG','4531','EPSG','5561','EPSG','9816',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16790','projected_crs','EPSG','9859','EPSG','4631','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9860','UCS-2000 / LCS-68 Khmelnytsky',NULL,'EPSG','4531','EPSG','5561','EPSG','9812',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16868','projected_crs','EPSG','9860','EPSG','4632','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9861','UCS-2000 / LCS-71 Cherkasy',NULL,'EPSG','4531','EPSG','5561','EPSG','9817',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16842','projected_crs','EPSG','9861','EPSG','4624','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9862','UCS-2000 / LCS-73 Chernivtsi',NULL,'EPSG','4531','EPSG','5561','EPSG','9818',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16841','projected_crs','EPSG','9862','EPSG','4626','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9863','UCS-2000 / LCS-74 Chernihiv',NULL,'EPSG','4531','EPSG','5561','EPSG','9819',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16840','projected_crs','EPSG','9863','EPSG','4625','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9864','UCS-2000 / LCS-80 Kyiv city',NULL,'EPSG','4531','EPSG','5561','EPSG','9796',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16922','projected_crs','EPSG','9864','EPSG','4623','EPSG','1207');
+INSERT INTO "projected_crs" VALUES('EPSG','9865','UCS-2000 / LCS-85 Sevastopol',NULL,'EPSG','4531','EPSG','5561','EPSG','9820',NULL,0);
+INSERT INTO "usage" VALUES('EPSG','16836','projected_crs','EPSG','9865','EPSG','4649','EPSG','1207');
INSERT INTO "projected_crs" VALUES('EPSG','9874','PNG94 / PNGMG94 zone 57',NULL,'EPSG','4400','EPSG','5546','EPSG','9872',NULL,0);
INSERT INTO "usage" VALUES('EPSG','16919','projected_crs','EPSG','9874','EPSG','4653','EPSG','1092');
INSERT INTO "projected_crs" VALUES('EPSG','9875','PNG94 / PNGMG94 zone 58',NULL,'EPSG','4400','EPSG','5546','EPSG','9873',NULL,0);
diff --git a/docs/source/install.rst b/docs/source/install.rst
index 73cc6e00..208b55d4 100644
--- a/docs/source/install.rst
+++ b/docs/source/install.rst
@@ -322,33 +322,43 @@ CMake configure options
Options to configure a CMake are provided using ``-D<var>=<value>``.
All cached entries can be viewed using ``cmake -LAH`` from a build directory.
+.. option:: BUILD_APPS=ON
+
+ Build PROJ applications. Default is ON. Control the default value for
+ BUILD_CCT, BUILD_CS2CS, BUILD_GEOD, BUILD_GIE, BUILD_PROJ, BUILD_PROJINFO
+ and BUILD_PROJSYNC.
+ Note that changing its value after having configured once will not change
+ the value of the individual BUILD_CCT, ... options.
+
+ .. versionchanged:: 8.2
+
.. option:: BUILD_CCT=ON
- Build :ref:`cct`, default ON.
+ Build :ref:`cct`, default is the value of BUILD_APPS.
.. option:: BUILD_CS2CS=ON
- Build :ref:`cs2cs`, default ON.
+ Build :ref:`cs2cs`,default is the value of BUILD_APPS.
.. option:: BUILD_GEOD=ON
- Build :ref:`geod`, default ON.
+ Build :ref:`geod`, default is the value of BUILD_APPS.
.. option:: BUILD_GIE=ON
- Build :ref:`gie`, default ON.
+ Build :ref:`gie`, default is the value of BUILD_APPS.
.. option:: BUILD_PROJ=ON
- Build :ref:`proj`, default ON.
+ Build :ref:`proj`, default is the value of BUILD_APPS.
.. option:: BUILD_PROJINFO=ON
- Build :ref:`projinfo`, default ON.
+ Build :ref:`projinfo`, default is the value of BUILD_APPS.
.. option:: BUILD_PROJSYNC=ON
- Build :ref:`projsync`, default ON.
+ Build :ref:`projsync`, default is the value of BUILD_APPS.
.. option:: BUILD_SHARED_LIBS
diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp
index 5a8e75ae..ce950c47 100644
--- a/include/proj/crs.hpp
+++ b/include/proj/crs.hpp
@@ -142,6 +142,8 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage,
PROJ_INTERNAL bool mustAxisOrderBeSwitchedForVisualization() const;
+ PROJ_INTERNAL CRSNNPtr applyAxisOrderReversal(const char *nameSuffix) const;
+
PROJ_FOR_TEST CRSNNPtr normalizeForVisualization() const;
PROJ_INTERNAL CRSNNPtr allowNonConformantWKT1Export() const;
diff --git a/include/proj/internal/Makefile.am b/include/proj/internal/Makefile.am
index 59325667..25b71cc5 100644
--- a/include/proj/internal/Makefile.am
+++ b/include/proj/internal/Makefile.am
@@ -1,6 +1,7 @@
SUBDIRS = vendor
noinst_HEADERS = \
+ crs_internal.hpp \
coordinatesystem_internal.hpp \
internal.hpp \
io_internal.hpp \
diff --git a/include/proj/internal/crs_internal.hpp b/include/proj/internal/crs_internal.hpp
new file mode 100644
index 00000000..9a68c8b0
--- /dev/null
+++ b/include/proj/internal/crs_internal.hpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: ISO19111:2019 implementation
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2021, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * 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.
+ ****************************************************************************/
+
+#ifndef FROM_PROJ_CPP
+#error This file should only be included from a PROJ cpp file
+#endif
+
+#ifndef CRS_INTERNAL_HH_INCLUDED
+#define CRS_INTERNAL_HH_INCLUDED
+
+#define NORMALIZED_AXIS_ORDER_SUFFIX_STR \
+ " (with axis order normalized for visualization)"
+
+#define AXIS_ORDER_REVERSED_SUFFIX_STR " (with axis order reversed)"
+
+#endif // CRS_INTERNAL_HH_INCLUDED
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 534bc311..311cdbac 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,20 +3,23 @@
include(lib_proj.cmake)
# configure executable build
+option(BUILD_APPS
+ "Build PROJ applications (default value for BUILD_CCT, BUILD_CS2CS, etc.)" ON)
+
option(BUILD_CCT
- "Build cct (coordinate conversion and transformation tool)" ON)
+ "Build cct (coordinate conversion and transformation tool)" "${BUILD_APPS}")
option(BUILD_CS2CS
- "Build cs2cs (coordinate systems to coordinate systems translation tool)" ON)
+ "Build cs2cs (coordinate systems to coordinate systems translation tool)" "${BUILD_APPS}")
option(BUILD_GEOD
- "Build geod (computation of geodesic lines)" ON)
+ "Build geod (computation of geodesic lines)" "${BUILD_APPS}")
option(BUILD_GIE
- "Build gie (geospatial integrity investigation environment)" ON)
+ "Build gie (geospatial integrity investigation environment)" "${BUILD_APPS}")
option(BUILD_PROJ
- "Build proj (cartographic projection tool)" ON)
+ "Build proj (cartographic projection tool)" "${BUILD_APPS}")
option(BUILD_PROJINFO
- "Build projinfo (SRS and coordinate operation metadata/query tool)" ON)
+ "Build projinfo (SRS and coordinate operation metadata/query tool)" "${BUILD_APPS}")
option(BUILD_PROJSYNC
- "Build projsync (synchronize transformation support data)" ON)
+ "Build projsync (synchronize transformation support data)" "${BUILD_APPS}")
if(NOT MSVC)
@@ -69,7 +72,8 @@ if(BUILD_PROJINFO)
set(BIN_TARGETS ${BIN_TARGETS} binprojinfo)
endif()
-if(BUILD_GIE)
+# Always build gie if testing is requested
+if(BUILD_GIE OR BUILD_TESTING)
include(bin_gie.cmake)
set(BIN_TARGETS ${BIN_TARGETS} gie)
endif()
diff --git a/src/Makefile.am b/src/Makefile.am
index bae922d5..1e847080 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -14,17 +14,23 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h geodesic.h \
EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \
bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \
- lib_proj.cmake CMakeLists.txt bin_geodtest.cmake \
+ lib_proj.cmake \
+ check_md5sum.cmake \
+ generate_wkt_parser.cmake \
+ CMakeLists.txt \
+ bin_geodtest.cmake \
bin_projsync.cmake \
tests/geodtest.cpp \
wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h \
apps/projsync.cpp
proj_SOURCES = apps/proj.cpp apps/emess.cpp apps/utils.cpp
+invproj_SOURCES = $(proj_SOURCES)
projinfo_SOURCES = apps/projinfo.cpp
cs2cs_SOURCES = apps/cs2cs.cpp apps/emess.cpp apps/utils.cpp
cct_SOURCES = apps/cct.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h
geod_SOURCES = apps/geod.cpp apps/geod_set.cpp apps/geod_interface.cpp apps/geod_interface.h apps/emess.cpp
+invgeod_SOURCES = $(geod_SOURCES)
if HAVE_CURL
projsync_SOURCES = apps/projsync.cpp
@@ -33,6 +39,7 @@ PROJSYNC_BIN = projsync
endif
bin_PROGRAMS = proj geod cs2cs gie cct projinfo $(PROJSYNC_BIN)
+noinst_PROGRAMS = invproj invgeod
gie_SOURCES = apps/gie.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h
multistresstest_SOURCES = tests/multistresstest.cpp
@@ -41,7 +48,9 @@ geodtest_SOURCES = tests/geodtest.cpp
cct_LDADD = libproj.la
cs2cs_LDADD = libproj.la
geod_LDADD = libproj.la
+invgeod_LDADD = $(geod_LDADD)
proj_LDADD = libproj.la
+invproj_LDADD = $(proj_LDADD)
projinfo_LDADD = libproj.la
gie_LDADD = libproj.la
@@ -272,7 +281,6 @@ wkt2_parser:
sed "s/\*yyssp = yystate/\*yyssp = (yytype_int16)yystate/" < $(top_srcdir)/src/wkt2_generated_parser.c | sed "s/yyerrorlab:/#if 0\nyyerrorlab:/" | sed "s/yyerrlab1:/#endif\nyyerrlab1:/" | sed "s/for (yylen = 0; yystr\[yylen\]; yylen++)/for (yylen = 0; yystr \&\& yystr\[yylen\]; yylen++)/"| sed "s/return yystpcpy (yyres, yystr) - yyres;/return (YYPTRDIFF_T)(yystpcpy (yyres, yystr) - yyres);/" | sed "s/YYPTRDIFF_T yysize = yyssp - yyss + 1;/YYPTRDIFF_T yysize = (YYPTRDIFF_T)(yyssp - yyss + 1);/"> $(top_srcdir)/src/wkt2_generated_parser.c.tmp
mv $(top_srcdir)/src/wkt2_generated_parser.c.tmp $(top_srcdir)/src/wkt2_generated_parser.c
-
install-exec-local: install-binPROGRAMS
rm -f $(DESTDIR)$(bindir)/invproj$(EXEEXT)
(cd $(DESTDIR)$(bindir); ln -s proj$(EXEEXT) invproj$(EXEEXT))
diff --git a/src/apps/geod.cpp b/src/apps/geod.cpp
index 35a8e826..6e3f059e 100644
--- a/src/apps/geod.cpp
+++ b/src/apps/geod.cpp
@@ -138,7 +138,8 @@ int main(int argc, char **argv) {
if ((emess_dat.Prog_name = strrchr(*argv,'/')) != nullptr) ++emess_dat.Prog_name;
else emess_dat.Prog_name = *argv;
- inverse = ! strncmp(emess_dat.Prog_name, "inv", 3);
+ inverse = strncmp(emess_dat.Prog_name, "inv", 3) == 0 ||
+ strncmp(emess_dat.Prog_name, "lt-inv", 6) == 0; // older libtool have a lt- prefix
if (argc <= 1 ) {
(void)fprintf(stderr, usage, pj_get_release(),
emess_dat.Prog_name);
diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp
index 6368ef2c..f93164e0 100644
--- a/src/apps/proj.cpp
+++ b/src/apps/proj.cpp
@@ -303,7 +303,8 @@ int main(int argc, char **argv) {
if ( (emess_dat.Prog_name = strrchr(*argv,DIR_CHAR)) != nullptr)
++emess_dat.Prog_name;
else emess_dat.Prog_name = *argv;
- inverse = ! strncmp(emess_dat.Prog_name, "inv", 3);
+ inverse = strncmp(emess_dat.Prog_name, "inv", 3) == 0 ||
+ strncmp(emess_dat.Prog_name, "lt-inv", 6) == 0; // older libtool have a lt- prefix
if (argc <= 1 ) {
(void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name);
exit (0);
diff --git a/src/bin_geod.cmake b/src/bin_geod.cmake
index 4ec28b04..3f3f9bf5 100644
--- a/src/bin_geod.cmake
+++ b/src/bin_geod.cmake
@@ -18,3 +18,38 @@ install(TARGETS geod
if(MSVC AND BUILD_SHARED_LIBS)
target_compile_definitions(geod PRIVATE PROJ_MSVC_DLL_IMPORT=1)
endif()
+
+# invgeod target: symlink or copy of geod executable
+
+if(UNIX)
+
+ set(link_target "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/invgeod${CMAKE_EXECUTABLE_SUFFIX}")
+ set(link_source "geod${CMAKE_EXECUTABLE_SUFFIX}")
+
+ add_custom_command(
+ OUTPUT ${link_target}
+ COMMAND ${CMAKE_COMMAND} -E create_symlink ${link_source} ${link_target}
+ WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
+ DEPENDS geod
+ COMMENT "Generating invgeod"
+ VERBATIM
+ )
+
+ add_custom_target(invgeod ALL DEPENDS ${link_target})
+
+ install(FILES ${link_target} DESTINATION ${BINDIR})
+
+else()
+
+ add_executable(invgeod ${GEOD_SRC} ${GEOD_INCLUDE})
+ target_link_libraries(invgeod PRIVATE ${PROJ_LIBRARIES})
+ target_compile_options(invgeod PRIVATE ${PROJ_CXX_WARN_FLAGS})
+
+ install(TARGETS invgeod
+ DESTINATION ${BINDIR})
+
+ if(MSVC AND BUILD_SHARED_LIBS)
+ target_compile_definitions(invgeod PRIVATE PROJ_MSVC_DLL_IMPORT=1)
+ endif()
+
+endif()
diff --git a/src/bin_gie.cmake b/src/bin_gie.cmake
index a26ead3e..6ad7d4ab 100644
--- a/src/bin_gie.cmake
+++ b/src/bin_gie.cmake
@@ -11,8 +11,10 @@ add_executable(gie ${GIE_SRC} ${GIE_INCLUDE})
target_link_libraries(gie PRIVATE ${PROJ_LIBRARIES})
target_compile_options(gie PRIVATE ${PROJ_CXX_WARN_FLAGS})
-install(TARGETS gie
- DESTINATION ${BINDIR})
+if(BUILD_GIE)
+ install(TARGETS gie
+ DESTINATION ${BINDIR})
+endif()
if(MSVC AND BUILD_SHARED_LIBS)
target_compile_definitions(gie PRIVATE PROJ_MSVC_DLL_IMPORT=1)
diff --git a/src/bin_proj.cmake b/src/bin_proj.cmake
index b5a4c140..2df467af 100644
--- a/src/bin_proj.cmake
+++ b/src/bin_proj.cmake
@@ -19,3 +19,38 @@ install(TARGETS binproj
if(MSVC AND BUILD_SHARED_LIBS)
target_compile_definitions(binproj PRIVATE PROJ_MSVC_DLL_IMPORT=1)
endif()
+
+# invproj target: symlink or copy of proj executable
+
+if(UNIX)
+
+ set(link_target "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/invproj${CMAKE_EXECUTABLE_SUFFIX}")
+ set(link_source "proj${CMAKE_EXECUTABLE_SUFFIX}")
+
+ add_custom_command(
+ OUTPUT ${link_target}
+ COMMAND ${CMAKE_COMMAND} -E create_symlink ${link_source} ${link_target}
+ WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
+ DEPENDS binproj
+ COMMENT "Generating invproj"
+ VERBATIM
+ )
+
+ add_custom_target(invproj ALL DEPENDS ${link_target})
+
+ install(FILES ${link_target} DESTINATION ${BINDIR})
+
+else()
+
+ add_executable(invproj ${PROJ_SRC})
+ target_link_libraries(invproj PRIVATE ${PROJ_LIBRARIES})
+ target_compile_options(invproj PRIVATE ${PROJ_CXX_WARN_FLAGS})
+
+ install(TARGETS invproj
+ DESTINATION ${BINDIR})
+
+ if(MSVC AND BUILD_SHARED_LIBS)
+ target_compile_definitions(invproj PRIVATE PROJ_MSVC_DLL_IMPORT=1)
+ endif()
+
+endif()
diff --git a/src/check_md5sum.cmake b/src/check_md5sum.cmake
new file mode 100644
index 00000000..bab6fc6f
--- /dev/null
+++ b/src/check_md5sum.cmake
@@ -0,0 +1,7 @@
+file(READ "${IN_FILE}" CONTENTS)
+
+string(MD5 MD5SUM "${CONTENTS}")
+
+if(NOT("${MD5SUM}" STREQUAL "${EXPECTED_MD5SUM}"))
+ message(FATAL_ERROR "File ${IN_FILE} has been modified. target ${TARGET} should be manually run. And lib_proj.cmake should be updated with \"${MD5SUM}\"")
+endif()
diff --git a/src/generate_wkt_parser.cmake b/src/generate_wkt_parser.cmake
new file mode 100644
index 00000000..bcfc09be
--- /dev/null
+++ b/src/generate_wkt_parser.cmake
@@ -0,0 +1,18 @@
+message("Generating ${OUT_FILE}")
+
+execute_process(COMMAND "bison" "--no-lines" "-d" "-p" "${PREFIX}" "-o${OUT_FILE}" "${IN_FILE}"
+ RESULT_VARIABLE STATUS)
+
+if(STATUS AND NOT STATUS EQUAL 0)
+ message(FATAL_ERROR "bison failed")
+endif()
+
+# Post processing of the generated file
+# All those replacements are to please MSVC
+file(READ ${OUT_FILE} CONTENTS)
+string(REPLACE "yyerrorlab:" "#if 0\nyyerrorlab:" CONTENTS "${CONTENTS}")
+string(REPLACE "yyerrlab1:" "#endif\nyyerrlab1:" CONTENTS "${CONTENTS}")
+string(REPLACE "for (yylen = 0; yystr[yylen]; yylen++)" "for (yylen = 0; yystr && yystr[yylen]; yylen++)" CONTENTS "${CONTENTS}")
+string(REPLACE "return yystpcpy (yyres, yystr) - yyres;" "return (YYPTRDIFF_T)(yystpcpy (yyres, yystr) - yyres);" CONTENTS "${CONTENTS}")
+string(REPLACE "YYPTRDIFF_T yysize = yyssp - yyss + 1;" "YYPTRDIFF_T yysize = (YYPTRDIFF_T)(yyssp - yyss + 1);" CONTENTS "${CONTENTS}")
+file(WRITE "${OUT_FILE}" "${CONTENTS}")
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 9493452c..acf36402 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -8479,7 +8479,12 @@ PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) {
const auto &datumEnsemble = l_crs->datumEnsemble();
assert(datumEnsemble);
auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
- return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
+ try {
+ return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
+ } catch (const std::exception &e) {
+ proj_log_debug(ctx, __FUNCTION__, e.what());
+ return nullptr;
+ }
}
// ---------------------------------------------------------------------------
diff --git a/src/iso19111/crs.cpp b/src/iso19111/crs.cpp
index b7d57767..575f6e2b 100644
--- a/src/iso19111/crs.cpp
+++ b/src/iso19111/crs.cpp
@@ -43,6 +43,7 @@
#include "proj/util.hpp"
#include "proj/internal/coordinatesystem_internal.hpp"
+#include "proj/internal/crs_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"
@@ -887,51 +888,63 @@ void CRS::setProperties(
//! @cond Doxygen_Suppress
-CRSNNPtr CRS::normalizeForVisualization() const {
+// ---------------------------------------------------------------------------
- const auto createProperties = [this](const std::string &newName =
- std::string()) {
- auto props = util::PropertyMap().set(
- common::IdentifiedObject::NAME_KEY,
- !newName.empty()
- ? newName
- : nameStr() +
- " (with axis order normalized for visualization)");
- const auto &l_domains = domains();
- if (!l_domains.empty()) {
- auto array = util::ArrayOfBaseObject::create();
- for (const auto &domain : l_domains) {
- array->add(domain);
+CRSNNPtr CRS::applyAxisOrderReversal(const char *nameSuffix) const {
+
+ const auto createProperties =
+ [this, nameSuffix](const std::string &newNameIn = std::string()) {
+ std::string newName(newNameIn);
+ if (newName.empty()) {
+ newName = nameStr();
+ if (ends_with(newName, NORMALIZED_AXIS_ORDER_SUFFIX_STR)) {
+ newName.resize(newName.size() -
+ strlen(NORMALIZED_AXIS_ORDER_SUFFIX_STR));
+ } else if (ends_with(newName, AXIS_ORDER_REVERSED_SUFFIX_STR)) {
+ newName.resize(newName.size() -
+ strlen(AXIS_ORDER_REVERSED_SUFFIX_STR));
+ } else {
+ newName += nameSuffix;
+ }
}
- if (!array->empty()) {
- props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY, array);
+ auto props = util::PropertyMap().set(
+ common::IdentifiedObject::NAME_KEY, newName);
+ const auto &l_domains = domains();
+ if (!l_domains.empty()) {
+ auto array = util::ArrayOfBaseObject::create();
+ for (const auto &domain : l_domains) {
+ array->add(domain);
+ }
+ if (!array->empty()) {
+ props.set(common::ObjectUsage::OBJECT_DOMAIN_KEY, array);
+ }
}
- }
- const auto &l_identifiers = identifiers();
- const auto &l_remarks = remarks();
- if (l_identifiers.size() == 1) {
- std::string remarks("Axis order reversed compared to ");
- remarks += *(l_identifiers[0]->codeSpace());
- remarks += ':';
- remarks += l_identifiers[0]->code();
- if (!l_remarks.empty()) {
- remarks += ". ";
- remarks += l_remarks;
+ const auto &l_identifiers = identifiers();
+ const auto &l_remarks = remarks();
+ if (l_identifiers.size() == 1) {
+ std::string remarks("Axis order reversed compared to ");
+ if (!starts_with(l_remarks, remarks)) {
+ remarks += *(l_identifiers[0]->codeSpace());
+ remarks += ':';
+ remarks += l_identifiers[0]->code();
+ if (!l_remarks.empty()) {
+ remarks += ". ";
+ remarks += l_remarks;
+ }
+ props.set(common::IdentifiedObject::REMARKS_KEY, remarks);
+ }
+ } else if (!l_remarks.empty()) {
+ props.set(common::IdentifiedObject::REMARKS_KEY, l_remarks);
}
- props.set(common::IdentifiedObject::REMARKS_KEY, remarks);
- } else if (!l_remarks.empty()) {
- props.set(common::IdentifiedObject::REMARKS_KEY, l_remarks);
- }
- return props;
- };
+ return props;
+ };
const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
if (compoundCRS) {
const auto &comps = compoundCRS->componentReferenceSystems();
- if (!comps.empty() &&
- comps[0]->mustAxisOrderBeSwitchedForVisualization()) {
+ if (!comps.empty()) {
std::vector<CRSNNPtr> newComps;
- newComps.emplace_back(comps[0]->normalizeForVisualization());
+ newComps.emplace_back(comps[0]->applyAxisOrderReversal(nameSuffix));
std::string l_name = newComps.back()->nameStr();
for (size_t i = 1; i < comps.size(); i++) {
newComps.emplace_back(comps[i]);
@@ -946,16 +959,53 @@ CRSNNPtr CRS::normalizeForVisualization() const {
const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
if (geogCRS) {
const auto &axisList = geogCRS->coordinateSystem()->axisList();
+ auto cs =
+ axisList.size() == 2
+ ? cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1],
+ axisList[0])
+ : cs::EllipsoidalCS::create(util::PropertyMap(), axisList[1],
+ axisList[0], axisList[2]);
+ return util::nn_static_pointer_cast<CRS>(
+ GeographicCRS::create(createProperties(), geogCRS->datum(),
+ geogCRS->datumEnsemble(), cs));
+ }
+
+ const ProjectedCRS *projCRS = dynamic_cast<const ProjectedCRS *>(this);
+ if (projCRS) {
+ const auto &axisList = projCRS->coordinateSystem()->axisList();
+ auto cs =
+ axisList.size() == 2
+ ? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
+ axisList[0])
+ : cs::CartesianCS::create(util::PropertyMap(), axisList[1],
+ axisList[0], axisList[2]);
+ return util::nn_static_pointer_cast<CRS>(
+ ProjectedCRS::create(createProperties(), projCRS->baseCRS(),
+ projCRS->derivingConversion(), cs));
+ }
+
+ throw util::UnsupportedOperationException(
+ "axis order reversal not supported on this type of CRS");
+}
+
+// ---------------------------------------------------------------------------
+
+CRSNNPtr CRS::normalizeForVisualization() const {
+
+ const CompoundCRS *compoundCRS = dynamic_cast<const CompoundCRS *>(this);
+ if (compoundCRS) {
+ const auto &comps = compoundCRS->componentReferenceSystems();
+ if (!comps.empty() &&
+ comps[0]->mustAxisOrderBeSwitchedForVisualization()) {
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ }
+ }
+
+ const GeographicCRS *geogCRS = dynamic_cast<const GeographicCRS *>(this);
+ if (geogCRS) {
+ const auto &axisList = geogCRS->coordinateSystem()->axisList();
if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
- auto cs = axisList.size() == 2
- ? cs::EllipsoidalCS::create(util::PropertyMap(),
- axisList[1], axisList[0])
- : cs::EllipsoidalCS::create(util::PropertyMap(),
- axisList[1], axisList[0],
- axisList[2]);
- return util::nn_static_pointer_cast<CRS>(
- GeographicCRS::create(createProperties(), geogCRS->datum(),
- geogCRS->datumEnsemble(), cs));
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
}
}
@@ -963,15 +1013,7 @@ CRSNNPtr CRS::normalizeForVisualization() const {
if (projCRS) {
const auto &axisList = projCRS->coordinateSystem()->axisList();
if (mustAxisOrderBeSwitchedForVisualizationInternal(axisList)) {
- auto cs =
- axisList.size() == 2
- ? cs::CartesianCS::create(util::PropertyMap(), axisList[1],
- axisList[0])
- : cs::CartesianCS::create(util::PropertyMap(), axisList[1],
- axisList[0], axisList[2]);
- return util::nn_static_pointer_cast<CRS>(
- ProjectedCRS::create(createProperties(), projCRS->baseCRS(),
- projCRS->derivingConversion(), cs));
+ return applyAxisOrderReversal(NORMALIZED_AXIS_ORDER_SUFFIX_STR);
}
}
@@ -1786,15 +1828,16 @@ static bool exportAsESRIWktCompoundCRSWithEllipsoidalHeight(
return false;
}
const auto l_datum = geodCRS->datumNonNull(formatter->databaseContext());
- auto l_alias = dbContext->getAliasFromOfficialName(
+ auto l_esri_name = dbContext->getAliasFromOfficialName(
l_datum->nameStr(), "geodetic_datum", "ESRI");
- if (l_alias.empty()) {
- return false;
+ if (l_esri_name.empty()) {
+ l_esri_name = l_datum->nameStr();
}
auto authFactory =
io::AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string());
auto list = authFactory->createObjectsFromName(
- l_alias, {io::AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME},
+ l_esri_name,
+ {io::AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME},
false /* approximate=false*/);
if (list.empty()) {
return false;
@@ -1807,11 +1850,39 @@ static bool exportAsESRIWktCompoundCRSWithEllipsoidalHeight(
auto vertCRSList = authFactory->createVerticalCRSFromDatum(
"ESRI", "from_geogdatum_" + *gdatum_ids[0]->codeSpace() + '_' +
gdatum_ids[0]->code());
- if (vertCRSList.size() != 1) {
- return false;
- }
self->demoteTo2D(std::string(), dbContext)->_exportToWKT(formatter);
- vertCRSList.front()->_exportToWKT(formatter);
+ if (vertCRSList.size() == 1) {
+ vertCRSList.front()->_exportToWKT(formatter);
+ } else {
+ // This will not be recognized properly by ESRI software
+ // See https://github.com/OSGeo/PROJ/issues/2757
+
+ const auto &axisList = geodCRS->coordinateSystem()->axisList();
+ assert(axisList.size() == 3U);
+
+ formatter->startNode(io::WKTConstants::VERTCS, false);
+ auto vertcs_name = l_esri_name;
+ if (starts_with(vertcs_name.c_str(), "GCS_"))
+ vertcs_name = vertcs_name.substr(4);
+ formatter->addQuotedString(vertcs_name);
+
+ gdatum->_exportToWKT(formatter);
+
+ // Seems to be a constant value...
+ formatter->startNode(io::WKTConstants::PARAMETER, false);
+ formatter->addQuotedString("Vertical_Shift");
+ formatter->add(0.0);
+ formatter->endNode();
+
+ formatter->startNode(io::WKTConstants::PARAMETER, false);
+ formatter->addQuotedString("Direction");
+ formatter->add(
+ axisList[2]->direction() == cs::AxisDirection::UP ? 1.0 : -1.0);
+ formatter->endNode();
+
+ axisList[2]->unit()._exportToWKT(formatter);
+ formatter->endNode();
+ }
return true;
}
@@ -1941,6 +2012,18 @@ void GeodeticCRS::_exportToWKT(io::WKTFormatter *formatter) const {
aliasFound = true;
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound =
+ authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::GEODETIC_CRS},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
if (!starts_with(l_name, "GCS_")) {
@@ -3224,6 +3307,18 @@ void VerticalCRS::_exportToWKT(io::WKTFormatter *formatter) const {
aliasFound = true;
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory =
+ io::AuthorityFactory::create(NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound =
+ authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::VERTICAL_CRS},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
}
@@ -3924,10 +4019,24 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
"Projected 3D CRS can only be exported since WKT2:2019");
}
- std::string l_alias;
+ std::string l_esri_name;
if (formatter->useESRIDialect() && dbContext) {
- l_alias = dbContext->getAliasFromOfficialName(l_name, "projected_crs",
- "ESRI");
+ l_esri_name = dbContext->getAliasFromOfficialName(
+ l_name, "projected_crs", "ESRI");
+ if (l_esri_name.empty()) {
+ auto authFactory =
+ io::AuthorityFactory::create(NN_NO_CHECK(dbContext), "ESRI");
+ const bool found =
+ authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::PROJECTED_CRS},
+ false // approximateMatch
+ )
+ .size() == 1;
+ if (found)
+ l_esri_name = l_name;
+ }
}
if (!isWKT2 && formatter->useESRIDialect() && !l_identifiers.empty() &&
@@ -3949,12 +4058,12 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
}
} catch (const std::exception &) {
}
- } else if (!isWKT2 && formatter->useESRIDialect() && !l_alias.empty()) {
+ } else if (!isWKT2 && formatter->useESRIDialect() && !l_esri_name.empty()) {
try {
auto res =
io::AuthorityFactory::create(NN_NO_CHECK(dbContext), "ESRI")
->createObjectsFromName(
- l_alias,
+ l_esri_name,
{io::AuthorityFactory::ObjectType::PROJECTED_CRS},
false);
if (res.size() == 1) {
@@ -4033,10 +4142,10 @@ void ProjectedCRS::_exportToWKT(io::WKTFormatter *formatter) const {
!l_identifiers.empty());
if (formatter->useESRIDialect()) {
- if (l_alias.empty()) {
+ if (l_esri_name.empty()) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
} else {
- l_name = l_alias;
+ l_name = l_esri_name;
}
}
if (!isWKT2 && !formatter->useESRIDialect() && isDeprecated()) {
diff --git a/src/iso19111/datum.cpp b/src/iso19111/datum.cpp
index ebef94a2..7c76061e 100644
--- a/src/iso19111/datum.cpp
+++ b/src/iso19111/datum.cpp
@@ -363,6 +363,18 @@ void PrimeMeridian::_exportToWKT(
aliasFound = true;
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound =
+ authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::PRIME_MERIDIAN},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
}
@@ -820,6 +832,18 @@ void Ellipsoid::_exportToWKT(
aliasFound = true;
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound = authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::
+ ELLIPSOID},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
}
@@ -1247,6 +1271,18 @@ void GeodeticReferenceFrame::_exportToWKT(
}
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound = authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::
+ GEODETIC_REFERENCE_FRAME},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
if (!starts_with(l_name, "D_")) {
@@ -1985,6 +2021,18 @@ void VerticalReferenceFrame::_exportToWKT(
aliasFound = true;
}
}
+ if (!aliasFound && dbContext) {
+ auto authFactory = io::AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), "ESRI");
+ aliasFound = authFactory
+ ->createObjectsFromName(
+ l_name,
+ {io::AuthorityFactory::ObjectType::
+ VERTICAL_REFERENCE_FRAME},
+ false // approximateMatch
+ )
+ .size() == 1;
+ }
if (!aliasFound) {
l_name = io::WKTFormatter::morphNameToESRI(l_name);
}
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 24201ee1..9ce642e7 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -3800,6 +3800,39 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
tryToIdentifyWKT1Method ? getMappingFromWKT1(projectionName) : nullptr;
if (mapping) {
mapping = selectSphericalOrEllipsoidal(mapping, baseGeodCRS);
+ } else if (metadata::Identifier::isEquivalentName(
+ projectionName.c_str(), "Lambert Conformal Conic")) {
+ // Lambert Conformal Conic or Lambert_Conformal_Conic are respectively
+ // used by Oracle WKT and Trimble for either LCC 1SP or 2SP, so we
+ // have to look at parameters to figure out the variant.
+ bool found2ndStdParallel = false;
+ bool foundScaleFactor = false;
+ for (const auto &childNode : projCRSNode->GP()->children()) {
+ if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) {
+ const auto &childNodeChildren = childNode->GP()->children();
+ if (childNodeChildren.size() < 2) {
+ ThrowNotEnoughChildren(WKTConstants::PARAMETER);
+ }
+ const std::string wkt1ParameterName(
+ stripQuotes(childNodeChildren[0]));
+ if (metadata::Identifier::isEquivalentName(
+ wkt1ParameterName.c_str(), WKT1_STANDARD_PARALLEL_2)) {
+ found2ndStdParallel = true;
+ } else if (metadata::Identifier::isEquivalentName(
+ wkt1ParameterName.c_str(), WKT1_SCALE_FACTOR)) {
+ foundScaleFactor = true;
+ }
+ }
+ }
+ if (found2ndStdParallel && !foundScaleFactor) {
+ mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
+ } else if (!found2ndStdParallel && foundScaleFactor) {
+ mapping = getMapping(EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
+ } else if (found2ndStdParallel && foundScaleFactor) {
+ // Not sure if that happens
+ mapping = getMapping(
+ EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN);
+ }
}
// For Krovak, we need to look at axis to decide between the Krovak and
@@ -3833,7 +3866,7 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
}
foundParameters.resize(countParams);
}
- bool found2ndStdParallel = false;
+
for (const auto &childNode : projCRSNode->GP()->children()) {
if (ci_equal(childNode->GP()->value(), WKTConstants::PARAMETER)) {
const auto &childNodeChildren = childNode->GP()->children();
@@ -3886,10 +3919,6 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
propertiesParameter.set(Identifier::CODESPACE_KEY,
Identifier::EPSG);
}
- if (paramMapping->epsg_code ==
- EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL) {
- found2ndStdParallel = true;
- }
}
propertiesParameter.set(IdentifiedObject::NAME_KEY, parameterName);
parameters.push_back(
@@ -3906,14 +3935,6 @@ ConversionNNPtr WKTParser::Private::buildProjectionStandard(
}
}
- // Oracle WKT: make sure that the 2nd std parallel parameter is found to
- // select the LCC_2SP mapping
- if (metadata::Identifier::isEquivalentName(wkt1ProjectionName.c_str(),
- "Lambert Conformal Conic") &&
- !found2ndStdParallel) {
- propertiesMethod.set(IdentifiedObject::NAME_KEY, wkt1ProjectionName);
- }
-
// Add back important parameters that should normally be present, but
// are sometimes missing. Currently we only deal with Scale factor at
// natural origin. This is to avoid a default value of 0 to slip in later.
diff --git a/src/iso19111/operation/concatenatedoperation.cpp b/src/iso19111/operation/concatenatedoperation.cpp
index 185ebb4a..7da561b4 100644
--- a/src/iso19111/operation/concatenatedoperation.cpp
+++ b/src/iso19111/operation/concatenatedoperation.cpp
@@ -37,6 +37,7 @@
#include "proj/metadata.hpp"
#include "proj/util.hpp"
+#include "proj/internal/crs_internal.hpp"
#include "proj/internal/internal.hpp"
#include "proj/internal/io_internal.hpp"
@@ -245,15 +246,44 @@ void ConcatenatedOperation::fixStepsDirection(
return false;
};
+ // Apply axis order reversal operation on first operation if needed
+ // to set CRSs on it
+ if (operationsInOut.size() >= 1) {
+ auto &op = operationsInOut.front();
+ auto l_sourceCRS = op->sourceCRS();
+ auto l_targetCRS = op->targetCRS();
+ auto conv = dynamic_cast<const Conversion *>(op.get());
+ if (conv && !l_sourceCRS && !l_targetCRS &&
+ isAxisOrderReversal(conv->method()->getEPSGCode())) {
+ auto reversedCRS = concatOpSourceCRS->applyAxisOrderReversal(
+ NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ op->setCRSs(concatOpSourceCRS, reversedCRS, nullptr);
+ }
+ }
+
+ // Apply axis order reversal operation on last operation if needed
+ // to set CRSs on it
+ if (operationsInOut.size() >= 2) {
+ auto &op = operationsInOut.back();
+ auto l_sourceCRS = op->sourceCRS();
+ auto l_targetCRS = op->targetCRS();
+ auto conv = dynamic_cast<const Conversion *>(op.get());
+ if (conv && !l_sourceCRS && !l_targetCRS &&
+ isAxisOrderReversal(conv->method()->getEPSGCode())) {
+ auto reversedCRS = concatOpTargetCRS->applyAxisOrderReversal(
+ NORMALIZED_AXIS_ORDER_SUFFIX_STR);
+ op->setCRSs(reversedCRS, concatOpTargetCRS, nullptr);
+ }
+ }
+
for (size_t i = 0; i < operationsInOut.size(); ++i) {
auto &op = operationsInOut[i];
auto l_sourceCRS = op->sourceCRS();
auto l_targetCRS = op->targetCRS();
auto conv = dynamic_cast<const Conversion *>(op.get());
if (conv && i == 0 && !l_sourceCRS && !l_targetCRS) {
- auto derivedCRS =
- dynamic_cast<const crs::DerivedCRS *>(concatOpSourceCRS.get());
- if (derivedCRS) {
+ if (auto derivedCRS = dynamic_cast<const crs::DerivedCRS *>(
+ concatOpSourceCRS.get())) {
if (i + 1 < operationsInOut.size()) {
// use the sourceCRS of the next operation as our target CRS
l_targetCRS = operationsInOut[i + 1]->sourceCRS();
diff --git a/src/iso19111/operation/coordinateoperationfactory.cpp b/src/iso19111/operation/coordinateoperationfactory.cpp
index b59eeb91..e9bd3cfe 100644
--- a/src/iso19111/operation/coordinateoperationfactory.cpp
+++ b/src/iso19111/operation/coordinateoperationfactory.cpp
@@ -4347,6 +4347,9 @@ void CoordinateOperationFactory::Private::createOperationsBoundToGeog(
const bool heightDepthReversal =
((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp));
+ if (convDst == 0)
+ throw InvalidOperation(
+ "Conversion factor of target unit is 0");
const double factor = convSrc / convDst;
auto conv = Conversion::createChangeVerticalUnit(
util::PropertyMap().set(
@@ -4460,6 +4463,8 @@ void CoordinateOperationFactory::Private::createOperationsVertToVert(
const bool heightDepthReversal =
((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp));
+ if (convDst == 0)
+ throw InvalidOperation("Conversion factor of target unit is 0");
const double factor = convSrc / convDst;
if (!equivalentVDatum) {
auto name = buildTransfName(sourceCRS->nameStr(), targetCRS->nameStr());
@@ -4557,6 +4562,8 @@ void CoordinateOperationFactory::Private::createOperationsVertToGeogBallpark(
const bool heightDepthReversal =
((srcIsUp && dstIsDown) || (srcIsDown && dstIsUp));
+ if (convDst == 0)
+ throw InvalidOperation("Conversion factor of target unit is 0");
const double factor = convSrc / convDst;
const auto &sourceCRSExtent = getExtent(sourceCRS);
diff --git a/src/iso19111/operation/parammappings.cpp b/src/iso19111/operation/parammappings.cpp
index df5ae7c5..240a2b50 100644
--- a/src/iso19111/operation/parammappings.cpp
+++ b/src/iso19111/operation/parammappings.cpp
@@ -598,11 +598,6 @@ static const MethodMapping projectionMethodMappings[] = {
EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP,
"Lambert_Conformal_Conic_2SP", "lcc", nullptr, paramsLCC2SP},
- // Oracle WKT
- {EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP,
- EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP, "Lambert Conformal Conic",
- "lcc", nullptr, paramsLCC2SP},
-
{EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN,
EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP_MICHIGAN,
nullptr, // no mapping to WKT1_GDAL
diff --git a/src/iso19111/operation/singleoperation.cpp b/src/iso19111/operation/singleoperation.cpp
index 2f60828e..4dadfa40 100644
--- a/src/iso19111/operation/singleoperation.cpp
+++ b/src/iso19111/operation/singleoperation.cpp
@@ -2118,15 +2118,18 @@ bool SingleOperation::exportToPROJStringGeneric(
}
if (methodEPSGCode == EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT) {
- double convFactor = parameterValueNumericAsSI(
+ const double convFactor = parameterValueNumericAsSI(
EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR);
- auto uom = common::UnitOfMeasure(std::string(), convFactor,
- common::UnitOfMeasure::Type::LINEAR)
- .exportToPROJString();
- auto reverse_uom =
- common::UnitOfMeasure(std::string(), 1.0 / convFactor,
+ const auto uom =
+ common::UnitOfMeasure(std::string(), convFactor,
common::UnitOfMeasure::Type::LINEAR)
.exportToPROJString();
+ const auto reverse_uom =
+ convFactor == 0.0
+ ? std::string()
+ : common::UnitOfMeasure(std::string(), 1.0 / convFactor,
+ common::UnitOfMeasure::Type::LINEAR)
+ .exportToPROJString();
if (uom == "m") {
// do nothing
} else if (!uom.empty()) {
diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake
index 785ec6f0..4ece9094 100644
--- a/src/lib_proj.cmake
+++ b/src/lib_proj.cmake
@@ -298,6 +298,60 @@ source_group("CMake Files" FILES CMakeLists.txt)
# Embed PROJ_LIB data files location
add_definitions(-DPROJ_LIB="${CMAKE_INSTALL_PREFIX}/${DATADIR}")
+
+###########################################################
+# targets to refresh wkt1_parser.cpp and wkt2_parser.cpp
+###########################################################
+
+# Those targets need to be run manually each time wkt1_grammar.y / wkt2_grammar.y
+# is modified.
+# We could of course run them automatically, but that would make building
+# PROJ harder.
+
+# This target checks that wkt1_grammar.y md5sum has not changed
+# If it has, then it should be updated and the generate_wkt1_parser target
+# should be manually run
+add_custom_target(check_wkt1_grammar_md5 ALL
+ COMMAND ${CMAKE_COMMAND}
+ "-DIN_FILE=wkt1_grammar.y"
+ "-DTARGET=generate_wkt1_parser"
+ "-DEXPECTED_MD5SUM=3a1720c3fa1b759719e33dd558603efb"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/check_md5sum.cmake"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/wkt1_grammar.y"
+ VERBATIM)
+
+add_custom_target(generate_wkt1_parser
+ COMMAND ${CMAKE_COMMAND}
+ "-DPREFIX=pj_wkt1_"
+ "-DIN_FILE=wkt1_grammar.y"
+ "-DOUT_FILE=wkt1_generated_parser.c"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/generate_wkt_parser.cmake"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM)
+
+# This target checks that wkt2_grammar.y md5sum has not changed
+# If it has, then it should be updated and the generate_wkt2_parser target
+# should be manually run
+add_custom_target(check_wkt2_grammar_md5 ALL
+ COMMAND ${CMAKE_COMMAND}
+ "-DIN_FILE=wkt2_grammar.y"
+ "-DTARGET=generate_wkt2_parser"
+ "-DEXPECTED_MD5SUM=1691b7d213073d5a1b49db2e080bc96e"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/check_md5sum.cmake"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/wkt2_grammar.y"
+ VERBATIM)
+
+add_custom_target(generate_wkt2_parser
+ COMMAND ${CMAKE_COMMAND}
+ "-DPREFIX=pj_wkt2_"
+ "-DIN_FILE=wkt2_grammar.y"
+ "-DOUT_FILE=wkt2_generated_parser.c"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/generate_wkt_parser.cmake"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM)
+
#################################################
## targets: libproj and proj_config.h
#################################################
diff --git a/test/cli/CMakeLists.txt b/test/cli/CMakeLists.txt
index 0f86ca3d..b136b05d 100644
--- a/test/cli/CMakeLists.txt
+++ b/test/cli/CMakeLists.txt
@@ -3,12 +3,14 @@
#
set(CS2CS_BIN "cs2cs")
set(PROJ_BIN "proj")
+set(INVPROJ_BIN "invproj")
set(PROJINFO_BIN "projinfo")
set(CCT_BIN "cct")
set(PROJSYNC_BIN "projsync")
proj_add_test_script_sh("test27" PROJ_BIN)
proj_add_test_script_sh("test83" PROJ_BIN)
proj_add_test_script_sh("testproj" PROJ_BIN)
+proj_add_test_script_sh("testinvproj" INVPROJ_BIN)
proj_add_test_script_sh("testvarious" CS2CS_BIN)
proj_add_test_script_sh("testdatumfile" CS2CS_BIN)
proj_add_test_script_sh("testIGNF" CS2CS_BIN)
diff --git a/test/cli/Makefile.am b/test/cli/Makefile.am
index 46d9d36c..bc840738 100644
--- a/test/cli/Makefile.am
+++ b/test/cli/Makefile.am
@@ -3,6 +3,7 @@ PROJ_LIB = ../../data/for_tests
THIS_DIR = $(top_srcdir)/test/cli
EXEPATH = ../../src
PROJEXE = $(EXEPATH)/proj
+INVPROJEXE = $(EXEPATH)/invproj
CS2CSEXE = $(EXEPATH)/cs2cs
PROJINFOEXE = $(EXEPATH)/projinfo
CCTEXE = $(EXEPATH)/cct
@@ -12,6 +13,7 @@ PROJSYNC_EXE = $(EXEPATH)/projsync
TEST27 = $(THIS_DIR)/test27
TEST83 = $(THIS_DIR)/test83
TESTPROJ = $(THIS_DIR)/testproj
+TESTINVPROJ = $(THIS_DIR)/testinvproj
TESTNTV2 = $(THIS_DIR)/testntv2
TESTVARIOUS = $(THIS_DIR)/testvarious
TESTFLAKY = $(THIS_DIR)/testflaky
@@ -28,6 +30,7 @@ EXTRA_DIST = pj_out27.dist pj_out83.dist td_out.dist \
testprojinfo testprojinfo_out.dist \
testcct testcct_out.dist \
testproj testproj_out.dist \
+ testinvproj testinvproj_out.dist \
test_projsync \
CMakeLists.txt
@@ -43,6 +46,9 @@ test83-check:
testproj-check:
PROJ_LIB=$(PROJ_LIB) $(TESTPROJ) $(PROJEXE)
+testinvproj-check:
+ PROJ_LIB=$(PROJ_LIB) $(TESTINVPROJ) $(INVPROJEXE)
+
testvarious-check:
PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTVARIOUS) $(CS2CSEXE)
@@ -67,4 +73,4 @@ testprojsync-check:
echo "Skipping testprojsync-check"
endif
-check-local: testprojinfo-check test27-check test83-check testproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check testprojsync-check
+check-local: testprojinfo-check test27-check test83-check testproj-check testinvproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check testprojsync-check
diff --git a/test/cli/testinvproj b/test/cli/testinvproj
new file mode 100755
index 00000000..878fd118
--- /dev/null
+++ b/test/cli/testinvproj
@@ -0,0 +1,55 @@
+:
+# Script to test invproj exe
+#
+TEST_CLI_DIR=`dirname $0`
+EXE=$1
+
+usage()
+{
+ echo "Usage: ${0} <path to 'invproj' program>"
+ echo
+ exit 1
+}
+
+if test -z "${EXE}"; then
+ EXE=../../src/invproj
+fi
+
+if test ! -x ${EXE}; then
+ echo "*** ERROR: Can not find '${EXE}' program!"
+ exit 1
+fi
+
+if test -z "${PROJ_LIB}"; then
+ export PROJ_LIB="`dirname $0`/../../data"
+fi
+
+echo "============================================"
+echo "Running ${0} using ${EXE}:"
+echo "============================================"
+
+OUT=testinvproj_out
+#
+echo "doing tests into file ${OUT}, please wait"
+#
+$EXE +proj=tmerc +ellps=GRS80 -E -f '%.3f' >${OUT} <<EOF
+146339.48 5431555.61
+EOF
+
+#
+# do 'diff' with distribution results
+echo "diff ${OUT} with testinvproj_out.dist"
+diff -u -b ${OUT} ${TEST_CLI_DIR}/testinvproj_out.dist
+if [ $? -ne 0 ] ; then
+ echo ""
+ echo "PROBLEMS HAVE OCCURRED"
+ echo "test file ${OUT} saved"
+ echo
+ exit 100
+else
+ echo "TEST OK"
+ echo "test file ${OUT} removed"
+ echo
+ /bin/rm -f ${OUT}
+ exit 0
+fi
diff --git a/test/cli/testinvproj_out.dist b/test/cli/testinvproj_out.dist
new file mode 100644
index 00000000..6b52306f
--- /dev/null
+++ b/test/cli/testinvproj_out.dist
@@ -0,0 +1 @@
+146339.48 5431555.61 2.000 49.000
diff --git a/test/cli/testproj b/test/cli/testproj
index 8686224e..d03932fb 100755
--- a/test/cli/testproj
+++ b/test/cli/testproj
@@ -12,7 +12,7 @@ usage()
}
if test -z "${EXE}"; then
- EXE=../../src/cs2cs
+ EXE=../../src/proj
fi
if test ! -x ${EXE}; then
diff --git a/test/fuzzers/build.sh b/test/fuzzers/build.sh
index 54909fea..25c43fa2 100755
--- a/test/fuzzers/build.sh
+++ b/test/fuzzers/build.sh
@@ -50,10 +50,20 @@ make -j$(nproc)
make install
cd ..
-./autogen.sh
-SQLITE3_CFLAGS=-I/usr/include SQLITE3_LIBS=-lsqlite3 TIFF_CFLAGS=-I$SRC/install/include TIFF_LIBS="-L$SRC/install/lib -ltiff" ./configure --disable-shared --with-curl=$SRC/install/bin/curl-config
+mkdir build
+cd build
+cmake .. -DBUILD_SHARED_LIBS:BOOL=OFF \
+ -DCURL_INCLUDE_DIR:PATH="$SRC/install/include" \
+ -DCURL_LIBRARY_RELEASE:FILEPATH="$SRC/install/lib/libcurl.a" \
+ -DTIFF_INCLUDE_DIR:PATH="$SRC/install/include" \
+ -DTIFF_LIBRARY_RELEASE:FILEPATH="$SRC/install/lib/libtiff.a" \
+ -DCMAKE_INSTALL_PREFIX=$SRC/install \
+ -DBUILD_APPS:BOOL=OFF \
+ -DBUILD_TESTING:BOOL=OFF
make clean -s
make -j$(nproc) -s
+make install
+cd ..
EXTRA_LIBS="-lpthread -Wl,-Bstatic -lsqlite3 -L$SRC/install/lib -ltiff -lcurl -lssl -lcrypto -lz -Wl,-Bdynamic"
@@ -66,7 +76,7 @@ build_fuzzer()
echo "Building fuzzer $fuzzerName"
$CXX $CXXFLAGS -std=c++11 -fvisibility=hidden -Isrc -Iinclude \
$sourceFilename $* -o $OUT/$fuzzerName \
- $LIB_FUZZING_ENGINE src/.libs/libproj.a $EXTRA_LIBS
+ $LIB_FUZZING_ENGINE "$SRC/install/lib/libproj.a" $EXTRA_LIBS
}
build_fuzzer proj_crs_to_crs_fuzzer test/fuzzers/proj_crs_to_crs_fuzzer.cpp
diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp
index 987ffe1b..500ff4a4 100644
--- a/test/unit/test_crs.cpp
+++ b/test/unit/test_crs.cpp
@@ -747,6 +747,77 @@ TEST(crs, EPSG_4268_geogcrs_deprecated_as_WKT1_GDAL) {
// ---------------------------------------------------------------------------
+TEST(crs, ESRI_104971_as_WKT1_ESRI_with_database) {
+ auto dbContext = DatabaseContext::create();
+ auto factory = AuthorityFactory::create(dbContext, "ESRI");
+ auto crs = factory->createCoordinateReferenceSystem("104971");
+ WKTFormatterNNPtr f(WKTFormatter::create(
+ WKTFormatter::Convention::WKT1_ESRI, DatabaseContext::create()));
+ // Check that the _(Sphere) suffix is preserved
+ EXPECT_EQ(crs->exportToWKT(f.get()),
+ "GEOGCS[\"Mars_2000_(Sphere)\",DATUM[\"Mars_2000_(Sphere)\","
+ "SPHEROID[\"Mars_2000_(Sphere)\",3396190.0,0.0]],"
+ "PRIMEM[\"Reference_Meridian\",0.0],"
+ "UNIT[\"Degree\",0.0174532925199433]]");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(crs,
+ implicit_compound_ESRI_104024_plus_115844_as_WKT1_ESRI_with_database) {
+ auto dbContext = DatabaseContext::create();
+ auto obj = createFromUserInput("ESRI:104024+115844", dbContext);
+ auto crs = nn_dynamic_pointer_cast<GeographicCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+ EXPECT_EQ(crs->coordinateSystem()->axisList().size(), 3U);
+ WKTFormatterNNPtr f(WKTFormatter::create(
+ WKTFormatter::Convention::WKT1_ESRI, DatabaseContext::create()));
+ // Situation where there is no EPSG official name
+ EXPECT_EQ(crs->exportToWKT(f.get()),
+ "GEOGCS[\"California_SRS_Epoch_2017.50_(NAD83)\","
+ "DATUM[\"California_SRS_Epoch_2017.50_(NAD83)\","
+ "SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],"
+ "PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0.0174532925199433]],"
+ "VERTCS[\"California_SRS_Epoch_2017.50_(NAD83)\","
+ "DATUM[\"California_SRS_Epoch_2017.50_(NAD83)\","
+ "SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],"
+ "PARAMETER[\"Vertical_Shift\",0.0],"
+ "PARAMETER[\"Direction\",1.0],UNIT[\"Meter\",1.0]]");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(crs, implicit_compound_ESRI_104971_to_3D_as_WKT1_ESRI_with_database) {
+ auto dbContext = DatabaseContext::create();
+ auto factory = AuthorityFactory::create(dbContext, "ESRI");
+ auto crs = factory->createGeographicCRS("104971")->promoteTo3D(
+ std::string(), dbContext);
+ WKTFormatterNNPtr f(WKTFormatter::create(
+ WKTFormatter::Convention::WKT1_ESRI, DatabaseContext::create()));
+ // Situation where there is no ESRI vertical CRS, but the GEOGCS does exist
+ // This will be only partly recognized by ESRI software.
+ // See https://github.com/OSGeo/PROJ/issues/2757
+ const char *wkt = "GEOGCS[\"Mars_2000_(Sphere)\","
+ "DATUM[\"Mars_2000_(Sphere)\","
+ "SPHEROID[\"Mars_2000_(Sphere)\",3396190.0,0.0]],"
+ "PRIMEM[\"Reference_Meridian\",0.0],"
+ "UNIT[\"Degree\",0.0174532925199433]],"
+ "VERTCS[\"Mars_2000_(Sphere)\","
+ "DATUM[\"Mars_2000_(Sphere)\","
+ "SPHEROID[\"Mars_2000_(Sphere)\",3396190.0,0.0]],"
+ "PARAMETER[\"Vertical_Shift\",0.0],"
+ "PARAMETER[\"Direction\",1.0],"
+ "UNIT[\"Meter\",1.0]]";
+ EXPECT_EQ(crs->exportToWKT(f.get()), wkt);
+
+ auto obj2 = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt);
+ auto crs2 = nn_dynamic_pointer_cast<GeographicCRS>(obj2);
+ ASSERT_TRUE(crs2 != nullptr);
+ EXPECT_EQ(crs2->coordinateSystem()->axisList().size(), 3U);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(crs, IAU_1000_as_WKT2) {
auto dbContext = DatabaseContext::create();
auto factory = AuthorityFactory::create(dbContext, "IAU_2015");
@@ -3877,6 +3948,23 @@ TEST(crs, verticalCRS_down_as_WKT1_ESRI) {
// ---------------------------------------------------------------------------
+TEST(crs, verticalCRS_ESRI_115834_as_WKT1_ESRI_with_database) {
+ auto dbContext = DatabaseContext::create();
+ auto factory = AuthorityFactory::create(dbContext, "ESRI");
+ auto crs = factory->createCoordinateReferenceSystem("115834");
+ WKTFormatterNNPtr f(WKTFormatter::create(
+ WKTFormatter::Convention::WKT1_ESRI, DatabaseContext::create()));
+ // Check that the parentheses in the VERTCS and DATUM names are preserved
+ EXPECT_EQ(crs->exportToWKT(f.get()),
+ "VERTCS[\"NAD83(CSRS)v5\","
+ "DATUM[\"North_American_Datum_of_1983_(CSRS)_version_5\","
+ "SPHEROID[\"GRS_1980\",6378137.0,298.257222101]],"
+ "PARAMETER[\"Vertical_Shift\",0.0],"
+ "PARAMETER[\"Direction\",1.0],UNIT[\"Meter\",1.0]]");
+}
+
+// ---------------------------------------------------------------------------
+
TEST(crs, verticalCRS_identify_db) {
auto dbContext = DatabaseContext::create();
auto factory = AuthorityFactory::create(dbContext, "EPSG");
diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp
index 4e888f2c..196552d4 100644
--- a/test/unit/test_io.cpp
+++ b/test/unit/test_io.cpp
@@ -6394,6 +6394,37 @@ TEST(wkt_parse, wkt1_oracle) {
// ---------------------------------------------------------------------------
+TEST(wkt_parse, wkt1_lcc_1sp_without_1sp_suffix) {
+ // WKT from Trimble
+ auto wkt = "PROJCS[\"TWM-Madison Co LDP\","
+ "GEOGCS[\"WGS 1984\","
+ "DATUM[\"WGS 1984\","
+ "SPHEROID[\"World Geodetic System 1984\","
+ "6378137,298.257223563]],"
+ "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
+ "UNIT[\"Degree\",0.01745329251994,"
+ "AUTHORITY[\"EPSG\",\"9102\"]],"
+ "AXIS[\"Long\",EAST],AXIS[\"Lat\",NORTH]],"
+ "PROJECTION[\"Lambert_Conformal_Conic\"],"
+ "PARAMETER[\"False_Easting\",103000.0000035],"
+ "PARAMETER[\"False_Northing\",79000.00007055],"
+ "PARAMETER[\"Latitude_Of_Origin\",38.83333333333],"
+ "PARAMETER[\"Central_Meridian\",-89.93333333333],"
+ "PARAMETER[\"Scale_Factor\",1.000019129],"
+ "UNIT[\"Foot_US\",0.3048006096012,AUTHORITY[\"EPSG\",\"9003\"]],"
+ "AXIS[\"East\",EAST],AXIS[\"North\",NORTH]]";
+
+ auto dbContext = DatabaseContext::create();
+ auto obj = WKTParser().attachDatabaseContext(dbContext).createFromWKT(wkt);
+ auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
+ ASSERT_TRUE(crs != nullptr);
+
+ EXPECT_EQ(crs->derivingConversion()->method()->nameStr(),
+ "Lambert Conic Conformal (1SP)");
+}
+
+// ---------------------------------------------------------------------------
+
TEST(wkt_parse, invalid) {
EXPECT_THROW(WKTParser().createFromWKT(""), ParsingException);
EXPECT_THROW(WKTParser().createFromWKT("A"), ParsingException);
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index f76647e2..0e27aa9b 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -5429,6 +5429,20 @@ TEST(operation, normalizeForVisualization) {
auto authFactory =
AuthorityFactory::create(DatabaseContext::create(), "EPSG");
+ const auto checkThroughWKTRoundtrip = [](const CoordinateOperationNNPtr
+ &opRef) {
+ auto wkt = opRef->exportToWKT(
+ WKTFormatter::create(WKTFormatter::Convention::WKT2_2019).get());
+ auto objFromWkt = WKTParser().createFromWKT(wkt);
+ auto opFromWkt =
+ nn_dynamic_pointer_cast<CoordinateOperation>(objFromWkt);
+ ASSERT_TRUE(opFromWkt != nullptr);
+ EXPECT_TRUE(opRef->_isEquivalentTo(opFromWkt.get()));
+ EXPECT_EQ(
+ opFromWkt->exportToPROJString(PROJStringFormatter::create().get()),
+ opRef->exportToPROJString(PROJStringFormatter::create().get()));
+ };
+
// Source(geographic) must be inverted
{
auto src = authFactory->createCoordinateReferenceSystem("4326");
@@ -5443,6 +5457,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +proj=unitconvert +xy_in=deg +xy_out=rad "
"+step +proj=utm +zone=31 +ellps=WGS84");
+ checkThroughWKTRoundtrip(opNormalized);
}
// Target(geographic) must be inverted
@@ -5459,6 +5474,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +inv +proj=utm +zone=31 +ellps=WGS84 "
"+step +proj=unitconvert +xy_in=rad +xy_out=deg");
+ checkThroughWKTRoundtrip(opNormalized);
}
// Source(geographic) and target(projected) must be inverted
@@ -5475,6 +5491,7 @@ TEST(operation, normalizeForVisualization) {
"+proj=pipeline "
"+step +proj=unitconvert +xy_in=deg +xy_out=rad "
"+step +proj=utm +zone=28 +ellps=GRS80");
+ checkThroughWKTRoundtrip(opNormalized);
}
// No inversion