aboutsummaryrefslogtreecommitdiff
path: root/src/apps
diff options
context:
space:
mode:
authorAndrew Bell <andrew.bell.ia@gmail.com>2019-05-15 10:47:03 -0400
committerAndrew Bell <andrew.bell.ia@gmail.com>2019-05-15 10:47:03 -0400
commit8f268409d37cea329d263e177b83e42f8384d3c7 (patch)
treec4d0f3dd19456600f718a6e0c8573577f433549b /src/apps
parent886ced02f0aaab5d66d16459435f7447cf976650 (diff)
parentd67203a6f76a74f5ac029ff052dbcc72e3b59624 (diff)
downloadPROJ-8f268409d37cea329d263e177b83e42f8384d3c7.tar.gz
PROJ-8f268409d37cea329d263e177b83e42f8384d3c7.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src/apps')
-rw-r--r--src/apps/cct.cpp3
-rw-r--r--src/apps/cs2cs.cpp48
-rw-r--r--src/apps/emess.cpp3
-rw-r--r--src/apps/geod.cpp2
-rw-r--r--src/apps/gie.cpp104
-rw-r--r--src/apps/optargpm.h6
-rw-r--r--src/apps/proj.cpp14
-rw-r--r--src/apps/proj_strtod.cpp2
-rw-r--r--src/apps/projinfo.cpp308
-rw-r--r--src/apps/utils.cpp58
-rw-r--r--src/apps/utils.h29
11 files changed, 429 insertions, 148 deletions
diff --git a/src/apps/cct.cpp b/src/apps/cct.cpp
index 34bf0777..d29b58fb 100644
--- a/src/apps/cct.cpp
+++ b/src/apps/cct.cpp
@@ -81,7 +81,6 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26
#include "proj.h"
#include "proj_internal.h"
#include "proj_strtod.h"
-#include "proj_internal.h"
#include "optargpm.h"
@@ -228,6 +227,7 @@ int main(int argc, char **argv) {
fout = stdout;
+ /* coverity[tainted_data] */
o = opt_parse (argc, argv, "hvI", "cdozts", longflags, longkeys);
if (nullptr==o)
return 0;
@@ -368,6 +368,7 @@ int main(int argc, char **argv) {
point.lpzt.phi = proj_torad (point.lpzt.phi);
}
err = proj_errno_reset (P);
+ /* coverity[returned_value] */
point = proj_trans (P, direction, point);
if (HUGE_VAL==point.xyzt.x) {
diff --git a/src/apps/cs2cs.cpp b/src/apps/cs2cs.cpp
index 150548c5..20e5e73c 100644
--- a/src/apps/cs2cs.cpp
+++ b/src/apps/cs2cs.cpp
@@ -45,6 +45,7 @@
#include "proj.h"
#include "proj_internal.h"
#include "emess.h"
+#include "utils.h"
// clang-format on
#define MAX_LINE 1000
@@ -68,8 +69,8 @@ static const char *oform =
static char oform_buffer[16]; /* buffer for oform when using -d */
static const char *oterr = "*\t*"; /* output line for unprojectable input */
static const char *usage =
- "%s\nusage: %s [ -dDeEfIlrstvwW [args] ] [ +opts[=arg] ]\n"
- " [+to [+opts[=arg] [ files ]\n";
+ "%s\nusage: %s [-dDeEfIlrstvwW [args]] [+opt[=arg] ...]\n"
+ " [+to +opt[=arg] ...] [file ...]\n";
static double (*informat)(const char *,
char **); /* input data deformatter function */
@@ -113,6 +114,19 @@ static void process(FILE *fid)
z = strtod(s, &s);
+ /* To avoid breaking existing tests, we read what is a possible t */
+ /* component of the input and rewind the s-pointer so that the final */
+ /* output has consistant behaviour, with or without t values. */
+ /* This is a bit of a hack, in most cases 4D coordinates will be */
+ /* written to STDOUT (except when using -E) but the output format */
+ /* speficied with -f is not respected for the t component, rather it */
+ /* is forward verbatim from the input. */
+ char *before_time = s;
+ double t = strtod(s, &s);
+ if( s == before_time )
+ t = HUGE_VAL;
+ s = before_time;
+
if (data.v == HUGE_VAL)
data.u = HUGE_VAL;
@@ -120,11 +134,11 @@ static void process(FILE *fid)
--s; /* assumed we gobbled \n */
if (echoin) {
- char t;
- t = *s;
+ char temp;
+ temp = *s;
*s = '\0';
(void)fputs(line, stdout);
- *s = t;
+ *s = temp;
putchar('\t');
}
@@ -141,7 +155,7 @@ static void process(FILE *fid)
coord.xyzt.x = data.u;
coord.xyzt.y = data.v;
coord.xyzt.z = z;
- coord.xyzt.t = HUGE_VAL;
+ coord.xyzt.t = t;
coord = proj_trans(transformation, PJ_FWD, coord);
data.u = coord.xyz.x;
data.v = coord.xyz.y;
@@ -206,10 +220,10 @@ static void process(FILE *fid)
}
/************************************************************************/
-/* instanciate_crs() */
+/* instantiate_crs() */
/************************************************************************/
-static PJ *instanciate_crs(const std::string &definition,
+static PJ *instantiate_crs(const std::string &definition,
bool &isGeog, double &toRadians,
bool &isLatFirst) {
PJ *crs = proj_create(nullptr,
@@ -263,13 +277,6 @@ static std::string get_geog_crs_proj_string_from_proj_crs(PJ *src,
double &toRadians,
bool &isLatFirst) {
auto srcType = proj_get_type(src);
- if (srcType == PJ_TYPE_BOUND_CRS) {
- auto base = proj_get_source_crs(nullptr, src);
- assert(base);
- proj_destroy(src);
- src = base;
- srcType = proj_get_type(src);
- }
if (srcType != PJ_TYPE_PROJECTED_CRS) {
return std::string();
}
@@ -518,6 +525,13 @@ int main(int argc, char **argv) {
if (eargc == 0) /* if no specific files force sysin */
eargv[eargc++] = const_cast<char *>("-");
+ if( oform ) {
+ if( !validate_form_string_for_numbers(oform) ) {
+ emess(3, "invalid format string");
+ exit(0);
+ }
+ }
+
/*
* If the user has requested inverse, then just reverse the
* coordinate systems.
@@ -541,7 +555,7 @@ int main(int argc, char **argv) {
PJ *src = nullptr;
if (!fromStr.empty()) {
bool ignored;
- src = instanciate_crs(fromStr, srcIsGeog,
+ src = instantiate_crs(fromStr, srcIsGeog,
srcToRadians, ignored);
if (!src) {
emess(3, "cannot instantiate source coordinate system");
@@ -550,7 +564,7 @@ int main(int argc, char **argv) {
PJ *dst = nullptr;
if (!toStr.empty()) {
- dst = instanciate_crs(toStr, destIsGeog,
+ dst = instantiate_crs(toStr, destIsGeog,
destToRadians, destIsLatLong);
if (!dst) {
emess(3, "cannot instantiate target coordinate system");
diff --git a/src/apps/emess.cpp b/src/apps/emess.cpp
index 144e9e23..53018ba8 100644
--- a/src/apps/emess.cpp
+++ b/src/apps/emess.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include "proj_api.h"
+#include "proj_config.h"
#define EMESS_ROUTINE
#include "emess.h"
@@ -29,7 +30,7 @@ emess(int code, const char *fmt, ...) {
va_start(args, fmt);
/* prefix program name, if given */
- if (fmt != nullptr)
+ if (emess_dat.Prog_name != nullptr)
(void)fprintf(stderr,"%s\n<%s>: ",pj_get_release(),
emess_dat.Prog_name);
/* print file name and line, if given */
diff --git a/src/apps/geod.cpp b/src/apps/geod.cpp
index 7225856e..b46188d3 100644
--- a/src/apps/geod.cpp
+++ b/src/apps/geod.cpp
@@ -22,7 +22,7 @@ static const char *osform = "%.3f"; /* output format for S */
static char pline[50]; /* work string */
static const char *usage =
-"%s\nusage: %s [ -afFIlptwW [args] ] [ +opts[=arg] ] [ files ]\n";
+"%s\nusage: %s [-afFIlptwW [args]] [+opt[=arg] ...] [file ...]\n";
static void
printLL(double p, double l) {
diff --git a/src/apps/gie.cpp b/src/apps/gie.cpp
index abed11c0..2f401984 100644
--- a/src/apps/gie.cpp
+++ b/src/apps/gie.cpp
@@ -117,7 +117,6 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08
#include "proj_internal.h"
#include "proj_math.h"
#include "proj_strtod.h"
-#include "proj_internal.h"
#include "optargpm.h"
@@ -148,7 +147,7 @@ static ffio *ffio_destroy (ffio *G);
static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
static const char *gie_tags[] = {
- "<gie>", "operation", "use_proj4_init_rules",
+ "<gie>", "operation", "crs_src", "crs_dst", "use_proj4_init_rules",
"accept", "expect", "roundtrip", "banner", "verbose",
"direction", "tolerance", "ignore", "require_grid", "echo", "skip", "</gie>"
};
@@ -175,6 +174,8 @@ static const char *err_const_from_errno (int err);
typedef struct {
char operation[MAX_OPERATION+1];
+ char crs_dst[MAX_OPERATION+1];
+ char crs_src[MAX_OPERATION+1];
PJ *P;
PJ_COORD a, b, c, e;
PJ_DIRECTION dir;
@@ -247,6 +248,7 @@ int main (int argc, char **argv) {
T.ignore = 5555; /* Error code that will not be issued by proj_create() */
T.use_proj4_init_rules = FALSE;
+ /* coverity[tainted_data] */
o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys);
if (nullptr==o)
return 0;
@@ -300,6 +302,20 @@ int main (int argc, char **argv) {
return 1;
}
+ for (i = 0; i < o->fargc; i++ ) {
+ FILE* f = fopen (o->fargv[i], "rt");
+ if (f == nullptr) {
+ fprintf (
+ T.fout,
+ "%sCannot open specified input file '%s' - bye!\n",
+ delim,
+ o->fargv[i]
+ );
+ return 1;
+ }
+ fclose(f);
+ }
+
for (i = 0; i < o->fargc; i++)
process_file (o->fargv[i]);
@@ -370,8 +386,6 @@ static int another_failing_roundtrip (void) {
}
static int process_file (const char *fname) {
- FILE *f;
-
F->lineno = F->next_lineno = F->level = 0;
T.op_ok = T.total_ok = 0;
T.op_ko = T.total_ko = 0;
@@ -383,15 +397,8 @@ static int process_file (const char *fname) {
return 0;
}
- f = fopen (fname, "rt");
- if (nullptr==f) {
- if (T.verbosity > 0) {
- fprintf (T.fout, "%sCannot open spec'd input file '%s' - bye!\n", delim, fname);
- return 2;
- }
- errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname);
- }
- F->f = f;
+ /* We have already tested in main that the file exists */
+ F->f = fopen (fname, "rt");
if (T.verbosity > 0)
fprintf (T.fout, "%sReading file '%s'\n", delim, fname);
@@ -405,7 +412,7 @@ static int process_file (const char *fname) {
}
}
- fclose (f);
+ fclose (F->f);
F->lineno = F->next_lineno = 0;
T.grand_ok += T.total_ok;
@@ -602,6 +609,65 @@ either a conversion or a transformation)
return 0;
}
+static int crs_to_crs_operation() {
+ T.op_id++;
+ T.operation_lineno = F->lineno;
+
+ if (T.verbosity > 1) {
+ char buffer[80];
+ finish_previous_operation (F->args);
+ snprintf(buffer, 80, "%-36.36s -> %-36.36s", T.crs_src, T.crs_dst);
+ banner (buffer);
+ }
+
+ T.op_ok = 0;
+ T.op_ko = 0;
+ T.op_skip = 0;
+ T.skip_test = 0;
+
+ direction ("forward");
+ tolerance ("0.5 mm");
+ ignore ("pjd_err_dont_skip");
+
+ proj_errno_reset (T.P);
+
+ if (T.P)
+ proj_destroy (T.P);
+ proj_errno_reset (nullptr);
+ proj_context_use_proj4_init_rules(nullptr, T.use_proj4_init_rules);
+
+
+ T.P = proj_create_crs_to_crs(nullptr, T.crs_src, T.crs_dst, nullptr);
+
+ strcpy(T.crs_src, "");
+ strcpy(T.crs_dst, "");
+ return 0;
+}
+
+static int crs_src(const char *args) {
+ strncpy (&(T.crs_src[0]), F->args, MAX_OPERATION);
+ T.crs_src[MAX_OPERATION] = '\0';
+ (void) args;
+
+ if (strcmp(T.crs_src, "") != 0 && strcmp(T.crs_dst, "") != 0) {
+ crs_to_crs_operation();
+ }
+
+ return 0;
+}
+
+static int crs_dst(const char *args) {
+ strncpy (&(T.crs_dst[0]), F->args, MAX_OPERATION);
+ T.crs_dst[MAX_OPERATION] = '\0';
+ (void) args;
+
+ if (strcmp(T.crs_src, "") != 0 && strcmp(T.crs_dst, "") != 0) {
+ crs_to_crs_operation();
+ }
+
+ return 0;
+}
+
static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) {
size_t i, n;
const char *axis = "enut";
@@ -939,7 +1005,8 @@ Tell GIE what to expect, when transforming the ACCEPTed input
else
d = proj_xyz_dist (co, ce);
- if (d > T.tolerance)
+ // Test written like that to handle NaN
+ if (!(d <= T.tolerance))
return expect_message (d, args);
succs++;
@@ -995,6 +1062,8 @@ static int dispatch (const char *cmnd, const char *args) {
if (T.skip)
return SKIP;
if (0==strcmp (cmnd, "operation")) return operation ((char *) args);
+ if (0==strcmp (cmnd, "crs_src")) return crs_src (args);
+ if (0==strcmp (cmnd, "crs_dst")) return crs_dst (args);
if (T.skip_test)
{
if (0==strcmp (cmnd, "expect")) return another_skip();
@@ -1028,7 +1097,7 @@ static const struct errno_vs_err_const lookup[] = {
{"pjd_err_no_colon_in_init_string" , -3},
{"pjd_err_proj_not_named" , -4},
{"pjd_err_unknown_projection_id" , -5},
- {"pjd_err_eccentricity_is_one" , -6},
+ {"pjd_err_invalid_eccentricity" , -6},
{"pjd_err_unknown_unit_id" , -7},
{"pjd_err_invalid_boolean_param" , -8},
{"pjd_err_unknown_ellp_param" , -9},
@@ -1052,7 +1121,7 @@ static const struct errno_vs_err_const lookup[] = {
{"pjd_err_w_or_m_zero_or_less" , -27},
{"pjd_err_lsat_not_in_range" , -28},
{"pjd_err_path_not_in_range" , -29},
- {"pjd_err_h_less_than_zero" , -30},
+ {"pjd_err_invalid_h" , -30},
{"pjd_err_k_less_than_zero" , -31},
{"pjd_err_lat_1_or_2_zero_or_90" , -32},
{"pjd_err_lat_0_or_alpha_eq_90" , -33},
@@ -1083,6 +1152,7 @@ static const struct errno_vs_err_const lookup[] = {
{"pjd_err_invalid_arg" , -58},
{"pjd_err_inconsistent_unit" , -59},
{"pjd_err_mutually_exclusive_args" , -60},
+ {"pjd_err_generic_error" , -61},
{"pjd_err_dont_skip" , 5555},
{"pjd_err_unknown" , 9999},
{"pjd_err_enomem" , ENOMEM},
diff --git a/src/apps/optargpm.h b/src/apps/optargpm.h
index 035c6f92..f293ad98 100644
--- a/src/apps/optargpm.h
+++ b/src/apps/optargpm.h
@@ -526,6 +526,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
c = opt_ordinal (o, crepr);
if (0==c) {
fprintf (stderr, "Invalid option \"%s\"\n", crepr);
+ free (o);
return nullptr;
}
@@ -534,6 +535,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
*equals = '=';
if (opt_is_flag (o, c)) {
fprintf (stderr, "Option \"%s\" takes no arguments\n", crepr);
+ free (o);
return nullptr;
}
o->optarg[c] = equals + 1;
@@ -544,6 +546,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
if (!opt_is_flag (o, c)) {
if ((argc==i + 1) || ('+'==argv[i+1][0]) || ('-'==argv[i+1][0])) {
fprintf (stderr, "Missing argument for option \"%s\"\n", crepr);
+ free (o);
return nullptr;
}
o->optarg[c] = argv[i + 1];
@@ -553,6 +556,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
if (!opt_is_flag (o, c)) {
fprintf (stderr, "Expected flag style long option here, but got \"%s\"\n", crepr);
+ free (o);
return nullptr;
}
@@ -564,6 +568,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
/* classic short options */
if (nullptr==o->optarg[c]) {
fprintf (stderr, "Invalid option \"%s\"\n", crepr);
+ free (o);
return nullptr;
}
@@ -580,6 +585,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
if ((argc==i + 1) || ('+'==argv[i+1][0]) || ('-'==argv[i+1][0]))
{
fprintf (stderr, "Bad or missing arg for option \"%s\"\n", crepr);
+ free (o);
return nullptr;
}
o->optarg[(int) c] = argv[i + 1];
diff --git a/src/apps/proj.cpp b/src/apps/proj.cpp
index 298a44e8..888d723f 100644
--- a/src/apps/proj.cpp
+++ b/src/apps/proj.cpp
@@ -7,6 +7,7 @@
#include <string.h>
#include <math.h>
#include "emess.h"
+#include "utils.h"
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__)
# include <fcntl.h>
@@ -22,7 +23,6 @@
static PJ *Proj;
static union {
- PJ_UV (*generic)(PJ_UV, PJ *);
PJ_XY (*fwd)(PJ_LP, PJ *);
PJ_LP (*inv)(PJ_XY, PJ *);
} proj;
@@ -46,7 +46,7 @@ static char oform_buffer[16]; /* Buffer for oform when using -d */
static const char
*oterr = "*\t*", /* output line for unprojectable input */
- *usage = "%s\nusage: %s [ -bdeEfiIlmorsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n";
+ *usage = "%s\nusage: %s [-bdeEfiIlmorsStTvVwW [args]] [+opt[=arg] ...] [file ...]\n";
static PJ_FACTORS facs;
@@ -462,6 +462,13 @@ int main(int argc, char **argv) {
if (eargc == 0) /* if no specific files force sysin */
eargv[eargc++] = const_cast<char*>("-");
+ if( oform ) {
+ if( !validate_form_string_for_numbers(oform) ) {
+ emess(3, "invalid format string");
+ exit(0);
+ }
+ }
+
/* done with parameter and control input */
if (inverse && postscale) {
prescale = 1;
@@ -488,7 +495,6 @@ int main(int argc, char **argv) {
proj.inv = pj_inv;
} else
proj.fwd = pj_fwd;
-
/* set input formatting control */
if (mon) {
pj_pr_list(Proj);
@@ -533,7 +539,7 @@ int main(int argc, char **argv) {
} else {
if ((fid = fopen(*eargv, "rb")) == nullptr) {
- emess(-2, *eargv, "input file");
+ emess(-2, "input file: %s", *eargv);
continue;
}
emess_dat.File_name = *eargv;
diff --git a/src/apps/proj_strtod.cpp b/src/apps/proj_strtod.cpp
index b8edc6a3..d4674705 100644
--- a/src/apps/proj_strtod.cpp
+++ b/src/apps/proj_strtod.cpp
@@ -309,7 +309,7 @@ double proj_strtod(const char *str, char **endptr) {
number = exponent < 0? number / ex: number * ex;
}
else
- number *= pow (10, exponent);
+ number *= pow (10.0, static_cast<double>(exponent));
return number;
}
diff --git a/src/apps/projinfo.cpp b/src/apps/projinfo.cpp
index 9f908c8a..9d724522 100644
--- a/src/apps/projinfo.cpp
+++ b/src/apps/projinfo.cpp
@@ -77,7 +77,8 @@ struct OutputOptions {
static void usage() {
std::cerr
- << "usage: projinfo [-o formats] [-k crs|operation] [--summary] [-q]"
+ << "usage: projinfo [-o formats] [-k crs|operation|ellipsoid] "
+ "[--summary] [-q]"
<< std::endl
<< " ([--area name_or_code] | "
"[--bbox west_long,south_lat,east_long,north_lat]) "
@@ -136,11 +137,12 @@ static std::string c_ify_string(const std::string &str) {
// ---------------------------------------------------------------------------
-static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext,
- const std::string &user_string,
- bool kindIsCRS, const std::string &context,
- bool buildBoundCRSToWGS84, CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS,
- bool quiet) {
+static BaseObjectNNPtr buildObject(
+ DatabaseContextPtr dbContext, const std::string &user_string,
+ const std::string &kind, const std::string &context,
+ bool buildBoundCRSToWGS84,
+ CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS,
+ bool quiet) {
BaseObjectPtr obj;
std::string l_user_string(user_string);
@@ -174,10 +176,13 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext,
try {
auto tokens = split(l_user_string, ':');
- if (!kindIsCRS && tokens.size() == 2) {
+ if (kind == "operation" && tokens.size() == 2) {
auto urn = "urn:ogc:def:coordinateOperation:" + tokens[0] + "::" +
tokens[1];
obj = createFromUserInput(urn, dbContext).as_nullable();
+ } else if (kind == "ellipsoid" && tokens.size() == 2) {
+ auto urn = "urn:ogc:def:ellipsoid:" + tokens[0] + "::" + tokens[1];
+ obj = createFromUserInput(urn, dbContext).as_nullable();
} else {
// Convenience to be able to use C escaped strings...
if (l_user_string.size() > 2 && l_user_string[0] == '"' &&
@@ -213,7 +218,8 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext,
if (buildBoundCRSToWGS84) {
auto crs = std::dynamic_pointer_cast<CRS>(obj);
if (crs) {
- obj = crs->createBoundCRSToWGS84IfPossible(dbContext, allowUseIntermediateCRS)
+ obj = crs->createBoundCRSToWGS84IfPossible(dbContext,
+ allowUseIntermediateCRS)
.as_nullable();
}
}
@@ -223,8 +229,10 @@ static BaseObjectNNPtr buildObject(DatabaseContextPtr dbContext,
// ---------------------------------------------------------------------------
-static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
- CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS, const OutputOptions &outputOpt) {
+static void outputObject(
+ DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
+ CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS,
+ const OutputOptions &outputOpt) {
auto identified = dynamic_cast<const IdentifiedObject *>(obj.get());
if (!outputOpt.quiet && identified && identified->isDeprecated()) {
@@ -260,7 +268,7 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
}
auto crs = nn_dynamic_pointer_cast<CRS>(obj);
if (!outputOpt.quiet) {
- if( crs ) {
+ if (crs) {
std::cout << "PROJ.4 string:" << std::endl;
} else {
std::cout << "PROJ string:" << std::endl;
@@ -271,8 +279,8 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
if (crs) {
objToExport =
nn_dynamic_pointer_cast<IPROJStringExportable>(
- crs->createBoundCRSToWGS84IfPossible(dbContext,
- allowUseIntermediateCRS));
+ crs->createBoundCRSToWGS84IfPossible(
+ dbContext, allowUseIntermediateCRS));
}
if (!objToExport) {
objToExport = projStringExportable;
@@ -411,8 +419,8 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
std::shared_ptr<IWKTExportable> objToExport;
if (crs) {
objToExport = nn_dynamic_pointer_cast<IWKTExportable>(
- crs->createBoundCRSToWGS84IfPossible(dbContext,
- allowUseIntermediateCRS));
+ crs->createBoundCRSToWGS84IfPossible(
+ dbContext, allowUseIntermediateCRS));
}
if (!objToExport) {
objToExport = wktExportable;
@@ -461,11 +469,42 @@ static void outputObject(DatabaseContextPtr dbContext, BaseObjectNNPtr obj,
}
}
}
+
+ auto op = dynamic_cast<CoordinateOperation *>(obj.get());
+ if (op && dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
+ try {
+ auto setGrids = op->gridsNeeded(dbContext);
+ bool firstWarning = true;
+ for (const auto &grid : setGrids) {
+ if (!grid.available) {
+ if (firstWarning) {
+ std::cout << std::endl;
+ firstWarning = false;
+ }
+ std::cout << "Grid " << grid.shortName
+ << " needed but not found on the system.";
+ if (!grid.packageName.empty()) {
+ std::cout << " Can be obtained from the "
+ << grid.packageName << " package";
+ if (!grid.url.empty()) {
+ std::cout << " at " << grid.url;
+ }
+ } else if (!grid.url.empty()) {
+ std::cout << " Can be obtained at " << grid.url;
+ }
+ std::cout << std::endl;
+ }
+ }
+ } catch (const std::exception &e) {
+ std::cerr << "Error in gridsNeeded(): " << e.what() << std::endl;
+ }
+ }
}
// ---------------------------------------------------------------------------
-static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
+static void outputOperationSummary(const CoordinateOperationNNPtr &op,
+ const DatabaseContextPtr &dbContext) {
auto ids = op->identifiers();
if (!ids.empty()) {
std::cout << *(ids[0]->codeSpace()) << ":" << ids[0]->code();
@@ -505,6 +544,22 @@ static void outputOperationSummary(const CoordinateOperationNNPtr &op) {
std::cout << "unknown domain of validity";
}
+ if (op->hasBallparkTransformation()) {
+ std::cout << ", has ballpark transformation";
+ }
+
+ if (dbContext && getenv("PROJINFO_NO_GRID_CHECK") == nullptr) {
+ try {
+ auto setGrids = op->gridsNeeded(dbContext);
+ for (const auto &grid : setGrids) {
+ if (!grid.available) {
+ std::cout << ", at least one grid missing";
+ break;
+ }
+ }
+ } catch (const std::exception &) {
+ }
+ }
std::cout << std::endl;
}
@@ -514,26 +569,25 @@ static void outputOperations(
DatabaseContextPtr dbContext, const std::string &sourceCRSStr,
const std::string &targetCRSStr, const ExtentPtr &bboxFilter,
CoordinateOperationContext::SpatialCriterion spatialCriterion,
+ bool spatialCriterionExplicitlySpecified,
CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse,
CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse,
CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS,
const std::vector<std::pair<std::string, std::string>> &pivots,
const std::string &authority, bool usePROJGridAlternatives,
bool showSuperseded, const OutputOptions &outputOpt, bool summary) {
- auto sourceObj = buildObject(dbContext, sourceCRSStr, true, "source CRS",
- false,
- CoordinateOperationContext::IntermediateCRSUse::NEVER,
- outputOpt.quiet);
+ auto sourceObj = buildObject(
+ dbContext, sourceCRSStr, "crs", "source CRS", false,
+ CoordinateOperationContext::IntermediateCRSUse::NEVER, outputOpt.quiet);
auto sourceCRS = nn_dynamic_pointer_cast<CRS>(sourceObj);
if (!sourceCRS) {
std::cerr << "source CRS string is not a CRS" << std::endl;
std::exit(1);
}
- auto targetObj = buildObject(dbContext, targetCRSStr, true, "target CRS",
- false,
- CoordinateOperationContext::IntermediateCRSUse::NEVER,
- outputOpt.quiet);
+ auto targetObj = buildObject(
+ dbContext, targetCRSStr, "crs", "target CRS", false,
+ CoordinateOperationContext::IntermediateCRSUse::NEVER, outputOpt.quiet);
auto targetCRS = nn_dynamic_pointer_cast<CRS>(targetObj);
if (!targetCRS) {
std::cerr << "target CRS string is not a CRS" << std::endl;
@@ -541,6 +595,7 @@ static void outputOperations(
}
std::vector<CoordinateOperationNNPtr> list;
+ size_t spatialCriterionPartialIntersectionResultCount = 0;
try {
auto authFactory =
dbContext
@@ -558,6 +613,21 @@ static void outputOperations(
ctxt->setDiscardSuperseded(!showSuperseded);
list = CoordinateOperationFactory::create()->createOperations(
NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), ctxt);
+ if (!spatialCriterionExplicitlySpecified &&
+ spatialCriterion == CoordinateOperationContext::SpatialCriterion::
+ STRICT_CONTAINMENT) {
+ try {
+ ctxt->setSpatialCriterion(
+ CoordinateOperationContext::SpatialCriterion::
+ PARTIAL_INTERSECTION);
+ spatialCriterionPartialIntersectionResultCount =
+ CoordinateOperationFactory::create()
+ ->createOperations(NN_NO_CHECK(sourceCRS),
+ NN_NO_CHECK(targetCRS), ctxt)
+ .size();
+ } catch (const std::exception &) {
+ }
+ }
} catch (const std::exception &e) {
std::cerr << "createOperations() failed with: " << e.what()
<< std::endl;
@@ -567,28 +637,31 @@ static void outputOperations(
outputObject(dbContext, list[0], allowUseIntermediateCRS, outputOpt);
return;
}
+ std::cout << "Candidate operations found: " << list.size() << std::endl;
+ if (spatialCriterionPartialIntersectionResultCount > list.size()) {
+ std::cout << "Note: using '--spatial-test intersects' would bring "
+ "more results ("
+ << spatialCriterionPartialIntersectionResultCount << ")"
+ << std::endl;
+ }
if (summary) {
- std::cout << "Candidate operations found: " << list.size() << std::endl;
for (const auto &op : list) {
- outputOperationSummary(op);
+ outputOperationSummary(op, dbContext);
}
} else {
bool first = true;
for (size_t i = 0; i < list.size(); ++i) {
const auto &op = list[i];
- if (list.size() > 1) {
- if (!first) {
- std::cout << std::endl;
- }
- first = false;
- std::cout << "-------------------------------------"
- << std::endl;
- std::cout << "Operation n"
- "\xC2\xB0"
- << (i + 1) << ":" << std::endl
- << std::endl;
+ if (!first) {
+ std::cout << std::endl;
}
- outputOperationSummary(op);
+ first = false;
+ std::cout << "-------------------------------------" << std::endl;
+ std::cout << "Operation n"
+ "\xC2\xB0"
+ << (i + 1) << ":" << std::endl
+ << std::endl;
+ outputOperationSummary(op, dbContext);
std::cout << std::endl;
outputObject(dbContext, op, allowUseIntermediateCRS, outputOpt);
}
@@ -610,10 +683,11 @@ int main(int argc, char **argv) {
std::string targetCRSStr;
bool outputSwithSpecified = false;
OutputOptions outputOpt;
- bool kindIsCRS = true;
+ std::string objectKind;
bool summary = false;
ExtentPtr bboxFilter = nullptr;
std::string area;
+ bool spatialCriterionExplicitlySpecified = false;
CoordinateOperationContext::SpatialCriterion spatialCriterion =
CoordinateOperationContext::SpatialCriterion::STRICT_CONTAINMENT;
CoordinateOperationContext::SourceTargetCRSExtentUse crsExtentUse =
@@ -622,7 +696,8 @@ int main(int argc, char **argv) {
CoordinateOperationContext::GridAvailabilityUse gridAvailabilityUse =
CoordinateOperationContext::GridAvailabilityUse::USE_FOR_SORTING;
CoordinateOperationContext::IntermediateCRSUse allowUseIntermediateCRS =
- CoordinateOperationContext::IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
+ CoordinateOperationContext::IntermediateCRSUse::
+ IF_NO_DIRECT_TRANSFORMATION;
std::vector<std::pair<std::string, std::string>> pivots;
bool usePROJGridAlternatives = true;
std::string mainDBPath;
@@ -736,9 +811,11 @@ int main(int argc, char **argv) {
i++;
std::string kind(argv[i]);
if (ci_equal(kind, "crs") || ci_equal(kind, "srs")) {
- kindIsCRS = true;
+ objectKind = "crs";
} else if (ci_equal(kind, "operation")) {
- kindIsCRS = false;
+ objectKind = "operation";
+ } else if (ci_equal(kind, "ellipsoid")) {
+ objectKind = "ellipsoid";
} else {
std::cerr << "Unrecognized value for option -k: " << kind
<< std::endl;
@@ -768,6 +845,7 @@ int main(int argc, char **argv) {
} else if (arg == "--spatial-test" && i + 1 < argc) {
i++;
std::string value(argv[i]);
+ spatialCriterionExplicitlySpecified = true;
if (ci_equal(value, "contains")) {
spatialCriterion = CoordinateOperationContext::
SpatialCriterion::STRICT_CONTAINMENT;
@@ -822,9 +900,10 @@ int main(int argc, char **argv) {
if (ci_equal(std::string(value), "always")) {
allowUseIntermediateCRS =
CoordinateOperationContext::IntermediateCRSUse::ALWAYS;
- } else if (ci_equal(std::string(value), "if_no_direct_transformation")) {
- allowUseIntermediateCRS =
- CoordinateOperationContext::IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
+ } else if (ci_equal(std::string(value),
+ "if_no_direct_transformation")) {
+ allowUseIntermediateCRS = CoordinateOperationContext::
+ IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
} else if (ci_equal(std::string(value), "never")) {
allowUseIntermediateCRS =
CoordinateOperationContext::IntermediateCRSUse::NEVER;
@@ -918,77 +997,86 @@ int main(int argc, char **argv) {
}
if (outputOpt.quiet &&
- (outputOpt.PROJ5 + outputOpt.WKT2_2018 +
- outputOpt.WKT2_2015 + outputOpt.WKT1_GDAL) != 1) {
+ (outputOpt.PROJ5 + outputOpt.WKT2_2018 + outputOpt.WKT2_2015 +
+ outputOpt.WKT1_GDAL) != 1) {
std::cerr << "-q can only be used with a single output format"
<< std::endl;
usage();
}
if (!user_string.empty()) {
- auto obj(buildObject(dbContext, user_string, kindIsCRS, "input string",
- buildBoundCRSToWGS84, allowUseIntermediateCRS,
- outputOpt.quiet));
- if (guessDialect) {
- auto dialect = WKTParser().guessDialect(user_string);
- std::cout << "Guessed WKT dialect: ";
- if (dialect == WKTParser::WKTGuessedDialect::WKT2_2018) {
- std::cout << "WKT2_2018";
- } else if (dialect == WKTParser::WKTGuessedDialect::WKT2_2015) {
- std::cout << "WKT2_2015";
- } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_GDAL) {
- std::cout << "WKT1_GDAL";
- } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_ESRI) {
- std::cout << "WKT1_ESRI";
- } else {
- std::cout << "Not WKT / unknown";
+ try {
+ auto obj(buildObject(dbContext, user_string, objectKind,
+ "input string", buildBoundCRSToWGS84,
+ allowUseIntermediateCRS, outputOpt.quiet));
+ if (guessDialect) {
+ auto dialect = WKTParser().guessDialect(user_string);
+ std::cout << "Guessed WKT dialect: ";
+ if (dialect == WKTParser::WKTGuessedDialect::WKT2_2018) {
+ std::cout << "WKT2_2018";
+ } else if (dialect == WKTParser::WKTGuessedDialect::WKT2_2015) {
+ std::cout << "WKT2_2015";
+ } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_GDAL) {
+ std::cout << "WKT1_GDAL";
+ } else if (dialect == WKTParser::WKTGuessedDialect::WKT1_ESRI) {
+ std::cout << "WKT1_ESRI";
+ } else {
+ std::cout << "Not WKT / unknown";
+ }
+ std::cout << std::endl;
}
- std::cout << std::endl;
- }
- outputObject(dbContext, obj, allowUseIntermediateCRS, outputOpt);
- if (identify) {
- auto crs = dynamic_cast<CRS *>(obj.get());
- if (crs) {
- try {
- auto res = crs->identify(
- dbContext
- ? AuthorityFactory::create(NN_NO_CHECK(dbContext),
- authority)
- .as_nullable()
- : nullptr);
- std::cout << std::endl;
- std::cout << "Identification match count: " << res.size()
- << std::endl;
- for (const auto &pair : res) {
- const auto &identifiedCRS = pair.first;
- const auto &ids = identifiedCRS->identifiers();
- if (!ids.empty()) {
- std::cout << *ids[0]->codeSpace() << ":"
- << ids[0]->code() << ": " << pair.second
- << " %" << std::endl;
- } else {
- auto boundCRS =
- dynamic_cast<BoundCRS *>(identifiedCRS.get());
- if (boundCRS &&
- !boundCRS->baseCRS()->identifiers().empty()) {
- const auto &idsBase =
- boundCRS->baseCRS()->identifiers();
- std::cout << "BoundCRS of "
- << *idsBase[0]->codeSpace() << ":"
- << idsBase[0]->code() << ": "
+ outputObject(dbContext, obj, allowUseIntermediateCRS, outputOpt);
+ if (identify) {
+ auto crs = dynamic_cast<CRS *>(obj.get());
+ if (crs) {
+ try {
+ auto res = crs->identify(
+ dbContext
+ ? AuthorityFactory::create(
+ NN_NO_CHECK(dbContext), authority)
+ .as_nullable()
+ : nullptr);
+ std::cout << std::endl;
+ std::cout
+ << "Identification match count: " << res.size()
+ << std::endl;
+ for (const auto &pair : res) {
+ const auto &identifiedCRS = pair.first;
+ const auto &ids = identifiedCRS->identifiers();
+ if (!ids.empty()) {
+ std::cout << *ids[0]->codeSpace() << ":"
+ << ids[0]->code() << ": "
<< pair.second << " %" << std::endl;
} else {
- std::cout
- << "un-identifier CRS: " << pair.second
- << " %" << std::endl;
+ auto boundCRS = dynamic_cast<BoundCRS *>(
+ identifiedCRS.get());
+ if (boundCRS &&
+ !boundCRS->baseCRS()
+ ->identifiers()
+ .empty()) {
+ const auto &idsBase =
+ boundCRS->baseCRS()->identifiers();
+ std::cout << "BoundCRS of "
+ << *idsBase[0]->codeSpace() << ":"
+ << idsBase[0]->code() << ": "
+ << pair.second << " %"
+ << std::endl;
+ } else {
+ std::cout
+ << "un-identifier CRS: " << pair.second
+ << " %" << std::endl;
+ }
}
}
+ } catch (const std::exception &e) {
+ std::cerr << "Identification failed: " << e.what()
+ << std::endl;
}
- } catch (const std::exception &e) {
- std::cerr << "Identification failed: " << e.what()
- << std::endl;
}
}
+ } catch (const std::exception &e) {
+ std::cerr << "buildObject failed: " << e.what() << std::endl;
+ std::exit(1);
}
} else {
@@ -1053,10 +1141,18 @@ int main(int argc, char **argv) {
}
}
- outputOperations(
- dbContext, sourceCRSStr, targetCRSStr, bboxFilter, spatialCriterion,
- crsExtentUse, gridAvailabilityUse, allowUseIntermediateCRS, pivots, authority,
- usePROJGridAlternatives, showSuperseded, outputOpt, summary);
+ try {
+ outputOperations(dbContext, sourceCRSStr, targetCRSStr, bboxFilter,
+ spatialCriterion,
+ spatialCriterionExplicitlySpecified, crsExtentUse,
+ gridAvailabilityUse, allowUseIntermediateCRS,
+ pivots, authority, usePROJGridAlternatives,
+ showSuperseded, outputOpt, summary);
+ } catch (const std::exception &e) {
+ std::cerr << "outputOperations() failed with: " << e.what()
+ << std::endl;
+ std::exit(1);
+ }
}
return 0;
diff --git a/src/apps/utils.cpp b/src/apps/utils.cpp
new file mode 100644
index 00000000..7dc809c9
--- /dev/null
+++ b/src/apps/utils.cpp
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: Utilities for command line arguments
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#include "utils.h"
+
+#include <string.h>
+
+bool validate_form_string_for_numbers(const char* formatString) {
+ /* Only accepts '%[+]?[number]?[.]?[number]?[e|E|f|F|g|G]' */
+ bool valid = true;
+ if( formatString[0] != '%' )
+ valid = false;
+ else {
+ auto oformLen = strlen(formatString);
+ for( int i = 1; i < static_cast<int>(oformLen) - 1; i++ ) {
+ if( !(formatString[i] == '.' ||
+ formatString[i] == '+' ||
+ (formatString[i] >= '0' && formatString[i] <= '9')) ) {
+ valid = false;
+ break;
+ }
+ }
+ if( valid ) {
+ valid = formatString[oformLen-1] == 'e' ||
+ formatString[oformLen-1] == 'E' ||
+ formatString[oformLen-1] == 'f' ||
+ formatString[oformLen-1] == 'F' ||
+ formatString[oformLen-1] == 'g' ||
+ formatString[oformLen-1] == 'G';
+ }
+ }
+ return valid;
+}
diff --git a/src/apps/utils.h b/src/apps/utils.h
new file mode 100644
index 00000000..99c14091
--- /dev/null
+++ b/src/apps/utils.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: Utilities for command line arguments
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+bool validate_form_string_for_numbers(const char* formatString);