Coverage Report

Created: 2026-08-13 07:07

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