Coverage Report

Created: 2026-02-03 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/projections/calcofi.cpp
Line
Count
Source
1
2
3
#include <math.h>
4
5
#include "proj.h"
6
#include "proj_internal.h"
7
8
PROJ_HEAD(calcofi, "Cal Coop Ocean Fish Invest Lines/Stations")
9
"\n\tCyl, Sph&Ell";
10
11
/* Conversions for the California Cooperative Oceanic Fisheries Investigations
12
Line/Station coordinate system following the algorithm of:
13
Eber, L.E., and  R.P. Hewitt. 1979. Conversion algorithms for the CalCOFI
14
station grid. California Cooperative Oceanic Fisheries Investigations Reports
15
20:135-137. (corrected for typographical errors).
16
http://www.calcofi.org/publications/calcofireports/v20/Vol_20_Eber___Hewitt.pdf
17
They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or
18
meridional units at reference point O, and similarly 1 unit of CalCOFI
19
Station == 1/15 of a degree at O.
20
By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use
21
whatever ellipsoid is provided. */
22
23
7.56k
#define EPS10 1.e-10
24
7.56k
#define DEG_TO_LINE 5
25
7.56k
#define DEG_TO_STATION 15
26
0
#define LINE_TO_RAD 0.0034906585039886592
27
0
#define STATION_TO_RAD 0.0011635528346628863
28
7.56k
#define PT_O_LINE 80                    /* reference point O is at line 80,  */
29
7.56k
#define PT_O_STATION 60                 /* station 60,  */
30
7.56k
#define PT_O_LAMBDA -2.1144663887911301 /* long -121.15 and */
31
22.6k
#define PT_O_PHI 0.59602993955606354    /* lat 34.15 */
32
37.8k
#define ROTATION_ANGLE 0.52359877559829882 /*CalCOFI angle of 30 deg in rad */
33
34
7.56k
static PJ_XY calcofi_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */
35
7.56k
    PJ_XY xy = {0.0, 0.0};
36
7.56k
    double oy; /* pt O y value in Mercator */
37
7.56k
    double l1; /* l1 and l2 are distances calculated using trig that sum
38
               to the east/west distance between point O and point xy */
39
7.56k
    double l2;
40
7.56k
    double ry; /* r is the point on the same station as o (60) and the same
41
               line as xy xy, r, o form a right triangle */
42
43
7.56k
    if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
44
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
45
0
        return xy;
46
0
    }
47
48
7.56k
    xy.x = lp.lam;
49
7.56k
    xy.y = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); /* Mercator transform xy*/
50
7.56k
    oy = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e));
51
7.56k
    l1 = (xy.y - oy) * tan(ROTATION_ANGLE);
52
7.56k
    l2 = -xy.x - l1 + PT_O_LAMBDA;
53
7.56k
    ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y;
54
7.56k
    ry = pj_phi2(P->ctx, exp(-ry), P->e); /*inverse Mercator*/
55
7.56k
    xy.x = PT_O_LINE -
56
7.56k
           RAD_TO_DEG * (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE);
57
7.56k
    xy.y = PT_O_STATION +
58
7.56k
           RAD_TO_DEG * (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE);
59
    /* set a = 1, x0 = 0, and y0 = 0 so that no further unit adjustments
60
    are done */
61
62
7.56k
    return xy;
63
7.56k
}
64
65
0
static PJ_XY calcofi_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
66
0
    PJ_XY xy = {0.0, 0.0};
67
0
    double oy;
68
0
    double l1;
69
0
    double l2;
70
0
    double ry;
71
0
    if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
72
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
73
0
        return xy;
74
0
    }
75
0
    xy.x = lp.lam;
76
0
    xy.y = log(tan(M_FORTPI + .5 * lp.phi));
77
0
    oy = log(tan(M_FORTPI + .5 * PT_O_PHI));
78
0
    l1 = (xy.y - oy) * tan(ROTATION_ANGLE);
79
0
    l2 = -xy.x - l1 + PT_O_LAMBDA;
80
0
    ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y;
81
0
    ry = M_HALFPI - 2. * atan(exp(-ry));
82
0
    xy.x = PT_O_LINE -
83
0
           RAD_TO_DEG * (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE);
84
0
    xy.y = PT_O_STATION +
85
0
           RAD_TO_DEG * (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE);
86
87
0
    return xy;
88
0
}
89
90
0
static PJ_LP calcofi_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */
91
0
    PJ_LP lp = {0.0, 0.0};
92
0
    double ry;     /* y value of point r */
93
0
    double oymctr; /* Mercator-transformed y value of point O */
94
0
    double rymctr; /* Mercator-transformed ry */
95
0
    double xymctr; /* Mercator-transformed xy.y */
96
0
    double l1;
97
0
    double l2;
98
99
0
    ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * cos(ROTATION_ANGLE);
100
0
    lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE);
101
0
    oymctr = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e));
102
0
    rymctr = -log(pj_tsfn(ry, sin(ry), P->e));
103
0
    xymctr = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e));
104
0
    l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE);
105
0
    l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE));
106
0
    lp.lam = PT_O_LAMBDA - (l1 + l2);
107
108
0
    return lp;
109
0
}
110
111
0
static PJ_LP calcofi_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
112
0
    PJ_LP lp = {0.0, 0.0};
113
0
    double ry;
114
0
    double oymctr;
115
0
    double rymctr;
116
0
    double xymctr;
117
0
    double l1;
118
0
    double l2;
119
0
    (void)P;
120
121
0
    ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * cos(ROTATION_ANGLE);
122
0
    lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE);
123
0
    oymctr = log(tan(M_FORTPI + .5 * PT_O_PHI));
124
0
    rymctr = log(tan(M_FORTPI + .5 * ry));
125
0
    xymctr = log(tan(M_FORTPI + .5 * lp.phi));
126
0
    l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE);
127
0
    l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE));
128
0
    lp.lam = PT_O_LAMBDA - (l1 + l2);
129
130
0
    return lp;
131
0
}
132
133
117
PJ *PJ_PROJECTION(calcofi) {
134
117
    P->opaque = nullptr;
135
136
    /* if the user has specified +lon_0 or +k0 for some reason,
137
    we're going to ignore it so that xy is consistent with point O */
138
117
    P->lam0 = 0;
139
117
    P->ra = 1;
140
117
    P->a = 1;
141
117
    P->x0 = 0;
142
117
    P->y0 = 0;
143
117
    P->over = 1;
144
145
117
    if (P->es != 0.0) { /* ellipsoid */
146
112
        P->inv = calcofi_e_inverse;
147
112
        P->fwd = calcofi_e_forward;
148
112
    } else { /* sphere */
149
5
        P->inv = calcofi_s_inverse;
150
5
        P->fwd = calcofi_s_forward;
151
5
    }
152
117
    return P;
153
117
}