From b9793738b6e15579d7a0979467bcaec2aeeaf06d Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Fri, 11 Oct 2024 15:20:48 +0200 Subject: [PATCH] JSON TileMatrixSet parser: accept crs.uri and crs.wkt encodings Fixes #10989 --- autotest/cpp/test_gdal.cpp | 243 +++++++++++++++++++++++++++++++++++++ gcore/tilematrixset.cpp | 37 +++++- gcore/tilematrixset.hpp | 3 +- 3 files changed, 280 insertions(+), 3 deletions(-) diff --git a/autotest/cpp/test_gdal.cpp b/autotest/cpp/test_gdal.cpp index f05d79e9f2ae..45ebfb4ab870 100644 --- a/autotest/cpp/test_gdal.cpp +++ b/autotest/cpp/test_gdal.cpp @@ -2548,6 +2548,249 @@ TEST_F(test_gdal, TileMatrixSet) } } } + + // TMS v2 with crs.uri + { + auto poTMS = gdal::TileMatrixSet::parse( + "{" + " \"id\" : \"test\"," + " \"title\" : \"test\"," + " \"uri\" : " + "\"http://www.opengis.net/def/tilematrixset/OGC/1.0/test\"," + " \"crs\" : {\"uri\": " + "\"http://www.opengis.net/def/crs/EPSG/0/4326\"}," + " \"orderedAxes\" : [" + " \"Lat\"," + " \"Lon\"" + " ]," + " \"wellKnownScaleSet\" : " + "\"http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad\"," + " \"tileMatrices\" : [" + " {" + " \"id\" : \"0\"," + " \"scaleDenominator\" : 139770566.0071794390678," + " \"cellSize\" : 0.3515625," + " \"cornerOfOrigin\" : \"topLeft\"," + " \"pointOfOrigin\" : [ 90, -180 ]," + " \"matrixWidth\" : 4," + " \"matrixHeight\" : 2," + " \"tileWidth\" : 256," + " \"tileHeight\" : 256" + " }" + " ]" + "}"); + EXPECT_TRUE(poTMS != nullptr); + if (poTMS) + { + EXPECT_EQ(poTMS->crs(), + "http://www.opengis.net/def/crs/EPSG/0/4326"); + } + } + + // TMS v2 with crs.wkt + { + auto poTMS = gdal::TileMatrixSet::parse( + "{" + " \"id\" : \"test\"," + " \"title\" : \"test\"," + " \"uri\" : " + "\"http://www.opengis.net/def/tilematrixset/OGC/1.0/test\"," + " \"crs\" : {\"wkt\": \"GEOGCRS[\\\"WGS 84\\\"," + "ENSEMBLE[\\\"World Geodetic System 1984 ensemble\\\"," + "MEMBER[\\\"World Geodetic System 1984 (Transit)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G730)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G873)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G1150)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G1674)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G1762)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G2139)\\\"]," + "MEMBER[\\\"World Geodetic System 1984 (G2296)\\\"]," + "ELLIPSOID[\\\"WGS 84\\\",6378137,298.257223563," + "LENGTHUNIT[\\\"metre\\\",1]]," + "ENSEMBLEACCURACY[2.0]]," + "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]]," + "USAGE[" + "SCOPE[\\\"Horizontal component of 3D system.\\\"]," + "AREA[\\\"World.\\\"]," + "BBOX[-90,-180,90,180]]," + "ID[\\\"EPSG\\\",4326]]\" }," + " \"orderedAxes\" : [" + " \"Lat\"," + " \"Lon\"" + " ]," + " \"wellKnownScaleSet\" : " + "\"http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad\"," + " \"tileMatrices\" : [" + " {" + " \"id\" : \"0\"," + " \"scaleDenominator\" : 139770566.0071794390678," + " \"cellSize\" : 0.3515625," + " \"cornerOfOrigin\" : \"topLeft\"," + " \"pointOfOrigin\" : [ 90, -180 ]," + " \"matrixWidth\" : 4," + " \"matrixHeight\" : 2," + " \"tileWidth\" : 256," + " \"tileHeight\" : 256" + " }" + " ]" + "}"); + EXPECT_TRUE(poTMS != nullptr); + if (poTMS) + { + EXPECT_TRUE( + STARTS_WITH(poTMS->crs().c_str(), "GEOGCRS[\"WGS 84\"")); + } + } + + // TMS v2 with crs.wkt with JSON content + { + auto poTMS = gdal::TileMatrixSet::parse( + "{" + " \"id\" : \"test\"," + " \"title\" : \"test\"," + " \"uri\" : " + "\"http://www.opengis.net/def/tilematrixset/OGC/1.0/test\"," + " \"crs\" : {\"wkt\": " + "{" + " \"type\": \"GeographicCRS\"," + " \"name\": \"WGS 84\"," + " \"datum_ensemble\": {" + " \"name\": \"World Geodetic System 1984 ensemble\"," + " \"members\": [" + " {" + " \"name\": \"World Geodetic System 1984 (Transit)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1166" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G730)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1152" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G873)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1153" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G1150)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1154" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G1674)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1155" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G1762)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1156" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G2139)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1309" + " }" + " }," + " {" + " \"name\": \"World Geodetic System 1984 (G2296)\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 1383" + " }" + " }" + " ]," + " \"ellipsoid\": {" + " \"name\": \"WGS 84\"," + " \"semi_major_axis\": 6378137," + " \"inverse_flattening\": 298.257223563" + " }," + " \"accuracy\": \"2.0\"," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 6326" + " }" + " }," + " \"coordinate_system\": {" + " \"subtype\": \"ellipsoidal\"," + " \"axis\": [" + " {" + " \"name\": \"Geodetic latitude\"," + " \"abbreviation\": \"Lat\"," + " \"direction\": \"north\"," + " \"unit\": \"degree\"" + " }," + " {" + " \"name\": \"Geodetic longitude\"," + " \"abbreviation\": \"Lon\"," + " \"direction\": \"east\"," + " \"unit\": \"degree\"" + " }" + " ]" + " }," + " \"scope\": \"Horizontal component of 3D system.\"," + " \"area\": \"World.\"," + " \"bbox\": {" + " \"south_latitude\": -90," + " \"west_longitude\": -180," + " \"north_latitude\": 90," + " \"east_longitude\": 180" + " }," + " \"id\": {" + " \"authority\": \"EPSG\"," + " \"code\": 4326" + " }" + "}" + "}," + " \"orderedAxes\" : [" + " \"Lat\"," + " \"Lon\"" + " ]," + " \"wellKnownScaleSet\" : " + "\"http://www.opengis.net/def/wkss/OGC/1.0/GoogleCRS84Quad\"," + " \"tileMatrices\" : [" + " {" + " \"id\" : \"0\"," + " \"scaleDenominator\" : 139770566.0071794390678," + " \"cellSize\" : 0.3515625," + " \"cornerOfOrigin\" : \"topLeft\"," + " \"pointOfOrigin\" : [ 90, -180 ]," + " \"matrixWidth\" : 4," + " \"matrixHeight\" : 2," + " \"tileWidth\" : 256," + " \"tileHeight\" : 256" + " }" + " ]" + "}"); + EXPECT_TRUE(poTMS != nullptr); + if (poTMS) + { + EXPECT_TRUE(STARTS_WITH(poTMS->crs().c_str(), + "{ \"type\": \"GeographicCRS\"")); + } + } } // Test that PCIDSK GetMetadataItem() return is stable diff --git a/gcore/tilematrixset.cpp b/gcore/tilematrixset.cpp index a6cd7829dd14..21cd649543e6 100644 --- a/gcore/tilematrixset.cpp +++ b/gcore/tilematrixset.cpp @@ -197,13 +197,46 @@ std::unique_ptr TileMatrixSet::parse(const char *fileOrDef) return nullptr; } + const auto GetCRS = [](const CPLJSONObject &j) + { + if (j.IsValid()) + { + if (j.GetType() == CPLJSONObject::Type::String) + return j.ToString(); + + else if (j.GetType() == CPLJSONObject::Type::Object) + { + const std::string osURI = j.GetString("uri"); + if (!osURI.empty()) + return osURI; + + // Quite a bit of confusion around wkt. + // See https://github.com/opengeospatial/ogcapi-tiles/issues/170 + const auto jWKT = j.GetObj("wkt"); + if (jWKT.GetType() == CPLJSONObject::Type::String) + { + const std::string osWKT = jWKT.ToString(); + if (!osWKT.empty()) + return osWKT; + } + else if (jWKT.GetType() == CPLJSONObject::Type::Object) + { + const std::string osWKT = jWKT.ToString(); + if (!osWKT.empty()) + return osWKT; + } + } + } + return std::string(); + }; + poTMS->mIdentifier = oRoot.GetString(bIsTMSv2 ? "id" : "identifier"); poTMS->mTitle = oRoot.GetString("title"); poTMS->mAbstract = oRoot.GetString(bIsTMSv2 ? "description" : "abstract"); const auto oBbox = oRoot.GetObj("boundingBox"); if (oBbox.IsValid()) { - poTMS->mBbox.mCrs = oBbox.GetString("crs"); + poTMS->mBbox.mCrs = GetCRS(oBbox.GetObj("crs")); const auto oLowerCorner = oBbox.GetArray("lowerCorner"); if (oLowerCorner.IsValid() && oLowerCorner.Size() == 2) { @@ -217,7 +250,7 @@ std::unique_ptr TileMatrixSet::parse(const char *fileOrDef) poTMS->mBbox.mUpperCornerY = oUpperCorner[1].ToDouble(NaN); } } - poTMS->mCrs = oRoot.GetString(bIsTMSv2 ? "crs" : "supportedCRS"); + poTMS->mCrs = GetCRS(oRoot.GetObj(bIsTMSv2 ? "crs" : "supportedCRS")); poTMS->mWellKnownScaleSet = oRoot.GetString("wellKnownScaleSet"); OGRSpatialReference oCrs; diff --git a/gcore/tilematrixset.hpp b/gcore/tilematrixset.hpp index f0ade1977e0b..431767885e46 100644 --- a/gcore/tilematrixset.hpp +++ b/gcore/tilematrixset.hpp @@ -49,7 +49,7 @@ class CPL_DLL TileMatrixSet struct CPL_DLL BoundingBox { - std::string mCrs{}; + std::string mCrs{}; //! Can be a URL, a URI, a WKT or PROJJSON string. double mLowerCornerX{NaN}; double mLowerCornerY{NaN}; double mUpperCornerX{NaN}; @@ -61,6 +61,7 @@ class CPL_DLL TileMatrixSet return mBbox; } + //! Can be a URL, a URI, a WKT or PROJJSON string. const std::string &crs() const { return mCrs;