Coverage Report

Created: 2025-06-22 06:59

/src/proj/src/iso19111/operation/projbasedoperation.cpp
Line
Count
Source (jump to first uncovered line)
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
10.3k
PROJBasedOperation::~PROJBasedOperation() = default;
75
76
// ---------------------------------------------------------------------------
77
78
PROJBasedOperation::PROJBasedOperation(const OperationMethodNNPtr &methodIn)
79
10.3k
    : 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
10.3k
    : 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
10.3k
    const std::vector<metadata::PositionalAccuracyNNPtr> &accuracies) {
87
10.3k
    auto method = OperationMethod::create(
88
10.3k
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
89
10.3k
                                "PROJ-based operation method: " + PROJString),
90
10.3k
        std::vector<GeneralOperationParameterNNPtr>{});
91
10.3k
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
92
10.3k
    op->assignSelf(op);
93
10.3k
    op->projString_ = PROJString;
94
10.3k
    if (sourceCRS && targetCRS) {
95
20
        op->setCRSs(NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS), nullptr);
96
20
    }
97
10.3k
    op->setProperties(
98
10.3k
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
99
10.3k
    op->setAccuracies(accuracies);
100
101
10.3k
    auto formatter = io::PROJStringFormatter::create();
102
10.3k
    try {
103
10.3k
        formatter->ingestPROJString(PROJString);
104
10.3k
        op->setRequiresPerCoordinateInputTime(
105
10.3k
            formatter->requiresPerCoordinateInputTime());
106
10.3k
    } catch (const io::ParsingException &e) {
107
0
        throw util::UnsupportedOperationException(
108
0
            std::string("PROJBasedOperation::create() failed: ") + e.what());
109
0
    }
110
111
10.3k
    return op;
112
10.3k
}
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
0
    bool hasBallparkTransformation) {
123
124
0
    auto formatter = io::PROJStringFormatter::create();
125
0
    if (inverse) {
126
0
        formatter->startInversion();
127
0
    }
128
0
    const bool bRequiresPerCoordinateInputTime =
129
0
        formatter->requiresPerCoordinateInputTime();
130
0
    projExportable->_exportToPROJString(formatter.get());
131
0
    if (inverse) {
132
0
        formatter->stopInversion();
133
0
    }
134
0
    const auto &projString = formatter->toString();
135
136
0
    auto method = OperationMethod::create(
137
0
        util::PropertyMap().set(common::IdentifiedObject::NAME_KEY,
138
0
                                "PROJ-based operation method (approximate): " +
139
0
                                    projString),
140
0
        std::vector<GeneralOperationParameterNNPtr>{});
141
0
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(method);
142
0
    op->assignSelf(op);
143
0
    op->projString_ = projString;
144
0
    op->setCRSs(sourceCRS, targetCRS, interpolationCRS);
145
0
    op->setProperties(
146
0
        addDefaultNameIfNeeded(properties, "PROJ-based coordinate operation"));
147
0
    op->setAccuracies(accuracies);
148
0
    op->projStringExportable_ = projExportable.as_nullable();
149
0
    op->inverse_ = inverse;
150
0
    op->setHasBallparkTransformation(hasBallparkTransformation);
151
0
    op->setRequiresPerCoordinateInputTime(bRequiresPerCoordinateInputTime);
152
153
0
    return op;
154
0
}
155
156
// ---------------------------------------------------------------------------
157
158
5
CoordinateOperationNNPtr PROJBasedOperation::inverse() const {
159
160
5
    if (projStringExportable_ && sourceCRS() && targetCRS()) {
161
0
        return util::nn_static_pointer_cast<CoordinateOperation>(
162
0
            PROJBasedOperation::create(
163
0
                createPropertiesForInverse(this, false, false),
164
0
                NN_NO_CHECK(projStringExportable_), !inverse_,
165
0
                NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
166
0
                interpolationCRS(), coordinateOperationAccuracies(),
167
0
                hasBallparkTransformation()));
168
0
    }
169
170
5
    auto formatter = io::PROJStringFormatter::create();
171
5
    formatter->startInversion();
172
5
    try {
173
5
        formatter->ingestPROJString(projString_);
174
5
    } catch (const io::ParsingException &e) {
175
0
        throw util::UnsupportedOperationException(
176
0
            std::string("PROJBasedOperation::inverse() failed: ") + e.what());
177
0
    }
178
5
    formatter->stopInversion();
179
180
5
    auto op = PROJBasedOperation::create(
181
5
        createPropertiesForInverse(this, false, false), formatter->toString(),
182
5
        targetCRS(), sourceCRS(), coordinateOperationAccuracies());
183
5
    if (sourceCRS() && targetCRS()) {
184
5
        op->setCRSs(NN_NO_CHECK(targetCRS()), NN_NO_CHECK(sourceCRS()),
185
5
                    interpolationCRS());
186
5
    }
187
5
    op->setHasBallparkTransformation(hasBallparkTransformation());
188
5
    op->setRequiresPerCoordinateInputTime(
189
5
        formatter->requiresPerCoordinateInputTime());
190
5
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
191
5
}
192
193
// ---------------------------------------------------------------------------
194
195
0
void PROJBasedOperation::_exportToWKT(io::WKTFormatter *formatter) const {
196
197
0
    if (sourceCRS() && targetCRS()) {
198
0
        exportTransformationToWKT(formatter);
199
0
        return;
200
0
    }
201
202
0
    const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
203
0
    if (!isWKT2) {
204
0
        throw io::FormattingException(
205
0
            "PROJBasedOperation can only be exported to WKT2");
206
0
    }
207
208
0
    formatter->startNode(io::WKTConstants::CONVERSION, false);
209
0
    formatter->addQuotedString(nameStr());
210
0
    method()->_exportToWKT(formatter);
211
212
0
    for (const auto &paramValue : parameterValues()) {
213
0
        paramValue->_exportToWKT(formatter);
214
0
    }
215
0
    formatter->endNode();
216
0
}
217
218
// ---------------------------------------------------------------------------
219
220
void PROJBasedOperation::_exportToJSON(
221
    io::JSONFormatter *formatter) const // throw(FormattingException)
