Coverage Report

Created: 2026-04-09 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/iso19111/operation/projbasedoperation.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  PROJ
4
 * Purpose:  ISO19111:2019 implementation
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot 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
#ifndef FROM_PROJ_CPP
30
#define FROM_PROJ_CPP
31
#endif
32
33
#include "proj/common.hpp"
34
#include "proj/coordinateoperation.hpp"
35
#include "proj/crs.hpp"
36
#include "proj/io.hpp"
37
#include "proj/metadata.hpp"
38
#include "proj/util.hpp"
39
40
#include "proj/internal/internal.hpp"
41
#include "proj/internal/io_internal.hpp"
42
43
#include "coordinateoperation_internal.hpp"
44
#include "oputils.hpp"
45
46
// PROJ include order is sensitive
47
// clang-format off
48
#include "proj.h"
49
#include "proj_internal.h" // M_PI
50
// clang-format on
51
#include "proj_constants.h"
52
#include "proj_json_streaming_writer.hpp"
53
54
#include <algorithm>
55
#include <cassert>
56
#include <cmath>
57
#include <cstring>
58
#include <memory>
59
#include <set>
60
#include <string>
61
#include <vector>
62
63
using namespace NS_PROJ::internal;
64
65
// ---------------------------------------------------------------------------
66
67
NS_PROJ_START
68
namespace operation {
69
70
//! @cond Doxygen_Suppress
71
72
// ---------------------------------------------------------------------------
73
74
116k
PROJBasedOperation::~PROJBasedOperation() = default;
75
76
// ---------------------------------------------------------------------------
77
78
PROJBasedOperation::PROJBasedOperation(const OperationMethodNNPtr &methodIn)
79
107k
    : SingleOperation(methodIn) {}
Unexecuted instantiation: osgeo::proj::operation::PROJBasedOperation::PROJBasedOperation(dropbox::oxygen::nn<std::__1::shared_ptr<osgeo::proj::operation::OperationMethod> > const&)
osgeo::proj::operation::PROJBasedOperation::PROJBasedOperation(dropbox::oxygen::nn<std::__1::shared_ptr<osgeo::proj::operation::OperationMethod> > const&)
Line
Count
Source
79
107k
    : SingleOperation(methodIn) {}
80
81
// ---------------------------------------------------------------------------
82
83
PROJBasedOperationNNPtr PROJBasedOperation::create(
84
    const util::PropertyMap &properties, const std::string &PROJString,
85
    const crs::CRSPtr &sourceCRS, const crs::CRSPtr &targetCRS,
86
69.5k
    const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
87
69.5k
    auto method = OperationMethod::create(
88
69.5k
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
89
69.5k
                                "PROJ-based operation method: " + PROJString),
90
69.5k
        std::vector<GeneralOperationParameterNNPtr>{});
91
69.5k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
92
69.5k
    op->assignSelf(op);
93
69.5k
    op->projString_ = PROJString;
94
69.5k
    if (sourceCRS && targetCRS) {
95
67.7k
        op->setCRSs(NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), nullptr);
96
67.7k
    }
97
69.5k
    op->setProperties(
98
69.5k
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
99
69.5k
    op->setAccuracies(accuracies);
100
101
69.5k
    auto formatter = io::PROJStringFormatter::create();
102
69.5k
    try {
103
69.5k
        formatter->ingestPROJString(PROJString);
104
69.5k
        op->setRequiresPerCoordinateInputTime(
105
69.5k
            formatter->requiresPerCoordinateInputTime());
106
69.5k
    } catch (const io::ParsingException &e) {
107
14
        throw util::UnsupportedOperationException(
108
14
            std::string("PROJBasedOperation::create() failed: ") + e.what());
109
14
    }
110
111
69.5k
    return op;
112
69.5k
}
113
114
// ---------------------------------------------------------------------------
115
116
PROJBasedOperationNNPtr PROJBasedOperation::create(
117
    const util::PropertyMap &properties,
118
    const io::IPROJStringExportableNNPtr &projExportable, bool inverse,
119
    const crs::CRSNNPtr &sourceCRS, const crs::CRSNNPtr &targetCRS,
120
    const crs::CRSPtr &interpolationCRS,
121
    const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies,
122
37.7k
    bool hasBallparkTransformation) {
123
124
37.7k
    auto formatter = io::PROJStringFormatter::create();
125
37.7k
    if (inverse) {
126
17.7k
        formatter->startInversion();
127
17.7k
    }
128
37.7k
    projExportable->_exportToPROJString(formatter.get());
129
37.7k
    if (inverse) {
130
17.7k
        formatter->stopInversion();
131
17.7k
    }
132
37.7k
    const auto &projString = formatter->toString();
133
134
37.7k
    auto method = OperationMethod::create(
135
37.7k
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
136
37.7k
                                "PROJ-based operation method (approximate): " +
137
37.7k
                                    projString),
138
37.7k
        std::vector<GeneralOperationParameterNNPtr>{});
