Coverage Report

Created: 2026-08-31 06:51

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
92.9k
PROJBasedOperation::~PROJBasedOperation() = default;
75
76
// ---------------------------------------------------------------------------
77
78
PROJBasedOperation::PROJBasedOperation(const OperationMethodNNPtr &methodIn)
79
85.4k
    : 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
85.4k
    : 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
48.6k
    const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
87
48.6k
    auto method = OperationMethod::create(
88
48.6k
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
89
48.6k
                                "PROJ-based operation method: " + PROJString),
90
48.6k
        std::vector<GeneralOperationParameterNNPtr>{});
91
48.6k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
92
48.6k
    op->assignSelf(op);
93
48.6k
    op->projString_ = PROJString;
94
48.6k
    if (sourceCRS && targetCRS) {
95
46.9k
        op->setCRSs(NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), nullptr);
96
46.9k
    }
97
48.6k
    op->setProperties(
98
48.6k
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
99
48.6k
    op->setAccuracies(accuracies);
100
101
48.6k
    auto formatter = io::PROJStringFormatter::create();
102
48.6k
    try {
103
48.6k
        formatter->ingestPROJString(PROJString);
104
48.6k
        op->setRequiresPerCoordinateInputTime(
105
48.6k
            formatter->requiresPerCoordinateInputTime());
106
48.6k
    } catch (const io::ParsingException &e) {
107
14
        throw util::UnsupportedOperationException(
108
14
            std::string("PROJBasedOperation::create() failed: ") + e.what());
109
14
    }
110
111
48.5k
    return op;
112
48.6k
}
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
36.8k
    bool hasBallparkTransformation) {
123
124
36.8k
    auto formatter = io::PROJStringFormatter::create();
125
36.8k
    if (inverse) {
126
13.9k
        formatter->startInversion();
127
13.9k
    }
128
36.8k
    projExportable->_exportToPROJString(formatter.get());
129
36.8k
    if (inverse) {
130
13.9k
        formatter->stopInversion();
131
13.9k
    }
132
36.8k
    const auto &projString = formatter->toString();
133
134
36.8k
    auto method = OperationMethod::create(
135
36.8k
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
136
36.8k
                                "PROJ-based operation method (approximate): " +
137
36.8k
                                    projString),
138
36.8k
        std::vector<GeneralOperationParameterNNPtr>{});
139
36.8k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
140
36.8k
    op->assignSelf(op);
141
36.8k
    op->projString_ = projString;
142
36.8k
    op->setCRSs(sourceCRS, targetCRS, interpolationCRS);
143
36.8k
    op->setProperties(
144
36.8k
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
145
36.8k
    op->setAccuracies(accuracies);
146
36.8k
    op->projStringExportable_ = projExportable.as_nullable();
147
36.8k
    op->inverse_ = inverse;
148
36.8k
    op->setHasBallparkTransformation(hasBallparkTransformation);
149
36.8k
    op->setRequiresPerCoordinateInputTime(
150
36.8k
        formatter->requiresPerCoordinateInputTime());
151
152
36.8k
    return op;
153
36.8k
}
154
155
// ---------------------------------------------------------------------------
156
157
58.9k
CoordinateOperationNNPtr PROJBasedOperation::inverse() const {
158
159
58.9k
    if (projStringExportable_ && sourceCRS() && targetCRS()) {
160
26.5k
        return util::nn_static_pointer_cast<CoordinateOperation>(
161
26.5k
            PROJBasedOperation::create(
162
26.5k
                createPropertiesForInverse(this, false, false),
163
26.5k
                NN_NO_CHECK(projStringExportable_), !inverse_,
164
26.5k
                NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
165
26.5k
                interpolationCRS(), coordinateOperationAccuracies(),
166
26.5k
                hasBallparkTransformation()));
167
26.5k
    }
168
169
32.3k
    auto formatter = io::PROJStringFormatter::create();
170
32.3k
    formatter->startInversion();
171
32.3k
    try {
172
32.3k
        formatter->ingestPROJString(projString_);
173
32.3k
    } catch (const io::ParsingException &e) {
174
0
        throw util::UnsupportedOperationException(
175
0
            std::string("PROJBasedOperation::inverse() failed: ") + e.what());
176
0
    }
177
32.3k
    formatter->stopInversion();
178
179
32.3k
    auto op = PROJBasedOperation::create(
180
32.3k
        createPropertiesForInverse(this, false, false), formatter->toString(),
181
32.3k
        targetCRS(), sourceCRS(), coordinateOperationAccuracies());
182
32.3k
    if (sourceCRS() && targetCRS()) {
183
32.3k
        op->setCRSs(NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
184
32.3k
                    interpolationCRS());
185
32.3k
    }
186
32.3k
    op->setHasBallparkTransformation(hasBallparkTransformation());
187
32.3k
    op->setRequiresPerCoordinateInputTime(
188
32.3k
        formatter->requiresPerCoordinateInputTime());
189
32.3k
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
190
32.3k
}
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
121k
    io::PROJStringFormatter *formatter) const {
266
121k
    if (projStringExportable_) {
267
60.3k
        if (inverse_) {
268
32.3k
            formatter->startInversion();
269
32.3k
        }
270
60.3k
        projStringExportable_->_exportToPROJString(formatter);
271
60.3k
        if (inverse_) {
272
32.3k
            formatter->stopInversion();
273
32.3k
        }
274
60.3k
        return;
275
60.3k
    }
276
277
60.6k
    try {
278
60.6k
        formatter->ingestPROJString(projString_);
279
60.6k
    } catch (const io::ParsingException &e) {
280
0
        throw io::FormattingException(
281
0
            std::string("PROJBasedOperation::exportToPROJString() failed: ") +
282
0
            e.what());
283
0
    }
284
60.6k
}
285
286
// ---------------------------------------------------------------------------
287
288
7.54k
CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const {
289
7.54k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(*this);
290
7.54k
    op->assignSelf(op);
291
7.54k
    op->setCRSs(this, false);
292
7.54k
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
293
7.54k
}
294
295
// ---------------------------------------------------------------------------
296
297
std::set<GridDescription>
298
PROJBasedOperation::gridsNeeded(const io::DatabaseContextPtr &databaseContext,
299
29.6k
                                bool considerKnownGridsAsAvailable) const {
300
29.6k
    std::set<GridDescription> res;
301
302
29.6k
    try {
303
29.6k
        auto formatterOut = io::PROJStringFormatter::create();
304
29.6k
        auto formatter = io::PROJStringFormatter::create();
305
29.6k
        formatter->ingestPROJString(exportToPROJString(formatterOut.get()));
306
29.6k
        const auto usedGridNames = formatter->getUsedGridNames();
307
45.7k
        for (const auto &shortName : usedGridNames) {
308
45.7k
            GridDescription desc;
309
45.7k
            desc.shortName = shortName;
310
45.7k
            if (databaseContext) {
311
45.5k
                databaseContext->lookForGridInfo(
312
45.5k
                    desc.shortName, considerKnownGridsAsAvailable,
313
45.5k
                    desc.fullName, desc.packageName, desc.url,
314
45.5k
                    desc.directDownload, desc.openLicense, desc.available);
315
45.5k
            }
316
45.7k
            res.insert(std::move(desc));
317
45.7k
        }
318
29.6k
    } catch (const io::ParsingException &) {
319
2
    }
320
321
29.6k
    return res;
322
29.6k
}
323
324
//! @endcond
325
326
// ---------------------------------------------------------------------------
327
328
} // namespace operation
329
330
NS_PROJ_END