Skip to content

Commit

Permalink
Merge pull request #1189 from rouault/projinfo_improvements
Browse files Browse the repository at this point in the history
Projinfo improvements: output operation summary and add --area option
  • Loading branch information
rouault authored Dec 3, 2018
2 parents d0506e1 + 0ba9d24 commit addf30e
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 44 deletions.
5 changes: 4 additions & 1 deletion include/proj/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,10 @@ class PROJ_GCC_DLL AuthorityFactory {
const std::vector<ObjectType> &allowedObjectTypes =
std::vector<ObjectType>(),
bool approximateMatch = true,
size_t limitResultCount = 0);
size_t limitResultCount = 0) const;

PROJ_DLL std::list<std::pair<std::string, std::string>>
listAreaOfUseFromName(const std::string &name, bool approximateMatch) const;

PROJ_PRIVATE :
//! @cond Doxygen_Suppress
Expand Down
35 changes: 34 additions & 1 deletion src/factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3642,7 +3642,7 @@ std::list<common::IdentifiedObjectNNPtr>
AuthorityFactory::createObjectsFromName(
const std::string &searchedName,
const std::vector<ObjectType> &allowedObjectTypes, bool approximateMatch,
size_t limitResultCount) {
size_t limitResultCount) const {

std::string searchedNameWithoutDeprecated(searchedName);
bool deprecated = false;
Expand Down Expand Up @@ -3885,6 +3885,39 @@ AuthorityFactory::createObjectsFromName(

// ---------------------------------------------------------------------------

/** \brief Return a list of area of use from their name
*
* @param name Searched name.
* @param approximateMatch Whether approximate name identification is allowed.
* @return list of (auth_name, code) of matched objects.
* @throw FactoryException
*/
std::list<std::pair<std::string, std::string>>
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()) {
sql += " auth_name = ? AND ";
params.emplace_back(getAuthority());
}
sql += "name LIKE ?";
if (!approximateMatch) {
params.push_back(name);
} else {
params.push_back('%' + name + '%');
}
auto sqlRes = d->run(sql, params);
std::list<std::pair<std::string, std::string>> res;
for (const auto &row : sqlRes) {
res.emplace_back(row[0], row[1]);
}
return res;
}

// ---------------------------------------------------------------------------