139
37.7k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
140
37.7k
    op->assignSelf(op);
141
37.7k
    op->projString_ = projString;
142
37.7k
    op->setCRSs(sourceCRS, targetCRS, interpolationCRS);
143
37.7k
    op->setProperties(
144
37.7k
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
145
37.7k
    op->setAccuracies(accuracies);
146
37.7k
    op->projStringExportable_ = projExportable.as_nullable();
147
37.7k
    op->inverse_ = inverse;
148
37.7k
    op->setHasBallparkTransformation(hasBallparkTransformation);
149
37.7k
    op->setRequiresPerCoordinateInputTime(
150
37.7k
        formatter->requiresPerCoordinateInputTime());
151
152
37.7k
    return op;
153
37.7k
}
154
155
// ---------------------------------------------------------------------------
156
157
70.2k
CoordinateOperationNNPtr PROJBasedOperation::inverse() const {
158
159
70.2k
    if (projStringExportable_ && sourceCRS() && targetCRS()) {
160
27.4k
        return util::nn_static_pointer_cast<CoordinateOperation>(
161
27.4k
            PROJBasedOperation::create(
162
27.4k
                createPropertiesForInverse(this, false, false),
163
27.4k
                NN_NO_CHECK(projStringExportable_), !inverse_,
164
27.4k
                NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
165
27.4k
                interpolationCRS(), coordinateOperationAccuracies(),
166
27.4k
                hasBallparkTransformation()));
167
27.4k
    }
168
169
42.7k
    auto formatter = io::PROJStringFormatter::create();
170
42.7k
    formatter->startInversion();
171
42.7k
    try {
172
42.7k
        formatter->ingestPROJString(projString_);
173
42.7k
    } catch (const io::ParsingException &e) {
174
0
        throw util::UnsupportedOperationException(
175
0
            std::string("PROJBasedOperation::inverse() failed: ") + e.what());
176
0
    }
177
42.7k
    formatter->stopInversion();
178
179
42.7k
    auto op = PROJBasedOperation::create(
180
42.7k
        createPropertiesForInverse(this, false, false), formatter->toString(),
181
42.7k
        targetCRS(), sourceCRS(), coordinateOperationAccuracies());
182
42.7k
    if (sourceCRS() && targetCRS()) {
183
42.7k
        op->setCRSs(NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
184
42.7k
                    interpolationCRS());
185
42.7k
    }
186
42.7k
    op->setHasBallparkTransformation(hasBallparkTransformation());
187
42.7k
    op->setRequiresPerCoordinateInputTime(
188
42.7k
        formatter->requiresPerCoordinateInputTime());
189
42.7k
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
190
42.7k
}
191
192
// ---------------------------------------------------------------------------
193
194
0
void PROJBasedOperation::_exportToWKT(io::WKTFormatter *formatter) const {
195
196
0
    if (sourceCRS() && targetCRS()) {
197
0
        exportTransformationToWKT(formatter);
198
0
        return;
199
0
    }
200
201
0
    const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
202
0
    if (!isWKT2) {
203
0
        throw io::FormattingException(
204
0
            "PROJBasedOperation can only be exported to WKT2");
205
0
    }
206
207
0
    formatter->startNode(io::WKTConstants::CONVERSION, false);
208
0
    formatter->addQuotedString(nameStr());
209
0
    method()->_exportToWKT(formatter);
210
211
0
    for (const auto &paramValue : parameterValues()) {
212
0
        paramValue->_exportToWKT(formatter);
213
0
    }
214
0
    formatter->endNode();
215
0
}
216
217
// ---------------------------------------------------------------------------
218
219
void PROJBasedOperation::_exportToJSON(
220
    io::JSONFormatter *formatter) const // throw(FormattingException)
