From 5cc1095970127f8df5c4f636b1ce782829510fa0 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 9 Dec 2019 09:08:13 +0100 Subject: CRS identification: use case insensitive comparison for authority name (fixes #1779) --- test/unit/test_crs.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 427fd741..e896853f 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -2281,6 +2281,38 @@ TEST(crs, projectedCRS_identify_db) { // --------------------------------------------------------------------------- +TEST(crs, projectedCRS_identify_wrong_auth_name_case) { + auto dbContext = DatabaseContext::create(); + auto factoryAnonymous = AuthorityFactory::create(dbContext, std::string()); + auto obj = + WKTParser() + .attachDatabaseContext(dbContext) + .setStrict(false) + .createFromWKT( + "PROJCS[\"World_Cylindrical_Equal_Area\"," + "GEOGCS[\"GCS_WGS_1984\"," + "DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\"," + "6378137.0,298.257223563]]," + "PRIMEM[\"Greenwich\",0.0]," + "UNIT[\"Degree\",0.0174532925199433]]," + "PROJECTION[\"Cylindrical_Equal_Area\"]," + "PARAMETER[\"False_Easting\",0.0]," + "PARAMETER[\"False_Northing\",0.0]," + "PARAMETER[\"Central_Meridian\",0.0]," + "PARAMETER[\"Standard_Parallel_1\",0.0],UNIT[\"Meter\",1.0]," + "AUTHORITY[\"Esri\",54034]]"); // should be ESRI all caps + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + auto res = crs->identify(factoryAnonymous); + ASSERT_EQ(res.size(), 1U); + const auto &ids = res.front().first->identifiers(); + ASSERT_EQ(ids.size(), 1U); + EXPECT_EQ(*(ids.front()->codeSpace()), "ESRI"); + EXPECT_EQ(ids.front()->code(), "54034"); +} + +// --------------------------------------------------------------------------- + TEST(crs, mercator_1SP_as_WKT1_ESRI) { auto obj = PROJStringParser().createFromPROJString( -- cgit v1.2.3 From a2b2663d2a35c9ff6ff56e9348f166a99cc618a4 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 13 Dec 2019 11:04:00 +0100 Subject: normalizeForVisualization(): address EPSG:32661 "WGS 84 / UPS North (N,E)" and EPSG:32761 "WGS 84 / UPS South (N,E)" Fixes https://github.com/qgis/QGIS/issues/33077 --- test/unit/test_crs.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index e896853f..33d67e0a 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -943,6 +943,16 @@ TEST(crs, EPSG_32661_projected_north_pole_north_east) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -964,6 +974,16 @@ TEST(crs, EPSG_5041_projected_north_pole_east_north) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -985,6 +1005,16 @@ TEST(crs, EPSG_32761_projected_south_pole_north_east) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=-90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- @@ -1006,6 +1036,16 @@ TEST(crs, EPSG_5042_projected_south_pole_east_north) { ASSERT_TRUE(op != nullptr); EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()), proj_string); + + auto opNormalized = op->normalizeForVisualization(); + auto proj_string_normalized = + "+proj=pipeline +step +proj=unitconvert +xy_in=deg +xy_out=rad " + "+step " + "+proj=stere +lat_0=-90 +lon_0=0 +k=0.994 +x_0=2000000 +y_0=2000000 " + "+ellps=WGS84"; + EXPECT_EQ( + opNormalized->exportToPROJString(PROJStringFormatter::create().get()), + proj_string_normalized); } // --------------------------------------------------------------------------- -- cgit v1.2.3 From a68c146d7f3c1efb0f42b46c708a0a195e51a2ff Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 16 Dec 2019 13:24:11 +0100 Subject: BoundCRS::identify(): improvements to discard CRS that aren't relevant (fixes #1801) Fix for ``` projinfo --identify "+proj=utm +zone=48 +a=6377276.345 +b=6356075.41314024 +towgs84=198,881,317,0,0,0,0 +units=m +no_defs +type=crs" ``` to only return BoundCRS of EPSG:3148: 70 % Previously it also returned EPSG:23948 and EPSG:24048 whose projected CRS-only parts where likely matches, but those 2 CRSs don't have a +towgs84=198,881,317,0,0,0,0, so discard them. --- test/unit/test_crs.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 33d67e0a..df1c257d 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -3825,6 +3825,21 @@ TEST(crs, boundCRS_identify_db) { WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); EXPECT_TRUE(wkt.find("32122") != std::string::npos) << wkt; } + + { + // Identify from a PROJ string with +towgs84 + auto obj = PROJStringParser().createFromPROJString( + "+proj=utm +zone=48 +a=6377276.345 +b=6356075.41314024 " + "+towgs84=198,881,317,0,0,0,0 +units=m +no_defs +type=crs"); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + auto res = crs->identify(factoryEPSG); + ASSERT_EQ(res.size(), 1U); + auto boundCRS = dynamic_cast(res.front().first.get()); + ASSERT_TRUE(boundCRS != nullptr); + EXPECT_EQ(boundCRS->baseCRS()->getEPSGCode(), 3148); + EXPECT_EQ(res.front().second, 70); + } } // --------------------------------------------------------------------------- -- cgit v1.2.3 From bd9ecbacf575a1926fb8e223c4add1a4cc45d7f3 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Mon, 16 Dec 2019 15:23:22 +0100 Subject: identify(): take into datum name aliases (fixes #1800) --- test/unit/test_crs.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index df1c257d..88a7813a 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -57,7 +57,8 @@ namespace { struct UnrelatedObject : public IComparable { UnrelatedObject() = default; - bool _isEquivalentTo(const IComparable *, Criterion) const override { + bool _isEquivalentTo(const IComparable *, Criterion, + const DatabaseContextPtr &) const override { assert(false); return false; } @@ -1551,6 +1552,75 @@ TEST(crs, geodeticcrs_identify_db) { EXPECT_EQ(res.front().first->identifiers()[0]->code(), "4326"); EXPECT_EQ(res.front().second, 70); } + + { + // Identify by code, but datum name is an alias of the official one + auto wkt = "GEOGCRS[\"GDA2020\",\n" + " DATUM[\"GDA2020\",\n" + " ELLIPSOID[\"GRS_1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"Degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " ID[\"EPSG\",7844]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + + auto allFactory = AuthorityFactory::create(dbContext, std::string()); + auto res = crs->identify(allFactory); + ASSERT_EQ(res.size(), 1U); + ASSERT_TRUE(!res.front().first->identifiers().empty()); + EXPECT_EQ(*res.front().first->identifiers()[0]->codeSpace(), "EPSG"); + EXPECT_EQ(res.front().first->identifiers()[0]->code(), "7844"); + EXPECT_EQ(res.front().second, 100); + + EXPECT_TRUE(crs->_isEquivalentTo(res.front().first.get(), + IComparable::Criterion::EQUIVALENT, + dbContext)); + EXPECT_TRUE(res.front().first->_isEquivalentTo( + crs.get(), IComparable::Criterion::EQUIVALENT, dbContext)); + } + + { + // Identify by name, but datum name is an alias of the official one + auto wkt = "GEOGCRS[\"GDA2020\",\n" + " DATUM[\"GDA2020\",\n" + " ELLIPSOID[\"GRS_1980\",6378137,298.257222101,\n" + " LENGTHUNIT[\"metre\",1]]],\n" + " PRIMEM[\"Greenwich\",0,\n" + " ANGLEUNIT[\"Degree\",0.0174532925199433]],\n" + " CS[ellipsoidal,2],\n" + " AXIS[\"geodetic latitude (Lat)\",north,\n" + " ORDER[1],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]],\n" + " AXIS[\"geodetic longitude (Lon)\",east,\n" + " ORDER[2],\n" + " ANGLEUNIT[\"degree\",0.0174532925199433]]]"; + auto obj = WKTParser().createFromWKT(wkt); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + + auto allFactory = AuthorityFactory::create(dbContext, std::string()); + auto res = crs->identify(allFactory); + ASSERT_EQ(res.size(), 1U); + ASSERT_TRUE(!res.front().first->identifiers().empty()); + EXPECT_EQ(*res.front().first->identifiers()[0]->codeSpace(), "EPSG"); + EXPECT_EQ(res.front().first->identifiers()[0]->code(), "7844"); + EXPECT_EQ(res.front().second, 100); + + EXPECT_TRUE(crs->_isEquivalentTo(res.front().first.get(), + IComparable::Criterion::EQUIVALENT, + dbContext)); + EXPECT_TRUE(res.front().first->_isEquivalentTo( + crs.get(), IComparable::Criterion::EQUIVALENT, dbContext)); + } } // --------------------------------------------------------------------------- -- cgit v1.2.3 From 30205fef7c4a73342a384eca40893a52ea9d6794 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 10 Jan 2020 19:04:32 +0100 Subject: CompoundCRS::identify(): avoid exception when horiz/vertical part is a BoundCRS The exception only affects C++ users. It was caught by the C layer. --- test/unit/test_crs.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 88a7813a..82eb4562 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -3493,6 +3493,17 @@ TEST(crs, compoundCRS_identify_db) { EXPECT_EQ(res.front().first->getEPSGCode(), 8769); EXPECT_EQ(res.front().second, 70); } + { + auto obj = PROJStringParser().createFromPROJString( + "+proj=tmerc +lat_0=0 +lon_0=72.05 +k=1 +x_0=3500000 " + "+y_0=-5811057.63 +ellps=krass " + "+towgs84=23.57,-140.95,-79.8,0,-0.35,-0.79,-0.22 " + "+geoidgrids=egm08_25.gtx +units=m +no_defs +type=crs"); + auto crs = nn_dynamic_pointer_cast(obj); + ASSERT_TRUE(crs != nullptr); + // Just check we don't get an exception + crs->identify(factory); + } } // --------------------------------------------------------------------------- -- cgit v1.2.3 From d2674e044505d0fcee920f5c0315d8db6b842caa Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Sat, 11 Jan 2020 00:25:51 +0100 Subject: WKT1_GDAL export: limit datum name massaging to names matching EPSG (fixes #1835) --- test/unit/test_crs.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'test/unit/test_crs.cpp') diff --git a/test/unit/test_crs.cpp b/test/unit/test_crs.cpp index 88a7813a..8ecb8540 100644 --- a/test/unit/test_crs.cpp +++ b/test/unit/test_crs.cpp @@ -756,7 +756,9 @@ TEST(crs, EPSG_27561_projected_with_geodetic_in_grad_as_PROJ_string_and_WKT1) { nn_crs->DerivedCRS::isEquivalentTo(createUnrelatedObject().get())); auto wkt1 = crs->exportToWKT( - WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL, + DatabaseContext::create()) + .get()); EXPECT_EQ( wkt1, "PROJCS[\"NTF (Paris) / Lambert Nord France\",\n" @@ -857,7 +859,9 @@ TEST(crs, EPSG_2222_projected_unit_foot_as_PROJ_string_and_WKT1) { "+x_0=213360 +y_0=0 +datum=NAD83 +units=ft +no_defs +type=crs"); auto wkt1 = crs->exportToWKT( - WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL, + DatabaseContext::create()) + .get()); EXPECT_EQ(wkt1, "PROJCS[\"NAD83 / Arizona East (ft)\",\n" " GEOGCS[\"NAD83\",\n" @@ -904,11 +908,13 @@ TEST(crs, projected_with_parameter_unit_different_than_cs_unit_as_WKT1) { ASSERT_TRUE(crs != nullptr); auto wkt1 = crs->exportToWKT( - WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()); + WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL, + DatabaseContext::create()) + .get()); EXPECT_EQ(wkt1, "PROJCS[\"unknown\",\n" " GEOGCS[\"unknown\",\n" - " DATUM[\"Unknown_based_on_GRS80_ellipsoid\",\n" + " DATUM[\"Unknown based on GRS80 ellipsoid\",\n" " SPHEROID[\"GRS 1980\",6378137,298.257222101]],\n" " PRIMEM[\"Greenwich\",0],\n" " UNIT[\"degree\",0.0174532925199433,\n" -- cgit v1.2.3