Skip to content

Commit

Permalink
Merge a0d6896 into 06236ec
Browse files Browse the repository at this point in the history
  • Loading branch information
kbevers committed Feb 13, 2019
2 parents 06236ec + a0d6896 commit fafb6cc
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/source/operations/conversions/index.rst
Expand Up @@ -15,4 +15,6 @@ conversions.
cart
geoc
latlon
pop
push
unitconvert
94 changes: 94 additions & 0 deletions docs/source/operations/conversions/pop.rst
@@ -0,0 +1,94 @@
.. _pop:

================================================================================
Pop coordinate value to pipeline stack
================================================================================

.. versionadded:: 6.0.0

Retrieve components of a coordinate that was saved in a previous pipeline step.

+---------------------+--------------------------------------------------------+
| **Alias** | pop |
+---------------------+--------------------------------------------------------+
| **Domain** | 4D |
+---------------------+--------------------------------------------------------+
| **Input type** | Any |
+---------------------+--------------------------------------------------------+
| **Output type** | Any |
+---------------------+--------------------------------------------------------+

This operations makes it possible to retrieve coordinate components that was
saved in previous pipeline steps. A retrieved coordinate component is loaded,
or *popped*, from a memory stack that is part of a :ref:`pipeline<pipeline>`.
The pipeline coordinate stack is inspired by the stack data structure that is
commonly used in computer science. There's four stacks available: One four each
coordinate dimension. The dimensions, or coordinate components, are numbered
1--4. It is only possible to move data to and from the stack within the same
coordinate component number. Values can be saved to the stack by using the
:ref:`push operation<push>`.

If the pop operation is used by itself, e.g. not in a pipeline, it will
function as a no-operation that passes the coordinate through unchanged.
Similarly, if no coordinate component is available on the stack to be popped
the operation does nothing.

Examples
################################################################################

A common use of the :ref:`push<push>` and pop operations is in 3D
:ref:`Helmert<helmert>` transformations where only the horizontal components
are needed. This is often the case when combining heights from a legacy
vertical reference with a modern geocentric reference. Below is a an example of
such a transformation, where the horizontal part is transformed with a Helmert
operation but the vertical part is kept exactly as the input was.

::

$ echo 12 56 12.3 2020 | cct +proj=pipeline \
+step +proj=push +v_3 \
+step +proj=cart +ellps=GRS80 \
+step +proj=helmert +x=3000 +y=1000 +z=2000 \
+step +proj=cart +ellps=GRS80 +inv \
+step +proj=pop +v_3 \

12.0056753463 55.9866540552 12.3000 2000.0000

Note that the third coordinate component in the output is the same as the input.

The same transformation without the push and pop operations would look like this::

$ echo 12 56 12.3 2020 | cct +proj=pipeline \
+step +proj=cart +ellps=GRS80 \
+step +proj=helmert +x=3000 +y=1000 +z=2000 \
+step +proj=cart +ellps=GRS80 +inv \

12.0057 55.9867 3427.7404 2000.0000

Here the vertical component is adjusted significantly.

Parameters
################################################################################

.. option:: +v_1

Retrieves the first coordinate component from the pipeline stack

.. option:: +v_2

Retrieves the second coordinate component from the pipeline stack

.. option:: +v_3

Retrieves the third coordinate component from the pipeline stack

.. option:: +v_4

Retrieves the fourth coordinate component from the pipeline stack


Further reading
################################################################################

#. `Stack data structure on Wikipedia <https://en.wikipedia.org/wiki/Stack_(abstract_data_type)>`_

93 changes: 93 additions & 0 deletions docs/source/operations/conversions/push.rst
@@ -0,0 +1,93 @@
.. _push:

================================================================================
Push coordinate value to pipeline stack
================================================================================

.. versionadded:: 6.0.0

Save components of a coordinate from one step of a pipeline and make it
available for retrieving in another pipeline step.

+---------------------+--------------------------------------------------------+
| **Alias** | push |
+---------------------+--------------------------------------------------------+
| **Domain** | 4D |
+---------------------+--------------------------------------------------------+
| **Input type** | Any |
+---------------------+--------------------------------------------------------+
| **Output type** | Any |
+---------------------+--------------------------------------------------------+