221
0
{
222
0
    auto writer = formatter->writer();
223
0
    auto objectContext(formatter->MakeObjectContext(
224
0
        (sourceCRS() && targetCRS()) ? "Transformation" : "Conversion",
225
0
        !identifiers().empty()));
226
227
0
    writer->AddObjKey("name");
228
0
    const auto &l_name = nameStr();
229
0
    if (l_name.empty()) {
230
0
        writer->Add("unnamed");
231
0
    } else {
232
0
        writer->Add(l_name);
233
0
    }
234
235
0
    if (sourceCRS() && targetCRS()) {
236
0
        writer->AddObjKey("source_crs");
237
0
        formatter->setAllowIDInImmediateChild();
238
0
        sourceCRS()->_exportToJSON(formatter);
239
240
0
        writer->AddObjKey("target_crs");
241
0
        formatter->setAllowIDInImmediateChild();
242
0
        targetCRS()->_exportToJSON(formatter);
243
0
    }
244
245
0
    writer->AddObjKey("method");
246
0
    formatter->setOmitTypeInImmediateChild();
247
0
    formatter->setAllowIDInImmediateChild();
248
0
    method()->_exportToJSON(formatter);
249
250
0
    const auto &l_parameterValues = parameterValues();
251
0
    writer->AddObjKey("parameters");
252
0
    {
253
0
        auto parametersContext(writer->MakeArrayContext(false));
254
0
        for (const auto &genOpParamvalue : l_parameterValues) {
255
0
            formatter->setAllowIDInImmediateChild();
256
0
            formatter->setOmitTypeInImmediateChild();
257
0
            genOpParamvalue->_exportToJSON(formatter);
258
0
        }
259
0
    }
260
0
}
261
262
// ---------------------------------------------------------------------------
263
264
void PROJBasedOperation::_exportToPROJString(
265
126k
    io::PROJStringFormatter *formatter) const {
266
126k
    if (projStringExportable_) {
267
57.0k
        if (inverse_) {
268
33.3k
            formatter->startInversion();
269
33.3k
        }
270
57.0k
        projStringExportable_->_exportToPROJString(formatter);
271
57.0k
        if (inverse_) {
272
33.3k
            formatter->stopInversion();
273
33.3k
        }
274
57.0k
        return;
275
57.0k
    }
276
277
69.9k
    try {
278
69.9k
        formatter->ingestPROJString(projString_);
279
69.9k
    } catch (const io::ParsingException &e) {
280
0
        throw io::FormattingException(
281
0
            std::string("PROJBasedOperation::exportToPROJString() failed: ") +
282
0
            e.what());
283
0
    }
284
69.9k
}
285
286
// ---------------------------------------------------------------------------
287
288
9.57k
CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const {
289
9.57k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(*this);
290
9.57k
    op->assignSelf(op);
291
9.57k
    op->setCRSs(this, false);
292
9.57k
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
293
9.57k
}
294
295
// ---------------------------------------------------------------------------
296
297
std::set<GridDescription>
298
PROJBasedOperation::gridsNeeded(const io::DatabaseContextPtr &databaseContext,
299
31.2k
                                bool considerKnownGridsAsAvailable) const {
300
31.2k
    std::set<GridDescription> res;
301
302
31.2k
    try {
303
31.2k
        auto formatterOut = io::PROJStringFormatter::create();
304
31.2k
        auto formatter = io::PROJStringFormatter::create();
305
31.2k
        formatter->ingestPROJString(exportToPROJString(formatterOut.get()));
306
31.2k
        const auto usedGridNames = formatter->getUsedGridNames();
307
48.7k
        for (const auto &shortName : usedGridNames) {
308
48.7k
            GridDescription desc;
309
48.7k
            desc.shortName = shortName;
310
48.7k
            if (databaseContext) {
311
48.0k
                databaseContext->lookForGridInfo(
312
48.0k
                    desc.shortName, considerKnownGridsAsAvailable,
313
48.0k
                    desc.fullName, desc.packageName, desc.url,
314
48.0k
                    desc.directDownload, desc.openLicense, desc.available);
315
48.0k
            }
316
48.7k
            res.insert(std::move(desc));
317
48.7k
        }
318
31.2k
    } catch (const io::ParsingException &) {
319
6
    }
320
321
31.2k
    return res;
322
31.2k
}
323
324
//! @endcond
325
326
// ---------------------------------------------------------------------------
327
328
} // namespace operation
329
330
NS_PROJ_END