aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/apps/projinfo.cpp283
-rw-r--r--src/iso19111/factory.cpp28
-rw-r--r--src/iso19111/io.cpp4
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);