1 /******************************************************************************
2  *
3  * Project:  PROJ
4  * Purpose:  Test 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 #include "gtest_include.h"
30 
31 // to be able to use internal::replaceAll
32 #ifndef FROM_PROJ_CPP
33 #define FROM_PROJ_CPP
34 #endif
35 
36 #include "proj/common.hpp"
37 #include "proj/coordinateoperation.hpp"
38 #include "proj/coordinatesystem.hpp"
39 #include "proj/crs.hpp"
40 #include "proj/datum.hpp"
41 #include "proj/io.hpp"
42 #include "proj/metadata.hpp"
43 #include "proj/util.hpp"
44 
45 #include "proj/internal/internal.hpp"
46 
47 #include "proj_constants.h"
48 
49 #include <string>
50 #include <vector>
51 
52 using namespace osgeo::proj::common;
53 using namespace osgeo::proj::crs;
54 using namespace osgeo::proj::cs;
55 using namespace osgeo::proj::datum;
56 using namespace osgeo::proj::io;
57 using namespace osgeo::proj::internal;
58 using namespace osgeo::proj::metadata;
59 using namespace osgeo::proj::operation;
60 using namespace osgeo::proj::util;
61 
62 namespace {
63 struct UnrelatedObject : public IComparable {
64     UnrelatedObject() = default;
65 
_isEquivalentTo__anon321e84e00111::UnrelatedObject66     bool _isEquivalentTo(const IComparable *, Criterion,
67                          const DatabaseContextPtr &) const override {
68         assert(false);
69         return false;
70     }
71 };
72 
createUnrelatedObject()73 static nn<std::shared_ptr<UnrelatedObject>> createUnrelatedObject() {
74     return nn_make_shared<UnrelatedObject>();
75 }
76 } // namespace
77 
78 // ---------------------------------------------------------------------------
79 
TEST(operation,method)80 TEST(operation, method) {
81 
82     auto method = OperationMethod::create(
83         PropertyMap(), std::vector<OperationParameterNNPtr>{});
84     EXPECT_TRUE(method->isEquivalentTo(method.get()));
85     EXPECT_FALSE(method->isEquivalentTo(createUnrelatedObject().get()));
86     auto otherMethod = OperationMethod::create(
87         PropertyMap(),
88         std::vector<OperationParameterNNPtr>{OperationParameter::create(
89             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))});
90     EXPECT_TRUE(otherMethod->isEquivalentTo(otherMethod.get()));
91     EXPECT_FALSE(method->isEquivalentTo(otherMethod.get()));
92     auto otherMethod2 = OperationMethod::create(
93         PropertyMap(),
94         std::vector<OperationParameterNNPtr>{OperationParameter::create(
95             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))});
96     EXPECT_FALSE(otherMethod->isEquivalentTo(otherMethod2.get()));
97     EXPECT_FALSE(otherMethod->isEquivalentTo(
98         otherMethod2.get(), IComparable::Criterion::EQUIVALENT));
99 }
100 
101 // ---------------------------------------------------------------------------
102 
TEST(operation,method_parameter_different_order)103 TEST(operation, method_parameter_different_order) {
104 
105     auto method1 = OperationMethod::create(
106         PropertyMap(), std::vector<OperationParameterNNPtr>{
107                            OperationParameter::create(PropertyMap().set(
108                                IdentifiedObject::NAME_KEY, "paramName")),
109                            OperationParameter::create(PropertyMap().set(
110                                IdentifiedObject::NAME_KEY, "paramName2"))});
111 
112     auto method2 = OperationMethod::create(
113         PropertyMap(), std::vector<OperationParameterNNPtr>{
114                            OperationParameter::create(PropertyMap().set(
115                                IdentifiedObject::NAME_KEY, "paramName2")),
116                            OperationParameter::create(PropertyMap().set(
117                                IdentifiedObject::NAME_KEY, "paramName"))});
118 
119     auto method3 = OperationMethod::create(
120         PropertyMap(), std::vector<OperationParameterNNPtr>{
121                            OperationParameter::create(PropertyMap().set(
122                                IdentifiedObject::NAME_KEY, "paramName3")),
123                            OperationParameter::create(PropertyMap().set(
124                                IdentifiedObject::NAME_KEY, "paramName"))});
125 
126     EXPECT_FALSE(method1->isEquivalentTo(method2.get()));
127     EXPECT_TRUE(method1->isEquivalentTo(method2.get(),
128                                         IComparable::Criterion::EQUIVALENT));
129     EXPECT_FALSE(method1->isEquivalentTo(method3.get(),
130                                          IComparable::Criterion::EQUIVALENT));
131 }
132 
133 // ---------------------------------------------------------------------------
134 
TEST(operation,ParameterValue)135 TEST(operation, ParameterValue) {
136 
137     auto valStr1 = ParameterValue::create("str1");
138     auto valStr2 = ParameterValue::create("str2");
139     EXPECT_TRUE(valStr1->isEquivalentTo(valStr1.get()));
140     EXPECT_FALSE(valStr1->isEquivalentTo(createUnrelatedObject().get()));
141     EXPECT_FALSE(valStr1->isEquivalentTo(valStr2.get()));
142 
143     auto valMeasure1 = ParameterValue::create(Angle(-90.0));
144     auto valMeasure1Eps = ParameterValue::create(Angle(-90.0 - 1e-11));
145     auto valMeasure2 = ParameterValue::create(Angle(-89.0));
146     EXPECT_TRUE(valMeasure1->isEquivalentTo(valMeasure1.get()));
147     EXPECT_TRUE(valMeasure1->isEquivalentTo(
148         valMeasure1.get(), IComparable::Criterion::EQUIVALENT));
149     EXPECT_FALSE(valMeasure1->isEquivalentTo(valMeasure1Eps.get()));
150     EXPECT_TRUE(valMeasure1->isEquivalentTo(
151         valMeasure1Eps.get(), IComparable::Criterion::EQUIVALENT));
152 
153     EXPECT_FALSE(valMeasure1->isEquivalentTo(valStr1.get()));
154     EXPECT_FALSE(valMeasure1->isEquivalentTo(valMeasure2.get()));
155     EXPECT_FALSE(valMeasure1->isEquivalentTo(
156         valMeasure2.get(), IComparable::Criterion::EQUIVALENT));
157 
158     auto valInt1 = ParameterValue::create(1);
159     auto valInt2 = ParameterValue::create(2);
160     EXPECT_TRUE(valInt1->isEquivalentTo(valInt1.get()));
161     EXPECT_FALSE(valInt1->isEquivalentTo(valInt2.get()));
162 
163     auto valTrue = ParameterValue::create(true);
164     auto valFalse = ParameterValue::create(false);
165     EXPECT_TRUE(valTrue->isEquivalentTo(valTrue.get()));
166     EXPECT_FALSE(valTrue->isEquivalentTo(valFalse.get()));
167 }
168 
169 // ---------------------------------------------------------------------------
170 
TEST(operation,OperationParameter)171 TEST(operation, OperationParameter) {
172 
173     auto op1 = OperationParameter::create(
174         PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"));
175     auto op2 = OperationParameter::create(
176         PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"));
177     EXPECT_TRUE(op1->isEquivalentTo(op1.get()));
178     EXPECT_FALSE(op1->isEquivalentTo(createUnrelatedObject().get()));
179     EXPECT_FALSE(op1->isEquivalentTo(op2.get()));
180 }
181 
182 // ---------------------------------------------------------------------------
183 
TEST(operation,OperationParameterValue)184 TEST(operation, OperationParameterValue) {
185 
186     auto op1 = OperationParameter::create(
187         PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"));
188     auto op2 = OperationParameter::create(
189         PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"));
190     auto valStr1 = ParameterValue::create("str1");
191     auto valStr2 = ParameterValue::create("str2");
192     auto opv11 = OperationParameterValue::create(op1, valStr1);
193     EXPECT_TRUE(opv11->isEquivalentTo(opv11.get()));
194     EXPECT_FALSE(opv11->isEquivalentTo(createUnrelatedObject().get()));
195     auto opv12 = OperationParameterValue::create(op1, valStr2);
196     EXPECT_FALSE(opv11->isEquivalentTo(opv12.get()));
197     auto opv21 = OperationParameterValue::create(op2, valStr1);
198     EXPECT_FALSE(opv11->isEquivalentTo(opv12.get()));
199 }
200 
201 // ---------------------------------------------------------------------------
202 
TEST(operation,SingleOperation)203 TEST(operation, SingleOperation) {
204 
205     auto sop1 = Transformation::create(
206         PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
207         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
208         static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()),
209         PropertyMap(),
210         std::vector<OperationParameterNNPtr>{OperationParameter::create(
211             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
212         std::vector<ParameterValueNNPtr>{
213             ParameterValue::createFilename("foo.bin")},
214         std::vector<PositionalAccuracyNNPtr>{
215             PositionalAccuracy::create("0.1")});
216 
217     EXPECT_TRUE(sop1->isEquivalentTo(sop1.get()));
218     EXPECT_FALSE(sop1->isEquivalentTo(createUnrelatedObject().get()));
219 
220     EXPECT_TRUE(
221         sop1->isEquivalentTo(sop1->CoordinateOperation::shallowClone().get()));
222     EXPECT_TRUE(sop1->inverse()->isEquivalentTo(
223         sop1->inverse()->CoordinateOperation::shallowClone().get()));
224 
225     auto sop2 = Transformation::create(
226         PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
227         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
228         static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()),
229         PropertyMap(),
230         std::vector<OperationParameterNNPtr>{OperationParameter::create(
231             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))},
232         std::vector<ParameterValueNNPtr>{
233             ParameterValue::createFilename("foo.bin")},
234         std::vector<PositionalAccuracyNNPtr>{
235             PositionalAccuracy::create("0.1")});
236     EXPECT_FALSE(sop1->isEquivalentTo(sop2.get()));
237 
238     auto sop3 = Transformation::create(
239         PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
240         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
241         static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()),
242         PropertyMap(),
243         std::vector<OperationParameterNNPtr>{
244             OperationParameter::create(
245                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")),
246             OperationParameter::create(
247                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))},
248         std::vector<ParameterValueNNPtr>{
249             ParameterValue::createFilename("foo.bin"),
250             ParameterValue::createFilename("foo2.bin")},
251         std::vector<PositionalAccuracyNNPtr>{
252             PositionalAccuracy::create("0.1")});
253     EXPECT_FALSE(sop1->isEquivalentTo(sop3.get()));
254 
255     auto sop4 = Transformation::create(
256         PropertyMap(), nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
257         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
258         static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()),
259         PropertyMap(),
260         std::vector<OperationParameterNNPtr>{OperationParameter::create(
261             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
262         std::vector<ParameterValueNNPtr>{
263             ParameterValue::createFilename("foo2.bin")},
264         std::vector<PositionalAccuracyNNPtr>{
265             PositionalAccuracy::create("0.1")});
266     EXPECT_FALSE(sop1->isEquivalentTo(sop4.get()));
267 }
268 
269 // ---------------------------------------------------------------------------
270 
TEST(operation,SingleOperation_different_order)271 TEST(operation, SingleOperation_different_order) {
272 
273     auto sop1 = Transformation::create(
274         PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored1"),
275         GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr,
276         PropertyMap(),
277         std::vector<OperationParameterNNPtr>{
278             OperationParameter::create(
279                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")),
280             OperationParameter::create(
281                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))},
282         std::vector<ParameterValueNNPtr>{
283             ParameterValue::createFilename("foo.bin"),
284             ParameterValue::createFilename("foo2.bin")},
285         {});
286 
287     auto sop2 = Transformation::create(
288         PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored2"),
289         GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr,
290         PropertyMap(),
291         std::vector<OperationParameterNNPtr>{
292             OperationParameter::create(
293                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2")),
294             OperationParameter::create(
295                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
296         std::vector<ParameterValueNNPtr>{
297             ParameterValue::createFilename("foo2.bin"),
298             ParameterValue::createFilename("foo.bin")},
299         {});
300 
301     auto sop3 = Transformation::create(
302         PropertyMap().set(IdentifiedObject::NAME_KEY, "ignored3"),
303         GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4807, nullptr,
304         PropertyMap(),
305         std::vector<OperationParameterNNPtr>{
306             OperationParameter::create(
307                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName")),
308             OperationParameter::create(
309                 PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName2"))},
310         std::vector<ParameterValueNNPtr>{
311             ParameterValue::createFilename("foo2.bin"),
312             ParameterValue::createFilename("foo.bin")},
313         {});
314 
315     EXPECT_FALSE(sop1->isEquivalentTo(sop2.get()));
316     EXPECT_TRUE(
317         sop1->isEquivalentTo(sop2.get(), IComparable::Criterion::EQUIVALENT));
318     EXPECT_FALSE(
319         sop1->isEquivalentTo(sop3.get(), IComparable::Criterion::EQUIVALENT));
320 }
321 
322 // ---------------------------------------------------------------------------
323 
TEST(operation,transformation_to_wkt)324 TEST(operation, transformation_to_wkt) {
325     PropertyMap propertiesTransformation;
326     propertiesTransformation
327         .set(Identifier::CODESPACE_KEY, "codeSpaceTransformation")
328         .set(Identifier::CODE_KEY, "codeTransformation")
329         .set(IdentifiedObject::NAME_KEY, "transformationName")
330         .set(IdentifiedObject::REMARKS_KEY, "my remarks");
331 
332     auto transf = Transformation::create(
333         propertiesTransformation,
334         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
335         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
336         static_cast<CRSPtr>(GeographicCRS::EPSG_4979.as_nullable()),
337         PropertyMap()
338             .set(Identifier::CODESPACE_KEY, "codeSpaceOperationMethod")
339             .set(Identifier::CODE_KEY, "codeOperationMethod")
340             .set(IdentifiedObject::NAME_KEY, "operationMethodName"),
341         std::vector<OperationParameterNNPtr>{OperationParameter::create(
342             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
343         std::vector<ParameterValueNNPtr>{
344             ParameterValue::createFilename("foo.bin")},
345         std::vector<PositionalAccuracyNNPtr>{
346             PositionalAccuracy::create("0.1")});
347 
348     std::string src_wkt;
349     {
350         auto formatter = WKTFormatter::create();
351         formatter->setOutputId(false);
352         src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get());
353     }
354 
355     std::string dst_wkt;
356     {
357         auto formatter = WKTFormatter::create();
358         formatter->setOutputId(false);
359         dst_wkt = GeographicCRS::EPSG_4807->exportToWKT(formatter.get());
360     }
361 
362     std::string interpolation_wkt;
363     {
364         auto formatter = WKTFormatter::create();
365         formatter->setOutputId(false);
366         interpolation_wkt =
367             GeographicCRS::EPSG_4979->exportToWKT(formatter.get());
368     }
369 
370     auto expected =
371         "COORDINATEOPERATION[\"transformationName\",\n"
372         "    SOURCECRS[" +
373         src_wkt + "],\n"
374                   "    TARGETCRS[" +
375         dst_wkt +
376         "],\n"
377         "    METHOD[\"operationMethodName\",\n"
378         "        ID[\"codeSpaceOperationMethod\",\"codeOperationMethod\"]],\n"
379         "    PARAMETERFILE[\"paramName\",\"foo.bin\"],\n"
380         "    INTERPOLATIONCRS[" +
381         interpolation_wkt +
382         "],\n"
383         "    OPERATIONACCURACY[0.1],\n"
384         "    ID[\"codeSpaceTransformation\",\"codeTransformation\"],\n"
385         "    REMARK[\"my remarks\"]]";
386 
387     EXPECT_EQ(
388         replaceAll(replaceAll(transf->exportToWKT(WKTFormatter::create().get()),
389                               " ", ""),
390                    "\n", ""),
391         replaceAll(replaceAll(expected, " ", ""), "\n", ""));
392 
393     EXPECT_THROW(
394         transf->exportToWKT(
395             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
396         FormattingException);
397 
398     EXPECT_TRUE(transf->isEquivalentTo(transf.get()));
399     EXPECT_FALSE(transf->isEquivalentTo(createUnrelatedObject().get()));
400 }
401 
402 // ---------------------------------------------------------------------------
403 
TEST(operation,concatenated_operation)404 TEST(operation, concatenated_operation) {
405 
406     PropertyMap propertiesTransformation;
407     propertiesTransformation
408         .set(Identifier::CODESPACE_KEY, "codeSpaceTransformation")
409         .set(Identifier::CODE_KEY, "codeTransformation")
410         .set(IdentifiedObject::NAME_KEY, "transformationName")
411         .set(IdentifiedObject::REMARKS_KEY, "my remarks");
412 
413     auto transf_1 = Transformation::create(
414         propertiesTransformation,
415         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4326),
416         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807), nullptr,
417         PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"),
418         std::vector<OperationParameterNNPtr>{OperationParameter::create(
419             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
420         std::vector<ParameterValueNNPtr>{
421             ParameterValue::createFilename("foo.bin")},
422         std::vector<PositionalAccuracyNNPtr>());
423 
424     auto transf_2 = Transformation::create(
425         propertiesTransformation,
426         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4807),
427         nn_static_pointer_cast<CRS>(GeographicCRS::EPSG_4979), nullptr,
428         PropertyMap().set(IdentifiedObject::NAME_KEY, "operationMethodName"),
429         std::vector<OperationParameterNNPtr>{OperationParameter::create(
430             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
431         std::vector<ParameterValueNNPtr>{
432             ParameterValue::createFilename("foo.bin")},
433         std::vector<PositionalAccuracyNNPtr>());
434 
435     auto concat = ConcatenatedOperation::create(
436         PropertyMap()
437             .set(Identifier::CODESPACE_KEY, "codeSpace")
438             .set(Identifier::CODE_KEY, "code")
439             .set(IdentifiedObject::NAME_KEY, "name")
440             .set(IdentifiedObject::REMARKS_KEY, "my remarks"),
441         std::vector<CoordinateOperationNNPtr>{transf_1, transf_2},
442         std::vector<PositionalAccuracyNNPtr>{
443             PositionalAccuracy::create("0.1")});
444 
445     std::string src_wkt;
446     {
447         auto formatter =
448             WKTFormatter::create(WKTFormatter::Convention::WKT2_2019);
449         src_wkt = GeographicCRS::EPSG_4326->exportToWKT(formatter.get());
450     }
451 
452     std::string dst_wkt;
453     {
454         auto formatter =
455             WKTFormatter::create(WKTFormatter::Convention::WKT2_2019);
456         dst_wkt = GeographicCRS::EPSG_4979->exportToWKT(formatter.get());
457     }
458 
459     std::string step1_wkt;
460     {
461         auto formatter =
462             WKTFormatter::create(WKTFormatter::Convention::WKT2_2019);
463         step1_wkt = transf_1->exportToWKT(formatter.get());
464     }
465 
466     std::string step2_wkt;
467     {
468         auto formatter =
469             WKTFormatter::create(WKTFormatter::Convention::WKT2_2019);
470         step2_wkt = transf_2->exportToWKT(formatter.get());
471     }
472 
473     auto expected = "CONCATENATEDOPERATION[\"name\",\n"
474                     "    SOURCECRS[" +
475                     src_wkt + "],\n"
476                               "    TARGETCRS[" +
477                     dst_wkt + "],\n"
478                               "    STEP[" +
479                     step1_wkt + "],\n"
480                                 "    STEP[" +
481                     step2_wkt + "],\n"
482                                 "    ID[\"codeSpace\",\"code\"],\n"
483                                 "    REMARK[\"my remarks\"]]";
484 
485     EXPECT_EQ(replaceAll(replaceAll(concat->exportToWKT(
486                                         WKTFormatter::create(
487                                             WKTFormatter::Convention::WKT2_2019)
488                                             .get()),
489                                     " ", ""),
490                          "\n", ""),
491               replaceAll(replaceAll(expected, " ", ""), "\n", ""));
492 
493     EXPECT_THROW(concat->exportToWKT(WKTFormatter::create().get()),
494                  FormattingException);
495 
496     EXPECT_THROW(ConcatenatedOperation::create(
497                      PropertyMap().set(IdentifiedObject::NAME_KEY, "name"),
498                      std::vector<CoordinateOperationNNPtr>{transf_1, transf_1},
499                      std::vector<PositionalAccuracyNNPtr>()),
500                  InvalidOperation);
501 
502     auto inv = concat->inverse();
503     EXPECT_EQ(inv->nameStr(), "Inverse of name");
504     EXPECT_EQ(inv->sourceCRS()->nameStr(), concat->targetCRS()->nameStr());
505     EXPECT_EQ(inv->targetCRS()->nameStr(), concat->sourceCRS()->nameStr());
506     auto inv_as_concat = nn_dynamic_pointer_cast<ConcatenatedOperation>(inv);
507     ASSERT_TRUE(inv_as_concat != nullptr);
508 
509     ASSERT_EQ(inv_as_concat->operations().size(), 2U);
510     EXPECT_EQ(inv_as_concat->operations()[0]->nameStr(),
511               "Inverse of transformationName");
512     EXPECT_EQ(inv_as_concat->operations()[1]->nameStr(),
513               "Inverse of transformationName");
514 
515     EXPECT_TRUE(concat->isEquivalentTo(concat.get()));
516     EXPECT_FALSE(concat->isEquivalentTo(createUnrelatedObject().get()));
517     EXPECT_TRUE(concat->isEquivalentTo(
518         concat->CoordinateOperation::shallowClone().get()));
519     EXPECT_FALSE(
520         ConcatenatedOperation::create(PropertyMap(),
521                                       std::vector<CoordinateOperationNNPtr>{
522                                           transf_1, transf_1->inverse()},
523                                       std::vector<PositionalAccuracyNNPtr>())
524             ->isEquivalentTo(ConcatenatedOperation::create(
525                                  PropertyMap(),
526                                  std::vector<CoordinateOperationNNPtr>{
527                                      transf_1->inverse(), transf_1},
528                                  std::vector<PositionalAccuracyNNPtr>())
529                                  .get()));
530     EXPECT_FALSE(
531         ConcatenatedOperation::create(PropertyMap(),
532                                       std::vector<CoordinateOperationNNPtr>{
533                                           transf_1, transf_1->inverse()},
534                                       std::vector<PositionalAccuracyNNPtr>())
535             ->isEquivalentTo(ConcatenatedOperation::create(
536                                  PropertyMap(),
537                                  std::vector<CoordinateOperationNNPtr>{
538                                      transf_1, transf_1->inverse(), transf_1},
539                                  std::vector<PositionalAccuracyNNPtr>())
540                                  .get()));
541 }
542 
543 // ---------------------------------------------------------------------------
544 
TEST(operation,transformation_createGeocentricTranslations)545 TEST(operation, transformation_createGeocentricTranslations) {
546 
547     auto transf = Transformation::createGeocentricTranslations(
548         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
549         2.0, 3.0, std::vector<PositionalAccuracyNNPtr>());
550     EXPECT_TRUE(transf->validateParameters().empty());
551 
552     auto params = transf->getTOWGS84Parameters();
553     auto expected = std::vector<double>{1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 0.0};
554     EXPECT_EQ(params, expected);
555 
556     auto inv_transf = transf->inverse();
557     auto inv_transf_as_transf =
558         nn_dynamic_pointer_cast<Transformation>(inv_transf);
559     ASSERT_TRUE(inv_transf_as_transf != nullptr);
560 
561     EXPECT_EQ(transf->sourceCRS()->nameStr(),
562               inv_transf_as_transf->targetCRS()->nameStr());
563     EXPECT_EQ(transf->targetCRS()->nameStr(),
564               inv_transf_as_transf->sourceCRS()->nameStr());
565     auto expected_inv =
566         std::vector<double>{-1.0, -2.0, -3.0, 0.0, 0.0, 0.0, 0.0};
567     EXPECT_EQ(inv_transf_as_transf->getTOWGS84Parameters(), expected_inv);
568 
569     EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
570               "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
571               "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 "
572               "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 "
573               "+z=3 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 "
574               "+step +proj=unitconvert +xy_in=rad +xy_out=deg +step "
575               "+proj=axisswap +order=2,1");
576 }
577 
578 // ---------------------------------------------------------------------------
579 
createGeocentricDatumWGS84()580 static GeodeticCRSNNPtr createGeocentricDatumWGS84() {
581     PropertyMap propertiesCRS;
582     propertiesCRS.set(Identifier::CODESPACE_KEY, "EPSG")
583         .set(Identifier::CODE_KEY, 4328)
584         .set(IdentifiedObject::NAME_KEY, "WGS 84");
585     return GeodeticCRS::create(
586         propertiesCRS, GeodeticReferenceFrame::EPSG_6326,
587         CartesianCS::createGeocentric(UnitOfMeasure::METRE));
588 }
589 
590 // ---------------------------------------------------------------------------
591 
createGeocentricKM()592 static GeodeticCRSNNPtr createGeocentricKM() {
593     PropertyMap propertiesCRS;
594     propertiesCRS.set(IdentifiedObject::NAME_KEY, "Based on WGS 84");
595     return GeodeticCRS::create(
596         propertiesCRS, GeodeticReferenceFrame::EPSG_6326,
597         CartesianCS::createGeocentric(
598             UnitOfMeasure("kilometre", 1000.0, UnitOfMeasure::Type::LINEAR)));
599 }
600 
601 // ---------------------------------------------------------------------------
602 
TEST(operation,transformation_createGeocentricTranslations_between_geocentricCRS)603 TEST(operation,
604      transformation_createGeocentricTranslations_between_geocentricCRS) {
605 
606     auto transf1 = Transformation::createGeocentricTranslations(
607         PropertyMap(), createGeocentricDatumWGS84(), createGeocentricKM(), 1.0,
608         2.0, 3.0, std::vector<PositionalAccuracyNNPtr>());
609 
610     EXPECT_EQ(transf1->exportToPROJString(PROJStringFormatter::create().get()),
611               "+proj=pipeline +step +proj=helmert +x=1 +y=2 +z=3 +step "
612               "+proj=unitconvert +xy_in=m +z_in=m +xy_out=km +z_out=km");
613 
614     auto transf2 = Transformation::createGeocentricTranslations(
615         PropertyMap(), createGeocentricKM(), createGeocentricDatumWGS84(), 1.0,
616         2.0, 3.0, std::vector<PositionalAccuracyNNPtr>());
617 
618     EXPECT_EQ(transf2->exportToPROJString(PROJStringFormatter::create().get()),
619               "+proj=pipeline +step +proj=unitconvert +xy_in=km +z_in=km "
620               "+xy_out=m +z_out=m +step +proj=helmert +x=1 +y=2 +z=3");
621 
622     auto transf3 = Transformation::createGeocentricTranslations(
623         PropertyMap(), createGeocentricKM(), createGeocentricKM(), 1.0, 2.0,
624         3.0, std::vector<PositionalAccuracyNNPtr>());
625 
626     EXPECT_EQ(transf3->exportToPROJString(PROJStringFormatter::create().get()),
627               "+proj=pipeline +step +proj=unitconvert +xy_in=km +z_in=km "
628               "+xy_out=m +z_out=m +step +proj=helmert +x=1 +y=2 +z=3 +step "
629               "+proj=unitconvert +xy_in=m +z_in=m +xy_out=km +z_out=km");
630 }
631 
632 // ---------------------------------------------------------------------------
633 
TEST(operation,transformation_createGeocentricTranslations_null)634 TEST(operation, transformation_createGeocentricTranslations_null) {
635 
636     auto transf = Transformation::createGeocentricTranslations(
637         PropertyMap(), createGeocentricDatumWGS84(),
638         createGeocentricDatumWGS84(), 0.0, 0.0, 0.0,
639         std::vector<PositionalAccuracyNNPtr>());
640 
641     EXPECT_EQ(transf->inverse()->exportToPROJString(
642                   PROJStringFormatter::create().get()),
643               "+proj=noop");
644 }
645 
646 // ---------------------------------------------------------------------------
647 
TEST(operation,transformation_createGeocentricTranslations_neg_zero)648 TEST(operation, transformation_createGeocentricTranslations_neg_zero) {
649 
650     auto transf = Transformation::createGeocentricTranslations(
651         PropertyMap(), createGeocentricDatumWGS84(),
652         createGeocentricDatumWGS84(), 1.0, -0.0, 0.0,
653         std::vector<PositionalAccuracyNNPtr>());
654 
655     EXPECT_EQ(transf->inverse()->exportToPROJString(
656                   PROJStringFormatter::create().get()),
657               "+proj=helmert +x=-1 +y=0 +z=0");
658 }
659 
660 // ---------------------------------------------------------------------------
661 
TEST(operation,transformation_createPositionVector)662 TEST(operation, transformation_createPositionVector) {
663 
664     auto transf = Transformation::createPositionVector(
665         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
666         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>{
667                                           PositionalAccuracy::create("100")});
668     EXPECT_TRUE(transf->validateParameters().empty());
669 
670     ASSERT_EQ(transf->coordinateOperationAccuracies().size(), 1U);
671 
672     auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
673     EXPECT_EQ(transf->getTOWGS84Parameters(), expected);
674 
675     EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
676               "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
677               "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 "
678               "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 "
679               "+z=3 +rx=4 +ry=5 +rz=6 +s=7 +convention=position_vector +step "
680               "+inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step "
681               "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap "
682               "+order=2,1");
683 
684     auto inv_transf = transf->inverse();
685     ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 1U);
686 
687     EXPECT_EQ(transf->sourceCRS()->nameStr(),
688               inv_transf->targetCRS()->nameStr());
689     EXPECT_EQ(transf->targetCRS()->nameStr(),
690               inv_transf->sourceCRS()->nameStr());
691 
692 #ifdef USE_APPROXIMATE_HELMERT_INVERSE
693     auto inv_transf_as_transf =
694         nn_dynamic_pointer_cast<Transformation>(inv_transf);
695     ASSERT_TRUE(inv_transf_as_transf != nullptr);
696 #else
697     EXPECT_EQ(
698         inv_transf->exportToPROJString(PROJStringFormatter::create().get()),
699         "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
700         "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step "
701         "+proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 +rx=4 "
702         "+ry=5 +rz=6 +s=7 +convention=position_vector +step +inv +proj=cart "
703         "+ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad "
704         "+xy_out=deg +step +proj=axisswap +order=2,1");
705 
706     // In WKT, use approximate formula
707     auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get());
708     EXPECT_TRUE(
709         wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") !=
710         std::string::npos)
711         << wkt;
712     EXPECT_TRUE(wkt.find("Position Vector transformation (geog2D domain)") !=
713                 std::string::npos)
714         << wkt;
715     EXPECT_TRUE(wkt.find("ID[\"EPSG\",9606]]") != std::string::npos) << wkt;
716     EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos)
717         << wkt;
718     EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos)
719         << wkt;
720     EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos)
721         << wkt;
722     EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt;
723     EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt;
724     EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt;
725     EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos)
726         << wkt;
727 #endif
728 }
729 
730 // ---------------------------------------------------------------------------
731 
TEST(operation,transformation_createCoordinateFrameRotation)732 TEST(operation, transformation_createCoordinateFrameRotation) {
733 
734     auto transf = Transformation::createCoordinateFrameRotation(
735         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
736         2.0, 3.0, -4.0, -5.0, -6.0, 7.0,
737         std::vector<PositionalAccuracyNNPtr>());
738     EXPECT_TRUE(transf->validateParameters().empty());
739 
740     auto params = transf->getTOWGS84Parameters();
741     auto expected = std::vector<double>{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0};
742     EXPECT_EQ(params, expected);
743 
744     EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
745               "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
746               "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 "
747               "+step +proj=cart +ellps=GRS80 +step +proj=helmert +x=1 +y=2 "
748               "+z=3 +rx=-4 +ry=-5 +rz=-6 +s=7 +convention=coordinate_frame "
749               "+step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step "
750               "+proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap "
751               "+order=2,1");
752 
753     auto inv_transf = transf->inverse();
754     ASSERT_EQ(inv_transf->coordinateOperationAccuracies().size(), 0U);
755 
756     EXPECT_EQ(transf->sourceCRS()->nameStr(),
757               inv_transf->targetCRS()->nameStr());
758     EXPECT_EQ(transf->targetCRS()->nameStr(),
759               inv_transf->sourceCRS()->nameStr());
760 
761 #ifdef USE_APPROXIMATE_HELMERT_INVERSE
762     auto inv_transf_as_transf =
763         nn_dynamic_pointer_cast<Transformation>(inv_transf);
764     ASSERT_TRUE(inv_transf_as_transf != nullptr);
765 #else
766     EXPECT_EQ(
767         inv_transf->exportToPROJString(PROJStringFormatter::create().get()),
768         "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
769         "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step "
770         "+proj=cart +ellps=WGS84 +step +inv +proj=helmert +x=1 +y=2 +z=3 "
771         "+rx=-4 +ry=-5 +rz=-6 +s=7 +convention=coordinate_frame +step +inv "
772         "+proj=cart +ellps=GRS80 +step +proj=pop +v_3 +step +proj=unitconvert "
773         "+xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1");
774 
775     // In WKT, use approximate formula
776     auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get());
777     EXPECT_TRUE(
778         wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") !=
779         std::string::npos)
780         << wkt;
781     EXPECT_TRUE(wkt.find("Coordinate Frame rotation (geog2D domain)") !=
782                 std::string::npos)
783         << wkt;
784     EXPECT_TRUE(wkt.find("ID[\"EPSG\",9607]]") != std::string::npos) << wkt;
785     EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos)
786         << wkt;
787     EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos)
788         << wkt;
789     EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos)
790         << wkt;
791     EXPECT_TRUE(wkt.find("\"X-axis rotation\",4") != std::string::npos) << wkt;
792     EXPECT_TRUE(wkt.find("\"Y-axis rotation\",5") != std::string::npos) << wkt;
793     EXPECT_TRUE(wkt.find("\"Z-axis rotation\",6") != std::string::npos) << wkt;
794     EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos)
795         << wkt;
796 #endif
797 }
798 
799 // ---------------------------------------------------------------------------
800 
TEST(operation,transformation_createTimeDependentPositionVector)801 TEST(operation, transformation_createTimeDependentPositionVector) {
802 
803     auto transf = Transformation::createTimeDependentPositionVector(
804         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
805         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 2018.5,
806         std::vector<PositionalAccuracyNNPtr>());
807     EXPECT_TRUE(transf->validateParameters().empty());
808 
809     auto inv_transf = transf->inverse();
810 
811     EXPECT_EQ(transf->sourceCRS()->nameStr(),
812               inv_transf->targetCRS()->nameStr());
813     EXPECT_EQ(transf->targetCRS()->nameStr(),
814               inv_transf->sourceCRS()->nameStr());
815 
816     auto projString =
817         inv_transf->exportToPROJString(PROJStringFormatter::create().get());
818     EXPECT_TRUE(projString.find("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 "
819                                 "+rz=6 +s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 "
820                                 "+dry=0.5 +drz=0.6 +ds=0.7 +t_epoch=2018.5 "
821                                 "+convention=position_vector") !=
822                 std::string::npos)
823         << projString;
824 
825     // In WKT, use approximate formula
826     auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get());
827     EXPECT_TRUE(
828         wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") !=
829         std::string::npos)
830         << wkt;
831     EXPECT_TRUE(wkt.find("Time-dependent Position Vector tfm (geog2D)") !=
832                 std::string::npos)
833         << wkt;
834     EXPECT_TRUE(wkt.find("ID[\"EPSG\",1054]]") != std::string::npos) << wkt;
835     EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos)
836         << wkt;
837     EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos)
838         << wkt;
839     EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos)
840         << wkt;
841     EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt;
842     EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt;
843     EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt;
844     EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos)
845         << wkt;
846     EXPECT_TRUE(wkt.find("\"Rate of change of X-axis translation\",-0.1") !=
847                 std::string::npos)
848         << wkt;
849     EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis translation\",-0.2") !=
850                 std::string::npos)
851         << wkt;
852     EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis translation\",-0.3") !=
853                 std::string::npos)
854         << wkt;
855     EXPECT_TRUE(wkt.find("\"Rate of change of X-axis rotation\",-0.4") !=
856                 std::string::npos)
857         << wkt;
858     EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis rotation\",-0.5") !=
859                 std::string::npos)
860         << wkt;
861     EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis rotation\",-0.6") !=
862                 std::string::npos)
863         << wkt;
864     EXPECT_TRUE(wkt.find("\"Rate of change of Scale difference\",-0.7") !=
865                 std::string::npos)
866         << wkt;
867     EXPECT_TRUE(wkt.find("\"Parameter reference epoch\",2018.5") !=
868                 std::string::npos)
869         << wkt;
870 }
871 
872 // ---------------------------------------------------------------------------
873 
TEST(operation,transformation_createTimeDependentCoordinateFrameRotation)874 TEST(operation, transformation_createTimeDependentCoordinateFrameRotation) {
875 
876     auto transf = Transformation::createTimeDependentCoordinateFrameRotation(
877         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
878         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 2018.5,
879         std::vector<PositionalAccuracyNNPtr>());
880     EXPECT_TRUE(transf->validateParameters().empty());
881 
882     auto inv_transf = transf->inverse();
883 
884     EXPECT_EQ(transf->sourceCRS()->nameStr(),
885               inv_transf->targetCRS()->nameStr());
886     EXPECT_EQ(transf->targetCRS()->nameStr(),
887               inv_transf->sourceCRS()->nameStr());
888 
889     auto projString =
890         inv_transf->exportToPROJString(PROJStringFormatter::create().get());
891     EXPECT_TRUE(projString.find("+proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 "
892                                 "+rz=6 +s=7 +dx=0.1 +dy=0.2 +dz=0.3 +drx=0.4 "
893                                 "+dry=0.5 +drz=0.6 +ds=0.7 +t_epoch=2018.5 "
894                                 "+convention=coordinate_frame") !=
895                 std::string::npos)
896         << projString;
897 
898     // In WKT, use approximate formula
899     auto wkt = inv_transf->exportToWKT(WKTFormatter::create().get());
900     EXPECT_TRUE(
901         wkt.find("Transformation from WGS 84 to NAD83 (approx. inversion)") !=
902         std::string::npos)
903         << wkt;
904     EXPECT_TRUE(wkt.find("Time-dependent Coordinate Frame rotation (geog2D)") !=
905                 std::string::npos)
906         << wkt;
907     EXPECT_TRUE(wkt.find("ID[\"EPSG\",1057]]") != std::string::npos) << wkt;
908     EXPECT_TRUE(wkt.find("\"X-axis translation\",-1") != std::string::npos)
909         << wkt;
910     EXPECT_TRUE(wkt.find("\"Y-axis translation\",-2") != std::string::npos)
911         << wkt;
912     EXPECT_TRUE(wkt.find("\"Z-axis translation\",-3") != std::string::npos)
913         << wkt;
914     EXPECT_TRUE(wkt.find("\"X-axis rotation\",-4") != std::string::npos) << wkt;
915     EXPECT_TRUE(wkt.find("\"Y-axis rotation\",-5") != std::string::npos) << wkt;
916     EXPECT_TRUE(wkt.find("\"Z-axis rotation\",-6") != std::string::npos) << wkt;
917     EXPECT_TRUE(wkt.find("\"Scale difference\",-7") != std::string::npos)
918         << wkt;
919     EXPECT_TRUE(wkt.find("\"Rate of change of X-axis translation\",-0.1") !=
920                 std::string::npos)
921         << wkt;
922     EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis translation\",-0.2") !=
923                 std::string::npos)
924         << wkt;
925     EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis translation\",-0.3") !=
926                 std::string::npos)
927         << wkt;
928     EXPECT_TRUE(wkt.find("\"Rate of change of X-axis rotation\",-0.4") !=
929                 std::string::npos)
930         << wkt;
931     EXPECT_TRUE(wkt.find("\"Rate of change of Y-axis rotation\",-0.5") !=
932                 std::string::npos)
933         << wkt;
934     EXPECT_TRUE(wkt.find("\"Rate of change of Z-axis rotation\",-0.6") !=
935                 std::string::npos)
936         << wkt;
937     EXPECT_TRUE(wkt.find("\"Rate of change of Scale difference\",-0.7") !=
938                 std::string::npos)
939         << wkt;
940     EXPECT_TRUE(wkt.find("\"Parameter reference epoch\",2018.5") !=
941                 std::string::npos)
942         << wkt;
943 }
944 
945 // ---------------------------------------------------------------------------
946 
TEST(operation,transformation_successive_helmert_noop)947 TEST(operation, transformation_successive_helmert_noop) {
948 
949     auto transf_1 = Transformation::createPositionVector(
950         PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0,
951         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>());
952     auto transf_2 = Transformation::createPositionVector(
953         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0,
954         -2.0, -3.0, -4.0, -5.0, -6.0, -7.0,
955         std::vector<PositionalAccuracyNNPtr>());
956 
957     auto concat = ConcatenatedOperation::create(
958         PropertyMap(),
959         std::vector<CoordinateOperationNNPtr>{transf_1, transf_2},
960         std::vector<PositionalAccuracyNNPtr>{});
961 
962     EXPECT_EQ(concat->exportToPROJString(PROJStringFormatter::create().get()),
963               "+proj=noop");
964 }
965 
966 // ---------------------------------------------------------------------------
967 
TEST(operation,transformation_successive_helmert_non_trivial_1)968 TEST(operation, transformation_successive_helmert_non_trivial_1) {
969 
970     auto transf_1 = Transformation::createPositionVector(
971         PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0,
972         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>());
973     auto transf_2 = Transformation::createPositionVector(
974         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0,
975         -2.0, -3.0, -4.0, -5.0, -6.0, 7.0,
976         std::vector<PositionalAccuracyNNPtr>());
977 
978     auto concat = ConcatenatedOperation::create(
979         PropertyMap(),
980         std::vector<CoordinateOperationNNPtr>{transf_1, transf_2},
981         std::vector<PositionalAccuracyNNPtr>{});
982 
983     EXPECT_NE(concat->exportToPROJString(PROJStringFormatter::create().get()),
984               "");
985 }
986 
987 // ---------------------------------------------------------------------------
988 
TEST(operation,transformation_successive_helmert_non_trivial_2)989 TEST(operation, transformation_successive_helmert_non_trivial_2) {
990 
991     auto transf_1 = Transformation::createPositionVector(
992         PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0,
993         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, std::vector<PositionalAccuracyNNPtr>());
994     auto transf_2 = Transformation::createCoordinateFrameRotation(
995         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, -1.0,
996         -2.0, -3.0, -4.0, -5.0, -6.0, -7.0,
997         std::vector<PositionalAccuracyNNPtr>());
998 
999     auto concat = ConcatenatedOperation::create(
1000         PropertyMap(),
1001         std::vector<CoordinateOperationNNPtr>{transf_1, transf_2},
1002         std::vector<PositionalAccuracyNNPtr>{});
1003 
1004     EXPECT_NE(concat->exportToPROJString(PROJStringFormatter::create().get()),
1005               "");
1006 }
1007 
1008 // ---------------------------------------------------------------------------
1009 
TEST(operation,transformation_createMolodensky)1010 TEST(operation, transformation_createMolodensky) {
1011 
1012     auto transf = Transformation::createMolodensky(
1013         PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0,
1014         2.0, 3.0, 4.0, 5.0, std::vector<PositionalAccuracyNNPtr>());
1015     EXPECT_TRUE(transf->validateParameters().empty());
1016 
1017     auto wkt = transf->exportToWKT(WKTFormatter::create().get());
1018     EXPECT_TRUE(replaceAll(replaceAll(wkt, " ", ""), "\n", "")
1019                     .find("METHOD[\"Molodensky\",ID[\"EPSG\",9604]]") !=
1020                 std::string::npos)
1021         << wkt;
1022 
1023     auto inv_transf = transf->inverse();
1024     auto inv_transf_as_transf =
1025         nn_dynamic_pointer_cast<Transformation>(inv_transf);
1026     ASSERT_TRUE(inv_transf_as_transf != nullptr);
1027 
1028     EXPECT_EQ(transf->sourceCRS()->nameStr(),
1029               inv_transf_as_transf->targetCRS()->nameStr());
1030     EXPECT_EQ(transf->targetCRS()->nameStr(),
1031               inv_transf_as_transf->sourceCRS()->nameStr());
1032 
1033     auto projString = inv_transf_as_transf->exportToPROJString(
1034         PROJStringFormatter::create().get());
1035     EXPECT_EQ(projString, "+proj=pipeline +step +proj=axisswap +order=2,1 "
1036                           "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
1037                           "+step +proj=molodensky +ellps=GRS80 +dx=-1 +dy=-2 "
1038                           "+dz=-3 +da=-4 +df=-5 +step +proj=unitconvert "
1039                           "+xy_in=rad +xy_out=deg +step +proj=axisswap "
1040                           "+order=2,1");
1041 }
1042 
1043 // ---------------------------------------------------------------------------
1044 
TEST(operation,transformation_createAbridgedMolodensky)1045 TEST(operation, transformation_createAbridgedMolodensky) {
1046 
1047     auto transf = Transformation::createAbridgedMolodensky(
1048         PropertyMap(), GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, 1.0,
1049         2.0, 3.0, 4.0, 5.0, std::vector<PositionalAccuracyNNPtr>());
1050 
1051     auto wkt = transf->exportToWKT(WKTFormatter::create().get());
1052     EXPECT_TRUE(replaceAll(replaceAll(wkt, " ", ""), "\n", "")
1053                     .find(replaceAll(
1054                         "METHOD[\"Abridged Molodensky\",ID[\"EPSG\",9605]]",
1055                         " ", "")) != std::string::npos)
1056         << wkt;
1057 
1058     auto inv_transf = transf->inverse();
1059     auto inv_transf_as_transf =
1060         nn_dynamic_pointer_cast<Transformation>(inv_transf);
1061     ASSERT_TRUE(inv_transf_as_transf != nullptr);
1062 
1063     EXPECT_EQ(transf->sourceCRS()->nameStr(),
1064               inv_transf_as_transf->targetCRS()->nameStr());
1065     EXPECT_EQ(transf->targetCRS()->nameStr(),
1066               inv_transf_as_transf->sourceCRS()->nameStr());
1067 
1068     auto projString = inv_transf_as_transf->exportToPROJString(
1069         PROJStringFormatter::create().get());
1070     EXPECT_EQ(projString, "+proj=pipeline +step +proj=axisswap +order=2,1 "
1071                           "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
1072                           "+step +proj=molodensky +ellps=GRS80 +dx=-1 +dy=-2 "
1073                           "+dz=-3 +da=-4 +df=-5 +abridged +step "
1074                           "+proj=unitconvert +xy_in=rad +xy_out=deg +step "
1075                           "+proj=axisswap +order=2,1");
1076 }
1077 
1078 // ---------------------------------------------------------------------------
1079 
TEST(operation,transformation_inverse)1080 TEST(operation, transformation_inverse) {
1081 
1082     auto transf = Transformation::create(
1083         PropertyMap()
1084             .set(IdentifiedObject::NAME_KEY, "my transformation")
1085             .set(Identifier::CODESPACE_KEY, "my codeSpace")
1086             .set(Identifier::CODE_KEY, "my code"),
1087         GeographicCRS::EPSG_4326, GeographicCRS::EPSG_4269, nullptr,
1088         PropertyMap()
1089             .set(IdentifiedObject::NAME_KEY, "my operation")
1090             .set(Identifier::CODESPACE_KEY, "my codeSpace")
1091             .set(Identifier::CODE_KEY, "my code"),
1092         std::vector<OperationParameterNNPtr>{OperationParameter::create(
1093             PropertyMap().set(IdentifiedObject::NAME_KEY, "paramName"))},
1094         std::vector<ParameterValueNNPtr>{
1095             ParameterValue::createFilename("foo.bin")},
1096         std::vector<PositionalAccuracyNNPtr>{
1097             PositionalAccuracy::create("0.1")});
1098     auto inv = transf->inverse();
1099     EXPECT_EQ(inv->inverse(), transf);
1100     EXPECT_EQ(
1101         inv->exportToWKT(WKTFormatter::create().get()),
1102         "COORDINATEOPERATION[\"Inverse of my transformation\",\n"
1103         "    SOURCECRS[\n"
1104         "        GEODCRS[\"NAD83\",\n"
1105         "            DATUM[\"North American Datum 1983\",\n"
1106         "                ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
1107         "                    LENGTHUNIT[\"metre\",1]]],\n"
1108         "            PRIMEM[\"Greenwich\",0,\n"
1109         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1110         "            CS[ellipsoidal,2],\n"
1111         "                AXIS[\"latitude\",north,\n"
1112         "                    ORDER[1],\n"
1113         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1114         "                AXIS[\"longitude\",east,\n"
1115         "                    ORDER[2],\n"
1116         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
1117         "    TARGETCRS[\n"
1118         "        GEODCRS[\"WGS 84\",\n"
1119         "            DATUM[\"World Geodetic System 1984\",\n"
1120         "                ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
1121         "                    LENGTHUNIT[\"metre\",1]]],\n"
1122         "            PRIMEM[\"Greenwich\",0,\n"
1123         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1124         "            CS[ellipsoidal,2],\n"
1125         "                AXIS[\"latitude\",north,\n"
1126         "                    ORDER[1],\n"
1127         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1128         "                AXIS[\"longitude\",east,\n"
1129         "                    ORDER[2],\n"
1130         "                    ANGLEUNIT[\"degree\",0.0174532925199433]]]],\n"
1131         "    METHOD[\"Inverse of my operation\",\n"
1132         "        ID[\"INVERSE(my codeSpace)\",\"my code\"]],\n"
1133         "    PARAMETERFILE[\"paramName\",\"foo.bin\"],\n"
1134         "    OPERATIONACCURACY[0.1],\n"
1135         "    ID[\"INVERSE(my codeSpace)\",\"my code\"]]");
1136 
1137     EXPECT_THROW(inv->exportToPROJString(PROJStringFormatter::create().get()),
1138                  FormattingException);
1139 }
1140 
1141 // ---------------------------------------------------------------------------
1142 
createVerticalCRS()1143 static VerticalCRSNNPtr createVerticalCRS() {
1144     PropertyMap propertiesVDatum;
1145     propertiesVDatum.set(Identifier::CODESPACE_KEY, "EPSG")
1146         .set(Identifier::CODE_KEY, 5101)
1147         .set(IdentifiedObject::NAME_KEY, "Ordnance Datum Newlyn");
1148     auto vdatum = VerticalReferenceFrame::create(propertiesVDatum);
1149     PropertyMap propertiesCRS;
1150     propertiesCRS.set(Identifier::CODESPACE_KEY, "EPSG")
1151         .set(Identifier::CODE_KEY, 5701)
1152         .set(IdentifiedObject::NAME_KEY, "ODN height");
1153     return VerticalCRS::create(
1154         propertiesCRS, vdatum,
1155         VerticalCS::createGravityRelatedHeight(UnitOfMeasure::METRE));
1156 }
1157 
1158 // ---------------------------------------------------------------------------
1159 
TEST(operation,transformation_createTOWGS84)1160 TEST(operation, transformation_createTOWGS84) {
1161 
1162     EXPECT_THROW(Transformation::createTOWGS84(GeographicCRS::EPSG_4326,
1163                                                std::vector<double>()),
1164                  InvalidOperation);
1165 
1166     EXPECT_THROW(Transformation::createTOWGS84(createVerticalCRS(),
1167                                                std::vector<double>(7, 0)),
1168                  InvalidOperation);
1169 }
1170 
1171 // ---------------------------------------------------------------------------
1172 
TEST(operation,createAxisOrderReversal)1173 TEST(operation, createAxisOrderReversal) {
1174 
1175     {
1176         auto conv = Conversion::createAxisOrderReversal(false);
1177         EXPECT_TRUE(conv->validateParameters().empty());
1178     }
1179     {
1180         auto conv = Conversion::createAxisOrderReversal(true);
1181         EXPECT_TRUE(conv->validateParameters().empty());
1182     }
1183 
1184     auto latLongDeg = GeographicCRS::create(
1185         PropertyMap(), GeodeticReferenceFrame::EPSG_6326,
1186         EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE));
1187     auto longLatDeg = GeographicCRS::create(
1188         PropertyMap(), GeodeticReferenceFrame::EPSG_6326,
1189         EllipsoidalCS::createLongitudeLatitude(UnitOfMeasure::DEGREE));
1190     {
1191         auto op = CoordinateOperationFactory::create()->createOperation(
1192             latLongDeg, longLatDeg);
1193         ASSERT_TRUE(op != nullptr);
1194         EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
1195                   "+proj=axisswap +order=2,1");
1196     }
1197     {
1198         auto longLatRad = GeographicCRS::create(
1199             PropertyMap(), GeodeticReferenceFrame::EPSG_6326,
1200             EllipsoidalCS::createLongitudeLatitude(UnitOfMeasure::RADIAN));
1201         auto op = CoordinateOperationFactory::create()->createOperation(
1202             longLatRad, latLongDeg);
1203         ASSERT_TRUE(op != nullptr);
1204         EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
1205                   "+proj=pipeline +step +proj=axisswap +order=2,1 "
1206                   "+step +proj=unitconvert +xy_in=rad +xy_out=deg");
1207     }
1208 }
1209 
1210 // ---------------------------------------------------------------------------
1211 
TEST(operation,utm_export)1212 TEST(operation, utm_export) {
1213     auto conv = Conversion::createUTM(PropertyMap(), 1, false);
1214     EXPECT_TRUE(conv->validateParameters().empty());
1215     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1216               "+proj=utm +zone=1 +south");
1217 
1218     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1219               "CONVERSION[\"UTM zone 1S\",\n"
1220               "    METHOD[\"Transverse Mercator\",\n"
1221               "        ID[\"EPSG\",9807]],\n"
1222               "    PARAMETER[\"Latitude of natural origin\",0,\n"
1223               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1224               "        ID[\"EPSG\",8801]],\n"
1225               "    PARAMETER[\"Longitude of natural origin\",-177,\n"
1226               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1227               "        ID[\"EPSG\",8802]],\n"
1228               "    PARAMETER[\"Scale factor at natural origin\",0.9996,\n"
1229               "        SCALEUNIT[\"unity\",1],\n"
1230               "        ID[\"EPSG\",8805]],\n"
1231               "    PARAMETER[\"False easting\",500000,\n"
1232               "        LENGTHUNIT[\"metre\",1],\n"
1233               "        ID[\"EPSG\",8806]],\n"
1234               "    PARAMETER[\"False northing\",10000000,\n"
1235               "        LENGTHUNIT[\"metre\",1],\n"
1236               "        ID[\"EPSG\",8807]],\n"
1237               "    ID[\"EPSG\",17001]]");
1238 
1239     EXPECT_EQ(
1240         conv->exportToWKT(
1241             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1242         "PROJECTION[\"Transverse_Mercator\"],\n"
1243         "PARAMETER[\"latitude_of_origin\",0],\n"
1244         "PARAMETER[\"central_meridian\",-177],\n"
1245         "PARAMETER[\"scale_factor\",0.9996],\n"
1246         "PARAMETER[\"false_easting\",500000],\n"
1247         "PARAMETER[\"false_northing\",10000000]");
1248 }
1249 
1250 // ---------------------------------------------------------------------------
1251 
TEST(operation,tmerc_export)1252 TEST(operation, tmerc_export) {
1253     auto conv = Conversion::createTransverseMercator(
1254         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
1255     EXPECT_TRUE(conv->validateParameters().empty());
1256     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1257               "+proj=tmerc +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5");
1258 
1259     {
1260         auto formatter = PROJStringFormatter::create();
1261         formatter->setUseApproxTMerc(true);
1262         EXPECT_EQ(conv->exportToPROJString(formatter.get()),
1263                   "+proj=tmerc +approx +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5");
1264     }
1265 
1266     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1267               "CONVERSION[\"Transverse Mercator\",\n"
1268               "    METHOD[\"Transverse Mercator\",\n"
1269               "        ID[\"EPSG\",9807]],\n"
1270               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1271               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1272               "        ID[\"EPSG\",8801]],\n"
1273               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1274               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1275               "        ID[\"EPSG\",8802]],\n"
1276               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
1277               "        SCALEUNIT[\"unity\",1],\n"
1278               "        ID[\"EPSG\",8805]],\n"
1279               "    PARAMETER[\"False easting\",4,\n"
1280               "        LENGTHUNIT[\"metre\",1],\n"
1281               "        ID[\"EPSG\",8806]],\n"
1282               "    PARAMETER[\"False northing\",5,\n"
1283               "        LENGTHUNIT[\"metre\",1],\n"
1284               "        ID[\"EPSG\",8807]]]");
1285 
1286     EXPECT_EQ(
1287         conv->exportToWKT(
1288             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1289         "PROJECTION[\"Transverse_Mercator\"],\n"
1290         "PARAMETER[\"latitude_of_origin\",1],\n"
1291         "PARAMETER[\"central_meridian\",2],\n"
1292         "PARAMETER[\"scale_factor\",3],\n"
1293         "PARAMETER[\"false_easting\",4],\n"
1294         "PARAMETER[\"false_northing\",5]");
1295 }
1296 
1297 // ---------------------------------------------------------------------------
1298 
TEST(operation,gstmerc_export)1299 TEST(operation, gstmerc_export) {
1300     auto conv = Conversion::createGaussSchreiberTransverseMercator(
1301         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
1302     EXPECT_TRUE(conv->validateParameters().empty());
1303     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1304               "+proj=gstmerc +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5");
1305 
1306     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1307               "CONVERSION[\"Gauss Schreiber Transverse Mercator\",\n"
1308               "    METHOD[\"Gauss Schreiber Transverse Mercator\"],\n"
1309               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1310               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1311               "        ID[\"EPSG\",8801]],\n"
1312               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1313               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1314               "        ID[\"EPSG\",8802]],\n"
1315               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
1316               "        SCALEUNIT[\"unity\",1],\n"
1317               "        ID[\"EPSG\",8805]],\n"
1318               "    PARAMETER[\"False easting\",4,\n"
1319               "        LENGTHUNIT[\"metre\",1],\n"
1320               "        ID[\"EPSG\",8806]],\n"
1321               "    PARAMETER[\"False northing\",5,\n"
1322               "        LENGTHUNIT[\"metre\",1],\n"
1323               "        ID[\"EPSG\",8807]]]");
1324 
1325     EXPECT_EQ(
1326         conv->exportToWKT(
1327             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1328         "PROJECTION[\"Gauss_Schreiber_Transverse_Mercator\"],\n"
1329         "PARAMETER[\"latitude_of_origin\",1],\n"
1330         "PARAMETER[\"central_meridian\",2],\n"
1331         "PARAMETER[\"scale_factor\",3],\n"
1332         "PARAMETER[\"false_easting\",4],\n"
1333         "PARAMETER[\"false_northing\",5]");
1334 }
1335 
1336 // ---------------------------------------------------------------------------
1337 
TEST(operation,tmerc_south_oriented_export)1338 TEST(operation, tmerc_south_oriented_export) {
1339     auto conv = Conversion::createTransverseMercatorSouthOriented(
1340         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
1341     EXPECT_TRUE(conv->validateParameters().empty());
1342 
1343     // False easting/northing != 0 not supported
1344     EXPECT_THROW(conv->exportToPROJString(PROJStringFormatter::create().get()),
1345                  FormattingException);
1346 
1347     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1348               "CONVERSION[\"Transverse Mercator (South Orientated)\",\n"
1349               "    METHOD[\"Transverse Mercator (South Orientated)\",\n"
1350               "        ID[\"EPSG\",9808]],\n"
1351               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1352               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1353               "        ID[\"EPSG\",8801]],\n"
1354               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1355               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1356               "        ID[\"EPSG\",8802]],\n"
1357               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
1358               "        SCALEUNIT[\"unity\",1],\n"
1359               "        ID[\"EPSG\",8805]],\n"
1360               "    PARAMETER[\"False easting\",4,\n"
1361               "        LENGTHUNIT[\"metre\",1],\n"
1362               "        ID[\"EPSG\",8806]],\n"
1363               "    PARAMETER[\"False northing\",5,\n"
1364               "        LENGTHUNIT[\"metre\",1],\n"
1365               "        ID[\"EPSG\",8807]]]");
1366 
1367     EXPECT_EQ(
1368         conv->exportToWKT(
1369             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1370         "PROJECTION[\"Transverse_Mercator_South_Orientated\"],\n"
1371         "PARAMETER[\"latitude_of_origin\",1],\n"
1372         "PARAMETER[\"central_meridian\",2],\n"
1373         "PARAMETER[\"scale_factor\",3],\n"
1374         "PARAMETER[\"false_easting\",4],\n"
1375         "PARAMETER[\"false_northing\",5]");
1376 
1377     auto wkt = "PROJCRS[\"Hartebeesthoek94 / Lo29\","
1378                "  BASEGEODCRS[\"Hartebeesthoek94\","
1379                "    DATUM[\"Hartebeesthoek94\","
1380                "      ELLIPSOID[\"WGS "
1381                "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1.0]]]],"
1382                "  CONVERSION[\"South African Survey Grid zone 29\","
1383                "    METHOD[\"Transverse Mercator (South "
1384                "Orientated)\",ID[\"EPSG\",9808]],"
1385                "    PARAMETER[\"Latitude of natural "
1386                "origin\",0,ANGLEUNIT[\"degree\",0.01745329252]],"
1387                "    PARAMETER[\"Longitude of natural "
1388                "origin\",29,ANGLEUNIT[\"degree\",0.01745329252]],"
1389                "    PARAMETER[\"Scale factor at natural "
1390                "origin\",1,SCALEUNIT[\"unity\",1.0]],"
1391                "    PARAMETER[\"False easting\",0,LENGTHUNIT[\"metre\",1.0]],"
1392                "    PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1.0]]],"
1393                "  CS[cartesian,2],"
1394                "    AXIS[\"westing (Y)\",west,ORDER[1]],"
1395                "    AXIS[\"southing (X)\",south,ORDER[2]],"
1396                "    LENGTHUNIT[\"metre\",1.0],"
1397                "  ID[\"EPSG\",2053]]";
1398     auto obj = WKTParser().createFromWKT(wkt);
1399     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
1400     ASSERT_TRUE(crs != nullptr);
1401     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
1402               "+proj=tmerc +axis=wsu +lat_0=0 +lon_0=29 +k=1 +x_0=0 +y_0=0 "
1403               "+ellps=WGS84 +units=m +no_defs +type=crs");
1404 }
1405 
1406 // ---------------------------------------------------------------------------
1407 
TEST(operation,tped_export)1408 TEST(operation, tped_export) {
1409     auto conv = Conversion::createTwoPointEquidistant(
1410         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5),
1411         Length(6));
1412     EXPECT_TRUE(conv->validateParameters().empty());
1413 
1414     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1415               "+proj=tpeqd +lat_1=1 +lon_1=2 +lat_2=3 +lon_2=4 +x_0=5 +y_0=6");
1416 
1417     auto formatter = WKTFormatter::create();
1418     formatter->simulCurNodeHasId();
1419     EXPECT_EQ(conv->exportToWKT(formatter.get()),
1420               "CONVERSION[\"Two Point Equidistant\",\n"
1421               "    METHOD[\"Two Point Equidistant\"],\n"
1422               "    PARAMETER[\"Latitude of 1st point\",1,\n"
1423               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1424               "    PARAMETER[\"Longitude of 1st point\",2,\n"
1425               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1426               "    PARAMETER[\"Latitude of 2nd point\",3,\n"
1427               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1428               "    PARAMETER[\"Longitude of 2nd point\",4,\n"
1429               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
1430               "    PARAMETER[\"False easting\",5,\n"
1431               "        LENGTHUNIT[\"metre\",1],\n"
1432               "        ID[\"EPSG\",8806]],\n"
1433               "    PARAMETER[\"False northing\",6,\n"
1434               "        LENGTHUNIT[\"metre\",1],\n"
1435               "        ID[\"EPSG\",8807]]]");
1436 
1437     EXPECT_EQ(
1438         conv->exportToWKT(
1439             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1440         "PROJECTION[\"Two_Point_Equidistant\"],\n"
1441         "PARAMETER[\"Latitude_Of_1st_Point\",1],\n"
1442         "PARAMETER[\"Longitude_Of_1st_Point\",2],\n"
1443         "PARAMETER[\"Latitude_Of_2nd_Point\",3],\n"
1444         "PARAMETER[\"Longitude_Of_2nd_Point\",4],\n"
1445         "PARAMETER[\"false_easting\",5],\n"
1446         "PARAMETER[\"false_northing\",6]");
1447 }
1448 
1449 // ---------------------------------------------------------------------------
1450 
TEST(operation,tmg_export)1451 TEST(operation, tmg_export) {
1452     auto conv = Conversion::createTunisiaMappingGrid(
1453         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
1454     EXPECT_TRUE(conv->validateParameters().empty());
1455 
1456     EXPECT_THROW(conv->exportToPROJString(PROJStringFormatter::create().get()),
1457                  FormattingException);
1458 
1459     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1460               "CONVERSION[\"Tunisia Mapping Grid\",\n"
1461               "    METHOD[\"Tunisia Mapping Grid\",\n"
1462               "        ID[\"EPSG\",9816]],\n"
1463               "    PARAMETER[\"Latitude of false origin\",1,\n"
1464               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1465               "        ID[\"EPSG\",8821]],\n"
1466               "    PARAMETER[\"Longitude of false origin\",2,\n"
1467               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1468               "        ID[\"EPSG\",8822]],\n"
1469               "    PARAMETER[\"Easting at false origin\",3,\n"
1470               "        LENGTHUNIT[\"metre\",1],\n"
1471               "        ID[\"EPSG\",8826]],\n"
1472               "    PARAMETER[\"Northing at false origin\",4,\n"
1473               "        LENGTHUNIT[\"metre\",1],\n"
1474               "        ID[\"EPSG\",8827]]]");
1475 
1476     EXPECT_EQ(
1477         conv->exportToWKT(
1478             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1479         "PROJECTION[\"Tunisia_Mapping_Grid\"],\n"
1480         "PARAMETER[\"latitude_of_origin\",1],\n"
1481         "PARAMETER[\"central_meridian\",2],\n"
1482         "PARAMETER[\"false_easting\",3],\n"
1483         "PARAMETER[\"false_northing\",4]");
1484 }
1485 
1486 // ---------------------------------------------------------------------------
1487 
TEST(operation,aea_export)1488 TEST(operation, aea_export) {
1489     auto conv = Conversion::createAlbersEqualArea(PropertyMap(), Angle(1),
1490                                                   Angle(2), Angle(3), Angle(4),
1491                                                   Length(5), Length(6));
1492     EXPECT_TRUE(conv->validateParameters().empty());
1493 
1494     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1495               "+proj=aea +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6");
1496 
1497     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1498               "CONVERSION[\"Albers Equal Area\",\n"
1499               "    METHOD[\"Albers Equal Area\",\n"
1500               "        ID[\"EPSG\",9822]],\n"
1501               "    PARAMETER[\"Latitude of false origin\",1,\n"
1502               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1503               "        ID[\"EPSG\",8821]],\n"
1504               "    PARAMETER[\"Longitude of false origin\",2,\n"
1505               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1506               "        ID[\"EPSG\",8822]],\n"
1507               "    PARAMETER[\"Latitude of 1st standard parallel\",3,\n"
1508               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1509               "        ID[\"EPSG\",8823]],\n"
1510               "    PARAMETER[\"Latitude of 2nd standard parallel\",4,\n"
1511               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1512               "        ID[\"EPSG\",8824]],\n"
1513               "    PARAMETER[\"Easting at false origin\",5,\n"
1514               "        LENGTHUNIT[\"metre\",1],\n"
1515               "        ID[\"EPSG\",8826]],\n"
1516               "    PARAMETER[\"Northing at false origin\",6,\n"
1517               "        LENGTHUNIT[\"metre\",1],\n"
1518               "        ID[\"EPSG\",8827]]]");
1519 
1520     EXPECT_EQ(
1521         conv->exportToWKT(
1522             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1523         "PROJECTION[\"Albers_Conic_Equal_Area\"],\n"
1524         "PARAMETER[\"latitude_of_center\",1],\n"
1525         "PARAMETER[\"longitude_of_center\",2],\n"
1526         "PARAMETER[\"standard_parallel_1\",3],\n"
1527         "PARAMETER[\"standard_parallel_2\",4],\n"
1528         "PARAMETER[\"false_easting\",5],\n"
1529         "PARAMETER[\"false_northing\",6]");
1530 }
1531 
1532 // ---------------------------------------------------------------------------
1533 
TEST(operation,azimuthal_equidistant_export)1534 TEST(operation, azimuthal_equidistant_export) {
1535     auto conv = Conversion::createAzimuthalEquidistant(
1536         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
1537     EXPECT_TRUE(conv->validateParameters().empty());
1538 
1539     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1540               "+proj=aeqd +lat_0=1 +lon_0=2 +x_0=3 +y_0=4");
1541 
1542     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1543               "CONVERSION[\"Modified Azimuthal Equidistant\",\n"
1544               "    METHOD[\"Modified Azimuthal Equidistant\",\n"
1545               "        ID[\"EPSG\",9832]],\n"
1546               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1547               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1548               "        ID[\"EPSG\",8801]],\n"
1549               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1550               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1551               "        ID[\"EPSG\",8802]],\n"
1552               "    PARAMETER[\"False easting\",3,\n"
1553               "        LENGTHUNIT[\"metre\",1],\n"
1554               "        ID[\"EPSG\",8806]],\n"
1555               "    PARAMETER[\"False northing\",4,\n"
1556               "        LENGTHUNIT[\"metre\",1],\n"
1557               "        ID[\"EPSG\",8807]]]");
1558 
1559     EXPECT_EQ(
1560         conv->exportToWKT(
1561             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1562         "PROJECTION[\"Azimuthal_Equidistant\"],\n"
1563         "PARAMETER[\"latitude_of_center\",1],\n"
1564         "PARAMETER[\"longitude_of_center\",2],\n"
1565         "PARAMETER[\"false_easting\",3],\n"
1566         "PARAMETER[\"false_northing\",4]");
1567 }
1568 
1569 // ---------------------------------------------------------------------------
1570 
TEST(operation,guam_projection_export)1571 TEST(operation, guam_projection_export) {
1572     auto conv = Conversion::createGuamProjection(
1573         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
1574     EXPECT_TRUE(conv->validateParameters().empty());
1575 
1576     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1577               "+proj=aeqd +guam +lat_0=1 +lon_0=2 +x_0=3 +y_0=4");
1578 
1579     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1580               "CONVERSION[\"Guam Projection\",\n"
1581               "    METHOD[\"Guam Projection\",\n"
1582               "        ID[\"EPSG\",9831]],\n"
1583               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1584               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1585               "        ID[\"EPSG\",8801]],\n"
1586               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1587               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1588               "        ID[\"EPSG\",8802]],\n"
1589               "    PARAMETER[\"False easting\",3,\n"
1590               "        LENGTHUNIT[\"metre\",1],\n"
1591               "        ID[\"EPSG\",8806]],\n"
1592               "    PARAMETER[\"False northing\",4,\n"
1593               "        LENGTHUNIT[\"metre\",1],\n"
1594               "        ID[\"EPSG\",8807]]]");
1595 
1596     EXPECT_THROW(
1597         conv->exportToWKT(
1598             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1599         FormattingException);
1600 }
1601 
1602 // ---------------------------------------------------------------------------
1603 
TEST(operation,bonne_export)1604 TEST(operation, bonne_export) {
1605     auto conv = Conversion::createBonne(PropertyMap(), Angle(1), Angle(2),
1606                                         Length(3), Length(4));
1607     EXPECT_TRUE(conv->validateParameters().empty());
1608 
1609     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1610               "+proj=bonne +lat_1=1 +lon_0=2 +x_0=3 +y_0=4");
1611 
1612     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1613               "CONVERSION[\"Bonne\",\n"
1614               "    METHOD[\"Bonne\",\n"
1615               "        ID[\"EPSG\",9827]],\n"
1616               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1617               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1618               "        ID[\"EPSG\",8801]],\n"
1619               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1620               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1621               "        ID[\"EPSG\",8802]],\n"
1622               "    PARAMETER[\"False easting\",3,\n"
1623               "        LENGTHUNIT[\"metre\",1],\n"
1624               "        ID[\"EPSG\",8806]],\n"
1625               "    PARAMETER[\"False northing\",4,\n"
1626               "        LENGTHUNIT[\"metre\",1],\n"
1627               "        ID[\"EPSG\",8807]]]");
1628 
1629     EXPECT_EQ(
1630         conv->exportToWKT(
1631             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1632         "PROJECTION[\"Bonne\"],\n"
1633         "PARAMETER[\"standard_parallel_1\",1],\n"
1634         "PARAMETER[\"central_meridian\",2],\n"
1635         "PARAMETER[\"false_easting\",3],\n"
1636         "PARAMETER[\"false_northing\",4]");
1637 
1638     auto obj = WKTParser().createFromWKT(
1639         "PROJCS[\"unnamed\","
1640         "GEOGCS[\"unnamed ellipse\","
1641         "    DATUM[\"unknown\","
1642         "        SPHEROID[\"unnamed\",6378137,298.257223563]],"
1643         "    PRIMEM[\"Greenwich\",0],"
1644         "    UNIT[\"degree\",0.0174532925199433]],"
1645         "PROJECTION[\"Bonne\"],"
1646         "PARAMETER[\"standard_parallel_1\",1],"
1647         "PARAMETER[\"central_meridian\",2],"
1648         "PARAMETER[\"false_easting\",3],"
1649         "PARAMETER[\"false_northing\",4],"
1650         "UNIT[\"metre\",1]]");
1651     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
1652     ASSERT_TRUE(crs != nullptr);
1653     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
1654               "+proj=bonne +lat_1=1 +lon_0=2 +x_0=3 +y_0=4 +ellps=WGS84 "
1655               "+units=m +no_defs +type=crs");
1656 }
1657 
1658 // ---------------------------------------------------------------------------
1659 
TEST(operation,lambert_cylindrical_equal_area_spherical_export)1660 TEST(operation, lambert_cylindrical_equal_area_spherical_export) {
1661     auto conv = Conversion::createLambertCylindricalEqualAreaSpherical(
1662         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
1663     EXPECT_TRUE(conv->validateParameters().empty());
1664 
1665     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1666               "+proj=cea +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4");
1667 
1668     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1669               "CONVERSION[\"Lambert Cylindrical Equal Area (Spherical)\",\n"
1670               "    METHOD[\"Lambert Cylindrical Equal Area (Spherical)\",\n"
1671               "        ID[\"EPSG\",9834]],\n"
1672               "    PARAMETER[\"Latitude of 1st standard parallel\",1,\n"
1673               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1674               "        ID[\"EPSG\",8823]],\n"
1675               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1676               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1677               "        ID[\"EPSG\",8802]],\n"
1678               "    PARAMETER[\"False easting\",3,\n"
1679               "        LENGTHUNIT[\"metre\",1],\n"
1680               "        ID[\"EPSG\",8806]],\n"
1681               "    PARAMETER[\"False northing\",4,\n"
1682               "        LENGTHUNIT[\"metre\",1],\n"
1683               "        ID[\"EPSG\",8807]]]");
1684 
1685     EXPECT_EQ(
1686         conv->exportToWKT(
1687             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1688         "PROJECTION[\"Cylindrical_Equal_Area\"],\n"
1689         "PARAMETER[\"standard_parallel_1\",1],\n"
1690         "PARAMETER[\"central_meridian\",2],\n"
1691         "PARAMETER[\"false_easting\",3],\n"
1692         "PARAMETER[\"false_northing\",4]");
1693 }
1694 
1695 // ---------------------------------------------------------------------------
1696 
TEST(operation,lambert_cylindrical_equal_area_export)1697 TEST(operation, lambert_cylindrical_equal_area_export) {
1698     auto conv = Conversion::createLambertCylindricalEqualArea(
1699         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
1700     EXPECT_TRUE(conv->validateParameters().empty());
1701 
1702     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1703               "+proj=cea +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4");
1704 
1705     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1706               "CONVERSION[\"Lambert Cylindrical Equal Area\",\n"
1707               "    METHOD[\"Lambert Cylindrical Equal Area\",\n"
1708               "        ID[\"EPSG\",9835]],\n"
1709               "    PARAMETER[\"Latitude of 1st standard parallel\",1,\n"
1710               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1711               "        ID[\"EPSG\",8823]],\n"
1712               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1713               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1714               "        ID[\"EPSG\",8802]],\n"
1715               "    PARAMETER[\"False easting\",3,\n"
1716               "        LENGTHUNIT[\"metre\",1],\n"
1717               "        ID[\"EPSG\",8806]],\n"
1718               "    PARAMETER[\"False northing\",4,\n"
1719               "        LENGTHUNIT[\"metre\",1],\n"
1720               "        ID[\"EPSG\",8807]]]");
1721 
1722     EXPECT_EQ(
1723         conv->exportToWKT(
1724             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1725         "PROJECTION[\"Cylindrical_Equal_Area\"],\n"
1726         "PARAMETER[\"standard_parallel_1\",1],\n"
1727         "PARAMETER[\"central_meridian\",2],\n"
1728         "PARAMETER[\"false_easting\",3],\n"
1729         "PARAMETER[\"false_northing\",4]");
1730 }
1731 
1732 // ---------------------------------------------------------------------------
1733 
TEST(operation,lcc1sp_export)1734 TEST(operation, lcc1sp_export) {
1735     auto conv = Conversion::createLambertConicConformal_1SP(
1736         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
1737     EXPECT_TRUE(conv->validateParameters().empty());
1738 
1739     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1740               "+proj=lcc +lat_1=1 +lat_0=1 +lon_0=2 +k_0=3 +x_0=4 +y_0=5");
1741 
1742     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1743               "CONVERSION[\"Lambert Conic Conformal (1SP)\",\n"
1744               "    METHOD[\"Lambert Conic Conformal (1SP)\",\n"
1745               "        ID[\"EPSG\",9801]],\n"
1746               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1747               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1748               "        ID[\"EPSG\",8801]],\n"
1749               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1750               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1751               "        ID[\"EPSG\",8802]],\n"
1752               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
1753               "        SCALEUNIT[\"unity\",1],\n"
1754               "        ID[\"EPSG\",8805]],\n"
1755               "    PARAMETER[\"False easting\",4,\n"
1756               "        LENGTHUNIT[\"metre\",1],\n"
1757               "        ID[\"EPSG\",8806]],\n"
1758               "    PARAMETER[\"False northing\",5,\n"
1759               "        LENGTHUNIT[\"metre\",1],\n"
1760               "        ID[\"EPSG\",8807]]]");
1761 
1762     EXPECT_EQ(
1763         conv->exportToWKT(
1764             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1765         "PROJECTION[\"Lambert_Conformal_Conic_1SP\"],\n"
1766         "PARAMETER[\"latitude_of_origin\",1],\n"
1767         "PARAMETER[\"central_meridian\",2],\n"
1768         "PARAMETER[\"scale_factor\",3],\n"
1769         "PARAMETER[\"false_easting\",4],\n"
1770         "PARAMETER[\"false_northing\",5]");
1771 }
1772 
1773 // ---------------------------------------------------------------------------
1774 
TEST(operation,lcc2sp_export)1775 TEST(operation, lcc2sp_export) {
1776     auto conv = Conversion::createLambertConicConformal_2SP(
1777         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5),
1778         Length(6));
1779     EXPECT_TRUE(conv->validateParameters().empty());
1780 
1781     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1782               "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6");
1783 
1784     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1785               "CONVERSION[\"Lambert Conic Conformal (2SP)\",\n"
1786               "    METHOD[\"Lambert Conic Conformal (2SP)\",\n"
1787               "        ID[\"EPSG\",9802]],\n"
1788               "    PARAMETER[\"Latitude of false origin\",1,\n"
1789               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1790               "        ID[\"EPSG\",8821]],\n"
1791               "    PARAMETER[\"Longitude of false origin\",2,\n"
1792               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1793               "        ID[\"EPSG\",8822]],\n"
1794               "    PARAMETER[\"Latitude of 1st standard parallel\",3,\n"
1795               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1796               "        ID[\"EPSG\",8823]],\n"
1797               "    PARAMETER[\"Latitude of 2nd standard parallel\",4,\n"
1798               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1799               "        ID[\"EPSG\",8824]],\n"
1800               "    PARAMETER[\"Easting at false origin\",5,\n"
1801               "        LENGTHUNIT[\"metre\",1],\n"
1802               "        ID[\"EPSG\",8826]],\n"
1803               "    PARAMETER[\"Northing at false origin\",6,\n"
1804               "        LENGTHUNIT[\"metre\",1],\n"
1805               "        ID[\"EPSG\",8827]]]");
1806 
1807     EXPECT_EQ(
1808         conv->exportToWKT(
1809             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1810         "PROJECTION[\"Lambert_Conformal_Conic_2SP\"],\n"
1811         "PARAMETER[\"latitude_of_origin\",1],\n"
1812         "PARAMETER[\"central_meridian\",2],\n"
1813         "PARAMETER[\"standard_parallel_1\",3],\n"
1814         "PARAMETER[\"standard_parallel_2\",4],\n"
1815         "PARAMETER[\"false_easting\",5],\n"
1816         "PARAMETER[\"false_northing\",6]");
1817 }
1818 
1819 // ---------------------------------------------------------------------------
1820 
TEST(operation,lcc2sp_isEquivalentTo_parallels_switched)1821 TEST(operation, lcc2sp_isEquivalentTo_parallels_switched) {
1822     auto conv1 = Conversion::createLambertConicConformal_2SP(
1823         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5),
1824         Length(6));
1825     auto conv2 = Conversion::createLambertConicConformal_2SP(
1826         PropertyMap(), Angle(1), Angle(2), Angle(4), Angle(3), Length(5),
1827         Length(6));
1828 
1829     EXPECT_TRUE(
1830         conv1->isEquivalentTo(conv2.get(), IComparable::Criterion::EQUIVALENT));
1831 
1832     auto conv3 = Conversion::createLambertConicConformal_2SP(
1833         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(3), Length(5),
1834         Length(6));
1835 
1836     EXPECT_FALSE(
1837         conv1->isEquivalentTo(conv3.get(), IComparable::Criterion::EQUIVALENT));
1838 }
1839 
1840 // ---------------------------------------------------------------------------
1841 
TEST(operation,lcc2sp_michigan_export)1842 TEST(operation, lcc2sp_michigan_export) {
1843     auto conv = Conversion::createLambertConicConformal_2SP_Michigan(
1844         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5),
1845         Length(6), Scale(7));
1846     EXPECT_TRUE(conv->validateParameters().empty());
1847 
1848     EXPECT_EQ(
1849         conv->exportToPROJString(PROJStringFormatter::create().get()),
1850         "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6 +k_0=7");
1851 
1852     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1853               "CONVERSION[\"Lambert Conic Conformal (2SP Michigan)\",\n"
1854               "    METHOD[\"Lambert Conic Conformal (2SP Michigan)\",\n"
1855               "        ID[\"EPSG\",1051]],\n"
1856               "    PARAMETER[\"Latitude of false origin\",1,\n"
1857               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1858               "        ID[\"EPSG\",8821]],\n"
1859               "    PARAMETER[\"Longitude of false origin\",2,\n"
1860               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1861               "        ID[\"EPSG\",8822]],\n"
1862               "    PARAMETER[\"Latitude of 1st standard parallel\",3,\n"
1863               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1864               "        ID[\"EPSG\",8823]],\n"
1865               "    PARAMETER[\"Latitude of 2nd standard parallel\",4,\n"
1866               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1867               "        ID[\"EPSG\",8824]],\n"
1868               "    PARAMETER[\"Easting at false origin\",5,\n"
1869               "        LENGTHUNIT[\"metre\",1],\n"
1870               "        ID[\"EPSG\",8826]],\n"
1871               "    PARAMETER[\"Northing at false origin\",6,\n"
1872               "        LENGTHUNIT[\"metre\",1],\n"
1873               "        ID[\"EPSG\",8827]],\n"
1874               "    PARAMETER[\"Ellipsoid scaling factor\",7,\n"
1875               "        SCALEUNIT[\"unity\",1],\n"
1876               "        ID[\"EPSG\",1038]]]");
1877 
1878     EXPECT_THROW(
1879         conv->exportToWKT(
1880             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1881         FormattingException);
1882 }
1883 
1884 // ---------------------------------------------------------------------------
1885 
TEST(operation,lcc2sp_belgium_export)1886 TEST(operation, lcc2sp_belgium_export) {
1887     auto conv = Conversion::createLambertConicConformal_2SP_Belgium(
1888         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Length(5),
1889         Length(6));
1890     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1891               "+proj=lcc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6");
1892 
1893     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1894               "CONVERSION[\"Lambert Conic Conformal (2SP Belgium)\",\n"
1895               "    METHOD[\"Lambert Conic Conformal (2SP Belgium)\",\n"
1896               "        ID[\"EPSG\",9803]],\n"
1897               "    PARAMETER[\"Latitude of false origin\",1,\n"
1898               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1899               "        ID[\"EPSG\",8821]],\n"
1900               "    PARAMETER[\"Longitude of false origin\",2,\n"
1901               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1902               "        ID[\"EPSG\",8822]],\n"
1903               "    PARAMETER[\"Latitude of 1st standard parallel\",3,\n"
1904               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1905               "        ID[\"EPSG\",8823]],\n"
1906               "    PARAMETER[\"Latitude of 2nd standard parallel\",4,\n"
1907               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1908               "        ID[\"EPSG\",8824]],\n"
1909               "    PARAMETER[\"Easting at false origin\",5,\n"
1910               "        LENGTHUNIT[\"metre\",1],\n"
1911               "        ID[\"EPSG\",8826]],\n"
1912               "    PARAMETER[\"Northing at false origin\",6,\n"
1913               "        LENGTHUNIT[\"metre\",1],\n"
1914               "        ID[\"EPSG\",8827]]]");
1915 
1916     EXPECT_EQ(
1917         conv->exportToWKT(
1918             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1919         "PROJECTION[\"Lambert_Conformal_Conic_2SP_Belgium\"],\n"
1920         "PARAMETER[\"latitude_of_origin\",1],\n"
1921         "PARAMETER[\"central_meridian\",2],\n"
1922         "PARAMETER[\"standard_parallel_1\",3],\n"
1923         "PARAMETER[\"standard_parallel_2\",4],\n"
1924         "PARAMETER[\"false_easting\",5],\n"
1925         "PARAMETER[\"false_northing\",6]");
1926 }
1927 
1928 // ---------------------------------------------------------------------------
1929 
TEST(operation,cassini_soldner_export)1930 TEST(operation, cassini_soldner_export) {
1931     auto conv = Conversion::createCassiniSoldner(
1932         PropertyMap(), Angle(1), Angle(2), Length(4), Length(5));
1933     EXPECT_TRUE(conv->validateParameters().empty());
1934 
1935     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1936               "+proj=cass +lat_0=1 +lon_0=2 +x_0=4 +y_0=5");
1937 
1938     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1939               "CONVERSION[\"Cassini-Soldner\",\n"
1940               "    METHOD[\"Cassini-Soldner\",\n"
1941               "        ID[\"EPSG\",9806]],\n"
1942               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1943               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1944               "        ID[\"EPSG\",8801]],\n"
1945               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1946               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1947               "        ID[\"EPSG\",8802]],\n"
1948               "    PARAMETER[\"False easting\",4,\n"
1949               "        LENGTHUNIT[\"metre\",1],\n"
1950               "        ID[\"EPSG\",8806]],\n"
1951               "    PARAMETER[\"False northing\",5,\n"
1952               "        LENGTHUNIT[\"metre\",1],\n"
1953               "        ID[\"EPSG\",8807]]]");
1954 
1955     EXPECT_EQ(
1956         conv->exportToWKT(
1957             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
1958         "PROJECTION[\"Cassini_Soldner\"],\n"
1959         "PARAMETER[\"latitude_of_origin\",1],\n"
1960         "PARAMETER[\"central_meridian\",2],\n"
1961         "PARAMETER[\"false_easting\",4],\n"
1962         "PARAMETER[\"false_northing\",5]");
1963 }
1964 
1965 // ---------------------------------------------------------------------------
1966 
TEST(operation,equidistant_conic_export)1967 TEST(operation, equidistant_conic_export) {
1968     auto conv = Conversion::createEquidistantConic(PropertyMap(), Angle(1),
1969                                                    Angle(2), Angle(3), Angle(4),
1970                                                    Length(5), Length(6));
1971     EXPECT_TRUE(conv->validateParameters().empty());
1972 
1973     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
1974               "+proj=eqdc +lat_0=1 +lon_0=2 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6");
1975 
1976     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
1977               "CONVERSION[\"Equidistant Conic\",\n"
1978               "    METHOD[\"Equidistant Conic\"],\n"
1979               "    PARAMETER[\"Latitude of natural origin\",1,\n"
1980               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1981               "        ID[\"EPSG\",8801]],\n"
1982               "    PARAMETER[\"Longitude of natural origin\",2,\n"
1983               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1984               "        ID[\"EPSG\",8802]],\n"
1985               "    PARAMETER[\"Latitude of 1st standard parallel\",3,\n"
1986               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1987               "        ID[\"EPSG\",8823]],\n"
1988               "    PARAMETER[\"Latitude of 2nd standard parallel\",4,\n"
1989               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
1990               "        ID[\"EPSG\",8824]],\n"
1991               "    PARAMETER[\"False easting\",5,\n"
1992               "        LENGTHUNIT[\"metre\",1],\n"
1993               "        ID[\"EPSG\",8806]],\n"
1994               "    PARAMETER[\"False northing\",6,\n"
1995               "        LENGTHUNIT[\"metre\",1],\n"
1996               "        ID[\"EPSG\",8807]]]");
1997 
1998     EXPECT_EQ(
1999         conv->exportToWKT(
2000             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2001         "PROJECTION[\"Equidistant_Conic\"],\n"
2002         "PARAMETER[\"latitude_of_center\",1],\n"
2003         "PARAMETER[\"longitude_of_center\",2],\n"
2004         "PARAMETER[\"standard_parallel_1\",3],\n"
2005         "PARAMETER[\"standard_parallel_2\",4],\n"
2006         "PARAMETER[\"false_easting\",5],\n"
2007         "PARAMETER[\"false_northing\",6]");
2008 }
2009 
2010 // ---------------------------------------------------------------------------
2011 
TEST(operation,eckert_export)2012 TEST(operation, eckert_export) {
2013 
2014     std::vector<std::string> numbers{"", "1", "2", "3", "4", "5", "6"};
2015     std::vector<std::string> latinNumbers{"",   "I", "II", "III",
2016                                           "IV", "V", "VI"};
2017 
2018     for (int i = 1; i <= 6; i++) {
2019         auto conv =
2020             (i == 1)
2021                 ? Conversion::createEckertI(PropertyMap(), Angle(1), Length(2),
2022                                             Length(3))
2023                 : (i == 2)
2024                       ? Conversion::createEckertII(PropertyMap(), Angle(1),
2025                                                    Length(2), Length(3))
2026                       : (i == 3)
2027                             ? Conversion::createEckertIII(
2028                                   PropertyMap(), Angle(1), Length(2), Length(3))
2029                             : (i == 4) ? Conversion::createEckertIV(
2030                                              PropertyMap(), Angle(1), Length(2),
2031                                              Length(3))
2032                                        : (i == 5) ? Conversion::createEckertV(
2033                                                         PropertyMap(), Angle(1),
2034                                                         Length(2), Length(3))
2035                                                   :
2036 
2037                                                   Conversion::createEckertVI(
2038                                                       PropertyMap(), Angle(1),
2039                                                       Length(2), Length(3));
2040 
2041         EXPECT_TRUE(conv->validateParameters().empty());
2042 
2043         EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2044                   "+proj=eck" + numbers[i] + " +lon_0=1 +x_0=2 +y_0=3");
2045 
2046         EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2047                   "CONVERSION[\"Eckert " + latinNumbers[i] +
2048                       "\",\n"
2049                       "    METHOD[\"Eckert " +
2050                       latinNumbers[i] +
2051                       "\"],\n"
2052                       "    PARAMETER[\"Longitude of natural origin\",1,\n"
2053                       "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2054                       "        ID[\"EPSG\",8802]],\n"
2055                       "    PARAMETER[\"False easting\",2,\n"
2056                       "        LENGTHUNIT[\"metre\",1],\n"
2057                       "        ID[\"EPSG\",8806]],\n"
2058                       "    PARAMETER[\"False northing\",3,\n"
2059                       "        LENGTHUNIT[\"metre\",1],\n"
2060                       "        ID[\"EPSG\",8807]]]");
2061 
2062         EXPECT_EQ(conv->exportToWKT(
2063                       WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL)
2064                           .get()),
2065                   "PROJECTION[\"Eckert_" + latinNumbers[i] +
2066                       "\"],\n"
2067                       "PARAMETER[\"central_meridian\",1],\n"
2068                       "PARAMETER[\"false_easting\",2],\n"
2069                       "PARAMETER[\"false_northing\",3]");
2070     }
2071 }
2072 
2073 // ---------------------------------------------------------------------------
2074 
TEST(operation,createEquidistantCylindrical)2075 TEST(operation, createEquidistantCylindrical) {
2076     auto conv = Conversion::createEquidistantCylindrical(
2077         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
2078     EXPECT_TRUE(conv->validateParameters().empty());
2079 
2080     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2081               "+proj=eqc +lat_ts=1 +lat_0=0 +lon_0=2 +x_0=3 +y_0=4");
2082 
2083     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2084               "CONVERSION[\"Equidistant Cylindrical\",\n"
2085               "    METHOD[\"Equidistant Cylindrical\",\n"
2086               "        ID[\"EPSG\",1028]],\n"
2087               "    PARAMETER[\"Latitude of 1st standard parallel\",1,\n"
2088               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2089               "        ID[\"EPSG\",8823]],\n"
2090               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2091               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2092               "        ID[\"EPSG\",8802]],\n"
2093               "    PARAMETER[\"False easting\",3,\n"
2094               "        LENGTHUNIT[\"metre\",1],\n"
2095               "        ID[\"EPSG\",8806]],\n"
2096               "    PARAMETER[\"False northing\",4,\n"
2097               "        LENGTHUNIT[\"metre\",1],\n"
2098               "        ID[\"EPSG\",8807]]]");
2099 
2100     EXPECT_EQ(
2101         conv->exportToWKT(
2102             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2103         "PROJECTION[\"Equirectangular\"],\n"
2104         "PARAMETER[\"standard_parallel_1\",1],\n"
2105         "PARAMETER[\"central_meridian\",2],\n"
2106         "PARAMETER[\"false_easting\",3],\n"
2107         "PARAMETER[\"false_northing\",4]");
2108 
2109     EXPECT_TRUE(conv->validateParameters().empty());
2110 }
2111 
2112 // ---------------------------------------------------------------------------
2113 
TEST(operation,createEquidistantCylindricalSpherical)2114 TEST(operation, createEquidistantCylindricalSpherical) {
2115     auto conv = Conversion::createEquidistantCylindricalSpherical(
2116         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
2117     EXPECT_TRUE(conv->validateParameters().empty());
2118 
2119     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2120               "+proj=eqc +lat_ts=1 +lat_0=0 +lon_0=2 +x_0=3 +y_0=4");
2121 
2122     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2123               "CONVERSION[\"Equidistant Cylindrical (Spherical)\",\n"
2124               "    METHOD[\"Equidistant Cylindrical (Spherical)\",\n"
2125               "        ID[\"EPSG\",1029]],\n"
2126               "    PARAMETER[\"Latitude of 1st standard parallel\",1,\n"
2127               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2128               "        ID[\"EPSG\",8823]],\n"
2129               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2130               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2131               "        ID[\"EPSG\",8802]],\n"
2132               "    PARAMETER[\"False easting\",3,\n"
2133               "        LENGTHUNIT[\"metre\",1],\n"
2134               "        ID[\"EPSG\",8806]],\n"
2135               "    PARAMETER[\"False northing\",4,\n"
2136               "        LENGTHUNIT[\"metre\",1],\n"
2137               "        ID[\"EPSG\",8807]]]");
2138 
2139     EXPECT_EQ(
2140         conv->exportToWKT(
2141             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2142         "PROJECTION[\"Equirectangular\"],\n"
2143         "PARAMETER[\"standard_parallel_1\",1],\n"
2144         "PARAMETER[\"central_meridian\",2],\n"
2145         "PARAMETER[\"false_easting\",3],\n"
2146         "PARAMETER[\"false_northing\",4]");
2147 
2148     EXPECT_TRUE(conv->validateParameters().empty());
2149 }
2150 
2151 // ---------------------------------------------------------------------------
2152 
TEST(operation,equidistant_cylindrical_lat_0)2153 TEST(operation, equidistant_cylindrical_lat_0) {
2154 
2155     auto obj = PROJStringParser().createFromPROJString(
2156         "+proj=eqc +ellps=sphere +lat_0=-2 +lat_ts=1 +lon_0=-10 +type=crs");
2157     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
2158     ASSERT_TRUE(crs != nullptr);
2159 
2160     auto wkt = crs->exportToWKT(WKTFormatter::create().get());
2161     EXPECT_TRUE(wkt.find("PARAMETER[\"Latitude of natural origin\",-2") !=
2162                 std::string::npos)
2163         << wkt;
2164 
2165     auto projString = crs->exportToPROJString(
2166         PROJStringFormatter::create(PROJStringFormatter::Convention::PROJ_4)
2167             .get());
2168     EXPECT_EQ(projString,
2169               "+proj=eqc +lat_ts=1 +lat_0=-2 +lon_0=-10 +x_0=0 +y_0=0 "
2170               "+ellps=sphere +units=m +no_defs +type=crs");
2171 }
2172 
2173 // ---------------------------------------------------------------------------
2174 
TEST(operation,gall_export)2175 TEST(operation, gall_export) {
2176 
2177     auto conv =
2178         Conversion::createGall(PropertyMap(), Angle(1), Length(2), Length(3));
2179     EXPECT_TRUE(conv->validateParameters().empty());
2180 
2181     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2182               "+proj=gall +lon_0=1 +x_0=2 +y_0=3");
2183 
2184     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2185               "CONVERSION[\"Gall Stereographic\",\n"
2186               "    METHOD[\"Gall Stereographic\"],\n"
2187               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2188               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2189               "        ID[\"EPSG\",8802]],\n"
2190               "    PARAMETER[\"False easting\",2,\n"
2191               "        LENGTHUNIT[\"metre\",1],\n"
2192               "        ID[\"EPSG\",8806]],\n"
2193               "    PARAMETER[\"False northing\",3,\n"
2194               "        LENGTHUNIT[\"metre\",1],\n"
2195               "        ID[\"EPSG\",8807]]]");
2196 
2197     EXPECT_EQ(
2198         conv->exportToWKT(
2199             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2200         "PROJECTION[\"Gall_Stereographic\"],\n"
2201         "PARAMETER[\"central_meridian\",1],\n"
2202         "PARAMETER[\"false_easting\",2],\n"
2203         "PARAMETER[\"false_northing\",3]");
2204 }
2205 
2206 // ---------------------------------------------------------------------------
2207 
TEST(operation,goode_homolosine_export)2208 TEST(operation, goode_homolosine_export) {
2209 
2210     auto conv = Conversion::createGoodeHomolosine(PropertyMap(), Angle(1),
2211                                                   Length(2), Length(3));
2212     EXPECT_TRUE(conv->validateParameters().empty());
2213 
2214     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2215               "+proj=goode +lon_0=1 +x_0=2 +y_0=3");
2216 
2217     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2218               "CONVERSION[\"Goode Homolosine\",\n"
2219               "    METHOD[\"Goode Homolosine\"],\n"
2220               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2221               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2222               "        ID[\"EPSG\",8802]],\n"
2223               "    PARAMETER[\"False easting\",2,\n"
2224               "        LENGTHUNIT[\"metre\",1],\n"
2225               "        ID[\"EPSG\",8806]],\n"
2226               "    PARAMETER[\"False northing\",3,\n"
2227               "        LENGTHUNIT[\"metre\",1],\n"
2228               "        ID[\"EPSG\",8807]]]");
2229 
2230     EXPECT_EQ(
2231         conv->exportToWKT(
2232             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2233         "PROJECTION[\"Goode_Homolosine\"],\n"
2234         "PARAMETER[\"central_meridian\",1],\n"
2235         "PARAMETER[\"false_easting\",2],\n"
2236         "PARAMETER[\"false_northing\",3]");
2237 }
2238 
2239 // ---------------------------------------------------------------------------
2240 
TEST(operation,interrupted_goode_homolosine_export)2241 TEST(operation, interrupted_goode_homolosine_export) {
2242 
2243     auto conv = Conversion::createInterruptedGoodeHomolosine(
2244         PropertyMap(), Angle(1), Length(2), Length(3));
2245     EXPECT_TRUE(conv->validateParameters().empty());
2246 
2247     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2248               "+proj=igh +lon_0=1 +x_0=2 +y_0=3");
2249 
2250     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2251               "CONVERSION[\"Interrupted Goode Homolosine\",\n"
2252               "    METHOD[\"Interrupted Goode Homolosine\"],\n"
2253               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2254               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2255               "        ID[\"EPSG\",8802]],\n"
2256               "    PARAMETER[\"False easting\",2,\n"
2257               "        LENGTHUNIT[\"metre\",1],\n"
2258               "        ID[\"EPSG\",8806]],\n"
2259               "    PARAMETER[\"False northing\",3,\n"
2260               "        LENGTHUNIT[\"metre\",1],\n"
2261               "        ID[\"EPSG\",8807]]]");
2262 
2263     EXPECT_EQ(
2264         conv->exportToWKT(
2265             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2266         "PROJECTION[\"Interrupted_Goode_Homolosine\"],\n"
2267         "PARAMETER[\"central_meridian\",1],\n"
2268         "PARAMETER[\"false_easting\",2],\n"
2269         "PARAMETER[\"false_northing\",3]");
2270 }
2271 
2272 // ---------------------------------------------------------------------------
2273 
TEST(operation,geostationary_satellite_sweep_x_export)2274 TEST(operation, geostationary_satellite_sweep_x_export) {
2275 
2276     auto conv = Conversion::createGeostationarySatelliteSweepX(
2277         PropertyMap(), Angle(1), Length(2), Length(3), Length(4));
2278     EXPECT_TRUE(conv->validateParameters().empty());
2279 
2280     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2281               "+proj=geos +sweep=x +lon_0=1 +h=2 +x_0=3 +y_0=4");
2282 
2283     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2284               "CONVERSION[\"Geostationary Satellite (Sweep X)\",\n"
2285               "    METHOD[\"Geostationary Satellite (Sweep X)\"],\n"
2286               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2287               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2288               "        ID[\"EPSG\",8802]],\n"
2289               "    PARAMETER[\"Satellite Height\",2,\n"
2290               "        LENGTHUNIT[\"metre\",1,\n"
2291               "            ID[\"EPSG\",9001]]],\n"
2292               "    PARAMETER[\"False easting\",3,\n"
2293               "        LENGTHUNIT[\"metre\",1],\n"
2294               "        ID[\"EPSG\",8806]],\n"
2295               "    PARAMETER[\"False northing\",4,\n"
2296               "        LENGTHUNIT[\"metre\",1],\n"
2297               "        ID[\"EPSG\",8807]]]");
2298 
2299     auto crs = ProjectedCRS::create(
2300         PropertyMap(), GeographicCRS::EPSG_4326, conv,
2301         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
2302     auto wkt1 = crs->exportToWKT(
2303         WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get());
2304     EXPECT_TRUE(wkt1.find("PROJECTION[\"Geostationary_Satellite\"]") !=
2305                 std::string::npos)
2306         << wkt1;
2307     EXPECT_TRUE(wkt1.find("EXTENSION[\"PROJ4\",\"+proj=geos +sweep=x +lon_0=1 "
2308                           "+h=2 +x_0=3 +y_0=4 +datum=WGS84 +units=m "
2309                           "+no_defs\"]]") != std::string::npos)
2310         << wkt1;
2311 }
2312 
2313 // ---------------------------------------------------------------------------
2314 
TEST(operation,geostationary_satellite_sweep_y_export)2315 TEST(operation, geostationary_satellite_sweep_y_export) {
2316 
2317     auto conv = Conversion::createGeostationarySatelliteSweepY(
2318         PropertyMap(), Angle(1), Length(2), Length(3), Length(4));
2319     EXPECT_TRUE(conv->validateParameters().empty());
2320 
2321     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2322               "+proj=geos +lon_0=1 +h=2 +x_0=3 +y_0=4");
2323 
2324     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2325               "CONVERSION[\"Geostationary Satellite (Sweep Y)\",\n"
2326               "    METHOD[\"Geostationary Satellite (Sweep Y)\"],\n"
2327               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2328               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2329               "        ID[\"EPSG\",8802]],\n"
2330               "    PARAMETER[\"Satellite Height\",2,\n"
2331               "        LENGTHUNIT[\"metre\",1,\n"
2332               "            ID[\"EPSG\",9001]]],\n"
2333               "    PARAMETER[\"False easting\",3,\n"
2334               "        LENGTHUNIT[\"metre\",1],\n"
2335               "        ID[\"EPSG\",8806]],\n"
2336               "    PARAMETER[\"False northing\",4,\n"
2337               "        LENGTHUNIT[\"metre\",1],\n"
2338               "        ID[\"EPSG\",8807]]]");
2339 
2340     EXPECT_EQ(
2341         conv->exportToWKT(
2342             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2343         "PROJECTION[\"Geostationary_Satellite\"],\n"
2344         "PARAMETER[\"central_meridian\",1],\n"
2345         "PARAMETER[\"satellite_height\",2],\n"
2346         "PARAMETER[\"false_easting\",3],\n"
2347         "PARAMETER[\"false_northing\",4]");
2348 }
2349 
2350 // ---------------------------------------------------------------------------
2351 
TEST(operation,gnomonic_export)2352 TEST(operation, gnomonic_export) {
2353     auto conv = Conversion::createGnomonic(PropertyMap(), Angle(1), Angle(2),
2354                                            Length(4), Length(5));
2355     EXPECT_TRUE(conv->validateParameters().empty());
2356 
2357     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2358               "+proj=gnom +lat_0=1 +lon_0=2 +x_0=4 +y_0=5");
2359 
2360     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2361               "CONVERSION[\"Gnomonic\",\n"
2362               "    METHOD[\"Gnomonic\"],\n"
2363               "    PARAMETER[\"Latitude of natural origin\",1,\n"
2364               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2365               "        ID[\"EPSG\",8801]],\n"
2366               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2367               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2368               "        ID[\"EPSG\",8802]],\n"
2369               "    PARAMETER[\"False easting\",4,\n"
2370               "        LENGTHUNIT[\"metre\",1],\n"
2371               "        ID[\"EPSG\",8806]],\n"
2372               "    PARAMETER[\"False northing\",5,\n"
2373               "        LENGTHUNIT[\"metre\",1],\n"
2374               "        ID[\"EPSG\",8807]]]");
2375 
2376     EXPECT_EQ(
2377         conv->exportToWKT(
2378             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2379         "PROJECTION[\"Gnomonic\"],\n"
2380         "PARAMETER[\"latitude_of_origin\",1],\n"
2381         "PARAMETER[\"central_meridian\",2],\n"
2382         "PARAMETER[\"false_easting\",4],\n"
2383         "PARAMETER[\"false_northing\",5]");
2384 }
2385 
2386 // ---------------------------------------------------------------------------
2387 
TEST(operation,hotine_oblique_mercator_variant_A_export)2388 TEST(operation, hotine_oblique_mercator_variant_A_export) {
2389     auto conv = Conversion::createHotineObliqueMercatorVariantA(
2390         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Scale(5),
2391         Length(6), Length(7));
2392     EXPECT_TRUE(conv->validateParameters().empty());
2393 
2394     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2395               "+proj=omerc +no_uoff +lat_0=1 +lonc=2 +alpha=3 +gamma=4 +k=5 "
2396               "+x_0=6 +y_0=7");
2397 
2398     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2399               "CONVERSION[\"Hotine Oblique Mercator (variant A)\",\n"
2400               "    METHOD[\"Hotine Oblique Mercator (variant A)\",\n"
2401               "        ID[\"EPSG\",9812]],\n"
2402               "    PARAMETER[\"Latitude of projection centre\",1,\n"
2403               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2404               "        ID[\"EPSG\",8811]],\n"
2405               "    PARAMETER[\"Longitude of projection centre\",2,\n"
2406               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2407               "        ID[\"EPSG\",8812]],\n"
2408               "    PARAMETER[\"Azimuth of initial line\",3,\n"
2409               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2410               "        ID[\"EPSG\",8813]],\n"
2411               "    PARAMETER[\"Angle from Rectified to Skew Grid\",4,\n"
2412               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2413               "        ID[\"EPSG\",8814]],\n"
2414               "    PARAMETER[\"Scale factor on initial line\",5,\n"
2415               "        SCALEUNIT[\"unity\",1],\n"
2416               "        ID[\"EPSG\",8815]],\n"
2417               "    PARAMETER[\"False easting\",6,\n"
2418               "        LENGTHUNIT[\"metre\",1],\n"
2419               "        ID[\"EPSG\",8806]],\n"
2420               "    PARAMETER[\"False northing\",7,\n"
2421               "        LENGTHUNIT[\"metre\",1],\n"
2422               "        ID[\"EPSG\",8807]]]");
2423 
2424     EXPECT_EQ(
2425         conv->exportToWKT(
2426             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2427         "PROJECTION[\"Hotine_Oblique_Mercator\"],\n"
2428         "PARAMETER[\"latitude_of_center\",1],\n"
2429         "PARAMETER[\"longitude_of_center\",2],\n"
2430         "PARAMETER[\"azimuth\",3],\n"
2431         "PARAMETER[\"rectified_grid_angle\",4],\n"
2432         "PARAMETER[\"scale_factor\",5],\n"
2433         "PARAMETER[\"false_easting\",6],\n"
2434         "PARAMETER[\"false_northing\",7]");
2435 }
2436 
2437 // ---------------------------------------------------------------------------
2438 
TEST(operation,hotine_oblique_mercator_variant_A_export_swiss_mercator)2439 TEST(operation, hotine_oblique_mercator_variant_A_export_swiss_mercator) {
2440     auto conv = Conversion::createHotineObliqueMercatorVariantA(
2441         PropertyMap(), Angle(1), Angle(2), Angle(90), Angle(90), Scale(5),
2442         Length(6), Length(7));
2443     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2444               "+proj=somerc +lat_0=1 +lon_0=2 +k_0=5 "
2445               "+x_0=6 +y_0=7");
2446 }
2447 
2448 // ---------------------------------------------------------------------------
2449 
TEST(operation,hotine_oblique_mercator_variant_B_export)2450 TEST(operation, hotine_oblique_mercator_variant_B_export) {
2451     auto conv = Conversion::createHotineObliqueMercatorVariantB(
2452         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Scale(5),
2453         Length(6), Length(7));
2454     EXPECT_TRUE(conv->validateParameters().empty());
2455 
2456     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2457               "+proj=omerc +lat_0=1 +lonc=2 +alpha=3 +gamma=4 +k=5 "
2458               "+x_0=6 +y_0=7");
2459 
2460     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2461               "CONVERSION[\"Hotine Oblique Mercator (variant B)\",\n"
2462               "    METHOD[\"Hotine Oblique Mercator (variant B)\",\n"
2463               "        ID[\"EPSG\",9815]],\n"
2464               "    PARAMETER[\"Latitude of projection centre\",1,\n"
2465               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2466               "        ID[\"EPSG\",8811]],\n"
2467               "    PARAMETER[\"Longitude of projection centre\",2,\n"
2468               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2469               "        ID[\"EPSG\",8812]],\n"
2470               "    PARAMETER[\"Azimuth of initial line\",3,\n"
2471               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2472               "        ID[\"EPSG\",8813]],\n"
2473               "    PARAMETER[\"Angle from Rectified to Skew Grid\",4,\n"
2474               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2475               "        ID[\"EPSG\",8814]],\n"
2476               "    PARAMETER[\"Scale factor on initial line\",5,\n"
2477               "        SCALEUNIT[\"unity\",1],\n"
2478               "        ID[\"EPSG\",8815]],\n"
2479               "    PARAMETER[\"Easting at projection centre\",6,\n"
2480               "        LENGTHUNIT[\"metre\",1],\n"
2481               "        ID[\"EPSG\",8816]],\n"
2482               "    PARAMETER[\"Northing at projection centre\",7,\n"
2483               "        LENGTHUNIT[\"metre\",1],\n"
2484               "        ID[\"EPSG\",8817]]]");
2485 
2486     EXPECT_EQ(
2487         conv->exportToWKT(
2488             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2489         "PROJECTION[\"Hotine_Oblique_Mercator_Azimuth_Center\"],\n"
2490         "PARAMETER[\"latitude_of_center\",1],\n"
2491         "PARAMETER[\"longitude_of_center\",2],\n"
2492         "PARAMETER[\"azimuth\",3],\n"
2493         "PARAMETER[\"rectified_grid_angle\",4],\n"
2494         "PARAMETER[\"scale_factor\",5],\n"
2495         "PARAMETER[\"false_easting\",6],\n"
2496         "PARAMETER[\"false_northing\",7]");
2497 }
2498 
2499 // ---------------------------------------------------------------------------
2500 
TEST(operation,hotine_oblique_mercator_variant_B_export_swiss_mercator)2501 TEST(operation, hotine_oblique_mercator_variant_B_export_swiss_mercator) {
2502     auto conv = Conversion::createHotineObliqueMercatorVariantB(
2503         PropertyMap(), Angle(1), Angle(2), Angle(90), Angle(90), Scale(5),
2504         Length(6), Length(7));
2505     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2506               "+proj=somerc +lat_0=1 +lon_0=2 +k_0=5 "
2507               "+x_0=6 +y_0=7");
2508 }
2509 
2510 // ---------------------------------------------------------------------------
2511 
TEST(operation,hotine_oblique_mercator_two_point_natural_origin_export)2512 TEST(operation, hotine_oblique_mercator_two_point_natural_origin_export) {
2513     auto conv = Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(
2514         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Angle(5),
2515         Scale(6), Length(7), Length(8));
2516     EXPECT_TRUE(conv->validateParameters().empty());
2517 
2518     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2519               "+proj=omerc +lat_0=1 +lat_1=2 +lon_1=3 +lat_2=4 +lon_2=5 +k=6 "
2520               "+x_0=7 +y_0=8");
2521 
2522     auto formatter = WKTFormatter::create();
2523     formatter->simulCurNodeHasId();
2524     EXPECT_EQ(
2525         conv->exportToWKT(formatter.get()),
2526         "CONVERSION[\"Hotine Oblique Mercator Two Point Natural Origin\",\n"
2527         "    METHOD[\"Hotine Oblique Mercator Two Point Natural Origin\"],\n"
2528         "    PARAMETER[\"Latitude of projection centre\",1,\n"
2529         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2530         "        ID[\"EPSG\",8811]],\n"
2531         "    PARAMETER[\"Latitude of 1st point\",2,\n"
2532         "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
2533         "    PARAMETER[\"Longitude of 1st point\",3,\n"
2534         "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
2535         "    PARAMETER[\"Latitude of 2nd point\",4,\n"
2536         "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
2537         "    PARAMETER[\"Longitude of 2nd point\",5,\n"
2538         "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
2539         "    PARAMETER[\"Scale factor on initial line\",6,\n"
2540         "        SCALEUNIT[\"unity\",1],\n"
2541         "        ID[\"EPSG\",8815]],\n"
2542         "    PARAMETER[\"Easting at projection centre\",7,\n"
2543         "        LENGTHUNIT[\"metre\",1],\n"
2544         "        ID[\"EPSG\",8816]],\n"
2545         "    PARAMETER[\"Northing at projection centre\",8,\n"
2546         "        LENGTHUNIT[\"metre\",1],\n"
2547         "        ID[\"EPSG\",8817]]]");
2548 
2549     EXPECT_EQ(
2550         conv->exportToWKT(
2551             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2552         "PROJECTION[\"Hotine_Oblique_Mercator_Two_Point_Natural_Origin\"],\n"
2553         "PARAMETER[\"latitude_of_center\",1],\n"
2554         "PARAMETER[\"latitude_of_point_1\",2],\n"
2555         "PARAMETER[\"longitude_of_point_1\",3],\n"
2556         "PARAMETER[\"latitude_of_point_2\",4],\n"
2557         "PARAMETER[\"longitude_of_point_2\",5],\n"
2558         "PARAMETER[\"scale_factor\",6],\n"
2559         "PARAMETER[\"false_easting\",7],\n"
2560         "PARAMETER[\"false_northing\",8]");
2561 }
2562 
2563 // ---------------------------------------------------------------------------
2564 
TEST(operation,laborde_oblique_mercator_export)2565 TEST(operation, laborde_oblique_mercator_export) {
2566     auto conv = Conversion::createLabordeObliqueMercator(
2567         PropertyMap(), Angle(1), Angle(2), Angle(3), Scale(4), Length(5),
2568         Length(6));
2569     EXPECT_TRUE(conv->validateParameters().empty());
2570 
2571     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2572               "+proj=labrd +lat_0=1 +lon_0=2 +azi=3 +k=4 +x_0=5 +y_0=6");
2573 
2574     auto formatter = WKTFormatter::create();
2575     formatter->simulCurNodeHasId();
2576     EXPECT_EQ(conv->exportToWKT(formatter.get()),
2577               "CONVERSION[\"Laborde Oblique Mercator\",\n"
2578               "    METHOD[\"Laborde Oblique Mercator\",\n"
2579               "        ID[\"EPSG\",9813]],\n"
2580               "    PARAMETER[\"Latitude of projection centre\",1,\n"
2581               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2582               "        ID[\"EPSG\",8811]],\n"
2583               "    PARAMETER[\"Longitude of projection centre\",2,\n"
2584               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2585               "        ID[\"EPSG\",8812]],\n"
2586               "    PARAMETER[\"Azimuth of initial line\",3,\n"
2587               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2588               "        ID[\"EPSG\",8813]],\n"
2589               "    PARAMETER[\"Scale factor on initial line\",4,\n"
2590               "        SCALEUNIT[\"unity\",1],\n"
2591               "        ID[\"EPSG\",8815]],\n"
2592               "    PARAMETER[\"False easting\",5,\n"
2593               "        LENGTHUNIT[\"metre\",1],\n"
2594               "        ID[\"EPSG\",8806]],\n"
2595               "    PARAMETER[\"False northing\",6,\n"
2596               "        LENGTHUNIT[\"metre\",1],\n"
2597               "        ID[\"EPSG\",8807]]]");
2598 
2599     EXPECT_EQ(
2600         conv->exportToWKT(
2601             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2602         "PROJECTION[\"Laborde_Oblique_Mercator\"],\n"
2603         "PARAMETER[\"latitude_of_center\",1],\n"
2604         "PARAMETER[\"longitude_of_center\",2],\n"
2605         "PARAMETER[\"azimuth\",3],\n"
2606         "PARAMETER[\"scale_factor\",4],\n"
2607         "PARAMETER[\"false_easting\",5],\n"
2608         "PARAMETER[\"false_northing\",6]");
2609 }
2610 
2611 // ---------------------------------------------------------------------------
2612 
TEST(operation,imw_polyconic_export)2613 TEST(operation, imw_polyconic_export) {
2614     auto conv = Conversion::createInternationalMapWorldPolyconic(
2615         PropertyMap(), Angle(1), Angle(3), Angle(4), Length(5), Length(6));
2616     EXPECT_TRUE(conv->validateParameters().empty());
2617 
2618     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2619               "+proj=imw_p +lon_0=1 +lat_1=3 +lat_2=4 +x_0=5 +y_0=6");
2620 
2621     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2622               "CONVERSION[\"International Map of the World Polyconic\",\n"
2623               "    METHOD[\"International Map of the World Polyconic\"],\n"
2624               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2625               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2626               "        ID[\"EPSG\",8802]],\n"
2627               "    PARAMETER[\"Latitude of 1st point\",3,\n"
2628               "        ANGLEUNIT[\"degree\",0.0174532925199433,\n"
2629               "            ID[\"EPSG\",9122]]],\n"
2630               "    PARAMETER[\"Latitude of 2nd point\",4,\n"
2631               "        ANGLEUNIT[\"degree\",0.0174532925199433,\n"
2632               "            ID[\"EPSG\",9122]]],\n"
2633               "    PARAMETER[\"False easting\",5,\n"
2634               "        LENGTHUNIT[\"metre\",1],\n"
2635               "        ID[\"EPSG\",8806]],\n"
2636               "    PARAMETER[\"False northing\",6,\n"
2637               "        LENGTHUNIT[\"metre\",1],\n"
2638               "        ID[\"EPSG\",8807]]]");
2639 
2640     EXPECT_EQ(
2641         conv->exportToWKT(
2642             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2643         "PROJECTION[\"International_Map_of_the_World_Polyconic\"],\n"
2644         "PARAMETER[\"central_meridian\",1],\n"
2645         "PARAMETER[\"Latitude_Of_1st_Point\",3],\n"
2646         "PARAMETER[\"Latitude_Of_2nd_Point\",4],\n"
2647         "PARAMETER[\"false_easting\",5],\n"
2648         "PARAMETER[\"false_northing\",6]");
2649 }
2650 
2651 // ---------------------------------------------------------------------------
2652 
TEST(operation,krovak_north_oriented_export)2653 TEST(operation, krovak_north_oriented_export) {
2654     auto conv = Conversion::createKrovakNorthOriented(
2655         PropertyMap(), Angle(49.5), Angle(42.5), Angle(30.2881397527778),
2656         Angle(78.5), Scale(0.9999), Length(5), Length(6));
2657     EXPECT_TRUE(conv->validateParameters().empty());
2658 
2659     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2660               "+proj=krovak +lat_0=49.5 +lon_0=42.5 +alpha=30.2881397527778 "
2661               "+k=0.9999 +x_0=5 +y_0=6");
2662 
2663     EXPECT_EQ(
2664         conv->exportToWKT(WKTFormatter::create().get()),
2665         "CONVERSION[\"Krovak (North Orientated)\",\n"
2666         "    METHOD[\"Krovak (North Orientated)\",\n"
2667         "        ID[\"EPSG\",1041]],\n"
2668         "    PARAMETER[\"Latitude of projection centre\",49.5,\n"
2669         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2670         "        ID[\"EPSG\",8811]],\n"
2671         "    PARAMETER[\"Longitude of origin\",42.5,\n"
2672         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2673         "        ID[\"EPSG\",8833]],\n"
2674         "    PARAMETER[\"Co-latitude of cone axis\",30.2881397527778,\n"
2675         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2676         "        ID[\"EPSG\",1036]],\n"
2677         "    PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n"
2678         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2679         "        ID[\"EPSG\",8818]],\n"
2680         "    PARAMETER[\"Scale factor on pseudo standard parallel\",0.9999,\n"
2681         "        SCALEUNIT[\"unity\",1],\n"
2682         "        ID[\"EPSG\",8819]],\n"
2683         "    PARAMETER[\"False easting\",5,\n"
2684         "        LENGTHUNIT[\"metre\",1],\n"
2685         "        ID[\"EPSG\",8806]],\n"
2686         "    PARAMETER[\"False northing\",6,\n"
2687         "        LENGTHUNIT[\"metre\",1],\n"
2688         "        ID[\"EPSG\",8807]]]");
2689 
2690     EXPECT_EQ(
2691         conv->exportToWKT(
2692             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2693         "PROJECTION[\"Krovak\"],\n"
2694         "PARAMETER[\"latitude_of_center\",49.5],\n"
2695         "PARAMETER[\"longitude_of_center\",42.5],\n"
2696         "PARAMETER[\"azimuth\",30.2881397527778],\n"
2697         "PARAMETER[\"pseudo_standard_parallel_1\",78.5],\n"
2698         "PARAMETER[\"scale_factor\",0.9999],\n"
2699         "PARAMETER[\"false_easting\",5],\n"
2700         "PARAMETER[\"false_northing\",6]");
2701 }
2702 
2703 // ---------------------------------------------------------------------------
2704 
TEST(operation,krovak_export)2705 TEST(operation, krovak_export) {
2706     auto conv = Conversion::createKrovak(
2707         PropertyMap(), Angle(49.5), Angle(42.5), Angle(30.2881397527778),
2708         Angle(78.5), Scale(0.9999), Length(5), Length(6));
2709     EXPECT_TRUE(conv->validateParameters().empty());
2710 
2711     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2712               "+proj=krovak +axis=swu +lat_0=49.5 +lon_0=42.5 "
2713               "+alpha=30.2881397527778 +k=0.9999 +x_0=5 "
2714               "+y_0=6");
2715 
2716     EXPECT_EQ(
2717         conv->exportToWKT(WKTFormatter::create().get()),
2718         "CONVERSION[\"Krovak\",\n"
2719         "    METHOD[\"Krovak\",\n"
2720         "        ID[\"EPSG\",9819]],\n"
2721         "    PARAMETER[\"Latitude of projection centre\",49.5,\n"
2722         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2723         "        ID[\"EPSG\",8811]],\n"
2724         "    PARAMETER[\"Longitude of origin\",42.5,\n"
2725         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2726         "        ID[\"EPSG\",8833]],\n"
2727         "    PARAMETER[\"Co-latitude of cone axis\",30.2881397527778,\n"
2728         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2729         "        ID[\"EPSG\",1036]],\n"
2730         "    PARAMETER[\"Latitude of pseudo standard parallel\",78.5,\n"
2731         "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2732         "        ID[\"EPSG\",8818]],\n"
2733         "    PARAMETER[\"Scale factor on pseudo standard parallel\",0.9999,\n"
2734         "        SCALEUNIT[\"unity\",1],\n"
2735         "        ID[\"EPSG\",8819]],\n"
2736         "    PARAMETER[\"False easting\",5,\n"
2737         "        LENGTHUNIT[\"metre\",1],\n"
2738         "        ID[\"EPSG\",8806]],\n"
2739         "    PARAMETER[\"False northing\",6,\n"
2740         "        LENGTHUNIT[\"metre\",1],\n"
2741         "        ID[\"EPSG\",8807]]]");
2742 
2743     EXPECT_EQ(
2744         conv->exportToWKT(
2745             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2746         "PROJECTION[\"Krovak\"],\n"
2747         "PARAMETER[\"latitude_of_center\",49.5],\n"
2748         "PARAMETER[\"longitude_of_center\",42.5],\n"
2749         "PARAMETER[\"azimuth\",30.2881397527778],\n"
2750         "PARAMETER[\"pseudo_standard_parallel_1\",78.5],\n"
2751         "PARAMETER[\"scale_factor\",0.9999],\n"
2752         "PARAMETER[\"false_easting\",5],\n"
2753         "PARAMETER[\"false_northing\",6]");
2754 }
2755 
2756 // ---------------------------------------------------------------------------
2757 
TEST(operation,lambert_azimuthal_equal_area_export)2758 TEST(operation, lambert_azimuthal_equal_area_export) {
2759     auto conv = Conversion::createLambertAzimuthalEqualArea(
2760         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
2761     EXPECT_TRUE(conv->validateParameters().empty());
2762 
2763     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2764               "+proj=laea +lat_0=1 +lon_0=2 +x_0=3 +y_0=4");
2765 
2766     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2767               "CONVERSION[\"Lambert Azimuthal Equal Area\",\n"
2768               "    METHOD[\"Lambert Azimuthal Equal Area\",\n"
2769               "        ID[\"EPSG\",9820]],\n"
2770               "    PARAMETER[\"Latitude of natural origin\",1,\n"
2771               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2772               "        ID[\"EPSG\",8801]],\n"
2773               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2774               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2775               "        ID[\"EPSG\",8802]],\n"
2776               "    PARAMETER[\"False easting\",3,\n"
2777               "        LENGTHUNIT[\"metre\",1],\n"
2778               "        ID[\"EPSG\",8806]],\n"
2779               "    PARAMETER[\"False northing\",4,\n"
2780               "        LENGTHUNIT[\"metre\",1],\n"
2781               "        ID[\"EPSG\",8807]]]");
2782 
2783     EXPECT_EQ(
2784         conv->exportToWKT(
2785             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2786         "PROJECTION[\"Lambert_Azimuthal_Equal_Area\"],\n"
2787         "PARAMETER[\"latitude_of_center\",1],\n"
2788         "PARAMETER[\"longitude_of_center\",2],\n"
2789         "PARAMETER[\"false_easting\",3],\n"
2790         "PARAMETER[\"false_northing\",4]");
2791 }
2792 
2793 // ---------------------------------------------------------------------------
2794 
TEST(operation,miller_cylindrical_export)2795 TEST(operation, miller_cylindrical_export) {
2796     auto conv = Conversion::createMillerCylindrical(PropertyMap(), Angle(2),
2797                                                     Length(3), Length(4));
2798     EXPECT_TRUE(conv->validateParameters().empty());
2799 
2800     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2801               "+proj=mill +R_A +lon_0=2 +x_0=3 +y_0=4");
2802 
2803     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2804               "CONVERSION[\"Miller Cylindrical\",\n"
2805               "    METHOD[\"Miller Cylindrical\"],\n"
2806               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2807               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2808               "        ID[\"EPSG\",8802]],\n"
2809               "    PARAMETER[\"False easting\",3,\n"
2810               "        LENGTHUNIT[\"metre\",1],\n"
2811               "        ID[\"EPSG\",8806]],\n"
2812               "    PARAMETER[\"False northing\",4,\n"
2813               "        LENGTHUNIT[\"metre\",1],\n"
2814               "        ID[\"EPSG\",8807]]]");
2815 
2816     EXPECT_EQ(
2817         conv->exportToWKT(
2818             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2819         "PROJECTION[\"Miller_Cylindrical\"],\n"
2820         "PARAMETER[\"longitude_of_center\",2],\n"
2821         "PARAMETER[\"false_easting\",3],\n"
2822         "PARAMETER[\"false_northing\",4]");
2823 }
2824 
2825 // ---------------------------------------------------------------------------
2826 
TEST(operation,mercator_variant_A_export)2827 TEST(operation, mercator_variant_A_export) {
2828     auto conv = Conversion::createMercatorVariantA(
2829         PropertyMap(), Angle(0), Angle(1), Scale(2), Length(3), Length(4));
2830     EXPECT_TRUE(conv->validateParameters().empty());
2831 
2832     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2833               "+proj=merc +lon_0=1 +k=2 +x_0=3 +y_0=4");
2834 
2835     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2836               "CONVERSION[\"Mercator (variant A)\",\n"
2837               "    METHOD[\"Mercator (variant A)\",\n"
2838               "        ID[\"EPSG\",9804]],\n"
2839               "    PARAMETER[\"Latitude of natural origin\",0,\n"
2840               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2841               "        ID[\"EPSG\",8801]],\n"
2842               "    PARAMETER[\"Longitude of natural origin\",1,\n"
2843               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2844               "        ID[\"EPSG\",8802]],\n"
2845               "    PARAMETER[\"Scale factor at natural origin\",2,\n"
2846               "        SCALEUNIT[\"unity\",1],\n"
2847               "        ID[\"EPSG\",8805]],\n"
2848               "    PARAMETER[\"False easting\",3,\n"
2849               "        LENGTHUNIT[\"metre\",1],\n"
2850               "        ID[\"EPSG\",8806]],\n"
2851               "    PARAMETER[\"False northing\",4,\n"
2852               "        LENGTHUNIT[\"metre\",1],\n"
2853               "        ID[\"EPSG\",8807]]]");
2854 
2855     EXPECT_EQ(
2856         conv->exportToWKT(
2857             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2858         "PROJECTION[\"Mercator_1SP\"],\n"
2859         "PARAMETER[\"central_meridian\",1],\n"
2860         "PARAMETER[\"scale_factor\",2],\n"
2861         "PARAMETER[\"false_easting\",3],\n"
2862         "PARAMETER[\"false_northing\",4]");
2863 }
2864 
2865 // ---------------------------------------------------------------------------
2866 
TEST(operation,mercator_variant_A_export_latitude_origin_non_zero)2867 TEST(operation, mercator_variant_A_export_latitude_origin_non_zero) {
2868     auto conv = Conversion::createMercatorVariantA(
2869         PropertyMap(), Angle(10), Angle(1), Scale(2), Length(3), Length(4));
2870 
2871     EXPECT_THROW(conv->exportToPROJString(PROJStringFormatter::create().get()),
2872                  FormattingException);
2873 }
2874 
2875 // ---------------------------------------------------------------------------
2876 
TEST(operation,wkt1_import_mercator_variant_A)2877 TEST(operation, wkt1_import_mercator_variant_A) {
2878     auto wkt = "PROJCS[\"test\",\n"
2879                "    GEOGCS[\"WGS 84\",\n"
2880                "        DATUM[\"WGS 1984\",\n"
2881                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
2882                "        PRIMEM[\"Greenwich\",0],\n"
2883                "        UNIT[\"degree\",0.0174532925199433]],\n"
2884                "    PROJECTION[\"Mercator_1SP\"],\n"
2885                "    PARAMETER[\"central_meridian\",1],\n"
2886                "    PARAMETER[\"scale_factor\",2],\n"
2887                "    PARAMETER[\"false_easting\",3],\n"
2888                "    PARAMETER[\"false_northing\",4],\n"
2889                "    UNIT[\"metre\",1]]";
2890     auto obj = WKTParser().createFromWKT(wkt);
2891     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
2892     ASSERT_TRUE(crs != nullptr);
2893 
2894     auto conversion = crs->derivingConversion();
2895     auto convRef = Conversion::createMercatorVariantA(
2896         PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(0),
2897         Angle(1), Scale(2), Length(3), Length(4));
2898 
2899     EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()),
2900               convRef->exportToWKT(WKTFormatter::create().get()));
2901 }
2902 
2903 // ---------------------------------------------------------------------------
2904 
TEST(operation,wkt1_import_mercator_variant_A_that_is_variant_B)2905 TEST(operation, wkt1_import_mercator_variant_A_that_is_variant_B) {
2906     // Addresses https://trac.osgeo.org/gdal/ticket/3026
2907     auto wkt = "PROJCS[\"test\",\n"
2908                "    GEOGCS[\"WGS 84\",\n"
2909                "        DATUM[\"WGS 1984\",\n"
2910                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
2911                "        PRIMEM[\"Greenwich\",0],\n"
2912                "        UNIT[\"degree\",0.0174532925199433]],\n"
2913                "    PROJECTION[\"Mercator_1SP\"],\n"
2914                "    PARAMETER[\"latitude_of_origin\",-1],\n"
2915                "    PARAMETER[\"central_meridian\",2],\n"
2916                "    PARAMETER[\"scale_factor\",1],\n"
2917                "    PARAMETER[\"false_easting\",3],\n"
2918                "    PARAMETER[\"false_northing\",4],\n"
2919                "    UNIT[\"metre\",1]]";
2920     auto obj = WKTParser().createFromWKT(wkt);
2921     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
2922     ASSERT_TRUE(crs != nullptr);
2923 
2924     auto conversion = crs->derivingConversion();
2925     auto convRef = Conversion::createMercatorVariantB(
2926         PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(-1),
2927         Angle(2), Length(3), Length(4));
2928 
2929     EXPECT_TRUE(conversion->isEquivalentTo(convRef.get(),
2930                                            IComparable::Criterion::EQUIVALENT));
2931 }
2932 
2933 // ---------------------------------------------------------------------------
2934 
TEST(operation,mercator_variant_B_export)2935 TEST(operation, mercator_variant_B_export) {
2936     auto conv = Conversion::createMercatorVariantB(
2937         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
2938     EXPECT_TRUE(conv->validateParameters().empty());
2939 
2940     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
2941               "+proj=merc +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4");
2942 
2943     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
2944               "CONVERSION[\"Mercator (variant B)\",\n"
2945               "    METHOD[\"Mercator (variant B)\",\n"
2946               "        ID[\"EPSG\",9805]],\n"
2947               "    PARAMETER[\"Latitude of 1st standard parallel\",1,\n"
2948               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2949               "        ID[\"EPSG\",8823]],\n"
2950               "    PARAMETER[\"Longitude of natural origin\",2,\n"
2951               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
2952               "        ID[\"EPSG\",8802]],\n"
2953               "    PARAMETER[\"False easting\",3,\n"
2954               "        LENGTHUNIT[\"metre\",1],\n"
2955               "        ID[\"EPSG\",8806]],\n"
2956               "    PARAMETER[\"False northing\",4,\n"
2957               "        LENGTHUNIT[\"metre\",1],\n"
2958               "        ID[\"EPSG\",8807]]]");
2959 
2960     EXPECT_EQ(
2961         conv->exportToWKT(
2962             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
2963         "PROJECTION[\"Mercator_2SP\"],\n"
2964         "PARAMETER[\"standard_parallel_1\",1],\n"
2965         "PARAMETER[\"central_meridian\",2],\n"
2966         "PARAMETER[\"false_easting\",3],\n"
2967         "PARAMETER[\"false_northing\",4]");
2968 }
2969 
2970 // ---------------------------------------------------------------------------
2971 
TEST(operation,odd_mercator_1sp_with_non_null_latitude)2972 TEST(operation, odd_mercator_1sp_with_non_null_latitude) {
2973     auto obj = WKTParser().createFromWKT(
2974         "PROJCS[\"unnamed\",\n"
2975         "    GEOGCS[\"WGS 84\",\n"
2976         "        DATUM[\"WGS_1984\",\n"
2977         "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
2978         "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
2979         "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
2980         "        PRIMEM[\"Greenwich\",0,\n"
2981         "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
2982         "        UNIT[\"degree\",0.0174532925199433,\n"
2983         "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
2984         "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
2985         "    PROJECTION[\"Mercator_1SP\"],\n"
2986         "    PARAMETER[\"latitude_of_origin\",30],\n"
2987         "    PARAMETER[\"central_meridian\",0],\n"
2988         "    PARAMETER[\"scale_factor\",0.99],\n"
2989         "    PARAMETER[\"false_easting\",0],\n"
2990         "    PARAMETER[\"false_northing\",0],\n"
2991         "    UNIT[\"metre\",1],\n"
2992         "    AXIS[\"Easting\",EAST],\n"
2993         "    AXIS[\"Northing\",NORTH]]");
2994 
2995     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
2996     ASSERT_TRUE(crs != nullptr);
2997 
2998     EXPECT_THROW(crs->exportToPROJString(PROJStringFormatter::create().get()),
2999                  FormattingException);
3000 }
3001 
3002 // ---------------------------------------------------------------------------
3003 
TEST(operation,odd_mercator_2sp_with_latitude_of_origin)3004 TEST(operation, odd_mercator_2sp_with_latitude_of_origin) {
3005     auto obj = WKTParser().createFromWKT(
3006         "PROJCS[\"unnamed\",\n"
3007         "    GEOGCS[\"WGS 84\",\n"
3008         "        DATUM[\"WGS_1984\",\n"
3009         "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
3010         "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
3011         "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
3012         "        PRIMEM[\"Greenwich\",0,\n"
3013         "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
3014         "        UNIT[\"degree\",0.0174532925199433,\n"
3015         "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
3016         "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
3017         "    PROJECTION[\"Mercator_2SP\"],\n"
3018         "    PARAMETER[\"standard_parallel_1\",30],\n"
3019         "    PARAMETER[\"latitude_of_origin\",40],\n"
3020         "    PARAMETER[\"central_meridian\",0],\n"
3021         "    PARAMETER[\"false_easting\",0],\n"
3022         "    PARAMETER[\"false_northing\",0],\n"
3023         "    UNIT[\"metre\",1],\n"
3024         "    AXIS[\"Easting\",EAST],\n"
3025         "    AXIS[\"Northing\",NORTH]]");
3026 
3027     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3028     ASSERT_TRUE(crs != nullptr);
3029 
3030     EXPECT_THROW(crs->exportToPROJString(PROJStringFormatter::create().get()),
3031                  FormattingException);
3032 }
3033 
3034 // ---------------------------------------------------------------------------
3035 
TEST(operation,webmerc_export)3036 TEST(operation, webmerc_export) {
3037     auto conv = Conversion::createPopularVisualisationPseudoMercator(
3038         PropertyMap(), Angle(0), Angle(2), Length(3), Length(4));
3039     EXPECT_TRUE(conv->validateParameters().empty());
3040 
3041     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3042               "+proj=webmerc +lat_0=0 +lon_0=2 +x_0=3 +y_0=4");
3043 
3044     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3045               "CONVERSION[\"Popular Visualisation Pseudo Mercator\",\n"
3046               "    METHOD[\"Popular Visualisation Pseudo Mercator\",\n"
3047               "        ID[\"EPSG\",1024]],\n"
3048               "    PARAMETER[\"Latitude of natural origin\",0,\n"
3049               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3050               "        ID[\"EPSG\",8801]],\n"
3051               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3052               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3053               "        ID[\"EPSG\",8802]],\n"
3054               "    PARAMETER[\"False easting\",3,\n"
3055               "        LENGTHUNIT[\"metre\",1],\n"
3056               "        ID[\"EPSG\",8806]],\n"
3057               "    PARAMETER[\"False northing\",4,\n"
3058               "        LENGTHUNIT[\"metre\",1],\n"
3059               "        ID[\"EPSG\",8807]]]");
3060 
3061     auto projCRS = ProjectedCRS::create(
3062         PropertyMap().set(IdentifiedObject::NAME_KEY, "Pseudo-Mercator"),
3063         GeographicCRS::EPSG_4326, conv,
3064         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
3065 
3066     EXPECT_EQ(
3067         projCRS->exportToWKT(
3068             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3069         "PROJCS[\"Pseudo-Mercator\",\n"
3070         "    GEOGCS[\"WGS 84\",\n"
3071         "        DATUM[\"WGS_1984\",\n"
3072         "            SPHEROID[\"WGS 84\",6378137,298.257223563,\n"
3073         "                AUTHORITY[\"EPSG\",\"7030\"]],\n"
3074         "            AUTHORITY[\"EPSG\",\"6326\"]],\n"
3075         "        PRIMEM[\"Greenwich\",0,\n"
3076         "            AUTHORITY[\"EPSG\",\"8901\"]],\n"
3077         "        UNIT[\"degree\",0.0174532925199433,\n"
3078         "            AUTHORITY[\"EPSG\",\"9122\"]],\n"
3079         "        AUTHORITY[\"EPSG\",\"4326\"]],\n"
3080         "    PROJECTION[\"Mercator_1SP\"],\n"
3081         "    PARAMETER[\"central_meridian\",2],\n"
3082         "    PARAMETER[\"scale_factor\",1],\n"
3083         "    PARAMETER[\"false_easting\",3],\n"
3084         "    PARAMETER[\"false_northing\",4],\n"
3085         "    UNIT[\"metre\",1,\n"
3086         "        AUTHORITY[\"EPSG\",\"9001\"]],\n"
3087         "    AXIS[\"Easting\",EAST],\n"
3088         "    AXIS[\"Northing\",NORTH],\n"
3089         "    EXTENSION[\"PROJ4\",\"+proj=merc "
3090         "+a=6378137 +b=6378137 +lat_ts=0 +lon_0=2 "
3091         "+x_0=3 +y_0=4 +k=1 +units=m "
3092         "+nadgrids=@null +wktext +no_defs\"]]");
3093 
3094     auto op = CoordinateOperationFactory::create()->createOperation(
3095         GeographicCRS::EPSG_4326, projCRS);
3096     ASSERT_TRUE(op != nullptr);
3097     EXPECT_EQ(op->exportToPROJString(PROJStringFormatter::create().get()),
3098               "+proj=pipeline +step +proj=axisswap +order=2,1 +step "
3099               "+proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=webmerc "
3100               "+lat_0=0 +lon_0=2 +x_0=3 +y_0=4 +ellps=WGS84");
3101 
3102     EXPECT_EQ(
3103         projCRS->exportToPROJString(PROJStringFormatter::create().get()),
3104         "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=2 +x_0=3 "
3105         "+y_0=4 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs");
3106 }
3107 
3108 // ---------------------------------------------------------------------------
3109 
TEST(operation,webmerc_import_from_GDAL_wkt1)3110 TEST(operation, webmerc_import_from_GDAL_wkt1) {
3111 
3112     auto projCRS = ProjectedCRS::create(
3113         PropertyMap().set(IdentifiedObject::NAME_KEY, "Pseudo-Mercator"),
3114         GeographicCRS::EPSG_4326,
3115         Conversion::createPopularVisualisationPseudoMercator(
3116             PropertyMap(), Angle(0), Angle(0), Length(0), Length(0)),
3117         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
3118 
3119     auto wkt1 = projCRS->exportToWKT(
3120         WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get());
3121     auto obj = WKTParser().createFromWKT(wkt1);
3122     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3123     ASSERT_TRUE(crs != nullptr);
3124 
3125     auto convGot = crs->derivingConversion();
3126 
3127     EXPECT_EQ(convGot->exportToWKT(WKTFormatter::create().get()),
3128               "CONVERSION[\"unnamed\",\n"
3129               "    METHOD[\"Popular Visualisation Pseudo Mercator\",\n"
3130               "        ID[\"EPSG\",1024]],\n"
3131               "    PARAMETER[\"Latitude of natural origin\",0,\n"
3132               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3133               "        ID[\"EPSG\",8801]],\n"
3134               "    PARAMETER[\"Longitude of natural origin\",0,\n"
3135               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3136               "        ID[\"EPSG\",8802]],\n"
3137               "    PARAMETER[\"False easting\",0,\n"
3138               "        LENGTHUNIT[\"metre\",1],\n"
3139               "        ID[\"EPSG\",8806]],\n"
3140               "    PARAMETER[\"False northing\",0,\n"
3141               "        LENGTHUNIT[\"metre\",1],\n"
3142               "        ID[\"EPSG\",8807]]]");
3143 }
3144 
3145 // ---------------------------------------------------------------------------
3146 
TEST(operation,webmerc_import_from_GDAL_wkt1_with_EPSG_code)3147 TEST(operation, webmerc_import_from_GDAL_wkt1_with_EPSG_code) {
3148 
3149     auto projCRS = ProjectedCRS::create(
3150         PropertyMap()
3151             .set(IdentifiedObject::NAME_KEY, "Pseudo-Mercator")
3152             .set(Identifier::CODESPACE_KEY, "EPSG")
3153             .set(Identifier::CODE_KEY, "3857"),
3154         GeographicCRS::EPSG_4326,
3155         Conversion::createPopularVisualisationPseudoMercator(
3156             PropertyMap(), Angle(0), Angle(0), Length(0), Length(0)),
3157         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
3158 
3159     auto wkt1 = projCRS->exportToWKT(
3160         WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get());
3161     EXPECT_TRUE(wkt1.find("3857") != std::string::npos) << wkt1;
3162     auto obj = WKTParser().createFromWKT(wkt1);
3163     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3164     ASSERT_TRUE(crs != nullptr);
3165     EXPECT_EQ(crs->identifiers().size(), 1U);
3166 }
3167 
3168 // ---------------------------------------------------------------------------
3169 
TEST(operation,webmerc_import_from_GDAL_wkt1_EPSG_3785_deprecated)3170 TEST(operation, webmerc_import_from_GDAL_wkt1_EPSG_3785_deprecated) {
3171 
3172     auto wkt1 =
3173         "PROJCS[\"Popular Visualisation CRS / Mercator (deprecated)\","
3174         "    GEOGCS[\"Popular Visualisation CRS\","
3175         "        DATUM[\"Popular_Visualisation_Datum\","
3176         "            SPHEROID[\"Popular Visualisation Sphere\",6378137,0,"
3177         "                AUTHORITY[\"EPSG\",\"7059\"]],"
3178         "            TOWGS84[0,0,0,0,0,0,0],"
3179         "            AUTHORITY[\"EPSG\",\"6055\"]],"
3180         "        PRIMEM[\"Greenwich\",0,"
3181         "            AUTHORITY[\"EPSG\",\"8901\"]],"
3182         "        UNIT[\"degree\",0.0174532925199433,"
3183         "            AUTHORITY[\"EPSG\",\"9122\"]],"
3184         "        AUTHORITY[\"EPSG\",\"4055\"]],"
3185         "    PROJECTION[\"Mercator_1SP\"],"
3186         "    PARAMETER[\"central_meridian\",0],"
3187         "    PARAMETER[\"scale_factor\",1],"
3188         "    PARAMETER[\"false_easting\",0],"
3189         "    PARAMETER[\"false_northing\",0],"
3190         "    UNIT[\"metre\",1,"
3191         "        AUTHORITY[\"EPSG\",\"9001\"]],"
3192         "    AXIS[\"X\",EAST],"
3193         "    AXIS[\"Y\",NORTH],"
3194         "    EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 "
3195         "+lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m "
3196         "+nadgrids=@null +wktext  +no_defs\"]]";
3197 
3198     auto obj = WKTParser().createFromWKT(wkt1);
3199     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3200     ASSERT_TRUE(crs != nullptr);
3201 
3202     EXPECT_EQ(
3203         crs->exportToPROJString(PROJStringFormatter::create().get()),
3204         "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 "
3205         "+y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs");
3206 
3207     auto convGot = crs->derivingConversion();
3208 
3209     EXPECT_EQ(convGot->exportToWKT(WKTFormatter::create().get()),
3210               "CONVERSION[\"unnamed\",\n"
3211               "    METHOD[\"Popular Visualisation Pseudo Mercator\",\n"
3212               "        ID[\"EPSG\",1024]],\n"
3213               "    PARAMETER[\"Latitude of natural origin\",0,\n"
3214               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3215               "        ID[\"EPSG\",8801]],\n"
3216               "    PARAMETER[\"Longitude of natural origin\",0,\n"
3217               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3218               "        ID[\"EPSG\",8802]],\n"
3219               "    PARAMETER[\"False easting\",0,\n"
3220               "        LENGTHUNIT[\"metre\",1],\n"
3221               "        ID[\"EPSG\",8806]],\n"
3222               "    PARAMETER[\"False northing\",0,\n"
3223               "        LENGTHUNIT[\"metre\",1],\n"
3224               "        ID[\"EPSG\",8807]]]");
3225 }
3226 
3227 // ---------------------------------------------------------------------------
3228 
TEST(operation,webmerc_import_from_WKT2_EPSG_3785_deprecated)3229 TEST(operation, webmerc_import_from_WKT2_EPSG_3785_deprecated) {
3230     auto wkt2 =
3231         "PROJCRS[\"Popular Visualisation CRS / Mercator\",\n"
3232         "    BASEGEODCRS[\"Popular Visualisation CRS\",\n"
3233         "        DATUM[\"Popular Visualisation Datum\",\n"
3234         "            ELLIPSOID[\"Popular Visualisation Sphere\",6378137,0,\n"
3235         "                LENGTHUNIT[\"metre\",1]]],\n"
3236         "        PRIMEM[\"Greenwich\",0,\n"
3237         "            ANGLEUNIT[\"degree\",0.0174532925199433]]],\n"
3238         "    CONVERSION[\"Popular Visualisation Mercator\",\n"
3239         "        METHOD[\"Mercator (1SP) (Spherical)\",\n"
3240         "            ID[\"EPSG\",9841]],\n"
3241         "        PARAMETER[\"Latitude of natural origin\",0,\n"
3242         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3243         "            ID[\"EPSG\",8801]],\n"
3244         "        PARAMETER[\"Longitude of natural origin\",0,\n"
3245         "            ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3246         "            ID[\"EPSG\",8802]],\n"
3247         "        PARAMETER[\"Scale factor at natural origin\",1,\n"
3248         "            SCALEUNIT[\"unity\",1],\n"
3249         "            ID[\"EPSG\",8805]],\n"
3250         "        PARAMETER[\"False easting\",0,\n"
3251         "            LENGTHUNIT[\"metre\",1],\n"
3252         "            ID[\"EPSG\",8806]],\n"
3253         "        PARAMETER[\"False northing\",0,\n"
3254         "            LENGTHUNIT[\"metre\",1],\n"
3255         "            ID[\"EPSG\",8807]]],\n"
3256         "    CS[Cartesian,2],\n"
3257         "        AXIS[\"easting (X)\",east,\n"
3258         "            ORDER[1],\n"
3259         "            LENGTHUNIT[\"metre\",1]],\n"
3260         "        AXIS[\"northing (Y)\",north,\n"
3261         "            ORDER[2],\n"
3262         "            LENGTHUNIT[\"metre\",1]]]";
3263 
3264     auto obj = WKTParser().createFromWKT(wkt2);
3265     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3266     ASSERT_TRUE(crs != nullptr);
3267 
3268     EXPECT_EQ(
3269         crs->exportToPROJString(PROJStringFormatter::create().get()),
3270         "+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 "
3271         "+y_0=0 +k=1 +units=m +nadgrids=@null +wktext +no_defs +type=crs");
3272 
3273     EXPECT_EQ(
3274         crs->exportToWKT(
3275             WKTFormatter::create(WKTFormatter::Convention::WKT2_2015).get()),
3276         wkt2);
3277 
3278     EXPECT_EQ(
3279         crs->exportToWKT(
3280             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL,
3281                                  DatabaseContext::create())
3282                 .get()),
3283         "PROJCS[\"Popular Visualisation CRS / Mercator\",\n"
3284         "    GEOGCS[\"Popular Visualisation CRS\",\n"
3285         "        DATUM[\"Popular_Visualisation_Datum\",\n"
3286         "            SPHEROID[\"Popular Visualisation Sphere\",6378137,0],\n"
3287         "            TOWGS84[0,0,0,0,0,0,0]],\n"
3288         "        PRIMEM[\"Greenwich\",0],\n"
3289         "        UNIT[\"degree\",0.0174532925199433]],\n"
3290         "    PROJECTION[\"Mercator_1SP\"],\n"
3291         "    PARAMETER[\"central_meridian\",0],\n"
3292         "    PARAMETER[\"scale_factor\",1],\n"
3293         "    PARAMETER[\"false_easting\",0],\n"
3294         "    PARAMETER[\"false_northing\",0],\n"
3295         "    UNIT[\"metre\",1],\n"
3296         "    AXIS[\"Easting\",EAST],\n"
3297         "    AXIS[\"Northing\",NORTH],\n"
3298         "    EXTENSION[\"PROJ4\",\"+proj=merc +a=6378137 +b=6378137 +lat_ts=0 "
3299         "+lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +nadgrids=@null +wktext "
3300         "+no_defs\"]]");
3301 }
3302 
3303 // ---------------------------------------------------------------------------
3304 
TEST(operation,webmerc_import_from_broken_esri_WGS_84_Pseudo_Mercator)3305 TEST(operation, webmerc_import_from_broken_esri_WGS_84_Pseudo_Mercator) {
3306 
3307     // Likely the result of a broken export of GDAL morphToESRI()
3308     auto wkt1 = "PROJCS[\"WGS_84_Pseudo_Mercator\",GEOGCS[\"GCS_WGS_1984\","
3309                 "DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\","
3310                 "6378137,298.257223563]],PRIMEM[\"Greenwich\",0],"
3311                 "UNIT[\"Degree\",0.017453292519943295]],"
3312                 "PROJECTION[\"Mercator\"],PARAMETER[\"central_meridian\",0],"
3313                 "PARAMETER[\"false_easting\",0],"
3314                 "PARAMETER[\"false_northing\",0],UNIT[\"Meter\",1],"
3315                 "PARAMETER[\"standard_parallel_1\",0.0]]";
3316 
3317     auto obj = WKTParser().setStrict(false).createFromWKT(wkt1);
3318     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3319     ASSERT_TRUE(crs != nullptr);
3320 
3321     auto convGot = crs->derivingConversion();
3322 
3323     EXPECT_EQ(convGot->exportToWKT(WKTFormatter::create().get()),
3324               "CONVERSION[\"unnamed\",\n"
3325               "    METHOD[\"Popular Visualisation Pseudo Mercator\",\n"
3326               "        ID[\"EPSG\",1024]],\n"
3327               "    PARAMETER[\"Latitude of natural origin\",0,\n"
3328               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3329               "        ID[\"EPSG\",8801]],\n"
3330               "    PARAMETER[\"Longitude of natural origin\",0,\n"
3331               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3332               "        ID[\"EPSG\",8802]],\n"
3333               "    PARAMETER[\"False easting\",0,\n"
3334               "        LENGTHUNIT[\"metre\",1],\n"
3335               "        ID[\"EPSG\",8806]],\n"
3336               "    PARAMETER[\"False northing\",0,\n"
3337               "        LENGTHUNIT[\"metre\",1],\n"
3338               "        ID[\"EPSG\",8807]]]");
3339 }
3340 
3341 // ---------------------------------------------------------------------------
3342 
TEST(operation,mollweide_export)3343 TEST(operation, mollweide_export) {
3344 
3345     auto conv = Conversion::createMollweide(PropertyMap(), Angle(1), Length(2),
3346                                             Length(3));
3347     EXPECT_TRUE(conv->validateParameters().empty());
3348 
3349     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3350               "+proj=moll +lon_0=1 +x_0=2 +y_0=3");
3351 
3352     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3353               "CONVERSION[\"Mollweide\",\n"
3354               "    METHOD[\"Mollweide\"],\n"
3355               "    PARAMETER[\"Longitude of natural origin\",1,\n"
3356               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3357               "        ID[\"EPSG\",8802]],\n"
3358               "    PARAMETER[\"False easting\",2,\n"
3359               "        LENGTHUNIT[\"metre\",1],\n"
3360               "        ID[\"EPSG\",8806]],\n"
3361               "    PARAMETER[\"False northing\",3,\n"
3362               "        LENGTHUNIT[\"metre\",1],\n"
3363               "        ID[\"EPSG\",8807]]]");
3364 
3365     EXPECT_EQ(
3366         conv->exportToWKT(
3367             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3368         "PROJECTION[\"Mollweide\"],\n"
3369         "PARAMETER[\"central_meridian\",1],\n"
3370         "PARAMETER[\"false_easting\",2],\n"
3371         "PARAMETER[\"false_northing\",3]");
3372 }
3373 // ---------------------------------------------------------------------------
3374 
TEST(operation,nzmg_export)3375 TEST(operation, nzmg_export) {
3376     auto conv = Conversion::createNewZealandMappingGrid(
3377         PropertyMap(), Angle(1), Angle(2), Length(4), Length(5));
3378     EXPECT_TRUE(conv->validateParameters().empty());
3379 
3380     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3381               "+proj=nzmg +lat_0=1 +lon_0=2 +x_0=4 +y_0=5");
3382 
3383     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3384               "CONVERSION[\"New Zealand Map Grid\",\n"
3385               "    METHOD[\"New Zealand Map Grid\",\n"
3386               "        ID[\"EPSG\",9811]],\n"
3387               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3388               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3389               "        ID[\"EPSG\",8801]],\n"
3390               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3391               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3392               "        ID[\"EPSG\",8802]],\n"
3393               "    PARAMETER[\"False easting\",4,\n"
3394               "        LENGTHUNIT[\"metre\",1],\n"
3395               "        ID[\"EPSG\",8806]],\n"
3396               "    PARAMETER[\"False northing\",5,\n"
3397               "        LENGTHUNIT[\"metre\",1],\n"
3398               "        ID[\"EPSG\",8807]]]");
3399 
3400     EXPECT_EQ(
3401         conv->exportToWKT(
3402             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3403         "PROJECTION[\"New_Zealand_Map_Grid\"],\n"
3404         "PARAMETER[\"latitude_of_origin\",1],\n"
3405         "PARAMETER[\"central_meridian\",2],\n"
3406         "PARAMETER[\"false_easting\",4],\n"
3407         "PARAMETER[\"false_northing\",5]");
3408 }
3409 
3410 // ---------------------------------------------------------------------------
3411 
TEST(operation,oblique_stereographic_export)3412 TEST(operation, oblique_stereographic_export) {
3413     auto conv = Conversion::createObliqueStereographic(
3414         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
3415     EXPECT_TRUE(conv->validateParameters().empty());
3416 
3417     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3418               "+proj=sterea +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5");
3419 
3420     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3421               "CONVERSION[\"Oblique Stereographic\",\n"
3422               "    METHOD[\"Oblique Stereographic\",\n"
3423               "        ID[\"EPSG\",9809]],\n"
3424               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3425               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3426               "        ID[\"EPSG\",8801]],\n"
3427               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3428               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3429               "        ID[\"EPSG\",8802]],\n"
3430               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
3431               "        SCALEUNIT[\"unity\",1],\n"
3432               "        ID[\"EPSG\",8805]],\n"
3433               "    PARAMETER[\"False easting\",4,\n"
3434               "        LENGTHUNIT[\"metre\",1],\n"
3435               "        ID[\"EPSG\",8806]],\n"
3436               "    PARAMETER[\"False northing\",5,\n"
3437               "        LENGTHUNIT[\"metre\",1],\n"
3438               "        ID[\"EPSG\",8807]]]");
3439 
3440     EXPECT_EQ(
3441         conv->exportToWKT(
3442             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3443         "PROJECTION[\"Oblique_Stereographic\"],\n"
3444         "PARAMETER[\"latitude_of_origin\",1],\n"
3445         "PARAMETER[\"central_meridian\",2],\n"
3446         "PARAMETER[\"scale_factor\",3],\n"
3447         "PARAMETER[\"false_easting\",4],\n"
3448         "PARAMETER[\"false_northing\",5]");
3449 }
3450 
3451 // ---------------------------------------------------------------------------
3452 
TEST(operation,orthographic_export)3453 TEST(operation, orthographic_export) {
3454     auto conv = Conversion::createOrthographic(PropertyMap(), Angle(1),
3455                                                Angle(2), Length(4), Length(5));
3456     EXPECT_TRUE(conv->validateParameters().empty());
3457 
3458     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3459               "+proj=ortho +lat_0=1 +lon_0=2 +x_0=4 +y_0=5");
3460 
3461     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3462               "CONVERSION[\"Orthographic\",\n"
3463               "    METHOD[\"Orthographic\",\n"
3464               "        ID[\"EPSG\",9840]],\n"
3465               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3466               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3467               "        ID[\"EPSG\",8801]],\n"
3468               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3469               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3470               "        ID[\"EPSG\",8802]],\n"
3471               "    PARAMETER[\"False easting\",4,\n"
3472               "        LENGTHUNIT[\"metre\",1],\n"
3473               "        ID[\"EPSG\",8806]],\n"
3474               "    PARAMETER[\"False northing\",5,\n"
3475               "        LENGTHUNIT[\"metre\",1],\n"
3476               "        ID[\"EPSG\",8807]]]");
3477 
3478     EXPECT_EQ(
3479         conv->exportToWKT(
3480             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3481         "PROJECTION[\"Orthographic\"],\n"
3482         "PARAMETER[\"latitude_of_origin\",1],\n"
3483         "PARAMETER[\"central_meridian\",2],\n"
3484         "PARAMETER[\"false_easting\",4],\n"
3485         "PARAMETER[\"false_northing\",5]");
3486 }
3487 
3488 // ---------------------------------------------------------------------------
3489 
TEST(operation,american_polyconic_export)3490 TEST(operation, american_polyconic_export) {
3491     auto conv = Conversion::createAmericanPolyconic(
3492         PropertyMap(), Angle(1), Angle(2), Length(4), Length(5));
3493     EXPECT_TRUE(conv->validateParameters().empty());
3494 
3495     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3496               "+proj=poly +lat_0=1 +lon_0=2 +x_0=4 +y_0=5");
3497 
3498     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3499               "CONVERSION[\"American Polyconic\",\n"
3500               "    METHOD[\"American Polyconic\",\n"
3501               "        ID[\"EPSG\",9818]],\n"
3502               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3503               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3504               "        ID[\"EPSG\",8801]],\n"
3505               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3506               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3507               "        ID[\"EPSG\",8802]],\n"
3508               "    PARAMETER[\"False easting\",4,\n"
3509               "        LENGTHUNIT[\"metre\",1],\n"
3510               "        ID[\"EPSG\",8806]],\n"
3511               "    PARAMETER[\"False northing\",5,\n"
3512               "        LENGTHUNIT[\"metre\",1],\n"
3513               "        ID[\"EPSG\",8807]]]");
3514 
3515     EXPECT_EQ(
3516         conv->exportToWKT(
3517             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3518         "PROJECTION[\"Polyconic\"],\n"
3519         "PARAMETER[\"latitude_of_origin\",1],\n"
3520         "PARAMETER[\"central_meridian\",2],\n"
3521         "PARAMETER[\"false_easting\",4],\n"
3522         "PARAMETER[\"false_northing\",5]");
3523 }
3524 
3525 // ---------------------------------------------------------------------------
3526 
TEST(operation,polar_stereographic_variant_A_export)3527 TEST(operation, polar_stereographic_variant_A_export) {
3528     auto conv = Conversion::createPolarStereographicVariantA(
3529         PropertyMap(), Angle(90), Angle(2), Scale(3), Length(4), Length(5));
3530     EXPECT_TRUE(conv->validateParameters().empty());
3531 
3532     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3533               "+proj=stere +lat_0=90 +lon_0=2 +k=3 +x_0=4 +y_0=5");
3534 
3535     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3536               "CONVERSION[\"Polar Stereographic (variant A)\",\n"
3537               "    METHOD[\"Polar Stereographic (variant A)\",\n"
3538               "        ID[\"EPSG\",9810]],\n"
3539               "    PARAMETER[\"Latitude of natural origin\",90,\n"
3540               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3541               "        ID[\"EPSG\",8801]],\n"
3542               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3543               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3544               "        ID[\"EPSG\",8802]],\n"
3545               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
3546               "        SCALEUNIT[\"unity\",1],\n"
3547               "        ID[\"EPSG\",8805]],\n"
3548               "    PARAMETER[\"False easting\",4,\n"
3549               "        LENGTHUNIT[\"metre\",1],\n"
3550               "        ID[\"EPSG\",8806]],\n"
3551               "    PARAMETER[\"False northing\",5,\n"
3552               "        LENGTHUNIT[\"metre\",1],\n"
3553               "        ID[\"EPSG\",8807]]]");
3554 
3555     EXPECT_EQ(
3556         conv->exportToWKT(
3557             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3558         "PROJECTION[\"Polar_Stereographic\"],\n"
3559         "PARAMETER[\"latitude_of_origin\",90],\n"
3560         "PARAMETER[\"central_meridian\",2],\n"
3561         "PARAMETER[\"scale_factor\",3],\n"
3562         "PARAMETER[\"false_easting\",4],\n"
3563         "PARAMETER[\"false_northing\",5]");
3564 }
3565 
3566 // ---------------------------------------------------------------------------
3567 
TEST(operation,polar_stereographic_variant_B_export_positive_lat)3568 TEST(operation, polar_stereographic_variant_B_export_positive_lat) {
3569     auto conv = Conversion::createPolarStereographicVariantB(
3570         PropertyMap(), Angle(70), Angle(2), Length(4), Length(5));
3571     EXPECT_TRUE(conv->validateParameters().empty());
3572 
3573     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3574               "+proj=stere +lat_0=90 +lat_ts=70 +lon_0=2 +x_0=4 +y_0=5");
3575 
3576     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3577               "CONVERSION[\"Polar Stereographic (variant B)\",\n"
3578               "    METHOD[\"Polar Stereographic (variant B)\",\n"
3579               "        ID[\"EPSG\",9829]],\n"
3580               "    PARAMETER[\"Latitude of standard parallel\",70,\n"
3581               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3582               "        ID[\"EPSG\",8832]],\n"
3583               "    PARAMETER[\"Longitude of origin\",2,\n"
3584               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3585               "        ID[\"EPSG\",8833]],\n"
3586               "    PARAMETER[\"False easting\",4,\n"
3587               "        LENGTHUNIT[\"metre\",1],\n"
3588               "        ID[\"EPSG\",8806]],\n"
3589               "    PARAMETER[\"False northing\",5,\n"
3590               "        LENGTHUNIT[\"metre\",1],\n"
3591               "        ID[\"EPSG\",8807]]]");
3592 
3593     EXPECT_EQ(
3594         conv->exportToWKT(
3595             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3596         "PROJECTION[\"Polar_Stereographic\"],\n"
3597         "PARAMETER[\"latitude_of_origin\",70],\n"
3598         "PARAMETER[\"central_meridian\",2],\n"
3599         "PARAMETER[\"false_easting\",4],\n"
3600         "PARAMETER[\"false_northing\",5]");
3601 }
3602 
3603 // ---------------------------------------------------------------------------
3604 
TEST(operation,polar_stereographic_variant_B_export_negative_lat)3605 TEST(operation, polar_stereographic_variant_B_export_negative_lat) {
3606     auto conv = Conversion::createPolarStereographicVariantB(
3607         PropertyMap(), Angle(-70), Angle(2), Length(4), Length(5));
3608 
3609     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3610               "+proj=stere +lat_0=-90 +lat_ts=-70 +lon_0=2 +x_0=4 +y_0=5");
3611 }
3612 
3613 // ---------------------------------------------------------------------------
3614 
TEST(operation,wkt1_import_polar_stereographic_variantA)3615 TEST(operation, wkt1_import_polar_stereographic_variantA) {
3616     auto wkt = "PROJCS[\"test\",\n"
3617                "    GEOGCS[\"WGS 84\",\n"
3618                "        DATUM[\"WGS 1984\",\n"
3619                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
3620                "        PRIMEM[\"Greenwich\",0],\n"
3621                "        UNIT[\"degree\",0.0174532925199433]],\n"
3622                "    PROJECTION[\"Polar_Stereographic\"],\n"
3623                "    PARAMETER[\"latitude_of_origin\",-90],\n"
3624                "    PARAMETER[\"central_meridian\",2],\n"
3625                "    PARAMETER[\"scale_factor\",3],\n"
3626                "    PARAMETER[\"false_easting\",4],\n"
3627                "    PARAMETER[\"false_northing\",5],\n"
3628                "    UNIT[\"metre\",1]]";
3629     auto obj = WKTParser().createFromWKT(wkt);
3630     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3631     ASSERT_TRUE(crs != nullptr);
3632 
3633     auto conversion = crs->derivingConversion();
3634     auto convRef = Conversion::createPolarStereographicVariantA(
3635         PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(-90),
3636         Angle(2), Scale(3), Length(4), Length(5));
3637 
3638     EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()),
3639               convRef->exportToWKT(WKTFormatter::create().get()));
3640 }
3641 
3642 // ---------------------------------------------------------------------------
3643 
TEST(operation,wkt1_import_polar_stereographic_variantB)3644 TEST(operation, wkt1_import_polar_stereographic_variantB) {
3645     auto wkt = "PROJCS[\"test\",\n"
3646                "    GEOGCS[\"WGS 84\",\n"
3647                "        DATUM[\"WGS 1984\",\n"
3648                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
3649                "        PRIMEM[\"Greenwich\",0],\n"
3650                "        UNIT[\"degree\",0.0174532925199433]],\n"
3651                "    PROJECTION[\"Polar_Stereographic\"],\n"
3652                "    PARAMETER[\"latitude_of_origin\",-70],\n"
3653                "    PARAMETER[\"central_meridian\",2],\n"
3654                "    PARAMETER[\"scale_factor\",1],\n"
3655                "    PARAMETER[\"false_easting\",4],\n"
3656                "    PARAMETER[\"false_northing\",5],\n"
3657                "    UNIT[\"metre\",1]]";
3658     auto obj = WKTParser().createFromWKT(wkt);
3659     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3660     ASSERT_TRUE(crs != nullptr);
3661 
3662     auto conversion = crs->derivingConversion();
3663     auto convRef = Conversion::createPolarStereographicVariantB(
3664         PropertyMap().set(IdentifiedObject::NAME_KEY, "unnamed"), Angle(-70),
3665         Angle(2), Length(4), Length(5));
3666 
3667     EXPECT_EQ(conversion->exportToWKT(WKTFormatter::create().get()),
3668               convRef->exportToWKT(WKTFormatter::create().get()));
3669 }
3670 
3671 // ---------------------------------------------------------------------------
3672 
TEST(operation,wkt1_import_polar_stereographic_ambiguous)3673 TEST(operation, wkt1_import_polar_stereographic_ambiguous) {
3674     auto wkt = "PROJCS[\"test\",\n"
3675                "    GEOGCS[\"WGS 84\",\n"
3676                "        DATUM[\"WGS 1984\",\n"
3677                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
3678                "        PRIMEM[\"Greenwich\",0],\n"
3679                "        UNIT[\"degree\",0.0174532925199433]],\n"
3680                "    PROJECTION[\"Polar_Stereographic\"],\n"
3681                "    PARAMETER[\"latitude_of_origin\",-70],\n"
3682                "    PARAMETER[\"central_meridian\",2],\n"
3683                "    PARAMETER[\"scale_factor\",3],\n"
3684                "    PARAMETER[\"false_easting\",4],\n"
3685                "    PARAMETER[\"false_northing\",5],\n"
3686                "    UNIT[\"metre\",1]]";
3687     auto obj = WKTParser().createFromWKT(wkt);
3688     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3689     ASSERT_TRUE(crs != nullptr);
3690 
3691     auto conversion = crs->derivingConversion();
3692     EXPECT_EQ(conversion->method()->nameStr(), "Polar_Stereographic");
3693 }
3694 
3695 // ---------------------------------------------------------------------------
3696 
TEST(operation,wkt1_import_equivalent_parameters)3697 TEST(operation, wkt1_import_equivalent_parameters) {
3698     auto wkt = "PROJCS[\"test\",\n"
3699                "    GEOGCS[\"WGS 84\",\n"
3700                "        DATUM[\"WGS 1984\",\n"
3701                "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
3702                "        PRIMEM[\"Greenwich\",0],\n"
3703                "        UNIT[\"degree\",0.0174532925199433]],\n"
3704                "    PROJECTION[\"Hotine Oblique Mercator Two Point Natural "
3705                "Origin\"],\n"
3706                "    PARAMETER[\"latitude_of_origin\",1],\n"
3707                "    PARAMETER[\"Latitude_Of_1st_Point\",2],\n"
3708                "    PARAMETER[\"Longitude_Of_1st_Point\",3],\n"
3709                "    PARAMETER[\"Latitude_Of_2nd_Point\",4],\n"
3710                "    PARAMETER[\"Longitude_Of 2nd_Point\",5],\n"
3711                "    PARAMETER[\"scale_factor\",6],\n"
3712                "    PARAMETER[\"false_easting\",7],\n"
3713                "    PARAMETER[\"false_northing\",8],\n"
3714                "    UNIT[\"metre\",1]]";
3715     auto obj = WKTParser().createFromWKT(wkt);
3716     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
3717     ASSERT_TRUE(crs != nullptr);
3718 
3719     auto conversion = crs->derivingConversion();
3720     auto convRef = Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(
3721         PropertyMap(), Angle(1), Angle(2), Angle(3), Angle(4), Angle(5),
3722         Scale(6), Length(7), Length(8));
3723 
3724     EXPECT_EQ(
3725         conversion->exportToWKT(
3726             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3727         convRef->exportToWKT(
3728             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()));
3729 }
3730 
3731 // ---------------------------------------------------------------------------
3732 
TEST(operation,robinson_export)3733 TEST(operation, robinson_export) {
3734 
3735     auto conv = Conversion::createRobinson(PropertyMap(), Angle(1), Length(2),
3736                                            Length(3));
3737     EXPECT_TRUE(conv->validateParameters().empty());
3738 
3739     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3740               "+proj=robin +lon_0=1 +x_0=2 +y_0=3");
3741 
3742     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3743               "CONVERSION[\"Robinson\",\n"
3744               "    METHOD[\"Robinson\"],\n"
3745               "    PARAMETER[\"Longitude of natural origin\",1,\n"
3746               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3747               "        ID[\"EPSG\",8802]],\n"
3748               "    PARAMETER[\"False easting\",2,\n"
3749               "        LENGTHUNIT[\"metre\",1],\n"
3750               "        ID[\"EPSG\",8806]],\n"
3751               "    PARAMETER[\"False northing\",3,\n"
3752               "        LENGTHUNIT[\"metre\",1],\n"
3753               "        ID[\"EPSG\",8807]]]");
3754 
3755     EXPECT_EQ(
3756         conv->exportToWKT(
3757             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3758         "PROJECTION[\"Robinson\"],\n"
3759         "PARAMETER[\"longitude_of_center\",1],\n"
3760         "PARAMETER[\"false_easting\",2],\n"
3761         "PARAMETER[\"false_northing\",3]");
3762 }
3763 
3764 // ---------------------------------------------------------------------------
3765 
TEST(operation,sinusoidal_export)3766 TEST(operation, sinusoidal_export) {
3767 
3768     auto conv = Conversion::createSinusoidal(PropertyMap(), Angle(1), Length(2),
3769                                              Length(3));
3770     EXPECT_TRUE(conv->validateParameters().empty());
3771 
3772     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3773               "+proj=sinu +lon_0=1 +x_0=2 +y_0=3");
3774 
3775     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3776               "CONVERSION[\"Sinusoidal\",\n"
3777               "    METHOD[\"Sinusoidal\"],\n"
3778               "    PARAMETER[\"Longitude of natural origin\",1,\n"
3779               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3780               "        ID[\"EPSG\",8802]],\n"
3781               "    PARAMETER[\"False easting\",2,\n"
3782               "        LENGTHUNIT[\"metre\",1],\n"
3783               "        ID[\"EPSG\",8806]],\n"
3784               "    PARAMETER[\"False northing\",3,\n"
3785               "        LENGTHUNIT[\"metre\",1],\n"
3786               "        ID[\"EPSG\",8807]]]");
3787 
3788     EXPECT_EQ(
3789         conv->exportToWKT(
3790             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3791         "PROJECTION[\"Sinusoidal\"],\n"
3792         "PARAMETER[\"longitude_of_center\",1],\n"
3793         "PARAMETER[\"false_easting\",2],\n"
3794         "PARAMETER[\"false_northing\",3]");
3795 }
3796 
3797 // ---------------------------------------------------------------------------
3798 
TEST(operation,stereographic_export)3799 TEST(operation, stereographic_export) {
3800     auto conv = Conversion::createStereographic(
3801         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
3802     EXPECT_TRUE(conv->validateParameters().empty());
3803 
3804     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3805               "+proj=stere +lat_0=1 +lon_0=2 +k=3 +x_0=4 +y_0=5");
3806 
3807     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3808               "CONVERSION[\"Stereographic\",\n"
3809               "    METHOD[\"Stereographic\"],\n"
3810               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3811               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3812               "        ID[\"EPSG\",8801]],\n"
3813               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3814               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3815               "        ID[\"EPSG\",8802]],\n"
3816               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
3817               "        SCALEUNIT[\"unity\",1],\n"
3818               "        ID[\"EPSG\",8805]],\n"
3819               "    PARAMETER[\"False easting\",4,\n"
3820               "        LENGTHUNIT[\"metre\",1],\n"
3821               "        ID[\"EPSG\",8806]],\n"
3822               "    PARAMETER[\"False northing\",5,\n"
3823               "        LENGTHUNIT[\"metre\",1],\n"
3824               "        ID[\"EPSG\",8807]]]");
3825 
3826     EXPECT_EQ(
3827         conv->exportToWKT(
3828             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3829         "PROJECTION[\"Stereographic\"],\n"
3830         "PARAMETER[\"latitude_of_origin\",1],\n"
3831         "PARAMETER[\"central_meridian\",2],\n"
3832         "PARAMETER[\"scale_factor\",3],\n"
3833         "PARAMETER[\"false_easting\",4],\n"
3834         "PARAMETER[\"false_northing\",5]");
3835 }
3836 
3837 // ---------------------------------------------------------------------------
3838 
TEST(operation,vandergrinten_export)3839 TEST(operation, vandergrinten_export) {
3840 
3841     auto conv = Conversion::createVanDerGrinten(PropertyMap(), Angle(1),
3842                                                 Length(2), Length(3));
3843     EXPECT_TRUE(conv->validateParameters().empty());
3844 
3845     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3846               "+proj=vandg +R_A +lon_0=1 +x_0=2 +y_0=3");
3847 
3848     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3849               "CONVERSION[\"Van Der Grinten\",\n"
3850               "    METHOD[\"Van Der Grinten\"],\n"
3851               "    PARAMETER[\"Longitude of natural origin\",1,\n"
3852               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3853               "        ID[\"EPSG\",8802]],\n"
3854               "    PARAMETER[\"False easting\",2,\n"
3855               "        LENGTHUNIT[\"metre\",1],\n"
3856               "        ID[\"EPSG\",8806]],\n"
3857               "    PARAMETER[\"False northing\",3,\n"
3858               "        LENGTHUNIT[\"metre\",1],\n"
3859               "        ID[\"EPSG\",8807]]]");
3860 
3861     EXPECT_EQ(
3862         conv->exportToWKT(
3863             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3864         "PROJECTION[\"VanDerGrinten\"],\n"
3865         "PARAMETER[\"central_meridian\",1],\n"
3866         "PARAMETER[\"false_easting\",2],\n"
3867         "PARAMETER[\"false_northing\",3]");
3868 }
3869 
3870 // ---------------------------------------------------------------------------
3871 
TEST(operation,wagner_export)3872 TEST(operation, wagner_export) {
3873 
3874     std::vector<std::string> numbers{"", "1", "2", "3", "4", "5", "6", "7"};
3875     std::vector<std::string> latinNumbers{"",   "I", "II", "III",
3876                                           "IV", "V", "VI", "VII"};
3877 
3878     for (int i = 1; i <= 7; i++) {
3879         if (i == 3)
3880             continue;
3881         auto conv =
3882             (i == 1)
3883                 ? Conversion::createWagnerI(PropertyMap(), Angle(1), Length(2),
3884                                             Length(3))
3885                 : (i == 2)
3886                       ? Conversion::createWagnerII(PropertyMap(), Angle(1),
3887                                                    Length(2), Length(3))
3888                       : (i == 4)
3889                             ? Conversion::createWagnerIV(
3890                                   PropertyMap(), Angle(1), Length(2), Length(3))
3891                             : (i == 5) ? Conversion::createWagnerV(
3892                                              PropertyMap(), Angle(1), Length(2),
3893                                              Length(3))
3894                                        : (i == 6) ?
3895 
3896                                                   Conversion::createWagnerVI(
3897                                                       PropertyMap(), Angle(1),
3898                                                       Length(2), Length(3))
3899                                                   :
3900 
3901                                                   Conversion::createWagnerVII(
3902                                                       PropertyMap(), Angle(1),
3903                                                       Length(2), Length(3));
3904         EXPECT_TRUE(conv->validateParameters().empty());
3905 
3906         EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3907                   "+proj=wag" + numbers[i] + " +lon_0=1 +x_0=2 +y_0=3");
3908 
3909         auto formatter = WKTFormatter::create();
3910         formatter->simulCurNodeHasId();
3911         EXPECT_EQ(conv->exportToWKT(formatter.get()),
3912                   "CONVERSION[\"Wagner " + latinNumbers[i] +
3913                       "\",\n"
3914                       "    METHOD[\"Wagner " +
3915                       latinNumbers[i] +
3916                       "\"],\n"
3917                       "    PARAMETER[\"Longitude of natural origin\",1,\n"
3918                       "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3919                       "        ID[\"EPSG\",8802]],\n"
3920                       "    PARAMETER[\"False easting\",2,\n"
3921                       "        LENGTHUNIT[\"metre\",1],\n"
3922                       "        ID[\"EPSG\",8806]],\n"
3923                       "    PARAMETER[\"False northing\",3,\n"
3924                       "        LENGTHUNIT[\"metre\",1],\n"
3925                       "        ID[\"EPSG\",8807]]]");
3926 
3927         EXPECT_EQ(conv->exportToWKT(
3928                       WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL)
3929                           .get()),
3930                   "PROJECTION[\"Wagner_" + latinNumbers[i] +
3931                       "\"],\n"
3932                       "PARAMETER[\"central_meridian\",1],\n"
3933                       "PARAMETER[\"false_easting\",2],\n"
3934                       "PARAMETER[\"false_northing\",3]");
3935     }
3936 }
3937 
3938 // ---------------------------------------------------------------------------
3939 
TEST(operation,wagnerIII_export)3940 TEST(operation, wagnerIII_export) {
3941 
3942     auto conv = Conversion::createWagnerIII(PropertyMap(), Angle(1), Angle(2),
3943                                             Length(3), Length(4));
3944 
3945     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3946               "+proj=wag3 +lat_ts=1 +lon_0=2 +x_0=3 +y_0=4");
3947 
3948     auto formatter = WKTFormatter::create();
3949     formatter->simulCurNodeHasId();
3950     EXPECT_EQ(conv->exportToWKT(formatter.get()),
3951               "CONVERSION[\"Wagner III\",\n"
3952               "    METHOD[\"Wagner III\"],\n"
3953               "    PARAMETER[\"Latitude of true scale\",1,\n"
3954               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
3955               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3956               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3957               "        ID[\"EPSG\",8802]],\n"
3958               "    PARAMETER[\"False easting\",3,\n"
3959               "        LENGTHUNIT[\"metre\",1],\n"
3960               "        ID[\"EPSG\",8806]],\n"
3961               "    PARAMETER[\"False northing\",4,\n"
3962               "        LENGTHUNIT[\"metre\",1],\n"
3963               "        ID[\"EPSG\",8807]]]");
3964 
3965     EXPECT_EQ(
3966         conv->exportToWKT(
3967             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
3968         "PROJECTION[\"Wagner_III\"],\n"
3969         "PARAMETER[\"latitude_of_origin\",1],\n"
3970         "PARAMETER[\"central_meridian\",2],\n"
3971         "PARAMETER[\"false_easting\",3],\n"
3972         "PARAMETER[\"false_northing\",4]");
3973 }
3974 
3975 // ---------------------------------------------------------------------------
3976 
TEST(operation,qsc_export)3977 TEST(operation, qsc_export) {
3978 
3979     auto conv = Conversion::createQuadrilateralizedSphericalCube(
3980         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4));
3981     EXPECT_TRUE(conv->validateParameters().empty());
3982 
3983     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
3984               "+proj=qsc +lat_0=1 +lon_0=2 +x_0=3 +y_0=4");
3985 
3986     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
3987               "CONVERSION[\"Quadrilateralized Spherical Cube\",\n"
3988               "    METHOD[\"Quadrilateralized Spherical Cube\"],\n"
3989               "    PARAMETER[\"Latitude of natural origin\",1,\n"
3990               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3991               "        ID[\"EPSG\",8801]],\n"
3992               "    PARAMETER[\"Longitude of natural origin\",2,\n"
3993               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
3994               "        ID[\"EPSG\",8802]],\n"
3995               "    PARAMETER[\"False easting\",3,\n"
3996               "        LENGTHUNIT[\"metre\",1],\n"
3997               "        ID[\"EPSG\",8806]],\n"
3998               "    PARAMETER[\"False northing\",4,\n"
3999               "        LENGTHUNIT[\"metre\",1],\n"
4000               "        ID[\"EPSG\",8807]]]");
4001 
4002     EXPECT_EQ(
4003         conv->exportToWKT(
4004             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
4005         "PROJECTION[\"Quadrilateralized_Spherical_Cube\"],\n"
4006         "PARAMETER[\"latitude_of_origin\",1],\n"
4007         "PARAMETER[\"central_meridian\",2],\n"
4008         "PARAMETER[\"false_easting\",3],\n"
4009         "PARAMETER[\"false_northing\",4]");
4010 }
4011 
4012 // ---------------------------------------------------------------------------
4013 
TEST(operation,sch_export)4014 TEST(operation, sch_export) {
4015 
4016     auto conv = Conversion::createSphericalCrossTrackHeight(
4017         PropertyMap(), Angle(1), Angle(2), Angle(3), Length(4));
4018     EXPECT_TRUE(conv->validateParameters().empty());
4019 
4020     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4021               "+proj=sch +plat_0=1 +plon_0=2 +phdg_0=3 +h_0=4");
4022 
4023     auto formatter = WKTFormatter::create();
4024     formatter->simulCurNodeHasId();
4025     EXPECT_EQ(conv->exportToWKT(formatter.get()),
4026               "CONVERSION[\"Spherical Cross-Track Height\",\n"
4027               "    METHOD[\"Spherical Cross-Track Height\"],\n"
4028               "    PARAMETER[\"Peg point latitude\",1,\n"
4029               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4030               "    PARAMETER[\"Peg point longitude\",2,\n"
4031               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4032               "    PARAMETER[\"Peg point heading\",3,\n"
4033               "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4034               "    PARAMETER[\"Peg point height\",4,\n"
4035               "        LENGTHUNIT[\"metre\",1]]]");
4036 
4037     EXPECT_EQ(
4038         conv->exportToWKT(
4039             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
4040         "PROJECTION[\"Spherical_Cross_Track_Height\"],\n"
4041         "PARAMETER[\"peg_point_latitude\",1],\n"
4042         "PARAMETER[\"peg_point_longitude\",2],\n"
4043         "PARAMETER[\"peg_point_heading\",3],\n"
4044         "PARAMETER[\"peg_point_height\",4]");
4045 }
4046 
4047 // ---------------------------------------------------------------------------
4048 
TEST(operation,conversion_inverse)4049 TEST(operation, conversion_inverse) {
4050     auto conv = Conversion::createTransverseMercator(
4051         PropertyMap(), Angle(1), Angle(2), Scale(3), Length(4), Length(5));
4052     auto inv = conv->inverse();
4053     EXPECT_EQ(inv->inverse(), conv);
4054     EXPECT_EQ(inv->exportToWKT(WKTFormatter::create().get()),
4055               "CONVERSION[\"Inverse of Transverse Mercator\",\n"
4056               "    METHOD[\"Inverse of Transverse Mercator\",\n"
4057               "        ID[\"INVERSE(EPSG)\",9807]],\n"
4058               "    PARAMETER[\"Latitude of natural origin\",1,\n"
4059               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4060               "        ID[\"EPSG\",8801]],\n"
4061               "    PARAMETER[\"Longitude of natural origin\",2,\n"
4062               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4063               "        ID[\"EPSG\",8802]],\n"
4064               "    PARAMETER[\"Scale factor at natural origin\",3,\n"
4065               "        SCALEUNIT[\"unity\",1],\n"
4066               "        ID[\"EPSG\",8805]],\n"
4067               "    PARAMETER[\"False easting\",4,\n"
4068               "        LENGTHUNIT[\"metre\",1],\n"
4069               "        ID[\"EPSG\",8806]],\n"
4070               "    PARAMETER[\"False northing\",5,\n"
4071               "        LENGTHUNIT[\"metre\",1],\n"
4072               "        ID[\"EPSG\",8807]]]");
4073 
4074     EXPECT_EQ(inv->exportToPROJString(PROJStringFormatter::create().get()),
4075               "+proj=pipeline +step +inv +proj=tmerc +lat_0=1 +lon_0=2 +k=3 "
4076               "+x_0=4 +y_0=5");
4077 
4078     EXPECT_TRUE(inv->isEquivalentTo(inv.get()));
4079     EXPECT_FALSE(inv->isEquivalentTo(createUnrelatedObject().get()));
4080 
4081     EXPECT_TRUE(
4082         conv->isEquivalentTo(conv->CoordinateOperation::shallowClone().get()));
4083     EXPECT_TRUE(
4084         inv->isEquivalentTo(inv->CoordinateOperation::shallowClone().get()));
4085 }
4086 
4087 // ---------------------------------------------------------------------------
4088 
TEST(operation,eqearth_export)4089 TEST(operation, eqearth_export) {
4090 
4091     auto conv = Conversion::createEqualEarth(PropertyMap(), Angle(1), Length(2),
4092                                              Length(3));
4093 
4094     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4095               "+proj=eqearth +lon_0=1 +x_0=2 +y_0=3");
4096 
4097     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
4098               "CONVERSION[\"Equal Earth\",\n"
4099               "    METHOD[\"Equal Earth\",\n"
4100               "        ID[\"EPSG\",1078]],\n"
4101               "    PARAMETER[\"Longitude of natural origin\",1,\n"
4102               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4103               "        ID[\"EPSG\",8802]],\n"
4104               "    PARAMETER[\"False easting\",2,\n"
4105               "        LENGTHUNIT[\"metre\",1],\n"
4106               "        ID[\"EPSG\",8806]],\n"
4107               "    PARAMETER[\"False northing\",3,\n"
4108               "        LENGTHUNIT[\"metre\",1],\n"
4109               "        ID[\"EPSG\",8807]]]");
4110 }
4111 
4112 // ---------------------------------------------------------------------------
4113 
TEST(operation,vertical_perspective_export)4114 TEST(operation, vertical_perspective_export) {
4115 
4116     auto conv = Conversion::createVerticalPerspective(
4117         PropertyMap(), Angle(1), Angle(2), Length(3), Length(4), Length(5),
4118         Length(6));
4119 
4120     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4121               "+proj=nsper +lat_0=1 +lon_0=2 +h=4 +x_0=5 +y_0=6");
4122 
4123     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
4124               "CONVERSION[\"Vertical Perspective\",\n"
4125               "    METHOD[\"Vertical Perspective\",\n"
4126               "        ID[\"EPSG\",9838]],\n"
4127               "    PARAMETER[\"Latitude of topocentric origin\",1,\n"
4128               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4129               "        ID[\"EPSG\",8834]],\n"
4130               "    PARAMETER[\"Longitude of topocentric origin\",2,\n"
4131               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4132               "        ID[\"EPSG\",8835]],\n"
4133               "    PARAMETER[\"Ellipsoidal height of topocentric origin\",3,\n"
4134               "        LENGTHUNIT[\"metre\",1],\n"
4135               "        ID[\"EPSG\",8836]],\n"
4136               "    PARAMETER[\"Viewpoint height\",4,\n"
4137               "        LENGTHUNIT[\"metre\",1],\n"
4138               "        ID[\"EPSG\",8840]],\n"
4139               "    PARAMETER[\"False easting\",5,\n"
4140               "        LENGTHUNIT[\"metre\",1],\n"
4141               "        ID[\"EPSG\",8806]],\n"
4142               "    PARAMETER[\"False northing\",6,\n"
4143               "        LENGTHUNIT[\"metre\",1],\n"
4144               "        ID[\"EPSG\",8807]]]");
4145 }
4146 
4147 // ---------------------------------------------------------------------------
4148 
TEST(operation,vertical_perspective_export_no_topocentric_height_and_false_easting_northing)4149 TEST(
4150     operation,
4151     vertical_perspective_export_no_topocentric_height_and_false_easting_northing) {
4152 
4153     auto conv = Conversion::createVerticalPerspective(
4154         PropertyMap(), Angle(1), Angle(2), Length(0), Length(4), Length(0),
4155         Length(0));
4156 
4157     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4158               "+proj=nsper +lat_0=1 +lon_0=2 +h=4 +x_0=0 +y_0=0");
4159 
4160     // Check that False esting and False northing are not exported, when they
4161     // are 0.
4162     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
4163               "CONVERSION[\"Vertical Perspective\",\n"
4164               "    METHOD[\"Vertical Perspective\",\n"
4165               "        ID[\"EPSG\",9838]],\n"
4166               "    PARAMETER[\"Latitude of topocentric origin\",1,\n"
4167               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4168               "        ID[\"EPSG\",8834]],\n"
4169               "    PARAMETER[\"Longitude of topocentric origin\",2,\n"
4170               "        ANGLEUNIT[\"degree\",0.0174532925199433],\n"
4171               "        ID[\"EPSG\",8835]],\n"
4172               "    PARAMETER[\"Ellipsoidal height of topocentric origin\",0,\n"
4173               "        LENGTHUNIT[\"metre\",1],\n"
4174               "        ID[\"EPSG\",8836]],\n"
4175               "    PARAMETER[\"Viewpoint height\",4,\n"
4176               "        LENGTHUNIT[\"metre\",1],\n"
4177               "        ID[\"EPSG\",8840]]]");
4178 }
4179 
4180 // ---------------------------------------------------------------------------
4181 
TEST(operation,laborde_oblique_mercator)4182 TEST(operation, laborde_oblique_mercator) {
4183 
4184     // Content of EPSG:29701 "Tananarive (Paris) / Laborde Grid"
4185     auto projString = "+proj=labrd +lat_0=-18.9 +lon_0=44.1 +azi=18.9 "
4186                       "+k=0.9995 +x_0=400000 +y_0=800000 +ellps=intl +pm=paris "
4187                       "+units=m +no_defs +type=crs";
4188     auto obj = PROJStringParser().createFromPROJString(projString);
4189     auto crs = nn_dynamic_pointer_cast<ProjectedCRS>(obj);
4190     ASSERT_TRUE(crs != nullptr);
4191     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
4192               projString);
4193 }
4194 
4195 // ---------------------------------------------------------------------------
4196 
TEST(operation,adams_ws2_export)4197 TEST(operation, adams_ws2_export) {
4198     auto dbContext = DatabaseContext::create();
4199     // ESRI:54098 WGS_1984_Adams_Square_II
4200     auto crs = AuthorityFactory::create(dbContext, "ESRI")
4201                    ->createProjectedCRS("54098");
4202     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
4203               "+proj=adams_ws2 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m "
4204               "+no_defs +type=crs");
4205 }
4206 
4207 // ---------------------------------------------------------------------------
4208 
TEST(operation,adams_ws2_export_failure)4209 TEST(operation, adams_ws2_export_failure) {
4210     auto dbContext = DatabaseContext::create();
4211     // ESRI:54099 WGS_1984_Spilhaus_Ocean_Map_in_Square
4212     auto crs = AuthorityFactory::create(dbContext, "ESRI")
4213                    ->createProjectedCRS("54099");
4214     EXPECT_THROW(crs->exportToPROJString(PROJStringFormatter::create().get()),
4215                  FormattingException);
4216 }
4217 
4218 // ---------------------------------------------------------------------------
4219 
TEST(operation,PROJ_based)4220 TEST(operation, PROJ_based) {
4221     auto conv = SingleOperation::createPROJBased(PropertyMap(), "+proj=merc",
4222                                                  nullptr, nullptr);
4223 
4224     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4225               "+proj=merc");
4226 
4227     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
4228               "CONVERSION[\"PROJ-based coordinate operation\",\n"
4229               "    METHOD[\"PROJ-based operation method: +proj=merc\"]]");
4230 
4231     EXPECT_EQ(conv->inverse()->exportToPROJString(
4232                   PROJStringFormatter::create().get()),
4233               "+proj=pipeline +step +inv +proj=merc");
4234 
4235     auto str = "+proj=pipeline +step +proj=unitconvert +xy_in=grad +xy_out=rad "
4236                "+step +proj=axisswap +order=2,1 +step +proj=longlat "
4237                "+ellps=clrk80ign +pm=paris +step +proj=axisswap +order=2,1";
4238     EXPECT_EQ(
4239         SingleOperation::createPROJBased(PropertyMap(), str, nullptr, nullptr)
4240             ->exportToPROJString(PROJStringFormatter::create().get()),
4241         str);
4242 
4243     EXPECT_THROW(SingleOperation::createPROJBased(
4244                      PropertyMap(), "+proj=pipeline +step +proj=pipeline",
4245                      nullptr, nullptr)
4246                      ->exportToPROJString(PROJStringFormatter::create().get()),
4247                  FormattingException);
4248 }
4249 
4250 // ---------------------------------------------------------------------------
4251 
TEST(operation,PROJ_based_empty)4252 TEST(operation, PROJ_based_empty) {
4253     auto conv =
4254         SingleOperation::createPROJBased(PropertyMap(), "", nullptr, nullptr);
4255 
4256     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4257               "+proj=noop");
4258 
4259     EXPECT_EQ(conv->exportToWKT(WKTFormatter::create().get()),
4260               "CONVERSION[\"PROJ-based coordinate operation\",\n"
4261               "    METHOD[\"PROJ-based operation method: \"]]");
4262 
4263     EXPECT_THROW(
4264         conv->exportToWKT(
4265             WKTFormatter::create(WKTFormatter::Convention::WKT1_GDAL).get()),
4266         FormattingException);
4267 
4268     EXPECT_EQ(conv->inverse()->exportToPROJString(
4269                   PROJStringFormatter::create().get()),
4270               "+proj=noop");
4271 }
4272 
4273 // ---------------------------------------------------------------------------
4274 
TEST(operation,PROJ_based_with_global_parameters)4275 TEST(operation, PROJ_based_with_global_parameters) {
4276     auto conv = SingleOperation::createPROJBased(
4277         PropertyMap(), "+proj=pipeline +ellps=WGS84 +step +proj=longlat",
4278         nullptr, nullptr);
4279 
4280     EXPECT_EQ(conv->exportToPROJString(PROJStringFormatter::create().get()),
4281               "+proj=pipeline +ellps=WGS84 +step +proj=longlat");
4282 }
4283 
4284 // ---------------------------------------------------------------------------
4285 
TEST(operation,mercator_variant_A_to_variant_B)4286 TEST(operation, mercator_variant_A_to_variant_B) {
4287     auto projCRS = ProjectedCRS::create(
4288         PropertyMap(), GeographicCRS::EPSG_4326,
4289         Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1),
4290                                            Scale(0.9), Length(3), Length(4)),
4291         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4292 
4293     auto conv = projCRS->derivingConversion();
4294     auto sameConv =
4295         conv->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_A);
4296     ASSERT_TRUE(sameConv);
4297     EXPECT_TRUE(sameConv->isEquivalentTo(conv.get()));
4298 
4299     auto targetConv =
4300         conv->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_B);
4301     ASSERT_TRUE(targetConv);
4302 
4303     auto lat_1 = targetConv->parameterValueNumeric(
4304         EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, UnitOfMeasure::DEGREE);
4305     EXPECT_EQ(lat_1, 25.917499691810534) << lat_1;
4306 
4307     EXPECT_EQ(targetConv->parameterValueNumeric(
4308                   EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN,
4309                   UnitOfMeasure::DEGREE),
4310               1);
4311 
4312     EXPECT_EQ(targetConv->parameterValueNumeric(
4313                   EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE),
4314               3);
4315 
4316     EXPECT_EQ(targetConv->parameterValueNumeric(
4317                   EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE),
4318               4);
4319 
4320     EXPECT_FALSE(
4321         conv->isEquivalentTo(targetConv.get(), IComparable::Criterion::STRICT));
4322     EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(),
4323                                      IComparable::Criterion::EQUIVALENT));
4324     EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(),
4325                                            IComparable::Criterion::EQUIVALENT));
4326 }
4327 
4328 // ---------------------------------------------------------------------------
4329 
TEST(operation,mercator_variant_A_to_variant_B_scale_1)4330 TEST(operation, mercator_variant_A_to_variant_B_scale_1) {
4331     auto projCRS = ProjectedCRS::create(
4332         PropertyMap(), GeographicCRS::EPSG_4326,
4333         Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1),
4334                                            Scale(1.0), Length(3), Length(4)),
4335         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4336 
4337     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4338         EPSG_CODE_METHOD_MERCATOR_VARIANT_B);
4339     ASSERT_TRUE(targetConv);
4340 
4341     auto lat_1 = targetConv->parameterValueNumeric(
4342         EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL, UnitOfMeasure::DEGREE);
4343     EXPECT_EQ(lat_1, 0.0) << lat_1;
4344 }
4345 
4346 // ---------------------------------------------------------------------------
4347 
TEST(operation,mercator_variant_A_to_variant_B_no_crs)4348 TEST(operation, mercator_variant_A_to_variant_B_no_crs) {
4349     auto targetConv =
4350         Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1),
4351                                            Scale(1.0), Length(3), Length(4))
4352             ->convertToOtherMethod(EPSG_CODE_METHOD_MERCATOR_VARIANT_B);
4353     EXPECT_FALSE(targetConv != nullptr);
4354 }
4355 
4356 // ---------------------------------------------------------------------------
4357 
TEST(operation,mercator_variant_A_to_variant_B_invalid_scale)4358 TEST(operation, mercator_variant_A_to_variant_B_invalid_scale) {
4359     auto projCRS = ProjectedCRS::create(
4360         PropertyMap(), GeographicCRS::EPSG_4326,
4361         Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1),
4362                                            Scale(0.0), Length(3), Length(4)),
4363         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4364 
4365     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4366         EPSG_CODE_METHOD_MERCATOR_VARIANT_B);
4367     EXPECT_FALSE(targetConv != nullptr);
4368 }
4369 
4370 // ---------------------------------------------------------------------------
4371 
geographicCRSInvalidEccentricity()4372 static GeographicCRSNNPtr geographicCRSInvalidEccentricity() {
4373     return GeographicCRS::create(
4374         PropertyMap(),
4375         GeodeticReferenceFrame::create(
4376             PropertyMap(), Ellipsoid::createFlattenedSphere(
4377                                PropertyMap(), Length(6378137), Scale(0.1)),
4378             optional<std::string>(), PrimeMeridian::GREENWICH),
4379         EllipsoidalCS::createLatitudeLongitude(UnitOfMeasure::DEGREE));
4380 }
4381 
4382 // ---------------------------------------------------------------------------
4383 
TEST(operation,mercator_variant_A_to_variant_B_invalid_eccentricity)4384 TEST(operation, mercator_variant_A_to_variant_B_invalid_eccentricity) {
4385     auto projCRS = ProjectedCRS::create(
4386         PropertyMap(), geographicCRSInvalidEccentricity(),
4387         Conversion::createMercatorVariantA(PropertyMap(), Angle(0), Angle(1),
4388                                            Scale(1.0), Length(3), Length(4)),
4389         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4390 
4391     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4392         EPSG_CODE_METHOD_MERCATOR_VARIANT_B);
4393     EXPECT_FALSE(targetConv != nullptr);
4394 }
4395 
4396 // ---------------------------------------------------------------------------
4397 
TEST(operation,mercator_variant_B_to_variant_A)4398 TEST(operation, mercator_variant_B_to_variant_A) {
4399     auto projCRS = ProjectedCRS::create(
4400         PropertyMap(), GeographicCRS::EPSG_4326,
4401         Conversion::createMercatorVariantB(PropertyMap(),
4402                                            Angle(25.917499691810534), Angle(1),
4403                                            Length(3), Length(4)),
4404         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4405     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4406         EPSG_CODE_METHOD_MERCATOR_VARIANT_A);
4407     ASSERT_TRUE(targetConv);
4408 
4409     EXPECT_EQ(targetConv->parameterValueNumeric(
4410                   EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN,
4411                   UnitOfMeasure::DEGREE),
4412               0);
4413 
4414     EXPECT_EQ(targetConv->parameterValueNumeric(
4415                   EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN,
4416                   UnitOfMeasure::DEGREE),
4417               1);
4418 
4419     auto k_0 = targetConv->parameterValueNumeric(
4420         EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN,
4421         UnitOfMeasure::SCALE_UNITY);
4422     EXPECT_EQ(k_0, 0.9) << k_0;
4423 
4424     EXPECT_EQ(targetConv->parameterValueNumeric(
4425                   EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE),
4426               3);
4427 
4428     EXPECT_EQ(targetConv->parameterValueNumeric(
4429                   EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE),
4430               4);
4431 }
4432 
4433 // ---------------------------------------------------------------------------
4434 
TEST(operation,mercator_variant_B_to_variant_A_invalid_std1)4435 TEST(operation, mercator_variant_B_to_variant_A_invalid_std1) {
4436     auto projCRS = ProjectedCRS::create(
4437         PropertyMap(), GeographicCRS::EPSG_4326,
4438         Conversion::createMercatorVariantB(PropertyMap(), Angle(100), Angle(1),
4439                                            Length(3), Length(4)),
4440         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4441     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4442         EPSG_CODE_METHOD_MERCATOR_VARIANT_A);
4443     EXPECT_FALSE(targetConv != nullptr);
4444 }
4445 
4446 // ---------------------------------------------------------------------------
4447 
TEST(operation,mercator_variant_B_to_variant_A_invalid_eccentricity)4448 TEST(operation, mercator_variant_B_to_variant_A_invalid_eccentricity) {
4449     auto projCRS = ProjectedCRS::create(
4450         PropertyMap(), geographicCRSInvalidEccentricity(),
4451         Conversion::createMercatorVariantB(PropertyMap(), Angle(0), Angle(1),
4452                                            Length(3), Length(4)),
4453         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4454     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4455         EPSG_CODE_METHOD_MERCATOR_VARIANT_A);
4456     EXPECT_FALSE(targetConv != nullptr);
4457 }
4458 
4459 // ---------------------------------------------------------------------------
4460 
TEST(operation,lcc2sp_to_lcc1sp)4461 TEST(operation, lcc2sp_to_lcc1sp) {
4462     // equivalent to EPSG:2154
4463     auto projCRS = ProjectedCRS::create(
4464         PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80
4465         Conversion::createLambertConicConformal_2SP(
4466             PropertyMap(), Angle(46.5), Angle(3), Angle(49), Angle(44),
4467             Length(700000), Length(6600000)),
4468         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4469 
4470     auto conv = projCRS->derivingConversion();
4471     auto targetConv = conv->convertToOtherMethod(
4472         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4473     ASSERT_TRUE(targetConv);
4474 
4475     {
4476         auto lat_0 = targetConv->parameterValueNumeric(
4477             EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN,
4478             UnitOfMeasure::DEGREE);
4479         EXPECT_NEAR(lat_0, 46.519430223986866, 1e-12) << lat_0;
4480 
4481         auto lon_0 = targetConv->parameterValueNumeric(
4482             EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN,
4483             UnitOfMeasure::DEGREE);
4484         EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0;
4485 
4486         auto k_0 = targetConv->parameterValueNumeric(
4487             EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN,
4488             UnitOfMeasure::SCALE_UNITY);
4489         EXPECT_NEAR(k_0, 0.9990510286374692, 1e-15) << k_0;
4490 
4491         auto x_0 = targetConv->parameterValueNumeric(
4492             EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE);
4493         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4494 
4495         auto y_0 = targetConv->parameterValueNumeric(
4496             EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE);
4497         EXPECT_NEAR(y_0, 6602157.8388103368, 1e-7) << y_0;
4498     }
4499 
4500     auto _2sp_from_1sp = targetConv->convertToOtherMethod(
4501         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4502     ASSERT_TRUE(_2sp_from_1sp);
4503 
4504     {
4505         auto lat_0 = _2sp_from_1sp->parameterValueNumeric(
4506             EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4507         EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0;
4508 
4509         auto lon_0 = _2sp_from_1sp->parameterValueNumeric(
4510             EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4511         EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0;
4512 
4513         auto lat_1 = _2sp_from_1sp->parameterValueNumeric(
4514             EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL,
4515             UnitOfMeasure::DEGREE);
4516         EXPECT_NEAR(lat_1, 49, 1e-15) << lat_1;
4517 
4518         auto lat_2 = _2sp_from_1sp->parameterValueNumeric(
4519             EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL,
4520             UnitOfMeasure::DEGREE);
4521         EXPECT_NEAR(lat_2, 44, 1e-15) << lat_2;
4522 
4523         auto x_0 = _2sp_from_1sp->parameterValueNumeric(
4524             EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4525         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4526 
4527         auto y_0 = _2sp_from_1sp->parameterValueNumeric(
4528             EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4529         EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0;
4530     }
4531 
4532     EXPECT_FALSE(
4533         conv->isEquivalentTo(targetConv.get(), IComparable::Criterion::STRICT));
4534     EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(),
4535                                      IComparable::Criterion::EQUIVALENT));
4536     EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(),
4537                                            IComparable::Criterion::EQUIVALENT));
4538 }
4539 
4540 // ---------------------------------------------------------------------------
4541 
TEST(operation,lcc2sp_to_lcc1sp_phi0_eq_phi1_eq_phi2)4542 TEST(operation, lcc2sp_to_lcc1sp_phi0_eq_phi1_eq_phi2) {
4543     auto projCRS = ProjectedCRS::create(
4544         PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80
4545         Conversion::createLambertConicConformal_2SP(
4546             PropertyMap(), Angle(46.5), Angle(3), Angle(46.5), Angle(46.5),
4547             Length(700000), Length(6600000)),
4548         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4549 
4550     auto conv = projCRS->derivingConversion();
4551     auto targetConv = conv->convertToOtherMethod(
4552         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4553     ASSERT_TRUE(targetConv);
4554 
4555     {
4556         auto lat_0 = targetConv->parameterValueNumeric(
4557             EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN,
4558             UnitOfMeasure::DEGREE);
4559         EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0;
4560 
4561         auto lon_0 = targetConv->parameterValueNumeric(
4562             EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN,
4563             UnitOfMeasure::DEGREE);
4564         EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0;
4565 
4566         auto k_0 = targetConv->parameterValueNumeric(
4567             EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN,
4568             UnitOfMeasure::SCALE_UNITY);
4569         EXPECT_NEAR(k_0, 1.0, 1e-15) << k_0;
4570 
4571         auto x_0 = targetConv->parameterValueNumeric(
4572             EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE);
4573         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4574 
4575         auto y_0 = targetConv->parameterValueNumeric(
4576             EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE);
4577         EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0;
4578     }
4579 
4580     auto _2sp_from_1sp = targetConv->convertToOtherMethod(
4581         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4582     ASSERT_TRUE(_2sp_from_1sp);
4583 
4584     {
4585         auto lat_0 = _2sp_from_1sp->parameterValueNumeric(
4586             EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4587         EXPECT_NEAR(lat_0, 46.5, 1e-15) << lat_0;
4588 
4589         auto lon_0 = _2sp_from_1sp->parameterValueNumeric(
4590             EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4591         EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0;
4592 
4593         auto lat_1 = _2sp_from_1sp->parameterValueNumeric(
4594             EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL,
4595             UnitOfMeasure::DEGREE);
4596         EXPECT_NEAR(lat_1, 46.5, 1e-15) << lat_1;
4597 
4598         auto lat_2 = _2sp_from_1sp->parameterValueNumeric(
4599             EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL,
4600             UnitOfMeasure::DEGREE);
4601         EXPECT_NEAR(lat_2, 46.5, 1e-15) << lat_2;
4602 
4603         auto x_0 = _2sp_from_1sp->parameterValueNumeric(
4604             EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4605         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4606 
4607         auto y_0 = _2sp_from_1sp->parameterValueNumeric(
4608             EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4609         EXPECT_NEAR(y_0, 6600000, 1e-15) << y_0;
4610     }
4611 
4612     EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(),
4613                                      IComparable::Criterion::EQUIVALENT));
4614     EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(),
4615                                            IComparable::Criterion::EQUIVALENT));
4616 }
4617 
4618 // ---------------------------------------------------------------------------
4619 
TEST(operation,lcc2sp_to_lcc1sp_phi0_diff_phi1_and_phi1_eq_phi2)4620 TEST(operation, lcc2sp_to_lcc1sp_phi0_diff_phi1_and_phi1_eq_phi2) {
4621 
4622     auto projCRS = ProjectedCRS::create(
4623         PropertyMap(), GeographicCRS::EPSG_4269, // something using GRS80
4624         Conversion::createLambertConicConformal_2SP(
4625             PropertyMap(), Angle(46.123), Angle(3), Angle(46.4567),
4626             Angle(46.4567), Length(700000), Length(6600000)),
4627         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4628 
4629     auto conv = projCRS->derivingConversion();
4630     auto targetConv = conv->convertToOtherMethod(
4631         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4632     ASSERT_TRUE(targetConv);
4633 
4634     {
4635         auto lat_0 = targetConv->parameterValueNumeric(
4636             EPSG_CODE_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN,
4637             UnitOfMeasure::DEGREE);
4638         EXPECT_NEAR(lat_0, 46.4567, 1e-14) << lat_0;
4639 
4640         auto lon_0 = targetConv->parameterValueNumeric(
4641             EPSG_CODE_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN,
4642             UnitOfMeasure::DEGREE);
4643         EXPECT_NEAR(lon_0, 3.0, 1e-15) << lon_0;
4644 
4645         auto k_0 = targetConv->parameterValueNumeric(
4646             EPSG_CODE_PARAMETER_SCALE_FACTOR_AT_NATURAL_ORIGIN,
4647             UnitOfMeasure::SCALE_UNITY);
4648         EXPECT_NEAR(k_0, 1.0, 1e-15) << k_0;
4649 
4650         auto x_0 = targetConv->parameterValueNumeric(
4651             EPSG_CODE_PARAMETER_FALSE_EASTING, UnitOfMeasure::METRE);
4652         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4653 
4654         auto y_0 = targetConv->parameterValueNumeric(
4655             EPSG_CODE_PARAMETER_FALSE_NORTHING, UnitOfMeasure::METRE);
4656         EXPECT_NEAR(y_0, 6637093.292952879, 1e-8) << y_0;
4657     }
4658 
4659     auto _2sp_from_1sp = targetConv->convertToOtherMethod(
4660         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4661     ASSERT_TRUE(_2sp_from_1sp);
4662 
4663     {
4664         auto lat_0 = _2sp_from_1sp->parameterValueNumeric(
4665             EPSG_CODE_PARAMETER_LATITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4666         EXPECT_NEAR(lat_0, 46.4567, 1e-14) << lat_0;
4667 
4668         auto lon_0 = _2sp_from_1sp->parameterValueNumeric(
4669             EPSG_CODE_PARAMETER_LONGITUDE_FALSE_ORIGIN, UnitOfMeasure::DEGREE);
4670         EXPECT_NEAR(lon_0, 3, 1e-15) << lon_0;
4671 
4672         auto lat_1 = _2sp_from_1sp->parameterValueNumeric(
4673             EPSG_CODE_PARAMETER_LATITUDE_1ST_STD_PARALLEL,
4674             UnitOfMeasure::DEGREE);
4675         EXPECT_NEAR(lat_1, 46.4567, 1e-14) << lat_1;
4676 
4677         auto lat_2 = _2sp_from_1sp->parameterValueNumeric(
4678             EPSG_CODE_PARAMETER_LATITUDE_2ND_STD_PARALLEL,
4679             UnitOfMeasure::DEGREE);
4680         EXPECT_NEAR(lat_2, 46.4567, 1e-14) << lat_2;
4681 
4682         auto x_0 = _2sp_from_1sp->parameterValueNumeric(
4683             EPSG_CODE_PARAMETER_EASTING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4684         EXPECT_NEAR(x_0, 700000, 1e-15) << x_0;
4685 
4686         auto y_0 = _2sp_from_1sp->parameterValueNumeric(
4687             EPSG_CODE_PARAMETER_NORTHING_FALSE_ORIGIN, UnitOfMeasure::METRE);
4688         EXPECT_NEAR(y_0, 6637093.292952879, 1e-8) << y_0;
4689     }
4690 
4691     EXPECT_TRUE(conv->isEquivalentTo(targetConv.get(),
4692                                      IComparable::Criterion::EQUIVALENT));
4693     EXPECT_TRUE(targetConv->isEquivalentTo(conv.get(),
4694                                            IComparable::Criterion::EQUIVALENT));
4695 
4696     EXPECT_TRUE(_2sp_from_1sp->isEquivalentTo(
4697         targetConv.get(), IComparable::Criterion::EQUIVALENT));
4698     EXPECT_TRUE(targetConv->isEquivalentTo(_2sp_from_1sp.get(),
4699                                            IComparable::Criterion::EQUIVALENT));
4700 
4701     EXPECT_TRUE(conv->isEquivalentTo(_2sp_from_1sp.get(),
4702                                      IComparable::Criterion::EQUIVALENT));
4703 }
4704 
4705 // ---------------------------------------------------------------------------
4706 
TEST(operation,lcc1sp_to_lcc2sp_invalid_eccentricity)4707 TEST(operation, lcc1sp_to_lcc2sp_invalid_eccentricity) {
4708     auto projCRS = ProjectedCRS::create(
4709         PropertyMap(), geographicCRSInvalidEccentricity(),
4710         Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(40),
4711                                                     Angle(1), Scale(0.99),
4712                                                     Length(3), Length(4)),
4713         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4714     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4715         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4716     EXPECT_FALSE(targetConv != nullptr);
4717 }
4718 
4719 // ---------------------------------------------------------------------------
4720 
TEST(operation,lcc1sp_to_lcc2sp_invalid_scale)4721 TEST(operation, lcc1sp_to_lcc2sp_invalid_scale) {
4722     auto projCRS = ProjectedCRS::create(
4723         PropertyMap(), GeographicCRS::EPSG_4326,
4724         Conversion::createLambertConicConformal_1SP(
4725             PropertyMap(), Angle(40), Angle(1), Scale(0), Length(3), Length(4)),
4726         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4727     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4728         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4729     EXPECT_FALSE(targetConv != nullptr);
4730 }
4731 
4732 // ---------------------------------------------------------------------------
4733 
TEST(operation,lcc1sp_to_lcc2sp_invalid_lat0)4734 TEST(operation, lcc1sp_to_lcc2sp_invalid_lat0) {
4735     auto projCRS = ProjectedCRS::create(
4736         PropertyMap(), GeographicCRS::EPSG_4326,
4737         Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(100),
4738                                                     Angle(1), Scale(0.99),
4739                                                     Length(3), Length(4)),
4740         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4741     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4742         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4743     EXPECT_FALSE(targetConv != nullptr);
4744 }
4745 
4746 // ---------------------------------------------------------------------------
4747 
TEST(operation,lcc1sp_to_lcc2sp_null_lat0)4748 TEST(operation, lcc1sp_to_lcc2sp_null_lat0) {
4749     auto projCRS = ProjectedCRS::create(
4750         PropertyMap(), GeographicCRS::EPSG_4326,
4751         Conversion::createLambertConicConformal_1SP(PropertyMap(), Angle(0),
4752                                                     Angle(1), Scale(0.99),
4753                                                     Length(3), Length(4)),
4754         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4755     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4756         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP);
4757     EXPECT_FALSE(targetConv != nullptr);
4758 }
4759 
4760 // ---------------------------------------------------------------------------
4761 
TEST(operation,lcc2sp_to_lcc1sp_invalid_lat0)4762 TEST(operation, lcc2sp_to_lcc1sp_invalid_lat0) {
4763     auto projCRS = ProjectedCRS::create(
4764         PropertyMap(), GeographicCRS::EPSG_4326,
4765         Conversion::createLambertConicConformal_2SP(
4766             PropertyMap(), Angle(100), Angle(3), Angle(44), Angle(49),
4767             Length(700000), Length(6600000)),
4768         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4769     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4770         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4771     EXPECT_FALSE(targetConv != nullptr);
4772 }
4773 
4774 // ---------------------------------------------------------------------------
4775 
TEST(operation,lcc2sp_to_lcc1sp_invalid_lat1)4776 TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1) {
4777     auto projCRS = ProjectedCRS::create(
4778         PropertyMap(), GeographicCRS::EPSG_4326,
4779         Conversion::createLambertConicConformal_2SP(
4780             PropertyMap(), Angle(46.5), Angle(3), Angle(100), Angle(49),
4781             Length(700000), Length(6600000)),
4782         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4783     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4784         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4785     EXPECT_FALSE(targetConv != nullptr);
4786 }
4787 
4788 // ---------------------------------------------------------------------------
4789 
TEST(operation,lcc2sp_to_lcc1sp_invalid_lat2)4790 TEST(operation, lcc2sp_to_lcc1sp_invalid_lat2) {
4791     auto projCRS = ProjectedCRS::create(
4792         PropertyMap(), GeographicCRS::EPSG_4326,
4793         Conversion::createLambertConicConformal_2SP(
4794             PropertyMap(), Angle(46.5), Angle(3), Angle(44), Angle(100),
4795             Length(700000), Length(6600000)),
4796         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4797     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4798         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4799     EXPECT_FALSE(targetConv != nullptr);
4800 }
4801 
4802 // ---------------------------------------------------------------------------
4803 
TEST(operation,lcc2sp_to_lcc1sp_invalid_lat1_opposite_lat2)4804 TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1_opposite_lat2) {
4805     auto projCRS = ProjectedCRS::create(
4806         PropertyMap(), GeographicCRS::EPSG_4326,
4807         Conversion::createLambertConicConformal_2SP(
4808             PropertyMap(), Angle(46.5), Angle(3), Angle(-49), Angle(49),
4809             Length(700000), Length(6600000)),
4810         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4811     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4812         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4813     EXPECT_FALSE(targetConv != nullptr);
4814 }
4815 
4816 // ---------------------------------------------------------------------------
4817 
TEST(operation,lcc2sp_to_lcc1sp_invalid_lat1_and_lat2_close_to_zero)4818 TEST(operation, lcc2sp_to_lcc1sp_invalid_lat1_and_lat2_close_to_zero) {
4819     auto projCRS = ProjectedCRS::create(
4820         PropertyMap(), GeographicCRS::EPSG_4326,
4821         Conversion::createLambertConicConformal_2SP(
4822             PropertyMap(), Angle(46.5), Angle(3), Angle(.0000000000000001),
4823             Angle(.0000000000000002), Length(700000), Length(6600000)),
4824         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4825     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4826         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4827     EXPECT_FALSE(targetConv != nullptr);
4828 }
4829 
4830 // ---------------------------------------------------------------------------
4831 
TEST(operation,lcc2sp_to_lcc1sp_invalid_eccentricity)4832 TEST(operation, lcc2sp_to_lcc1sp_invalid_eccentricity) {
4833     auto projCRS = ProjectedCRS::create(
4834         PropertyMap(), geographicCRSInvalidEccentricity(),
4835         Conversion::createLambertConicConformal_2SP(
4836             PropertyMap(), Angle(46.5), Angle(3), Angle(44), Angle(49),
4837             Length(700000), Length(6600000)),
4838         CartesianCS::createEastingNorthing(UnitOfMeasure::METRE));
4839     auto targetConv = projCRS->derivingConversion()->convertToOtherMethod(
4840         EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP);
4841     EXPECT_FALSE(targetConv != nullptr);
4842 }
4843 
4844 // ---------------------------------------------------------------------------
4845 
TEST(operation,three_param_equivalent_to_seven_param)4846 TEST(operation, three_param_equivalent_to_seven_param) {
4847 
4848     auto three_param = Transformation::createGeocentricTranslations(
4849         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4850         2.0, 3.0, {});
4851 
4852     auto seven_param_pv = Transformation::createPositionVector(
4853         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4854         2.0, 3.0, 0.0, 0.0, 0.0, 0.0, {});
4855 
4856     auto seven_param_cf = Transformation::createCoordinateFrameRotation(
4857         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4858         2.0, 3.0, 0.0, 0.0, 0.0, 0.0, {});
4859 
4860     auto seven_param_non_eq = Transformation::createPositionVector(
4861         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4862         2.0, 3.0, 1.0, 0.0, 0.0, 0.0, {});
4863 
4864     EXPECT_TRUE(three_param->isEquivalentTo(
4865         seven_param_pv.get(), IComparable::Criterion::EQUIVALENT));
4866 
4867     EXPECT_TRUE(three_param->isEquivalentTo(
4868         seven_param_cf.get(), IComparable::Criterion::EQUIVALENT));
4869 
4870     EXPECT_TRUE(seven_param_cf->isEquivalentTo(
4871         three_param.get(), IComparable::Criterion::EQUIVALENT));
4872 
4873     EXPECT_TRUE(seven_param_pv->isEquivalentTo(
4874         three_param.get(), IComparable::Criterion::EQUIVALENT));
4875 
4876     EXPECT_FALSE(three_param->isEquivalentTo(
4877         seven_param_non_eq.get(), IComparable::Criterion::EQUIVALENT));
4878 }
4879 
4880 // ---------------------------------------------------------------------------
4881 
TEST(operation,position_vector_equivalent_coordinate_frame)4882 TEST(operation, position_vector_equivalent_coordinate_frame) {
4883 
4884     auto pv = Transformation::createPositionVector(
4885         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4886         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, {});
4887 
4888     auto cf = Transformation::createCoordinateFrameRotation(
4889         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4890         2.0, 3.0, -4 + 1e-11, -5.0, -6.0, 7.0, {});
4891 
4892     auto cf_non_eq = Transformation::createCoordinateFrameRotation(
4893         PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326, 1.0,
4894         2.0, 3.0, 4.0, 5.0, 6.0, 7.0, {});
4895 
4896     EXPECT_TRUE(
4897         pv->isEquivalentTo(cf.get(), IComparable::Criterion::EQUIVALENT));
4898 
4899     EXPECT_TRUE(
4900         cf->isEquivalentTo(pv.get(), IComparable::Criterion::EQUIVALENT));
4901 
4902     EXPECT_FALSE(pv->isEquivalentTo(cf_non_eq.get(),
4903                                     IComparable::Criterion::EQUIVALENT));
4904 }
4905 
4906 // ---------------------------------------------------------------------------
4907 
TEST(operation,conversion_missing_parameter)4908 TEST(operation, conversion_missing_parameter) {
4909 
4910     auto wkt1 = "PROJCS[\"NAD83(CSRS98) / UTM zone 20N (deprecated)\","
4911                 "    GEOGCS[\"NAD83(CSRS98)\","
4912                 "        DATUM[\"NAD83_Canadian_Spatial_Reference_System\","
4913                 "            SPHEROID[\"GRS 1980\",6378137,298.257222101,"
4914                 "                AUTHORITY[\"EPSG\",\"7019\"]],"
4915                 "            AUTHORITY[\"EPSG\",\"6140\"]],"
4916                 "        PRIMEM[\"Greenwich\",0,"
4917                 "            AUTHORITY[\"EPSG\",\"8901\"]],"
4918                 "        UNIT[\"degree\",0.0174532925199433,"
4919                 "            AUTHORITY[\"EPSG\",\"9108\"]],"
4920                 "        AUTHORITY[\"EPSG\",\"4140\"]],"
4921                 "    PROJECTION[\"Transverse_Mercator\"],"
4922                 "    PARAMETER[\"latitude_of_origin\",0],"
4923                 "    PARAMETER[\"central_meridian\",-63],"
4924                 "    PARAMETER[\"scale_factor\",0.9996],"
4925                 "    PARAMETER[\"false_easting\",500000],"
4926                 "    UNIT[\"metre\",1,"
4927                 "        AUTHORITY[\"EPSG\",\"9001\"]],"
4928                 "    AXIS[\"Easting\",EAST],"
4929                 "    AXIS[\"Northing\",NORTH],"
4930                 "    AUTHORITY[\"EPSG\",\"2038\"]]";
4931     auto obj1 = WKTParser().createFromWKT(wkt1);
4932     auto crs1 = nn_dynamic_pointer_cast<ProjectedCRS>(obj1);
4933     ASSERT_TRUE(crs1 != nullptr);
4934 
4935     // Difference with wkt1: latitude_of_origin missing, but false_northing
4936     // added to 0
4937     auto wkt2 = "PROJCS[\"NAD83(CSRS98) / UTM zone 20N (deprecated)\","
4938                 "    GEOGCS[\"NAD83(CSRS98)\","
4939                 "        DATUM[\"NAD83_Canadian_Spatial_Reference_System\","
4940                 "            SPHEROID[\"GRS 1980\",6378137,298.257222101,"
4941                 "                AUTHORITY[\"EPSG\",\"7019\"]],"
4942                 "            AUTHORITY[\"EPSG\",\"6140\"]],"
4943                 "        PRIMEM[\"Greenwich\",0,"
4944                 "            AUTHORITY[\"EPSG\",\"8901\"]],"
4945                 "        UNIT[\"degree\",0.0174532925199433,"
4946                 "            AUTHORITY[\"EPSG\",\"9108\"]],"
4947                 "        AUTHORITY[\"EPSG\",\"4140\"]],"
4948                 "    PROJECTION[\"Transverse_Mercator\"],"
4949                 "    PARAMETER[\"central_meridian\",-63],"
4950                 "    PARAMETER[\"scale_factor\",0.9996],"
4951                 "    PARAMETER[\"false_easting\",500000],"
4952                 "    PARAMETER[\"false_northing\",0],"
4953                 "    UNIT[\"metre\",1,"
4954                 "        AUTHORITY[\"EPSG\",\"9001\"]],"
4955                 "    AXIS[\"Easting\",EAST],"
4956                 "    AXIS[\"Northing\",NORTH],"
4957                 "    AUTHORITY[\"EPSG\",\"2038\"]]";
4958     auto obj2 = WKTParser().createFromWKT(wkt2);
4959     auto crs2 = nn_dynamic_pointer_cast<ProjectedCRS>(obj2);
4960     ASSERT_TRUE(crs2 != nullptr);
4961 
4962     // Difference with wkt1: false_northing added to 0
4963     auto wkt3 = "PROJCS[\"NAD83(CSRS98) / UTM zone 20N (deprecated)\","
4964                 "    GEOGCS[\"NAD83(CSRS98)\","
4965                 "        DATUM[\"NAD83_Canadian_Spatial_Reference_System\","
4966                 "            SPHEROID[\"GRS 1980\",6378137,298.257222101,"
4967                 "                AUTHORITY[\"EPSG\",\"7019\"]],"
4968                 "            AUTHORITY[\"EPSG\",\"6140\"]],"
4969                 "        PRIMEM[\"Greenwich\",0,"
4970                 "            AUTHORITY[\"EPSG\",\"8901\"]],"
4971                 "        UNIT[\"degree\",0.0174532925199433,"
4972                 "            AUTHORITY[\"EPSG\",\"9108\"]],"
4973                 "        AUTHORITY[\"EPSG\",\"4140\"]],"
4974                 "    PROJECTION[\"Transverse_Mercator\"],"
4975                 "    PARAMETER[\"latitude_of_origin\",0],"
4976                 "    PARAMETER[\"central_meridian\",-63],"
4977                 "    PARAMETER[\"scale_factor\",0.9996],"
4978                 "    PARAMETER[\"false_easting\",500000],"
4979                 "    PARAMETER[\"false_northing\",0],"
4980                 "    UNIT[\"metre\",1,"
4981                 "        AUTHORITY[\"EPSG\",\"9001\"]],"
4982                 "    AXIS[\"Easting\",EAST],"
4983                 "    AXIS[\"Northing\",NORTH],"
4984                 "    AUTHORITY[\"EPSG\",\"2038\"]]";
4985     auto obj3 = WKTParser().createFromWKT(wkt3);
4986     auto crs3 = nn_dynamic_pointer_cast<ProjectedCRS>(obj3);
4987     ASSERT_TRUE(crs3 != nullptr);
4988 
4989     // Difference with wkt1: UNKNOWN added to non-zero
4990     auto wkt4 = "PROJCS[\"NAD83(CSRS98) / UTM zone 20N (deprecated)\","
4991                 "    GEOGCS[\"NAD83(CSRS98)\","
4992                 "        DATUM[\"NAD83_Canadian_Spatial_Reference_System\","
4993                 "            SPHEROID[\"GRS 1980\",6378137,298.257222101,"
4994                 "                AUTHORITY[\"EPSG\",\"7019\"]],"
4995                 "            AUTHORITY[\"EPSG\",\"6140\"]],"
4996                 "        PRIMEM[\"Greenwich\",0,"
4997                 "            AUTHORITY[\"EPSG\",\"8901\"]],"
4998                 "        UNIT[\"degree\",0.0174532925199433,"
4999                 "            AUTHORITY[\"EPSG\",\"9108\"]],"
5000                 "        AUTHORITY[\"EPSG\",\"4140\"]],"
5001                 "    PROJECTION[\"Transverse_Mercator\"],"
5002                 "    PARAMETER[\"latitude_of_origin\",0],"
5003                 "    PARAMETER[\"central_meridian\",-63],"
5004                 "    PARAMETER[\"scale_factor\",0.9996],"
5005                 "    PARAMETER[\"false_easting\",500000],"
5006                 "    PARAMETER[\"false_northing\",0],"
5007                 "    PARAMETER[\"UNKNOWN\",13],"
5008                 "    UNIT[\"metre\",1,"
5009                 "        AUTHORITY[\"EPSG\",\"9001\"]],"
5010                 "    AXIS[\"Easting\",EAST],"
5011                 "    AXIS[\"Northing\",NORTH],"
5012                 "    AUTHORITY[\"EPSG\",\"2038\"]]";
5013     auto obj4 = WKTParser().createFromWKT(wkt4);
5014     auto crs4 = nn_dynamic_pointer_cast<ProjectedCRS>(obj4);
5015     ASSERT_TRUE(crs4 != nullptr);
5016 
5017     // Difference with wkt1: latitude_of_origin missing, but false_northing
5018     // added to non-zero
5019     auto wkt5 = "PROJCS[\"NAD83(CSRS98) / UTM zone 20N (deprecated)\","
5020                 "    GEOGCS[\"NAD83(CSRS98)\","
5021                 "        DATUM[\"NAD83_Canadian_Spatial_Reference_System\","
5022                 "            SPHEROID[\"GRS 1980\",6378137,298.257222101,"
5023                 "                AUTHORITY[\"EPSG\",\"7019\"]],"
5024                 "            AUTHORITY[\"EPSG\",\"6140\"]],"
5025                 "        PRIMEM[\"Greenwich\",0,"
5026                 "            AUTHORITY[\"EPSG\",\"8901\"]],"
5027                 "        UNIT[\"degree\",0.0174532925199433,"
5028                 "            AUTHORITY[\"EPSG\",\"9108\"]],"
5029                 "        AUTHORITY[\"EPSG\",\"4140\"]],"
5030                 "    PROJECTION[\"Transverse_Mercator\"],"
5031                 "    PARAMETER[\"central_meridian\",-63],"
5032                 "    PARAMETER[\"scale_factor\",0.9996],"
5033                 "    PARAMETER[\"false_easting\",500000],"
5034                 "    PARAMETER[\"false_northing\",-99999],"
5035                 "    UNIT[\"metre\",1,"
5036                 "        AUTHORITY[\"EPSG\",\"9001\"]],"
5037                 "    AXIS[\"Easting\",EAST],"
5038                 "    AXIS[\"Northing\",NORTH],"
5039                 "    AUTHORITY[\"EPSG\",\"2038\"]]";
5040     auto obj5 = WKTParser().createFromWKT(wkt5);
5041     auto crs5 = nn_dynamic_pointer_cast<ProjectedCRS>(obj5);
5042     ASSERT_TRUE(crs5 != nullptr);
5043 
5044     EXPECT_TRUE(
5045         crs1->isEquivalentTo(crs2.get(), IComparable::Criterion::EQUIVALENT));
5046     EXPECT_TRUE(
5047         crs2->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5048     EXPECT_TRUE(
5049         crs1->isEquivalentTo(crs3.get(), IComparable::Criterion::EQUIVALENT));
5050     EXPECT_TRUE(
5051         crs3->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5052     EXPECT_TRUE(
5053         crs2->isEquivalentTo(crs3.get(), IComparable::Criterion::EQUIVALENT));
5054     EXPECT_TRUE(
5055         crs3->isEquivalentTo(crs2.get(), IComparable::Criterion::EQUIVALENT));
5056 
5057     EXPECT_FALSE(
5058         crs1->isEquivalentTo(crs4.get(), IComparable::Criterion::EQUIVALENT));
5059     EXPECT_FALSE(
5060         crs4->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5061 
5062     EXPECT_FALSE(
5063         crs1->isEquivalentTo(crs5.get(), IComparable::Criterion::EQUIVALENT));
5064     EXPECT_FALSE(
5065         crs5->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5066 }
5067 
5068 // ---------------------------------------------------------------------------
5069 
TEST(operation,conversion_missing_parameter_scale)5070 TEST(operation, conversion_missing_parameter_scale) {
5071 
5072     auto wkt1 = "PROJCS[\"test\",\n"
5073                 "    GEOGCS[\"WGS 84\",\n"
5074                 "        DATUM[\"WGS 1984\",\n"
5075                 "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
5076                 "        PRIMEM[\"Greenwich\",0],\n"
5077                 "        UNIT[\"degree\",0.0174532925199433]],\n"
5078                 "    PROJECTION[\"Mercator_1SP\"],\n"
5079                 "    PARAMETER[\"latitude_of_origin\",-1],\n"
5080                 "    PARAMETER[\"central_meridian\",2],\n"
5081                 "    PARAMETER[\"scale_factor\",1],\n"
5082                 "    PARAMETER[\"false_easting\",3],\n"
5083                 "    PARAMETER[\"false_northing\",4],\n"
5084                 "    UNIT[\"metre\",1]]";
5085 
5086     auto obj1 = WKTParser().createFromWKT(wkt1);
5087     auto crs1 = nn_dynamic_pointer_cast<ProjectedCRS>(obj1);
5088     ASSERT_TRUE(crs1 != nullptr);
5089 
5090     // Difference with wkt1: scale_factor missing
5091     auto wkt2 = "PROJCS[\"test\",\n"
5092                 "    GEOGCS[\"WGS 84\",\n"
5093                 "        DATUM[\"WGS 1984\",\n"
5094                 "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
5095                 "        PRIMEM[\"Greenwich\",0],\n"
5096                 "        UNIT[\"degree\",0.0174532925199433]],\n"
5097                 "    PROJECTION[\"Mercator_1SP\"],\n"
5098                 "    PARAMETER[\"latitude_of_origin\",-1],\n"
5099                 "    PARAMETER[\"central_meridian\",2],\n"
5100                 "    PARAMETER[\"false_easting\",3],\n"
5101                 "    PARAMETER[\"false_northing\",4],\n"
5102                 "    UNIT[\"metre\",1]]";
5103 
5104     auto obj2 = WKTParser().createFromWKT(wkt2);
5105     auto crs2 = nn_dynamic_pointer_cast<ProjectedCRS>(obj2);
5106     ASSERT_TRUE(crs2 != nullptr);
5107 
5108     // Difference with wkt1: scale_factor set to non-1
5109     auto wkt3 = "PROJCS[\"test\",\n"
5110                 "    GEOGCS[\"WGS 84\",\n"
5111                 "        DATUM[\"WGS 1984\",\n"
5112                 "            SPHEROID[\"WGS 84\",6378137,298.257223563]],\n"
5113                 "        PRIMEM[\"Greenwich\",0],\n"
5114                 "        UNIT[\"degree\",0.0174532925199433]],\n"
5115                 "    PROJECTION[\"Mercator_1SP\"],\n"
5116                 "    PARAMETER[\"latitude_of_origin\",-1],\n"
5117                 "    PARAMETER[\"central_meridian\",2],\n"
5118                 "    PARAMETER[\"scale_factor\",-1],\n"
5119                 "    PARAMETER[\"false_easting\",3],\n"
5120                 "    PARAMETER[\"false_northing\",4],\n"
5121                 "    UNIT[\"metre\",1]]";
5122     auto obj3 = WKTParser().createFromWKT(wkt3);
5123     auto crs3 = nn_dynamic_pointer_cast<ProjectedCRS>(obj3);
5124     ASSERT_TRUE(crs3 != nullptr);
5125 
5126     EXPECT_TRUE(
5127         crs1->isEquivalentTo(crs2.get(), IComparable::Criterion::EQUIVALENT));
5128     EXPECT_TRUE(
5129         crs2->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5130 
5131     EXPECT_FALSE(
5132         crs1->isEquivalentTo(crs3.get(), IComparable::Criterion::EQUIVALENT));
5133     EXPECT_FALSE(
5134         crs3->isEquivalentTo(crs1.get(), IComparable::Criterion::EQUIVALENT));
5135     EXPECT_FALSE(
5136         crs2->isEquivalentTo(crs3.get(), IComparable::Criterion::EQUIVALENT));
5137     EXPECT_FALSE(
5138         crs3->isEquivalentTo(crs2.get(), IComparable::Criterion::EQUIVALENT));
5139 }
5140 
5141 // ---------------------------------------------------------------------------
5142 
TEST(operation,hotine_oblique_mercator_variant_A_export_equivalent_modulo_360)5143 TEST(operation,
5144      hotine_oblique_mercator_variant_A_export_equivalent_modulo_360) {
5145     auto conv1 = Conversion::createHotineObliqueMercatorVariantA(
5146         PropertyMap(), Angle(1), Angle(2), Angle(-3), Angle(-4), Scale(5),
5147         Length(6), Length(7));
5148     auto conv2 = Conversion::createHotineObliqueMercatorVariantA(
5149         PropertyMap(), Angle(1), Angle(2), Angle(-3 + 360), Angle(-4 + 360),
5150         Scale(5), Length(6), Length(7));
5151 
5152     EXPECT_TRUE(
5153         conv1->isEquivalentTo(conv2.get(), IComparable::Criterion::EQUIVALENT));
5154 }
5155 
5156 // ---------------------------------------------------------------------------
5157 
TEST(operation,createChangeVerticalUnit)5158 TEST(operation, createChangeVerticalUnit) {
5159     auto conv = Conversion::createChangeVerticalUnit(PropertyMap(), Scale(1));
5160     EXPECT_TRUE(conv->validateParameters().empty());
5161 }
5162 
5163 // ---------------------------------------------------------------------------
5164 
TEST(operation,createGeographicGeocentric)5165 TEST(operation, createGeographicGeocentric) {
5166     auto conv = Conversion::createGeographicGeocentric(PropertyMap());
5167     EXPECT_TRUE(conv->validateParameters().empty());
5168 }
5169 
5170 // ---------------------------------------------------------------------------
5171 
TEST(operation,validateParameters)5172 TEST(operation, validateParameters) {
5173     {
5174         auto conv = Conversion::create(
5175             PropertyMap(),
5176             PropertyMap().set(IdentifiedObject::NAME_KEY, "unknown"), {}, {});
5177         auto validation = conv->validateParameters();
5178         EXPECT_EQ(validation, std::list<std::string>{"Unknown method unknown"});
5179     }
5180 
5181     {
5182         auto conv = Conversion::create(
5183             PropertyMap(), PropertyMap().set(IdentifiedObject::NAME_KEY,
5184                                              "change of vertical unit"),
5185             {}, {});
5186         auto validation = conv->validateParameters();
5187         auto expected = std::list<std::string>{
5188             "Method name change of vertical unit is equivalent to official "
5189             "Change of Vertical Unit but not strictly equal",
5190             "Cannot find expected parameter Unit conversion scalar"};
5191         EXPECT_EQ(validation, expected);
5192     }
5193 
5194     {
5195         auto conv = Conversion::create(
5196             PropertyMap(), PropertyMap()
5197                                .set(IdentifiedObject::NAME_KEY,
5198                                     EPSG_NAME_METHOD_CHANGE_VERTICAL_UNIT)
5199                                .set(Identifier::CODESPACE_KEY, Identifier::EPSG)
5200                                .set(Identifier::CODE_KEY, "1234"),
5201             {}, {});
5202         auto validation = conv->validateParameters();
5203         auto expected = std::list<std::string>{
5204             "Method of EPSG code 1234 does not match official code (1069)",
5205             "Cannot find expected parameter Unit conversion scalar"};
5206         EXPECT_EQ(validation, expected);
5207     }
5208 
5209     {
5210         auto conv = Conversion::create(
5211             PropertyMap(),
5212             PropertyMap()
5213                 .set(IdentifiedObject::NAME_KEY, "some fancy name")
5214                 .set(Identifier::CODESPACE_KEY, Identifier::EPSG)
5215                 .set(Identifier::CODE_KEY,
5216                      EPSG_CODE_METHOD_CHANGE_VERTICAL_UNIT),
5217             {}, {});
5218         auto validation = conv->validateParameters();
5219         auto expected = std::list<std::string>{
5220             "Method name some fancy name, matched to Change of Vertical Unit, "
5221             "through its EPSG code has not an equivalent name",
5222             "Cannot find expected parameter Unit conversion scalar"};
5223         EXPECT_EQ(validation, expected);
5224     }
5225 
5226     {
5227         auto conv = Conversion::create(
5228             PropertyMap(),
5229             PropertyMap().set(IdentifiedObject::NAME_KEY,
5230                               EPSG_NAME_METHOD_CHANGE_VERTICAL_UNIT),
5231             {OperationParameter::create(PropertyMap().set(
5232                 IdentifiedObject::NAME_KEY, "unit conversion scalar"))},
5233             {ParameterValue::create(Measure(1.0, UnitOfMeasure::SCALE_UNITY))});
5234         auto validation = conv->validateParameters();
5235         auto expected = std::list<std::string>{
5236             "Parameter name unit conversion scalar is equivalent to official "
5237             "Unit conversion scalar but not strictly equal"};
5238         EXPECT_EQ(validation, expected);
5239     }
5240 
5241     {
5242         auto conv = Conversion::create(
5243             PropertyMap(),
5244             PropertyMap().set(IdentifiedObject::NAME_KEY,
5245                               EPSG_NAME_METHOD_CHANGE_VERTICAL_UNIT),
5246             {OperationParameter::create(
5247                 PropertyMap()
5248                     .set(IdentifiedObject::NAME_KEY, "fancy name")
5249                     .set(Identifier::CODESPACE_KEY, Identifier::EPSG)
5250                     .set(Identifier::CODE_KEY,
5251                          EPSG_CODE_PARAMETER_UNIT_CONVERSION_SCALAR))},
5252             {ParameterValue::create(Measure(1.0, UnitOfMeasure::SCALE_UNITY))});
5253         auto validation = conv->validateParameters();
5254         auto expected = std::list<std::string>{
5255             "Parameter name fancy name, matched to Unit conversion scalar, "
5256             "through its EPSG code has not an equivalent name"};
5257         EXPECT_EQ(validation, expected);
5258     }
5259 
5260     {
5261         auto conv = Conversion::create(
5262             PropertyMap(),
5263             PropertyMap().set(IdentifiedObject::NAME_KEY,
5264                               EPSG_NAME_METHOD_CHANGE_VERTICAL_UNIT),
5265             {OperationParameter::create(
5266                 PropertyMap().set(IdentifiedObject::NAME_KEY, "extra param"))},
5267             {ParameterValue::create(Measure(1.0, UnitOfMeasure::SCALE_UNITY))});
5268         auto validation = conv->validateParameters();
5269         auto expected = std::list<std::string>{
5270             "Cannot find expected parameter Unit conversion scalar",
5271             "Parameter extra param found but not expected for this method"};
5272         EXPECT_EQ(validation, expected);
5273     }
5274 }
5275 
5276 // ---------------------------------------------------------------------------
5277 
TEST(operation,normalizeForVisualization)5278 TEST(operation, normalizeForVisualization) {
5279 
5280     auto authFactory =
5281         AuthorityFactory::create(DatabaseContext::create(), "EPSG");
5282 
5283     // Source(geographic) must be inverted
5284     {
5285         auto src = authFactory->createCoordinateReferenceSystem("4326");
5286         auto dst = authFactory->createCoordinateReferenceSystem("32631");
5287         auto op =
5288             CoordinateOperationFactory::create()->createOperation(src, dst);
5289         ASSERT_TRUE(op != nullptr);
5290         auto opNormalized = op->normalizeForVisualization();
5291         EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get()));
5292         EXPECT_EQ(opNormalized->exportToPROJString(
5293                       PROJStringFormatter::create().get()),
5294                   "+proj=pipeline "
5295                   "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
5296                   "+step +proj=utm +zone=31 +ellps=WGS84");
5297     }
5298 
5299     // Target(geographic) must be inverted
5300     {
5301         auto src = authFactory->createCoordinateReferenceSystem("32631");
5302         auto dst = authFactory->createCoordinateReferenceSystem("4326");
5303         auto op =
5304             CoordinateOperationFactory::create()->createOperation(src, dst);
5305         ASSERT_TRUE(op != nullptr);
5306         auto opNormalized = op->normalizeForVisualization();
5307         EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get()));
5308         EXPECT_EQ(opNormalized->exportToPROJString(
5309                       PROJStringFormatter::create().get()),
5310                   "+proj=pipeline "
5311                   "+step +inv +proj=utm +zone=31 +ellps=WGS84 "
5312                   "+step +proj=unitconvert +xy_in=rad +xy_out=deg");
5313     }
5314 
5315     // Source(geographic) and target(projected) must be inverted
5316     {
5317         auto src = authFactory->createCoordinateReferenceSystem("4326");
5318         auto dst = authFactory->createCoordinateReferenceSystem("3040");
5319         auto op =
5320             CoordinateOperationFactory::create()->createOperation(src, dst);
5321         ASSERT_TRUE(op != nullptr);
5322         auto opNormalized = op->normalizeForVisualization();
5323         EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get()));
5324         EXPECT_EQ(opNormalized->exportToPROJString(
5325                       PROJStringFormatter::create().get()),
5326                   "+proj=pipeline "
5327                   "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
5328                   "+step +proj=utm +zone=28 +ellps=GRS80");
5329     }
5330 
5331     // No inversion
5332     {
5333         auto src = authFactory->createCoordinateReferenceSystem("32631");
5334         auto dst = authFactory->createCoordinateReferenceSystem("32632");
5335         auto op =
5336             CoordinateOperationFactory::create()->createOperation(src, dst);
5337         ASSERT_TRUE(op != nullptr);
5338         auto opNormalized = op->normalizeForVisualization();
5339         EXPECT_TRUE(opNormalized->_isEquivalentTo(op.get()));
5340     }
5341 
5342     // Source(compoundCRS) and target(geographic 3D) must be inverted
5343     {
5344         auto ctxt =
5345             CoordinateOperationContext::create(authFactory, nullptr, 0.0);
5346         ctxt->setUsePROJAlternativeGridNames(false);
5347         auto src = CompoundCRS::create(
5348             PropertyMap(),
5349             std::vector<CRSNNPtr>{
5350                 authFactory->createCoordinateReferenceSystem("4326"),
5351                 // EGM2008 height
5352                 authFactory->createCoordinateReferenceSystem("3855")});
5353         auto list = CoordinateOperationFactory::create()->createOperations(
5354             src,
5355             authFactory->createCoordinateReferenceSystem("4979"), // WGS 84 3D
5356             ctxt);
5357         ASSERT_EQ(list.size(), 3U);
5358         auto op = list[1];
5359         auto opNormalized = op->normalizeForVisualization();
5360         EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get()));
5361         EXPECT_EQ(
5362             opNormalized->exportToPROJString(
5363                 PROJStringFormatter::create(
5364                     PROJStringFormatter::Convention::PROJ_5,
5365                     authFactory->databaseContext())
5366                     .get()),
5367             "+proj=pipeline "
5368             "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
5369             "+step +proj=vgridshift +grids=us_nga_egm08_25.tif +multiplier=1 "
5370             "+step +proj=unitconvert +xy_in=rad +xy_out=deg");
5371     }
5372 
5373     // Source(boundCRS) and target(geographic) must be inverted
5374     {
5375         auto src = BoundCRS::createFromTOWGS84(
5376             GeographicCRS::EPSG_4269, std::vector<double>{1, 2, 3, 4, 5, 6, 7});
5377         auto dst = authFactory->createCoordinateReferenceSystem("4326");
5378         auto op =
5379             CoordinateOperationFactory::create()->createOperation(src, dst);
5380         ASSERT_TRUE(op != nullptr);
5381         auto opNormalized = op->normalizeForVisualization();
5382         EXPECT_FALSE(opNormalized->_isEquivalentTo(op.get()));
5383         EXPECT_EQ(opNormalized->exportToPROJString(
5384                       PROJStringFormatter::create().get()),
5385                   "+proj=pipeline "
5386                   "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
5387                   "+step +proj=push +v_3 "
5388                   "+step +proj=cart +ellps=GRS80 "
5389                   "+step +proj=helmert +x=1 +y=2 +z=3 +rx=4 +ry=5 +rz=6 +s=7 "
5390                   "+convention=position_vector "
5391                   "+step +inv +proj=cart +ellps=WGS84 "
5392                   "+step +proj=pop +v_3 "
5393                   "+step +proj=unitconvert +xy_in=rad +xy_out=deg");
5394     }
5395 }
5396 
5397 // ---------------------------------------------------------------------------
5398 
TEST(operation,export_of_Geographic3D_to_GravityRelatedHeight_gtx_unknown_grid)5399 TEST(operation,
5400      export_of_Geographic3D_to_GravityRelatedHeight_gtx_unknown_grid) {
5401 
5402     auto wkt =
5403         "COORDINATEOPERATION[\"bla\",\n"
5404         "    SOURCECRS[\n"
5405         "        GEOGCRS[\"ETRS89\",\n"
5406         "            DATUM[\"European Terrestrial Reference System 1989\",\n"
5407         "                ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
5408         "                    LENGTHUNIT[\"metre\",1]]],\n"
5409         "            PRIMEM[\"Greenwich\",0,\n"
5410         "                ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
5411         "            CS[ellipsoidal,3],\n"
5412         "                AXIS[\"geodetic latitude (Lat)\",north,\n"
5413         "                    ORDER[1],\n"
5414         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
5415         "                AXIS[\"geodetic longitude (Lon)\",east,\n"
5416         "                    ORDER[2],\n"
5417         "                    ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
5418         "                AXIS[\"ellipsoidal height (h)\",up,\n"
5419         "                    ORDER[3],\n"
5420         "                    LENGTHUNIT[\"metre\",1]],\n"
5421         "            ID[\"EPSG\",4937]]],\n"
5422         "    TARGETCRS[\n"
5423         "        VERTCRS[\"bar\",\n"
5424         "            VDATUM[\"bar\"],\n"
5425         "            CS[vertical,1],\n"
5426         "                AXIS[\"gravity-related height (H)\",up,\n"
5427         "                    LENGTHUNIT[\"metre\",1]]]],\n"
5428         "    METHOD[\"Geographic3D to GravityRelatedHeight (gtx)\",\n"
5429         "        ID[\"EPSG\",9665]],\n"
5430         "    PARAMETERFILE[\"Geoid (height correction) model "
5431         "file\",\"foo.gtx\"]]";
5432 
5433     auto obj = WKTParser().createFromWKT(wkt);
5434     auto crs = nn_dynamic_pointer_cast<Transformation>(obj);
5435     ASSERT_TRUE(crs != nullptr);
5436     // Test that even if the .gtx file is unknown, we export in the correct
5437     // direction
5438     EXPECT_EQ(crs->exportToPROJString(PROJStringFormatter::create().get()),
5439               "+proj=pipeline "
5440               "+step +proj=axisswap +order=2,1 "
5441               "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
5442               "+step +inv +proj=vgridshift +grids=foo.gtx +multiplier=1 "
5443               "+step +proj=unitconvert +xy_in=rad +xy_out=deg "
5444               "+step +proj=axisswap +order=2,1");
5445 }
5446