aboutsummaryrefslogtreecommitdiff
path: root/src/PJ_vgridshift.c
blob: 5ab4a16296f25936c5c345295a1e2d7f66d8a4c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#define PJ_LIB__
#include "proj_internal.h"
#include "projects.h"

PROJ_HEAD(vgridshift, "Vertical grid shift");


static XYZ forward_3d(LPZ lpz, PJ *P) {
    PJ_TRIPLET point;
    point.lpz = lpz;

    if (P->vgridlist_geoid != NULL) {
        /* Only try the gridshift if at least one grid is loaded,
         * otherwise just pass the coordinate through unchanged. */
        point.xyz.z -= proj_vgrid_value(P, point.lp);
    }

    return point.xyz;
}


static LPZ reverse_3d(XYZ xyz, PJ *P) {
    PJ_TRIPLET point;
    point.xyz = xyz;

    if (P->vgridlist_geoid != NULL) {
        /* Only try the gridshift if at least one grid is loaded,
         * otherwise just pass the coordinate through unchanged. */
        point.xyz.z += proj_vgrid_value(P, point.lp);
    }

    return point.lpz;
}


static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) {
    PJ_COORD point;
    point.xyz = forward_3d (obs.lpz, P);
    return point;
}

static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) {
    PJ_COORD point;
    point.lpz = reverse_3d (obs.xyz, P);
    return point;
}


PJ *TRANSFORMATION(vgridshift,0) {

   if (!pj_param(P->ctx, P->params, "tgrids").i) {
        proj_log_error(P, "vgridshift: +grids parameter missing.");
        return pj_default_destructor(P, PJD_ERR_NO_ARGS);
    }

    /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */
    proj_vgrid_init(P, "grids");

    /* Was gridlist compiled properly? */
    if ( proj_errno(P) ) {
        proj_log_error(P, "vgridshift: could not find required grid(s).");
        return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID);
    }

    P->fwd4d = forward_4d;
    P->inv4d = reverse_4d;
    P->fwd3d  = forward_3d;
    P->inv3d  = reverse_3d;
    P->fwd    = 0;
    P->inv    = 0;

    P->left  = PJ_IO_UNITS_RADIANS;
    P->right = PJ_IO_UNITS_RADIANS;

    return P;
}


#ifndef PJ_SELFTEST
/* selftest stub */
int pj_vgridshift_selftest (void) {return 0;}
#else
int pj_vgridshift_selftest (void) {
    PJ *P;
    PJ_COORD expect, a, b;
    double dist;
    int failures = 0;

    /* fail on purpose: +grids parameter is mandatory*/
    P = proj_create(PJ_DEFAULT_CTX, "+proj=vgridshift");
    if (0!=P) {
        proj_destroy (P);
        return 99;
    }

    /* fail on purpose: open non-existing grid */
    P = proj_create(PJ_DEFAULT_CTX, "+proj=vgridshift +grids=nonexistinggrid.gtx");
    if (0!=P) {
        proj_destroy (P);
        return 999;
    }

    /* Failure most likely means the grid is missing */
    P = proj_create(PJ_DEFAULT_CTX, "+proj=vgridshift +grids=egm96_15.gtx +ellps=GRS80");
    if (0==P)
        return 10;

    a = proj_coord(0,0,0,0);
    a.lpz.lam = PJ_TORAD(12.5);
    a.lpz.phi = PJ_TORAD(55.5);
    b = a;
    dist = proj_roundtrip (P, PJ_FWD, 1, &b);
    if (dist > 0.00000001)
        return 1;

    expect = a;
    /* Appears there is a difference between the egm96_15.gtx distributed by OSGeo4W,  */
    /* and the one from http://download.osgeo.org/proj/vdatum/egm96_15/egm96_15.gtx    */
    /* Was: expect.lpz.z   = -36.021305084228515625;  (download.osgeo.org)         */
    /* Was: expect.lpz.z   = -35.880001068115234000;  (OSGeo4W)                    */
    /* This is annoying, but must be handled elsewhere. So for now, we check for both. */
    expect.lpz.z   = -36.021305084228516;
    failures = 0;
    b = proj_trans(P, PJ_FWD, a);
    if (proj_xyz_dist(expect.xyz, b.xyz) > 1e-4)  failures++;
    expect.lpz.z   = -35.880001068115234000;
    if (proj_xyz_dist(expect.xyz, b.xyz) > 1e-4)  failures++;
    /* manual roundtrip a->b, b<-b, b==a */
    b = proj_trans(P, PJ_INV, b);
    if (proj_xyz_dist(a.xyz, b.xyz) > 1e-9)  failures++;
    if (failures > 1)
        return 2;

    proj_destroy (P);

    return 0;
}
#endif