aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Knudsen <busstoptaktik@users.noreply.github.com>2017-12-17 18:04:13 +0100
committerGitHub <noreply@github.com>2017-12-17 18:04:13 +0100
commit74ae4b09bbc80edb44a123a8272014d15b7d4b8d (patch)
tree066537112cdc8624136049fade9d61d0ca9ad8b8
parent533207012bfd2c5de652b9df8b2104cad82b6988 (diff)
downloadPROJ-74ae4b09bbc80edb44a123a8272014d15b7d4b8d.tar.gz
PROJ-74ae4b09bbc80edb44a123a8272014d15b7d4b8d.zip
Free format everywhere (#693)
* Free format now in cmd lines, in gie, and in init files * Corrected handling of defaults * Add demo of integrated definition and validation * Repair stack-smashing memmove in get_init * repair paralist corruption, clean up debug output * Install test files for nmake builds * Add many improvements following suggestions by @schwehr * Be consistent in requiring lower case everywhere in gie.c Also, this Fixes #703 and Fixes #697
-rw-r--r--appveyor.yml4
-rw-r--r--nad/val_def.demo83
-rw-r--r--src/PJ_pipeline.c9
-rw-r--r--src/cct.c15
-rw-r--r--src/gie.c676
-rw-r--r--src/makefile.vc3
-rw-r--r--src/optargpm.h33
-rw-r--r--src/pj_factors.c15
-rw-r--r--src/pj_init.c636
-rw-r--r--src/pj_internal.c288
-rw-r--r--src/pj_malloc.c3
-rw-r--r--src/pj_param.c217
-rw-r--r--src/proj.def2
-rw-r--r--src/proj_4D_api.c102
-rw-r--r--src/proj_internal.h7
-rw-r--r--src/projects.h6
-rw-r--r--test/gie/GDA.gie50
-rw-r--r--test/gie/axisswap.gie118
-rw-r--r--test/gie/builtins.gie7
-rw-r--r--test/gie/deformation.gie27
-rw-r--r--test/gie/ellipsoid.gie7
-rw-r--r--test/gie/more_builtins.gie92
-rw-r--r--test/gie/pip.gie41
-rwxr-xr-xtravis/install.sh2
24 files changed, 1566 insertions, 877 deletions
diff --git a/appveyor.yml b/appveyor.yml
index 7de5d23c..92740d7e 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -58,11 +58,13 @@ test_script:
- cd bin
- echo "Contents of current directory:"
- dir
+ - echo "Contents of ..\test\gie:"
+ - dir ..\test\gie
- gie.exe ..\test\gie\builtins.gie
- - gie.exe ..\test\gie\more_builtins.gie
- gie.exe ..\test\gie\deformation.gie
- gie.exe ..\test\gie\axisswap.gie
- gie.exe ..\test\gie\ellipsoid.gie
+ - gie.exe -vvvvv ..\test\gie\more_builtins.gie
- gie.exe ..\test\gie\GDA.gie
deploy: off
diff --git a/nad/val_def.demo b/nad/val_def.demo
new file mode 100644
index 00000000..87777b9d
--- /dev/null
+++ b/nad/val_def.demo
@@ -0,0 +1,83 @@
+
+-----------------------------------------------------------------------
+
+ INTEGRATING DEFINITION AND VALIDATION OF GEODETIC SYSTEMS
+
+-----------------------------------------------------------------------
+ Thomas Knudsen, thokn@sdfe.dk, 2017-12-06
+-----------------------------------------------------------------------
+
+This demo shows how to use the free format definition strings,
+introduced in PROJ version 5.0.0, to integrate system definition
+information with system validation data.
+
+The system definition parts are used when doing actual transformations,
+e.g. using the cct 4D transformation program:
+
+ echo 9 55 0 0 | cct +init=val_def.demo:DKTM1
+
+The system validation parts are used when validating the systems
+defined in the file. This is done using the gie test program:
+
+ gie val_def.demo
+
+Which will respond with a report detailing how many tests succeeded,
+resp. failed.
+
+The syntax of proj init files is orthogonal to the syntax of gie
+integrity evaluation files. This makes it possible to interleave
+init and gie blocks in the same file.
+
+#-----------------------------------------------------------------------
+
+
+
+#-----------------------------------------------------------------------
+# Danish Transverse Mercator, zone 1
+#-----------------------------------------------------------------------
+<DKTM1> proj = etmerc
+ lat_0 = 0 lon_0 = 9
+ x_0 = 200000 y_0 = -5000000
+
+ k = 0.99998
+
+ ellps = GRS80
+ units = m
+
+ no_defs
+#-----------------------------------------------------------------------
+<gie>
+operation init = val_def.demo:DKTM1
+tolerance 100 um
+accept 9 55
+expect 200000.0000 1097108.3684
+roundtrip 1000 1 nm
+</gie>
+#-----------------------------------------------------------------------
+
+
+
+#-----------------------------------------------------------------------
+# Danish Transverse Mercator, zone 2
+#-----------------------------------------------------------------------
+<DKTM2> proj = etmerc
+ lat_0 = 0 lon_0 = 10
+ x_0 = 400000 y_0 = -5000000
+
+ k = 0.99998
+
+ ellps = GRS80
+ units = m
+
+ no_defs
+#-----------------------------------------------------------------------
+<gie>
+operation init = val_def.demo:DKTM2
+tolerance 100 um
+accept 10 55
+expect 400000.0000 1097108.3684
+accept 10.5 55.5
+expect 431597.1668 1152884.9398
+roundtrip 1000 100 um
+</gie>
+#-----------------------------------------------------------------------
diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c
index c8ce8582..a0ef7c1e 100644
--- a/src/PJ_pipeline.c
+++ b/src/PJ_pipeline.c
@@ -51,12 +51,9 @@
pj_init code, implementing support for multilevel, embedded pipelines.
Syntactically, the pipeline system introduces the "+step" keyword (which
- indicates the start of each transformation step), the "+omit_fwd" and
- "+omit_inv" keywords (which indicate that a given transformation step
- should be omitted when the pipeline is executed in forward, resp. inverse
- direction), and reintroduces the +inv keyword (indicating that a given
- transformation step should run in reverse, i.e. forward, when the pipeline
- is executed in inverse direction, and vice versa).
+ indicates the start of each transformation step), and reintroduces the +inv
+ keyword (indicating that a given transformation step should run in reverse, i.e.
+ forward, when the pipeline is executed in inverse direction, and vice versa).
Hence, the first transformation example above, can be implemented as:
diff --git a/src/cct.c b/src/cct.c
index a1c275b5..eae815e9 100644
--- a/src/cct.c
+++ b/src/cct.c
@@ -71,20 +71,23 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26
***********************************************************************/
-#include "optargpm.h"
-#include "proj_internal.h"
-#include <proj.h>
-#include "projects.h"
+#include <ctype.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
#include <string.h>
-#include <math.h>
+
+#include <proj.h>
+#include "proj_internal.h"
+#include "projects.h"
+#include "optargpm.h"
+/* Prototypes for functions in proj_strtod.c */
double proj_strtod(const char *str, char **endptr);
double proj_atof(const char *str);
+/* Prototypes from functions in this file */
char *column (char *buf, int n);
PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time);
diff --git a/src/gie.c b/src/gie.c
index c768c765..e23ca92b 100644
--- a/src/gie.c
+++ b/src/gie.c
@@ -15,9 +15,9 @@ C compiler already employed for compiling the library.
The basic functionality of the gie command language is implemented
through just 3 command verbs:
-OPERATION, which defines the PROJ.4 operation to test,
-ACCEPT, which defines the input coordinate to read, and
-EXPECT, which defines the result to expect.
+operation, which defines the PROJ.4 operation to test,
+accept, which defines the input coordinate to read, and
+expect, which defines the result to expect.
E.g:
@@ -32,7 +32,7 @@ long strings of numbers typically required in projected coordinates.
By default, gie considers the EXPECTation met, if the result comes to
within 0.5 mm of the expected. This default can be changed using the
-TOLERANCE command verb (and yes, I know, linguistically speaking, both
+'tolerance' command verb (and yes, I know, linguistically speaking, both
"operation" and "tolerance" are nouns, not verbs). See the first
few hundred lines of the "builtins.gie" test file for more details of
the command verbs available (verbs of both the VERBal and NOUNistic
@@ -64,15 +64,16 @@ hence making a map projection function call, pj_fwd(PJ, point), as easy
as a traditional function call like hypot(x,y).
While today, we may have more formally well defined metadata systems
-(most prominent the OGC WKT representation), nothing comes close being
+(most prominent the OGC WKT2 representation), nothing comes close being
as easily readable ("human compatible") as Gerald's key-value system.
This system in particular, and the PROJ.4 system in general, was
Gerald's great gift to anyone using and/or communicating about geodata.
-It is only reasonable to name a program keeping an eye on the integrity
-of the PROJ.4 system in honour of Gerald. So in honour, and hopefully
-also in the spirit, of Gerald Ian Evenden (1935--2016), this is the
-Geospatial Integrity Investigation Environment.
+It is only reasonable to name a program, keeping an eye on the integrity
+of the PROJ.4 system, in honour of Gerald.
+
+So in honour, and hopefully also in the spirit, of Gerald Ian Evenden
+(1935--2016), this is the Geospatial Integrity Investigation Environment.
************************************************************************
@@ -103,22 +104,54 @@ Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08
***********************************************************************/
-#include "optargpm.h"
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include <string.h>
-#include <ctype.h>
+#include "optargpm.h"
-#include <math.h>
-#include <errno.h>
+/* Package for flexible format I/O - ffio */
+typedef struct ffio {
+ FILE *f;
+ const char **tags;
+ const char *tag;
+ char *args;
+ char *next_args;
+ size_t n_tags;
+ size_t args_size;
+ size_t next_args_size;
+ size_t argc;
+ size_t lineno, next_lineno;
+ size_t level;
+} ffio;
+
+FILE *test = 0;
+
+static int get_inp (ffio *F);
+static int skip_to_next_tag (ffio *F);
+static int step_into_gie_block (ffio *F);
+static int locate_tag (ffio *F, const char *tag);
+static int nextline (ffio *F);
+static int at_end_delimiter (ffio *F);
+static const char *at_tag (ffio *F);
+static int at_decorative_element (ffio *F);
+static ffio *ffio_destroy (ffio *F);
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
+
+static const char *gie_tags[] = {
+ "<gie>", "operation", "accept", "expect", "roundtrip", "banner", "verbose",
+ "direction", "tolerance", "builtins", "echo", "</gie>"
+};
+static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0];
/* from proj_strtod.c */
@@ -127,16 +160,14 @@ double proj_atof(const char *str);
int main(int argc, char **argv);
-static int process_file (const char *fname);
-static int errmsg (int errlev, const char *msg, ...);
-static int get_inp (FILE *f, char *inp, int size);
-static int get_cmnd (const char *inp, char *cmnd, int len);
-static const char *get_args (const char *inp);
static int dispatch (const char *cmnd, const char *args);
+static int errmsg (int errlev, const char *msg, ...);
+static int errno_from_err_const (const char *err_const);
+static int list_err_codes (void);
+static int process_file (const char *fname);
+
static const char *column (const char *buf, int n);
-static int errno_from_err_const (const char *err_const);
static const char *err_const_from_errno (int err);
-static void list_err_codes (void);
@@ -148,7 +179,6 @@ typedef struct {
PJ_COORD a, b, c, e;
PJ_DIRECTION dir;
int verbosity;
- int nargs;
int op_id;
int op_ok, op_ko;
int total_ok, total_ko;
@@ -159,20 +189,15 @@ typedef struct {
FILE *fout;
} gie_ctx;
-gie_ctx T = {{""}, 0, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, {{0,0,0,0}}, PJ_FWD, 1, 0, 0,0,0,0,0,0,0,0, 0.0005, 0, 0};
+ffio *F = 0;
-OPTARGS *o;
+static gie_ctx T;
+static const char delim[] = {"-------------------------------------------------------------------------------\n"};
-size_t tol_lineno = 0;
-size_t lineno = 0;
-size_t level = 0;
-char delim[] = {"-------------------------------------------------------------------------------\n"};
-char DELIM[] = {"===============================================================================\n"};
-
-#define CMDLEN 25000
+#define CMDLEN 250000
int nfiles = 0;
@@ -212,7 +237,14 @@ int main (int argc, char **argv) {
int i;
const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", 0};
const char *longkeys[] = {"o=output", 0};
+ OPTARGS *o;
+
+ memset (&T, 0, sizeof (T));
+ T.dir = PJ_FWD;
+ T.verbosity = 1;
+ T.tolerance = 5e-4;
+ test = fopen ("test.c", "wt");
o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys);
if (0==o)
return 0;
@@ -223,7 +255,7 @@ int main (int argc, char **argv) {
}
if (opt_given (o, "l"))
- list_err_codes ();
+ return list_err_codes ();
T.verbosity = opt_given (o, "q");
@@ -235,6 +267,7 @@ int main (int argc, char **argv) {
T.fout = stdout;
if (opt_given (o, "o"))
T.fout = fopen (opt_arg (o, "output"), "rt");
+
if (0==T.fout) {
fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output"));
free (o);
@@ -245,10 +278,18 @@ int main (int argc, char **argv) {
if (T.verbosity==-1)
return -1;
fprintf (T.fout, "Nothing to do\n");
+ free (o);
return 0;
}
- for (i = 0; i < o->fargc; i++)
+ F = ffio_create (gie_tags, n_gie_tags, 1000);
+ if (0==F) {
+ fprintf (stderr, "%s: No memory\n", o->progname);
+ free (o);
+ return 1;
+ }
+
+ for (i = 0; i < o->fargc; i++)
process_file (o->fargv[i]);
if (T.verbosity > 0) {
@@ -264,6 +305,7 @@ int main (int argc, char **argv) {
fclose (T.fout);
free (o);
+ ffio_destroy (F);
return T.grand_ko;
}
@@ -282,11 +324,8 @@ static int another_success (void) {
static int process_file (const char *fname) {
FILE *f;
- char inp[CMDLEN];
- char cmnd[1000];
- const char *args;
- lineno = level = 0;
+ F->lineno = F->next_lineno = F->level = 0;
T.op_ok = T.total_ok = 0;
T.op_ko = T.total_ko = 0;
@@ -298,37 +337,29 @@ static int process_file (const char *fname) {
}
errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname);
}
+ F->f = f;
+
if (T.verbosity > 0)
fprintf (T.fout, "%sReading file '%s'\n", delim, fname);
T.curr_file = fname;
- while (get_inp(f, inp, CMDLEN)) {
- int len;
-
- if (feof(f))
- break;
- len = get_cmnd (inp, cmnd, 1000);
- if (len>=999) {
- errmsg (2, "Command verb too long: '%s' - bye!\n", cmnd);
- proj_destroy (T.P);
- T.P = 0;
- return 0;
- }
- args = get_args (inp);
- if (SKIP==dispatch (cmnd, args))
+ while (get_inp(F)) {
+ if (SKIP==dispatch (F->tag, F->args))
return proj_destroy (T.P), T.P = 0, 0;
}
+
fclose (f);
+ F->lineno = F->next_lineno = 0;
T.grand_ok += T.total_ok;
T.grand_ko += T.total_ko;
if (T.verbosity > 0)
fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests %s\n", delim, T.total_ok, T.total_ko, T.total_ko? "FAILED!": "failed.");
- if (level==0)
- return errmsg (-3, "File '%s':Missing 'BEGIN' cmnd - bye!\n", fname);
- if (level && level%2)
- return errmsg (-4, "File '%s':Missing 'END' cmnd - bye!\n", fname);
+ if (F->level==0)
+ return errmsg (-3, "File '%s':Missing '<gie>' cmnd - bye!\n", fname);
+ if (F->level && F->level%2)
+ return errmsg (-4, "File '%s':Missing '</gie>' cmnd - bye!\n", fname);
return 0;
}
@@ -336,7 +367,7 @@ static int process_file (const char *fname) {
/*****************************************************************************/
const char *column (const char *buf, int n) {
/*****************************************************************************
- Return a pointer to the n'th column of buf. Coulmn numbers start at 0.
+Return a pointer to the n'th column of buf. Column numbers start at 0.
******************************************************************************/
int i;
if (n <= 0)
@@ -356,8 +387,8 @@ const char *column (const char *buf, int n) {
/*****************************************************************************/
static double strtod_scaled (const char *args, double default_scale) {
/*****************************************************************************
- Interpret <args> as a numeric followed by a linear decadal prefix.
- Return the properly scaled numeric
+Interpret <args> as a numeric followed by a linear decadal prefix.
+Return the properly scaled numeric
******************************************************************************/
double s;
const char *endp = args;
@@ -424,6 +455,7 @@ static int direction (const char *args) {
default:
return 1;
}
+
return 0;
}
@@ -439,46 +471,38 @@ static void finish_previous_operation (const char *args) {
/*****************************************************************************/
static int operation (char *args) {
/*****************************************************************************
- Define the operation to apply to the input data (in ISO 19100 lingo,
- an operation is the general term describing something that can be
- either a conversion or a transformation)
+Define the operation to apply to the input data (in ISO 19100 lingo,
+an operation is the general term describing something that can be
+either a conversion or a transformation)
******************************************************************************/
- int i, j, n;
T.op_id++;
- T.operation_lineno = lineno;
-
- /* compactify the args, so we can fit more info on a line in verbose mode */
- n = (int) strlen (args);
- for (i = j = 0; i < n; ) {
- /* skip prefix whitespace */
- while (isspace (args[i]))
- i++;
- /* move a whitespace delimited text string to the left, skipping over superfluous whitespace */
- while ((0!=args[i]) && (!isspace (args[i])))
- args[j++] = args[i++];
- if (args[j+1]!=0)
- args[j++] = ' ';
- i++;
- }
- args[j++] = 0;
- strcpy (&(T.operation[0]), args);
+ T.operation_lineno = F->lineno;
+
+ strcpy (&(T.operation[0]), F->args);
if (T.verbosity > 1) {
- finish_previous_operation (args);
+ finish_previous_operation (F->args);
banner (args);
}
+
T.op_ok = 0;
T.op_ko = 0;
direction ("forward");
tolerance ("0.5 mm");
+ proj_errno_reset (T.P);
if (T.P)
proj_destroy (T.P);
- T.P = proj_create (0, args);
+ proj_errno_reset (0);
+
+ T.P = proj_create (0, F->args);
+
+ /* Checking that proj_create succeeds is first done at "expect" time, */
+ /* since we want to support "expect"ing specific error codes */
return 0;
}
@@ -493,9 +517,9 @@ static int pj_horner_selftest (void);
/*****************************************************************************/
static int builtins (const char *args) {
/*****************************************************************************
- There are still a few tests that cannot be described using gie
- primitives. Instead, they are implemented as builtins, and invoked
- using the "builtins" command verb.
+There are still a few tests that cannot be described using gie
+primitives. Instead, they are implemented as builtins, and invoked
+using the "builtins" command verb.
******************************************************************************/
int i;
if (T.verbosity > 1) {
@@ -504,10 +528,9 @@ static int builtins (const char *args) {
}
T.op_ok = 0;
T.op_ko = 0;
-
i = pj_unitconvert_selftest ();
if (i!=0) {
- printf ("pj_unitconvert_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_unitconvert_selftest fails with %d\n", i);
another_failure();
}
else
@@ -516,7 +539,7 @@ static int builtins (const char *args) {
i = pj_cart_selftest ();
if (i!=0) {
- printf ("pj_cart_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_cart_selftest fails with %d\n", i);
another_failure();
}
else
@@ -524,7 +547,7 @@ static int builtins (const char *args) {
i = pj_horner_selftest ();
if (i!=0) {
- printf ("pj_horner_selftest fails with %d\n", i);
+ fprintf (T.fout, "pj_horner_selftest fails with %d\n", i);
another_failure();
}
else
@@ -554,7 +577,7 @@ static PJ_COORD todeg_coord (PJ_COORD a) {
/*****************************************************************************/
static PJ_COORD parse_coord (const char *args) {
/*****************************************************************************
- Attempt to interpret args as a PJ_COORD.
+Attempt to interpret args as a PJ_COORD.
******************************************************************************/
int i;
const char *endp, *prev = args;
@@ -575,7 +598,7 @@ static PJ_COORD parse_coord (const char *args) {
/*****************************************************************************/
static int accept (const char *args) {
/*****************************************************************************
- Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate.
+Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate.
******************************************************************************/
T.a = parse_coord (args);
if (T.verbosity > 3)
@@ -587,8 +610,8 @@ static int accept (const char *args) {
/*****************************************************************************/
static int roundtrip (const char *args) {
/*****************************************************************************
- Check how far we go from the ACCEPTed point when doing successive
- back/forward transformation pairs.
+Check how far we go from the ACCEPTed point when doing successive
+back/forward transformation pairs.
******************************************************************************/
int ntrips;
double d, r, ans;
@@ -602,7 +625,6 @@ static int roundtrip (const char *args) {
ntrips = (int) (endp==args? 100: fabs(ans));
d = strtod_scaled (endp, 1);
d = d==HUGE_VAL? T.tolerance: d;
- coo = T.a;
/* input ("accepted") values - probably in degrees */
coo = proj_angular_input (T.P, T.dir)? torad_coord (T.a): T.a;
@@ -615,7 +637,7 @@ static int roundtrip (const char *args) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " roundtrip deviation: %.3f mm, expected: %.3f mm\n", 1000*r, 1000*d);
}
return another_failure ();
@@ -633,7 +655,7 @@ static int expect_message (double d, const char *args) {
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " expected: %s\n", args);
fprintf (T.fout, " got: %.9f %.9f", T.b.xy.x, T.b.xy.y);
if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0)
@@ -652,7 +674,7 @@ static int expect_message_cannot_parse (const char *args) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) lineno, args);
+ fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args);
}
return 1;
}
@@ -665,7 +687,7 @@ static int expect_failure_with_errno_message (int expected, int got) {
if (0==T.op_ko && T.verbosity < 2)
banner (T.operation);
fprintf (T.fout, "%s", T.op_ko? " -----\n": delim);
- fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) lineno);
+ fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno);
fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, pj_strerrno (got));
fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, pj_strerrno (expected));
fprintf (T.fout, "\n");
@@ -676,7 +698,7 @@ static int expect_failure_with_errno_message (int expected, int got) {
/*****************************************************************************/
static int expect (const char *args) {
/*****************************************************************************
- Tell GIE what to expect, when transforming the ACCEPTed input
+Tell GIE what to expect, when transforming the ACCEPTed input
******************************************************************************/
PJ_COORD ci, co, ce;
double d;
@@ -737,6 +759,7 @@ static int expect (const char *args) {
puts (T.dir== 1? "forward": "reverse");
puts (proj_angular_input (T.P, T.dir)? "angular in": "linear in");
puts (proj_angular_output (T.P, T.dir)? "angular out": "linear out");
+ printf ("left: %d right: %d\n", T.P->left, T.P->right);
}
T.e = parse_coord (args);
@@ -753,7 +776,6 @@ static int expect (const char *args) {
if (T.verbosity > 3)
printf ("ACCEPTS %.4f %.4f %.4f %.4f\n", ci.v[0],ci.v[1],ci.v[2],ci.v[3]);
-
/* angular output from proj_trans comes in radians */
co = proj_trans (T.P, T.dir, ci);
T.b = proj_angular_output (T.P, T.dir)? todeg_coord (co): co;
@@ -791,7 +813,7 @@ static int expect (const char *args) {
/*****************************************************************************/
static int verbose (const char *args) {
/*****************************************************************************
- Tell the system how noisy it should be
+Tell the system how noisy it should be
******************************************************************************/
int i = (int) proj_atof (args);
@@ -806,20 +828,11 @@ static int verbose (const char *args) {
return 0;
}
-/*****************************************************************************/
-static int comment (const char *args) {
-/*****************************************************************************
- in line comment. Equivalent to #
-******************************************************************************/
- (void) args;
- return 0;
-}
-
/*****************************************************************************/
static int echo (const char *args) {
/*****************************************************************************
- Add user defined noise to the output stream
+Add user defined noise to the output stream
******************************************************************************/
fprintf (T.fout, "%s\n", args);
return 0;
@@ -828,46 +841,17 @@ fprintf (T.fout, "%s\n", args);
static int dispatch (const char *cmnd, const char *args) {
-#if 0
- int last_errno = proj_errno_reset (T.P);
-#endif
-
- if (0==level%2) {
- if (0==strcmp (cmnd, "BEGIN") || 0==strcmp (cmnd, "<begin>") || 0==strcmp (cmnd, "<gie>"))
- level++;
- return 0;
- }
-
- if (0==strcmp (cmnd, "OPERATION")) return operation ((char *) args);
if (0==strcmp (cmnd, "operation")) return operation ((char *) args);
- if (0==strcmp (cmnd, "ACCEPT")) return accept (args);
if (0==strcmp (cmnd, "accept")) return accept (args);
- if (0==strcmp (cmnd, "EXPECT")) return expect (args);
if (0==strcmp (cmnd, "expect")) return expect (args);
- if (0==strcmp (cmnd, "ROUNDTRIP")) return roundtrip (args);
if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args);
- if (0==strcmp (cmnd, "BANNER")) return banner (args);
if (0==strcmp (cmnd, "banner")) return banner (args);
- if (0==strcmp (cmnd, "VERBOSE")) return verbose (args);
if (0==strcmp (cmnd, "verbose")) return verbose (args);
- if (0==strcmp (cmnd, "DIRECTION")) return direction (args);
if (0==strcmp (cmnd, "direction")) return direction (args);
- if (0==strcmp (cmnd, "TOLERANCE")) return tolerance (args);
if (0==strcmp (cmnd, "tolerance")) return tolerance (args);
- if (0==strcmp (cmnd, "BUILTINS")) return builtins (args);
if (0==strcmp (cmnd, "builtins")) return builtins (args);
- if (0==strcmp (cmnd, "ECHO")) return echo (args);
if (0==strcmp (cmnd, "echo")) return echo (args);
- if (0==strcmp (cmnd, "END")) return finish_previous_operation (args), level++, 0;
- if (0==strcmp (cmnd, "<end>")) return finish_previous_operation (args), level++, 0;
- if (0==strcmp (cmnd, "</gie>")) return finish_previous_operation (args), level++, 0;
- if ('#'==cmnd[0]) return comment (args);
-
-#if 0
- if (proj_errno(T.P))
- printf ("#####***** ERRNO=%d\n", proj_errno(T.P));
- proj_errno_restore (T.P, last_errno);
-#endif
+
return 0;
}
@@ -941,7 +925,7 @@ static const struct errno_vs_err_const lookup[] = {
static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999};
-static void list_err_codes (void) {
+static int list_err_codes (void) {
int i;
const int n = sizeof lookup / sizeof lookup[0];
@@ -950,7 +934,7 @@ static void list_err_codes (void) {
break;
printf ("%25s (%2.2d): %s\n", lookup[i].the_err_const + 8, lookup[i].the_errno, pj_strerrno(lookup[i].the_errno));
}
- exit (0);
+ return 0;
}
@@ -966,7 +950,6 @@ static const char *err_const_from_errno (int err) {
}
-
static int errno_from_err_const (const char *err_const) {
const size_t n = sizeof lookup / sizeof lookup[0];
size_t i, len;
@@ -1006,12 +989,6 @@ static int errno_from_err_const (const char *err_const) {
}
-
-
-
-
-
-
static int errmsg (int errlev, const char *msg, ...) {
va_list args;
va_start(args, msg);
@@ -1022,105 +999,318 @@ static int errmsg (int errlev, const char *msg, ...) {
return errlev;
}
-#define skipspace(f, c) \
- do { \
- while (isspace (c=fgetc(f)) && !feof(f)){ \
- if ('\n'==c) lineno++; \
- } \
- if (feof(f)) \
- break; \
- } while (ungetc(c, f), 0)
-
-#define skipline(f, c) \
- do { \
- while ((c=fgetc(f)) && !feof(f)) { \
- if ((c=='\r') || (c=='\n')) \
- break; \
- } \
- skipspace (f, c); \
- } while (0)
-
-
-/* skip whitespace at continuation line */
-#define continuation(f, buf, c) \
- if ((c=='\r')||(c=='\n')) { \
- if (c=='\n') lineno++; \
- next--; \
- while (isspace (c=fgetc(f)) && !feof(f)); \
- }
-static int get_inp (FILE *f, char *inp, int size) {
- char *next;
- int c = 0, esc;
- char *endp = inp + size - 2;
-
- skipspace (f, c);
-
- for (c = esc = 0, next = inp; !feof(f); ) {
- c = fgetc(f);
- if (esc) {
- continuation (f, next, c);
- esc = 0;
- /* handle escape sequences here */
- switch (c) {
- case '\\': c = '\\'; break;
- default: (void) c;
- }
- }
- if (c=='\r')
- break;
- if (c=='\n') {
- lineno++;
- break;
- }
- *next++ = (char) c;
- if ('\\'==c)
- esc = 1;
- if (feof(f) || (next==endp))
- break;
+
+
+
+
+
+/****************************************************************************************
+
+FFIO - Flexible format I/O
+
+FFIO provides functionality for reading proj style instruction strings written
+in a less strict format than usual:
+
+* Whitespace is generally allowed everywhere
+* Comments can be written inline, '#' style
+* ... or as free format blocks
+
+The overall mission of FFIO is to facilitate communications of geodetic
+parameters and test material in a format that is highly human readable,
+and provides ample room for comment, documentation, and test material.
+
+See the PROJ ".gie" test suites for examples of supported formatting.
+
+****************************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <string.h>
+#include <ctype.h>
+
+#include <math.h>
+#include <errno.h>
+
+
+
+static int get_inp (ffio *F);
+static int skip_to_next_tag (ffio *F);
+static int step_into_gie_block (ffio *F);
+static int locate_tag (ffio *F, const char *tag);
+static int nextline (ffio *F);
+static int at_end_delimiter (ffio *F);
+static const char *at_tag (ffio *F);
+static int at_decorative_element (ffio *F);
+static ffio *ffio_destroy (ffio *F);
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size);
+
+
+
+/***************************************************************************************/
+static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) {
+/****************************************************************************************
+Constructor for the ffio object.
+****************************************************************************************/
+ ffio *G = calloc (1, sizeof (ffio));
+ if (0==G)
+ return 0;
+
+ if (0==max_record_size)
+ max_record_size = 1000;
+
+ G->args = calloc (1, 5*max_record_size);
+ if (0==G->args) {
+ free (G);
+ return 0;
}
- *(next) = 0;
- return (int) strlen(inp);
+
+ G->next_args = calloc (1, max_record_size);
+ if (0==G->args) {
+ free (G->args);
+ free (G);
+ return 0;
+ }
+
+ G->args_size = 5*max_record_size;
+ G->next_args_size = max_record_size;
+
+ G->tags = tags;
+ G->n_tags = n_tags;
+ return G;
}
-static int get_cmnd (const char *inp, char *cmnd, int len) {
- cmnd[0] = 0;
- while (isspace(*inp++));
- inp--;
- while (len-- && !isspace(*inp) && *inp)
- *cmnd++ = *inp++;
- *cmnd = 0;
- return len;
+
+
+/***************************************************************************************/
+static ffio *ffio_destroy (ffio *G) {
+/****************************************************************************************
+Free all allocated associated memory, then free G itself. For extra RAII compliancy,
+the file object should also be closed if still open, but this will require additional
+control logic, and ffio is a gie tool specific package, so we fall back to asserting that
+fclose has been called prior to ffio_destroy.
+****************************************************************************************/
+ free (G->args);
+ free (G->next_args);
+ free (G);
+ return 0;
}
-static const char *get_args (const char *inp) {
- const char *args = inp;
- while (isspace(*args++))
- if (0==*args)
- return args;
- while (!isspace(*++args))
- if (0==*args)
- return args;
- while (isspace(*args++))
- if (0==*args)
- return args;
- return --args;
+
+
+/***************************************************************************************/
+static int at_decorative_element (ffio *G) {
+/****************************************************************************************
+A decorative element consists of a line of at least 5 consecutive identical chars,
+starting at buffer position 0:
+"-----", "=====", "*****", etc.
+
+A decorative element serves as a end delimiter for the current element, and
+continues until a gie command verb is found at the start of a line
+****************************************************************************************/
+ int i;
+ char *c;
+ if (0==G)
+ return 0;
+ c = G->next_args;
+ if (0==c)
+ return 0;
+ if (0==c[0])
+ return 0;
+ for (i = 1; i < 5; i++)
+ if (c[i]!=c[0])
+ return 0;
+ return 1;
+}
+
+
+
+/***************************************************************************************/
+static const char *at_tag (ffio *G) {
+/****************************************************************************************
+A start of a new command serves as an end delimiter for the current command
+****************************************************************************************/
+ size_t j;
+ for (j = 0; j < G->n_tags; j++)
+ if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0)
+ return G->tags[j];
+ return 0;
}
+/***************************************************************************************/
+static int at_end_delimiter (ffio *G) {
+/****************************************************************************************
+An instruction consists of everything from its introductory tag to its end
+delimiter. An end delimiter can be either the introductory tag of the next
+instruction, or a "decorative element", i.e. one of the "ascii art" style
+block delimiters typically used to mark up block comments in a free format
+file.
+****************************************************************************************/
+ if (G==0)
+ return 0;
+ if (at_decorative_element (G))
+ return 1;
+ if (at_tag (G))
+ return 1;
+ return 0;
+}
+/***************************************************************************************/
+static int nextline (ffio *G) {
+/****************************************************************************************
+Read next line of input file. Returns 1 on success, 0 on failure.
+****************************************************************************************/
+ G->next_args[0] = 0;
+ if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f))
+ return 0;
+ if (feof (G->f))
+ return 0;
+ pj_chomp (G->next_args);
+ G->next_lineno++;
+ return 1;
+}
+/***************************************************************************************/
+static int locate_tag (ffio *G, const char *tag) {
+/****************************************************************************************
+Find start-of-line tag (currently only used to search for for <gie>, but any tag
+valid).
+
+Returns 1 on success, 0 on failure.
+****************************************************************************************/
+ size_t n = strlen (tag);
+ while (0!=strncmp (tag, G->next_args, n))
+ if (0==nextline (G))
+ return 0;
+ return 1;
+}
+
+/***************************************************************************************/
+static int step_into_gie_block (ffio *G) {
+/****************************************************************************************
+Make sure we're inside a <gie>-block. Return 1 on success, 0 otherwise.
+****************************************************************************************/
+ /* Already inside */
+ if (G->level % 2)
+ return 1;
+ if (0==locate_tag (G, "<gie>"))
+ return 0;
+ while (0!=strncmp ("<gie>", G->next_args, 5)) {
+ G->next_args[0] = 0;
+ if (feof (G->f))
+ return 0;
+ if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f))
+ return 0;
+ pj_chomp (G->next_args);
+ G->next_lineno++;
+ }
+ G->level++;
+ /* We're ready at the start - now step into the block */
+ return nextline (G);
+}
+
+
+
+/***************************************************************************************/
+static int skip_to_next_tag (ffio *G) {
+/****************************************************************************************
+Skip forward to the next command tag. Return 1 on success, 0 otherwise.
+****************************************************************************************/
+ const char *c;
+ if (0==step_into_gie_block (G))
+ return 0;
+
+ c = at_tag (G);
+
+ /* If not already there - get there */
+ while (!c) {
+ if (0==nextline (G))
+ return 0;
+ c = at_tag (G);
+ }
+
+ /* If we reached the end of a <gie> block, locate the next and retry */
+ if (0==strcmp (c, "</gie>")) {
+ G->level++;
+ if (feof (G->f))
+ return 0;
+ if (0==step_into_gie_block (G))
+ return 0;
+ G->args[0] = 0;
+ return skip_to_next_tag (G);
+ }
+ G->lineno = G->next_lineno;
+
+ return 1;
+}
+
+/* Add the most recently read line of input to the block already stored. */
+static int append_args (ffio *G) {
+ size_t skip_chars = 0;
+ size_t next_len = strlen (G->next_args);
+ size_t args_len = strlen (G->args);
+ const char *tag = at_tag (G);
+
+ if (tag)
+ skip_chars = strlen (tag);
+
+ if (G->args_size < args_len + next_len - skip_chars + 1) {
+ void *p = realloc (G->args, 2 * G->args_size);
+ if (0==p)
+ return 0;
+ G->args = p;
+ G->args_size = 2 * G->args_size;
+ }
+
+ G->args[args_len] = ' ';
+ strcpy (G->args + args_len + 1, G->next_args + skip_chars);
+
+ G->next_args[0] = 0;
+ return 1;
+}
+
+
+
+
+
+/***************************************************************************************/
+static int get_inp (ffio *G) {
+/****************************************************************************************
+The primary command reader for gie. Reads a block of gie input, cleans up repeated
+whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise.
+****************************************************************************************/
+ G->args[0] = 0;
+
+ if (0==skip_to_next_tag (G))
+ return 0;
+ G->tag = at_tag (G);
+
+ if (0==G->tag)
+ return 0;
+
+ do {
+ append_args (G);
+ if (0==nextline (G))
+ return 0;
+ } while (!at_end_delimiter (G));
+
+ pj_shrink (G->args);
+ return 1;
+}
@@ -1171,6 +1361,7 @@ static int pj_horner_selftest (void) {
dist = proj_roundtrip (P, PJ_FWD, 1, &c);
if (dist > 0.01)
return 1;
+ proj_destroy(P);
/* The complex polynomial transformation between the "System Storebaelt" and utm32/ed50 */
P = proj_create (PJ_DEFAULT_CTX, sb_utm32);
@@ -1239,7 +1430,7 @@ static int pj_cart_selftest (void) {
size_t n, sz;
double dist, h, t;
char *args[3] = {"proj=utm", "zone=32", "ellps=GRS80"};
- char *arg = {"+proj=utm +zone=32 +ellps=GRS80"};
+ const char *arg = {"+proj=utm +zone=32 +ellps=GRS80"};
char buf[40];
/* An utm projection on the GRS80 ellipsoid */
@@ -1279,7 +1470,7 @@ static int pj_cart_selftest (void) {
return 3;
/* Clear any previous error */
- proj_errno_set (P, 0);
+ proj_errno_reset (P);
/* Invalid projection */
a = proj_trans (P, 42, a);
@@ -1290,7 +1481,7 @@ static int pj_cart_selftest (void) {
return 5;
/* Clear error again */
- proj_errno_set (P, 0);
+ proj_errno_reset (P);
/* Clean up */
proj_destroy (P);
@@ -1310,9 +1501,9 @@ static int pj_cart_selftest (void) {
b = proj_trans (P, PJ_FWD, a);
/* Check roundtrip precision for 10000 iterations each way */
- dist = proj_roundtrip (P, PJ_FWD, 10000, &a);
- dist = proj_roundtrip (P, PJ_INV, 10000, &b);
- if (dist > 2e-9)
+ dist = proj_roundtrip (P, PJ_FWD, 10000, &a);
+ dist += proj_roundtrip (P, PJ_INV, 10000, &b);
+ if (dist > 4e-9)
return 7;
@@ -1463,6 +1654,7 @@ static int pj_cart_selftest (void) {
/* proj_info() */
/* this one is difficult to test, since the output changes with the setup */
info = proj_info();
+
if (info.version[0] != '\0' ) {
char tmpstr[64];
sprintf(tmpstr, "%d.%d.%d", info.major, info.minor, info.patch);
@@ -1481,6 +1673,7 @@ static int pj_cart_selftest (void) {
if ( !pj_info.has_inverse ) { proj_destroy(P); return 61; }
if ( strcmp(pj_info.definition, arg) ) { proj_destroy(P); return 62; }
if ( strcmp(pj_info.id, "utm") ) { proj_destroy(P); return 63; }
+
proj_destroy(P);
/* proj_grid_info() */
@@ -1500,7 +1693,6 @@ static int pj_cart_selftest (void) {
if ( strcmp(init_info.name, "epsg") ) return 68;
-
/* test proj_rtodms() and proj_dmstor() */
if (strcmp("180dN", proj_rtodms(buf, M_PI, 'N', 'S')))
return 70;
@@ -1616,14 +1808,6 @@ static int pj_cart_selftest (void) {
-
-
-
-
-
-
-
-
static int test_time(const char* args, double tol, double t_in, double t_exp) {
PJ_COORD in, out;
PJ *P = proj_create(PJ_DEFAULT_CTX, args);
@@ -1706,10 +1890,10 @@ static int pj_unitconvert_selftest (void) {
ret = test_time(args3, 1e-6, in3, in3); if (ret) return ret + 30;
ret = test_time(args4, 1e-6, in4, exp4); if (ret) return ret + 40;
ret = test_xyz (args5, 1e-10, in5, exp5); if (ret) return ret + 50;
+ ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 50;
ret = test_xyz (args6, 1e-10, in6, in6); if (ret) return ret + 60;
ret = test_time(args7, 1e-6, in7, in7); if (ret) return ret + 70;
return 0;
}
-
diff --git a/src/makefile.vc b/src/makefile.vc
index ef460719..1155035e 100644
--- a/src/makefile.vc
+++ b/src/makefile.vc
@@ -159,6 +159,8 @@ install: all
-mkdir $(INSTDIR)\share
-mkdir $(INSTDIR)\lib
-mkdir $(INSTDIR)\include
+ -mkdir $(INSTDIR)\test
+ -mkdir $(INSTDIR)\test\gie
copy *.exe $(INSTDIR)\bin
copy *.dll $(INSTDIR)\bin
copy *.lib $(INSTDIR)\lib
@@ -166,3 +168,4 @@ install: all
copy proj_api.h $(INSTDIR)\include
copy projects.h $(INSTDIR)\include
copy geodesic.h $(INSTDIR)\include
+ copy ..\test\gie\*.gie $(INSTDIR)\test\gie
diff --git a/src/optargpm.h b/src/optargpm.h
index acb96583..97755cdb 100644
--- a/src/optargpm.h
+++ b/src/optargpm.h
@@ -183,15 +183,12 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-09-10
* DEALINGS IN THE SOFTWARE.
***********************************************************************/
-
-#define PJ_LIB__
-#include <proj.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
#include <string.h>
-#include <math.h>
-#include <errno.h>
/**************************************************************************************************/
struct OPTARGS;
@@ -219,6 +216,7 @@ struct OPTARGS {
FILE *input;
int input_index;
int record_index;
+ int free_format; /* plus-style specs replaced by free format */
const char *progname; /* argv[0], stripped from /path/to, if present */
char flaglevel[21]; /* if flag -f is specified n times, its optarg pointer is set to flaglevel + n */
char *optarg[256]; /* optarg[(int) 'f'] holds a pointer to the argument of option "-f" */
@@ -426,6 +424,12 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
o->argc = argc;
o->argv = argv;
o->progname = opt_strip_path (argv[0]);
+ o->free_format = 0;
+
+ /* Is free format in use, instead of plus-style? */
+ for (i = 1; i < argc; i++)
+ if (0==strcmp ("--", argv[i]))
+ o->free_format = i;
/* Reset all flags */
for (i = 0; i < (int) strlen (flags); i++)
@@ -442,7 +446,8 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
o->longkeys = longkeys;
- /* check aliases, An end user should never experience this, but the developer should make sure that aliases are valid */
+ /* check aliases, An end user should never experience this, but */
+ /* the developer should make sure that aliases are valid */
for (i = 0; longflags && longflags[i]; i++) {
/* Go on if it does not look like an alias */
if (strlen (longflags[i]) < 3)
@@ -500,6 +505,7 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
if ('-' != argv[i][0])
break;
+
if (0==o->margv)
o->margv = argv + i;
o->margc++;
@@ -516,7 +522,10 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
char *equals;
crepr = argv[i] + 2;
- /* need to maniplulate a bit to support gnu style --pap=pop syntax */
+ /* We need to manipulate a bit to support gnu style --foo=bar syntax. */
+ /* NOTE: This will segfault for read-only (const char * style) storage, */
+ /* but since the canonical use case, int main (int argc, char **argv), */
+ /* is non-const, we ignore this for now */
equals = strchr (crepr, '=');
if (equals)
*equals = 0;
@@ -579,6 +588,14 @@ OPTARGS *opt_parse (int argc, char **argv, const char *flags, const char *keys,
/* Process all '+'-style options, starting from where '-'-style processing ended */
o->pargv = argv + i;
+ if (o->free_format) {
+ o->pargc = o->free_format - (o->margc + 1);
+ o->fargc = argc - (o->free_format + 1);
+ if (0 != o->fargc)
+ o->fargv = argv + o->free_format + 1;
+ return o;
+ }
+
for (/* empty */; i < argc; i++) {
if ('-' == argv[i][0]) {
free (o);
diff --git a/src/pj_factors.c b/src/pj_factors.c
index 31c0e539..6ba993b9 100644
--- a/src/pj_factors.c
+++ b/src/pj_factors.c
@@ -17,12 +17,19 @@ int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) {
PJ_COORD coo = {{0, 0, 0, 0}};
coo.lp = lp;
- err = proj_errno_reset (P);
+ /* Failing the 3 initial checks will most likely be due to */
+ /* earlier errors, so we leave errno alone */
+ if (0==fac)
+ return 1;
- if (0==fac) {
- proj_errno_set (P, ENOMEM);
+ if (0==P)
return 1;
- }
+
+ if (HUGE_VAL==lp.lam)
+ return 1;
+
+ /* But from here, we're ready to make our own mistakes */
+ err = proj_errno_reset (P);
/* Indicate that all factors are numerical approximations */
fac->code = 0;
diff --git a/src/pj_init.c b/src/pj_init.c
index 2d588ee4..8f4eb477 100644
--- a/src/pj_init.c
+++ b/src/pj_init.c
@@ -27,320 +27,352 @@
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
+
+
#define PJ_LIB__
#include <geodesic.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
+#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
-/* Maximum size of files using the "escape carriage return" feature */
-#define MAX_CR_ESCAPE 65537
-typedef struct {
- projCtx ctx;
- PAFile fid;
- char buffer[MAX_CR_ESCAPE];
- int buffer_filled;
- int at_eof;
-} pj_read_state;
-/************************************************************************/
-/* fill_buffer() */
-/************************************************************************/
-static const char *fill_buffer(pj_read_state *state, const char *last_char)
-{
- size_t bytes_read;
- size_t char_remaining, char_requested;
- char *r, *w;
-
-/* -------------------------------------------------------------------- */
-/* Don't bother trying to read more if we are at eof, or if the */
-/* buffer is still over half full. */
-/* -------------------------------------------------------------------- */
- if (last_char == NULL)
- last_char = state->buffer;
-
- if (state->at_eof)
- return last_char;
-
- char_remaining = state->buffer_filled - (last_char - state->buffer);
- if (char_remaining >= sizeof(state->buffer) / 2)
- return last_char;
-
-/* -------------------------------------------------------------------- */
-/* Move the existing data to the start of the buffer. */
-/* -------------------------------------------------------------------- */
- memmove(state->buffer, last_char, char_remaining);
- state->buffer_filled = (int)char_remaining;
- last_char = state->buffer;
-
-/* -------------------------------------------------------------------- */
-/* Refill. */
-/* -------------------------------------------------------------------- */
- char_requested = sizeof(state->buffer) - state->buffer_filled - 1;
- bytes_read = pj_ctx_fread( state->ctx, state->buffer + state->buffer_filled,
- 1, char_requested, state->fid );
- if (bytes_read < char_requested)
- {
- state->at_eof = 1;
- state->buffer[state->buffer_filled + bytes_read] = '\0';
- }
+/**************************************************************************************/
+static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) {
+/***************************************************************************************
+ Convert a string (presumably originating from get_init_string) to a paralist.
+***************************************************************************************/
+ char *c = definition;
+ paralist *first = 0, *next = 0;
-/* -------------------------------------------------------------------- */
-/* Line continuations: skip whitespace after escaped newlines */
-/* -------------------------------------------------------------------- */
- r = state->buffer;
- w = state->buffer;
- while (*r) {
- /* Escaped newline? */
- while ((r[0]=='\\') && ((r[1]=='\n') || (r[1]=='\r'))) {
- r += 2;
- while (isspace (*r))
- r++;
- /* we also skip comments immediately after an escaped newline */
- while (*r=='#') {
- while( *r && (*r != '\n') )
- r++;
- while (isspace (*r))
- r++;
- /* Reaching end of buffer while skipping continuation comment is currently an error */
- if (0==*r) {
- pj_ctx_set_errno (state->ctx, -2);
- pj_log (state->ctx, PJ_LOG_ERROR, "init file too big");
- return 0;
- }
- }
- }
- *w++ = *r++;
+ while (*c) {
+ /* Find start of next substring */
+ while (isspace (*c))
+ c++;
+
+ /* Keep a handle to the start of the list, so we have something to return */
+ if (0==first)
+ first = next = pj_mkparam_ws (c);
+ else
+ next = next->next = pj_mkparam_ws (c);
+ if (0==next)
+ return pj_dealloc_params (ctx, first, ENOMEM);
+
+ /* And skip to the end of the substring */
+ while ((!isspace(*c)) && 0!=*c)
+ c++;
}
- *w = 0;
- state->buffer_filled += (int)(bytes_read - (r-w));
- return last_char;
+ /* Terminate list and return */
+ next->next = 0;
+ return first;
}
-/************************************************************************/
-/* get_opt() */
-/************************************************************************/
-static paralist *
-get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next,
- int *found_def) {
- pj_read_state *state = (pj_read_state*) calloc(1,sizeof(pj_read_state));
- char sword[MAX_CR_ESCAPE];
- int len;
- int in_target = 0;
- const char *next_char = NULL;
- state->fid = fid;
- state->ctx = ctx;
- next_char = fill_buffer(state, NULL);
- if(found_def)
- *found_def = 0;
-
- len = (int)strlen(name);
- *sword = 't';
-
- if (0==next_char)
+
+
+/**************************************************************************************/
+static char *get_init_string (PJ_CONTEXT *ctx, char *name) {
+/***************************************************************************************
+ Read a section of an init file. Return its contents as a plain character string.
+ It is the duty of the caller to free the memory allocated for the string.
+***************************************************************************************/
+#define MAX_LINE_LENGTH 1000
+ size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1);
+ char *fname, *section, *key;
+ char *buffer = 0;
+ char *line = 0;
+ PAFile fid;
+ size_t n;
+
+
+ line = pj_malloc (MAX_LINE_LENGTH + 1);
+ if (0==line)
return 0;
- /* loop till we find our target keyword */
- while (*next_char)
- {
- next_char = fill_buffer(state, next_char);
+ fname = pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3);
+ if (0==fname) {
+ pj_dealloc (line);
+ return 0;
+ }
+
+ /* Support "init=file:section", "+init=file:section", and "file:section" format */
+ key = strstr (name, "init=");
+ if (0==key)
+ key = name;
+ else
+ key += 5;
+ if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return 0;
+ }
+ memmove (fname, key, strlen (key) + 1);
+
+ /* Locate the name of the section we search for */
+ section = strrchr(fname, ':');
+ if (0==section) {
+ proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return 0;
+ }
+ *section = 0;
+ section++;
+ n = strlen (section);
+ pj_log (ctx, 3, "get_init_string: searching for section [%s] in init file [%s]\n", section, fname);
+
+ fid = pj_open_lib (ctx, fname, "rt");
+ if (0==fid) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
+ return 0;
+ }
- /* Skip white space. */
- while( isspace(*next_char) )
- next_char++;
+ /* Search for section in init file */
+ for (;;) {
- next_char = fill_buffer(state, next_char);
- if (0==next_char)
+ /* End of file? */
+ if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) {
+ pj_dealloc (buffer);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
return 0;
+ }
- /* for comments, skip past end of line. */
- if( *next_char == '#' )
- {
- while( *next_char && *next_char != '\n' )
- next_char++;
+ /* At start of right section? */
+ pj_chomp (line);
+ if ('<'!=line[0])
+ continue;
+ if (strlen (line) < n + 2)
+ continue;
+ if (line[n + 1] != '>')
+ continue;
+ if (0==strncmp (line + 1, section, n))
+ break;
+ }
- next_char = fill_buffer(state, next_char);
- if (0==next_char)
- return 0;
- if (*next_char == '\n')
- next_char++;
- if (*next_char == '\r')
- next_char++;
+ /* We're at the first line of the right section - copy line to buffer */
+ buffer = pj_malloc (current_buffer_size);
+ if (0==buffer) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ return 0;
+ }
- }
+ /* Skip the "<section>" indicator, and copy the rest of the line over */
+ strcpy (buffer, line + strlen (section) + 2);
- /* Is this our target? */
- else if( *next_char == '<' )
- {
- /* terminate processing target on the next block definition */
- if (in_target)
- break;
+ /* Copy the remaining lines of the section to buffer */
+ for (;;) {
+ char *end_i_cator;
+ size_t next_length, buffer_length;
- next_char++;
- if (strncmp(name, next_char, len) == 0
- && next_char[len] == '>')
- {
- /* skip past target word */
- next_char += len + 1;
- in_target = 1;
- if(found_def)
- *found_def = 1;
- }
- else
- {
- /* skip past end of line */
- while( *next_char && *next_char != '\n' )
- next_char++;
- }
+ /* Did the section end somewhere in the most recently read line? */
+ end_i_cator = strchr (buffer, '<');
+ if (end_i_cator) {
+ *end_i_cator = 0;
+ break;
}
- else if (in_target)
- {
- const char *start_of_word = next_char;
- int word_len = 0;
- if (*start_of_word == '+')
- {
- start_of_word++;
- next_char++;
- }
+ /* End of file? - done! */
+ if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid))
+ break;
- /* capture parameter */
- while ( *next_char && !isspace(*next_char) )
- {
- next_char++;
- word_len++;
+ /* Otherwise, handle the line. It MAY be the start of the next section, */
+ /* but that will be handled at the start of next trip through the loop */
+ buffer_length = strlen (buffer);
+ next_length = strlen (line) + buffer_length + 2;
+ if (next_length > current_buffer_size) {
+ char *b = pj_malloc (2 * current_buffer_size);
+ if (0==b) {
+ pj_dealloc (buffer);
+ buffer = 0;
+ break;
}
+ strcpy (b, buffer);
+ current_buffer_size *= 2;
+ pj_dealloc (buffer);
+ buffer = b;
+ }
+ buffer[buffer_length] = ' ';
+ strcpy (buffer + buffer_length + 1, line);
+ }
- strncpy(sword+1, start_of_word, word_len);
- sword[word_len+1] = '\0';
+ pj_ctx_fclose (ctx, fid);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ if (0==buffer)
+ return 0;
+ pj_shrink (buffer);
+ pj_log (ctx, 3, "key=%s, value: [%s]\n", key, buffer);
+ return buffer;
+}
- /* do not override existing parameter value of same name */
- if (!pj_param(ctx, *start, sword).i) {
- /* don't default ellipse if datum, ellps or any earth model information is set */
- if (0==strncmp(sword,"tellps=", 7)) {
- int n = 0;
- n += pj_param(ctx, *start, "tdatum").i;
- n += pj_param(ctx, *start, "tellps").i;
- n += pj_param(ctx, *start, "ta").i;
- n += pj_param(ctx, *start, "tb").i;
- n += pj_param(ctx, *start, "trf").i;
- n += pj_param(ctx, *start, "tf").i;
- n += pj_param(ctx, *start, "te").i;
- n += pj_param(ctx, *start, "tes").i;
+/************************************************************************/
+static paralist *get_init(PJ_CONTEXT *ctx, char *key) {
+/*************************************************************************
+Expand key from buffer or (if not in buffer) from init file
+*************************************************************************/
+ char *xkey, *definition;
+ paralist *init_items = 0;
+
+ /* support "init=file:section", "+init=file:section", and "file:section" format */
+ xkey = strstr (key, "init=");
+ if (0==xkey)
+ xkey = key;
+ else
+ xkey += 5;
+ pj_log (ctx, 3, "get_init: searching cache for key: [%s]\n", xkey);
+
+ /* Is file/key pair already in cache? */
+ init_items = pj_search_initcache (xkey);
+ if (init_items)
+ return init_items;
+
+ /* If not, we must read it from file */
+ pj_log (ctx, 3, "get_init: searching on in init files for [%s]\n", xkey);
+ definition = get_init_string (ctx, xkey);
+ if (0==definition)
+ return 0;
+ init_items = string_to_paralist (ctx, definition);
+ pj_log (ctx, 3, "get_init: got [%s], paralist[0,1]: [%s,%s]\n", definition, init_items->param, init_items->next? init_items->next->param: "(empty)");
+ pj_dealloc (definition);
+ if (0==init_items)
+ return 0;
- if (0==n)
- next = next->next = pj_mkparam(sword+1);
- }
- else
- next = next->next = pj_mkparam(sword+1);
- }
+ /* We found it in file - now insert into the cache, before returning */
+ pj_insert_initcache (xkey, init_items);
+ return init_items;
+}
- }
- else
- {
- /* skip past word */
- while( *next_char && !isspace(*next_char) ) {
- next_char++;
- }
- }
- }
+static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, char *key) {
+ paralist *defaults, *last = 0;
+ char keystring[ID_TAG_MAX + 20];
+ paralist *next, *proj;
+ int err;
- if (errno == 25)
- errno = 0;
+ if (0==start)
+ return 0;
- free(state);
- return next;
-}
+ if (strlen(key) > ID_TAG_MAX)
+ return 0;
-/************************************************************************/
-/* get_defaults() */
-/************************************************************************/
-static paralist *get_defaults(projCtx ctx, paralist **start, paralist *next, char *name) {
- PAFile fid;
+ /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */
+ /* or implicitly, because we are initializing a pipeline) */
+ if (pj_param_exists (start, "no_defs"))
+ return start;
+ proj = pj_param_exists (start, "proj");
+ if (0==proj)
+ return start;
+ if (strlen (proj->param) < 6)
+ return start;
+ if (0==strcmp ("pipeline", proj->param + 5))
+ return start;
+
+ err = pj_ctx_get_errno (ctx);
+ pj_ctx_set_errno (ctx, 0);
+
+ /* Locate end of start-list */
+ for (last = start; last->next; last = last->next);
+
+ strcpy (keystring, "proj_def.dat:");
+ strcat (keystring, key);
+ defaults = get_init (ctx, keystring);
+
+ /* Defaults are optional - so we don't care if we cannot open the file */
+ pj_ctx_set_errno (ctx, err);
+
+ if (!defaults)
+ return last;
+
+ /* Loop over all default items */
+ for (next = defaults; next; next = next->next) {
+
+ /* Don't override existing parameter value of same name */
+ if (pj_param_exists (start, next->param))
+ continue;
+
+ /* Don't default ellipse if datum, ellps or any ellipsoid information is set */
+ if (0==strncmp(next->param,"ellps=", 6)) {
+ if (pj_param_exists (start, "datum")) continue;
+ if (pj_param_exists (start, "ellps")) continue;
+ if (pj_param_exists (start, "a")) continue;
+ if (pj_param_exists (start, "b")) continue;
+ if (pj_param_exists (start, "rf")) continue;
+ if (pj_param_exists (start, "f")) continue;
+ if (pj_param_exists (start, "e")) continue;
+ if (pj_param_exists (start, "es")) continue;
+ }
- if ( (fid = pj_open_lib(ctx,"proj_def.dat", "rt")) != NULL) {
- next = get_opt(ctx, start, fid, "general", next, NULL);
- pj_ctx_fseek(ctx, fid, 0, SEEK_SET);
- next = get_opt(ctx, start, fid, name, next, NULL);
- pj_ctx_fclose(ctx, fid);
+ /* If we're here, it's OK to append the current default item */
+ last = last->next = pj_mkparam(next->param);
}
- if (errno)
- errno = 0; /* don't care if can't open file */
- ctx->last_errno = 0;
+ last->next = 0;
- return next;
+ pj_dealloc_params (ctx, defaults, 0);
+ return last;
}
-/************************************************************************/
-/* get_init() */
-/************************************************************************/
-static paralist *get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) {
- char fname[MAX_PATH_FILENAME+ID_TAG_MAX+3], *opt;
- PAFile fid;
- paralist *init_items = NULL;
- const paralist *orig_next = next;
+/*****************************************************************************/
+paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) {
+/******************************************************************************
+Append expansion of <key> to the paralist <init>. The expansion is appended,
+rather than inserted at <init>'s place, since <init> may contain
+overrides to the expansion. These must take precedence, and hence come first
+in the expanded list.
- (void)strncpy(fname, name, sizeof(fname)-2);
- fname[sizeof(fname)-2] = '\0';
+Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm
+zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid.
- /*
- ** Search for file/key pair in cache
- */
+The expression 'init=foo:bar ellps=intl' will then expand to:
- init_items = pj_search_initcache( name );
- if( init_items != NULL )
- {
- next->next = init_items;
- while( next->next != NULL )
- next = next->next;
- *found_def = 1;
- return next;
- }
+ 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80',
- /*
- ** Otherwise we try to open the file and search for it.
- */
- if ((opt = strrchr(fname, ':')) != NULL)
- *opt++ = '\0';
- else { pj_ctx_set_errno(ctx,-3); return NULL; }
+where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence,
+turning the expansion into an UTM projection on the Hayford ellipsoid.
- if ( (fid = pj_open_lib(ctx,fname, "rt")) != NULL)
- next = get_opt(ctx, start, fid, opt, next, found_def);
- else
- return NULL;
+Note that 'init=foo:bar' stays in the list. It is ignored after expansion.
+******************************************************************************/
+ paralist *last;
+ paralist *expn;
- pj_ctx_fclose(ctx, fid);
- if (errno == 25)
- errno = 0; /* unknown problem with some sys errno<-25 */
+ /* Nowhere to start? */
+ if (0==init)
+ return 0;
- /*
- ** If we seem to have gotten a result, insert it into the
- ** init file cache.
- */
- if( next != NULL && next != orig_next )
- pj_insert_initcache( name, orig_next->next );
+ expn = get_init(ctx, init->param);
- return next;
-}
+ /* Nothing in expansion? */
+ if (0==expn)
+ return 0;
-paralist * pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def) {
- return get_init(ctx, start, next, name, found_def);
+ /* Locate the end of the list */
+ for (last = init; last && last->next; last = last->next);
+
+ /* Then append and return */
+ last->next = expn;
+ return init;
}
+
+
/************************************************************************/
/* pj_init_plus() */
/* */
/* Same as pj_init() except it takes one argument string with */
-/* individual arguments preceded by '+', such as "+proj=utm */
+/* individual arguments preceded by '+', such as "+proj=utm */
/* +zone=11 +ellps=WGS84". */
/************************************************************************/
@@ -434,15 +466,27 @@ pj_init(int argc, char **argv) {
return pj_init_ctx( pj_get_default_ctx(), argc, argv );
}
+
+typedef PJ *(constructor)(PJ *);
+typedef constructor *(*function_returning_constructor)(const char *);
+
+static constructor *pj_constructor (const char *name) {
+ int i;
+ char *s;
+ for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ;
+ if (0==s)
+ return 0;
+ return (constructor *) pj_list[i].proj;
+}
+
+
PJ *
pj_init_ctx(projCtx ctx, int argc, char **argv) {
char *s, *name;
- paralist *start = NULL;
- PJ *(*proj)(PJ *);
- paralist *curr;
+ constructor *proj;
+ paralist *curr, *init, *start;
int i;
int err;
- int found_def = 0;
PJ *PIN = 0;
int n_pipelines = 0;
int n_inits = 0;
@@ -451,7 +495,6 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
ctx = pj_get_default_ctx ();
ctx->last_errno = 0;
- start = NULL;
if (argc <= 0) {
pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS);
@@ -478,6 +521,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return 0;
}
+
/* put arguments into internal linked list */
start = curr = pj_mkparam(argv[0]);
if (!curr)
@@ -490,41 +534,45 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
curr = curr->next;
}
- /* Only expand +init's in non-pipeline operations. +init's in pipelines are */
- /* expanded in the individual pipeline steps during pipeline initialization. */
- /* Potentially this leads to many nested pipelines, which shouldn't be a */
- /* problem when +inits are expanded as late as possible. */
- if (pj_param(ctx, start, "tinit").i && n_pipelines == 0) {
- found_def = 0;
- curr = get_init(ctx, &start, curr, pj_param(ctx, start, "sinit").s, &found_def);
- if (!curr)
+
+ /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */
+ /* expanded in the individual pipeline steps during pipeline initialization. */
+ /* Potentially this leads to many nested pipelines, which shouldn't be a */
+ /* problem when '+init's are expanded as late as possible. */
+ init = pj_param_exists (start, "init");
+ if (init && n_pipelines == 0) {
+ init = pj_expand_init (ctx, init);
+ if (!init)
return pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS);
- if (!found_def)
- return pj_dealloc_params (ctx, start, PJD_ERR_NO_OPTION_IN_INIT_FILE);
}
-
if (ctx->last_errno)
return pj_dealloc_params (ctx, start, ctx->last_errno);
- /* find projection selection */
- if (!(name = pj_param(ctx, start, "sproj").s))
+ /* Find projection selection */
+ curr = pj_param_exists (start, "proj");
+ if (0==curr)
return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
- for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ;
+ name = curr->param;
+ if (strlen (name) < 6)
+ return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
+ name += 5;
- if (!s)
+ proj = pj_constructor (name);
+ if (0==proj)
return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID);
- /* set defaults, unless inhibited or we are initializing a pipeline */
- if (!(pj_param(ctx, start, "bno_defs").i) && n_pipelines == 0)
- curr = get_defaults(ctx,&start, curr, name);
- proj = (PJ *(*)(PJ *)) pj_list[i].proj;
+ /* Append general and projection specific defaults to the definition list */
+ append_defaults_to_paralist (ctx, start, "general");
+ append_defaults_to_paralist (ctx, start, name);
+
- /* allocate projection structure */
+ /* Allocate projection structure */
PIN = proj(0);
if (0==PIN)
return pj_dealloc_params (ctx, start, ENOMEM);
+
PIN->ctx = ctx;
PIN->params = start;
PIN->is_latlong = 0;
@@ -539,7 +587,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->vgridlist_geoid = NULL;
PIN->vgridlist_geoid_count = 0;
- /* set datum parameters */
+ /* Set datum parameters */
if (pj_datum_set(ctx, start, PIN))
return pj_default_destructor (PIN, proj_errno(PIN));
@@ -566,18 +614,18 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->datum_type = PJD_WGS84;
}
- /* set PIN->geoc coordinate system */
+ /* Set PIN->geoc coordinate system */
PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i);
- /* over-ranging flag */
+ /* Over-ranging flag */
PIN->over = pj_param(ctx, start, "bover").i;
- /* vertical datum geoid grids */
+ /* Vertical datum geoid grids */
PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i;
if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */
pj_param(ctx, start, "sgeoidgrids");
- /* longitude center for wrapping */
+ /* Longitude center for wrapping */
PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i;
if (PIN->is_long_wrap_set) {
PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f;
@@ -588,7 +636,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT);
}
- /* axis orientation */
+ /* Axis orientation */
if( (pj_param(ctx, start,"saxis").s) != NULL )
{
static const char *axis_legal = "ewnsud";
@@ -601,23 +649,23 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
|| strchr( axis_legal, axis_arg[2] ) == NULL)
return pj_default_destructor (PIN, PJD_ERR_AXIS);
- /* it would be nice to validate we don't have on axis repeated */
+ /* TODO: it would be nice to validate we don't have on axis repeated */
strcpy( PIN->axis, axis_arg );
}
- /* central meridian */
+ /* Central meridian */
PIN->lam0=pj_param(ctx, start, "rlon_0").f;
- /* central latitude */
+ /* Central latitude */
PIN->phi0 = pj_param(ctx, start, "rlat_0").f;
- /* false easting and northing */
+ /* False easting and northing */
PIN->x0 = pj_param(ctx, start, "dx_0").f;
PIN->y0 = pj_param(ctx, start, "dy_0").f;
PIN->z0 = pj_param(ctx, start, "dz_0").f;
PIN->t0 = pj_param(ctx, start, "dt_0").f;
- /* general scaling factor */
+ /* General scaling factor */
if (pj_param(ctx, start, "tk_0").i)
PIN->k0 = pj_param(ctx, start, "dk_0").f;
else if (pj_param(ctx, start, "tk").i)
@@ -627,7 +675,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
if (PIN->k0 <= 0.)
return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO);
- /* set units */
+ /* Set units */
s = 0;
if ((name = pj_param(ctx, start, "sunits").s) != NULL) {
for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ;
@@ -655,7 +703,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
} else
PIN->to_meter = PIN->fr_meter = 1.;
- /* set vertical units */
+ /* Set vertical units */
s = 0;
if ((name = pj_param(ctx, start, "svunits").s) != NULL) {
for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ;
@@ -675,7 +723,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
PIN->vfr_meter = PIN->fr_meter;
}
- /* prime meridian */
+ /* Prime meridian */
s = 0;
if ((name = pj_param(ctx, start, "spm").s) != NULL) {
const char *value = NULL;
@@ -708,7 +756,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return pj_default_destructor (PIN, ENOMEM);
geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es)));
- /* projection specific initialization */
+ /* Projection specific initialization */
err = proj_errno_reset (PIN);
PIN = proj(PIN);
if (proj_errno (PIN)) {
@@ -719,6 +767,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) {
return PIN;
}
+
+
/************************************************************************/
/* pj_free() */
/* */
diff --git a/src/pj_internal.c b/src/pj_internal.c
index 8a5d2d15..7bfd192b 100644
--- a/src/pj_internal.c
+++ b/src/pj_internal.c
@@ -33,6 +33,7 @@
#include "projects.h"
#include <geodesic.h>
+#include <ctype.h>
#include <stddef.h>
#include <stdarg.h>
#include <errno.h>
@@ -114,14 +115,15 @@ void proj_context_inherit (PJ *parent, PJ *child) {
-size_t pj_strlcpy(char *dst, const char *src, size_t siz) {
-/*******************************************************************
+/**************************************************************************************/
+size_t pj_strlcpy(char *dst, const char *src, size_t dsize) {
+/***************************************************************************************
Copy src to string dst of size siz. At most siz-1 characters
will be copied. Always NUL terminates (unless siz == 0).
Returns strlen(src); if retval >= siz, truncation occurred.
-
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Copyright (c) 1998, 2015 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -134,40 +136,243 @@ size_t pj_strlcpy(char *dst, const char *src, size_t siz) {
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
- Source: http://www.i-pi.com/Training/EthicalHacking/Solutions/strlcpy.c
+ Source: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/strlcpy.c
-********************************************************************/
- register char *d = dst;
- register const char *s = src;
- register size_t n = siz;
+***************************************************************************************/
+ const char *osrc = src;
+ size_t nleft = dsize;
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0) {
- do {
- if ((*d++ = *s++) == 0)
+ /* Copy as many bytes as will fit. */
+ if (nleft != 0) {
+ while (--nleft != 0) {
+ if ((*dst++ = *src++) == '\0')
break;
- } while (--n != 0);
+ }
}
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
+ /* Not enough room in dst, add NUL and traverse rest of src. */
+ if (nleft == 0) {
+ if (dsize != 0)
+ *dst = '\0'; /* NUL-terminate dst */
+ while (*src++)
;
}
- return(s - src - 1); /* count does not include NUL */
+ return(src - osrc - 1); /* count does not include NUL */
}
-/* stuff below is *not* considered API, and will be moved to an "internal plumbing toolset" */
+/*****************************************************************************/
+char *pj_chomp (char *c) {
+/******************************************************************************
+Strip pre- and postfix whitespace. Inline comments (indicated by '#') are
+considered whitespace.
+******************************************************************************/
+ size_t i, n;
+ char *comment;
+ char *start = c;
+
+ if (0==c)
+ return 0;
+
+ comment = strchr (c, '#');
+ if (comment)
+ *comment = 0;
+
+ n = strlen (c);
+ if (0==n)
+ return c;
+
+ /* Eliminate postfix whitespace */
+ for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--)
+ c[i] = 0;
+
+ /* Find start of non-whitespace */
+ while (0 != *start && (';'==*start || isspace (*start)))
+ start++;
+
+ n = strlen (start);
+ if (0==n) {
+ c[0] = 0;
+ return c;
+ }
+
+ memmove (c, start, n + 1);
+ return c;
+}
+/*****************************************************************************/
+char *pj_shrink (char *c) {
+/******************************************************************************
+Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy,
+consuming their surrounding whitespace.
+******************************************************************************/
+ size_t i, j, n;
+
+ /* Flag showing that a whitespace (ws) has been written after last non-ws */
+ size_t ws;
+
+ if (0==c)
+ return 0;
+
+ pj_chomp (c);
+ n = strlen (c);
+
+ /* First collapse repeated whitespace (including +/;) */
+ for (i = j = 0, ws = 0; j < n; j++) {
+
+ /* Eliminate prefix '+', only if preceeded by whitespace */
+ /* (i.e. keep it in 1.23e+08) */
+ if ((i > 0) && ('+'==c[j]) && isspace (c[i]))
+ c[j] = ' ';
+
+ if (isspace (c[j]) || ';'==c[j]) {
+ if (0==ws && (i > 0))
+ c[i++] = ' ';
+ ws = 1;
+ continue;
+ }
+ else {
+ ws = 0;
+ c[i++] = c[j];
+ }
+ }
+ c[i] = 0;
+ n = strlen(c);
+
+ /* Then make ',' and '=' greedy */
+ for (i = j = 0; j < n; j++) {
+ if (i==0) {
+ c[i++] = c[j];
+ continue;
+ }
+
+ /* Skip space before '='/',' */
+ if ('='==c[j] || ','==c[j]) {
+ if (c[i - 1]==' ')
+ c[i - 1] = c[j];
+ else
+ c[i++] = c[j];
+ continue;
+ }
+
+ if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) )
+ continue;
+
+ c[i++] = c[j];
+ }
+ c[i] = 0;
+ return c;
+}
+
+
+
+/*****************************************************************************/
+size_t pj_trim_argc (char *args) {
+/******************************************************************************
+Trim all unnecessary whitespace (and non-essential syntactic tokens) from the
+argument string, args, and count its number of elements.
+******************************************************************************/
+ size_t i, m, n;
+ pj_shrink (args);
+ n = strlen (args);
+ if (n==0)
+ return 0;
+ for (i = m = 0; i < n; i++) {
+ if (' '==args[i]) {
+ args[i] = 0;
+ m++;
+ }
+ }
+ return m + 1;
+}
+
+
+
+/*****************************************************************************/
+char **pj_trim_argv (size_t argc, char *args) {
+/******************************************************************************
+Create an argv-style array from elements placed in the argument string, args.
+
+args is a trimmed string as returned by pj_trim_argc(), and argc is the number
+of trimmed strings found (i.e. the return value of pj_trim_args()). Hence,
+ int argc = pj_trim_argc (args);
+ char **argv = pj_trim_argv (argc, args);
+will produce a classic style (argc, argv) pair from a string of whitespace
+separated args. No new memory is allocated for storing the individual args
+(they stay in the args string), but for the pointers to the args a new array
+is allocated and returned.
+
+It is the duty of the caller to free this array.
+******************************************************************************/
+ size_t i, j;
+ char **argv;
+
+ if (0==args)
+ return 0;
+ if (0==argc)
+ return 0;
+
+
+ /* turn the input string into an array of strings */
+ argv = (char **) calloc (argc, sizeof (char *));
+ if (0==argv)
+ return 0;
+ argv[0] = args;
+ for (i = 0, j = 1; ; i++) {
+ if (0==args[i]) {
+ argv[j++] = args + (i + 1);
+ }
+ if (j==argc)
+ break;
+ }
+ return argv;
+}
+
+
+
+/*****************************************************************************/
+char *pj_make_args (size_t argc, char **argv) {
+/******************************************************************************
+pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It
+converts free format command line input to something proj_create can consume.
+
+Allocates, and returns, an array of char, large enough to hold a whitespace
+separated copy of the args in argv. It is the duty of the caller to free this
+array.
+******************************************************************************/
+ size_t i, n;
+ char *p;
+
+ for (i = n = 0; i < argc; i++)
+ n += strlen (argv[i]);
+
+ p = pj_calloc (n + argc + 1, sizeof (char));
+ if (0==p)
+ return 0;
+ if (0==argc)
+ return p;
+
+ for (i = n = 0; i < argc; i++) {
+ strcat (p, argv[i]);
+ strcat (p, " ");
+ }
+ return pj_shrink (p);
+}
+
+
+
+/*****************************************************************************/
void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
+/******************************************************************************
+Raise an error directly on a context, without going through a PJ belonging
+to that context.
+******************************************************************************/
if (0==ctx)
ctx = pj_get_default_ctx();
pj_ctx_set_errno (ctx, err);
@@ -176,8 +381,20 @@ void proj_context_errno_set (PJ_CONTEXT *ctx, int err) {
-/* Set logging level 0-3. Higher number means more debug info. 0 turns it off */
+
+
+
+/* logging */
+
+/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
+void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
+
+
+/***************************************************************************************/
enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_level) {
+/****************************************************************************************
+ Set logging level 0-3. Higher number means more debug info. 0 turns it off
+****************************************************************************************/
enum proj_log_level previous;
if (0==ctx)
ctx = pj_get_default_ctx();
@@ -191,35 +408,48 @@ enum proj_log_level proj_log_level (PJ_CONTEXT *ctx, enum proj_log_level log_lev
}
-
-/* logging */
-
-/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */
-void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args );
-
+/*****************************************************************************/
void proj_log_error (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting the most severe events.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args);
va_end( args );
}
+
+/*****************************************************************************/
void proj_log_debug (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting debugging information.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args);
va_end( args );
}
+
+/*****************************************************************************/
void proj_log_trace (PJ *P, const char *fmt, ...) {
+/******************************************************************************
+ For reporting embarrasingly detailed debugging information.
+******************************************************************************/
va_list args;
va_start( args, fmt );
pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args);
va_end( args );
}
-/* Put a new logging function into P's context. The opaque object app_data is passed as first arg at each call to the logger */
+
+/*****************************************************************************/
void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION log) {
+/******************************************************************************
+ Put a new logging function into P's context. The opaque object app_data is
+ passed as first arg at each call to the logger
+******************************************************************************/
if (0==ctx)
pj_get_default_ctx ();
if (0==ctx)
diff --git a/src/pj_malloc.c b/src/pj_malloc.c
index c9275074..63278a56 100644
--- a/src/pj_malloc.c
+++ b/src/pj_malloc.c
@@ -40,6 +40,7 @@
** projection system memory allocation/deallocation call with custom
** application procedures. */
+#include <proj.h>
#include "projects.h"
#include <errno.h>
@@ -143,7 +144,7 @@ char *pj_strdup(const char *str)
/*****************************************************************************/
-void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev) {
+void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) {
/*****************************************************************************
Companion to pj_default_destructor (below). Deallocates a linked list
of "+proj=xxx" initialization parameters.
diff --git a/src/pj_param.c b/src/pj_param.c
index ee952eca..5024346d 100644
--- a/src/pj_param.c
+++ b/src/pj_param.c
@@ -1,20 +1,79 @@
/* put parameters in linked list and retrieve */
-#include <projects.h>
+#include <ctype.h>
#include <stdio.h>
#include <string.h>
+#include "projects.h"
+
/* create parameter list entry */
paralist *pj_mkparam(char *str) {
- paralist *newitem;
-
- if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) {
- newitem->used = 0;
- newitem->next = 0;
- if (*str == '+')
- ++str;
- (void)strcpy(newitem->param, str);
- }
- return newitem;
+ paralist *newitem;
+
+ if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) {
+ newitem->used = 0;
+ newitem->next = 0;
+ if (*str == '+')
+ ++str;
+ (void)strcpy(newitem->param, str);
+ }
+ return newitem;
+}
+
+
+/* As pj_mkparam, but payload ends at first whitespace, rather than at end of <str> */
+paralist *pj_mkparam_ws (char *str) {
+ paralist *newitem;
+ size_t len = 0;
+
+ if (0==str)
+ return 0;
+
+ /* Find start and length of string */
+ while (isspace (*str))
+ str++;
+ while ((!isspace(str[len])) && 0!=str[len])
+ len++;
+ if (*str == '+') {
+ str++;
+ len--;
+ }
+
+ /* Use calloc to automagically 0-terminate the copy */
+ newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len);
+ if (0==newitem)
+ return 0;
+ memmove(newitem->param, str, len);
+
+ newitem->used = 0;
+ newitem->next = 0;
+
+ return newitem;
+}
+
+/**************************************************************************************/
+paralist *pj_param_exists (paralist *list, const char *parameter) {
+/***************************************************************************************
+ Determine whether a given parameter exists in a paralist. If it does, return
+ a pointer to the corresponding list element - otherwise return 0.
+
+ This function is equivalent to the pj_param (...) call with the "opt" argument
+ set to the parameter name preceeeded by a 't'. But by using this one, one avoids
+ writing the code allocating memory for a new copy of parameter name, and prepending
+ the t (for compile time known names, this is obviously not an issue).
+***************************************************************************************/
+ paralist *next = list;
+ char *c = strchr (parameter, '=');
+ size_t len = strlen (parameter);
+ if (c)
+ len = c - parameter;
+ if (list==0)
+ return 0;
+
+ for (next = list; next; next = next->next)
+ if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0))
+ return next;
+
+ return 0;
}
@@ -35,76 +94,76 @@ paralist *pj_mkparam(char *str) {
/* */
/************************************************************************/
- PROJVALUE /* test for presence or get parameter value */
+ PROJVALUE /* test for presence or get parameter value */
pj_param(projCtx ctx, paralist *pl, const char *opt) {
- int type;
- unsigned l;
- PROJVALUE value;
-
- if( ctx == NULL )
- ctx = pj_get_default_ctx();
-
- type = *opt++;
- /* simple linear lookup */
- l = (int)strlen(opt);
- while (pl && !(!strncmp(pl->param, opt, l) &&
- (!pl->param[l] || pl->param[l] == '=')))
- pl = pl->next;
- if (type == 't')
- value.i = pl != 0;
- else if (pl) {
- pl->used |= 1;
- opt = pl->param + l;
- if (*opt == '=')
- ++opt;
- switch (type) {
- case 'i': /* integer input */
- value.i = atoi(opt);
- break;
- case 'd': /* simple real input */
- value.f = pj_atof(opt);
- break;
- case 'r': /* degrees input */
- value.f = dmstor_ctx(ctx, opt, 0);
- break;
- case 's': /* char string */
+ int type;
+ unsigned l;
+ PROJVALUE value;
+
+ if( ctx == NULL )
+ ctx = pj_get_default_ctx();
+
+ type = *opt++;
+ /* simple linear lookup */
+ l = (int)strlen(opt);
+ while (pl && !(!strncmp(pl->param, opt, l) &&
+ (!pl->param[l] || pl->param[l] == '=')))
+ pl = pl->next;
+ if (type == 't')
+ value.i = pl != 0;
+ else if (pl) {
+ pl->used |= 1;
+ opt = pl->param + l;
+ if (*opt == '=')
+ ++opt;
+ switch (type) {
+ case 'i': /* integer input */
+ value.i = atoi(opt);
+ break;
+ case 'd': /* simple real input */
+ value.f = pj_atof(opt);
+ break;
+ case 'r': /* degrees input */
+ value.f = dmstor_ctx(ctx, opt, 0);
+ break;
+ case 's': /* char string */
value.s = (char *) opt;
- break;
- case 'b': /* boolean */
- switch (*opt) {
- case 'F': case 'f':
- value.i = 0;
- break;
- case '\0': case 'T': case 't':
- value.i = 1;
- break;
- default:
- pj_ctx_set_errno(ctx, -8);
- value.i = 0;
- break;
- }
- break;
- default:
+ break;
+ case 'b': /* boolean */
+ switch (*opt) {
+ case 'F': case 'f':
+ value.i = 0;
+ break;
+ case '\0': case 'T': case 't':
+ value.i = 1;
+ break;
+ default:
+ pj_ctx_set_errno(ctx, -8);
+ value.i = 0;
+ break;
+ }
+ break;
+ default:
bum_type: /* note: this is an error in parameter, not a user error */
- fprintf(stderr, "invalid request to pj_param, fatal\n");
- exit(1);
- }
- } else /* not given */
- switch (type) {
- case 'b':
- case 'i':
- value.i = 0;
- break;
- case 'd':
- case 'r':
- value.f = 0.;
- break;
- case 's':
- value.s = 0;
- break;
- default:
- goto bum_type;
- }
- return value;
+ fprintf(stderr, "invalid request to pj_param, fatal\n");
+ exit(1);
+ }
+ } else /* not given */
+ switch (type) {
+ case 'b':
+ case 'i':
+ value.i = 0;
+ break;
+ case 'd':
+ case 'r':
+ value.f = 0.;
+ break;
+ case 's':
+ value.s = 0;
+ break;
+ default:
+ goto bum_type;
+ }
+ return value;
}
diff --git a/src/proj.def b/src/proj.def
index 887f4719..51b79558 100644
--- a/src/proj.def
+++ b/src/proj.def
@@ -149,3 +149,5 @@ EXPORTS
pj_ellipsoid @134
pj_calc_ellipsoid_params @135
+ pj_chomp @136
+ pj_shrink @137
diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c
index 5f4bf334..0fba5097 100644
--- a/src/proj_4D_api.c
+++ b/src/proj_4D_api.c
@@ -28,7 +28,6 @@
*****************************************************************************/
#include <stddef.h>
#include <errno.h>
-#include <ctype.h>
#include <proj.h>
#include "proj_internal.h"
#include "projects.h"
@@ -81,6 +80,8 @@ double proj_lpz_dist (const PJ *P, LPZ a, LPZ b) {
PJ_COORD aa, bb;
aa.lpz = a;
bb.lpz = b;
+ if (HUGE_VAL==a.lam || HUGE_VAL==b.lam)
+ return HUGE_VAL;
return hypot (proj_lp_dist (P, aa.lp, bb.lp), a.z - b.z);
}
@@ -340,7 +341,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
direction = PJ_INV)
The conversion involves a call to the tangent function, which goes through the
- roof at the poles, so very close (the last few micrometers) to the poles no
+ roof at the poles, so very close (the last centimeter) to the poles no
conversion takes place and the input latitude is copied directly to the output.
Fortunately, the geocentric latitude converges to the geographical at the
@@ -349,7 +350,7 @@ PJ_COORD proj_geoc_lat (const PJ *P, PJ_DIRECTION direction, PJ_COORD coo) {
For the spherical case, the geographical latitude equals the geocentric, and
consequently, the input is copied directly to the output.
**************************************************************************************/
- const double limit = M_HALFPI - 1e-12;
+ const double limit = M_HALFPI - 1e-9;
PJ_COORD res = coo;
if ((coo.lp.phi > limit) || (coo.lp.phi < -limit) || (P->es==0))
return res;
@@ -388,76 +389,29 @@ PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) {
**************************************************************************************/
PJ *P;
char *args, **argv;
- int argc, i, j, last, n;
+ size_t argc, n;
if (0==ctx)
ctx = pj_get_default_ctx ();
/* Make a copy that we can manipulate */
- n = (int) strlen (definition);
+ n = strlen (definition);
args = (char *) malloc (n + 1);
if (0==args)
return 0;
strcpy (args, definition);
- /* All-in-one: count args, eliminate superfluous whitespace, 0-terminate substrings */
- for (i = j = argc = last = 0; i < n; ) {
-
- /* Skip prefix whitespace */
- while (isspace (args[i]))
- i++;
-
- /* Skip at most one prefix '+' */
- if ('+'==args[i])
- i++;
-
- /* Whitespace after a '+' is a syntax error - but by Postel's prescription, we ignore and go on */
- if (isspace (args[i]))
- continue;
-
- /* Move a whitespace delimited text string to the left, skipping over superfluous whitespace */
- while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i]))
- args[j++] = args[i++];
-
- /* Skip postfix whitespace */
- while (isspace (args[i]) || ';'==args[i])
- i++;
-
- /* Greedy assignment operator: turn "a = b" into "a=b" */
- if ('='==args[i]) {
- args[j++] = '=';
- i++;
- while (isspace (args[i]))
- i++;
- while ((0!=args[i]) && (!isspace (args[i])) && (';'!=args[i]))
- args[j++] = args[i++];
- while (isspace (args[i]) || ';'==args[i])
- i++;
- }
-
- /* terminate string - if that makes j pass i (often the case for first arg), let i catch up */
- args[j++] = 0;
- if (i < j)
- i = j;
-
- /* we finished another arg */
- argc++;
+ argc = pj_trim_argc (args);
+ if (argc==0) {
+ pj_dealloc (args);
+ return 0;
}
- /* turn the massaged input into an array of strings */
- argv = (char **) calloc (argc, sizeof (char *));
- if (0==argv)
- return pj_dealloc (args);
- argv[0] = args;
- for (i = 0, j = 1; i < n; i++) {
- if (0==args[i])
- argv[j++] = args + (i + 1);
- if (j==argc)
- break;
- }
+ argv = pj_trim_argv (argc, args);
/* ...and let pj_init_ctx do the hard work */
- P = pj_init_ctx (ctx, argc, argv);
+ P = pj_init_ctx (ctx, (int) argc, argv);
+
pj_dealloc (argv);
pj_dealloc (args);
return P;
@@ -474,11 +428,22 @@ a null-pointer is returned. The definition arguments may use '+' as argument sta
indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm",
"zone=32"}.
**************************************************************************************/
+ PJ *P;
+ const char *c;
+
if (0==argv)
return 0;
if (0==ctx)
ctx = pj_get_default_ctx ();
- return pj_init_ctx (ctx, argc, argv);
+
+ /* We assume that free format is used, and build a full proj_create compatible string */
+ c = pj_make_args (argc, argv);
+ if (0==c)
+ return 0;
+
+ P = proj_create (ctx, c);
+ pj_dealloc ((char *) c);
+ return P;
}
@@ -781,6 +746,8 @@ PJ_GRID_INFO proj_grid_info(const char *gridname) {
return info;
}
+
+
/*****************************************************************************/
PJ_INIT_INFO proj_init_info(const char *initname){
/******************************************************************************
@@ -790,12 +757,14 @@ PJ_INIT_INFO proj_init_info(const char *initname){
Returns PJ_INIT_INFO struct.
- If the init file is not found all members of
- the return struct are set to 0. If the init file is found, but it the
- metadata is missing, the value is set to "Unknown".
+ If the init file is not found all members of the return struct are set
+ to the empty string.
+
+ If the init file is found, but the metadata is missing, the value is
+ set to "Unknown".
******************************************************************************/
- int file_found, def_found=0;
+ int file_found;
char param[80], key[74];
paralist *start, *next;
PJ_INIT_INFO info;
@@ -819,7 +788,7 @@ PJ_INIT_INFO proj_init_info(const char *initname){
strncat(param, key, 73);
start = pj_mkparam(param);
- next = pj_get_init(ctx, &start, start, key, &def_found);
+ pj_expand_init(ctx, start);
if (pj_param(ctx, start, "tversion").i) {
pj_strlcpy(info.version, pj_param(ctx, start, "sversion").s, sizeof(info.version));
@@ -858,6 +827,9 @@ PJ_FACTORS proj_factors(PJ *P, LP lp) {
PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0};
struct FACTORS f;
+ if (0==P)
+ return factors;
+
if (pj_factors(lp, P, 0.0, &f))
return factors;
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 4e70e690..6571bce9 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -57,6 +57,7 @@ extern "C" {
#define PJ_TORAD(deg) ((deg)*M_PI/180.0)
#endif
+
/* This enum is also conditionally defined in projects.h - but we need it here */
/* for the pj_left/right prototypes, and enums cannot be forward declared */
enum pj_io_units {
@@ -109,6 +110,12 @@ void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst);
void pj_erase_ellipsoid_def (PJ *P);
int pj_calc_ellipsoid_params (PJ *P, double a, double es);
+char *pj_chomp (char *c);
+char *pj_shrink (char *c);
+size_t pj_trim_argc (char *args);
+char **pj_trim_argv (size_t argc, char *args);
+char *pj_make_args (size_t argc, char **argv);
+
/* Lowest level: Minimum support for fileapi */
void proj_fileapi_set (PJ *P, void *fileapi);
diff --git a/src/projects.h b/src/projects.h
index 8c0f81fa..ca2cdbe3 100644
--- a/src/projects.h
+++ b/src/projects.h
@@ -683,7 +683,10 @@ double adjlon(double);
double aacos(projCtx,double), aasin(projCtx,double), asqrt(double), aatan2(double, double);
PROJVALUE pj_param(projCtx ctx, paralist *, const char *);
+paralist *pj_param_exists (paralist *list, const char *parameter);
paralist *pj_mkparam(char *);
+paralist *pj_mkparam_ws (char *str);
+
int pj_ellipsoid (PJ *);
int pj_ell_set(projCtx ctx, paralist *, double *, double *);
@@ -694,7 +697,8 @@ int pj_angular_units_set(paralist *, PJ *);
paralist *pj_clone_paralist( const paralist* );
paralist *pj_search_initcache( const char *filekey );
void pj_insert_initcache( const char *filekey, const paralist *list);
-paralist *pj_get_init(projCtx ctx, paralist **start, paralist *next, char *name, int *found_def);
+paralist *pj_expand_init(projCtx ctx, paralist *init);
+
void *pj_dealloc_params (projCtx ctx, paralist *start, int errlev);
diff --git a/test/gie/GDA.gie b/test/gie/GDA.gie
index 423443a6..b77ca934 100644
--- a/test/gie/GDA.gie
+++ b/test/gie/GDA.gie
@@ -13,58 +13,64 @@ Which is distributed under Creative Commons CC-BY 4.0
These tests will probably be useful as a template for an AU setup file, defining
transformations for Australian systems, but I'm reluctant to provide such a file
-directly - I believe it should come from official AU sources.
+myself - it probably should come from official AU sources.
Thomas Knudsen, thokn@sdfe.dk, 2017-11-27
-----------------------------------------------------------------------------------
<gie>
+
-----------------------------------------------------------------------------------
GDA94 to GDA2020
-----------------------------------------------------------------------------------
Just the Helmert transformation, to verify that we are within 100 um
-----------------------------------------------------------------------------------
-operation proj=helmert x=0.06155 y=-0.01087 z=-0.04019 s=-0.009994 \
- rx=-0.0394924 ry=-0.0327221 rz=-0.0328979
+operation proj=helmert
+ x = 0.06155 rx = -0.0394924
+ y = -0.01087 ry = -0.0327221
+ z = -0.04019 rz = -0.0328979 s = -0.009994
+
-----------------------------------------------------------------------------------
tolerance 75 um
accept -4052051.7643 4212836.2017 -2545106.0245
expect -4052052.7379 4212835.9897 -2545104.5898
+-------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
GDA94 to GDA2020
-----------------------------------------------------------------------------------
-All the way from geographic-to-geographic
+All the way from geographic-to-cartesian-and-back-to-geographic
-----------------------------------------------------------------------------------
-operation proj=pipeline ellps=GRS80 \
- step proj=cart \
- step proj=helmert x=0.06155 y=-0.01087 z=-0.04019 s=-0.009994 \
- rx=-0.0394924 ry=-0.0327221 rz=-0.0328979 \
- step proj=cart inv
+operation proj = pipeline ellps=GRS80;
+ step proj = cart;
+ step proj = helmert
+ x = 0.06155; rx = -0.0394924;
+ y = -0.01087; ry = -0.0327221;
+ z = -0.04019; rz = -0.0328979; s = -0.009994;
+ step proj = cart inv;
-----------------------------------------------------------------------------------
accept 133.88551329 -23.67012389 603.3466 0 # Alice Springs GDA94
expect 133.8855216 -23.67011014 603.2489 0 # Alice Springs GDA2020
+-------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
-ITRF2014@2018 to GDA2020
+ITRF2014@2018 to GDA2020 - Test point ALIC (Alice Springs)
-----------------------------------------------------------------------------------
Just the Helmert transformation, to verify that we are within 100 um
-----------------------------------------------------------------------------------
-operation proj = helmert; \
- x = 0; rx = 0; dx = 0; drx = 0.00150379; \
- y = 0; ry = 0; dy = 0; dry = 0.00118346; \
- z = 0; rz = 0; dz = 0; drz = 0.00120716; \
- \
- ds = 0; t_epoch=2020.0;
------------------------------------------------------------------------------------
-tolerance 50 um
+operation proj = helmert ellps=GRS80 exact
- # Alice Springs ITRF2014@2018.0
-accept -4052052.6588 4212835.9938 -2545104.6946 2018.0
+ x = 0 rx = 0 dx = 0 drx = 0.00150379
+ y = 0 ry = 0 dy = 0 dry = 0.00118346
+ z = 0 rz = 0 dz = 0 drz = 0.00120716
- # Alice Springs GDA2020
-expect -4052052.7373 4212835.9835 -2545104.5867
+ s = 0 ds = 0 t_epoch=2020.0
+-----------------------------------------------------------------------------------
+tolerance 40 um
+accept -4052052.6588 4212835.9938 -2545104.6946 2018.0 # ITRF2014@2018.0
+expect -4052052.7373 4212835.9835 -2545104.5867 # GDA2020
+-----------------------------------------------------------------------------------
</gie>
diff --git a/test/gie/axisswap.gie b/test/gie/axisswap.gie
index b9281ef7..7f0c366c 100644
--- a/test/gie/axisswap.gie
+++ b/test/gie/axisswap.gie
@@ -2,73 +2,75 @@
Tests for the axisswap operation
-------------------------------------------------------------------------------
-BEGIN
+<gie>
-OPERATION +proj=axisswap +order=1,2,3,4
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT 1 2 3 4
-ROUNDTRIP 100
+operation proj=axisswap order=1,2,3,4
+tolerance 0.000001 m
+accept 1 2 3 4
+expect 1 2 3 4
+roundtrip 100
-OPERATION +proj=axisswap +order=4,3,2,1
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT 4 3 2 1
-ROUNDTRIP 100
+operation proj=axisswap order=4,3,2,1
+tolerance 0.000001 m
+accept 1 2 3 4
+expect 4 3 2 1
+roundtrip 100
-OPERATION +proj=axisswap +order=-1,-2,-3,-4
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT -1 -2 -3 -4
-ROUNDTRIP 100
+operation proj=axisswap order=-1,-2,-3,-4
+tolerance 0.000001 m
+accept 1 2 3 4
+expect -1 -2 -3 -4
+roundtrip 100
-OPERATION +proj=axisswap +order=1,2,-3,4
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT 1 2 -3 4
-ROUNDTRIP 100
+operation proj=axisswap order=1,2,-3,4
+tolerance 0.000001 m
+accept 1 2 3 4
+expect 1 2 -3 4
+roundtrip 100
-OPERATION +proj=axisswap +order=-1,2,3,4
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT -1 2 3 4
-ROUNDTRIP 100
+operation proj=axisswap order=-1,2,3,4
+tolerance 0.000001 m
+accept 1 2 3 4
+expect -1 2 3 4
+roundtrip 100
-OPERATION +proj=axisswap +order=1,2,3,-4
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT 1 2 3 -4
-ROUNDTRIP 100
+operation proj=axisswap order=1,2,3,-4
+tolerance 0.000001 m
+accept 1 2 3 4
+expect 1 2 3 -4
+roundtrip 100
-OPERATION +proj=axisswap +order=-2,1
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT -2 1 3 4
-ROUNDTRIP 100
-$
-OPERATION +proj=axisswap +order=3,-2,1
-TOLERANCE 0.000001 m
-ACCEPT 1 2 3 4
-EXPECT 3 -2 1 4
-ROUNDTRIP 100
+operation proj=axisswap order=-2,1
+tolerance 0.000001 m
+accept 1 2 3 4
+expect -2 1 3 4
+roundtrip 100
+operation proj=axisswap order=3,-2,1
+tolerance 0.000001 m
+accept 1 2 3 4
+expect 3 -2 1 4
+roundtrip 100
-OPERATION proj=pipeline step proj=latlong step proj=axisswap order=1,2,3,4 angularunits
-TOLERANCE 0.00001 m
-ACCEPT 12 55 0 0
-EXPECT 12 55 0 0
-OPERATION +proj=pipeline +step +proj=latlong +step +proj=axisswap +order=-2,-1,3,4 +angularunits
-TOLERANCE 0.00001 m
-ACCEPT 12 55 0 0
-EXPECT -55 -12 0 0
+operation proj=pipeline
+ step proj=latlong
+ step proj=axisswap
+ order=1,2,3,4
+ angularunits
--------------------------------------------------------------------------------
-operation +proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2
--------------------------------------------------------------------------------
-tolerance 0.00010 mm
-accept 2 1
-expect 222571.608757106 110653.326743030
-ROUNDTRIP 100
+tolerance 0.00001 m
+accept 12 55 0 0
+expect 12 55 0 0
+
+operation proj=pipeline
+ step proj=latlong
+ step proj=axisswap
+ order=-2,-1,3,4
+ angularunits
+
+tolerance 0.00001 m
+accept 12 55 0 0
+expect -55 -12 0 0
-END
+</gie>
diff --git a/test/gie/builtins.gie b/test/gie/builtins.gie
index 6b820c01..a9acf4f9 100644
--- a/test/gie/builtins.gie
+++ b/test/gie/builtins.gie
@@ -10,7 +10,7 @@ See more_builtins.gie for some test cases with a more human touch.
===============================================================================
-BEGIN
+<gie>
===============================================================================
Albers Equal Area
@@ -505,7 +505,7 @@ expect -0.001790493 -0.000895247
===============================================================================
Central Conic
Sph
- lat_1
+ lat_1
===============================================================================
-------------------------------------------------------------------------------
@@ -4716,6 +4716,5 @@ accept -200 100
expect -0.001790493 0.000895247
accept -200 -100
expect -0.001790493 -0.000895247
-Internal regression tests done. [Stubs: 0] Total: 151. Failure: 0. Success: 151
-END
+</gie>
diff --git a/test/gie/deformation.gie b/test/gie/deformation.gie
index 07a8b45e..029387c4 100644
--- a/test/gie/deformation.gie
+++ b/test/gie/deformation.gie
@@ -9,27 +9,28 @@ The input coordinate is located at lon=60, lam=-160 - somewhere in Alaska.
===============================================================================
-BEGIN
+<gie>
-------------------------------------------------------------------------------
Test using both horizontal and vertical grids as well as the +tobs parameter
-------------------------------------------------------------------------------
-OPERATION +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +t_obs=2000.0 +ellps=GRS80
+operation +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx
+ +t_epoch=2016.0 +t_obs=2000.0 +ellps=GRS80
-------------------------------------------------------------------------------
-TOLERANCE 0.1 mm
-ACCEPT -3004295.5882503074 -1093474.1690603832 5500477.1338251457
-EXPECT -3004295.7025 -1093474.2106 5500477.3444
-ROUNDTRIP 5
+tolerance 0.1 mm
+accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457
+expect -3004295.7025 -1093474.2106 5500477.3444
+roundtrip 5
-------------------------------------------------------------------------------
Test using both horizontal and vertical grids
-------------------------------------------------------------------------------
-OPERATION +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +ellps=GRS80
+operation +proj=deformation
+ +xy_grids=alaska +z_grids=egm96_15.gtx +t_epoch=2016.0 +ellps=GRS80
-------------------------------------------------------------------------------
-TOLERANCE 0.1 mm
-ACCEPT -3004295.5882503074 -1093474.1690603832 5500477.1338251457 2000.0
-EXPECT -3004295.7025 -1093474.2106 5500477.3444 2000.0
-ROUNDTRIP 5
+tolerance 0.1 mm
+accept -3004295.5882503074 -1093474.1690603832 5500477.1338251457 2000.0
+expect -3004295.7025 -1093474.2106 5500477.3444 2000.0
+roundtrip 5
-
-END
+</gie>
diff --git a/test/gie/ellipsoid.gie b/test/gie/ellipsoid.gie
index 65101f6c..81208bc1 100644
--- a/test/gie/ellipsoid.gie
+++ b/test/gie/ellipsoid.gie
@@ -5,7 +5,7 @@ Test pj_ellipsoid, the reimplementation of pj_ell_set
===============================================================================
-BEGIN
+<gie>
-------------------------------------------------------------------------------
First a spherical example
@@ -56,7 +56,8 @@ operation proj=merc ellps=GRS80000000000
expect failure errno unknown_ellp_param
operation proj=merc +a=-1
expect failure errno major_axis_not_given
-operation proj=merc +no_defs
+
+operation proj=merc no_defs
expect failure errno major_axis_not_given
# This one should succeed due to ellps=WGS84 in proj_def.dat
@@ -158,4 +159,4 @@ accept 12 55
expect 700416.5900 5669475.8884
-------------------------------------------------------------------------------
-END
+</gie>
diff --git a/test/gie/more_builtins.gie b/test/gie/more_builtins.gie
index 501406ae..2d558f88 100644
--- a/test/gie/more_builtins.gie
+++ b/test/gie/more_builtins.gie
@@ -8,7 +8,7 @@ intends to exercise corner cases.
===============================================================================
-BEGIN
+<gie>
-------------------------------------------------------------------------------
Two ob_tran tests from nad/testvarious
@@ -34,8 +34,8 @@ expect -1384841.18787 7581707.88240 0 0
-------------------------------------------------------------------------------
Two tests from PJ_molodensky.c
-------------------------------------------------------------------------------
-operation proj=molodensky a=6378160 rf=298.25 \
- da=-23 df=-8.120449e-8 dx=-134 dy=-48 dz=149 \
+operation proj=molodensky a=6378160 rf=298.25
+ da=-23 df=-8.120449e-8 dx=-134 dy=-48 dz=149
abridged
-------------------------------------------------------------------------------
tolerance 2 m
@@ -47,7 +47,7 @@ roundtrip 100 1 m
-------------------------------------------------------------------------------
Same thing once more, but this time unabridged
-------------------------------------------------------------------------------
-operation proj=molodensky a=6378160 rf=298.25 \
+operation proj=molodensky a=6378160 rf=298.25
da=-23 df=-8.120449e-8 dx=-134 dy=-48 dz=149
-------------------------------------------------------------------------------
tolerance 2 m
@@ -67,8 +67,8 @@ Some tests from PJ_pipeline.c
-------------------------------------------------------------------------------
Forward-reverse geo->utm->geo
-------------------------------------------------------------------------------
-operation proj=pipeline zone=32 step \
- proj=utm ellps=GRS80 step \
+operation proj=pipeline zone=32 step
+ proj=utm ellps=GRS80 step
proj=utm ellps=GRS80 inv
-------------------------------------------------------------------------------
tolerance 0.1 mm
@@ -83,8 +83,8 @@ expect 12 55 0 0
-------------------------------------------------------------------------------
And now the back-to-back situation utm->geo->utm
-------------------------------------------------------------------------------
-operation proj=pipeline zone=32 ellps=GRS80 step \
- proj=utm inv step \
+operation proj=pipeline zone=32 ellps=GRS80 step
+ proj=utm inv step
proj=utm
-------------------------------------------------------------------------------
accept 691875.63214 6098907.82501 0 0
@@ -104,10 +104,10 @@ expect 12 55 0 0
-------------------------------------------------------------------------------
Finally test a pipeline with more than one init step
-------------------------------------------------------------------------------
-operation proj=pipeline step \
- init=epsg:25832 inv step \
- init=epsg:25833 step \
- init=epsg:25833 inv step \
+operation proj=pipeline step
+ init=epsg:25832 inv step
+ init=epsg:25833 step
+ init=epsg:25833 inv step
init=epsg:25832
-------------------------------------------------------------------------------
accept 691875.63214 6098907.82501 0 0
@@ -131,7 +131,7 @@ N(12.5, 55.5) = -36.021305084228515625 (download.osgeo.org)
N(12.5, 55.5) = -35.880001068115234000 (OSGeo4W)
This is annoying, but must be handled elsewhere. We jump through the hoop by
setting a very lax tolerance.
-
+-------------------------------------------------------------------------------
tolerance 15 cm
accept 12.5 55.5 0 0
expect 12.5 55.5 -36.021305084228516 0
@@ -145,6 +145,7 @@ expect failure errno no_args
Fail on purpose: open non-existing grid
operation proj=vgridshift grids=nonexistinggrid.gtx
expect failure errno failed_to_load_grid
+-------------------------------------------------------------------------------
@@ -164,13 +165,14 @@ direction inverse
accept 172.999892181021551 -45.001620431954613 0 0
expect 173 -45 0 0
-------------------------------------------------------------------------------
-Fail on purpose: +grids parameter is mandatory:
-operation proj=hgridshift
-expect failure errno no_args
-Fail on purpose: open non-existing grid:
+# Fail on purpose: open non-existing grid:
operation proj=hgridshift grids=@nonexistinggrid.gsb,anothernonexistinggrid.gsb
expect failure errno failed_to_load_grid
+
+# Fail on purpose: +grids parameter is mandatory:
+operation proj=hgridshift
+expect failure errno no_args
-------------------------------------------------------------------------------
@@ -179,44 +181,57 @@ expect failure errno failed_to_load_grid
-------------------------------------------------------------------------------
A number of tests from PJ_helmert.c
-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
This example is from Lotti Jivall: "Simplified transformations from
ITRF2008/IGS08 to ETRS89 for maritime applications"
-------------------------------------------------------------------------------
-operation proj=helmert ellps=GRS80 \
- x=0.67678 y=0.65495 z=-0.52827 \
+operation proj=helmert ellps=GRS80
+ x=0.67678 y=0.65495 z=-0.52827
rx=-0.022742 ry=0.012667 rz=0.022704 s=-0.01070
-------------------------------------------------------------------------------
tolerance 1 um
accept 3565285.00000000 855949.00000000 5201383.00000000
expect 3565285.41342351 855948.67986759 5201382.72939791
-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
This example is a random point, transformed from ED50 to ETRS89 using KMStrans2
-------------------------------------------------------------------------------
-operation proj=helmert ellps=GRS80 exact \
- x=-81.0703 y=-89.3603 z=-115.7526 \
- rx=-0.48488 ry=-0.02436 rz=-0.41321 s=-0.540645
+operation proj=helmert ellps=GRS80 exact
+ x=-081.0703 rx=-0.48488
+ y=-089.3603 ry=-0.02436
+ z=-115.7526 rz=-0.41321 s=-0.540645
-------------------------------------------------------------------------------
tolerance 1 um
accept 3494994.30120000 1056601.97250000 5212382.16660000
expect 3494909.84026368 1056506.78938633 5212265.66699761
-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
This example is a coordinate from the geodetic observatory in Onsala,
Sweden transformed from ITRF2000 @ 2017.0 to ITRF93 @ 2017.0.
The test coordinate was transformed using GNSStrans, using transformation
parameters published by ITRF: ftp://itrf.ensg.ign.fr/pub/itrf/ITRF.TP
-------------------------------------------------------------------------------
-operation proj=helmert ellps=GRS80 \
- x=0.0127 y=0.0065 z=-0.0209 s=0.00195 \
- dx=-0.0029 dy=-0.0002 dz=-0.0006 ds=0.00001 \
- rx=-0.00039 ry=0.00080 rz=-0.00114 \
- drx=-0.00011 dry=-0.00019 drz=0.00007 \
- t_epoch=1988.0 t_obs=2017.0 transpose
+operation proj=helmert ellps=GRS80 transpose
+ x = 0.0127 dx = -0.0029 rx = -0.00039 drx = -0.00011
+ y = 0.0065 dy = -0.0002 ry = 0.00080 dry = -0.00019
+ z = -0.0209 dz = -0.0006 rz = -0.00114 drz = 0.00007
+
+ s = 0.00195 ds = 0.00001 t_epoch = 1988.0
-------------------------------------------------------------------------------
tolerance 0.03 mm
accept 3370658.37800 711877.31400 5349787.08600 2017.0 # ITRF2000@2017.0
expect 3370658.18890 711877.42370 5349787.12430 2017.0 # ITRF93@2017.0
-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
This example is from "A mathematical relationship between NAD27 and NAD83 (91)
State Plane coordinates in Southeastern Wisconsin":
http://www.sewrpc.org/SEWRPCFiles/Publications/TechRep/tr-034-Mathematical-Relationship-Between-NAD27-and-NAD83-91-State-Plane-Coordinates-Southeastern-Wisconsin.pdf
@@ -225,25 +240,28 @@ The test data is taken from p. 29. Here we are using point 203 and converting it
from NAD27 (ft) -> NAD83 (m). The paper reports a difference of 0.0014 m from
measured to computed coordinates, hence the test tolerance is set accordingly.
-------------------------------------------------------------------------------
-operation proj=helmert ellps=GRS80 \
- x=-9597.3572 y=.6112 \
+operation proj=helmert ellps=GRS80
+ x=-9597.3572 y=.6112
s=0.304794780637 theta=-1.244048
-------------------------------------------------------------------------------
tolerance 1 mm
accept 2546506.957 542256.609 0
expect 766563.675 165282.277 0
-------------------------------------------------------------------------------
+
+
+-------------------------------------------------------------------------------
Finally test the 4D-capabilities of the proj.h API, especially that the
rotation matrix is updated when necessary.
Test coordinates from GNSStrans.
-------------------------------------------------------------------------------
-operation proj=helmert ellps=GRS80 \
- x=0.01270 y=0.00650 z=-0.0209 s=0.00195 \
- dx=-0.0029 dy=-0.0002 dz=-0.0006 ds=0.00001 \
- rx=-0.00039 ry=0.00080 rz=-0.00114 \
- drx=-0.00011 dry=-0.00019 drz=0.00007 \
- t_epoch=1988.0 transpose
+operation proj=helmert ellps=GRS80 transpose
+ x = 0.01270 dx =-0.0029 rx =-0.00039 drx =-0.00011
+ y = 0.00650 dy =-0.0002 ry = 0.00080 dry =-0.00019
+ z =-0.0209 dz =-0.0006 rz =-0.00114 drz = 0.00007
+ s = 0.00195 ds = 0.00001
+ t_epoch=1988.0
-------------------------------------------------------------------------------
tolerance 0.1 mm
accept 3370658.378 711877.314 5349787.086 2017.0
@@ -293,4 +311,4 @@ to express in the gie command set
builtins
-------------------------------------------------------------------------------
-END
+</gie>
diff --git a/test/gie/pip.gie b/test/gie/pip.gie
new file mode 100644
index 00000000..0cb54614
--- /dev/null
+++ b/test/gie/pip.gie
@@ -0,0 +1,41 @@
+These two tests have occasionally been causing segfaults from gie, but
+only at low PROJ_DEBUG levels.
+
+<gie>
+-------------------------------------------------------------------------------
+Finally test a pipeline with more than one init step
+-------------------------------------------------------------------------------
+operation proj=pipeline step
+ init=epsg:25832 inv step
+ init=epsg:25833 step
+ init=epsg:25833 inv step
+ init=epsg:25832
+-------------------------------------------------------------------------------
+accept 691875.63214 6098907.82501 0 0
+expect 691875.63214 6098907.82501 0 0
+direction inverse
+accept 12 55 0 0
+expect 12 55 0 0
+-------------------------------------------------------------------------------
+
+
+
+-------------------------------------------------------------------------------
+This example is a coordinate from the geodetic observatory in Onsala,
+Sweden transformed from ITRF2000 @ 2017.0 to ITRF93 @ 2017.0.
+
+The test coordinate was transformed using GNSStrans, using transformation
+parameters published by ITRF: ftp://itrf.ensg.ign.fr/pub/itrf/ITRF.TP
+-------------------------------------------------------------------------------
+operation proj=helmert ellps=GRS80 transpose
+ x = 0.0127 dx = -0.0029 rx = -0.00039 drx = -0.00011
+ y = 0.0065 dy = -0.0002 ry = 0.00080 dry = -0.00019
+ z = -0.0209 dz = -0.0006 rz = -0.00114 drz = 0.00007
+
+ s = 0.00195 ds = 0.00001 t_epoch = 1988.0
+-------------------------------------------------------------------------------
+tolerance 0.03 mm
+accept 3370658.37800 711877.31400 5349787.08600 2017.0 # ITRF2000@2017.0
+expect 3370658.18890 711877.42370 5349787.12430 2017.0 # ITRF93@2017.0
+-------------------------------------------------------------------------------
+</gie>
diff --git a/travis/install.sh b/travis/install.sh
index 8126182b..0066c6b7 100755
--- a/travis/install.sh
+++ b/travis/install.sh
@@ -81,7 +81,7 @@ if [ $TRAVIS_OS_NAME == "osx" ]; then
make -j3
make check
PROJ_LIB=$GRIDDIR ./src/gie ./test/gie/builtins.gie
-PROJ_LIB=$GRIDDIR ./src/gie ./test/gie/more_builtins.gie
+PROJ_LIB=$GRIDDIR ./src/gie -vvvvv ./test/gie/more_builtins.gie
PROJ_LIB=$GRIDDIR ./src/gie ./test/gie/deformation.gie
PROJ_LIB=$GRIDDIR ./src/gie ./test/gie/axisswap.gie
PROJ_LIB=$GRIDDIR ./src/gie ./test/gie/ellipsoid.gie