Coverage Report

Created: 2025-06-13 06:29

/src/proj/src/coord_operation.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  PROJ
3
 * Purpose:  PJCoordOperation methods
4
 *
5
 * Author:   Even Rouault, <even.rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018-2024, Even Rouault, <even.rouault at spatialys.com>
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 *****************************************************************************/
28
29
#define FROM_PROJ_CPP
30
31
#include "proj.h"
32
#include "proj_internal.h"
33
#include <math.h>
34
35
#include "proj/internal/internal.hpp"
36
37
using namespace NS_PROJ::internal;
38
39
//! @cond Doxygen_Suppress
40
/**************************************************************************************/
41
0
PJCoordOperation::~PJCoordOperation() {
42
    /**************************************************************************************/
43
0
    proj_destroy(pj);
44
0
    proj_destroy(pjSrcGeocentricToLonLat);
45
0
    proj_destroy(pjDstGeocentricToLonLat);
46
0
}
47
48
/**************************************************************************************/
49
0
bool PJCoordOperation::isInstantiable() const {
50
    /**************************************************************************************/
51
0
    if (isInstantiableCached == INSTANTIABLE_STATUS_UNKNOWN)
52
0
        isInstantiableCached = proj_coordoperation_is_instantiable(pj->ctx, pj);
53
0
    return (isInstantiableCached == 1);
54
0
}
55
56
// Returns true if the passed operation uses NADCON5 grids for NAD83 to
57
// NAD83(HARN)
58
0
static bool isSpecialCaseForNAD83_to_NAD83HARN(const std::string &opName) {
59
0
    return opName.find("NAD83 to NAD83(HARN) (47)") != std::string::npos ||
60
0
           opName.find("NAD83 to NAD83(HARN) (48)") != std::string::npos ||
61
0
           opName.find("NAD83 to NAD83(HARN) (49)") != std::string::npos ||
62
0
           opName.find("NAD83 to NAD83(HARN) (50)") != std::string::npos;
63
0
}
64
65
// Returns true if the passed operation uses "GDA94 to WGS 84 (1)", which
66
// is the null transformation
67
0
static bool isSpecialCaseForGDA94_to_WGS84(const std::string &opName) {
68
0
    return opName.find("GDA94 to WGS 84 (1)") != std::string::npos;
69
0
}
70
71
// Returns true if the passed operation uses "GDA2020 to WGS 84 (2)", which
72
// is the null transformation
73
0
static bool isSpecialCaseForWGS84_to_GDA2020(const std::string &opName) {
74
0
    return opName.find("GDA2020 to WGS 84 (2)") != std::string::npos;
75
0
}
76
77
PJCoordOperation::PJCoordOperation(
78
    int idxInOriginalListIn, double minxSrcIn, double minySrcIn,
79
    double maxxSrcIn, double maxySrcIn, double minxDstIn, double minyDstIn,
80
    double maxxDstIn, double maxyDstIn, PJ *pjIn, const std::string &nameIn,
81
    double accuracyIn, double pseudoAreaIn, const char *areaNameIn,
82
    const PJ *pjSrcGeocentricToLonLatIn, const PJ *pjDstGeocentricToLonLatIn)
83
0
    : idxInOriginalList(idxInOriginalListIn), minxSrc(minxSrcIn),
84
0
      minySrc(minySrcIn), maxxSrc(maxxSrcIn), maxySrc(maxySrcIn),
85
0
      minxDst(minxDstIn), minyDst(minyDstIn), maxxDst(maxxDstIn),
86
0
      maxyDst(maxyDstIn), pj(pjIn), name(nameIn), accuracy(accuracyIn),
87
0
      pseudoArea(pseudoAreaIn), areaName(areaNameIn ? areaNameIn : ""),
88
0
      isOffshore(areaName.find("- offshore") != std::string::npos),
89
0
      isUnknownAreaName(areaName.empty() || areaName == "unknown"),
90
0
      isPriorityOp(isSpecialCaseForNAD83_to_NAD83HARN(name) ||
91
0
                   isSpecialCaseForGDA94_to_WGS84(name) ||
92
0
                   isSpecialCaseForWGS84_to_GDA2020(name)),
93
0
      pjSrcGeocentricToLonLat(pjSrcGeocentricToLonLatIn
94
0
                                  ? proj_clone(pjSrcGeocentricToLonLatIn->ctx,
95
0
                                               pjSrcGeocentricToLonLatIn)
96
0
                                  : nullptr),
97
0
      pjDstGeocentricToLonLat(pjDstGeocentricToLonLatIn
98
0
                                  ? proj_clone(pjDstGeocentricToLonLatIn->ctx,
99
0
                                               pjDstGeocentricToLonLatIn)
100
0
                                  : nullptr) {
101
0
    const auto IsLonLatOrLatLon = [](const PJ *crs, bool &isLonLatDegreeOut,
102
0
                                     bool &isLatLonDegreeOut) {
103
0
        const auto eType = proj_get_type(crs);
104
0
        if (eType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
105
0
            eType == PJ_TYPE_GEOGRAPHIC_3D_CRS) {
106
0
            const auto cs = proj_crs_get_coordinate_system(crs->ctx, crs);
107
0
            const char *direction = "";
108
0
            double conv_factor = 0;
109
0
            constexpr double EPS = 1e-14;
110
0
            if (proj_cs_get_axis_info(crs->ctx, cs, 0, nullptr, nullptr,
111
0
                                      &direction, &conv_factor, nullptr,
112
0
                                      nullptr, nullptr) &&
113
0
                ci_equal(direction, "East")) {
114
0
                isLonLatDegreeOut = fabs(conv_factor - M_PI / 180) < EPS;
115
0
            } else if (proj_cs_get_axis_info(crs->ctx, cs, 1, nullptr, nullptr,
116
0
                                             &direction, &conv_factor, nullptr,
117
0
                                             nullptr, nullptr) &&
118
0
                       ci_equal(direction, "East")) {
119
0
                isLatLonDegreeOut = fabs(conv_factor - M_PI / 180) < EPS;
120
0
            }
121
0
            proj_destroy(cs);
122
0
        }
123
0
    };
124
125
0
    const auto source = proj_get_source_crs(pj->ctx, pj);
126
0
    if (source) {
127
0
        IsLonLatOrLatLon(source, srcIsLonLatDegree, srcIsLatLonDegree);
128
0
        proj_destroy(source);
129
0
    }
130
131
0
    const auto target = proj_get_target_crs(pj->ctx, pj);
132
0
    if (target) {
133
0
        IsLonLatOrLatLon(target, dstIsLonLatDegree, dstIsLatLonDegree);
134
0
        proj_destroy(target);
135
0
    }
136
0
}
137
138
//! @endcond