diff options
| author | Kristian Evers <kristianevers@gmail.com> | 2017-10-28 15:42:00 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-10-28 15:42:00 +0200 |
| commit | 0d0beff91ddfc2cc4d195a141524a139f3afb756 (patch) | |
| tree | 4e0ff81f91f0cf96ba4c3f6e2fde8d86f97e4fec | |
| parent | 1bb8efeb43e99dd0b032548abb9b390a63d12be6 (diff) | |
| download | PROJ-0d0beff91ddfc2cc4d195a141524a139f3afb756.tar.gz PROJ-0d0beff91ddfc2cc4d195a141524a139f3afb756.zip | |
Allow nested pipelines. (#629)
Allow nested pipelines when wrapped in +init's.
The previous behaviour was to quit pipeline initialization when
encountering a nested pipeline definition. With this commit that
behaviour is changed so that it is possible to nest pipelines as
long as they are defined elsewhere in a init-file.
This is useful in init-files where steps in complicated transformations
can be grouped in "sub-pipelines". These "sub-pipelines" can then be
used as individual steps in a larger and more complicated pipeline.
Nested pipelines are governed by the following rules:
1. You can't have more than one literal +proj=pipeline in a proj-string
2. Pipelines can be nested if they are wrapped up in a +init
3. More than one +init is disallowed in non-pipeline proj-strings
4. +inits are expanded as late as possible, that is they will only be
expanded in single operations (that can be a part of a pipeline)
| -rw-r--r-- | src/PJ_pipeline.c | 2 | ||||
| -rw-r--r-- | src/pj_init.c | 96 | ||||
| -rw-r--r-- | src/pj_strerrno.c | 3 | ||||
| -rw-r--r-- | src/projects.h | 1 |
4 files changed, 62 insertions, 40 deletions
diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c index ab2d3420..f1411cb4 100644 --- a/src/PJ_pipeline.c +++ b/src/PJ_pipeline.c @@ -380,7 +380,7 @@ PJ *PROJECTION(pipeline) { if (0==strcmp ("proj=pipeline", argv[i])) { if (-1 != i_pipeline) { - proj_log_error (P, "Pipeline: Nesting invalid"); + proj_log_error (P, "Pipeline: Nesting only allowed when child pipelines are wrapped in +init's"); return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: nested pipelines */ } i_pipeline = i; diff --git a/src/pj_init.c b/src/pj_init.c index 704a8b55..2a945397 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -381,8 +381,9 @@ pj_init_plus_ctx( projCtx ctx, const char *definition ) if( argc+1 == MAX_ARG ) { - pj_ctx_set_errno( ctx, -44 ); - goto bum_call; + pj_dalloc( defn_copy ); + pj_ctx_set_errno( ctx, PJD_ERR_UNPARSEABLE_CS_DEF ); + return 0; } argv[argc++] = defn_copy + i + 1; @@ -410,9 +411,7 @@ pj_init_plus_ctx( projCtx ctx, const char *definition ) /* perform actual initialization */ result = pj_init_ctx( ctx, argc, argv ); -bum_call: pj_dalloc( defn_copy ); - return result; } @@ -441,6 +440,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { int i; int found_def = 0; PJ *PIN = 0; + int n_pipelines = 0; + int n_inits = 0; if (0==ctx) ctx = pj_get_default_ctx (); @@ -452,36 +453,45 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); return 0; } - + + /* count occurrences of pipelines and inits */ + for (i = 0; i < argc; ++i) { + if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") ) + n_pipelines++; + if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5)) + n_inits++; + } + + /* can't have nested pipeline directly */ + if (n_pipelines > 1) { + pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); + return 0; + } + + /* don't allow more than one +init in non-pipeline operations */ + if (n_pipelines == 0 && n_inits > 1) { + pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS); + return 0; + } + /* put arguments into internal linked list */ start = curr = pj_mkparam(argv[0]); if (!curr) return pj_dealloc_params (ctx, start, ENOMEM); - /* build parameter list and expand +init's. Does not take care of a single +init. */ for (i = 1; i < argc; ++i) { curr->next = pj_mkparam(argv[i]); if (!curr->next) return pj_dealloc_params (ctx, start, ENOMEM); - - /* check if +init present */ - if (pj_param(ctx, curr, "tinit").i) { - found_def = 0; - curr = get_init(ctx, &curr, curr->next, pj_param(ctx, curr, "sinit").s, &found_def); - if (!curr) - 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); - - } else { - curr = curr->next; - } + curr = curr->next; } - /* in case the parameter list only consist of a +init parameter - it is expanded here (will not be handled in the above loop). */ - if (pj_param(ctx, start, "tinit").i && argc == 1) { + + /* 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) @@ -489,10 +499,10 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { 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)) return pj_default_destructor (PIN, PJD_ERR_PROJ_NOT_NAMED); @@ -500,10 +510,11 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { if (!s) return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); - - /* set defaults, unless inhibited */ - if (!(pj_param(ctx, start, "bno_defs").i)) - curr = get_defaults(ctx,&start, curr, name); + + /* 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; /* allocate projection structure */ @@ -511,6 +522,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { if (0==PIN) return pj_dealloc_params (ctx, start, ENOMEM); + PIN->ctx = ctx; PIN->params = start; PIN->is_latlong = 0; @@ -526,13 +538,21 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->vgridlist_geoid_count = 0; /* set datum parameters */ - if (pj_datum_set(ctx, start, PIN)) + if (pj_datum_set(ctx, start, PIN)) return pj_default_destructor (PIN, PJD_ERR_MISSING_ARGS); - /* set ellipsoid/sphere parameters */ - if (pj_ell_set(ctx, start, &PIN->a, &PIN->es)) { - pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); - return pj_default_destructor (PIN, PJD_ERR_MISSING_ARGS); + /* set ellipsoid/sphere parameters. If we are initializing a pipeline we */ + /* override ellipsoid-setter, since it also adds a "+ellps=WGS84" to the */ + /* parameter list which creates problems when the pipeline driver passes */ + /* the global parameters on the it's children. */ + if (n_pipelines > 0) { + PIN->a = 6378137.0; + PIN->es = .00669438002290341575; + } else { + if (pj_ell_set(ctx, start, &PIN->a, &PIN->es)) { + pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); + return pj_default_destructor (PIN, PJD_ERR_MISSING_ARGS); + } } PIN->a_orig = PIN->a; @@ -606,7 +626,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) ) return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); } - + /* axis orientation */ if( (pj_param(ctx, start,"saxis").s) != NULL ) { @@ -619,7 +639,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { || strchr( axis_legal, axis_arg[1] ) == NULL || 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 */ strcpy( PIN->axis, axis_arg ); } @@ -643,7 +663,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->k0 = pj_param(ctx, start, "dk").f; else PIN->k0 = 1.; - if (PIN->k0 <= 0.) + if (PIN->k0 <= 0.) return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); /* set units */ @@ -704,7 +724,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { && *next_str == '\0' ) value = name; - if (!value) + if (!value) return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN); PIN->from_greenwich = dmstor_ctx(ctx,value,NULL); } diff --git a/src/pj_strerrno.c b/src/pj_strerrno.c index 89a5a025..fe137b87 100644 --- a/src/pj_strerrno.c +++ b/src/pj_strerrno.c @@ -62,7 +62,8 @@ pj_err_list[] = { "missing required arguments", /* -54 */ "lat_0 = 0", /* -55 */ "ellipsoidal usage unsupported", /* -56 */ - + "only one +init allowed for non-pipeline operations", /* -57 */ + /* When adding error messages, remember to update ID defines in projects.h, and transient_error array in pj_transform */ }; diff --git a/src/projects.h b/src/projects.h index 1fe595bc..6ea0c914 100644 --- a/src/projects.h +++ b/src/projects.h @@ -532,6 +532,7 @@ struct FACTORS { #define PJD_ERR_MISSING_ARGS -54 #define PJD_ERR_LAT_0_IS_ZERO -55 #define PJD_ERR_ELLIPSOIDAL_UNSUPPORTED -56 +#define PJD_ERR_TOO_MANY_INITS -57 struct projFileAPI_t; |
