diff options
| -rw-r--r-- | docs/source/operations/pipeline.rst | 31 | ||||
| -rw-r--r-- | src/iso19111/io.cpp | 57 | ||||
| -rw-r--r-- | src/pipeline.cpp | 311 | ||||
| -rw-r--r-- | test/gie/4D-API_cs2cs-style.gie | 49 | ||||
| -rw-r--r-- | test/unit/test_io.cpp | 89 |
5 files changed, 393 insertions, 144 deletions
diff --git a/docs/source/operations/pipeline.rst b/docs/source/operations/pipeline.rst index 33112536..90112c05 100644 --- a/docs/source/operations/pipeline.rst +++ b/docs/source/operations/pipeline.rst @@ -128,3 +128,34 @@ Optional .. option:: +inv Invert a step in a pipeline. + +.. option:: +omit_fwd + + .. versionadded:: 6.3.0 + + Skip a step of the pipeline when it is followed in the forward path. + + The following example shows a combined use of :ref:`push <push>` and :ref:`pop <pop>` operators, + with ``omit_fwd`` and ``omit_inv`` options, to implement a vertical adjustment that must + be done in a interpolation CRS that is different from the horizontal CRS + used in input and output. +omit_fwd in the forward path avoid a useless + inverse horizontal transformation and relies on the pop operator to restore + initial horizontal coordinates. +omit_inv serves the similar purpose when + the pipeline is executed in the reverse direction + + :: + + +proj=pipeline + +step +proj=unitconvert +xy_in=deg +xy_out=rad + +step +proj=push +v_1 +v_2 + +step +proj=hgridshift +grids=nvhpgn.gsb +omit_inv + +step +proj=vgridshift +grids=g1999u05.gtx +multiplier=1 + +step +inv +proj=hgridshift +grids=nvhpgn.gsb +omit_fwd + +step +proj=pop +v_1 +v_2 + +step +proj=unitconvert +xy_in=rad +xy_out=deg + +.. option:: +omit_inv + + .. versionadded:: 6.3.0 + + Skip a step of the pipeline when it is followed in the reverse path. diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp index 678c5d98..0d98e2de 100644 --- a/src/iso19111/io.cpp +++ b/src/iso19111/io.cpp @@ -6278,12 +6278,25 @@ struct Step { return key == otherKey && value == otherVal; } + bool operator==(const KeyValue &other) const noexcept { + return key == other.key && value == other.value; + } + bool operator!=(const KeyValue &other) const noexcept { return key != other.key || value != other.value; } }; std::vector<KeyValue> paramValues{}; + + bool hasKey(const char *keyName) const { + for (const auto &kv : paramValues) { + if (kv.key == keyName) { + return true; + } + } + return false; + } }; Step::KeyValue::KeyValue(const char *keyIn, const std::string &valueIn) @@ -6790,6 +6803,46 @@ const std::string &PROJStringFormatter::toString() const { } } + // +step +proj=hgridshift +grids=grid_A + // +step +proj=vgridshift [...] <== curStep + // +step +inv +proj=hgridshift +grids=grid_A + // ==> + // +step +proj=push +v_1 +v_2 + // +step +proj=hgridshift +grids=grid_A +omit_inv + // +step +proj=vgridshift [...] + // +step +inv +proj=hgridshift +grids=grid_A +omit_fwd + // +step +proj=pop +v_1 +v_2 + if (i + 1 < d->steps_.size() && prevStep.name == "hgridshift" && + prevStepParamCount == 1 && curStep.name == "vgridshift") { + auto iterNext = iterCur; + ++iterNext; + auto &nextStep = *iterNext; + if (nextStep.name == "hgridshift" && + nextStep.inverted != prevStep.inverted && + nextStep.paramValues.size() == 1 && + prevStep.paramValues[0] == nextStep.paramValues[0]) { + Step pushStep; + pushStep.name = "push"; + pushStep.paramValues.emplace_back("v_1"); + pushStep.paramValues.emplace_back("v_2"); + d->steps_.insert(iterPrev, pushStep); + + prevStep.paramValues.emplace_back("omit_inv"); + + nextStep.paramValues.emplace_back("omit_fwd"); + + Step popStep; + popStep.name = "pop"; + popStep.paramValues.emplace_back("v_1"); + popStep.paramValues.emplace_back("v_2"); + ++iterNext; + d->steps_.insert(iterNext, popStep); + + changeDone = true; + break; + } + } + // detect a step and its inverse if (curStep.inverted != prevStep.inverted && curStep.name == prevStep.name && @@ -6813,7 +6866,9 @@ const std::string &PROJStringFormatter::toString() const { if (d->steps_.size() > 1 || (d->steps_.size() == 1 && - (d->steps_.front().inverted || !d->globalParamValues_.empty()))) { + (d->steps_.front().inverted || d->steps_.front().hasKey("omit_inv") || + d->steps_.front().hasKey("omit_fwd") || + !d->globalParamValues_.empty()))) { d->appendToResult("+proj=pipeline"); for (const auto ¶mValue : d->globalParamValues_) { diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 847f8194..96767143 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -101,6 +101,7 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 #include <stddef.h> #include <string.h> #include <stack> +#include <vector> #include "geodesic.h" #include "proj.h" @@ -112,15 +113,33 @@ PROJ_HEAD(push, "Save coordinate value on pipeline stack"); /* Projection specific elements for the PJ object */ namespace { // anonymous namespace -struct pj_opaque { - int steps; - char **argv; - char **current_argv; - PJ **pipeline; - std::stack<double> *stack[4]; + +struct Step { + PJ* pj = nullptr; + bool omit_fwd = false; + bool omit_inv = false; + + Step(PJ* pjIn, bool omitFwdIn, bool omitInvIn): + pj(pjIn), omit_fwd(omitFwdIn), omit_inv(omitInvIn) {} + Step(Step&& other): pj(std::move(other.pj)), + omit_fwd(other.omit_fwd), + omit_inv(other.omit_inv) { other.pj = nullptr; } + Step(const Step&) = delete; + Step& operator=(const Step&) = delete; + + ~Step() { + proj_destroy(pj); + } +}; + +struct Pipeline { + char **argv = nullptr; + char **current_argv = nullptr; + std::vector<Step> steps{}; + std::stack<double> stack[4]; }; -struct pj_opaque_pushpop { +struct PushPop { bool v1; bool v2; bool v3; @@ -138,32 +157,37 @@ static PJ_LP pipeline_reverse (PJ_XY xy, PJ *P); void pj_pipeline_assign_context_to_steps( PJ* P, PJ_CONTEXT* ctx ) { - for (int i = 1; i <= static_cast<struct pj_opaque*>(P->opaque)->steps; i++) - proj_assign_context(static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], ctx); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto& step: pipeline->steps ) + proj_assign_context(step.pj, ctx); } static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { - int i, first_step, last_step; - - first_step = 1; - last_step = static_cast<struct pj_opaque*>(P->opaque)->steps + 1; - - for (i = first_step; i != last_step; i++) - point = proj_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_FWD, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto& step: pipeline->steps ) + { + if( !step.omit_fwd ) + { + point = proj_trans (step.pj, PJ_FWD, point); + } + } return point; } static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { - int i, first_step, last_step; - - first_step = static_cast<struct pj_opaque*>(P->opaque)->steps; - last_step = 0; - - for (i = first_step; i != last_step; i--) - point = proj_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_INV, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto iterStep = pipeline->steps.rbegin(); + iterStep != pipeline->steps.rend(); ++iterStep ) + { + const auto& step = *iterStep; + if( !step.omit_inv ) + { + point = proj_trans (step.pj, PJ_INV, point); + } + } return point; } @@ -173,11 +197,15 @@ static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { static PJ_XYZ pipeline_forward_3d (PJ_LPZ lpz, PJ *P) { PJ_COORD point = {{0,0,0,0}}; - int i; point.lpz = lpz; - - for (i = 1; i <= static_cast<struct pj_opaque*>(P->opaque)->steps; i++) - point = pj_approx_3D_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_FWD, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto& step: pipeline->steps ) + { + if( !step.omit_fwd ) + { + point = pj_approx_3D_trans (step.pj, PJ_FWD, point); + } + } return point.xyz; } @@ -185,11 +213,17 @@ static PJ_XYZ pipeline_forward_3d (PJ_LPZ lpz, PJ *P) { static PJ_LPZ pipeline_reverse_3d (PJ_XYZ xyz, PJ *P) { PJ_COORD point = {{0,0,0,0}}; - int i; point.xyz = xyz; - - for (i = static_cast<struct pj_opaque*>(P->opaque)->steps; i > 0 ; i--) - point = pj_approx_3D_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_INV, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto iterStep = pipeline->steps.rbegin(); + iterStep != pipeline->steps.rend(); ++iterStep ) + { + const auto& step = *iterStep; + if( !step.omit_inv ) + { + point = proj_trans (step.pj, PJ_INV, point); + } + } return point.lpz; } @@ -199,11 +233,15 @@ static PJ_LPZ pipeline_reverse_3d (PJ_XYZ xyz, PJ *P) { static PJ_XY pipeline_forward (PJ_LP lp, PJ *P) { PJ_COORD point = {{0,0,0,0}}; - int i; point.lp = lp; - - for (i = 1; i <= static_cast<struct pj_opaque*>(P->opaque)->steps; i++) - point = pj_approx_2D_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_FWD, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto& step: pipeline->steps ) + { + if( !step.omit_fwd ) + { + point = pj_approx_2D_trans (step.pj, PJ_FWD, point); + } + } return point.xy; } @@ -211,10 +249,17 @@ static PJ_XY pipeline_forward (PJ_LP lp, PJ *P) { static PJ_LP pipeline_reverse (PJ_XY xy, PJ *P) { PJ_COORD point = {{0,0,0,0}}; - int i; point.xy = xy; - for (i = static_cast<struct pj_opaque*>(P->opaque)->steps; i > 0 ; i--) - point = pj_approx_2D_trans (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i], PJ_INV, point); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + for( auto iterStep = pipeline->steps.rbegin(); + iterStep != pipeline->steps.rend(); ++iterStep ) + { + const auto& step = *iterStep; + if( !step.omit_inv ) + { + point = pj_approx_2D_trans (step.pj, PJ_INV, point); + } + } return point.lp; } @@ -223,44 +268,24 @@ static PJ_LP pipeline_reverse (PJ_XY xy, PJ *P) { static PJ *destructor (PJ *P, int errlev) { - int i; if (nullptr==P) return nullptr; if (nullptr==P->opaque) return pj_default_destructor (P, errlev); - /* Deallocate each pipeline step, then pipeline array */ - if (nullptr!=static_cast<struct pj_opaque*>(P->opaque)->pipeline) - for (i = 0; i < static_cast<struct pj_opaque*>(P->opaque)->steps; i++) - proj_destroy (static_cast<struct pj_opaque*>(P->opaque)->pipeline[i+1]); - pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->pipeline); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); - pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->argv); - pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->current_argv); + pj_dealloc (pipeline->argv); + pj_dealloc (pipeline->current_argv); - for (i=0; i<4; i++) - delete static_cast<struct pj_opaque*>(P->opaque)->stack[i]; + delete pipeline; + P->opaque = nullptr; return pj_default_destructor(P, errlev); } -static PJ *pj_create_pipeline (PJ *P, size_t steps) { - - /* Room for the pipeline: An array of PJ * with room for sentinels at both ends */ - static_cast<struct pj_opaque*>(P->opaque)->pipeline = static_cast<PJ**>(pj_calloc (steps + 2, sizeof(PJ *))); - if (nullptr==static_cast<struct pj_opaque*>(P->opaque)->pipeline) - return nullptr; - - static_cast<struct pj_opaque*>(P->opaque)->steps = (int)steps; - - return P; -} - - - - /* count the number of args in pipeline definition, and mark all args as used */ static size_t argc_params (paralist *params) { size_t argc = 0; @@ -345,28 +370,30 @@ static void set_ellipsoid(PJ *P) { } -static enum pj_io_units get_next_non_whatever_unit(void *pipeline_data, int step, PJ_DIRECTION dir) { - PJ **pipeline = static_cast<struct pj_opaque*>(pipeline_data)->pipeline; - int nsteps = static_cast<struct pj_opaque*>(pipeline_data)->steps; - int i; +static enum pj_io_units get_next_non_whatever_unit(struct Pipeline* pipeline, size_t step, PJ_DIRECTION dir) { + const auto& steps = pipeline->steps; + const auto nsteps = steps.size(); if (dir == PJ_FWD) { - for (i = step+1; i<=nsteps; i++) { - if (pj_left(pipeline[i]) != pj_right(pipeline[i])) - return pj_left(pipeline[i]); - if (pj_left(pipeline[i]) != PJ_IO_UNITS_WHATEVER) - return pj_left(pipeline[i]); - if (pj_right(pipeline[i]) != PJ_IO_UNITS_WHATEVER) - return pj_right(pipeline[i]); + for (size_t i = step+1; i<nsteps; i++) { + auto pj = steps[i].pj; + if (pj_left(pj) != pj_right(pj)) + return pj_left(pj); + if (pj_left(pj) != PJ_IO_UNITS_WHATEVER) + return pj_left(pj); + if (pj_right(pj) != PJ_IO_UNITS_WHATEVER) + return pj_right(pj); } } else { - for (i=step; i>1; i--) { - if (pj_right(pipeline[i]) != pj_left(pipeline[i])) - return pj_right(pipeline[i]); - if (pj_right(pipeline[i]) != PJ_IO_UNITS_WHATEVER) - return pj_right(pipeline[i]); - if (pj_left(pipeline[i]) != PJ_IO_UNITS_WHATEVER) - return pj_left(pipeline[i]); + for (size_t i=step; i>0;) { + i--; + auto pj = steps[i].pj; + if (pj_right(pj) != pj_left(pj)) + return pj_right(pj); + if (pj_right(pj) != PJ_IO_UNITS_WHATEVER) + return pj_right(pj); + if (pj_left(pj) != PJ_IO_UNITS_WHATEVER) + return pj_left(pj); } } return PJ_IO_UNITS_WHATEVER; @@ -396,20 +423,17 @@ PJ *OPERATION(pipeline,0) { P->skip_inv_finalize = 1; - P->opaque = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof(struct pj_opaque))); + P->opaque = new (std::nothrow) Pipeline(); if (nullptr==P->opaque) return destructor(P, ENOMEM); - /* initialize stack */ - for (i=0; i<4; i++) - static_cast<struct pj_opaque*>(P->opaque)->stack[i] = new std::stack<double>; - argc = (int)argc_params (P->params); - static_cast<struct pj_opaque*>(P->opaque)->argv = argv = argv_params (P->params, argc); + auto pipeline = static_cast<struct Pipeline*>(P->opaque); + pipeline->argv = argv = argv_params (P->params, argc); if (nullptr==argv) return destructor (P, ENOMEM); - static_cast<struct pj_opaque*>(P->opaque)->current_argv = current_argv = static_cast<char**>(pj_calloc (argc, sizeof (char *))); + pipeline->current_argv = current_argv = static_cast<char**>(pj_calloc (argc, sizeof (char *))); if (nullptr==current_argv) return destructor (P, ENOMEM); @@ -435,7 +459,6 @@ PJ *OPERATION(pipeline,0) { } } nsteps--; /* Last instance of +step is just a sentinel */ - static_cast<struct pj_opaque*>(P->opaque)->steps = nsteps; if (-1==i_pipeline) return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ @@ -443,10 +466,6 @@ PJ *OPERATION(pipeline,0) { if (0==nsteps) return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ - /* Make room for the pipeline and execution indicators */ - if (nullptr==pj_create_pipeline (P, nsteps)) - return destructor (P, ENOMEM); - set_ellipsoid(P); /* Now loop over all steps, building a new set of arguments for each init */ @@ -492,20 +511,23 @@ PJ *OPERATION(pipeline,0) { proj_errno_restore (P, err); /* Is this step inverted? */ - for (j = 0; j < current_argc; j++) + for (j = 0; j < current_argc; j++) { if (0==strcmp("inv", current_argv[j])) { /* if +inv exists in both global and local args the forward operation should be used */ next_step->inverted = next_step->inverted == 0 ? 1 : 0; } + } - static_cast<struct pj_opaque*>(P->opaque)->pipeline[i+1] = next_step; + bool omit_fwd = pj_param(P->ctx, next_step->params, "bomit_fwd").i != 0; + bool omit_inv = pj_param(P->ctx, next_step->params, "bomit_inv").i != 0; + pipeline->steps.emplace_back(next_step, omit_fwd, omit_inv); proj_log_trace (P, "Pipeline at [%p]: step at [%p] (%s) done", P, next_step, current_argv[0]); } /* Require a forward path through the pipeline */ - for (i = 1; i <= nsteps; i++) { - PJ *Q = static_cast<struct pj_opaque*>(P->opaque)->pipeline[i]; + for( auto& step: pipeline->steps) { + PJ *Q = step.pj; if ( ( Q->inverted && (Q->inv || Q->inv3d || Q->fwd4d) ) || (!Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) ) { continue; @@ -516,8 +538,8 @@ PJ *OPERATION(pipeline,0) { } /* determine if an inverse operation is possible */ - for (i = 1; i <= nsteps; i++) { - PJ *Q = static_cast<struct pj_opaque*>(P->opaque)->pipeline[i]; + for( auto& step: pipeline->steps) { + PJ *Q = step.pj; if ( pj_has_inverse(Q) ) { continue; } else { @@ -534,31 +556,33 @@ PJ *OPERATION(pipeline,0) { /* proj=pipeline step proj=unitconvert xy_in=deg xy_out=rad step ... */ /* where the left-hand side units of the first step shouldn't be changed to RADIANS */ /* as it will result in deg->rad conversions in cs2cs and other applications. */ - PJ **pipeline = static_cast<struct pj_opaque*>(P->opaque)->pipeline; - for (i=1; i<=nsteps; i++) { - if (pj_left(pipeline[i]) == PJ_IO_UNITS_WHATEVER && pj_right(pipeline[i]) == PJ_IO_UNITS_WHATEVER) { - pipeline[i]->left = get_next_non_whatever_unit(P->opaque, i, PJ_FWD); - pipeline[i]->right = get_next_non_whatever_unit(P->opaque, i, PJ_FWD); + for (i=0; i<nsteps; i++) { + auto pj = pipeline->steps[i].pj; + if (pj_left(pj) == PJ_IO_UNITS_WHATEVER && pj_right(pj) == PJ_IO_UNITS_WHATEVER) { + pj->left = get_next_non_whatever_unit(pipeline, i, PJ_FWD); + pj->right = get_next_non_whatever_unit(pipeline, i, PJ_FWD); } } - for (i=nsteps; i>0; i--) { - if (pj_left(pipeline[i]) == PJ_IO_UNITS_WHATEVER && pj_right(pipeline[i]) == PJ_IO_UNITS_WHATEVER) { - pipeline[i]->right = get_next_non_whatever_unit(P->opaque, i, PJ_INV); - pipeline[i]->left = get_next_non_whatever_unit(P->opaque, i, PJ_INV); + for (i=nsteps; i>0;) { + --i; + auto pj = pipeline->steps[i].pj; + if (pj_left(pj) == PJ_IO_UNITS_WHATEVER && pj_right(pj) == PJ_IO_UNITS_WHATEVER) { + pj->right = get_next_non_whatever_unit(pipeline, i, PJ_INV); + pj->left = get_next_non_whatever_unit(pipeline, i, PJ_INV); } } /* Check that units between each steps match each other, fail if they don't */ - for (i = 1; i < nsteps; i++) { - enum pj_io_units curr_step_output = pj_right (pipeline[i]); - enum pj_io_units next_step_input = pj_left (pipeline[i+1]); + for (i = 0; i + 1 < nsteps; i++) { + enum pj_io_units curr_step_output = pj_right (pipeline->steps[i].pj); + enum pj_io_units next_step_input = pj_left (pipeline->steps[i+1].pj); if ( curr_step_output == PJ_IO_UNITS_WHATEVER || next_step_input == PJ_IO_UNITS_WHATEVER ) continue; if ( curr_step_output != next_step_input ) { - proj_log_error (P, "Pipeline: Mismatched units between step %d and %d", i, i+1); + proj_log_error (P, "Pipeline: Mismatched units between step %d and %d", i+1, i+2); return destructor (P, PJD_ERR_MALFORMED_PIPELINE); } } @@ -566,10 +590,10 @@ PJ *OPERATION(pipeline,0) { proj_log_trace (P, "Pipeline: %d steps built. Determining i/o characteristics", nsteps); /* Determine forward input (= reverse output) data type */ - P->left = pj_left (static_cast<struct pj_opaque*>(P->opaque)->pipeline[1]); + P->left = pj_left (pipeline->steps.front().pj); /* Now, correspondingly determine forward output (= reverse input) data type */ - P->right = pj_right (static_cast<struct pj_opaque*>(P->opaque)->pipeline[nsteps]); + P->right = pj_right (pipeline->steps.back().pj); return P; } @@ -577,17 +601,17 @@ static PJ_COORD push(PJ_COORD point, PJ *P) { if (P->parent == nullptr) return point; - struct pj_opaque *pipeline = static_cast<struct pj_opaque*>(P->parent->opaque); - struct pj_opaque_pushpop *opaque = static_cast<struct pj_opaque_pushpop*>(P->opaque); + struct Pipeline *pipeline = static_cast<struct Pipeline*>(P->parent->opaque); + struct PushPop *pushpop = static_cast<struct PushPop*>(P->opaque); - if (opaque->v1) - pipeline->stack[0]->push(point.v[0]); - if (opaque->v2) - pipeline->stack[1]->push(point.v[1]); - if (opaque->v3) - pipeline->stack[2]->push(point.v[2]); - if (opaque->v4) - pipeline->stack[3]->push(point.v[3]); + if (pushpop->v1) + pipeline->stack[0].push(point.v[0]); + if (pushpop->v2) + pipeline->stack[1].push(point.v[1]); + if (pushpop->v3) + pipeline->stack[2].push(point.v[2]); + if (pushpop->v4) + pipeline->stack[3].push(point.v[3]); return point; } @@ -596,27 +620,27 @@ static PJ_COORD pop(PJ_COORD point, PJ *P) { if (P->parent == nullptr) return point; - struct pj_opaque *pipeline = static_cast<struct pj_opaque*>(P->parent->opaque); - struct pj_opaque_pushpop *opaque = static_cast<struct pj_opaque_pushpop*>(P->opaque); + struct Pipeline *pipeline = static_cast<struct Pipeline*>(P->parent->opaque); + struct PushPop *pushpop = static_cast<struct PushPop*>(P->opaque); - if (opaque->v1 && !pipeline->stack[0]->empty()) { - point.v[0] = pipeline->stack[0]->top(); - pipeline->stack[0]->pop(); + if (pushpop->v1 && !pipeline->stack[0].empty()) { + point.v[0] = pipeline->stack[0].top(); + pipeline->stack[0].pop(); } - if (opaque->v2 && !pipeline->stack[1]->empty()) { - point.v[1] = pipeline->stack[1]->top(); - pipeline->stack[1]->pop(); + if (pushpop->v2 && !pipeline->stack[1].empty()) { + point.v[1] = pipeline->stack[1].top(); + pipeline->stack[1].pop(); } - if (opaque->v3 && !pipeline->stack[2]->empty()) { - point.v[2] = pipeline->stack[2]->top(); - pipeline->stack[2]->pop(); + if (pushpop->v3 && !pipeline->stack[2].empty()) { + point.v[2] = pipeline->stack[2].top(); + pipeline->stack[2].pop(); } - if (opaque->v4 && !pipeline->stack[3]->empty()) { - point.v[3] = pipeline->stack[3]->top(); - pipeline->stack[3]->pop(); + if (pushpop->v4 && !pipeline->stack[3].empty()) { + point.v[3] = pipeline->stack[3].top(); + pipeline->stack[3].pop(); } return point; @@ -625,21 +649,22 @@ static PJ_COORD pop(PJ_COORD point, PJ *P) { static PJ *setup_pushpop(PJ *P) { - P->opaque = static_cast<struct pj_opaque_pushpop*>(pj_calloc (1, sizeof(struct pj_opaque_pushpop))); + auto pushpop = static_cast<struct PushPop*>(pj_calloc (1, sizeof(struct PushPop))); + P->opaque = pushpop; if (nullptr==P->opaque) return destructor(P, ENOMEM); if (pj_param_exists(P->params, "v_1")) - static_cast<struct pj_opaque_pushpop*>(P->opaque)->v1 = true; + pushpop->v1 = true; if (pj_param_exists(P->params, "v_2")) - static_cast<struct pj_opaque_pushpop*>(P->opaque)->v2 = true; + pushpop->v2 = true; if (pj_param_exists(P->params, "v_3")) - static_cast<struct pj_opaque_pushpop*>(P->opaque)->v3 = true; + pushpop->v3 = true; if (pj_param_exists(P->params, "v_4")) - static_cast<struct pj_opaque_pushpop*>(P->opaque)->v4 = true; + pushpop->v4 = true; P->left = PJ_IO_UNITS_WHATEVER; P->right = PJ_IO_UNITS_WHATEVER; diff --git a/test/gie/4D-API_cs2cs-style.gie b/test/gie/4D-API_cs2cs-style.gie index 19b3ad96..e5722b5e 100644 --- a/test/gie/4D-API_cs2cs-style.gie +++ b/test/gie/4D-API_cs2cs-style.gie @@ -386,6 +386,55 @@ operation +proj=pop +v_3 accept 12 56 0 0 expect 12 56 0 0 +------------------------------------------------------------------------------- +Test Pipeline +omit_inv +------------------------------------------------------------------------------- + +operation +proj=pipeline + +step +proj=affine +xoff=1 +yoff=1 +omit_inv + +accept 2 49 0 0 +expect 3 50 0 0 + +direction inverse +accept 2 49 0 0 +expect 2 49 0 0 + + +operation +proj=pipeline + +step +inv +proj=affine +xoff=1 +yoff=1 +omit_inv + +accept 2 49 0 0 +expect 1 48 0 0 + +direction inverse +accept 2 49 0 0 +expect 2 49 0 0 + +------------------------------------------------------------------------------- +Test Pipeline +omit_fwd +------------------------------------------------------------------------------- + +operation +proj=pipeline + +step +proj=affine +xoff=1 +yoff=1 +omit_fwd + +accept 2 49 0 0 +expect 2 49 0 0 + +direction inverse +accept 2 49 0 0 +expect 1 48 0 0 + + +operation +proj=pipeline + +step +inv +proj=affine +xoff=1 +yoff=1 +omit_fwd + +accept 2 49 0 0 +expect 2 49 0 0 + +direction inverse +accept 2 49 0 0 +expect 3 50 0 0 ------------------------------------------------------------------------------- Test bugfix of https://github.com/OSGeo/proj.4/issues/1002 diff --git a/test/unit/test_io.cpp b/test/unit/test_io.cpp index fff86d27..552eb5bf 100644 --- a/test/unit/test_io.cpp +++ b/test/unit/test_io.cpp @@ -6832,6 +6832,95 @@ TEST(io, projstringformatter_axisswap_unitconvert_axisswap) { // --------------------------------------------------------------------------- +TEST(io, projstringformatter_optim_hgridshift_vgridshift_hgridshift_inv) { + // Nominal case + { + auto fmt = PROJStringFormatter::create(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + + fmt->addStep("vgridshift"); + fmt->addParam("grids", "bar"); + + fmt->startInversion(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + fmt->stopInversion(); + + EXPECT_EQ(fmt->toString(), + "+proj=pipeline " + "+step +proj=push +v_1 +v_2 " + "+step +proj=hgridshift +grids=foo +omit_inv " + "+step +proj=vgridshift +grids=bar " + "+step +inv +proj=hgridshift +grids=foo +omit_fwd " + "+step +proj=pop +v_1 +v_2"); + } + + // Variant with first hgridshift inverted, and second forward + { + auto fmt = PROJStringFormatter::create(); + + fmt->startInversion(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + fmt->stopInversion(); + + fmt->addStep("vgridshift"); + fmt->addParam("grids", "bar"); + + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + + EXPECT_EQ(fmt->toString(), + "+proj=pipeline " + "+step +proj=push +v_1 +v_2 " + "+step +inv +proj=hgridshift +grids=foo +omit_inv " + "+step +proj=vgridshift +grids=bar " + "+step +proj=hgridshift +grids=foo +omit_fwd " + "+step +proj=pop +v_1 +v_2"); + } + + // Do not apply ! not same grid name + { + auto fmt = PROJStringFormatter::create(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + + fmt->addStep("vgridshift"); + fmt->addParam("grids", "bar"); + + fmt->startInversion(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo2"); + fmt->stopInversion(); + + EXPECT_EQ(fmt->toString(), "+proj=pipeline " + "+step +proj=hgridshift +grids=foo " + "+step +proj=vgridshift +grids=bar " + "+step +inv +proj=hgridshift +grids=foo2"); + } + + // Do not apply ! missing inversion + { + auto fmt = PROJStringFormatter::create(); + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + + fmt->addStep("vgridshift"); + fmt->addParam("grids", "bar"); + + fmt->addStep("hgridshift"); + fmt->addParam("grids", "foo"); + + EXPECT_EQ(fmt->toString(), "+proj=pipeline " + "+step +proj=hgridshift +grids=foo " + "+step +proj=vgridshift +grids=bar " + "+step +proj=hgridshift +grids=foo"); + } +} + +// --------------------------------------------------------------------------- + TEST(io, projparse_longlat) { auto expected = "GEODCRS[\"unknown\",\n" |