This operations allows for components of coordinates to be saved for
application in a later step. A saved coordinate component is moved, or
*pushed*, to a memory stack that is part of a :ref:`pipeline<pipeline>`. The
pipeline coordinate stack is inspired by the stack data structure that is
commonly used in computer science. There's four stacks available: One four each
coordinate dimension. The dimensions, or coordinate components, are numbered
1--4. It is only possible to move data to and from the stack within the same
coordinate component number. Values can be moved off the stack again by using
the :ref:`pop operation<pop>`.

If the push operation is used by itself, e.g. not in a pipeline, it will
function as a no-operation that passes the coordinate through unchanged.

Examples
################################################################################

A common use of the push and :ref:`pop<pop>` operations is in 3D
:ref:`Helmert<helmert>` transformations where only the horizontal components
are needed. This is often the case when combining heights from a legacy
vertical reference with a modern geocentric reference. Below is a an example of
such a transformation, where the horizontal part is transformed with a Helmert
operation but the vertical part is kept exactly as the input was.

::

$ echo 12 56 12.3 2020 | cct +proj=pipeline \
+step +proj=push +v_3 \
+step +proj=cart +ellps=GRS80 \
+step +proj=helmert +x=3000 +y=1000 +z=2000 \
+step +proj=cart +ellps=GRS80 +inv \
+step +proj=pop +v_3 \

12.0056753463 55.9866540552 12.3000 2000.0000

Note that the third coordinate component in the output is the same as the input.

The same transformation without the push and pop operations would look like this::

$ echo 12 56 12.3 2020 | cct +proj=pipeline \
+step +proj=cart +ellps=GRS80 \
+step +proj=helmert +x=3000 +y=1000 +z=2000 \
+step +proj=cart +ellps=GRS80 +inv \

12.0057 55.9867 3427.7404 2000.0000

Here the vertical component is adjusted significantly.

Parameters
################################################################################

.. option:: +v_1

Stores the first coordinate component on the pipeline stack

.. option:: +v_2

Stores the second coordinate component on the pipeline stack

.. option:: +v_3

Stores the third coordinate component on the pipeline stack

.. option:: +v_4

Stores the fourth coordinate component on the pipeline stack


Further reading
################################################################################

#. `Stack data structure on Wikipedia <https://en.wikipedia.org/wiki/Stack_(abstract_data_type)>`_

2 changes: 2 additions & 0 deletions src/iso19111/io.cpp
Expand Up @@ -7771,6 +7771,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
if (d->ctx_) {
d->ctx_->curStringInCreateFromPROJString = projString;
}
auto log_level = proj_log_level(pj_context, PJ_LOG_NONE);
auto pj = pj_create_internal(
pj_context, (projString.find("type=crs") != std::string::npos
? projString + " +disable_grid_presence_check"
Expand All @@ -7780,6 +7781,7 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
d->ctx_->curStringInCreateFromPROJString.clear();
}
valid = pj != nullptr;
proj_log_level(pj_context, log_level);

// Remove parameters not understood by PROJ.
if (valid && d->steps_.size() == 1) {
Expand Down
111 changes: 109 additions & 2 deletions src/pipeline.cpp
Expand Up @@ -100,13 +100,16 @@ Thomas Knudsen, thokn@sdfe.dk, 2016-05-20
#include <math.h>
#include <stddef.h>
#include <string.h>
#include <stack>

#include "geodesic.h"
#include "proj.h"
#include "proj_internal.h"
#include "proj_internal.h"

PROJ_HEAD(pipeline, "Transformation pipeline manager");
PROJ_HEAD(pop, "Retrieve coordinate value from pipeline stack");
PROJ_HEAD(push, "Save coordinate value on pipeline stack");

/* Projection specific elements for the PJ object */
namespace { // anonymous namespace
Expand All @@ -115,9 +118,16 @@ struct pj_opaque {
char **argv;
char **current_argv;
PJ **pipeline;
std::stack<double> *stack[4];
};
} // anonymous namespace

struct pj_opaque_pushpop {
bool v1;
bool v2;
bool v3;
bool v4;
};
} // anonymous namespace


static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P);
Expand Down Expand Up @@ -217,7 +227,7 @@ static PJ *destructor (PJ *P, int errlev) {
if (nullptr==P->opaque)
return pj_default_destructor (P, errlev);

/* Deallocate each pipeine step, then pipeline array */
/* 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]);
Expand All @@ -226,6 +236,9 @@ static PJ *destructor (PJ *P, int errlev) {
pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->argv);
pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->current_argv);

for (i=0; i<4; i++)
delete static_cast<struct pj_opaque*>(P->opaque)->stack[i];

return pj_default_destructor(P, errlev);
}

Expand Down Expand Up @@ -384,6 +397,10 @@ PJ *OPERATION(pipeline,0) {
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);
if (nullptr==argv)
Expand Down Expand Up @@ -467,6 +484,7 @@ PJ *OPERATION(pipeline,0) {
proj_log_error (P, "Pipeline: Bad step definition: %s (%s)", current_argv[0], pj_strerrno (err_to_report));
return destructor (P, err_to_report); /* ERROR: bad pipeline def */
}
next_step->parent = P;

