Coverage Report

Created: 2025-08-29 07:11

/src/PROJ/src/projections/chamb.cpp
Line
Count
Source (jump to first uncovered line)
1
2
3
#include <errno.h>
4
#include <math.h>
5
6
#include "proj.h"
7
#include "proj_internal.h"
8
9
typedef struct {
10
    double r, Az;
11
} VECT;
12
namespace { // anonymous namespace
13
struct pj_chamb {
14
    struct { /* control point data */
15
        double phi, lam;
16
        double cosphi, sinphi;
17
        VECT v;
18
        PJ_XY p;
19
    } c[3];
20
    PJ_XY p;
21
    double beta_0, beta_1, beta_2;
22
};
23
} // anonymous namespace
24
25
PROJ_HEAD(chamb, "Chamberlin Trimetric")
26
"\n\tMisc Sph, no inv"
27
    "\n\tlat_1= lon_1= lat_2= lon_2= lat_3= lon_3=";
28
29
#include <stdio.h>
30
0
#define THIRD 0.333333333333333333
31
54
#define TOL 1e-9
32
33
/* distance and azimuth from point 1 to point 2 */
34
static VECT vect(PJ_CONTEXT *ctx, double dphi, double c1, double s1, double c2,
35
54
                 double s2, double dlam) {
36
54
    VECT v;
37
54
    double cdl, dp, dl;
38
39
54
    cdl = cos(dlam);
40
54
    if (fabs(dphi) > 1. || fabs(dlam) > 1.)
41
17
        v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl);
42
37
    else { /* more accurate for smaller distances */
43
37
        dp = sin(.5 * dphi);
44
37
        dl = sin(.5 * dlam);
45
37
        v.r = 2. * aasin(ctx, sqrt(dp * dp + c1 * c2 * dl * dl));
46
37
    }
47
54
    if (fabs(v.r) > TOL)
48
51
        v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl);
49
3
    else
50
3
        v.r = v.Az = 0.;
51
54
    return v;
52
54
}
53
54
/* law of cosines */
55
32
static double lc(PJ_CONTEXT *ctx, double b, double c, double a) {
56
32
    return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c));
57
32
}
58
59
0
static PJ_XY chamb_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
60
0
    PJ_XY xy;
61
0
    struct pj_chamb *Q = static_cast<struct pj_chamb *>(P->opaque);
62
0
    double sinphi, cosphi, a;
63
0
    VECT v[3];
64
0
    int i, j;
65
66
0
    sinphi = sin(lp.phi);
67
0
    cosphi = cos(lp.phi);
68
0
    for (i = 0; i < 3; ++i) { /* dist/azimiths from control */
69
0
        v[i] = vect(P->ctx, lp.phi - Q->c[i].phi, Q->c[i].cosphi,
70
0
                    Q->c[i].sinphi, cosphi, sinphi, lp.lam - Q->c[i].lam);
71
0
        if (v[i].r == 0.0)
72
0
            break;
73
0
        v[i].Az = adjlon(v[i].Az - Q->c[i].v.Az);
74
0
    }
75
0
    if (i < 3) /* current point at control point */
76
0
        xy = Q->c[i].p;
77
0
    else { /* point mean of intercepts */
78
0
        xy = Q->p;
79
0
        for (i = 0; i < 3; ++i) {
80
0
            j = i == 2 ? 0 : i + 1;
81
0
            a = lc(P->ctx, Q->c[i].v.r, v[i].r, v[j].r);
82
0
            if (v[i].Az < 0.)
83
0
                a = -a;
84
0
            if (!i) { /* coord comp unique to each arc */
85
0
                xy.x += v[i].r * cos(a);
86
0
                xy.y -= v[i].r * sin(a);
87
0
            } else if (i == 1) {
88
0
                a = Q->beta_1 - a;
89
0
                xy.x -= v[i].r * cos(a);
90
0
                xy.y -= v[i].r * sin(a);
91
0
            } else {
92
0
                a = Q->beta_2 - a;
93
0
                xy.x += v[i].r * cos(a);
94
0
                xy.y += v[i].r * sin(a);
95
0
            }
96
0
        }
97
0
        xy.x *= THIRD; /* mean of arc intercepts */
98
0
        xy.y *= THIRD;
99
0
    }
100
0
    return xy;
101
0
}
102
103
19
PJ *PJ_PROJECTION(chamb) {
104
19
    int i, j;
105
19
    char line[10];
106
19
    struct pj_chamb *Q =
107
19
        static_cast<struct pj_chamb *>(calloc(1, sizeof(struct pj_chamb)));
108
19
    if (nullptr == Q)
109
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
110
19
    P->opaque = Q;
111
112
76
    for (i = 0; i < 3; ++i) { /* get control point locations */
113
57
        (void)snprintf(line, sizeof(line), "rlat_%d", i + 1);
114
57
        Q->c[i].phi = pj_param(P->ctx, P->params, line).f;
115
57
        (void)snprintf(line, sizeof(line), "rlon_%d", i + 1);
116
57
        Q->c[i].lam = pj_param(P->ctx, P->params, line).f;
117
57
        Q->c[i].lam = adjlon(Q->c[i].lam - P->lam0);
118
57
        Q->c[i].cosphi = cos(Q->c[i].phi);
119
57
        Q->c[i].sinphi = sin(Q->c[i].phi);
120
57
    }
121
70
    for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */
122
54
        j = i == 2 ? 0 : i + 1;
123
54
        Q->c[i].v = vect(P->ctx, Q->c[j].phi - Q->c[i].phi, Q->c[i].cosphi,
124
54
                         Q->c[i].sinphi, Q->c[j].cosphi, Q->c[j].sinphi,
125
54
                         Q->c[j].lam - Q->c[i].lam);
126
54
        if (Q->c[i].v.r == 0.0) {
127
3
            proj_log_error(
128
3
                P,
129
3
                _("Invalid value for control points: they should be distinct"));
130
3
            return pj_default_destructor(P,
131
3
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
132
3
        }
133
        /* co-linearity problem ignored for now */
134
54
    }
135
16
    Q->beta_0 = lc(P->ctx, Q->c[0].v.r, Q->c[2].v.r, Q->c[1].v.r);
136
16
    Q->beta_1 = lc(P->ctx, Q->c[0].v.r, Q->c[1].v.r, Q->c[2].v.r);
137
16
    Q->beta_2 = M_PI - Q->beta_0;
138
16
    Q->c[0].p.y = Q->c[2].v.r * sin(Q->beta_0);
139
16
    Q->c[1].p.y = Q->c[0].p.y;
140
16
    Q->p.y = 2. * Q->c[0].p.y;
141
16
    Q->c[2].p.y = 0.;
142
16
    Q->c[1].p.x = 0.5 * Q->c[0].v.r;
143
16
    Q->c[0].p.x = -Q->c[1].p.x;
144
16
    Q->c[2].p.x = Q->c[0].p.x + Q->c[2].v.r * cos(Q->beta_0);
145
16
    Q->p.x = Q->c[2].p.x;
146
147
16
    P->es = 0.;
148
16
    P->fwd = chamb_s_forward;
149
150
16
    return P;
151
19
}
152
153
#undef THIRD
154
#undef TOL