diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/apps/projinfo.cpp | 283 | ||||
| -rw-r--r-- | src/iso19111/factory.cpp | 28 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 4 |
3 files changed, 218 insertions, 97 deletions
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp index d2fb6911..404007e5 100644 --- a/src/apps/projinfo.cpp +++ b/src/apps/projinfo.cpp @@ -33,6 +33,7 @@ #include <cstdlib> #include <fstream> // std::ifstream #include <iostream> +#include <set> #include <utility> #include "proj.h" @@ -112,6 +113,7 @@ static void usage() { << " [--output-id AUTH:CODE]" << std::endl << " [--c-ify] [--single-line]" << std::endl << " --searchpaths | --remote-data |" << std::endl + << " --list-crs [list-crs-filter] |" << std::endl << " --dump-db-structure [{object_definition} | " "{object_reference}] |" << std::endl @@ -127,6 +129,13 @@ static void usage() { "by '-' to disable them" << std::endl; std::cerr << std::endl; + std::cerr << "list-crs-filter is a comma separated combination of: " + "allow_deprecated,geodetic,geocentric," + << std::endl; + std::cerr + << "geographic,geographic_2d,geographic_3d,vertical,projected,compound" + << std::endl; + std::cerr << std::endl; std::cerr << "{object_definition} might be a PROJ string, a WKT string, " "a AUTHORITY:CODE, or urn:ogc:def:OBJECT_TYPE:AUTHORITY::CODE" << std::endl; @@ -157,6 +166,92 @@ static std::string c_ify_string(const std::string &str) { // --------------------------------------------------------------------------- +static ExtentPtr makeBboxFilter(DatabaseContextPtr dbContext, + const std::string &bboxStr, + const std::string &area, + bool errorIfSeveralAreaMatches) { + ExtentPtr bboxFilter = nullptr; + if (!bboxStr.empty()) { + auto bbox(split(bboxStr, ',')); + if (bbox.size() != 4) { + std::cerr << "Incorrect number of values for option --bbox: " + << bboxStr << std::endl; + usage(); + } + try { + std::vector<double> bboxValues = { + c_locale_stod(bbox[0]), c_locale_stod(bbox[1]), + c_locale_stod(bbox[2]), c_locale_stod(bbox[3])}; + bboxFilter = Extent::createFromBBOX(bboxValues[0], bboxValues[1], + bboxValues[2], bboxValues[3]) + .as_nullable(); + } catch (const std::exception &e) { + std::cerr << "Invalid value for option --bbox: " << bboxStr << ", " + << e.what() << std::endl; + usage(); + } + } else if (!area.empty()) { + assert(dbContext); + try { + if (area.find(' ') == std::string::npos && + area.find(':') != std::string::npos) { + auto tokens = split(area, ':'); + if (tokens.size() == 2) { + const std::string &areaAuth = tokens[0]; + const std::string &areaCode = tokens[1]; + bboxFilter = AuthorityFactory::create( + NN_NO_CHECK(dbContext), areaAuth) + ->createExtent(areaCode) + .as_nullable(); + } + } + if (!bboxFilter) { + auto authFactory = AuthorityFactory::create( + NN_NO_CHECK(dbContext), std::string()); + auto res = authFactory->listAreaOfUseFromName(area, false); + if (res.size() == 1) { + bboxFilter = AuthorityFactory::create( + NN_NO_CHECK(dbContext), res.front().first) + ->createExtent(res.front().second) + .as_nullable(); + } else { + res = authFactory->listAreaOfUseFromName(area, true); + if (res.size() == 1) { + bboxFilter = + AuthorityFactory::create(NN_NO_CHECK(dbContext), + res.front().first) + ->createExtent(res.front().second) + .as_nullable(); + } else if (res.empty()) { + std::cerr << "No area of use matching provided name" + << std::endl; + std::exit(1); + } else if (errorIfSeveralAreaMatches) { + std::cerr << "Several candidates area of use " + "matching provided name :" + << std::endl; + for (const auto &candidate : res) { + auto obj = + AuthorityFactory::create(NN_NO_CHECK(dbContext), + candidate.first) + ->createExtent(candidate.second); + std::cerr << " " << candidate.first << ":" + << candidate.second << " : " + << *obj->description() << std::endl; + } + std::exit(1); + } + } + } + } catch (const std::exception &e) { + std::cerr << "Area of use retrieval failed: " << e.what() + << std::endl; + std::exit(1); + } + } + return bboxFilter; +} + static BaseObjectNNPtr buildObject( DatabaseContextPtr dbContext, const std::string &user_string, const std::string &kind, const std::string &context, @@ -845,7 +940,7 @@ int main(int argc, char **argv) { OutputOptions outputOpt; std::string objectKind; bool summary = false; - ExtentPtr bboxFilter = nullptr; + std::string bboxStr; std::string area; bool spatialCriterionExplicitlySpecified = false; CoordinateOperationContext::SpatialCriterion spatialCriterion = @@ -873,6 +968,8 @@ int main(int argc, char **argv) { double minimumAccuracy = -1; bool outputAll = false; bool dumpDbStructure = false; + std::string listCRS; + bool listCRSSpecified = false; for (int i = 1; i < argc; i++) { std::string arg(argv[i]); @@ -977,23 +1074,7 @@ int main(int argc, char **argv) { } } else if (arg == "--bbox" && i + 1 < argc) { i++; - auto bboxStr(argv[i]); - auto bbox(split(bboxStr, ',')); - if (bbox.size() != 4) { - std::cerr << "Incorrect number of values for option --bbox: " - << bboxStr << std::endl; - usage(); - } - try { - bboxFilter = Extent::createFromBBOX( - c_locale_stod(bbox[0]), c_locale_stod(bbox[1]), - c_locale_stod(bbox[2]), c_locale_stod(bbox[3])) - .as_nullable(); - } catch (const std::exception &e) { - std::cerr << "Invalid value for option --bbox: " << bboxStr - << ", " << e.what() << std::endl; - usage(); - } + bboxStr = argv[i]; } else if (arg == "--accuracy" && i + 1 < argc) { i++; try { @@ -1166,6 +1247,12 @@ int main(int argc, char **argv) { outputOpt.outputCode = tokens[1]; } else if (arg == "--dump-db-structure") { dumpDbStructure = true; + } else if (arg == "--list-crs") { + listCRSSpecified = true; + if (i + 1 < argc && argv[i + 1][0] != '-') { + i++; + listCRS = argv[i]; + } } else if (ci_equal(arg, "--searchpaths")) { #ifdef _WIN32 constexpr char delim = ';'; @@ -1210,7 +1297,7 @@ int main(int argc, char **argv) { } } - if (bboxFilter && !area.empty()) { + if (!bboxStr.empty() && !area.empty()) { std::cerr << "ERROR: --bbox and --area are exclusive" << std::endl; std::exit(1); } @@ -1258,6 +1345,99 @@ int main(int argc, char **argv) { } } + if (listCRSSpecified) { + bool allow_deprecated = false; + std::set<AuthorityFactory::ObjectType> types; + auto tokens = split(listCRS, ','); + if (listCRS.empty()) { + tokens.clear(); + } + for (auto token : tokens) { + if (ci_equal(token, "allow_deprecated")) { + allow_deprecated = true; + } else if (ci_equal(token, "geodetic")) { + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS); + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); + types.insert(AuthorityFactory::ObjectType::GEOCENTRIC_CRS); + } else if (ci_equal(token, "geocentric")) { + types.insert(AuthorityFactory::ObjectType::GEOCENTRIC_CRS); + } else if (ci_equal(token, "geographic")) { + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS); + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); + } else if (ci_equal(token, "geographic_2d")) { + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS); + } else if (ci_equal(token, "geographic_3d")) { + types.insert(AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS); + } else if (ci_equal(token, "vertical")) { + types.insert(AuthorityFactory::ObjectType::VERTICAL_CRS); + } else if (ci_equal(token, "projected")) { + types.insert(AuthorityFactory::ObjectType::PROJECTED_CRS); + } else if (ci_equal(token, "compound")) { + types.insert(AuthorityFactory::ObjectType::COMPOUND_CRS); + } else { + std::cerr << "Unrecognized value for option --list-crs: " + << token << std::endl; + usage(); + } + } + + const std::string areaLower = tolower(area); + // If the area name has more than a single match, we + // will do filtering on info.areaName + auto bboxFilter = makeBboxFilter(dbContext, bboxStr, area, false); + auto allowedAuthorities(outputOpt.allowedAuthorities); + if (allowedAuthorities.empty()) { + allowedAuthorities.emplace_back(std::string()); + } + for (const auto &auth_name : allowedAuthorities) { + try { + auto factory = + AuthorityFactory::create(NN_NO_CHECK(dbContext), auth_name); + const auto list = factory->getCRSInfoList(); + for (const auto &info : list) { + if (!allow_deprecated && info.deprecated) { + continue; + } + if (!types.empty() && + types.find(info.type) == types.end()) { + continue; + } + if (bboxFilter) { + if (!info.bbox_valid) { + continue; + } + auto crsExtent = Extent::createFromBBOX( + info.west_lon_degree, info.south_lat_degree, + info.east_lon_degree, info.north_lat_degree); + if (spatialCriterion == + CoordinateOperationContext::SpatialCriterion:: + STRICT_CONTAINMENT) { + if (!bboxFilter->contains(crsExtent)) { + continue; + } + } else { + if (!bboxFilter->intersects(crsExtent)) { + continue; + } + } + } else if (!area.empty() && + tolower(info.areaName).find(areaLower) == + std::string::npos) { + continue; + } + std::cout << info.authName << ":" << info.code << " \"" + << info.name << "\"" + << (info.deprecated ? " [deprecated]" : "") + << std::endl; + } + } catch (const std::exception &e) { + std::cerr << "ERROR: list-crs failed with: " << e.what() + << std::endl; + std::exit(1); + } + } + } + if (!sourceCRSStr.empty() && targetCRSStr.empty()) { std::cerr << "Source CRS specified, but missing target CRS" << std::endl; @@ -1272,7 +1452,7 @@ int main(int argc, char **argv) { usage(); } } else if (!user_string_specified) { - if (dumpDbStructure) { + if (dumpDbStructure || listCRSSpecified) { std::exit(0); } std::cerr << "Missing user string" << std::endl; @@ -1391,68 +1571,7 @@ int main(int argc, char **argv) { std::exit(1); } } else { - - if (!area.empty()) { - assert(dbContext); - try { - if (area.find(' ') == std::string::npos && - area.find(':') != std::string::npos) { - auto tokens = split(area, ':'); - if (tokens.size() == 2) { - const std::string &areaAuth = tokens[0]; - const std::string &areaCode = tokens[1]; - bboxFilter = AuthorityFactory::create( - NN_NO_CHECK(dbContext), areaAuth) - ->createExtent(areaCode) - .as_nullable(); - } - } - if (!bboxFilter) { - auto authFactory = AuthorityFactory::create( - NN_NO_CHECK(dbContext), std::string()); - auto res = authFactory->listAreaOfUseFromName(area, false); - if (res.size() == 1) { - bboxFilter = - AuthorityFactory::create(NN_NO_CHECK(dbContext), - res.front().first) - ->createExtent(res.front().second) - .as_nullable(); - } else { - res = authFactory->listAreaOfUseFromName(area, true); - if (res.size() == 1) { - bboxFilter = - AuthorityFactory::create(NN_NO_CHECK(dbContext), - res.front().first) - ->createExtent(res.front().second) - .as_nullable(); - } else if (res.empty()) { - std::cerr << "No area of use matching provided name" - << std::endl; - std::exit(1); - } else { - std::cerr << "Several candidates area of use " - "matching provided name :" - << std::endl; - for (const auto &candidate : res) { - auto obj = - AuthorityFactory::create( - NN_NO_CHECK(dbContext), candidate.first) - ->createExtent(candidate.second); - std::cerr << " " << candidate.first << ":" - << candidate.second << " : " - << *obj->description() << std::endl; - } - std::exit(1); - } - } - } - } catch (const std::exception &e) { - std::cerr << "Area of use retrieval failed: " << e.what() - << std::endl; - std::exit(1); - } - } - + auto bboxFilter = makeBboxFilter(dbContext, bboxStr, area, true); try { outputOperations(dbContext, sourceCRSStr, targetCRSStr, bboxFilter, spatialCriterion, diff --git a/src/iso19111/factory.cpp b/src/iso19111/factory.cpp index 3367f55c..7037d642 100644 --- a/src/iso19111/factory.cpp +++ b/src/iso19111/factory.cpp @@ -7338,19 +7338,20 @@ AuthorityFactory::getDescriptionText(const std::string &code) const { */ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { - const auto getSqlArea = [](const std::string &table_name) { - return "JOIN usage u ON " - "u.object_table_name = '" + - table_name + - "' AND " + const auto getSqlArea = [](const char *table_name) { + std::string sql("JOIN usage u ON u.object_table_name = '"); + sql += table_name; + sql += "' AND " "u.object_auth_name = c.auth_name AND " "u.object_code = c.code " "JOIN extent a " "ON a.auth_name = u.extent_auth_name AND " "a.code = u.extent_code "; + return sql; }; - std::string sql = "SELECT c.auth_name, c.code, c.name, c.type, " + std::string sql = "SELECT * FROM (" + "SELECT c.auth_name, c.code, c.name, c.type, " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.description, NULL FROM geodetic_crs c " + @@ -7365,9 +7366,9 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " "a.description, cm.name AS conversion_method_name FROM " - "projected_crs c " + - getSqlArea("projected_crs") + - "LEFT JOIN conversion_table conv ON " + "projected_crs c "; + sql += getSqlArea("projected_crs"); + sql += "LEFT JOIN conversion_table conv ON " "c.conversion_auth_name = conv.auth_name AND " "c.conversion_code = conv.code " "LEFT JOIN conversion_method cm ON " @@ -7381,8 +7382,8 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { sql += "SELECT c.auth_name, c.code, c.name, 'vertical', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " - "a.description, NULL FROM vertical_crs c " + - getSqlArea("vertical_crs"); + "a.description, NULL FROM vertical_crs c "; + sql += getSqlArea("vertical_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); @@ -7391,12 +7392,13 @@ std::list<AuthorityFactory::CRSInfo> AuthorityFactory::getCRSInfoList() const { sql += "SELECT c.auth_name, c.code, c.name, 'compound', " "c.deprecated, " "a.west_lon, a.south_lat, a.east_lon, a.north_lat, " - "a.description, NULL FROM compound_crs c " + - getSqlArea("compound_crs"); + "a.description, NULL FROM compound_crs c "; + sql += getSqlArea("compound_crs"); if (d->hasAuthorityRestriction()) { sql += " WHERE c.auth_name = ?"; params.emplace_back(d->authority()); } + sql += ") r ORDER BY auth_name, code"; auto sqlRes = d->run(sql, params); std::list<AuthorityFactory::CRSInfo> res; for (const auto &row : sqlRes) { diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index bd29c310..d4b3dcb0 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -6626,9 +6626,9 @@ static BaseObjectNNPtr createFromUserInput(const std::string &text, }; // urn:ogc:def:crs:EPSG::4326 - if (tokens.size() == 7 && tokens[0] == "urn") { + if (tokens.size() == 7 && tolower(tokens[0]) == "urn") { - const auto &type = tokens[3]; + const auto type = tokens[3] == "CRS" ? "crs" : tokens[3]; const auto &authName = tokens[4]; const auto &code = tokens[6]; return createFromURNPart(type, authName, code); |
