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