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 &paramValue : 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