222
0
{
223
0
    auto writer = formatter->writer();
224
0
    auto objectContext(formatter->MakeObjectContext(
225
0
        (sourceCRS() && targetCRS()) ? "Transformation" : "Conversion",
226
0
        !identifiers().empty()));
227
228
0
    writer->AddObjKey("name");
229
0
    const auto &l_name = nameStr();
230
0
    if (l_name.empty()) {
231
0
        writer->Add("unnamed");
232
0
    } else {
233
0
        writer->Add(l_name);
234
0
    }
235
236
0
    if (sourceCRS() && targetCRS()) {
237
0
        writer->AddObjKey("source_crs");
238
0
        formatter->setAllowIDInImmediateChild();
239
0
        sourceCRS()->_exportToJSON(formatter);
240
241
0
        writer->AddObjKey("target_crs");
242
0
        formatter->setAllowIDInImmediateChild();
243
0
        targetCRS()->_exportToJSON(formatter);
244
0
    }
245
246
0
    writer->AddObjKey("method");
247
0
    formatter->setOmitTypeInImmediateChild();
248
0
    formatter->setAllowIDInImmediateChild();
249
0
    method()->_exportToJSON(formatter);
250
251
0
    const auto &l_parameterValues = parameterValues();
252
0
    writer->AddObjKey("parameters");
253
0
    {
254
0
        auto parametersContext(writer->MakeArrayContext(false));
255
0
        for (const auto &genOpParamvalue : l_parameterValues) {
256
0
            formatter->setAllowIDInImmediateChild();
257
0
            formatter->setOmitTypeInImmediateChild();
258
0
            genOpParamvalue->_exportToJSON(formatter);
259
0
        }
260
0
    }
261
0
}
262
263
// ---------------------------------------------------------------------------
264
265
void PROJBasedOperation::_exportToPROJString(
266
9.48k
    io::PROJStringFormatter *formatter) const {
267
9.48k
    if (projStringExportable_) {
268
0
        if (inverse_) {
269
0
            formatter->startInversion();
270
0
        }
271
0
        projStringExportable_->_exportToPROJString(formatter);
272
0
        if (inverse_) {
273
0
            formatter->stopInversion();
274
0
        }
275
0
        return;
276
0
    }
277
278
9.48k
    try {
279
9.48k
        formatter->ingestPROJString(projString_);
280
9.48k
    } catch (const io::ParsingException &e) {
281
0
        throw io::FormattingException(
282
0
            std::string("PROJBasedOperation::exportToPROJString() failed: ") +
283
0
            e.what());
284
0
    }
285
9.48k
}
286
287
// ---------------------------------------------------------------------------
288
289
0
CoordinateOperationNNPtr PROJBasedOperation::_shallowClone() const {
290
0
    auto op = PROJBasedOperation::nn_make_shared<PROJBasedOperation>(*this);
291
0
    op->assignSelf(op);
292
0
    op->setCRSs(this, false);
293
0
    return util::nn_static_pointer_cast<CoordinateOperation>(op);
294
0
}
295
296
// ---------------------------------------------------------------------------
297
298
std::set<GridDescription>
299
PROJBasedOperation::gridsNeeded(const io::DatabaseContextPtr &databaseContext,
300
0
                                bool considerKnownGridsAsAvailable) const {
301
0
    std::set<GridDescription> res;
302
303
0
    try {
304
0
        auto formatterOut = io::PROJStringFormatter::create();
305
0
        auto formatter = io::PROJStringFormatter::create();
306
0
        formatter->ingestPROJString(exportToPROJString(formatterOut.get()));
307
0
        const auto usedGridNames = formatter->getUsedGridNames();
308
0
        for (const auto &shortName : usedGridNames) {
309
0
            GridDescription desc;
310
0
            desc.shortName = shortName;
311
0
            if (databaseContext) {
312
0
                databaseContext->lookForGridInfo(
313
0
                    desc.shortName, considerKnownGridsAsAvailable,
314
0
                    desc.fullName, desc.packageName, desc.url,
315
0
                    desc.directDownload, desc.openLicense, desc.available);
316
0
            }
317
0
            res.insert(std::move(desc));
318
0
        }
319
0
    } catch (const io::ParsingException &) {
320
0
    }
321
322
0
    return res;
323
0
}
324
325
//! @endcond
326
327
// ---------------------------------------------------------------------------
328
329
} // namespace operation
330
331
NS_PROJ_END