proj_errno_restore (P, err);

Expand Down Expand Up @@ -551,3 +569,92 @@ PJ *OPERATION(pipeline,0) {
P->right = pj_right (static_cast<struct pj_opaque*>(P->opaque)->pipeline[nsteps]);
return P;
}

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);

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]);

return point;
}

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);

if (opaque->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 (opaque->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();
}

return point;
}



static PJ *setup_pushpop(PJ *P) {
P->opaque = static_cast<struct pj_opaque_pushpop*>(pj_calloc (1, sizeof(struct pj_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;

if (pj_param_exists(P->params, "v_2"))
static_cast<struct pj_opaque_pushpop*>(P->opaque)->v2 = true;

if (pj_param_exists(P->params, "v_3"))
static_cast<struct pj_opaque_pushpop*>(P->opaque)->v3 = true;

if (pj_param_exists(P->params, "v_4"))
static_cast<struct pj_opaque_pushpop*>(P->opaque)->v4 = true;

P->left = PJ_IO_UNITS_WHATEVER;
P->right = PJ_IO_UNITS_WHATEVER;

return P;
}


PJ *OPERATION(push, 0) {
P->fwd4d = push;
P->inv4d = pop;

return setup_pushpop(P);
}

PJ *OPERATION(pop, 0) {
P->inv4d = push;
P->fwd4d = pop;

return setup_pushpop(P);
}
2 changes: 2 additions & 0 deletions src/pj_list.h
Expand Up @@ -116,6 +116,8 @@ PROJ_HEAD(pconic, "Perspective Conic")
PROJ_HEAD(patterson, "Patterson Cylindrical")
PROJ_HEAD(pipeline, "Transformation pipeline manager")
PROJ_HEAD(poly, "Polyconic (American)")
PROJ_HEAD(pop, "Retrieve coordinate value from pipeline stack")
PROJ_HEAD(push, "Save coordinate value on pipeline stack")
PROJ_HEAD(putp1, "Putnins P1")
PROJ_HEAD(putp2, "Putnins P2")
PROJ_HEAD(putp3, "Putnins P3")
Expand Down
1 change: 1 addition & 0 deletions src/proj_internal.h
Expand Up @@ -321,6 +321,7 @@ struct PJconsts {
const char *descr = nullptr; /* From pj_list.h or individual PJ_*.c file */
paralist *params = nullptr; /* Parameter list */
char *def_full = nullptr; /* Full textual definition (usually 0 - set by proj_pj_info) */
PJconsts *parent = nullptr; /* Parent PJ of pipeline steps - nullptr if not a pipeline step */

/* For debugging / logging purposes */
char *def_size = nullptr; /* Shape and size parameters extracted from params */
Expand Down

0 comments on commit fafb6cc

Please sign in to comment.