diff options
| author | Even Rouault <even.rouault@spatialys.com> | 2018-12-07 19:53:48 +0100 |
|---|---|---|
| committer | Even Rouault <even.rouault@spatialys.com> | 2018-12-07 19:53:48 +0100 |
| commit | 23127c01ad535902665a975da81e27c389bb7aeb (patch) | |
| tree | d6cc90dfe1069425824de40fbe42342a1afca9bd | |
| parent | 706fac8bc70312f5729e2f3aeeb4f67ecb211b1d (diff) | |
| parent | 29b522b4b80b43fe03cb1a955789676eec8051e7 (diff) | |
| download | PROJ-23127c01ad535902665a975da81e27c389bb7aeb.tar.gz PROJ-23127c01ad535902665a975da81e27c389bb7aeb.zip | |
Merge remote-tracking branch 'rouault/gdalbarn'
| -rw-r--r-- | data/Makefile.am | 1 | ||||
| -rw-r--r-- | data/sql/commit.sql | 9 | ||||
| -rw-r--r-- | data/sql/customizations.sql | 18 | ||||
| -rw-r--r-- | data/sql/deprecation.sql | 456 | ||||
| -rw-r--r-- | data/sql/proj_db_table_defs.sql | 33 | ||||
| -rw-r--r-- | data/sql_filelist.cmake | 1 | ||||
| -rw-r--r-- | include/proj/common.hpp | 12 | ||||
| -rw-r--r-- | include/proj/crs.hpp | 8 | ||||
| -rw-r--r-- | include/proj/io.hpp | 12 | ||||
| -rw-r--r-- | include/proj/metadata.hpp | 4 | ||||
| -rw-r--r-- | include/proj/util.hpp | 9 | ||||
| -rwxr-xr-x | scripts/build_db.py | 19 | ||||
| -rw-r--r-- | src/c_api.cpp | 102 | ||||
| -rw-r--r-- | src/common.cpp | 80 | ||||
| -rw-r--r-- | src/coordinateoperation.cpp | 114 | ||||
| -rw-r--r-- | src/coordinatesystem.cpp | 8 | ||||
| -rw-r--r-- | src/crs.cpp | 194 | ||||
| -rw-r--r-- | src/factory.cpp | 1005 | ||||
| -rw-r--r-- | src/internal.cpp | 33 | ||||
| -rw-r--r-- | src/io.cpp | 25 | ||||
| -rw-r--r-- | src/metadata.cpp | 70 | ||||
| -rw-r--r-- | src/proj.h | 3 | ||||
| -rw-r--r-- | src/proj_experimental.h | 10 | ||||
| -rw-r--r-- | src/projinfo.cpp | 52 | ||||
| -rw-r--r-- | src/util.cpp | 104 | ||||
| -rwxr-xr-x | test/cli/testprojinfo | 4 | ||||
| -rw-r--r-- | test/cli/testprojinfo_out.dist | 98 | ||||
| -rw-r--r-- | test/unit/test_c_api.cpp | 43 | ||||
| -rw-r--r-- | test/unit/test_crs.cpp | 159 | ||||
| -rw-r--r-- | test/unit/test_operation.cpp | 8 |
30 files changed, 1973 insertions, 721 deletions
diff --git a/data/Makefile.am b/data/Makefile.am index a85047c6..fe5ad532 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -28,6 +28,7 @@ SQL_ORDERED_LIST = sql/begin.sql \ sql/concatenated_operation.sql \ sql/alias_name.sql \ sql/supersession.sql \ + sql/deprecation.sql \ sql/esri.sql \ sql/ignf.sql \ sql/grid_alternatives.sql \ diff --git a/data/sql/commit.sql b/data/sql/commit.sql index 346c5b76..7aa91e8e 100644 --- a/data/sql/commit.sql +++ b/data/sql/commit.sql @@ -1,5 +1,14 @@ COMMIT; +CREATE INDEX geodetic_crs_datum_idx ON geodetic_crs(datum_auth_name, datum_code); +CREATE INDEX geodetic_datum_ellipsoid_idx ON geodetic_datum(ellipsoid_auth_name, ellipsoid_code); +CREATE INDEX supersession_idx ON supersession(superseded_table_name, superseded_auth_name, superseded_code); +CREATE INDEX deprecation_idx ON deprecation(table_name, deprecated_auth_name, deprecated_code); +CREATE INDEX helmert_transformation_idx ON helmert_transformation(source_crs_auth_name, source_crs_code, target_crs_auth_name, target_crs_code); +CREATE INDEX grid_transformation_idx ON grid_transformation(source_crs_auth_name, source_crs_code, target_crs_auth_name, target_crs_code); +CREATE INDEX other_transformation_idx ON other_transformation(source_crs_auth_name, source_crs_code, target_crs_auth_name, target_crs_code); +CREATE INDEX concatenated_operation_idx ON concatenated_operation(source_crs_auth_name, source_crs_code, target_crs_auth_name, target_crs_code); + -- Do an explicit foreign_key_check as foreign key checking is a no-op within -- a transaction. Unfortunately we can't ask for this to be an error, so this -- is just for verbose output. In Makefile, we check this separately diff --git a/data/sql/customizations.sql b/data/sql/customizations.sql index 2181cc2b..8e31c233 100644 --- a/data/sql/customizations.sql +++ b/data/sql/customizations.sql @@ -6,3 +6,21 @@ INSERT INTO "other_transformation" VALUES('PROJ','CRS84_TO_EPSG_4326','OGC:CRS84 -- alias of EPSG:3857 INSERT INTO "projected_crs" VALUES('EPSG','900913','Google Maps Global Mercator',NULL,NULL,'EPSG','4499','EPSG','4326','EPSG','3856','EPSG','3544',NULL,1); + +-- Define the allowed authorities, and their precedence, when researching a +-- coordinate operation + +INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES + ('any', 'EPSG', 'PROJ,EPSG,any' ); + +INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES + ('EPSG', 'EPSG', 'PROJ,EPSG' ); + +INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES + ('PROJ', 'EPSG', 'PROJ,EPSG' ); + +INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES + ('IGNF', 'EPSG', 'PROJ,IGNF,EPSG' ); + +INSERT INTO authority_to_authority_preference(source_auth_name, target_auth_name, allowed_authorities) VALUES + ('ESRI', 'EPSG', 'PROJ,ESRI,EPSG' );
\ No newline at end of file diff --git a/data/sql/deprecation.sql b/data/sql/deprecation.sql new file mode 100644 index 00000000..4ea3e558 --- /dev/null +++ b/data/sql/deprecation.sql @@ -0,0 +1,456 @@ +--- This file has been generated by scripts/build_db.py. DO NOT EDIT ! + +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2156','EPSG','2195','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2291','EPSG','2292','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4035','EPSG','4047','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4226','EPSG','4142','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31462','EPSG','31466','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31463','EPSG','31467','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31464','EPSG','31468','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31465','EPSG','31469','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31265','EPSG','31275','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31266','EPSG','31276','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31267','EPSG','31277','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31268','EPSG','31278','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31278','EPSG','31279','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31291','EPSG','31281','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31292','EPSG','31282','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31293','EPSG','31283','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31294','EPSG','31284','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31295','EPSG','31285','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31296','EPSG','31286','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31297','EPSG','31287','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29900','EPSG','29902','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2155','EPSG','2194','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4226','EPSG','4143','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4172','EPSG','4190','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27581','EPSG','27571','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27582','EPSG','27572','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27583','EPSG','27573','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27584','EPSG','27574','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27591','EPSG','27561','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27592','EPSG','27562','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27593','EPSG','27563','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27594','EPSG','27564','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7401','EPSG','7411','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7402','EPSG','7412','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7403','EPSG','7413','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32036','EPSG','2204','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26979','EPSG','2205','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4228','EPSG','4192','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4260','EPSG','4193','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','22832','EPSG','2214','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4287','EPSG','4194','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32074','EPSG','32064','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32075','EPSG','32065','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32076','EPSG','32066','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32077','EPSG','32067','EPSG'); +INSERT INTO "deprecation" VALUES('vertical_crs','EPSG','5704','EPSG','5736','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21473','EPSG','21453','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21474','EPSG','21454','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21475','EPSG','21455','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21476','EPSG','21456','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21477','EPSG','21457','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21478','EPSG','21458','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21479','EPSG','21459','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21480','EPSG','21460','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21481','EPSG','21461','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21482','EPSG','21462','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21483','EPSG','21463','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2199','EPSG','2462','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2166','EPSG','2397','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2167','EPSG','2398','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2168','EPSG','2399','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2091','EPSG','2395','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2092','EPSG','2396','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20092','EPSG','2491','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20091','EPSG','2490','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20090','EPSG','2489','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20089','EPSG','2488','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20088','EPSG','2487','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20087','EPSG','2486','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20086','EPSG','2485','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20085','EPSG','2484','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20084','EPSG','2483','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20083','EPSG','2482','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20082','EPSG','2481','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20081','EPSG','2480','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20080','EPSG','2479','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20079','EPSG','2478','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20078','EPSG','2477','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20077','EPSG','2476','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20076','EPSG','2475','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20075','EPSG','2474','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20074','EPSG','2473','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20073','EPSG','2472','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20072','EPSG','2471','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20071','EPSG','2470','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20070','EPSG','2469','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20069','EPSG','2468','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20068','EPSG','2467','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20067','EPSG','2466','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20066','EPSG','2465','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20065','EPSG','2464','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','20064','EPSG','2463','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28462','EPSG','2492','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28463','EPSG','2493','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28464','EPSG','2494','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28465','EPSG','2495','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28466','EPSG','2496','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28467','EPSG','2497','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28468','EPSG','2498','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28469','EPSG','2499','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28470','EPSG','2500','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28471','EPSG','2501','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28472','EPSG','2502','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28473','EPSG','2503','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28474','EPSG','2504','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28475','EPSG','2505','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28476','EPSG','2506','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28477','EPSG','2507','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28478','EPSG','2508','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28479','EPSG','2509','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28480','EPSG','2510','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28481','EPSG','2511','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28482','EPSG','2512','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28483','EPSG','2513','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28484','EPSG','2514','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28485','EPSG','2515','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28486','EPSG','2516','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28487','EPSG','2517','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28488','EPSG','2518','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28489','EPSG','2519','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28490','EPSG','2520','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28491','EPSG','2521','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28492','EPSG','2522','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4294','EPSG','4613','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4125','EPSG','4613','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2550','EPSG','2933','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4185','EPSG','4615','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4185','EPSG','4616','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2191','EPSG','2942','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2191','EPSG','2943','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4140','EPSG','4617','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2147','EPSG','2952','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2140','EPSG','2945','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2141','EPSG','2946','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2142','EPSG','2947','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2143','EPSG','2948','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2144','EPSG','2949','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2145','EPSG','2950','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2146','EPSG','2951','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2036','EPSG','2953','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2292','EPSG','2954','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2139','EPSG','2944','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2153','EPSG','2955','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2152','EPSG','2956','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2151','EPSG','2957','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2150','EPSG','2958','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2149','EPSG','2959','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2037','EPSG','2960','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2038','EPSG','2961','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2148','EPSG','2962','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4234','EPSG','4197','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','23433','EPSG','2312','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4291','EPSG','4618','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29100','EPSG','29101','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29177','EPSG','29187','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29118','EPSG','29168','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29185','EPSG','29195','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29184','EPSG','29194','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29183','EPSG','29193','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29182','EPSG','29192','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29181','EPSG','29191','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29180','EPSG','29190','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29179','EPSG','29189','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29178','EPSG','29188','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29122','EPSG','29172','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29121','EPSG','29171','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29120','EPSG','29170','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29119','EPSG','29169','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26193','EPSG','26194','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2245','EPSG','2966','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2244','EPSG','2965','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2889','EPSG','2967','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2890','EPSG','2968','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4327','EPSG','4329','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4235','EPSG','4623','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21100','EPSG','3001','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','25700','EPSG','3002','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2934','EPSG','3000','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26591','EPSG','3003','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26592','EPSG','3004','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29635','EPSG','20135','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29636','EPSG','20136','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4296','EPSG','4201','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2400','EPSG','3021','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','30800','EPSG','3027','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4634','EPSG','4662','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2982','EPSG','3060','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4340','EPSG','4930','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4344','EPSG','4932','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4342','EPSG','4934','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4346','EPSG','4936','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4348','EPSG','4938','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4350','EPSG','4940','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4352','EPSG','4942','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4387','EPSG','4944','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4385','EPSG','4919','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4330','EPSG','4910','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4331','EPSG','4911','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4332','EPSG','4912','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4333','EPSG','4913','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4334','EPSG','4914','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4335','EPSG','4915','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4336','EPSG','4916','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4337','EPSG','4917','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4338','EPSG','4918','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4354','EPSG','4946','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4389','EPSG','4948','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4356','EPSG','4950','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4358','EPSG','4952','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4360','EPSG','4954','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4362','EPSG','4956','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4364','EPSG','4958','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4366','EPSG','4960','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4368','EPSG','4962','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4370','EPSG','4964','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4372','EPSG','4966','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4382','EPSG','4968','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4374','EPSG','4970','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4384','EPSG','4972','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4376','EPSG','4974','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4378','EPSG','4976','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4328','EPSG','4978','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4380','EPSG','4980','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4388','EPSG','4949','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4386','EPSG','4945','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4383','EPSG','4973','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4381','EPSG','4969','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4379','EPSG','4981','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4377','EPSG','4977','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4375','EPSG','4975','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4373','EPSG','4971','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4371','EPSG','4967','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4369','EPSG','4965','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4367','EPSG','4963','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4365','EPSG','4961','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4363','EPSG','4959','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4361','EPSG','4957','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4359','EPSG','4955','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4357','EPSG','4953','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4355','EPSG','4951','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4353','EPSG','4947','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4351','EPSG','4943','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4349','EPSG','4941','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4347','EPSG','4939','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4345','EPSG','4937','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4343','EPSG','4933','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4341','EPSG','4935','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4339','EPSG','4931','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4329','EPSG','4979','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4126','EPSG','4669','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4819','EPSG','4307','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31900','EPSG','31901','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2194','EPSG','3102','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4233','EPSG','4684','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4233','EPSG','4685','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21891','EPSG','21896','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21892','EPSG','21897','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21893','EPSG','21898','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','21894','EPSG','21899','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2214','EPSG','3119','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26747','EPSG','26799','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4172','EPSG','4694','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4685','EPSG','4696','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2171','EPSG','3120','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2979','EPSG','3336','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4631','EPSG','4698','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2600','EPSG','3346','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26432','EPSG','3353','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26432','EPSG','3354','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4264','EPSG','4704','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4264','EPSG','4705','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7408','EPSG','7415','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4681','EPSG','4700','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4681','EPSG','4702','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3103','EPSG','3343','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3103','EPSG','3367','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3104','EPSG','3344','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3104','EPSG','3368','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3105','EPSG','3345','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3105','EPSG','3369','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5532','EPSG','5858','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2577','EPSG','3389','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2694','EPSG','3390','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3785','EPSG','3857','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4968','EPSG','4906','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4645','EPSG','4749','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4969','EPSG','4907','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2984','EPSG','3163','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4635','EPSG','4750','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2983','EPSG','3164','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','24571','EPSG','3167','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','24571','EPSG','3168','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4731','EPSG','4752','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3359','EPSG','3404','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3366','EPSG','3407','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29700','EPSG','29701','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','29700','EPSG','29702','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3143','EPSG','3460','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2063','EPSG','3461','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2064','EPSG','3462','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3073','EPSG','3463','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3076','EPSG','3464','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2990','EPSG','3727','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4902','EPSG','4901','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7412','EPSG','7421','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','7413','EPSG','7422','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27492','EPSG','27493','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32662','EPSG','32663','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32662','EPSG','3786','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2086','EPSG','3796','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2085','EPSG','3795','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26814','EPSG','26847','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26815','EPSG','26848','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26825','EPSG','26855','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26826','EPSG','26856','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26836','EPSG','26863','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26837','EPSG','26864','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26819','EPSG','26849','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26820','EPSG','26850','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26821','EPSG','26851','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26830','EPSG','26857','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26831','EPSG','26858','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26832','EPSG','26859','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26841','EPSG','26865','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26842','EPSG','26866','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26843','EPSG','26867','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26822','EPSG','26852','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26833','EPSG','26860','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26844','EPSG','26868','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26823','EPSG','26853','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26824','EPSG','26854','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26834','EPSG','26861','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26835','EPSG','26862','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26845','EPSG','26869','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26846','EPSG','26870','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3774','EPSG','3800','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3778','EPSG','3801','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3782','EPSG','3802','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3349','EPSG','3832','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3752','EPSG','3994','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28403','EPSG','3333','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','28402','EPSG','3833','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31700','EPSG','3844','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4317','EPSG','4179','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31275','EPSG','3907','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31276','EPSG','3908','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31277','EPSG','3909','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','31279','EPSG','3910','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3787','EPSG','3912','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2170','EPSG','3911','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3314','EPSG','3985','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3315','EPSG','3989','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3315','EPSG','3988','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3315','EPSG','3987','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3315','EPSG','3986','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32663','EPSG','4087','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3786','EPSG','4088','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3985','EPSG','4415','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3842','EPSG','4417','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3843','EPSG','4434','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32029','EPSG','4455','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32018','EPSG','4456','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3454','EPSG','4457','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4972','EPSG','4556','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4973','EPSG','4557','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4640','EPSG','4558','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2989','EPSG','4559','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4868','EPSG','5118','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4869','EPSG','5119','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4870','EPSG','5120','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4861','EPSG','5111','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4860','EPSG','5110','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4871','EPSG','5121','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4872','EPSG','5122','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4873','EPSG','5123','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4855','EPSG','5105','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4856','EPSG','5106','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4857','EPSG','5107','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4858','EPSG','5108','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4859','EPSG','5109','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4862','EPSG','5112','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4863','EPSG','5113','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4864','EPSG','5114','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4865','EPSG','5115','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4866','EPSG','5116','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4867','EPSG','5117','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4874','EPSG','5124','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4875','EPSG','5125','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4876','EPSG','5126','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4877','EPSG','5127','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4878','EPSG','5128','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4879','EPSG','5129','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4880','EPSG','5130','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32061','EPSG','5458','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','32062','EPSG','5459','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5466','EPSG','5589','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5458','EPSG','5559','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26801','EPSG','5623','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26802','EPSG','5624','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26803','EPSG','5625','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','2192','EPSG','2154','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5388','EPSG','5839','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','4474','EPSG','5879','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3356','EPSG','6128','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3357','EPSG','6129','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26811','EPSG','6200','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26812','EPSG','6201','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','26813','EPSG','6202','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','4268','EPSG','4267','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5570','EPSG','6381','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5577','EPSG','6381','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5571','EPSG','6382','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5578','EPSG','6382','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5579','EPSG','6383','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5572','EPSG','6383','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5573','EPSG','6384','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5580','EPSG','6384','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5574','EPSG','6385','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5581','EPSG','6385','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5575','EPSG','6386','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5582','EPSG','6386','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5576','EPSG','6387','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','5583','EPSG','6387','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6141','EPSG','6391','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6604','EPSG','6879','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6517','EPSG','6880','EPSG'); +INSERT INTO "deprecation" VALUES('compound_crs','EPSG','6871','EPSG','6893','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3975','EPSG','6933','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3973','EPSG','6931','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3974','EPSG','6932','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6200','EPSG','6966','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','7088','EPSG','7133','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6996','EPSG','7131','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6997','EPSG','7132','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27037','EPSG','7005','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3907','EPSG','8677','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6978','EPSG','7134','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6980','EPSG','7136','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6979','EPSG','7135','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6985','EPSG','7137','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6986','EPSG','7138','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','6987','EPSG','7139','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3908','EPSG','8678','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3909','EPSG','6316','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','27038','EPSG','7006','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6956','EPSG','5896','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6957','EPSG','5897','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6958','EPSG','5898','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','6959','EPSG','5899','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3910','EPSG','8679','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','7082','EPSG','8456','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','7082','EPSG','8455','EPSG'); +INSERT INTO "deprecation" VALUES('projected_crs','EPSG','3911','EPSG','8686','EPSG'); +INSERT INTO "deprecation" VALUES('geodetic_crs','EPSG','8449','EPSG','8860','EPSG'); diff --git a/data/sql/proj_db_table_defs.sql b/data/sql/proj_db_table_defs.sql index 553dab38..d403f0eb 100644 --- a/data/sql/proj_db_table_defs.sql +++ b/data/sql/proj_db_table_defs.sql @@ -946,6 +946,30 @@ FOR EACH ROW BEGIN END; +CREATE TABLE deprecation( + table_name TEXT NOT NULL CHECK (table_name IN ( + 'unit_of_measure', 'celestial_body', 'ellipsoid', + 'area', 'prime_meridian', 'geodetic_datum', 'vertical_datum', 'geodetic_crs', + 'projected_crs', 'vertical_crs', 'compound_crs', 'conversion', 'grid_transformation', + 'helmert_transformation', 'other_transformation', 'concatenated_operation')), + deprecated_auth_name TEXT NOT NULL, + deprecated_code TEXT NOT NULL, + replacement_auth_name TEXT NOT NULL, + replacement_code TEXT NOT NULL, + source TEXT +); + +CREATE TRIGGER deprecation_insert_trigger +BEFORE INSERT ON deprecation +FOR EACH ROW BEGIN + SELECT RAISE(ABORT, 'insert on deprecation violates constraint: deprecated entry refers to unexisting code') + WHERE NOT EXISTS (SELECT 1 FROM object_view o WHERE o.table_name = NEW.table_name AND o.auth_name = NEW.deprecated_auth_name AND o.code = NEW.deprecated_code); + + SELECT RAISE(ABORT, 'insert on deprecation violates constraint: replacement entry refers to unexisting code') + WHERE NOT EXISTS (SELECT 1 FROM object_view o WHERE o.table_name = NEW.table_name AND o.auth_name = NEW.replacement_auth_name AND o.code = NEW.replacement_code); +END; + + CREATE VIEW coordinate_operation_view AS SELECT 'grid_transformation' AS table_name, auth_name, code, name, @@ -1048,3 +1072,12 @@ CREATE VIEW authority_list AS UNION SELECT DISTINCT auth_name FROM coordinate_operation_view ; + +-- Define the allowed authorities, and their precedence, when researching a +-- coordinate operation +CREATE TABLE authority_to_authority_preference( + source_auth_name TEXT NOT NULL, -- 'any' for any source + target_auth_name TEXT NOT NULL, -- 'any' for any target + allowed_authorities TEXT NOT NULL, -- for example 'PROJ,EPSG,any' + CONSTRAINT unique_authority_to_authority_preference UNIQUE (source_auth_name, target_auth_name) +); diff --git a/data/sql_filelist.cmake b/data/sql_filelist.cmake index 09e45716..b9054d23 100644 --- a/data/sql_filelist.cmake +++ b/data/sql_filelist.cmake @@ -23,6 +23,7 @@ set(SQL_FILES "${SQL_DIR}/begin.sql" "${SQL_DIR}/concatenated_operation.sql" "${SQL_DIR}/alias_name.sql" "${SQL_DIR}/supersession.sql" + "${SQL_DIR}/deprecation.sql" "${SQL_DIR}/esri.sql" "${SQL_DIR}/ignf.sql" "${SQL_DIR}/grid_alternatives.sql" diff --git a/include/proj/common.hpp b/include/proj/common.hpp index f1e683e7..91756c62 100644 --- a/include/proj/common.hpp +++ b/include/proj/common.hpp @@ -277,9 +277,9 @@ using IdentifiedObjectNNPtr = util::nn<IdentifiedObjectPtr>; * * \remark Implements IdentifiedObject from \ref ISO_19111_2018 */ -class IdentifiedObject : public util::BaseObject, - public util::IComparable, - public io::IWKTExportable { +class PROJ_GCC_DLL IdentifiedObject : public util::BaseObject, + public util::IComparable, + public io::IWKTExportable { public: //! @cond Doxygen_Suppress PROJ_DLL ~IdentifiedObject() override; @@ -311,14 +311,14 @@ class IdentifiedObject : public util::BaseObject, //! @cond Doxygen_Suppress void formatID(io::WKTFormatter *formatter) const; - void formatRemarks(io::WKTFormatter *formatter) const; + PROJ_INTERNAL void formatRemarks(io::WKTFormatter *formatter) const; - bool + PROJ_INTERNAL bool _isEquivalentTo(const util::IComparable *other, util::IComparable::Criterion criterion = util::IComparable::Criterion::STRICT) const override; - bool + PROJ_INTERNAL bool _isEquivalentTo(const IdentifiedObject *other, util::IComparable::Criterion criterion = util::IComparable::Criterion::STRICT) PROJ_CONST_DECL; diff --git a/include/proj/crs.hpp b/include/proj/crs.hpp index bdb36cd4..1066f886 100644 --- a/include/proj/crs.hpp +++ b/include/proj/crs.hpp @@ -92,8 +92,9 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage { PROJ_DLL GeodeticCRSPtr extractGeodeticCRS() const; PROJ_DLL GeographicCRSPtr extractGeographicCRS() const; PROJ_DLL VerticalCRSPtr extractVerticalCRS() const; - PROJ_DLL CRSNNPtr createBoundCRSToWGS84IfPossible( - const io::DatabaseContextPtr &dbContext) const; + PROJ_DLL CRSNNPtr + createBoundCRSToWGS84IfPossible(const io::DatabaseContextPtr &dbContext, + bool allowIntermediateCRS) const; PROJ_DLL CRSNNPtr stripVerticalComponent() const; PROJ_DLL const BoundCRSPtr &canonicalBoundCRS() PROJ_CONST_DECL; @@ -101,6 +102,9 @@ class PROJ_GCC_DLL CRS : public common::ObjectUsage { PROJ_DLL std::list<std::pair<CRSNNPtr, int>> identify(const io::AuthorityFactoryPtr &authorityFactory) const; + PROJ_DLL std::list<CRSNNPtr> + getNonDeprecated(const io::DatabaseContextNNPtr &dbContext) const; + PROJ_PRIVATE : //! @cond Doxygen_Suppress PROJ_INTERNAL const GeodeticCRS * diff --git a/include/proj/io.hpp b/include/proj/io.hpp index 26420150..c0d4fef5 100644 --- a/include/proj/io.hpp +++ b/include/proj/io.hpp @@ -731,6 +731,15 @@ class PROJ_GCC_DLL DatabaseContext { PROJ_INTERNAL std::string getTextDefinition(const std::string &tableName, const std::string &authName, const std::string &code) const; + + PROJ_INTERNAL std::vector<std::string> + getAllowedAuthorities(const std::string &sourceAuthName, + const std::string &targetAuthName) const; + + PROJ_INTERNAL std::list<std::pair<std::string, std::string>> + getNonDeprecated(const std::string &tableName, const std::string &authName, + const std::string &code) const; + //! @endcond protected: @@ -952,7 +961,8 @@ class PROJ_GCC_DLL AuthorityFactory { PROJ_INTERNAL operation::CoordinateOperationNNPtr createCoordinateOperation(const std::string &code, bool allowConcatenated, - bool usePROJAlternativeGridNames) const; + bool usePROJAlternativeGridNames, + const std::string &type) const; INLINED_MAKE_SHARED diff --git a/include/proj/metadata.hpp b/include/proj/metadata.hpp index fc86693d..d1b91515 100644 --- a/include/proj/metadata.hpp +++ b/include/proj/metadata.hpp @@ -406,6 +406,7 @@ class PROJ_GCC_DLL Identifier : public util::BaseObject, protected: PROJ_INTERNAL explicit Identifier(const std::string &codeIn, const util::PropertyMap &properties); + PROJ_INTERNAL explicit Identifier(); PROJ_FRIEND_OPTIONAL(Identifier); INLINED_MAKE_SHARED @@ -413,6 +414,9 @@ class PROJ_GCC_DLL Identifier : public util::BaseObject, PROJ_FRIEND(common::IdentifiedObject); + PROJ_INTERNAL static IdentifierNNPtr + createFromDescription(const std::string &descriptionIn); + private: PROJ_OPAQUE_PRIVATE_DATA }; diff --git a/include/proj/util.hpp b/include/proj/util.hpp index eb7288b2..c2f2b7fe 100644 --- a/include/proj/util.hpp +++ b/include/proj/util.hpp @@ -499,12 +499,13 @@ class PropertyMap { PROJ_PRIVATE : //! @cond Doxygen_Suppress - std::map<std::string, BaseObjectNNPtr>::iterator - find(const std::string &key) const; - std::map<std::string, BaseObjectNNPtr>::iterator end() const; + const BaseObjectNNPtr * + get(const std::string &key) const; // throw(InvalidValueTypeException) bool getStringValue(const std::string &key, std::string &outVal) const; + bool getStringValue(const std::string &key, + optional<std::string> &outVal) const; static PropertyMap createAndSetName(const char *name); static PropertyMap createAndSetName(const std::string &name); @@ -513,8 +514,6 @@ class PropertyMap { private: PropertyMap &operator=(const PropertyMap &) = delete; - PropertyMap &set(const std::string &key, const BoxedValue &val); - PROJ_OPAQUE_PRIVATE_DATA }; diff --git a/scripts/build_db.py b/scripts/build_db.py index 46bef8c8..4f09a659 100755 --- a/scripts/build_db.py +++ b/scripts/build_db.py @@ -520,7 +520,7 @@ def fill_alias(proj_db_cursor): def find_table(proj_db_cursor, code): - for table_name in ('helmert_transformation', 'grid_transformation', 'concatenated_operation'): + for table_name in ('helmert_transformation', 'grid_transformation', 'concatenated_operation', 'geodetic_crs', 'projected_crs', 'vertical_crs', 'compound_crs'): proj_db_cursor.execute('SELECT name FROM %s WHERE code = ?' % table_name, (code,)) row = proj_db_cursor.fetchone() if row is not None: @@ -545,6 +545,22 @@ def fill_supersession(proj_db_cursor): continue proj_db_cursor.execute("INSERT INTO supersession VALUES (?,'EPSG',?,?,'EPSG',?,'EPSG')", (superseded_table_name, code, replacement_table_name, superseded_by)) +def fill_deprecation(proj_db_cursor): + proj_db_cursor.execute("SELECT object_code, replaced_by FROM epsg.epsg_deprecation WHERE object_table_name = 'epsg_coordinatereferencesystem' AND object_code != replaced_by") + for row in proj_db_cursor.fetchall(): + code, replaced_by = row + proj_db_cursor.execute('SELECT 1 FROM crs_view WHERE code = ?', (code,)) + if proj_db_cursor.fetchone() is None: + print('Skipping deprecation of %d since it has not been imported' % code) + continue + + src_name, deprecated_table_name = find_table(proj_db_cursor, code) + dst_name, replacement_table_name = find_table(proj_db_cursor, replaced_by) + assert deprecated_table_name, row + assert replacement_table_name, row + assert deprecated_table_name == replacement_table_name + proj_db_cursor.execute("INSERT INTO deprecation VALUES (?,'EPSG',?,'EPSG',?,'EPSG')", (deprecated_table_name, code, replaced_by)) + def report_non_imported_operations(proj_db_cursor): proj_db_cursor.execute("SELECT coord_op_code, coord_op_type, coord_op_name, coord_op_method_code, coord_op_method_name, source_crs_code, target_crs_code, area_of_use_code, coord_op_accuracy, epsg_coordoperation.deprecated FROM epsg.epsg_coordoperation LEFT JOIN epsg.epsg_coordoperationmethod USING (coord_op_method_code) WHERE coord_op_code NOT IN (SELECT code FROM coordinate_operation_with_conversion_view)") rows = [] @@ -591,6 +607,7 @@ fill_other_transformation(proj_db_cursor) fill_concatenated_operation(proj_db_cursor) fill_alias(proj_db_cursor) fill_supersession(proj_db_cursor) +fill_deprecation(proj_db_cursor) non_imported_operations = report_non_imported_operations(proj_db_cursor) proj_db_cursor.close() diff --git a/src/c_api.cpp b/src/c_api.cpp index fed91750..9d66071b 100644 --- a/src/c_api.cpp +++ b/src/c_api.cpp @@ -522,6 +522,42 @@ PJ_OBJ *proj_obj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name, // --------------------------------------------------------------------------- +/** \brief Return GeodeticCRS that use the specified datum. + * + * @param ctx Context, or NULL for default context. + * @param crs_auth_name CRS authority name, or NULL. + * @param datum_auth_name Datum authority name (must not be NULL) + * @param datum_code Datum code (must not be NULL) + * @param crs_type "geographic 2D", "geographic 3D", "geocentric" or NULL + * @return a result set that must be unreferenced with + * proj_obj_list_unref(), or NULL in case of error. + */ +PJ_OBJ_LIST *proj_obj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx, + const char *crs_auth_name, + const char *datum_auth_name, + const char *datum_code, + const char *crs_type) { + assert(datum_auth_name); + assert(datum_code); + SANITIZE_CTX(ctx); + try { + auto factory = AuthorityFactory::create( + getDBcontext(ctx), crs_auth_name ? crs_auth_name : ""); + auto res = factory->createGeodeticCRSFromDatum( + datum_auth_name, datum_code, crs_type ? crs_type : ""); + std::vector<IdentifiedObjectNNPtr> objects; + for (const auto &obj : res) { + objects.push_back(obj); + } + return new PJ_OBJ_LIST(std::move(objects)); + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + } + return nullptr; +} + +// --------------------------------------------------------------------------- + /** \brief Drops a reference on an object. * * This method should be called one and exactly one for each function @@ -791,6 +827,36 @@ int proj_obj_is_deprecated(const PJ_OBJ *obj) { // --------------------------------------------------------------------------- +/** \brief Return a list of non-deprecated objects related to the passed one + * + * @param ctx Context, or NULL for default context. + * @param obj Object (of type CRS for now) for which non-deprecated objects + * must be searched. Must not be NULL + * @return a result set that must be unreferenced with + * proj_obj_list_unref(), or NULL in case of error. + */ +PJ_OBJ_LIST *proj_obj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ_OBJ *obj) { + assert(obj); + SANITIZE_CTX(ctx); + auto crs = dynamic_cast<const CRS *>(obj->obj.get()); + if (!crs) { + return nullptr; + } + try { + std::vector<IdentifiedObjectNNPtr> objects; + auto res = crs->getNonDeprecated(getDBcontext(ctx)); + for (const auto &resObj : res) { + objects.push_back(resObj); + } + return new PJ_OBJ_LIST(std::move(objects)); + } catch (const std::exception &e) { + proj_log_error(ctx, __FUNCTION__, e.what()); + } + return nullptr; +} + +// --------------------------------------------------------------------------- + /** \brief Return whether two objects are equivalent. * * @param obj Object (must not be NULL) @@ -1289,11 +1355,19 @@ PJ_OBJ *proj_obj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ_OBJ *base_crs, * * @param ctx PROJ context, or NULL for default context * @param crs Objet of type CRS (must not be NULL) + * @param options null-terminated list of options, or NULL. Currently + * supported options are: + * <ul> + * <li>ALLOW_INTERMEDIATE_CRS=YES/NO. Defaults to NO. When set to YES, + * intermediate CRS may be considered when computing the possible + * tranformations. Slower.</li> + * </ul> * @return Object that must be unreferenced with proj_obj_unref(), or NULL * in case of error. */ PJ_OBJ *proj_obj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, - const PJ_OBJ *crs) { + const PJ_OBJ *crs, + const char *const *options) { SANITIZE_CTX(ctx); assert(crs); auto l_crs = dynamic_cast<const CRS *>(crs->obj.get()); @@ -1303,8 +1377,20 @@ PJ_OBJ *proj_obj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, } auto dbContext = getDBcontextNoException(ctx, __FUNCTION__); try { - return PJ_OBJ::create( - l_crs->createBoundCRSToWGS84IfPossible(dbContext)); + bool allowIntermediateCRS = false; + for (auto iter = options; iter && iter[0]; ++iter) { + const char *value; + if ((value = getOptionValue(*iter, "ALLOW_INTERMEDIATE_CRS="))) { + allowIntermediateCRS = ci_equal(value, "YES"); + } else { + std::string msg("Unknown option :"); + msg += *iter; + proj_log_error(ctx, __FUNCTION__, msg.c_str()); + return nullptr; + } + } + return PJ_OBJ::create(l_crs->createBoundCRSToWGS84IfPossible( + dbContext, allowIntermediateCRS)); } catch (const std::exception &e) { proj_log_error(ctx, __FUNCTION__, e.what()); return nullptr; @@ -5354,9 +5440,17 @@ struct PJ_OPERATION_FACTORY_CONTEXT { * The returned object must be unreferenced with * proj_operation_factory_context_unref() after use. * + * If authority is NULL or the empty string, then coordinate + * operations from any authority will be searched, with the restrictions set + * in the authority_to_authority_preference database table. + * If authority is set to "any", then coordinate + * operations from any authority will be searched + * If authority is a non-empty string different of "any", + * then coordinate operatiosn will be searched only in that authority namespace. + * * @param ctx Context, or NULL for default context. * @param authority Name of authority to which to restrict the search of - * canidate operations. Or NULL to allow any authority. + * candidate operations. * @return Object that must be unreferenced with * proj_operation_factory_context_unref(), or NULL in * case of error. diff --git a/src/common.cpp b/src/common.cpp index 94bc8678..2a9d17c7 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -650,23 +650,20 @@ bool IdentifiedObject::isDeprecated() PROJ_CONST_DEFN { void IdentifiedObject::Private::setName( const PropertyMap &properties) // throw(InvalidValueTypeException) { - auto oIter = properties.find(NAME_KEY); - if (oIter == properties.end()) { + const auto pVal = properties.get(NAME_KEY); + if (!pVal) { return; } - if (auto genVal = - util::nn_dynamic_pointer_cast<BoxedValue>(oIter->second)) { + if (const auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) { if (genVal->type() == BoxedValue::Type::STRING) { - name = Identifier::create( - std::string(), PropertyMap().set(Identifier::DESCRIPTION_KEY, - genVal->stringValue())); + name = Identifier::createFromDescription(genVal->stringValue()); } else { throw InvalidValueTypeException("Invalid value type for " + NAME_KEY); } } else { if (auto identifier = - util::nn_dynamic_pointer_cast<Identifier>(oIter->second)) { + util::nn_dynamic_pointer_cast<Identifier>(*pVal)) { name = NN_NO_CHECK(identifier); } else { throw InvalidValueTypeException("Invalid value type for " + @@ -680,23 +677,21 @@ void IdentifiedObject::Private::setName( void IdentifiedObject::Private::setIdentifiers( const PropertyMap &properties) // throw(InvalidValueTypeException) { - auto oIter = properties.find(IDENTIFIERS_KEY); - if (oIter == properties.end()) { + auto pVal = properties.get(IDENTIFIERS_KEY); + if (!pVal) { - oIter = properties.find(Identifier::CODE_KEY); - if (oIter != properties.end()) { + pVal = properties.get(Identifier::CODE_KEY); + if (pVal) { identifiers.push_back( Identifier::create(std::string(), properties)); } return; } - if (auto identifier = - util::nn_dynamic_pointer_cast<Identifier>(oIter->second)) { + if (auto identifier = util::nn_dynamic_pointer_cast<Identifier>(*pVal)) { identifiers.clear(); identifiers.push_back(NN_NO_CHECK(identifier)); } else { - if (auto array = util::nn_dynamic_pointer_cast<ArrayOfBaseObject>( - oIter->second)) { + if (auto array = dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) { identifiers.clear(); for (const auto &val : *array) { identifier = util::nn_dynamic_pointer_cast<Identifier>(val); @@ -719,17 +714,16 @@ void IdentifiedObject::Private::setIdentifiers( void IdentifiedObject::Private::setAliases( const PropertyMap &properties) // throw(InvalidValueTypeException) { - auto oIter = properties.find(ALIAS_KEY); - if (oIter == properties.end()) { + const auto pVal = properties.get(ALIAS_KEY); + if (!pVal) { return; } - if (auto l_name = - util::nn_dynamic_pointer_cast<GenericName>(oIter->second)) { + if (auto l_name = util::nn_dynamic_pointer_cast<GenericName>(*pVal)) { aliases.clear(); aliases.push_back(NN_NO_CHECK(l_name)); } else { - if (auto array = util::nn_dynamic_pointer_cast<ArrayOfBaseObject>( - oIter->second)) { + if (const auto array = + dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) { aliases.clear(); for (const auto &val : *array) { l_name = util::nn_dynamic_pointer_cast<GenericName>(val); @@ -737,7 +731,7 @@ void IdentifiedObject::Private::setAliases( aliases.push_back(NN_NO_CHECK(l_name)); } else { if (auto genVal = - util::nn_dynamic_pointer_cast<BoxedValue>(val)) { + dynamic_cast<const BoxedValue *>(val.get())) { if (genVal->type() == BoxedValue::Type::STRING) { aliases.push_back(NameFactory::createLocalName( nullptr, genVal->stringValue())); @@ -777,10 +771,10 @@ void IdentifiedObject::setProperties( properties.getStringValue(REMARKS_KEY, d->remarks); { - auto oIter = properties.find(DEPRECATED_KEY); - if (oIter != properties.end()) { - if (auto genVal = - util::nn_dynamic_pointer_cast<BoxedValue>(oIter->second)) { + const auto pVal = properties.get(DEPRECATED_KEY); + if (pVal) { + if (const auto genVal = + dynamic_cast<const BoxedValue *>(pVal->get())) { if (genVal->type() == BoxedValue::Type::BOOLEAN) { d->isDeprecated = genVal->booleanValue(); } else { @@ -930,8 +924,8 @@ void ObjectDomain::_exportToWKT(WKTFormatter *formatter) const { formatter->endNode(); } if (d->domainOfValidity_->geographicElements().size() == 1) { - auto bbox = util::nn_dynamic_pointer_cast<GeographicBoundingBox>( - d->domainOfValidity_->geographicElements()[0]); + const auto bbox = dynamic_cast<const GeographicBoundingBox *>( + d->domainOfValidity_->geographicElements()[0].get()); if (bbox) { formatter->startNode(WKTConstants::BBOX, false); formatter->add(bbox->southBoundLatitude()); @@ -1029,19 +1023,13 @@ void ObjectUsage::setProperties( IdentifiedObject::setProperties(properties); optional<std::string> scope; - { - std::string temp; - if (properties.getStringValue(SCOPE_KEY, temp)) { - scope = temp; - } - } + properties.getStringValue(SCOPE_KEY, scope); ExtentPtr domainOfValidity; { - auto oIter = properties.find(DOMAIN_OF_VALIDITY_KEY); - if (oIter != properties.end()) { - domainOfValidity = - util::nn_dynamic_pointer_cast<Extent>(oIter->second); + const auto pVal = properties.get(DOMAIN_OF_VALIDITY_KEY); + if (pVal) { + domainOfValidity = util::nn_dynamic_pointer_cast<Extent>(*pVal); if (!domainOfValidity) { throw InvalidValueTypeException("Invalid value type for " + DOMAIN_OF_VALIDITY_KEY); @@ -1054,14 +1042,14 @@ void ObjectUsage::setProperties( } { - auto oIter = properties.find(OBJECT_DOMAIN_KEY); - if (oIter != properties.end()) { - if (auto objectDomain = util::nn_dynamic_pointer_cast<ObjectDomain>( - oIter->second)) { + const auto pVal = properties.get(OBJECT_DOMAIN_KEY); + if (pVal) { + if (auto objectDomain = + util::nn_dynamic_pointer_cast<ObjectDomain>(*pVal)) { d->domains_.emplace_back(NN_NO_CHECK(objectDomain)); - } else if (auto array = - util::nn_dynamic_pointer_cast<ArrayOfBaseObject>( - oIter->second)) { + } else if (const auto array = + dynamic_cast<const ArrayOfBaseObject *>( + pVal->get())) { for (const auto &val : *array) { objectDomain = util::nn_dynamic_pointer_cast<ObjectDomain>(val); diff --git a/src/coordinateoperation.cpp b/src/coordinateoperation.cpp index 04f9bc9a..8f75864e 100644 --- a/src/coordinateoperation.cpp +++ b/src/coordinateoperation.cpp @@ -2055,8 +2055,7 @@ static util::PropertyMap createMethodMapNameEPSGCode(int code) { static util::PropertyMap getUTMConversionProperty(const util::PropertyMap &properties, int zone, bool north) { - if (properties.find(common::IdentifiedObject::NAME_KEY) == - properties.end()) { + if (!properties.get(common::IdentifiedObject::NAME_KEY)) { std::string conversionName("UTM zone "); conversionName += toString(zone); conversionName += (north ? 'N' : 'S'); @@ -2073,8 +2072,7 @@ getUTMConversionProperty(const util::PropertyMap &properties, int zone, static util::PropertyMap addDefaultNameIfNeeded(const util::PropertyMap &properties, const std::string &defaultName) { - if (properties.find(common::IdentifiedObject::NAME_KEY) == - properties.end()) { + if (!properties.get(common::IdentifiedObject::NAME_KEY)) { return util::PropertyMap(properties) .set(common::IdentifiedObject::NAME_KEY, defaultName); } else { @@ -5177,7 +5175,9 @@ void Conversion::_exportToPROJString( double latitudePseudoStandardParallel = parameterValueNumeric( EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL, common::UnitOfMeasure::DEGREE); - if (std::fabs(colatitude - 30.28813972222222) > 1e-8) { + // 30deg 17' 17.30311'' = 30.28813975277777776 + // 30deg 17' 17.303'' = 30.288139722222223 as used in GDAL WKT1 + if (std::fabs(colatitude - 30.2881397) > 1e-7) { throw io::FormattingException( std::string("Unsupported value for ") + EPSG_NAME_PARAMETER_COLATITUDE_CONE_AXIS); @@ -8937,6 +8937,14 @@ CoordinateOperationContext::getIntermediateCRS() const { * If a non null authorityFactory is provided, the resulting context should * not be used simultaneously by more than one thread. * + * If authorityFactory->getAuthority() is the empty string, then coordinate + * operations from any authority will be searched, with the restrictions set + * in the authority_to_authority_preference database table. + * If authorityFactory->getAuthority() is set to "any", then coordinate + * operations from any authority will be searched + * If authorityFactory->getAuthority() is a non-empty string different of "any", + * then coordinate operatiosn will be searched only in that authority namespace. + * * @param authorityFactory Authority factory, or null if no database lookup * is allowed. * Use io::authorityFactory::create(context, std::string()) to allow all @@ -9569,6 +9577,10 @@ struct FilterAndSort { // cppcheck-suppress functionStatic void removeDuplicateOps() { + if (res.size() <= 1) { + return; + } + // When going from EPSG:4807 (NTF Paris) to EPSG:4171 (RGC93), we get // EPSG:7811, NTF (Paris) to RGF93 (2), 1 m // and unknown id, NTF (Paris) to NTF (1) + Inverse of RGF93 to NTF (2), @@ -9663,6 +9675,8 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, const CoordinateOperationContextNNPtr &context) { const auto &authFactory = context->getAuthorityFactory(); assert(authFactory); + const auto &authFactoryName = authFactory->getAuthority(); + for (const auto &idSrc : sourceCRS->identifiers()) { const auto &srcAuthName = *(idSrc->codeSpace()); const auto &srcCode = idSrc->code(); @@ -9671,17 +9685,39 @@ findOpsInRegistryDirect(const crs::CRSNNPtr &sourceCRS, const auto &targetAuthName = *(idTarget->codeSpace()); const auto &targetCode = idTarget->code(); if (!targetAuthName.empty()) { - auto res = - authFactory->createFromCoordinateReferenceSystemCodes( - srcAuthName, srcCode, targetAuthName, targetCode, - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == - CoordinateOperationContext:: - GridAvailabilityUse:: - DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded()); - if (!res.empty()) { - return res; + std::vector<std::string> authorities; + if (authFactoryName == "any") { + authorities.emplace_back(); + } + if (authFactoryName.empty()) { + authorities = authFactory->databaseContext() + ->getAllowedAuthorities( + srcAuthName, targetAuthName); + if (authorities.empty()) { + authorities.emplace_back(); + } + } else { + authorities.emplace_back(authFactoryName); + } + for (const auto &authority : authorities) { + const auto tmpAuthFactory = + io::AuthorityFactory::create( + authFactory->databaseContext(), + authority == "any" ? std::string() : authority); + auto res = + tmpAuthFactory + ->createFromCoordinateReferenceSystemCodes( + srcAuthName, srcCode, targetAuthName, + targetCode, + context->getUsePROJAlternativeGridNames(), + context->getGridAvailabilityUse() == + CoordinateOperationContext:: + GridAvailabilityUse:: + DISCARD_OPERATION_IF_MISSING_GRID, + context->getDiscardSuperseded()); + if (!res.empty()) { + return res; + } } } } @@ -9706,6 +9742,8 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate( const auto &authFactory = context->getAuthorityFactory(); assert(authFactory); + const auto &authFactoryName = authFactory->getAuthority(); + for (const auto &idSrc : sourceCRS->identifiers()) { const auto &srcAuthName = *(idSrc->codeSpace()); const auto &srcCode = idSrc->code(); @@ -9714,16 +9752,40 @@ static std::vector<CoordinateOperationNNPtr> findsOpsInRegistryWithIntermediate( const auto &targetAuthName = *(idTarget->codeSpace()); const auto &targetCode = idTarget->code(); if (!targetAuthName.empty()) { - auto res = authFactory->createFromCRSCodesWithIntermediates( - srcAuthName, srcCode, targetAuthName, targetCode, - context->getUsePROJAlternativeGridNames(), - context->getGridAvailabilityUse() == - CoordinateOperationContext::GridAvailabilityUse:: - DISCARD_OPERATION_IF_MISSING_GRID, - context->getDiscardSuperseded(), - context->getIntermediateCRS()); - if (!res.empty()) { - return res; + std::vector<std::string> authorities; + if (authFactoryName == "any") { + authorities.emplace_back(); + } + if (authFactoryName.empty()) { + authorities = authFactory->databaseContext() + ->getAllowedAuthorities( + srcAuthName, targetAuthName); + if (authorities.empty()) { + authorities.emplace_back(); + } + } else { + authorities.emplace_back(authFactoryName); + } + for (const auto &authority : authorities) { + const auto tmpAuthFactory = + io::AuthorityFactory::create( + authFactory->databaseContext(), + authority == "any" ? std::string() : authority); + + auto res = + tmpAuthFactory->createFromCRSCodesWithIntermediates( + srcAuthName, srcCode, targetAuthName, + targetCode, + context->getUsePROJAlternativeGridNames(), + context->getGridAvailabilityUse() == + CoordinateOperationContext:: + GridAvailabilityUse:: + DISCARD_OPERATION_IF_MISSING_GRID, + context->getDiscardSuperseded(), + context->getIntermediateCRS()); + if (!res.empty()) { + return res; + } } } } diff --git a/src/coordinatesystem.cpp b/src/coordinatesystem.cpp index f1220878..2305e6c4 100644 --- a/src/coordinatesystem.cpp +++ b/src/coordinatesystem.cpp @@ -325,7 +325,13 @@ void CoordinateSystemAxis::_exportToWKT(io::WKTFormatter *formatter, int order, axisDesignation = tolower(axisName.substr(0, 1)) + axisName.substr(1); } else { - axisDesignation = axisName; + if (axisName == "Geodetic latitude") { + axisDesignation = "Latitude"; + } else if (axisName == "Geodetic longitude") { + axisDesignation = "Longitude"; + } else { + axisDesignation = axisName; + } } } diff --git a/src/crs.cpp b/src/crs.cpp index 546cfb0a..572fae5d 100644 --- a/src/crs.cpp +++ b/src/crs.cpp @@ -92,10 +92,10 @@ struct CRS::Private { bool implicitCS_ = false; void setImplicitCS(const util::PropertyMap &properties) { - auto oIter = properties.find("IMPLICIT_CS"); - if (oIter != properties.end()) { - if (auto genVal = util::nn_dynamic_pointer_cast<util::BoxedValue>( - oIter->second)) { + const auto pVal = properties.get("IMPLICIT_CS"); + if (pVal) { + if (const auto genVal = + dynamic_cast<const util::BoxedValue *>(pVal->get())) { if (genVal->type() == util::BoxedValue::Type::BOOLEAN && genVal->booleanValue()) { implicitCS_ = true; @@ -375,8 +375,9 @@ VerticalCRSPtr CRS::extractVerticalCRS() const { * * @return a CRS. */ -CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( - const io::DatabaseContextPtr &dbContext) const { +CRSNNPtr +CRS::createBoundCRSToWGS84IfPossible(const io::DatabaseContextPtr &dbContext, + bool allowIntermediateCRS) const { auto thisAsCRS = NN_NO_CHECK( std::static_pointer_cast<CRS>(shared_from_this().as_nullable())); auto boundCRS = util::nn_dynamic_pointer_cast<BoundCRS>(thisAsCRS); @@ -409,74 +410,98 @@ CRSNNPtr CRS::createBoundCRSToWGS84IfPossible( } else { geodCRS = geogCRS; } - auto l_domains = domains(); + + if (!dbContext) { + return thisAsCRS; + } + + const auto &l_domains = domains(); metadata::ExtentPtr extent; if (!l_domains.empty()) { extent = l_domains[0]->domainOfValidity(); } - try { - auto authFactory = dbContext - ? io::AuthorityFactory::create( - NN_NO_CHECK(dbContext), std::string()) - .as_nullable() - : nullptr; - auto ctxt = operation::CoordinateOperationContext::create(authFactory, - extent, 0.0); - // ctxt->setSpatialCriterion( - // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); - auto list = - operation::CoordinateOperationFactory::create()->createOperations( - NN_NO_CHECK(geodCRS), hubCRS, ctxt); - for (const auto &op : list) { - auto transf = - util::nn_dynamic_pointer_cast<operation::Transformation>(op); - if (transf) { - try { - transf->getTOWGS84Parameters(); - } catch (const std::exception &) { - continue; - } - return util::nn_static_pointer_cast<CRS>( - BoundCRS::create(thisAsCRS, hubCRS, NN_NO_CHECK(transf))); - } else { - auto concatenated = - dynamic_cast<const operation::ConcatenatedOperation *>( - op.get()); - if (concatenated) { - // Case for EPSG:4807 / "NTF (Paris)" that is made of a - // longitude rotation followed by a Helmert - // The prime meridian shift will be accounted elsewhere - const auto &subops = concatenated->operations(); - if (subops.size() == 2) { - auto firstOpIsTransformation = - dynamic_cast<const operation::Transformation *>( - subops[0].get()); - auto firstOpIsConversion = - dynamic_cast<const operation::Conversion *>( - subops[0].get()); - if ((firstOpIsTransformation && - firstOpIsTransformation->isLongitudeRotation()) || - (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) && - firstOpIsConversion)) { - transf = util::nn_dynamic_pointer_cast< - operation::Transformation>(subops[1]); - if (transf) { - try { - transf->getTOWGS84Parameters(); - } catch (const std::exception &) { - continue; + std::string crs_authority; + const auto &l_identifiers = identifiers(); + // If the object has an authority, restrict the transformations to + // come from that codespace too. This avoids for example EPSG:4269 + // (NAD83) to use a (dubious) ESRI transformation. + if (!l_identifiers.empty()) { + crs_authority = *(l_identifiers[0]->codeSpace()); + } + + auto authorities = dbContext->getAllowedAuthorities(crs_authority, "EPSG"); + if (authorities.empty()) { + authorities.emplace_back(); + } + for (const auto &authority : authorities) { + try { + + auto authFactory = io::AuthorityFactory::create( + NN_NO_CHECK(dbContext), + authority == "any" ? std::string() : authority); + auto ctxt = operation::CoordinateOperationContext::create( + authFactory, extent, 0.0); + ctxt->setAllowUseIntermediateCRS(allowIntermediateCRS); + // ctxt->setSpatialCriterion( + // operation::CoordinateOperationContext::SpatialCriterion::PARTIAL_INTERSECTION); + auto list = + operation::CoordinateOperationFactory::create() + ->createOperations(NN_NO_CHECK(geodCRS), hubCRS, ctxt); + for (const auto &op : list) { + auto transf = + util::nn_dynamic_pointer_cast<operation::Transformation>( + op); + if (transf && !starts_with(transf->nameStr(), "Null geo")) { + try { + transf->getTOWGS84Parameters(); + } catch (const std::exception &) { + continue; + } + return util::nn_static_pointer_cast<CRS>(BoundCRS::create( + thisAsCRS, hubCRS, NN_NO_CHECK(transf))); + } else { + auto concatenated = + dynamic_cast<const operation::ConcatenatedOperation *>( + op.get()); + if (concatenated) { + // Case for EPSG:4807 / "NTF (Paris)" that is made of a + // longitude rotation followed by a Helmert + // The prime meridian shift will be accounted elsewhere + const auto &subops = concatenated->operations(); + if (subops.size() == 2) { + auto firstOpIsTransformation = + dynamic_cast<const operation::Transformation *>( + subops[0].get()); + auto firstOpIsConversion = + dynamic_cast<const operation::Conversion *>( + subops[0].get()); + if ((firstOpIsTransformation && + firstOpIsTransformation + ->isLongitudeRotation()) || + (dynamic_cast<DerivedCRS *>(thisAsCRS.get()) && + firstOpIsConversion)) { + transf = util::nn_dynamic_pointer_cast< + operation::Transformation>(subops[1]); + if (transf && + !starts_with(transf->nameStr(), + "Null geo")) { + try { + transf->getTOWGS84Parameters(); + } catch (const std::exception &) { + continue; + } + return util::nn_static_pointer_cast<CRS>( + BoundCRS::create(thisAsCRS, hubCRS, + NN_NO_CHECK(transf))); } - return util::nn_static_pointer_cast<CRS>( - BoundCRS::create(thisAsCRS, hubCRS, - NN_NO_CHECK(transf))); } } } } } + } catch (const std::exception &) { } - } catch (const std::exception &) { } return thisAsCRS; } @@ -580,6 +605,40 @@ CRS::identify(const io::AuthorityFactoryPtr &authorityFactory) const { // --------------------------------------------------------------------------- +/** \brief Return CRSs that are non-deprecated substitutes for the current CRS. + */ +std::list<CRSNNPtr> +CRS::getNonDeprecated(const io::DatabaseContextNNPtr &dbContext) const { + std::list<CRSNNPtr> res; + const auto &l_identifiers = identifiers(); + if (l_identifiers.empty()) { + return res; + } + const char *tableName = nullptr; + if (dynamic_cast<const GeodeticCRS *>(this)) { + tableName = "geodetic_crs"; + } else if (dynamic_cast<const ProjectedCRS *>(this)) { + tableName = "projected_crs"; + } else if (dynamic_cast<const VerticalCRS *>(this)) { + tableName = "vertical_crs"; + } else if (dynamic_cast<const CompoundCRS *>(this)) { + tableName = "compound_crs"; + } + if (!tableName) { + return res; + } + const auto &id = l_identifiers[0]; + auto tmpRes = + dbContext->getNonDeprecated(tableName, *(id->codeSpace()), id->code()); + for (const auto &pair : tmpRes) { + res.emplace_back(io::AuthorityFactory::create(dbContext, pair.first) + ->createCoordinateReferenceSystem(pair.second)); + } + return res; +} + +// --------------------------------------------------------------------------- + //! @cond Doxygen_Suppress std::list<std::pair<CRSNNPtr, int>> @@ -3167,8 +3226,7 @@ CompoundCRSNNPtr CompoundCRS::create(const util::PropertyMap &properties, auto compoundCRS(CompoundCRS::nn_make_shared<CompoundCRS>(components)); compoundCRS->assignSelf(compoundCRS); compoundCRS->setProperties(properties); - if (properties.find(common::IdentifiedObject::NAME_KEY) == - properties.end()) { + if (!properties.get(common::IdentifiedObject::NAME_KEY)) { std::string name; for (const auto &crs : components) { if (!name.empty()) { @@ -4459,10 +4517,10 @@ EngineeringCRS::create(const util::PropertyMap &properties, crs->assignSelf(crs); crs->setProperties(properties); - auto oIter = properties.find("FORCE_OUTPUT_CS"); - if (oIter != properties.end()) { - if (auto genVal = util::nn_dynamic_pointer_cast<util::BoxedValue>( - oIter->second)) { + const auto pVal = properties.get("FORCE_OUTPUT_CS"); + if (pVal) { + if (const auto genVal = + dynamic_cast<const util::BoxedValue *>(pVal->get())) { if (genVal->type() == util::BoxedValue::Type::BOOLEAN && genVal->booleanValue()) { crs->d->forceOutputCS_ = true; diff --git a/src/factory.cpp b/src/factory.cpp index e24cee58..39679082 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -108,7 +108,8 @@ struct SQLValues { // --------------------------------------------------------------------------- using SQLRow = std::vector<std::string>; -using SQLResultSet = std::vector<SQLRow>; +using SQLResultSet = std::list<SQLRow>; +using ListOfParams = std::list<SQLValues>; // --------------------------------------------------------------------------- @@ -124,9 +125,8 @@ struct DatabaseContext::Private { PJ_CONTEXT *pjCtxt() const { return pjCtxt_; } void setPjCtxt(PJ_CONTEXT *ctxt) { pjCtxt_ = ctxt; } - SQLResultSet - run(const std::string &sql, - const std::vector<SQLValues> ¶meters = std::vector<SQLValues>()); + SQLResultSet run(const std::string &sql, + const ListOfParams ¶meters = ListOfParams()); std::vector<std::string> getDatabaseStructure(); @@ -160,6 +160,48 @@ struct DatabaseContext::Private { return mapCanonicalizeGRFName_; } + // cppcheck-suppress functionStatic + common::UnitOfMeasurePtr getUOMFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const common::UnitOfMeasureNNPtr &uom); + + // cppcheck-suppress functionStatic + crs::CRSPtr getCRSFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const crs::CRSNNPtr &crs); + + datum::GeodeticReferenceFramePtr + // cppcheck-suppress functionStatic + getGeodeticDatumFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, + const datum::GeodeticReferenceFrameNNPtr &datum); + + datum::PrimeMeridianPtr + // cppcheck-suppress functionStatic + getPrimeMeridianFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const datum::PrimeMeridianNNPtr &pm); + + // cppcheck-suppress functionStatic + cs::CoordinateSystemPtr + getCoordinateSystemFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const cs::CoordinateSystemNNPtr &cs); + + // cppcheck-suppress functionStatic + metadata::ExtentPtr getExtentFromCache(const std::string &code); + // cppcheck-suppress functionStatic + void cache(const std::string &code, const metadata::ExtentNNPtr &extent); + + // cppcheck-suppress functionStatic + bool getCRSToCRSCoordOpFromCache( + const std::string &code, + std::vector<operation::CoordinateOperationNNPtr> &list); + // cppcheck-suppress functionStatic + void cache(const std::string &code, + const std::vector<operation::CoordinateOperationNNPtr> &list); + private: friend class DatabaseContext; @@ -173,6 +215,25 @@ struct DatabaseContext::Private { std::string lastMetadataValue_{}; std::map<std::string, std::list<SQLRow>> mapCanonicalizeGRFName_{}; + using LRUCacheOfObjects = lru11::Cache<std::string, util::BaseObjectPtr>; + + static constexpr size_t CACHE_SIZE = 128; + LRUCacheOfObjects cacheUOM_{CACHE_SIZE}; + LRUCacheOfObjects cacheCRS_{CACHE_SIZE}; + LRUCacheOfObjects cacheGeodeticDatum_{CACHE_SIZE}; + LRUCacheOfObjects cachePrimeMeridian_{CACHE_SIZE}; + LRUCacheOfObjects cacheCS_{CACHE_SIZE}; + LRUCacheOfObjects cacheExtent_{CACHE_SIZE}; + lru11::Cache<std::string, std::vector<operation::CoordinateOperationNNPtr>> + cacheCRSToCrsCoordOp_{CACHE_SIZE}; + + static void insertIntoCache(LRUCacheOfObjects &cache, + const std::string &code, + const util::BaseObjectPtr &obj); + + static void getFromCache(LRUCacheOfObjects &cache, const std::string &code, + util::BaseObjectPtr &obj); + void closeDB(); // cppcheck-suppress functionStatic @@ -240,6 +301,133 @@ void DatabaseContext::Private::closeDB() { // --------------------------------------------------------------------------- +void DatabaseContext::Private::insertIntoCache(LRUCacheOfObjects &cache, + const std::string &code, + const util::BaseObjectPtr &obj) { + cache.insert(code, obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::getFromCache(LRUCacheOfObjects &cache, + const std::string &code, + util::BaseObjectPtr &obj) { + cache.tryGet(code, obj); +} + +// --------------------------------------------------------------------------- + +bool DatabaseContext::Private::getCRSToCRSCoordOpFromCache( + const std::string &code, + std::vector<operation::CoordinateOperationNNPtr> &list) { + return cacheCRSToCrsCoordOp_.tryGet(code, list); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache( + const std::string &code, + const std::vector<operation::CoordinateOperationNNPtr> &list) { + cacheCRSToCrsCoordOp_.insert(code, list); +} + +// --------------------------------------------------------------------------- + +crs::CRSPtr DatabaseContext::Private::getCRSFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheCRS_, code, obj); + return std::static_pointer_cast<crs::CRS>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const crs::CRSNNPtr &crs) { + insertIntoCache(cacheCRS_, code, crs.as_nullable()); +} + +// --------------------------------------------------------------------------- + +common::UnitOfMeasurePtr +DatabaseContext::Private::getUOMFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheUOM_, code, obj); + return std::static_pointer_cast<common::UnitOfMeasure>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const common::UnitOfMeasureNNPtr &uom) { + insertIntoCache(cacheUOM_, code, uom.as_nullable()); +} + +// --------------------------------------------------------------------------- + +datum::GeodeticReferenceFramePtr +DatabaseContext::Private::getGeodeticDatumFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheGeodeticDatum_, code, obj); + return std::static_pointer_cast<datum::GeodeticReferenceFrame>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache( + const std::string &code, const datum::GeodeticReferenceFrameNNPtr &datum) { + insertIntoCache(cacheGeodeticDatum_, code, datum.as_nullable()); +} + +// --------------------------------------------------------------------------- + +datum::PrimeMeridianPtr +DatabaseContext::Private::getPrimeMeridianFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cachePrimeMeridian_, code, obj); + return std::static_pointer_cast<datum::PrimeMeridian>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const datum::PrimeMeridianNNPtr &pm) { + insertIntoCache(cachePrimeMeridian_, code, pm.as_nullable()); +} + +// --------------------------------------------------------------------------- + +cs::CoordinateSystemPtr DatabaseContext::Private::getCoordinateSystemFromCache( + const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheCS_, code, obj); + return std::static_pointer_cast<cs::CoordinateSystem>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const cs::CoordinateSystemNNPtr &cs) { + insertIntoCache(cacheCS_, code, cs.as_nullable()); +} + +// --------------------------------------------------------------------------- + +metadata::ExtentPtr +DatabaseContext::Private::getExtentFromCache(const std::string &code) { + util::BaseObjectPtr obj; + getFromCache(cacheExtent_, code, obj); + return std::static_pointer_cast<metadata::Extent>(obj); +} + +// --------------------------------------------------------------------------- + +void DatabaseContext::Private::cache(const std::string &code, + const metadata::ExtentNNPtr &extent) { + insertIntoCache(cacheExtent_, code, extent.as_nullable()); +} + +// --------------------------------------------------------------------------- + #ifdef ENABLE_CUSTOM_LOCKLESS_VFS typedef int (*ClosePtr)(sqlite3_file *); @@ -564,9 +752,8 @@ void DatabaseContext::Private::registerFunctions() { // --------------------------------------------------------------------------- -SQLResultSet -DatabaseContext::Private::run(const std::string &sql, - const std::vector<SQLValues> ¶meters) { +SQLResultSet DatabaseContext::Private::run(const std::string &sql, + const ListOfParams ¶meters) { sqlite3_stmt *stmt = nullptr; auto iter = mapSqlToStatement_.find(sql); @@ -603,13 +790,15 @@ DatabaseContext::Private::run(const std::string &sql, while (true) { int ret = sqlite3_step(stmt); if (ret == SQLITE_ROW) { - SQLRow row; + SQLRow row(column_count); for (int i = 0; i < column_count; i++) { const char *txt = reinterpret_cast<const char *>( sqlite3_column_text(stmt, i)); - row.emplace_back(txt ? txt : std::string()); + if (txt) { + row[i] = txt; + } } - result.emplace_back(row); + result.emplace_back(std::move(row)); } else if (ret == SQLITE_DONE) { break; } else { @@ -722,7 +911,7 @@ const char *DatabaseContext::getMetadata(const char *key) const { if (res.empty()) { return nullptr; } - d->lastMetadataValue_ = res[0][0]; + d->lastMetadataValue_ = res.front()[0]; return d->lastMetadataValue_.c_str(); } @@ -761,9 +950,10 @@ bool DatabaseContext::lookForGridAlternative(const std::string &officialName, if (res.empty()) { return false; } - projFilename = res[0][0]; - projFormat = res[0][1]; - inverse = res[0][2] == "1"; + const auto &row = res.front(); + projFilename = row[0]; + projFormat = row[1]; + inverse = row[2] == "1"; return true; } @@ -807,10 +997,11 @@ bool DatabaseContext::lookForGridInfo(const std::string &projFilename, if (res.empty()) { return false; } - packageName = std::move(res[0][0]); - url = res[0][1].empty() ? std::move(res[0][2]) : std::move(res[0][1]); - openLicense = (res[0][3].empty() ? res[0][4] : res[0][3]) == "1"; - directDownload = (res[0][5].empty() ? res[0][6] : res[0][5]) == "1"; + const auto &row = res.front(); + packageName = std::move(row[0]); + url = row[1].empty() ? std::move(row[2]) : std::move(row[1]); + openLicense = (row[3].empty() ? row[4] : row[3]) == "1"; + directDownload = (row[5].empty() ? row[6] : row[5]) == "1"; return true; } @@ -848,13 +1039,14 @@ DatabaseContext::getAliasFromOfficialName(const std::string &officialName, if (res.empty()) { return std::string(); } + const auto &row = res.front(); res = d->run("SELECT alt_name FROM alias_name WHERE table_name = ? AND " "auth_name = ? AND code = ? AND source = ?", - {tableName, res[0][0], res[0][1], source}); + {tableName, row[0], row[1], source}); if (res.empty()) { return std::string(); } - return res[0][0]; + return res.front()[0]; } // --------------------------------------------------------------------------- @@ -877,7 +1069,77 @@ std::string DatabaseContext::getTextDefinition(const std::string &tableName, if (res.empty()) { return std::string(); } - return res[0][0]; + return res.front()[0]; +} + +// --------------------------------------------------------------------------- + +/** \brief Return the allowed authorities when researching transformations + * between different authorities. + * + * @throw FactoryException + */ +std::vector<std::string> DatabaseContext::getAllowedAuthorities( + const std::string &sourceAuthName, + const std::string &targetAuthName) const { + auto res = d->run( + "SELECT allowed_authorities FROM authority_to_authority_preference " + "WHERE source_auth_name = ? AND target_auth_name = ?", + {sourceAuthName, targetAuthName}); + if (res.empty()) { + res = d->run( + "SELECT allowed_authorities FROM authority_to_authority_preference " + "WHERE source_auth_name = ? AND target_auth_name = 'any'", + {sourceAuthName}); + } + if (res.empty()) { + res = d->run( + "SELECT allowed_authorities FROM authority_to_authority_preference " + "WHERE source_auth_name = 'any' AND target_auth_name = ?", + {targetAuthName}); + } + if (res.empty()) { + res = d->run( + "SELECT allowed_authorities FROM authority_to_authority_preference " + "WHERE source_auth_name = 'any' AND target_auth_name = 'any'", + {}); + } + if (res.empty()) { + return std::vector<std::string>(); + } + return split(res.front()[0], ','); +} + +// --------------------------------------------------------------------------- + +std::list<std::pair<std::string, std::string>> +DatabaseContext::getNonDeprecated(const std::string &tableName, + const std::string &authName, + const std::string &code) const { + auto sqlRes = + d->run("SELECT replacement_auth_name, replacement_code, source " + "FROM deprecation " + "WHERE table_name = ? AND deprecated_auth_name = ? " + "AND deprecated_code = ?", + {tableName, authName, code}); + std::list<std::pair<std::string, std::string>> res; + for (const auto &row : sqlRes) { + const auto &source = row[2]; + if (source == "PROJ") { + const auto &replacement_auth_name = row[0]; + const auto &replacement_code = row[1]; + res.emplace_back(replacement_auth_name, replacement_code); + } + } + if (!res.empty()) { + return res; + } + for (const auto &row : sqlRes) { + const auto &replacement_auth_name = row[0]; + const auto &replacement_code = row[1]; + res.emplace_back(replacement_auth_name, replacement_code); + } + return res; } //! @endcond @@ -904,24 +1166,12 @@ struct AuthorityFactory::Private { // cppcheck-suppress functionStatic AuthorityFactoryPtr getSharedFromThis() { return thisFactory_.lock(); } - AuthorityFactoryNNPtr createFactory(const std::string &auth_name); - - // cppcheck-suppress functionStatic - common::UnitOfMeasurePtr getUOMFromCache(const std::string &code); - // cppcheck-suppress functionStatic - void cache(const std::string &code, const common::UnitOfMeasureNNPtr &uom); - - // cppcheck-suppress functionStatic - crs::CRSPtr getCRSFromCache(const std::string &code); - // cppcheck-suppress functionStatic - void cache(const std::string &code, const crs::CRSNNPtr &crs); - - datum::GeodeticReferenceFramePtr - // cppcheck-suppress functionStatic - getGeodeticDatumFromCache(const std::string &code); - // cppcheck-suppress functionStatic - void cache(const std::string &code, - const datum::GeodeticReferenceFrameNNPtr &datum); + inline AuthorityFactoryNNPtr createFactory(const std::string &auth_name) { + if (auth_name == authority_) { + return NN_NO_CHECK(thisFactory_.lock()); + } + return AuthorityFactory::create(context_, auth_name); + } bool rejectOpDueToMissingGrid(const operation::CoordinateOperationNNPtr &op, bool discardIfMissingGrid); @@ -938,69 +1188,28 @@ struct AuthorityFactory::Private { const std::string &area_of_use_auth_name, const std::string &area_of_use_code); - SQLResultSet - run(const std::string &sql, - const std::vector<SQLValues> ¶meters = std::vector<SQLValues>()); + SQLResultSet run(const std::string &sql, + const ListOfParams ¶meters = ListOfParams()); SQLResultSet runWithCodeParam(const std::string &sql, const std::string &code); SQLResultSet runWithCodeParam(const char *sql, const std::string &code); + bool hasAuthorityRestriction() const { + return !authority_.empty() && authority_ != "any"; + } + private: DatabaseContextNNPtr context_; std::string authority_; std::weak_ptr<AuthorityFactory> thisFactory_{}; - std::weak_ptr<AuthorityFactory> parentFactory_{}; - std::map<std::string, AuthorityFactoryNNPtr> mapFactory_{}; - lru11::Cache<std::string, util::BaseObjectPtr> cacheUOM_{}; - lru11::Cache<std::string, util::BaseObjectPtr> cacheCRS_{}; - lru11::Cache<std::string, util::BaseObjectPtr> cacheGeodeticDatum_{}; - - static void - insertIntoCache(lru11::Cache<std::string, util::BaseObjectPtr> &cache, - const std::string &code, const util::BaseObjectPtr &obj); - - static void - getFromCache(lru11::Cache<std::string, util::BaseObjectPtr> &cache, - const std::string &code, util::BaseObjectPtr &obj); }; // --------------------------------------------------------------------------- -AuthorityFactoryNNPtr -AuthorityFactory::Private::createFactory(const std::string &auth_name) { - - // If we are a child factory, then create new factory on the parent - auto parentFactoryLocked(parentFactory_.lock()); - if (parentFactoryLocked) { - return parentFactoryLocked->d->createFactory(auth_name); - } - - // If asked for a factory with our name, return ourselves. - auto lockedThisFactory(thisFactory_.lock()); - assert(lockedThisFactory); - if (auth_name == lockedThisFactory->getAuthority()) { - return NN_NO_CHECK(lockedThisFactory); - } - - // Find if there is already a child factory with the passed name. - auto iter = mapFactory_.find(auth_name); - if (iter == mapFactory_.end()) { - auto newFactory = AuthorityFactory::create(context_, auth_name); - newFactory->d->parentFactory_ = thisFactory_; - mapFactory_.insert(std::pair<std::string, AuthorityFactoryNNPtr>( - auth_name, newFactory)); - return newFactory; - } - return iter->second; -} - -// --------------------------------------------------------------------------- - -SQLResultSet -AuthorityFactory::Private::run(const std::string &sql, - const std::vector<SQLValues> ¶meters) { +SQLResultSet AuthorityFactory::Private::run(const std::string &sql, + const ListOfParams ¶meters) { return context()->getPrivate()->run(sql, parameters); } @@ -1036,8 +1245,10 @@ util::PropertyMap AuthorityFactory::Private::createProperties( auto props = util::PropertyMap() .set(metadata::Identifier::CODESPACE_KEY, authority()) .set(metadata::Identifier::CODE_KEY, code) - .set(common::IdentifiedObject::NAME_KEY, name) - .set(common::IdentifiedObject::DEPRECATED_KEY, deprecated); + .set(common::IdentifiedObject::NAME_KEY, name); + if (deprecated) { + props.set(common::IdentifiedObject::DEPRECATED_KEY, true); + } if (extent) { props.set( common::ObjectUsage::DOMAIN_OF_VALIDITY_KEY, @@ -1062,70 +1273,6 @@ util::PropertyMap AuthorityFactory::Private::createProperties( // --------------------------------------------------------------------------- -void AuthorityFactory::Private::insertIntoCache( - lru11::Cache<std::string, util::BaseObjectPtr> &cache, - const std::string &code, const util::BaseObjectPtr &obj) { - cache.insert(code, obj); -} - -// --------------------------------------------------------------------------- - -void AuthorityFactory::Private::getFromCache( - lru11::Cache<std::string, util::BaseObjectPtr> &cache, - const std::string &code, util::BaseObjectPtr &obj) { - cache.tryGet(code, obj); -} - -// --------------------------------------------------------------------------- - -crs::CRSPtr -AuthorityFactory::Private::getCRSFromCache(const std::string &code) { - util::BaseObjectPtr obj; - getFromCache(cacheCRS_, code, obj); - return std::static_pointer_cast<crs::CRS>(obj); -} - -// --------------------------------------------------------------------------- - -void AuthorityFactory::Private::cache(const std::string &code, - const crs::CRSNNPtr &crs) { - insertIntoCache(cacheCRS_, code, crs.as_nullable()); -} - -// --------------------------------------------------------------------------- - -common::UnitOfMeasurePtr -AuthorityFactory::Private::getUOMFromCache(const std::string &code) { - util::BaseObjectPtr obj; - getFromCache(cacheUOM_, code, obj); - return std::static_pointer_cast<common::UnitOfMeasure>(obj); -} - -// --------------------------------------------------------------------------- - -void AuthorityFactory::Private::cache(const std::string &code, - const common::UnitOfMeasureNNPtr &uom) { - insertIntoCache(cacheUOM_, code, uom.as_nullable()); -} - -// --------------------------------------------------------------------------- - -datum::GeodeticReferenceFramePtr -AuthorityFactory::Private::getGeodeticDatumFromCache(const std::string &code) { - util::BaseObjectPtr obj; - getFromCache(cacheGeodeticDatum_, code, obj); - return std::static_pointer_cast<datum::GeodeticReferenceFrame>(obj); -} - -// --------------------------------------------------------------------------- - -void AuthorityFactory::Private::cache( - const std::string &code, const datum::GeodeticReferenceFrameNNPtr &datum) { - insertIntoCache(cacheGeodeticDatum_, code, datum.as_nullable()); -} - -// --------------------------------------------------------------------------- - bool AuthorityFactory::Private::rejectOpDueToMissingGrid( const operation::CoordinateOperationNNPtr &op, bool discardIfMissingGrid) { if (discardIfMissingGrid) { @@ -1170,6 +1317,7 @@ AuthorityFactory::AuthorityFactory(const DatabaseContextNNPtr &context, AuthorityFactoryNNPtr AuthorityFactory::create(const DatabaseContextNNPtr &context, const std::string &authorityName) { + auto factory = AuthorityFactory::nn_make_shared<AuthorityFactory>( context, authorityName); factory->d->setThis(factory); @@ -1210,7 +1358,7 @@ AuthorityFactory::createObject(const std::string &code) const { "SELECT table_name FROM object_view WHERE auth_name = ? AND code = ?", code); if (res.empty()) { - throw NoSuchAuthorityCodeException("not found", getAuthority(), code); + throw NoSuchAuthorityCodeException("not found", d->authority(), code); } if (res.size() != 1) { std::string msg( @@ -1224,7 +1372,7 @@ AuthorityFactory::createObject(const std::string &code) const { } throw FactoryException(msg); } - const auto &table_name = res[0][0]; + const auto &table_name = res.front()[0]; if (table_name == "area") { return util::nn_static_pointer_cast<util::BaseObject>( createExtent(code)); @@ -1276,7 +1424,7 @@ AuthorityFactory::createObject(const std::string &code) const { return util::nn_static_pointer_cast<util::BaseObject>( createCoordinateOperation(code, false)); } - throw FactoryException("unimplemented factory for " + res[0][0]); + throw FactoryException("unimplemented factory for " + res.front()[0]); } // --------------------------------------------------------------------------- @@ -1302,15 +1450,22 @@ static FactoryException buildFactoryException(const char *type, metadata::ExtentNNPtr AuthorityFactory::createExtent(const std::string &code) const { + const auto cacheKey(d->authority() + code); + { + auto extent = d->context()->d->getExtentFromCache(cacheKey); + if (extent) { + return NN_NO_CHECK(extent); + } + } auto sql = "SELECT name, south_lat, north_lat, west_lon, east_lon, " "deprecated FROM area WHERE auth_name = ? AND code = ?"; auto res = d->runWithCodeParam(sql, code); if (res.empty()) { - throw NoSuchAuthorityCodeException("area not found", getAuthority(), + throw NoSuchAuthorityCodeException("area not found", d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; double south_lat = c_locale_stod(row[1]); double north_lat = c_locale_stod(row[2]); @@ -1319,11 +1474,13 @@ AuthorityFactory::createExtent(const std::string &code) const { auto bbox = metadata::GeographicBoundingBox::create( west_lon, south_lat, east_lon, north_lat); - return metadata::Extent::create( + auto extent = metadata::Extent::create( util::optional<std::string>(name), std::vector<metadata::GeographicExtentNNPtr>{bbox}, std::vector<metadata::VerticalExtentNNPtr>(), std::vector<metadata::TemporalExtentNNPtr>()); + d->context()->d->cache(code, extent); + return extent; } catch (const std::exception &ex) { throw buildFactoryException("area", code, ex); @@ -1342,8 +1499,9 @@ AuthorityFactory::createExtent(const std::string &code) const { UnitOfMeasureNNPtr AuthorityFactory::createUnitOfMeasure(const std::string &code) const { + const auto cacheKey(d->authority() + code); { - auto uom = d->getUOMFromCache(code); + auto uom = d->context()->d->getUOMFromCache(cacheKey); if (uom) { return NN_NO_CHECK(uom); } @@ -1354,10 +1512,10 @@ AuthorityFactory::createUnitOfMeasure(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("unit of measure not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = (row[0] == "degree (supplier to define representation)") ? UnitOfMeasure::DEGREE.name() @@ -1386,8 +1544,8 @@ AuthorityFactory::createUnitOfMeasure(const std::string &code) const { else if (type_str == "time") unitType = UnitOfMeasure::Type::TIME; auto uom = util::nn_make_shared<UnitOfMeasure>( - name, conv_factor, unitType, getAuthority(), code); - d->cache(code, uom); + name, conv_factor, unitType, d->authority(), code); + d->context()->d->cache(cacheKey, uom); return uom; } catch (const std::exception &ex) { throw buildFactoryException("unit of measure", code, ex); @@ -1397,14 +1555,12 @@ AuthorityFactory::createUnitOfMeasure(const std::string &code) const { // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -static void normalizeMeasure(const std::string &uom_code, - const std::string &value, - std::string &normalized_uom_code, - double &normalized_value) { - normalized_uom_code = uom_code; - normalized_value = c_locale_stod(value); +static double normalizeMeasure(const std::string &uom_code, + const std::string &value, + std::string &normalized_uom_code) { if (uom_code == "9110") // DDD.MMSSsss..... { + double normalized_value = c_locale_stod(value); std::ostringstream buffer; buffer.imbue(std::locale::classic()); constexpr size_t precision = 12; @@ -1422,6 +1578,10 @@ static void normalizeMeasure(const std::string &uom_code, (c_locale_stod(seconds) / std::pow(10, seconds.size() - 2)) / 3600.); normalized_uom_code = common::UnitOfMeasure::DEGREE.code(); + return normalized_value; + } else { + normalized_uom_code = uom_code; + return c_locale_stod(value); } } //! @endcond @@ -1438,6 +1598,13 @@ static void normalizeMeasure(const std::string &uom_code, datum::PrimeMeridianNNPtr AuthorityFactory::createPrimeMeridian(const std::string &code) const { + const auto cacheKey(d->authority() + code); + { + auto pm = d->context()->d->getPrimeMeridianFromCache(cacheKey); + if (pm) { + return NN_NO_CHECK(pm); + } + } auto res = d->runWithCodeParam( "SELECT name, longitude, uom_auth_name, uom_code, deprecated FROM " "prime_meridian WHERE " @@ -1445,10 +1612,10 @@ AuthorityFactory::createPrimeMeridian(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("prime meridian not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &longitude = row[1]; const auto &uom_auth_name = row[2]; @@ -1456,14 +1623,15 @@ AuthorityFactory::createPrimeMeridian(const std::string &code) const { const bool deprecated = row[4] == "1"; std::string normalized_uom_code(uom_code); - double normalized_value(c_locale_stod(longitude)); - normalizeMeasure(uom_code, longitude, normalized_uom_code, - normalized_value); + const double normalized_value = + normalizeMeasure(uom_code, longitude, normalized_uom_code); auto uom = d->createUnitOfMeasure(uom_auth_name, normalized_uom_code); auto props = d->createProperties(code, name, deprecated, nullptr); - return datum::PrimeMeridian::create( + auto pm = datum::PrimeMeridian::create( props, common::Angle(normalized_value, uom)); + d->context()->d->cache(cacheKey, pm); + return pm; } catch (const std::exception &ex) { throw buildFactoryException("prime meridian", code, ex); } @@ -1492,7 +1660,7 @@ AuthorityFactory::identifyBodyFromSemiMajorAxis(double semi_major_axis, if (res.size() > 1) { throw FactoryException("more than one match found"); } - return res[0][0]; + return res.front()[0]; } // --------------------------------------------------------------------------- @@ -1519,10 +1687,10 @@ AuthorityFactory::createEllipsoid(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("ellipsoid not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &semi_major_axis_str = row[1]; double semi_major_axis = c_locale_stod(semi_major_axis_str); @@ -1563,8 +1731,9 @@ AuthorityFactory::createEllipsoid(const std::string &code) const { datum::GeodeticReferenceFrameNNPtr AuthorityFactory::createGeodeticDatum(const std::string &code) const { + const auto cacheKey(d->authority() + code); { - auto datum = d->getGeodeticDatumFromCache(code); + auto datum = d->context()->d->getGeodeticDatumFromCache(cacheKey); if (datum) { return NN_NO_CHECK(datum); } @@ -1577,10 +1746,10 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("geodetic datum not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &ellipsoid_auth_name = row[1]; const auto &ellipsoid_code = row[2]; @@ -1598,7 +1767,7 @@ AuthorityFactory::createGeodeticDatum(const std::string &code) const { auto anchor = util::optional<std::string>(); auto datum = datum::GeodeticReferenceFrame::create(props, ellipsoid, anchor, pm); - d->cache(code, datum); + d->context()->d->cache(cacheKey, datum); return datum; } catch (const std::exception &ex) { throw buildFactoryException("geodetic reference frame", code, ex); @@ -1623,10 +1792,10 @@ AuthorityFactory::createVerticalDatum(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("vertical datum not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &area_of_use_auth_name = row[1]; const auto &area_of_use_code = row[2]; @@ -1656,12 +1825,12 @@ datum::DatumNNPtr AuthorityFactory::createDatum(const std::string &code) const { "auth_name = ? AND code = ? " "UNION ALL SELECT 'vertical_datum' FROM vertical_datum WHERE " "auth_name = ? AND code = ?", - {getAuthority(), code, getAuthority(), code}); + {d->authority(), code, d->authority(), code}); if (res.empty()) { - throw NoSuchAuthorityCodeException("datum not found", getAuthority(), + throw NoSuchAuthorityCodeException("datum not found", d->authority(), code); } - if (res[0][0] == "geodetic_datum") { + if (res.front()[0] == "geodetic_datum") { return createGeodeticDatum(code); } return createVerticalDatum(code); @@ -1700,6 +1869,13 @@ static cs::MeridianPtr createMeridian(const std::string &val) { cs::CoordinateSystemNNPtr AuthorityFactory::createCoordinateSystem(const std::string &code) const { + const auto cacheKey(d->authority() + code); + { + auto cs = d->context()->d->getCoordinateSystemFromCache(cacheKey); + if (cs) { + return NN_NO_CHECK(cs); + } + } auto res = d->runWithCodeParam( "SELECT axis.name, abbrev, orientation, uom_auth_name, uom_code, " "cs.type FROM " @@ -1711,10 +1887,10 @@ AuthorityFactory::createCoordinateSystem(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("coordinate system not found", - getAuthority(), code); + d->authority(), code); } - const auto &csType = res[0][5]; + const auto &csType = res.front()[5]; std::vector<cs::CoordinateSystemAxisNNPtr> axisList; for (const auto &row : res) { const auto &name = row[0]; @@ -1755,32 +1931,41 @@ AuthorityFactory::createCoordinateSystem(const std::string &code) const { axisList.emplace_back(cs::CoordinateSystemAxis::create( props, abbrev, *direction, uom, meridian)); } + + const auto cacheAndRet = [this, + &cacheKey](const cs::CoordinateSystemNNPtr &cs) { + d->context()->d->cache(cacheKey, cs); + return cs; + }; + auto props = util::PropertyMap() - .set(metadata::Identifier::CODESPACE_KEY, getAuthority()) + .set(metadata::Identifier::CODESPACE_KEY, d->authority()) .set(metadata::Identifier::CODE_KEY, code); if (csType == "ellipsoidal") { if (axisList.size() == 2) { - return cs::EllipsoidalCS::create(props, axisList[0], axisList[1]); + return cacheAndRet( + cs::EllipsoidalCS::create(props, axisList[0], axisList[1])); } if (axisList.size() == 3) { - return cs::EllipsoidalCS::create(props, axisList[0], axisList[1], - axisList[2]); + return cacheAndRet(cs::EllipsoidalCS::create( + props, axisList[0], axisList[1], axisList[2])); } throw FactoryException("invalid number of axis for EllipsoidalCS"); } if (csType == "Cartesian") { if (axisList.size() == 2) { - return cs::CartesianCS::create(props, axisList[0], axisList[1]); + return cacheAndRet( + cs::CartesianCS::create(props, axisList[0], axisList[1])); } if (axisList.size() == 3) { - return cs::CartesianCS::create(props, axisList[0], axisList[1], - axisList[2]); + return cacheAndRet(cs::CartesianCS::create( + props, axisList[0], axisList[1], axisList[2])); } throw FactoryException("invalid number of axis for CartesianCS"); } if (csType == "vertical") { if (axisList.size() == 1) { - return cs::VerticalCS::create(props, axisList[0]); + return cacheAndRet(cs::VerticalCS::create(props, axisList[0])); } throw FactoryException("invalid number of axis for VerticalCS"); } @@ -1846,8 +2031,9 @@ cloneWithProps(const crs::GeodeticCRSNNPtr &geodCRS, crs::GeodeticCRSNNPtr AuthorityFactory::createGeodeticCRS(const std::string &code, bool geographicOnly) const { - auto crs = - std::dynamic_pointer_cast<crs::GeodeticCRS>(d->getCRSFromCache(code)); + const auto cacheKey(d->authority() + code); + auto crs = std::dynamic_pointer_cast<crs::GeodeticCRS>( + d->context()->d->getCRSFromCache(cacheKey)); if (crs) { return NN_NO_CHECK(crs); } @@ -1862,10 +2048,10 @@ AuthorityFactory::createGeodeticCRS(const std::string &code, auto res = d->runWithCodeParam(sql, code); if (res.empty()) { throw NoSuchAuthorityCodeException("geodeticCRS not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &type = row[1]; const auto &cs_auth_name = row[2]; @@ -1917,14 +2103,14 @@ AuthorityFactory::createGeodeticCRS(const std::string &code, ellipsoidalCS) { auto crsRet = crs::GeographicCRS::create( props, datum, NN_NO_CHECK(ellipsoidalCS)); - d->cache(code, crsRet); + d->context()->d->cache(cacheKey, crsRet); return crsRet; } auto geocentricCS = util::nn_dynamic_pointer_cast<cs::CartesianCS>(cs); if (type == "geocentric" && geocentricCS) { auto crsRet = crs::GeodeticCRS::create(props, datum, NN_NO_CHECK(geocentricCS)); - d->cache(code, crsRet); + d->context()->d->cache(cacheKey, crsRet); return crsRet; } throw FactoryException("unsupported (type, CS type) for geodeticCRS: " + @@ -1954,10 +2140,10 @@ AuthorityFactory::createVerticalCRS(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("verticalCRS not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &cs_auth_name = row[1]; const auto &cs_code = row[2]; @@ -1998,28 +2184,41 @@ AuthorityFactory::createVerticalCRS(const std::string &code) const { operation::ConversionNNPtr AuthorityFactory::createConversion(const std::string &code) const { - std::ostringstream buffer; - buffer.imbue(std::locale::classic()); - buffer << "SELECT name, area_of_use_auth_name, area_of_use_code, " - "method_auth_name, method_code, method_name"; - constexpr int N_MAX_PARAMS = 7; - for (int i = 1; i <= N_MAX_PARAMS; ++i) { - buffer << ", param" << i << "_auth_name"; - buffer << ", param" << i << "_code"; - buffer << ", param" << i << "_name"; - buffer << ", param" << i << "_value"; - buffer << ", param" << i << "_uom_auth_name"; - buffer << ", param" << i << "_uom_code"; - } - buffer << ", deprecated FROM conversion WHERE auth_name = ? AND code = ?"; - - auto res = d->runWithCodeParam(buffer.str(), code); + + static const char *sql = + "SELECT name, area_of_use_auth_name, area_of_use_code, " + "method_auth_name, method_code, method_name, " + + "param1_auth_name, param1_code, param1_name, param1_value, " + "param1_uom_auth_name, param1_uom_code, " + + "param2_auth_name, param2_code, param2_name, param2_value, " + "param2_uom_auth_name, param2_uom_code, " + + "param3_auth_name, param3_code, param3_name, param3_value, " + "param3_uom_auth_name, param3_uom_code, " + + "param4_auth_name, param4_code, param4_name, param4_value, " + "param4_uom_auth_name, param4_uom_code, " + + "param5_auth_name, param5_code, param5_name, param5_value, " + "param5_uom_auth_name, param5_uom_code, " + + "param6_auth_name, param6_code, param6_name, param6_value, " + "param6_uom_auth_name, param6_uom_code, " + + "param7_auth_name, param7_code, param7_name, param7_value, " + "param7_uom_auth_name, param7_uom_code, " + + "deprecated FROM conversion WHERE auth_name = ? AND code = ?"; + + auto res = d->runWithCodeParam(sql, code); if (res.empty()) { throw NoSuchAuthorityCodeException("conversion not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &area_of_use_auth_name = row[idx++]; @@ -2030,6 +2229,7 @@ AuthorityFactory::createConversion(const std::string &code) const { const size_t base_param_idx = idx; std::vector<operation::OperationParameterNNPtr> parameters; std::vector<operation::ParameterValueNNPtr> values; + constexpr int N_MAX_PARAMS = 7; for (int i = 0; i < N_MAX_PARAMS; ++i) { const auto ¶m_auth_name = row[base_param_idx + i * 6 + 0]; if (param_auth_name.empty()) { @@ -2046,9 +2246,8 @@ AuthorityFactory::createConversion(const std::string &code) const { .set(metadata::Identifier::CODE_KEY, param_code) .set(common::IdentifiedObject::NAME_KEY, param_name))); std::string normalized_uom_code(param_uom_code); - double normalized_value(c_locale_stod(param_value)); - normalizeMeasure(param_uom_code, param_value, normalized_uom_code, - normalized_value); + const double normalized_value = normalizeMeasure( + param_uom_code, param_value, normalized_uom_code); auto uom = d->createUnitOfMeasure(param_uom_auth_name, normalized_uom_code); values.emplace_back(operation::ParameterValue::create( @@ -2095,10 +2294,10 @@ AuthorityFactory::createProjectedCRS(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("projectedCRS not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &cs_auth_name = row[1]; const auto &cs_code = row[2]; @@ -2194,10 +2393,10 @@ AuthorityFactory::createCompoundCRS(const std::string &code) const { code); if (res.empty()) { throw NoSuchAuthorityCodeException("compoundCRS not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); const auto &name = row[0]; const auto &horiz_crs_auth_name = row[1]; const auto &horiz_crs_code = row[2]; @@ -2240,17 +2439,18 @@ crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem( crs::CRSNNPtr AuthorityFactory::createCoordinateReferenceSystem(const std::string &code, bool allowCompound) const { - auto crs = d->getCRSFromCache(code); + const auto cacheKey(d->authority() + code); + auto crs = d->context()->d->getCRSFromCache(cacheKey); if (crs) { return NN_NO_CHECK(crs); } auto res = d->runWithCodeParam( "SELECT type FROM crs_view WHERE auth_name = ? AND code = ?", code); if (res.empty()) { - throw NoSuchAuthorityCodeException("crs not found", getAuthority(), + throw NoSuchAuthorityCodeException("crs not found", d->authority(), code); } - const auto &type = res[0][0]; + const auto &type = res.front()[0]; if (type == "geographic 2D" || type == "geographic 3D" || type == "geocentric") { return createGeodeticCRS(code); @@ -2315,28 +2515,33 @@ static operation::ParameterValueNNPtr createAngle(const std::string &value, operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( const std::string &code, bool usePROJAlternativeGridNames) const { - return createCoordinateOperation(code, true, usePROJAlternativeGridNames); + return createCoordinateOperation(code, true, usePROJAlternativeGridNames, + std::string()); } operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( const std::string &code, bool allowConcatenated, - bool usePROJAlternativeGridNames) const { - auto res = d->runWithCodeParam( - "SELECT type FROM coordinate_operation_with_conversion_view " - "WHERE auth_name = ? AND code = ?", - code); - if (res.empty()) { - throw NoSuchAuthorityCodeException("coordinate operation not found", - getAuthority(), code); + bool usePROJAlternativeGridNames, const std::string &typeIn) const { + std::string type(typeIn); + if (type.empty()) { + auto res = d->runWithCodeParam( + "SELECT type FROM coordinate_operation_with_conversion_view " + "WHERE auth_name = ? AND code = ?", + code); + if (res.empty()) { + throw NoSuchAuthorityCodeException("coordinate operation not found", + d->authority(), code); + } + type = res.front()[0]; } - const auto type = res[0][0]; + if (type == "conversion") { return createConversion(code); } if (type == "helmert_transformation") { - res = d->runWithCodeParam( + auto res = d->runWithCodeParam( "SELECT name, method_auth_name, method_code, method_name, " "source_crs_auth_name, source_crs_code, target_crs_auth_name, " "target_crs_code, area_of_use_auth_name, area_of_use_code, " @@ -2356,10 +2561,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException( - "helmert_transformation not found", getAuthority(), code); + "helmert_transformation not found", d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &method_auth_name = row[idx++]; @@ -2573,7 +2778,7 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( } if (type == "grid_transformation") { - res = d->runWithCodeParam( + auto res = d->runWithCodeParam( "SELECT name, method_auth_name, method_code, method_name, " "source_crs_auth_name, source_crs_code, target_crs_auth_name, " "target_crs_code, area_of_use_auth_name, area_of_use_code, " @@ -2588,10 +2793,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException("grid_transformation not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &method_auth_name = row[idx++]; @@ -2704,14 +2909,14 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( buffer << ", deprecated FROM other_transformation WHERE auth_name = ? " "AND code = ?"; - res = d->runWithCodeParam(buffer.str(), code); + auto res = d->runWithCodeParam(buffer.str(), code); if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException("other_transformation not found", - getAuthority(), code); + d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &method_auth_name = row[idx++]; @@ -2746,9 +2951,8 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( .set(metadata::Identifier::CODE_KEY, param_code) .set(common::IdentifiedObject::NAME_KEY, param_name))); std::string normalized_uom_code(param_uom_code); - double normalized_value(c_locale_stod(param_value)); - normalizeMeasure(param_uom_code, param_value, - normalized_uom_code, normalized_value); + const double normalized_value = normalizeMeasure( + param_uom_code, param_value, normalized_uom_code); auto uom = d->createUnitOfMeasure(param_uom_auth_name, normalized_uom_code); values.emplace_back(operation::ParameterValue::create( @@ -2818,7 +3022,7 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( } if (allowConcatenated && type == "concatenated_operation") { - res = d->runWithCodeParam( + auto res = d->runWithCodeParam( "SELECT name, source_crs_auth_name, source_crs_code, " "target_crs_auth_name, target_crs_code, " "area_of_use_auth_name, area_of_use_code, accuracy, " @@ -2829,10 +3033,10 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( if (res.empty()) { // shouldn't happen if foreign keys are OK throw NoSuchAuthorityCodeException( - "concatenated_operation not found", getAuthority(), code); + "concatenated_operation not found", d->authority(), code); } try { - const auto &row = res[0]; + const auto &row = res.front(); size_t idx = 0; const auto &name = row[idx++]; const auto &source_crs_auth_name = row[idx++]; @@ -2855,17 +3059,20 @@ operation::CoordinateOperationNNPtr AuthorityFactory::createCoordinateOperation( operations.push_back( d->createFactory(step1_auth_name) ->createCoordinateOperation(step1_code, false, - usePROJAlternativeGridNames)); + usePROJAlternativeGridNames, + std::string())); operations.push_back( d->createFactory(step2_auth_name) ->createCoordinateOperation(step2_code, false, - usePROJAlternativeGridNames)); + usePROJAlternativeGridNames, + std::string())); if (!step3_auth_name.empty()) { operations.push_back( d->createFactory(step3_auth_name) - ->createCoordinateOperation( - step3_code, false, usePROJAlternativeGridNames)); + ->createCoordinateOperation(step3_code, false, + usePROJAlternativeGridNames, + std::string())); } // In case the operation is a conversion (we hope this is the @@ -3045,7 +3252,7 @@ std::vector<operation::CoordinateOperationNNPtr> AuthorityFactory::createFromCoordinateReferenceSystemCodes( const std::string &sourceCRSCode, const std::string &targetCRSCode) const { return createFromCoordinateReferenceSystemCodes( - getAuthority(), sourceCRSCode, getAuthority(), targetCRSCode, false, + d->authority(), sourceCRSCode, d->authority(), targetCRSCode, false, false, false); } @@ -3088,29 +3295,45 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( const std::string &targetCRSAuthName, const std::string &targetCRSCode, bool usePROJAlternativeGridNames, bool discardIfMissingGrid, bool discardSuperseded) const { + + auto cacheKey(d->authority()); + cacheKey += sourceCRSAuthName; + cacheKey += sourceCRSCode; + cacheKey += targetCRSAuthName; + cacheKey += targetCRSCode; + cacheKey += (usePROJAlternativeGridNames ? '1' : '0'); + cacheKey += (discardIfMissingGrid ? '1' : '0'); + cacheKey += (discardSuperseded ? '1' : '0'); + std::vector<operation::CoordinateOperationNNPtr> list; - // Look-up first for conversion which is the most precise. - std::string sql( - "SELECT conversion_auth_name, conversion_code FROM " - "projected_crs WHERE geodetic_crs_auth_name = ? AND geodetic_crs_code " - "= ? AND auth_name = ? AND code = ? AND deprecated != 1"); - auto params = std::vector<SQLValues>{sourceCRSAuthName, sourceCRSCode, - targetCRSAuthName, targetCRSCode}; - if (!getAuthority().empty()) { - sql += " AND conversion_auth_name = ?"; - params.emplace_back(getAuthority()); + if (d->context()->d->getCRSToCRSCoordOpFromCache(cacheKey, list)) { + return list; } + + // Look-up first for conversion which is the most precise. + std::string sql("SELECT conversion_auth_name, " + "geodetic_crs_auth_name, geodetic_crs_code FROM " + "projected_crs WHERE auth_name = ? AND code = ?"); + auto params = ListOfParams{targetCRSAuthName, targetCRSCode}; auto res = d->run(sql, params); if (!res.empty()) { - auto targetCRS = d->createFactory(targetCRSAuthName) - ->createProjectedCRS(targetCRSCode); - auto conv = targetCRS->derivingConversion(); - list.emplace_back(conv); - return list; + const auto &row = res.front(); + bool ok = row[1] == sourceCRSAuthName && row[2] == sourceCRSCode; + if (ok && d->hasAuthorityRestriction()) { + ok = row[0] == d->authority(); + } + if (ok) { + auto targetCRS = d->createFactory(targetCRSAuthName) + ->createProjectedCRS(targetCRSCode); + auto conv = targetCRS->derivingConversion(); + list.emplace_back(conv); + d->context()->d->cache(cacheKey, list); + return list; + } } if (discardSuperseded) { - sql = "SELECT cov.auth_name, cov.code, " + sql = "SELECT cov.auth_name, cov.code, cov.table_name, " "ss.replacement_auth_name, ss.replacement_code FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " @@ -3122,21 +3345,21 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( "ss.superseded_table_name = ss.replacement_table_name " "WHERE source_crs_auth_name = ? AND source_crs_code = ? AND " "target_crs_auth_name = ? AND target_crs_code = ? AND " - "cov.deprecated != 1"; + "cov.deprecated = 0"; } else { - sql = "SELECT cov.auth_name, cov.code FROM " + sql = "SELECT cov.auth_name, cov.code, cov.table_name FROM " "coordinate_operation_view cov JOIN area " "ON cov.area_of_use_auth_name = area.auth_name AND " "cov.area_of_use_code = area.code " "WHERE source_crs_auth_name = ? AND source_crs_code = ? AND " "target_crs_auth_name = ? AND target_crs_code = ? AND " - "cov.deprecated != 1"; + "cov.deprecated = 0"; } params = {sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode}; - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { sql += " AND cov.auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } sql += " ORDER BY pseudo_area_from_swne(south_lat, west_lon, north_lat, " "east_lon) DESC, " @@ -3153,8 +3376,8 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( } for (const auto &row : res) { if (discardSuperseded) { - const auto &replacement_auth_name = row[2]; - const auto &replacement_code = row[3]; + const auto &replacement_auth_name = row[3]; + const auto &replacement_code = row[4]; if (!replacement_auth_name.empty() && setTransf.find(std::pair<std::string, std::string>( replacement_auth_name, replacement_code)) != @@ -3167,12 +3390,14 @@ AuthorityFactory::createFromCoordinateReferenceSystemCodes( const auto &auth_name = row[0]; const auto &code = row[1]; + const auto &table_name = row[2]; auto op = d->createFactory(auth_name)->createCoordinateOperation( - code, true, usePROJAlternativeGridNames); + code, true, usePROJAlternativeGridNames, table_name); if (!d->rejectOpDueToMissingGrid(op, discardIfMissingGrid)) { list.emplace_back(op); } } + d->context()->d->cache(cacheKey, list); return list; } @@ -3290,8 +3515,10 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( discardSuperseded ? - "SELECT v1.auth_name AS auth_name1, v1.code AS code1, " + "SELECT v1.table_name as table1, " + "v1.auth_name AS auth_name1, v1.code AS code1, " "v1.accuracy AS accuracy1, " + "v2.table_name as table2, " "v2.auth_name AS auth_name2, v2.code AS code2, " "v2.accuracy as accuracy2, " "a1.south_lat AS south_lat1, " @@ -3310,8 +3537,10 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( "JOIN coordinate_operation_view v2 " : - "SELECT v1.auth_name AS auth_name1, v1.code AS code1, " + "SELECT v1.table_name as table1, " + "v1.auth_name AS auth_name1, v1.code AS code1, " "v1.accuracy AS accuracy1, " + "v2.table_name as table2, " "v2.auth_name AS auth_name2, v2.code AS code2, " "v2.accuracy as accuracy2, " "a1.south_lat AS south_lat1, " @@ -3354,17 +3583,17 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( joinArea + "WHERE v1.source_crs_auth_name = ? AND v1.source_crs_code = ? " "AND v2.target_crs_auth_name = ? AND v2.target_crs_code = ? "); - auto params = std::vector<SQLValues>{sourceCRSAuthName, sourceCRSCode, - targetCRSAuthName, targetCRSCode}; + auto params = ListOfParams{sourceCRSAuthName, sourceCRSCode, + targetCRSAuthName, targetCRSCode}; std::string additionalWhere( "AND v1.deprecated = 0 AND v2.deprecated = 0 " "AND intersects_bbox(south_lat1, west_lon1, north_lat1, east_lon1, " "south_lat2, west_lon2, north_lat2, east_lon2) == 1 "); - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { additionalWhere += "AND v1.auth_name = ? AND v2.auth_name = ? "; - params.emplace_back(getAuthority()); - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); + params.emplace_back(d->authority()); } std::string intermediateWhere = buildIntermediateWhere(intermediateCRSAuthCodes, "target", "source"); @@ -3381,11 +3610,13 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( std::set<std::pair<std::string, std::string>> setTransf1; std::set<std::pair<std::string, std::string>> setTransf2; for (const auto &row : resultSet) { - const auto &auth_name1 = row[0]; - const auto &code1 = row[1]; - // const auto &accuracy1 = row[2]; - const auto &auth_name2 = row[3]; - const auto &code2 = row[4]; + // table1 + const auto &auth_name1 = row[1]; + const auto &code1 = row[2]; + // accuracy1 + // table2 + const auto &auth_name2 = row[5]; + const auto &code2 = row[6]; setTransf1.insert( std::pair<std::string, std::string>(auth_name1, code1)); setTransf2.insert( @@ -3393,10 +3624,10 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( } SQLResultSet filteredResultSet; for (const auto &row : resultSet) { - const auto &replacement_auth_name1 = row[14]; - const auto &replacement_code1 = row[15]; - const auto &replacement_auth_name2 = row[16]; - const auto &replacement_code2 = row[17]; + const auto &replacement_auth_name1 = row[16]; + const auto &replacement_code1 = row[17]; + const auto &replacement_auth_name2 = row[18]; + const auto &replacement_code2 = row[19]; if (!replacement_auth_name1.empty() && setTransf1.find(std::pair<std::string, std::string>( replacement_auth_name1, replacement_code1)) != @@ -3422,22 +3653,24 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { - const auto &auth_name1 = row[0]; - const auto &code1 = row[1]; - // const auto &accuracy1 = row[2]; - const auto &auth_name2 = row[3]; - const auto &code2 = row[4]; - // const auto &accuracy2 = row[5]; + const auto &table1 = row[0]; + const auto &auth_name1 = row[1]; + const auto &code1 = row[2]; + // const auto &accuracy1 = row[3]; + const auto &table2 = row[4]; + const auto &auth_name2 = row[5]; + const auto &code2 = row[6]; + // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) - ->createCoordinateOperation(code1, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) - ->createCoordinateOperation(code2, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; @@ -3461,22 +3694,24 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { - const auto &auth_name1 = row[0]; - const auto &code1 = row[1]; - // const auto &accuracy1 = row[2]; - const auto &auth_name2 = row[3]; - const auto &code2 = row[4]; - // const auto &accuracy2 = row[5]; + const auto &table1 = row[0]; + const auto &auth_name1 = row[1]; + const auto &code1 = row[2]; + // const auto &accuracy1 = row[3]; + const auto &table2 = row[4]; + const auto &auth_name2 = row[5]; + const auto &code2 = row[6]; + // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) - ->createCoordinateOperation(code1, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) - ->createCoordinateOperation(code2, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; @@ -3500,22 +3735,24 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { - const auto &auth_name1 = row[0]; - const auto &code1 = row[1]; - // const auto &accuracy1 = row[2]; - const auto &auth_name2 = row[3]; - const auto &code2 = row[4]; - // const auto &accuracy2 = row[5]; + const auto &table1 = row[0]; + const auto &auth_name1 = row[1]; + const auto &code1 = row[2]; + // const auto &accuracy1 = row[3]; + const auto &table2 = row[4]; + const auto &auth_name2 = row[5]; + const auto &code2 = row[6]; + // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) - ->createCoordinateOperation(code1, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) - ->createCoordinateOperation(code2, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; @@ -3539,22 +3776,24 @@ AuthorityFactory::createFromCRSCodesWithIntermediates( res = filterOutSuperseded(std::move(res)); } for (const auto &row : res) { - const auto &auth_name1 = row[0]; - const auto &code1 = row[1]; - // const auto &accuracy1 = row[2]; - const auto &auth_name2 = row[3]; - const auto &code2 = row[4]; - // const auto &accuracy2 = row[5]; + const auto &table1 = row[0]; + const auto &auth_name1 = row[1]; + const auto &code1 = row[2]; + // const auto &accuracy1 = row[3]; + const auto &table2 = row[4]; + const auto &auth_name2 = row[5]; + const auto &code2 = row[6]; + // const auto &accuracy2 = row[7]; auto op1 = d->createFactory(auth_name1) - ->createCoordinateOperation(code1, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code1, true, usePROJAlternativeGridNames, table1); if (useIrrelevantPivot(op1, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; } auto op2 = d->createFactory(auth_name2) - ->createCoordinateOperation(code2, true, - usePROJAlternativeGridNames); + ->createCoordinateOperation( + code2, true, usePROJAlternativeGridNames, table2); if (useIrrelevantPivot(op2, sourceCRSAuthName, sourceCRSCode, targetCRSAuthName, targetCRSCode)) { continue; @@ -3661,10 +3900,10 @@ AuthorityFactory::getAuthorityCodes(const ObjectType &type, sql += "auth_name = ?"; if (!allowDeprecated) { - sql += " AND deprecated != 1"; + sql += " AND deprecated = 0"; } - auto res = d->run(sql, {getAuthority()}); + auto res = d->run(sql, {d->authority()}); std::set<std::string> set; for (const auto &row : res) { set.insert(row[0]); @@ -3690,10 +3929,10 @@ AuthorityFactory::getDescriptionText(const std::string &code) const { "? ORDER BY table_name"; auto res = d->runWithCodeParam(sql, code); if (res.empty()) { - throw NoSuchAuthorityCodeException("object not found", getAuthority(), + throw NoSuchAuthorityCodeException("object not found", d->authority(), code); } - return res[0][0]; + return res.front()[0]; } // --------------------------------------------------------------------------- @@ -3724,7 +3963,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( if (tryEquivalentNameSpelling) { std::string sql( "SELECT table_name, auth_name, code, alt_name FROM alias_name"); - std::vector<SQLValues> params; + ListOfParams params; if (!tableName.empty()) { sql += " WHERE table_name = ?"; params.push_back(tableName); @@ -3756,7 +3995,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( if (res.empty()) { // shouldn't happen normally return std::string(); } - return res[0][0]; + return res.front()[0]; } } return std::string(); @@ -3764,7 +4003,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( std::string sql( "SELECT table_name, auth_name, code FROM alias_name WHERE " "alt_name = ?"); - std::vector<SQLValues> params{aliasedName}; + ListOfParams params{aliasedName}; if (!tableName.empty()) { sql += " AND table_name = ?"; params.push_back(tableName); @@ -3777,9 +4016,10 @@ std::string AuthorityFactory::getOfficialNameFromAlias( if (res.empty()) { return std::string(); } - outTableName = res[0][0]; - outAuthName = res[0][1]; - outCode = res[0][2]; + const auto &row = res.front(); + outTableName = row[0]; + outAuthName = row[1]; + outCode = row[2]; sql = "SELECT name FROM \""; sql += replaceAll(outTableName, "\"", "\"\""); sql += "\" WHERE auth_name = ? AND code = ?"; @@ -3787,7 +4027,7 @@ std::string AuthorityFactory::getOfficialNameFromAlias( if (res.empty()) { // shouldn't happen normally return std::string(); } - return res[0][0]; + return res.front()[0]; } } @@ -3847,14 +4087,14 @@ AuthorityFactory::createObjectsFromName( std::string sql( "SELECT table_name, auth_name, code, name FROM object_view WHERE " "deprecated = ? AND "); - std::vector<SQLValues> params{deprecated ? 1.0 : 0.0}; + ListOfParams params{deprecated ? 1.0 : 0.0}; if (!approximateMatch) { sql += "name LIKE ? AND "; params.push_back(searchedNameWithoutDeprecated); } - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { sql += " auth_name = ? AND "; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } if (allowedObjectTypes.empty()) { @@ -3971,7 +4211,7 @@ AuthorityFactory::createObjectsFromName( // so cache results. if (allowedObjectTypes.size() == 1 && allowedObjectTypes[0] == ObjectType::GEODETIC_REFERENCE_FRAME && - approximateMatch && getAuthority().empty()) { + approximateMatch && d->authority().empty()) { auto &mapCanonicalizeGRFName = d->context()->getPrivate()->getMapCanonicalizeGRFName(); if (mapCanonicalizeGRFName.empty()) { @@ -4151,10 +4391,10 @@ AuthorityFactory::listAreaOfUseFromName(const std::string &name, bool approximateMatch) const { std::string sql( "SELECT auth_name, code FROM area WHERE deprecated = 0 AND "); - std::vector<SQLValues> params; - if (!getAuthority().empty()) { + ListOfParams params; + if (d->hasAuthorityRestriction()) { sql += " auth_name = ? AND "; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } sql += "name LIKE ?"; if (!approximateMatch) { @@ -4182,10 +4422,9 @@ std::list<datum::EllipsoidNNPtr> AuthorityFactory::createEllipsoidFromExisting( "abs(semi_minor_axis - ?) < 1e-10 * abs(semi_minor_axis)) OR " "((inv_flattening IS NOT NULL AND " "abs(inv_flattening - ?) < 1e-10 * abs(inv_flattening))))"); - std::vector<SQLValues> params{ - ellipsoid->semiMajorAxis().getSIValue(), - ellipsoid->computeSemiMinorAxis().getSIValue(), - ellipsoid->computedInverseFlattening()}; + ListOfParams params{ellipsoid->semiMajorAxis().getSIValue(), + ellipsoid->computeSemiMinorAxis().getSIValue(), + ellipsoid->computedInverseFlattening()}; auto sqlRes = d->run(sql, params); std::list<datum::EllipsoidNNPtr> res; for (const auto &row : sqlRes) { @@ -4205,10 +4444,10 @@ std::list<crs::GeodeticCRSNNPtr> AuthorityFactory::createGeodeticCRSFromDatum( std::string sql( "SELECT auth_name, code FROM geodetic_crs WHERE " "datum_auth_name = ? AND datum_code = ? AND deprecated = 0"); - std::vector<SQLValues> params{datum_auth_name, datum_code}; - if (!getAuthority().empty()) { + ListOfParams params{datum_auth_name, datum_code}; + if (d->hasAuthorityRestriction()) { sql += " AND auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } if (!geodetic_crs_type.empty()) { sql += " AND type = ?"; @@ -4241,10 +4480,10 @@ AuthorityFactory::createGeodeticCRSFromEllipsoid( "geodetic_datum.ellipsoid_code = ? AND " "geodetic_datum.deprecated = 0 AND " "geodetic_crs.deprecated = 0"); - std::vector<SQLValues> params{ellipsoid_auth_name, ellipsoid_code}; - if (!getAuthority().empty()) { + ListOfParams params{ellipsoid_auth_name, ellipsoid_code}; + if (d->hasAuthorityRestriction()) { sql += " AND geodetic_crs.auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } if (!geodetic_crs_type.empty()) { sql += " AND geodetic_crs.type = ?"; @@ -4265,8 +4504,8 @@ AuthorityFactory::createGeodeticCRSFromEllipsoid( //! @cond Doxygen_Suppress static std::string buildSqlLookForAuthNameCode( - const std::list<std::pair<crs::CRSNNPtr, int>> &list, - std::vector<SQLValues> ¶ms, const char *prefixField) { + const std::list<std::pair<crs::CRSNNPtr, int>> &list, ListOfParams ¶ms, + const char *prefixField) { std::string sql("("); std::set<std::string> authorities; @@ -4354,7 +4593,7 @@ AuthorityFactory::createProjectedCRSFromExisting( "projected_crs.conversion_auth_name = conversion.auth_name AND " "projected_crs.conversion_code = conversion.code WHERE " "projected_crs.deprecated = 0 AND "); - std::vector<SQLValues> params; + ListOfParams params; if (!candidatesGeodCRS.empty()) { sql += buildSqlLookForAuthNameCode(candidatesGeodCRS, params, "projected_crs.geodetic_crs_"); @@ -4363,9 +4602,9 @@ AuthorityFactory::createProjectedCRSFromExisting( sql += "conversion.method_auth_name = 'EPSG' AND " "conversion.method_code = ?"; params.emplace_back(toString(methodEPSGCode)); - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { sql += " AND projected_crs.auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } int iParam = 1; @@ -4533,9 +4772,9 @@ AuthorityFactory::createProjectedCRSFromExisting( params.emplace_back(patternVal); } sql += ")"; - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { sql += " AND auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } auto sqlRes2 = d->run(sql, params); @@ -4582,7 +4821,7 @@ AuthorityFactory::createCompoundCRSFromExisting( std::string sql("SELECT auth_name, code FROM compound_crs WHERE " "deprecated = 0 AND "); - std::vector<SQLValues> params; + ListOfParams params; bool addAnd = false; if (!candidatesHorizCRS.empty()) { sql += buildSqlLookForAuthNameCode(candidatesHorizCRS, params, @@ -4597,12 +4836,12 @@ AuthorityFactory::createCompoundCRSFromExisting( "vertical_crs_"); addAnd = true; } - if (!getAuthority().empty()) { + if (d->hasAuthorityRestriction()) { if (addAnd) { sql += " AND "; } sql += "auth_name = ?"; - params.emplace_back(getAuthority()); + params.emplace_back(d->authority()); } auto sqlRes = d->run(sql, params); diff --git a/src/internal.cpp b/src/internal.cpp index 4bec1bf9..ecb724c2 100644 --- a/src/internal.cpp +++ b/src/internal.cpp @@ -32,6 +32,7 @@ #include "proj/internal/internal.hpp" +#include <cstdint> #include <cstring> #ifdef _MSC_VER #include <string.h> @@ -237,6 +238,38 @@ bool ends_with(const std::string &str, const std::string &suffix) noexcept { // --------------------------------------------------------------------------- double c_locale_stod(const std::string &s) { + + const auto s_size = s.size(); + // Fast path + if (s_size > 0 && s_size < 15) { + std::int64_t acc = 0; + std::int64_t div = 1; + bool afterDot = false; + size_t i = 0; + if (s[0] == '-') { + ++i; + div = -1; + } else if (s[0] == '+') { + ++i; + } + for (; i < s_size; ++i) { + const auto ch = s[i]; + if (ch >= '0' && ch <= '9') { + acc = acc * 10 + ch - '0'; + if (afterDot) { + div *= 10; + } + } else if (ch == '.') { + afterDot = true; + } else { + div = 0; + } + } + if (div) { + return static_cast<double>(acc) / div; + } + } + std::istringstream iss(s); iss.imbue(std::locale::classic()); double d; @@ -490,7 +490,7 @@ static std::string normalizeSerializedString(const std::string &in) { return in; } #else -static std::string normalizeSerializedString(const std::string &in) { +static inline std::string normalizeSerializedString(const std::string &in) { return in; } #endif @@ -499,11 +499,19 @@ static std::string normalizeSerializedString(const std::string &in) { void WKTFormatter::add(double number, int precision) { d->startNewChild(); - std::string val( - normalizeSerializedString(internal::toString(number, precision))); - d->result_ += val; - if (d->params_.useESRIDialect_ && val.find('.') == std::string::npos) { - d->result_ += ".0"; + if (number == 0.0) { + if (d->params_.useESRIDialect_) { + d->result_ += "0.0"; + } else { + d->result_ += '0'; + } + } else { + std::string val( + normalizeSerializedString(internal::toString(number, precision))); + d->result_ += val; + if (d->params_.useESRIDialect_ && val.find('.') == std::string::npos) { + d->result_ += ".0"; + } } } @@ -1963,8 +1971,7 @@ GeodeticReferenceFrameNNPtr WKTParser::Private::buildGeodeticReferenceFrame( foundDatumName = true; properties.set(IdentifiedObject::NAME_KEY, refDatum->nameStr()); - if (properties.find(Identifier::CODESPACE_KEY) == - properties.end() && + if (!properties.get(Identifier::CODESPACE_KEY) && refDatum->identifiers().size() == 1) { const auto &id = refDatum->identifiers()[0]; auto identifiers = ArrayOfBaseObject::create(); @@ -6674,7 +6681,7 @@ CRSNNPtr PROJStringParser::Private::buildProjectedCRS( } else if (step.name == "krovak") { if (param->epsg_code == EPSG_CODE_PARAMETER_COLATITUDE_CONE_AXIS) { - value = 30.2881397222222; + value = 30.28813975277777776; } else if ( param->epsg_code == EPSG_CODE_PARAMETER_LATITUDE_PSEUDO_STANDARD_PARALLEL) { diff --git a/src/metadata.cpp b/src/metadata.cpp index af8dc1fe..fac2ae6b 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -858,6 +858,8 @@ struct Identifier::Private { optional<std::string> description_{}; optional<std::string> uri_{}; + Private() = default; + Private(const std::string &codeIn, const PropertyMap &properties) : code_(codeIn) { setProperties(properties); @@ -874,10 +876,9 @@ void Identifier::Private::setProperties( const PropertyMap &properties) // throw(InvalidValueTypeException) { { - auto oIter = properties.find(AUTHORITY_KEY); - if (oIter != properties.end()) { - if (auto genVal = - dynamic_cast<const BoxedValue *>(oIter->second.get())) { + const auto pVal = properties.get(AUTHORITY_KEY); + if (pVal) { + if (auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) { if (genVal->type() == BoxedValue::Type::STRING) { authority_ = Citation(genVal->stringValue()); } else { @@ -886,7 +887,7 @@ void Identifier::Private::setProperties( } } else { if (auto citation = - dynamic_cast<const Citation *>(oIter->second.get())) { + dynamic_cast<const Citation *>(pVal->get())) { authority_ = *citation; } else { throw InvalidValueTypeException("Invalid value type for " + @@ -897,10 +898,9 @@ void Identifier::Private::setProperties( } { - auto oIter = properties.find(CODE_KEY); - if (oIter != properties.end()) { - if (auto genVal = - dynamic_cast<const BoxedValue *>(oIter->second.get())) { + const auto pVal = properties.get(CODE_KEY); + if (pVal) { + if (auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) { if (genVal->type() == BoxedValue::Type::INTEGER) { code_ = toString(genVal->integerValue()); } else if (genVal->type() == BoxedValue::Type::STRING) { @@ -916,45 +916,30 @@ void Identifier::Private::setProperties( } } - { - std::string temp; - if (properties.getStringValue(CODESPACE_KEY, temp)) { - codeSpace_ = temp; - } - } - - { - std::string temp; - if (properties.getStringValue(VERSION_KEY, temp)) { - version_ = temp; - } - } - - { - std::string temp; - if (properties.getStringValue(DESCRIPTION_KEY, temp)) { - description_ = temp; - } - } - - { - std::string temp; - if (properties.getStringValue(URI_KEY, temp)) { - uri_ = temp; - } - } + properties.getStringValue(CODESPACE_KEY, codeSpace_); + properties.getStringValue(VERSION_KEY, version_); + properties.getStringValue(DESCRIPTION_KEY, description_); + properties.getStringValue(URI_KEY, uri_); } //! @endcond // --------------------------------------------------------------------------- -Identifier::Identifier(const std::string &codeIn, const PropertyMap &properties) +Identifier::Identifier(const std::string &codeIn, + const util::PropertyMap &properties) : d(internal::make_unique<Private>(codeIn, properties)) {} // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress + +// --------------------------------------------------------------------------- + +Identifier::Identifier() : d(internal::make_unique<Private>()) {} + +// --------------------------------------------------------------------------- + Identifier::Identifier(const Identifier &other) : d(internal::make_unique<Private>(*(other.d))) {} @@ -979,6 +964,17 @@ IdentifierNNPtr Identifier::create(const std::string &codeIn, // --------------------------------------------------------------------------- +//! @cond Doxygen_Suppress +IdentifierNNPtr +Identifier::createFromDescription(const std::string &descriptionIn) { + auto id = Identifier::nn_make_shared<Identifier>(); + id->d->description_ = descriptionIn; + return id; +} +//! @endcond + +// --------------------------------------------------------------------------- + /** \brief Return a citation for the organization responsible for definition and * maintenance of the code. * @@ -572,6 +572,9 @@ PJ_OBJ_TYPE PROJ_DLL proj_obj_get_type(const PJ_OBJ *obj); int PROJ_DLL proj_obj_is_deprecated(const PJ_OBJ *obj); +PJ_OBJ_LIST PROJ_DLL *proj_obj_get_non_deprecated(PJ_CONTEXT *ctx, + const PJ_OBJ *obj); + /** Comparison criterion. */ typedef enum { diff --git a/src/proj_experimental.h b/src/proj_experimental.h index b8c37054..698b235b 100644 --- a/src/proj_experimental.h +++ b/src/proj_experimental.h @@ -123,6 +123,13 @@ PJ_OBJ PROJ_DLL *proj_obj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx, const char* unit_name, double unit_conv_factor); +PJ_OBJ_LIST PROJ_DLL *proj_obj_query_geodetic_crs_from_datum( + PJ_CONTEXT *ctx, + const char *crs_auth_name, + const char *datum_auth_name, + const char *datum_code, + const char *crs_type); + PJ_OBJ PROJ_DLL *proj_obj_create_geographic_crs( PJ_CONTEXT *ctx, const char *crs_name, @@ -244,7 +251,8 @@ PJ_OBJ PROJ_DLL *proj_obj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ_OBJ *transformation); PJ_OBJ PROJ_DLL *proj_obj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, - const PJ_OBJ *crs); + const PJ_OBJ *crs, + const char *const *options); /* BEGIN: Generated by scripts/create_c_api_projections.py*/ PJ_OBJ PROJ_DLL *proj_obj_create_conversion_utm( diff --git a/src/projinfo.cpp b/src/projinfo.cpp index ddcc09da..d6fa37bc 100644 --- a/src/projinfo.cpp +++ b/src/projinfo.cpp @@ -37,6 +37,7 @@ #include "projects.h" +#include <proj/common.hpp> #include <proj/coordinateoperation.hpp> #include <proj/crs.hpp> #include <proj/io.hpp> @@ -45,6 +46,7 @@ #include "proj/internal/internal.hpp" // for split +using namespace NS_PROJ::common; using namespace NS_PROJ::crs; using namespace NS_PROJ::io; using namespace NS_PROJ::metadata; @@ -134,7 +136,8 @@ static std::string c_ify_string(const std::string &str) { static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext, const std::string &user_string, bool kindIsCRS, const std::string &context, - bool buildBoundCRSToWGS84) { + bool buildBoundCRSToWGS84, + bool allowPivots) { BaseObjectPtr obj; std::string l_user_string(user_string); @@ -190,7 +193,8 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext, if (buildBoundCRSToWGS84) { auto crs = std::dynamic_pointer_cast<CRS>(obj); if (crs) { - obj = crs->createBoundCRSToWGS84IfPossible(dbContext).as_nullable(); + obj = crs->createBoundCRSToWGS84IfPossible(dbContext, allowPivots) + .as_nullable(); } } @@ -200,7 +204,31 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext, // --------------------------------------------------------------------------- static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj, - const OutputOptions &outputOpt) { + bool allowPivots, const OutputOptions &outputOpt) { + + auto identified = dynamic_cast<const IdentifiedObject *>(obj.get()); + if (!outputOpt.quiet && identified && identified->isDeprecated()) { + std::cout << "Warning: object is deprecated" << std::endl; + auto crs = dynamic_cast<const CRS *>(obj.get()); + if (crs && dbContext) { + try { + auto list = crs->getNonDeprecated(NN_NO_CHECK(dbContext)); + if (!list.empty()) { + std::cout << "Alternative non-deprecated CRS:" << std::endl; + } + for (const auto &altCRS : list) { + const auto &ids = altCRS->identifiers(); + if (!ids.empty()) { + std::cout << " " << *(ids[0]->codeSpace()) << ":" + << ids[0]->code() << std::endl; + } + } + } catch (const std::exception &) { + } + } + std::cout << std::endl; + } + auto projStringExportable = nn_dynamic_pointer_cast<IPROJStringExportable>(obj); bool alreadyOutputed = false; @@ -237,7 +265,8 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj, if (crs) { objToExport = nn_dynamic_pointer_cast<IPROJStringExportable>( - crs->createBoundCRSToWGS84IfPossible(dbContext)); + crs->createBoundCRSToWGS84IfPossible(dbContext, + allowPivots)); } if (!objToExport) { objToExport = projStringExportable; @@ -372,7 +401,8 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj, std::shared_ptr<IWKTExportable> objToExport; if (crs) { objToExport = nn_dynamic_pointer_cast<IWKTExportable>( - crs->createBoundCRSToWGS84IfPossible(dbContext)); + crs->createBoundCRSToWGS84IfPossible(dbContext, + allowPivots)); } if (!objToExport) { objToExport = wktExportable; @@ -480,7 +510,7 @@ static void outputOperations( const std::string &authority, bool usePROJGridAlternatives, bool showSuperseded, const OutputOptions &outputOpt, bool summary) { auto sourceObj = - buildObject(dbContext, sourceCRSStr, true, "source CRS", false); + buildObject(dbContext, sourceCRSStr, true, "source CRS", false, false); auto sourceCRS = nn_dynamic_pointer_cast<CRS>(sourceObj); if (!sourceCRS) { std::cerr << "source CRS string is not a CRS" << std::endl; @@ -488,7 +518,7 @@ static void outputOperations( } auto targetObj = - buildObject(dbContext, targetCRSStr, true, "target CRS", false); + buildObject(dbContext, targetCRSStr, true, "target CRS", false, false); auto targetCRS = nn_dynamic_pointer_cast<CRS>(targetObj); if (!targetCRS) { std::cerr << "target CRS string is not a CRS" << std::endl; @@ -519,7 +549,7 @@ static void outputOperations( std::exit(1); } if (outputOpt.quiet && !list.empty()) { - outputObject(dbContext, list[0], outputOpt); + outputObject(dbContext, list[0], allowPivots, outputOpt); return; } if (summary) { @@ -545,7 +575,7 @@ static void outputOperations( } outputOperationSummary(op); std::cout << std::endl; - outputObject(dbContext, op, outputOpt); + outputObject(dbContext, op, allowPivots, outputOpt); } } } @@ -880,7 +910,7 @@ int main(int argc, char **argv) { if (!user_string.empty()) { auto obj(buildObject(dbContext, user_string, kindIsCRS, "input string", - buildBoundCRSToWGS84)); + buildBoundCRSToWGS84, allowPivots)); if (guessDialect) { auto dialect = WKTParser().guessDialect(user_string); std::cout << "Guessed WKT dialect: "; @@ -897,7 +927,7 @@ int main(int argc, char **argv) { } std::cout << std::endl; } - outputObject(dbContext, obj, outputOpt); + outputObject(dbContext, obj, allowPivots, outputOpt); if (identify) { auto crs = dynamic_cast<CRS *>(obj.get()); if (crs) { diff --git a/src/util.cpp b/src/util.cpp index b3a5149d..ac6357a2 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -239,7 +239,18 @@ ArrayOfBaseObjectNNPtr ArrayOfBaseObject::create() { //! @cond Doxygen_Suppress struct PropertyMap::Private { - std::map<std::string, BaseObjectNNPtr> map_{}; + std::list<std::pair<std::string, BaseObjectNNPtr>> list_{}; + + // cppcheck-suppress functionStatic + void set(const std::string &key, const BoxedValueNNPtr &val) { + for (auto &pair : list_) { + if (pair.first == key) { + pair.second = val; + return; + } + } + list_.emplace_back(key, val); + } }; //! @endcond @@ -263,15 +274,13 @@ PropertyMap::~PropertyMap() = default; // --------------------------------------------------------------------------- //! @cond Doxygen_Suppress -std::map<std::string, BaseObjectNNPtr>::iterator -PropertyMap::find(const std::string &key) const { - return d->map_.find(key); -} - -// --------------------------------------------------------------------------- - -std::map<std::string, BaseObjectNNPtr>::iterator PropertyMap::end() const { - return d->map_.end(); +const BaseObjectNNPtr *PropertyMap::get(const std::string &key) const { + for (const auto &pair : d->list_) { + if (pair.first == key) { + return &(pair.second); + } + } + return nullptr; } //! @endcond @@ -280,26 +289,13 @@ std::map<std::string, BaseObjectNNPtr>::iterator PropertyMap::end() const { /** \brief Set a BaseObjectNNPtr as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const BaseObjectNNPtr &val) { - auto iter = d->map_.find(key); - if (iter != d->map_.end()) { - iter->second = val; - } else { - d->map_.insert(std::pair<std::string, BaseObjectNNPtr>(key, val)); - } - return *this; -} - -// --------------------------------------------------------------------------- - -/** \brief Set a BoxedValue as the value of a key. */ -PropertyMap &PropertyMap::set(const std::string &key, const BoxedValue &val) { - auto iter = d->map_.find(key); - if (iter != d->map_.end()) { - iter->second = util::nn_make_shared<BoxedValue>(val); - } else { - d->map_.insert(std::pair<std::string, BaseObjectNNPtr>( - key, util::nn_make_shared<BoxedValue>(val))); + for (auto &pair : d->list_) { + if (pair.first == key) { + pair.second = val; + return *this; + } } + d->list_.emplace_back(key, val); return *this; } @@ -307,28 +303,32 @@ PropertyMap &PropertyMap::set(const std::string &key, const BoxedValue &val) { /** \brief Set a string as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const std::string &val) { - return set(key, BoxedValue(val)); + d->set(key, util::nn_make_shared<BoxedValue>(val)); + return *this; } // --------------------------------------------------------------------------- /** \brief Set a string as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, const char *val) { - return set(key, BoxedValue(val)); + d->set(key, util::nn_make_shared<BoxedValue>(val)); + return *this; } // --------------------------------------------------------------------------- /** \brief Set a integer as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, int val) { - return set(key, BoxedValue(val)); + d->set(key, util::nn_make_shared<BoxedValue>(val)); + return *this; } // --------------------------------------------------------------------------- /** \brief Set a boolean as the value of a key. */ PropertyMap &PropertyMap::set(const std::string &key, bool val) { - return set(key, BoxedValue(val)); + d->set(key, util::nn_make_shared<BoxedValue>(val)); + return *this; } // --------------------------------------------------------------------------- @@ -350,16 +350,38 @@ bool PropertyMap::getStringValue( const std::string &key, std::string &outVal) const // throw(InvalidValueTypeException) { - auto oIter = d->map_.find(key); - if (oIter == d->map_.end()) { - return false; + for (const auto &pair : d->list_) { + if (pair.first == key) { + auto genVal = dynamic_cast<const BoxedValue *>(pair.second.get()); + if (genVal && genVal->type() == BoxedValue::Type::STRING) { + outVal = genVal->stringValue(); + return true; + } + throw InvalidValueTypeException("Invalid value type for " + key); + } } - auto genVal = dynamic_cast<const BoxedValue *>(oIter->second.get()); - if (genVal && genVal->type() == BoxedValue::Type::STRING) { - outVal = genVal->stringValue(); - return true; + return false; +} +//! @endcond + +// --------------------------------------------------------------------------- + +//! @cond Doxygen_Suppress +bool PropertyMap::getStringValue( + const std::string &key, + optional<std::string> &outVal) const // throw(InvalidValueTypeException) +{ + for (const auto &pair : d->list_) { + if (pair.first == key) { + auto genVal = dynamic_cast<const BoxedValue *>(pair.second.get()); + if (genVal && genVal->type() == BoxedValue::Type::STRING) { + outVal = genVal->stringValue(); + return true; + } + throw InvalidValueTypeException("Invalid value type for " + key); + } } - throw InvalidValueTypeException("Invalid value type for " + key); + return false; } //! @endcond diff --git a/test/cli/testprojinfo b/test/cli/testprojinfo index 90829fdd..ea0dcc2d 100755 --- a/test/cli/testprojinfo +++ b/test/cli/testprojinfo @@ -74,6 +74,10 @@ echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area WRONG:CODE --summary" >> $EXE -s EPSG:4230 -t EPSG:4258 --area WRONG:CODE --summary >>${OUT} 2>&1 echo "" >>${OUT} +echo "Testing deprecated CRS: projinfo EPSG:26591" >> ${OUT} +$EXE EPSG:26591 >>${OUT} 2>&1 +echo "" >>${OUT} + # do 'diff' with distribution results echo "diff ${OUT} with testprojinfo_out.dist" diff -u ${OUT} ${TEST_CLI_DIR}/testprojinfo_out.dist diff --git a/test/cli/testprojinfo_out.dist b/test/cli/testprojinfo_out.dist index f3d4854a..40035489 100644 --- a/test/cli/testprojinfo_out.dist +++ b/test/cli/testprojinfo_out.dist @@ -110,12 +110,11 @@ CONVERSION["UTM zone 31N", ID["EPSG",16031]] Testing projinfo -s NAD27 -t NAD83 --grid-check none --spatial-test intersects --summary -Candidate operations found: 8 +Candidate operations found: 7 DERIVED_FROM(EPSG):1312, NAD27 to NAD83 (3), 1.0 m, Canada DERIVED_FROM(EPSG):1313, NAD27 to NAD83 (4), 1.5 m, Canada - NAD27 DERIVED_FROM(EPSG):1241, NAD27 to NAD83 (1), 0.15 m, USA - CONUS including EEZ DERIVED_FROM(EPSG):1243, NAD27 to NAD83 (2), 0.5 m, USA - Alaska including EEZ -ESRI:108003, NAD_1927_To_NAD_1983_PR_VI, 0.05 m, Caribbean - Puerto Rico and Virgin Islands - onshore unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World EPSG:1462, NAD27 to NAD83 (5), 1.0 m, Canada - Quebec EPSG:1573, NAD27 to NAD83 (6), 1.5 m, Canada - Quebec @@ -306,52 +305,6 @@ COORDINATEOPERATION["NAD27 to NAD83 (2)", ------------------------------------- Operation n°5: -ESRI:108003, NAD_1927_To_NAD_1983_PR_VI, 0.05 m, Caribbean - Puerto Rico and Virgin Islands - onshore - -PROJ string: -+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=hgridshift +grids=prvi +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1 - -WKT2_2015 string: -COORDINATEOPERATION["NAD_1927_To_NAD_1983_PR_VI", - SOURCECRS[ - GEODCRS["NAD27", - DATUM["North American Datum 1927", - ELLIPSOID["Clarke 1866",6378206.4,294.978698213898, - LENGTHUNIT["metre",1]]], - PRIMEM["Greenwich",0, - ANGLEUNIT["degree",0.0174532925199433]], - CS[ellipsoidal,2], - AXIS["geodetic latitude (Lat)",north, - ORDER[1], - ANGLEUNIT["degree",0.0174532925199433]], - AXIS["geodetic longitude (Lon)",east, - ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], - TARGETCRS[ - GEODCRS["NAD83", - DATUM["North American Datum 1983", - ELLIPSOID["GRS 1980",6378137,298.257222101, - LENGTHUNIT["metre",1]]], - PRIMEM["Greenwich",0, - ANGLEUNIT["degree",0.0174532925199433]], - CS[ellipsoidal,2], - AXIS["geodetic latitude (Lat)",north, - ORDER[1], - ANGLEUNIT["degree",0.0174532925199433]], - AXIS["geodetic longitude (Lon)",east, - ORDER[2], - ANGLEUNIT["degree",0.0174532925199433]]]], - METHOD["NTv2", - ID["EPSG",9615]], - PARAMETERFILE["Latitude and longitude difference file","prvi"], - OPERATIONACCURACY[0.05], - AREA["Caribbean - Puerto Rico and Virgin Islands - onshore"], - BBOX[17.62,-67.97,18.78,-64.25], - ID["ESRI",108003]] - -------------------------------------- -Operation n°6: - unknown id, Null geographic offset from NAD27 to NAD83, unknown accuracy, World PROJ string: @@ -405,7 +358,7 @@ COORDINATEOPERATION["Null geographic offset from NAD27 to NAD83", BBOX[-90,-180,90,180]] ------------------------------------- -Operation n°7: +Operation n°6: EPSG:1462, NAD27 to NAD83 (5), 1.0 m, Canada - Quebec @@ -451,7 +404,7 @@ COORDINATEOPERATION["NAD27 to NAD83 (5)", ID["EPSG",1462]] ------------------------------------- -Operation n°8: +Operation n°7: EPSG:1573, NAD27 to NAD83 (6), 1.5 m, Canada - Quebec @@ -525,3 +478,48 @@ No area of use matching provided name Testing projinfo -s EPSG:4230 -t EPSG:4258 --area WRONG:CODE --summary Area of use retrieval failed: area not found +Testing deprecated CRS: projinfo EPSG:26591 +Warning: object is deprecated +Alternative non-deprecated CRS: + EPSG:3003 + +PROJ string: ++proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +inv +proj=longlat +ellps=intl +pm=rome +step +proj=tmerc +lat_0=0 +lon_0=9 +k=0.9996 +x_0=1500000 +y_0=0 +ellps=intl +pm=rome + +WKT2_2015 string: +PROJCRS["Monte Mario (Rome) / Italy zone 1", + BASEGEODCRS["Monte Mario (Rome)", + DATUM["Monte Mario (Rome)", + ELLIPSOID["International 1924",6378388,297, + LENGTHUNIT["metre",1]]], + PRIMEM["Rome",12.4523333333333, + ANGLEUNIT["degree",0.0174532925199433]]], + CONVERSION["Italy zone 1", + METHOD["Transverse Mercator", + ID["EPSG",9807]], + PARAMETER["Latitude of natural origin",0, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8801]], + PARAMETER["Longitude of natural origin",9, + ANGLEUNIT["degree",0.0174532925199433], + ID["EPSG",8802]], + PARAMETER["Scale factor at natural origin",0.9996, + SCALEUNIT["unity",1], + ID["EPSG",8805]], + PARAMETER["False easting",1500000, + LENGTHUNIT["metre",1], + ID["EPSG",8806]], + PARAMETER["False northing",0, + LENGTHUNIT["metre",1], + ID["EPSG",8807]]], + CS[Cartesian,2], + AXIS["easting (X)",east, + ORDER[1], + LENGTHUNIT["metre",1]], + AXIS["northing (Y)",north, + ORDER[2], + LENGTHUNIT["metre",1]], + AREA["Italy - west of 12°E"], + BBOX[36.53,5.94,47.04,12], + ID["EPSG",26591]] + diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp index 42d00fa6..e9782ae8 100644 --- a/test/unit/test_c_api.cpp +++ b/test/unit/test_c_api.cpp @@ -402,7 +402,7 @@ TEST_F(CApi, proj_obj_crs_create_bound_crs_to_WGS84) { ObjectKeeper keeper(crs); ASSERT_NE(crs, nullptr); - auto res = proj_obj_crs_create_bound_crs_to_WGS84(m_ctxt, crs); + auto res = proj_obj_crs_create_bound_crs_to_WGS84(m_ctxt, crs, nullptr); ObjectKeeper keeper_res(res); ASSERT_NE(res, nullptr); @@ -447,7 +447,7 @@ TEST_F(CApi, proj_obj_crs_create_bound_crs_to_WGS84_on_invalid_type) { ObjectKeeper keeper(obj); ASSERT_NE(obj, nullptr); - auto res = proj_obj_crs_create_bound_crs_to_WGS84(m_ctxt, obj); + auto res = proj_obj_crs_create_bound_crs_to_WGS84(m_ctxt, obj, nullptr); ASSERT_EQ(res, nullptr); } @@ -1146,7 +1146,7 @@ TEST_F(CApi, proj_obj_create_operations) { ASSERT_NE(res, nullptr); ObjListKeeper keeper_res(res); - EXPECT_EQ(proj_obj_list_get_count(res), 8); + EXPECT_EQ(proj_obj_list_get_count(res), 7); EXPECT_EQ(proj_obj_list_get(m_ctxt, res, -1), nullptr); EXPECT_EQ(proj_obj_list_get(m_ctxt, res, proj_obj_list_get_count(res)), @@ -1247,7 +1247,7 @@ TEST_F(CApi, proj_obj_create_operations_with_pivot) { // Restrict pivot to JGD2000 { - auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr); + auto ctxt = proj_create_operation_factory_context(m_ctxt, "any"); ASSERT_NE(ctxt, nullptr); ContextKeeper keeper_ctxt(ctxt); @@ -1263,7 +1263,7 @@ TEST_F(CApi, proj_obj_create_operations_with_pivot) { proj_obj_create_operations(m_ctxt, source_crs, target_crs, ctxt); ASSERT_NE(res, nullptr); ObjListKeeper keeper_res(res); - // includes 2 results from ESRI + // includes results from ESRI EXPECT_EQ(proj_obj_list_get_count(res), 5); auto op = proj_obj_list_get(m_ctxt, res, 0); ASSERT_NE(op, nullptr); @@ -2552,4 +2552,37 @@ TEST_F(CApi, proj_obj_convert_conversion_to_other_method) { } } +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_obj_get_non_deprecated) { + auto crs = proj_obj_create_from_database( + m_ctxt, "EPSG", "4226", PJ_OBJ_CATEGORY_CRS, false, nullptr); + ObjectKeeper keeper(crs); + ASSERT_NE(crs, nullptr); + + auto list = proj_obj_get_non_deprecated(m_ctxt, crs); + ASSERT_NE(list, nullptr); + ObjListKeeper keeper_list(list); + EXPECT_EQ(proj_obj_list_get_count(list), 2); +} + +// --------------------------------------------------------------------------- + +TEST_F(CApi, proj_obj_query_geodetic_crs_from_datum) { + { + auto list = proj_obj_query_geodetic_crs_from_datum( + m_ctxt, nullptr, "EPSG", "6326", nullptr); + ASSERT_NE(list, nullptr); + ObjListKeeper keeper_list(list); + EXPECT_GE(proj_obj_list_get_count(list), 3); + } + { + auto list = proj_obj_query_geodetic_crs_from_datum( + m_ctxt, "EPSG", "EPSG", "6326", "geographic 2D"); + ASSERT_NE(list, nullptr); + ObjListKeeper keeper_list(list); + EXPECT_EQ(proj_obj_list_get_count(list), 1); + } +} + } // namespace diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 44b84401..6ed002a9 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -339,6 +339,28 @@ TEST(crs, EPSG_4326_as_WKT1_GDAL_with_axis) { // --------------------------------------------------------------------------- +TEST(crs, EPSG_4326_from_db_as_WKT1_GDAL_with_axis) { + auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto crs = factory->createCoordinateReferenceSystem("4326"); + auto wkt = crs->exportToWKT( + &(WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL) + ->setOutputAxis(WKTFormatter::OutputAxisRule::YES))); + EXPECT_EQ(wkt, "GEOGCS[\"WGS 84\",\n" + " DATUM[\"WGS_1984\",\n" + " SPHEROID[\"WGS 84\",6378137,298.257223563,\n" + " AUTHORITY[\"EPSG\",\"7030\"]],\n" + " AUTHORITY[\"EPSG\",\"6326\"]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " AUTHORITY[\"EPSG\",\"8901\"]],\n" + " UNIT[\"degree\",0.0174532925199433,\n" + " AUTHORITY[\"EPSG\",\"9122\"]],\n" + " AXIS[\"Latitude\",NORTH],\n" + " AXIS[\"Longitude\",EAST],\n" + " AUTHORITY[\"EPSG\",\"4326\"]]"); +} + +// --------------------------------------------------------------------------- + TEST(crs, EPSG_4326_as_WKT1_ESRI_with_database) { auto crs = GeographicCRS::EPSG_4326; WKTFormatterNNPtr f(WKTFormatter::create( @@ -1777,6 +1799,38 @@ TEST(crs, projectedCRS_as_PROJ_string) { // --------------------------------------------------------------------------- +TEST(crs, projectedCRS_Krovak_EPSG_5221_as_PROJ_string) { + auto factory = AuthorityFactory::create(DatabaseContext::create(), "EPSG"); + auto crs = factory->createProjectedCRS("5221"); + // 30deg 17' 17.30311'' = 30.28813975277777776 + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline +step +proj=axisswap +order=2,1 " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +inv +proj=longlat +ellps=bessel +pm=ferro " + "+step +proj=krovak +lat_0=49.5 +lon_0=42.5 " + "+alpha=30.2881397527778 +k=0.9999 +x_0=0 +y_0=0 " + "+ellps=bessel +pm=ferro"); +} + +// --------------------------------------------------------------------------- + +TEST(crs, projectedCRS_Krovak_with_approximate_alpha_as_PROJ_string) { + // 30deg 17' 17.303'' = 30.288139722222223 as used in GDAL WKT1 + auto obj = PROJStringParser().createFromPROJString( + "+proj=krovak +lat_0=49.5 +lon_0=42.5 +alpha=30.28813972222222 " + "+k=0.9999 +x_0=0 +y_0=0 +ellps=bessel +pm=ferro +units=m +no_defs"); + auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj); + EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()), + "+proj=pipeline " + "+step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step +inv +proj=longlat +ellps=bessel +pm=ferro " + "+step +proj=krovak +lat_0=49.5 +lon_0=42.5 " + "+alpha=30.2881397222222 +k=0.9999 +x_0=0 +y_0=0 " + "+ellps=bessel +pm=ferro"); +} + +// --------------------------------------------------------------------------- + TEST(crs, projectedCRS_identify_no_db) { { // Hard-coded case: WGS 84 / UTM. No name @@ -2433,7 +2487,7 @@ TEST(crs, Krovak_North_Orientated_as_WKT1_ESRI) { "PARAMETER[\"False_Northing\",0.0]," "PARAMETER[\"Pseudo_Standard_Parallel_1\",78.5]," "PARAMETER[\"Scale_Factor\",1.0]," - "PARAMETER[\"Azimuth\",30.2881397222222]," + "PARAMETER[\"Azimuth\",30.2881397527778]," "PARAMETER[\"Longitude_Of_Center\",0.0]," "PARAMETER[\"Latitude_Of_Center\",0.0]," "PARAMETER[\"X_Scale\",-1.0]," @@ -2465,7 +2519,7 @@ TEST(crs, Krovak_as_WKT1_ESRI) { "PARAMETER[\"False_Northing\",0.0]," "PARAMETER[\"Pseudo_Standard_Parallel_1\",78.5]," "PARAMETER[\"Scale_Factor\",1.0]," - "PARAMETER[\"Azimuth\",30.2881397222222]," + "PARAMETER[\"Azimuth\",30.2881397527778]," "PARAMETER[\"Longitude_Of_Center\",0.0]," "PARAMETER[\"Latitude_Of_Center\",0.0]," "PARAMETER[\"X_Scale\",1.0]," @@ -3425,9 +3479,8 @@ TEST(crs, boundCRS_crs_link) { EXPECT_TRUE(baseCRS->isEquivalentTo(GeographicCRS::EPSG_4267.get())); EXPECT_TRUE(baseCRS->canonicalBoundCRS() != nullptr); - EXPECT_TRUE( - baseCRS->createBoundCRSToWGS84IfPossible(nullptr)->isEquivalentTo( - baseCRS->canonicalBoundCRS().get())); + EXPECT_TRUE(baseCRS->createBoundCRSToWGS84IfPossible(nullptr, false) + ->isEquivalentTo(baseCRS->canonicalBoundCRS().get())); } { @@ -4726,26 +4779,28 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { auto factory = AuthorityFactory::create(dbContext, "EPSG"); { auto crs_4326 = factory->createCoordinateReferenceSystem("4326"); - EXPECT_EQ(crs_4326->createBoundCRSToWGS84IfPossible(dbContext), + EXPECT_EQ(crs_4326->createBoundCRSToWGS84IfPossible(dbContext, false), crs_4326); } { auto crs_32631 = factory->createCoordinateReferenceSystem("32631"); - EXPECT_EQ(crs_32631->createBoundCRSToWGS84IfPossible(dbContext), + EXPECT_EQ(crs_32631->createBoundCRSToWGS84IfPossible(dbContext, false), crs_32631); } { // Pulkovo 42 East Germany auto crs_5670 = factory->createCoordinateReferenceSystem("5670"); - EXPECT_EQ(crs_5670->createBoundCRSToWGS84IfPossible(dbContext), + EXPECT_EQ(crs_5670->createBoundCRSToWGS84IfPossible(dbContext, false), crs_5670); } { // Pulkovo 42 Romania auto crs_3844 = factory->createCoordinateReferenceSystem("3844"); - auto bound = crs_3844->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = + crs_3844->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs_3844); - EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext), bound); + EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext, false), + bound); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); EXPECT_EQ(boundCRS->exportToPROJString( @@ -4760,9 +4815,11 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { { // Pulkovo 42 Poland auto crs_2171 = factory->createCoordinateReferenceSystem("2171"); - auto bound = crs_2171->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = + crs_2171->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs_2171); - EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext), bound); + EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext, false), + bound); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); EXPECT_EQ(boundCRS->exportToPROJString( @@ -4777,9 +4834,11 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { { // NTF (Paris) auto crs_4807 = factory->createCoordinateReferenceSystem("4807"); - auto bound = crs_4807->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = + crs_4807->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs_4807); - EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext), bound); + EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext, false), + bound); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); EXPECT_EQ(boundCRS->exportToPROJString( @@ -4792,9 +4851,11 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { { // NTF (Paris) / Lambert zone II + NGF-IGN69 height auto crs_7421 = factory->createCoordinateReferenceSystem("7421"); - auto bound = crs_7421->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = + crs_7421->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs_7421); - EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext), bound); + EXPECT_EQ(bound->createBoundCRSToWGS84IfPossible(dbContext, false), + bound); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); EXPECT_EQ(boundCRS->exportToPROJString( @@ -4808,13 +4869,13 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { } { auto crs = createVerticalCRS(); - EXPECT_EQ(crs->createBoundCRSToWGS84IfPossible(dbContext), crs); + EXPECT_EQ(crs->createBoundCRSToWGS84IfPossible(dbContext, false), crs); } { auto factoryIGNF = AuthorityFactory::create(DatabaseContext::create(), "IGNF"); auto crs = factoryIGNF->createCoordinateReferenceSystem("TERA50STEREO"); - auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); @@ -4830,7 +4891,7 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { auto factoryIGNF = AuthorityFactory::create(DatabaseContext::create(), "IGNF"); auto crs = factoryIGNF->createCoordinateReferenceSystem("PGP50"); - auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext); + auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext, false); EXPECT_NE(bound, crs); auto boundCRS = nn_dynamic_pointer_cast<BoundCRS>(bound); ASSERT_TRUE(boundCRS != nullptr); @@ -4841,6 +4902,11 @@ TEST(crs, crs_createBoundCRSToWGS84IfPossible) { "+proj=geocent +ellps=intl " "+towgs84=324.8,153.6,172.1,0,0,0,0 +units=m +no_defs"); } + { + auto crs = factory->createCoordinateReferenceSystem("4269"); // NAD83 + auto bound = crs->createBoundCRSToWGS84IfPossible(dbContext, false); + EXPECT_EQ(bound, crs); + } } // --------------------------------------------------------------------------- @@ -5023,3 +5089,58 @@ TEST(crs, alterParametersLinearUnit) { << wkt; } } + +// --------------------------------------------------------------------------- + +TEST(crs, getNonDeprecated) { + auto dbContext = DatabaseContext::create(); + auto factory = AuthorityFactory::create(dbContext, "EPSG"); + + { + // No id + auto crs = ProjectedCRS::create( + PropertyMap(), GeographicCRS::EPSG_4326, + Conversion::createUTM(PropertyMap(), 31, true), + CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 0); + } + + { + // Non-deprecated + auto crs = factory->createGeodeticCRS("4326"); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 0); + } + + { + // Non supported CRS type + auto crs = BoundCRS::createFromTOWGS84( + createProjected(), std::vector<double>{1, 2, 3, 4, 5, 6, 7}); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 0); + } + + { + auto crs = factory->createGeodeticCRS("4226"); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 2); + } + + { + auto crs = factory->createProjectedCRS("26591"); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 1); + } + + { + auto crs = factory->createVerticalCRS("5704"); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 1); + } + { + auto crs = factory->createCompoundCRS("7401"); + auto list = crs->getNonDeprecated(dbContext); + ASSERT_EQ(list.size(), 1); + } +} diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp index 057f1717..e5734cd1 100644 --- a/test/unit/test_operation.cpp +++ b/test/unit/test_operation.cpp @@ -5941,7 +5941,7 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { AuthorityFactory::create(DatabaseContext::create(), "EPSG") ->createCoordinateReferenceSystem("4326"), ctxt); - ASSERT_GE(list2.size(), 4U); + ASSERT_GE(list2.size(), 3U); EXPECT_EQ(replaceAll(list2[0]->exportToPROJString( PROJStringFormatter::create().get()), @@ -5950,10 +5950,8 @@ TEST(operation, IGNF_LAMB1_TO_EPSG_4326) { // The second entry in list2 (list2[1]) uses the // weird +pm=2.33720833333333 from "NTF (Paris) to NTF (2)" - // and the third one uses the ESRI geographic offset method with another - // value - // so skip to the 4th method - EXPECT_EQ(replaceAll(list2[3]->exportToPROJString( + // so skip to the 3th method + EXPECT_EQ(replaceAll(list2[2]->exportToPROJString( PROJStringFormatter::create().get()), "0.999877341", "0.99987734"), list[1]->exportToPROJString(PROJStringFormatter::create().get())); |
