Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/transformations/vertoffset.cpp
Line
Count
Source
1
/************************************************************************
2
 * Copyright (c) 2022, Even Rouault <even.rouault at spatialys.com>
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a
5
 * copy of this software and associated documentation files (the "Software"),
6
 * to deal in the Software without restriction, including without limitation
7
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
 * and/or sell copies of the Software, and to permit persons to whom the
9
 * Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included
12
 * in all copies or substantial portions of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
 * DEALINGS IN THE SOFTWARE.
21
 *
22
 ***********************************************************************/
23
24
#include <errno.h>
25
#include <math.h>
26
27
#include "proj.h"
28
#include "proj_internal.h"
29
30
PROJ_HEAD(vertoffset, "Vertical Offset and Slope")
31
"\n\tTransformation"
32
    "\n\tlat_0= lon_0= dh= slope_lat= slope_lon=";
33
34
namespace { // anonymous namespace
35
struct pj_opaque_vertoffset {
36
    double slope_lon;
37
    double slope_lat;
38
    double zoff;
39
    double rho0;
40
    double nu0;
41
};
42
} // anonymous namespace
43
44
// Cf EPSG Dataset coordinate operation method code 1046 "Vertical Offset and
45
// Slope"
46
47
0
static double get_forward_offset(const PJ *P, double phi, double lam) {
48
0
    const struct pj_opaque_vertoffset *Q =
49
0
        (const struct pj_opaque_vertoffset *)P->opaque;
50
0
    return Q->zoff + Q->slope_lat * Q->rho0 * (phi - P->phi0) +
51
0
           Q->slope_lon * Q->nu0 * lam * cos(phi);
52
0
}
53
54
0
static PJ_XYZ forward_3d(PJ_LPZ lpz, PJ *P) {
55
0
    PJ_XYZ xyz;
56
    // We need to add lam0 (+lon_0) since it is subtracted in fwd_prepare(),
57
    // which is desirable for map projections, but not
58
    // for that method which modifies only the Z component.
59
0
    xyz.x = lpz.lam + P->lam0;
60
0
    xyz.y = lpz.phi;
61
0
    xyz.z = lpz.z + get_forward_offset(P, lpz.phi, lpz.lam);
62
0
    return xyz;
63
0
}
64
65
0
static PJ_LPZ reverse_3d(PJ_XYZ xyz, PJ *P) {
66
0
    PJ_LPZ lpz;
67
    // We need to subtract lam0 (+lon_0) since it is added in inv_finalize(),
68
    // which is desirable for map projections, but not
69
    // for that method which modifies only the Z component.
70
0
    lpz.lam = xyz.x - P->lam0;
71
0
    lpz.phi = xyz.y;
72
0
    lpz.z = xyz.z - get_forward_offset(P, lpz.phi, lpz.lam);
73
0
    return lpz;
74
0
}
75
76
/* Arcsecond to radians */
77
0
#define ARCSEC_TO_RAD (DEG_TO_RAD / 3600.0)
78
79
0
PJ *PJ_TRANSFORMATION(vertoffset, 1) {
80
0
    struct pj_opaque_vertoffset *Q = static_cast<struct pj_opaque_vertoffset *>(
81
0
        calloc(1, sizeof(struct pj_opaque_vertoffset)));
82
0
    if (nullptr == Q)
83
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
84
0
    P->opaque = (void *)Q;
85
86
0
    P->fwd3d = forward_3d;
87
0
    P->inv3d = reverse_3d;
88
89
0
    P->left = PJ_IO_UNITS_RADIANS;
90
0
    P->right = PJ_IO_UNITS_RADIANS;
91
92
    /* read args */
93
0
    Q->slope_lon = pj_param(P->ctx, P->params, "dslope_lon").f * ARCSEC_TO_RAD;
94
0
    Q->slope_lat = pj_param(P->ctx, P->params, "dslope_lat").f * ARCSEC_TO_RAD;
95
0
    Q->zoff = pj_param(P->ctx, P->params, "ddh").f;
96
0
    const double sinlat0 = sin(P->phi0);
97
0
    const double oneMinusEsSinlat0Square = 1 - P->es * (sinlat0 * sinlat0);
98
0
    Q->rho0 = P->a * (1 - P->es) /
99
0
              (oneMinusEsSinlat0Square * sqrt(oneMinusEsSinlat0Square));
100
0
    Q->nu0 = P->a / sqrt(oneMinusEsSinlat0Square);
101
102
0
    return P;
103
0
}