//! @cond Doxygen_Suppress
std::list<datum::EllipsoidNNPtr> AuthorityFactory::createEllipsoidFromExisting(
const datum::EllipsoidNNPtr &ellipsoid) const {
Expand Down
164 changes: 122 additions & 42 deletions src/projinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ static void usage() {
std::cerr
<< "usage: projinfo [-o formats] [-k crs|operation] [--summary] [-q]"
<< std::endl
<< " [--bbox min_long,min_lat,max_long,max_lat] "
<< " ([--area name_or_code] | "
"[--bbox min_long,min_lat,max_long,max_lat]) "
<< std::endl
<< " [--spatial-test contains|intersects]" << std::endl
<< " [--crs-extent-use none|both|intersection|smallest]"
Expand Down Expand Up @@ -422,6 +423,51 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,

// ---------------------------------------------------------------------------

static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
auto ids = op->identifiers();
if (!ids.empty()) {
std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code();
} else {
std::cout << "unknown id";
}

std::cout << ", ";

auto name = op->nameStr();
if (!name.empty()) {
std::cout << name;
} else {
std::cout << "unknown name";
}

std::cout << ", ";

auto accuracies = op->coordinateOperationAccuracies();
if (!accuracies.empty()) {
std::cout << accuracies[0]->value() << " m";
} else {
if (std::dynamic_pointer_cast<Conversion>(op.as_nullable())) {
std::cout << "0 m";
} else {
std::cout << "unknown accuracy";
}
}

std::cout << ", ";

auto domains = op->domains();
if (!domains.empty() && domains[0]->domainOfValidity() &&
domains[0]->domainOfValidity()->description().has_value()) {
std::cout << *(domains[0]->domainOfValidity()->description());
} else {
std::cout << "unknown domain of validity";
}

std::cout << std::endl;
}

// ---------------------------------------------------------------------------

static void outputOperations(
DatabaseContextPtr dbContext, const std::string &sourceCRSStr,
const std::string &targetCRSStr, const ExtentPtr &bboxFilter,
Expand Down Expand Up @@ -477,46 +523,7 @@ static void outputOperations(
if (summary) {
std::cout << "Candidate operations found: " << list.size() << std::endl;
for (const auto &op : list) {
auto ids = op->identifiers();
if (!ids.empty()) {
std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code();
} else {
std::cout << "unknown id";
}

std::cout << ", ";

auto name = op->nameStr();
if (!name.empty()) {
std::cout << name;
} else {
std::cout << "unknown name";
}

std::cout << ", ";

auto accuracies = op->coordinateOperationAccuracies();
if (!accuracies.empty()) {
std::cout << accuracies[0]->value() << " m";
} else {
if (std::dynamic_pointer_cast<Conversion>(op.as_nullable())) {
std::cout << "0 m";
} else {
std::cout << "unknown accuracy";
}
}

std::cout << ", ";

auto domains = op->domains();
if (!domains.empty() && domains[0]->domainOfValidity() &&
domains[0]->domainOfValidity()->description().has_value()) {
std::cout << *(domains[0]->domainOfValidity()->description());
} else {
std::cout << "unknown domain of validity";
}

std::cout << std::endl;
outputOperationSummary(op);
}
} else {
bool first = true;
Expand All @@ -534,6 +541,8 @@ static void outputOperations(
<< (i + 1) << ":" << std::endl
<< std::endl;
}
outputOperationSummary(op);
std::cout << std::endl;
outputObject(dbContext, op, outputOpt);
}
}
Expand All @@ -557,6 +566,7 @@ int main(int argc, char **argv) {
bool kindIsCRS = true;
bool summary = false;
ExtentPtr bboxFilter = nullptr;
std::string area;
CoordinateOperationContext::SpatialCriterion spatialCriterion =
CoordinateOperationContext::SpatialCriterion::STRICT_CONTAINMENT;
CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse =
Expand Down Expand Up @@ -678,6 +688,9 @@ int main(int argc, char **argv) {
<< ", " << e.what() << std::endl;
usage();
}
} else if (arg == "--area" && i + 1 < argc) {
i++;
area = argv[i];
} else if (arg == "-k" && i + 1 < argc) {
i++;
std::string kind(argv[i]);
Expand Down Expand Up @@ -810,12 +823,17 @@ int main(int argc, char **argv) {
}
}

if (bboxFilter && !area.empty()) {
std::cerr << "ERROR: --bbox and --area are exclusive" << std::endl;
std::exit(1);
}

DatabaseContextPtr dbContext;
try {
dbContext =
DatabaseContext::create(mainDBPath, auxDBPath).as_nullable();
} catch (const std::exception &e) {
if (!mainDBPath.empty() || !auxDBPath.empty()) {
if (!mainDBPath.empty() || !auxDBPath.empty() || !area.empty()) {
std::cerr << "ERROR: Cannot create database connection: "
<< e.what() << std::endl;
std::exit(1);
Expand Down Expand Up @@ -920,6 +938,68 @@ int main(int argc, char **argv) {
}
}
} 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);
}
}

outputOperations(dbContext, sourceCRSStr, targetCRSStr, bboxFilter,
spatialCriterion, crsExtentUse, gridAvailabilityUse,
allowPivots, pivots, authority,
Expand Down
25 changes: 25 additions & 0 deletions test/cli/testprojinfo
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,31 @@ echo "Testing projinfo -s NAD27 -t NAD83 --grid-check none --spatial-test inters
$EXE -s NAD27 -t NAD83 --grid-check none --spatial-test intersects >>${OUT}
echo "" >>${OUT}

echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --bbox 8,54.51,15.24,57.8 --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --bbox 8,54.51,15.24,57.8 --summary >>${OUT}
echo "" >>${OUT}

echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area EPSG:3237 --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --area EPSG:3237 --summary >>${OUT}
echo "" >>${OUT}

echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area 'Denmark - onshore' --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --area 'Denmark - onshore' --summary >>${OUT}
echo "" >>${OUT}

# several match
echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area 'Denmark -' --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --area 'Denmark -' --summary >>${OUT} 2>&1
echo "" >>${OUT}

echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area no_match --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --area no_match --summary >>${OUT} 2>&1
echo "" >>${OUT}

echo "Testing projinfo -s EPSG:4230 -t EPSG:4258 --area WRONG:CODE --summary" >> ${OUT}
$EXE -s EPSG:4230 -t EPSG:4258 --area WRONG:CODE --summary >>${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
Expand Down
Loading

0 comments on commit addf30e

Please sign in to comment.