aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/source/operations/pipeline.rst31
-rw-r--r--src/iso19111/io.cpp57
-rw-r--r--src/pipeline.cpp311
-rw-r--r--test/gie/4D-API_cs2cs-style.gie49
-rw-r--r--test/unit/test_io.cpp89
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 &paramValue : 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"