Coverage Report

Created: 2026-06-09 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/iso19111/coordinatesystem.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  PROJ
4
 * Purpose:  ISO19111:2019 implementation
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 ****************************************************************************/
28
29
#ifndef FROM_PROJ_CPP
30
#define FROM_PROJ_CPP
31
#endif
32
33
#include "proj/coordinatesystem.hpp"
34
#include "proj/common.hpp"
35
#include "proj/io.hpp"
36
#include "proj/metadata.hpp"
37
#include "proj/util.hpp"
38
39
#include "proj/internal/coordinatesystem_internal.hpp"
40
#include "proj/internal/internal.hpp"
41
#include "proj/internal/io_internal.hpp"
42
43
#include "proj_json_streaming_writer.hpp"
44
45
#include <map>
46
#include <memory>
47
#include <set>
48
#include <string>
49
#include <vector>
50
51
using namespace NS_PROJ::internal;
52
53
#if 0
54
namespace dropbox{ namespace oxygen {
55
template<> nn<NS_PROJ::cs::MeridianPtr>::~nn() = default;
56
template<> nn<NS_PROJ::cs::CoordinateSystemAxisPtr>::~nn() = default;
57
template<> nn<NS_PROJ::cs::CoordinateSystemPtr>::~nn() = default;
58
template<> nn<NS_PROJ::cs::SphericalCSPtr>::~nn() = default;
59
template<> nn<NS_PROJ::cs::EllipsoidalCSPtr>::~nn() = default;
60
template<> nn<NS_PROJ::cs::CartesianCSPtr>::~nn() = default;
61
template<> nn<NS_PROJ::cs::TemporalCSPtr>::~nn() = default;
62
template<> nn<NS_PROJ::cs::TemporalCountCSPtr>::~nn() = default;
63
template<> nn<NS_PROJ::cs::TemporalMeasureCSPtr>::~nn() = default;
64
template<> nn<NS_PROJ::cs::DateTimeTemporalCSPtr>::~nn() = default;
65
template<> nn<NS_PROJ::cs::VerticalCSPtr>::~nn() = default;
66
template<> nn<NS_PROJ::cs::ParametricCSPtr>::~nn() = default;
67
template<> nn<NS_PROJ::cs::OrdinalCSPtr>::~nn() = default;
68
}}
69
#endif
70
71
NS_PROJ_START
72
namespace cs {
73
74
// ---------------------------------------------------------------------------
75
76
//! @cond Doxygen_Suppress
77
struct Meridian::Private {
78
    common::Angle longitude_{};
79
80
226
    explicit Private(const common::Angle &longitude) : longitude_(longitude) {}
81
};
82
//! @endcond
83
84
// ---------------------------------------------------------------------------
85
86
Meridian::Meridian(const common::Angle &longitudeIn)
87
226
    : d(std::make_unique<Private>(longitudeIn)) {}
88
89
// ---------------------------------------------------------------------------
90
91
#ifdef notdef
92
Meridian::Meridian(const Meridian &other)
93
    : IdentifiedObject(other), d(std::make_unique<Private>(*other.d)) {}
94
#endif
95
96
// ---------------------------------------------------------------------------
97
98
//! @cond Doxygen_Suppress
99
226
Meridian::~Meridian() = default;
100
//! @endcond
101
102
// ---------------------------------------------------------------------------
103
104
/** \brief Return the longitude of the meridian that the axis follows from the
105
 * pole.
106
 *
107
 * @return the longitude.
108
 */
109
0
const common::Angle &Meridian::longitude() PROJ_PURE_DEFN {
110
0
    return d->longitude_;
111
0
}
112
113
// ---------------------------------------------------------------------------
114
115
/** \brief Instantiate a Meridian.
116
 *
117
 * @param longitudeIn longitude of the meridian that the axis follows from the
118
 * pole.
119
 * @return new Meridian.
120
 */
121
226
MeridianNNPtr Meridian::create(const common::Angle &longitudeIn) {
122
226
    return Meridian::nn_make_shared<Meridian>(longitudeIn);
123
226
}
124
125
// ---------------------------------------------------------------------------
126
127
//! @cond Doxygen_Suppress
128
void Meridian::_exportToWKT(
129
    io::WKTFormatter *formatter) const // throw(FormattingException)
130
0
{
131
0
    formatter->startNode(io::WKTConstants::MERIDIAN, !identifiers().empty());
132
0
    formatter->add(longitude().value());
133
0
    longitude().unit()._exportToWKT(formatter, io::WKTConstants::ANGLEUNIT);
134
0
    if (formatter->outputId()) {
135
0
        formatID(formatter);
136
0
    }
137
0
    formatter->endNode();
138
0
}
139
//! @endcond
140
141
// ---------------------------------------------------------------------------
142
143
//! @cond Doxygen_Suppress
144
void Meridian::_exportToJSON(
145
    io::JSONFormatter *formatter) const // throw(FormattingException)
146
0
{
147
0
    auto writer = formatter->writer();
148
0
    auto objectContext(
149
0
        formatter->MakeObjectContext("Meridian", !identifiers().empty()));
150
151
0
    const auto &l_long = longitude();
152
0
    writer->AddObjKey("longitude");
153
0
    const auto &unit = l_long.unit();
154
0
    if (unit == common::UnitOfMeasure::DEGREE) {
155
0
        writer->Add(l_long.value(), 15);
156
0
    } else {
157
0
        auto longitudeContext(formatter->MakeObjectContext(nullptr, false));
158
0
        writer->AddObjKey("value");
159
0
        writer->Add(l_long.value(), 15);
160
0
        writer->AddObjKey("unit");
161
0
        unit._exportToJSON(formatter);
162
0
    }
163
0
    if (formatter->outputId()) {
164
0
        formatID(formatter);
165
0
    }
166
0
}
167
//! @endcond
168
169
// ---------------------------------------------------------------------------
170
171
//! @cond Doxygen_Suppress
172
struct CoordinateSystemAxis::Private {
173
    std::string abbreviation{};
174
    const AxisDirection *direction = &(AxisDirection::UNSPECIFIED);
175
    common::UnitOfMeasure unit{};
176
    util::optional<RangeMeaning> rangeMeaning = util::optional<RangeMeaning>();
177
    util::optional<double> minimumValue{};
178
    util::optional<double> maximumValue{};
179
    MeridianPtr meridian{};
180
};
181
//! @endcond
182
183
// ---------------------------------------------------------------------------
184
185
271k
CoordinateSystemAxis::CoordinateSystemAxis() : d(std::make_unique<Private>()) {}
186
187
// ---------------------------------------------------------------------------
188
189
#ifdef notdef
190
CoordinateSystemAxis::CoordinateSystemAxis(const CoordinateSystemAxis &other)
191
    : IdentifiedObject(other), d(std::make_unique<Private>(*other.d)) {}
192
#endif
193
194
// ---------------------------------------------------------------------------
195
196
//! @cond Doxygen_Suppress
197
271k
CoordinateSystemAxis::~CoordinateSystemAxis() = default;
198
//! @endcond
199
200
// ---------------------------------------------------------------------------
201
202
/** \brief Return the axis abbreviation.
203
 *
204
 * The abbreviation used for this coordinate system axis; this abbreviation
205
 * is also used to identify the coordinates in the coordinate tuple.
206
 * Examples are X and Y.
207
 *
208
 * @return the abbreviation.
209
 */
210
32
const std::string &CoordinateSystemAxis::abbreviation() PROJ_PURE_DEFN {
211
32
    return d->abbreviation;
212
32
}
213
214
// ---------------------------------------------------------------------------
215
216
/** \brief Return the axis direction.
217
 *
218
 * The direction of this coordinate system axis (or in the case of Cartesian
219
 * projected coordinates, the direction of this coordinate system axis locally)
220
 * Examples: north or south, east or west, up or down. Within any set of
221
 * coordinate system axes, only one of each pair of terms can be used. For
222
 * Earth-fixed CRSs, this direction is often approximate and intended to
223
 * provide a human interpretable meaning to the axis. When a geodetic reference
224
 * frame is used, the precise directions of the axes may therefore vary
225
 * slightly from this approximate direction. Note that an EngineeringCRS often
226
 * requires specific descriptions of the directions of its coordinate system
227
 * axes.
228
 *
229
 * @return the direction.
230
 */
231
3.18M
const AxisDirection &CoordinateSystemAxis::direction() PROJ_PURE_DEFN {
232
3.18M
    return *(d->direction);
233
3.18M
}
234
235
// ---------------------------------------------------------------------------
236
237
/** \brief Return the axis unit.
238
 *
239
 * This is the spatial unit or temporal quantity used for this coordinate
240
 * system axis. The value of a coordinate in a coordinate tuple shall be
241
 * recorded using this unit.
242
 *
243
 * @return the axis unit.
244
 */
245
1.65M
const common::UnitOfMeasure &CoordinateSystemAxis::unit() PROJ_PURE_DEFN {
246
1.65M
    return d->unit;
247
1.65M
}
248
249
// ---------------------------------------------------------------------------
250
251
/** \brief Return the minimum value normally allowed for this axis, in the unit
252
 * for the axis.
253
 *
254
 * @return the minimum value, or empty.
255
 */
256
const util::optional<double> &
257
0
CoordinateSystemAxis::minimumValue() PROJ_PURE_DEFN {
258
0
    return d->minimumValue;
259
0
}
260
261
// ---------------------------------------------------------------------------
262
263
/** \brief Return the maximum value normally allowed for this axis, in the unit
264
 * for the axis.
265
 *
266
 * @return the maximum value, or empty.
267
 */
268
const util::optional<double> &
269
0
CoordinateSystemAxis::maximumValue() PROJ_PURE_DEFN {
270
0
    return d->maximumValue;
271
0
}
272
273
// ---------------------------------------------------------------------------
274
275
/** \brief Return the range meaning
276
 *
277
 * @return the range meaning, or empty.
278
 * @since 9.2
279
 */
280
const util::optional<RangeMeaning> &
281
0
CoordinateSystemAxis::rangeMeaning() PROJ_PURE_DEFN {
282
0
    return d->rangeMeaning;
283
0
}
284
285
// ---------------------------------------------------------------------------
286
287
/** \brief Return the meridian that the axis follows from the pole, for a
288
 * coordinate
289
 * reference system centered on a pole.
290
 *
291
 * @return the meridian, or null.
292
 */
293
0
const MeridianPtr &CoordinateSystemAxis::meridian() PROJ_PURE_DEFN {
294
0
    return d->meridian;
295
0
}
296
297
// ---------------------------------------------------------------------------
298
299
/** \brief Instantiate a CoordinateSystemAxis.
300
 *
301
 * @param properties See \ref general_properties. The name should generally be
302
 * defined.
303
 * @param abbreviationIn Axis abbreviation (might be empty)
304
 * @param directionIn Axis direction
305
 * @param unitIn Axis unit
306
 * @param meridianIn The meridian that the axis follows from the pole, for a
307
 * coordinate
308
 * reference system centered on a pole, or nullptr
309
 * @return a new CoordinateSystemAxis.
310
 */
311
CoordinateSystemAxisNNPtr CoordinateSystemAxis::create(
312
    const util::PropertyMap &properties, const std::string &abbreviationIn,
313
    const AxisDirection &directionIn, const common::UnitOfMeasure &unitIn,
314
271k
    const MeridianPtr &meridianIn) {
315
271k
    auto csa(CoordinateSystemAxis::nn_make_shared<CoordinateSystemAxis>());
316
271k
    csa->setProperties(properties);
317
271k
    csa->d->abbreviation = abbreviationIn;
318
271k
    csa->d->direction = &directionIn;
319
271k
    csa->d->unit = unitIn;
320
271k
    csa->d->meridian = meridianIn;
321
271k
    return csa;
322
271k
}
323
324
// ---------------------------------------------------------------------------
325
326
/** \brief Instantiate a CoordinateSystemAxis.
327
 *
328
 * @param properties See \ref general_properties. The name should generally be
329
 * defined.
330
 * @param abbreviationIn Axis abbreviation (might be empty)
331
 * @param directionIn Axis direction
332
 * @param unitIn Axis unit
333
 * @param minimumValueIn Minimum value along axis
334
 * @param maximumValueIn Maximum value along axis
335
 * @param rangeMeaningIn Range Meaning
336
 * @param meridianIn The meridian that the axis follows from the pole, for a
337
 * coordinate
338
 * reference system centered on a pole, or nullptr
339
 * @return a new CoordinateSystemAxis.
340
 * @since 9.2
341
 */
342
CoordinateSystemAxisNNPtr CoordinateSystemAxis::create(
343
    const util::PropertyMap &properties, const std::string &abbreviationIn,
344
    const AxisDirection &directionIn, const common::UnitOfMeasure &unitIn,
345
    const util::optional<double> &minimumValueIn,
346
    const util::optional<double> &maximumValueIn,
347
    const util::optional<RangeMeaning> &rangeMeaningIn,
348
78
    const MeridianPtr &meridianIn) {
349
78
    auto csa(CoordinateSystemAxis::nn_make_shared<CoordinateSystemAxis>());
350
78
    csa->setProperties(properties);
351
78
    csa->d->abbreviation = abbreviationIn;
352
78
    csa->d->direction = &directionIn;
353
78
    csa->d->unit = unitIn;
354
78
    csa->d->minimumValue = minimumValueIn;
355
78
    csa->d->maximumValue = maximumValueIn;
356
78
    csa->d->rangeMeaning = rangeMeaningIn;
357
78
    csa->d->meridian = meridianIn;
358
78
    return csa;
359
78
}
360
361
// ---------------------------------------------------------------------------
362
363
//! @cond Doxygen_Suppress
364
void CoordinateSystemAxis::_exportToWKT(
365
    // cppcheck-suppress passedByValue
366
    io::WKTFormatter *formatter) const // throw(FormattingException)
367
0
{
368
0
    _exportToWKT(formatter, 0, false);
369
0
}
370
//! @endcond
371
372
// ---------------------------------------------------------------------------
373
374
//! @cond Doxygen_Suppress
375
168
std::string CoordinateSystemAxis::normalizeAxisName(const std::string &str) {
376
168
    if (str.empty()) {
377
3
        return str;
378
3
    }
379
    // on import, transform from WKT2 "longitude" to "Longitude", as in the
380
    // EPSG database.
381
165
    return toupper(str.substr(0, 1)) + str.substr(1);
382
168
}
383
//! @endcond
384
385
// ---------------------------------------------------------------------------
386
387
//! @cond Doxygen_Suppress
388
void CoordinateSystemAxis::_exportToWKT(io::WKTFormatter *formatter, int order,
389
0
                                        bool disableAbbrev) const {
390
0
    const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
391
0
    formatter->startNode(io::WKTConstants::AXIS, !identifiers().empty());
392
0
    const std::string &axisName = nameStr();
393
0
    const std::string &abbrev = abbreviation();
394
0
    std::string parenthesizedAbbrev =
395
0
        std::string("(").append(abbrev).append(")");
396
0
    std::string dir = direction().toString();
397
0
    std::string axisDesignation;
398
399
    // It seems that the convention in WKT2 for axis name is first letter in
400
    // lower case. Whereas in WKT1 GDAL, it is in upper case (as in the EPSG
401
    // database)
402
0
    if (!axisName.empty()) {
403
0
        if (isWKT2) {
404
0
            axisDesignation =
405
0
                tolower(axisName.substr(0, 1)) + axisName.substr(1);
406
0
        } else {
407
0
            if (axisName == "Geodetic latitude") {
408
0
                axisDesignation = "Latitude";
409
0
            } else if (axisName == "Geodetic longitude") {
410
0
                axisDesignation = "Longitude";
411
0
            } else {
412
0
                axisDesignation = axisName;
413
0
            }
414
0
        }
415
0
    }
416
417
0
    if (!disableAbbrev && isWKT2 &&
418
        // For geodetic CS, export the axis name without abbreviation
419
0
        !(axisName == AxisName::Latitude || axisName == AxisName::Longitude)) {
420
0
        if (!axisDesignation.empty() && !abbrev.empty()) {
421
0
            axisDesignation += " ";
422
0
        }
423
0
        if (!abbrev.empty()) {
424
0
            axisDesignation += parenthesizedAbbrev;
425
0
        }
426
0
    }
427
0
    if (!isWKT2) {
428
0
        dir = toupper(dir);
429
430
0
        if (direction() == AxisDirection::GEOCENTRIC_Z) {
431
0
            dir = AxisDirectionWKT1::NORTH;
432
0
        } else if (AxisDirectionWKT1::valueOf(dir) == nullptr) {
433
0
            dir = AxisDirectionWKT1::OTHER;
434
0
        }
435
0
    } else if (!abbrev.empty()) {
436
        // For geocentric CS, just put the abbreviation
437
0
        if (direction() == AxisDirection::GEOCENTRIC_X ||
438
0
            direction() == AxisDirection::GEOCENTRIC_Y ||
439
0
            direction() == AxisDirection::GEOCENTRIC_Z) {
440
0
            axisDesignation = std::move(parenthesizedAbbrev);
441
0
        }
442
        // For cartesian CS with Easting/Northing, export only the abbreviation
443
0
        else if ((order == 1 && axisName == AxisName::Easting &&
444
0
                  abbrev == AxisAbbreviation::E) ||
445
0
                 (order == 2 && axisName == AxisName::Northing &&
446
0
                  abbrev == AxisAbbreviation::N)) {
447
0
            axisDesignation = std::move(parenthesizedAbbrev);
448
0
        }
449
0
    }
450
0
    formatter->addQuotedString(axisDesignation);
451
0
    formatter->add(dir);
452
0
    const auto &l_meridian = meridian();
453
0
    if (isWKT2 && l_meridian) {
454
0
        l_meridian->_exportToWKT(formatter);
455
0
    }
456
0
    if (formatter->outputAxisOrder() && order > 0) {
457
0
        formatter->startNode(io::WKTConstants::ORDER, false);
458
0
        formatter->add(order);
459
0
        formatter->endNode();
460
0
    }
461
0
    if (formatter->outputUnit() &&
462
0
        unit().type() != common::UnitOfMeasure::Type::NONE) {
463
0
        unit()._exportToWKT(formatter);
464
0
    }
465
0
    if (isWKT2 && formatter->use2019Keywords()) {
466
0
        if (d->minimumValue.has_value()) {
467
0
            formatter->startNode(io::WKTConstants::AXISMINVALUE, false);
468
0
            formatter->add(*(d->minimumValue));
469
0
            formatter->endNode();
470
0
        }
471
0
        if (d->maximumValue.has_value()) {
472
0
            formatter->startNode(io::WKTConstants::AXISMAXVALUE, false);
473
0
            formatter->add(*(d->maximumValue));
474
0
            formatter->endNode();
475
0
        }
476
0
        if (d->minimumValue.has_value() && d->maximumValue.has_value() &&
477
0
            d->rangeMeaning.has_value()) {
478
0
            formatter->startNode(io::WKTConstants::RANGEMEANING, false);
479
0
            formatter->add(d->rangeMeaning->toString());
480
0
            formatter->endNode();
481
0
        }
482
0
    }
483
0
    if (formatter->outputId()) {
484
0
        formatID(formatter);
485
0
    }
486
0
    formatter->endNode();
487
0
}
488
//! @endcond
489
490
// ---------------------------------------------------------------------------
491
492
//! @cond Doxygen_Suppress
493
void CoordinateSystemAxis::_exportToJSON(
494
    io::JSONFormatter *formatter) const // throw(FormattingException)
495
0
{
496
0
    auto writer = formatter->writer();
497
0
    auto objectContext(
498
0
        formatter->MakeObjectContext("Axis", !identifiers().empty()));
499
500
0
    writer->AddObjKey("name");
501
0
    writer->Add(nameStr());
502
503
0
    writer->AddObjKey("abbreviation");
504
0
    writer->Add(abbreviation());
505
506
0
    writer->AddObjKey("direction");
507
0
    writer->Add(direction().toString());
508
509
0
    const auto &l_meridian = meridian();
510
0
    if (l_meridian) {
511
0
        writer->AddObjKey("meridian");
512
0
        formatter->setOmitTypeInImmediateChild();
513
0
        l_meridian->_exportToJSON(formatter);
514
0
    }
515
516
0
    const auto &l_unit(unit());
517
0
    if (l_unit == common::UnitOfMeasure::METRE ||
518
0
        l_unit == common::UnitOfMeasure::DEGREE) {
519
0
        writer->AddObjKey("unit");
520
0
        writer->Add(l_unit.name());
521
0
    } else if (l_unit.type() != common::UnitOfMeasure::Type::NONE) {
522
0
        writer->AddObjKey("unit");
523
0
        l_unit._exportToJSON(formatter);
524
0
    }
525
526
0
    if (d->minimumValue.has_value()) {
527
0
        writer->AddObjKey("minimum_value");
528
0
        writer->Add(*(d->minimumValue));
529
0
    }
530
531
0
    if (d->maximumValue.has_value()) {
532
0
        writer->AddObjKey("maximum_value");
533
0
        writer->Add(*(d->maximumValue));
534
0
    }
535
536
0
    if (d->minimumValue.has_value() && d->maximumValue.has_value() &&
537
0
        d->rangeMeaning.has_value()) {
538
0
        writer->AddObjKey("range_meaning");
539
0
        writer->Add(d->rangeMeaning->toString());
540
0
    }
541
542
0
    if (formatter->outputId()) {
543
0
        formatID(formatter);
544
0
    }
545
0
}
546
//! @endcond
547
548
// ---------------------------------------------------------------------------
549
550
//! @cond Doxygen_Suppress
551
bool CoordinateSystemAxis::_isEquivalentTo(
552
    const util::IComparable *other, util::IComparable::Criterion criterion,
553
1.28M
    const io::DatabaseContextPtr &dbContext) const {
554
1.28M
    auto otherCSA = dynamic_cast<const CoordinateSystemAxis *>(other);
555
1.28M
    if (otherCSA == nullptr) {
556
0
        return false;
557
0
    }
558
    // For approximate comparison, only care about axis direction and unit.
559
1.28M
    if (!(*(d->direction) == *(otherCSA->d->direction) &&
560
1.26M
          d->unit._isEquivalentTo(otherCSA->d->unit, criterion))) {
561
22.8k
        return false;
562
22.8k
    }
563
1.26M
    if (criterion == util::IComparable::Criterion::STRICT) {
564
16
        if (!IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
565
0
            return false;
566
0
        }
567
16
        if (abbreviation() != otherCSA->abbreviation()) {
568
0
            return false;
569
0
        }
570
        // TODO other metadata
571
16
    }
572
573
1.26M
    return true;
574
1.26M
}
575
//! @endcond
576
577
// ---------------------------------------------------------------------------
578
579
//! @cond Doxygen_Suppress
580
CoordinateSystemAxisNNPtr
581
0
CoordinateSystemAxis::alterUnit(const common::UnitOfMeasure &newUnit) const {
582
0
    return create(util::PropertyMap().set(IdentifiedObject::NAME_KEY, name()),
583
0
                  abbreviation(), direction(), newUnit, meridian());
584
0
}
585
//! @endcond
586
587
// ---------------------------------------------------------------------------
588
589
//! @cond Doxygen_Suppress
590
struct CoordinateSystem::Private {
591
    std::vector<CoordinateSystemAxisNNPtr> axisList{};
592
593
    explicit Private(const std::vector<CoordinateSystemAxisNNPtr> &axisListIn)
594
132k
        : axisList(axisListIn) {}
595
};
596
//! @endcond
597
598
// ---------------------------------------------------------------------------
599
600
CoordinateSystem::CoordinateSystem(
601
    const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
602
132k
    : d(std::make_unique<Private>(axisIn)) {}
603
604
// ---------------------------------------------------------------------------
605
606
#ifdef notdef
607
CoordinateSystem::CoordinateSystem(const CoordinateSystem &other)
608
    : IdentifiedObject(other), d(std::make_unique<Private>(*other.d)) {}
609
#endif
610
611
// ---------------------------------------------------------------------------
612
613
//! @cond Doxygen_Suppress
614
132k
CoordinateSystem::~CoordinateSystem() = default;
615
//! @endcond
616
617
// ---------------------------------------------------------------------------
618
619
/** \brief Return the list of axes of this coordinate system.
620
 *
621
 * @return the axes.
622
 */
623
const std::vector<CoordinateSystemAxisNNPtr> &
624
5.62M
CoordinateSystem::axisList() PROJ_PURE_DEFN {
625
5.62M
    return d->axisList;
626
5.62M
}
627
628
// ---------------------------------------------------------------------------
629
630
//! @cond Doxygen_Suppress
631
void CoordinateSystem::_exportToWKT(
632
    io::WKTFormatter *formatter) const // throw(FormattingException)
633
0
{
634
0
    if (formatter->outputAxis() != io::WKTFormatter::OutputAxisRule::YES) {
635
0
        return;
636
0
    }
637
0
    const bool isWKT2 = formatter->version() == io::WKTFormatter::Version::WKT2;
638
639
0
    const auto &l_axisList = axisList();
640
0
    if (isWKT2) {
641
0
        formatter->startNode(io::WKTConstants::CS_, !identifiers().empty());
642
0
        formatter->add(getWKT2Type(formatter->use2019Keywords()));
643
0
        formatter->add(static_cast<int>(l_axisList.size()));
644
0
        formatter->endNode();
645
0
        formatter->startNode(std::string(),
646
0
                             false); // anonymous indentation level
647
0
    }
648
649
0
    common::UnitOfMeasure unit = common::UnitOfMeasure::NONE;
650
0
    bool bAllSameUnit = true;
651
0
    bool bFirstUnit = true;
652
0
    for (const auto &axis : l_axisList) {
653
0
        const auto &l_unit = axis->unit();
654
0
        if (bFirstUnit) {
655
0
            unit = l_unit;
656
0
            bFirstUnit = false;
657
0
        } else if (unit != l_unit) {
658
0
            bAllSameUnit = false;
659
0
        }
660
0
    }
661
662
0
    formatter->pushOutputUnit(
663
0
        isWKT2 && (!bAllSameUnit || !formatter->outputCSUnitOnlyOnceIfSame()));
664
665
0
    int order = 1;
666
0
    const bool disableAbbrev =
667
0
        (l_axisList.size() == 3 &&
668
0
         l_axisList[0]->nameStr() == AxisName::Latitude &&
669
0
         l_axisList[1]->nameStr() == AxisName::Longitude &&
670
0
         l_axisList[2]->nameStr() == AxisName::Ellipsoidal_height);
671
672
0
    for (auto &axis : l_axisList) {
673
0
        int axisOrder = (isWKT2 && l_axisList.size() > 1) ? order : 0;
674
0
        axis->_exportToWKT(formatter, axisOrder, disableAbbrev);
675
0
        order++;
676
0
    }
677
0
    if (isWKT2 && !l_axisList.empty() && bAllSameUnit &&
678
0
        formatter->outputCSUnitOnlyOnceIfSame()) {
679
0
        unit._exportToWKT(formatter);
680
0
    }
681
682
0
    formatter->popOutputUnit();
683
684
0
    if (isWKT2) {
685
0
        formatter->endNode();
686
0
    }
687
0
}
688
//! @endcond
689
690
// ---------------------------------------------------------------------------
691
692
//! @cond Doxygen_Suppress
693
void CoordinateSystem::_exportToJSON(
694
    io::JSONFormatter *formatter) const // throw(FormattingException)
695
0
{
696
0
    auto writer = formatter->writer();
697
0
    auto objectContext(formatter->MakeObjectContext("CoordinateSystem",
698
0
                                                    !identifiers().empty()));
699
700
0
    writer->AddObjKey("subtype");
701
0
    writer->Add(getWKT2Type(true));
702
703
0
    writer->AddObjKey("axis");
704
0
    {
705
0
        auto axisContext(writer->MakeArrayContext(false));
706
0
        const auto &l_axisList = axisList();
707
0
        for (auto &axis : l_axisList) {
708
0
            formatter->setOmitTypeInImmediateChild();
709
0
            axis->_exportToJSON(formatter);
710
0
        }
711
0
    }
712
713
0
    if (formatter->outputId()) {
714
0
        formatID(formatter);
715
0
    }
716
0
}
717
718
//! @endcond
719
720
// ---------------------------------------------------------------------------
721
722
//! @cond Doxygen_Suppress
723
bool CoordinateSystem::_isEquivalentTo(
724
    const util::IComparable *other, util::IComparable::Criterion criterion,
725
558k
    const io::DatabaseContextPtr &dbContext) const {
726
558k
    auto otherCS = dynamic_cast<const CoordinateSystem *>(other);
727
558k
    if (otherCS == nullptr ||
728
558k
        !IdentifiedObject::_isEquivalentTo(other, criterion, dbContext)) {
729
0
        return false;
730
0
    }
731
558k
    const auto &list = axisList();
732
558k
    const auto &otherList = otherCS->axisList();
733
558k
    if (list.size() != otherList.size()) {
734
33.5k
        return false;
735
33.5k
    }
736
525k
    if (getWKT2Type(true) != otherCS->getWKT2Type(true)) {
737
0
        return false;
738
0
    }
739
1.72M
    for (size_t i = 0; i < list.size(); i++) {
740
1.22M
        if (!list[i]->_isEquivalentTo(otherList[i].get(), criterion,
741
1.22M
                                      dbContext)) {
742
21.8k
            return false;
743
21.8k
        }
744
1.22M
    }
745
503k
    return true;
746
525k
}
747
//! @endcond
748
749
//! @cond Doxygen_Suppress
750
// ---------------------------------------------------------------------------
751
752
InvalidCoordinateSystem::InvalidCoordinateSystem(const char *message)
753
0
    : Exception(message) {}
754
755
// ---------------------------------------------------------------------------
756
757
InvalidCoordinateSystem::InvalidCoordinateSystem(const std::string &message)
758
0
    : Exception(message) {}
759
760
// ---------------------------------------------------------------------------
761
762
InvalidCoordinateSystem::InvalidCoordinateSystem(
763
0
    const InvalidCoordinateSystem &) = default;
764
765
// ---------------------------------------------------------------------------
766
767
0
InvalidCoordinateSystem::~InvalidCoordinateSystem() = default;
768
//! @endcond
769
770
// ---------------------------------------------------------------------------
771
772
//! @cond Doxygen_Suppress
773
SphericalCS::~SphericalCS() = default;
774
//! @endcond
775
776
// ---------------------------------------------------------------------------
777
778
SphericalCS::SphericalCS(const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
779
237
    : CoordinateSystem(axisIn) {}
780
781
// ---------------------------------------------------------------------------
782
783
#ifdef notdef
784
SphericalCS::SphericalCS(const SphericalCS &) = default;
785
#endif
786
787
// ---------------------------------------------------------------------------
788
789
/** \brief Instantiate a SphericalCS.
790
 *
791
 * @param properties See \ref general_properties.
792
 * @param axis1 The first axis.
793
 * @param axis2 The second axis.
794
 * @param axis3 The third axis.
795
 * @return a new SphericalCS.
796
 */
797
SphericalCSNNPtr SphericalCS::create(const util::PropertyMap &properties,
798
                                     const CoordinateSystemAxisNNPtr &axis1,
799
                                     const CoordinateSystemAxisNNPtr &axis2,
800
0
                                     const CoordinateSystemAxisNNPtr &axis3) {
801
0
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2, axis3};
802
0
    auto cs(SphericalCS::nn_make_shared<SphericalCS>(axis));
803
0
    cs->setProperties(properties);
804
0
    return cs;
805
0
}
806
807
// ---------------------------------------------------------------------------
808
809
/** \brief Instantiate a SphericalCS with 2 axis.
810
 *
811
 * This is an extension to ISO19111 to support (planet)-ocentric CS with
812
 * geocentric latitude.
813
 *
814
 * @param properties See \ref general_properties.
815
 * @param axis1 The first axis.
816
 * @param axis2 The second axis.
817
 * @return a new SphericalCS.
818
 */
819
SphericalCSNNPtr SphericalCS::create(const util::PropertyMap &properties,
820
                                     const CoordinateSystemAxisNNPtr &axis1,
821
237
                                     const CoordinateSystemAxisNNPtr &axis2) {
822
237
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2};
823
237
    auto cs(SphericalCS::nn_make_shared<SphericalCS>(axis));
824
237
    cs->setProperties(properties);
825
237
    return cs;
826
237
}
827
828
// ---------------------------------------------------------------------------
829
830
//! @cond Doxygen_Suppress
831
EllipsoidalCS::~EllipsoidalCS() = default;
832
//! @endcond
833
834
// ---------------------------------------------------------------------------
835
836
EllipsoidalCS::EllipsoidalCS(
837
    const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
838
97.7k
    : CoordinateSystem(axisIn) {}
839
840
// ---------------------------------------------------------------------------
841
842
#ifdef notdef
843
EllipsoidalCS::EllipsoidalCS(const EllipsoidalCS &) = default;
844
#endif
845
846
// ---------------------------------------------------------------------------
847
848
/** \brief Instantiate a EllipsoidalCS.
849
 *
850
 * @param properties See \ref general_properties.
851
 * @param axis1 The first axis.
852
 * @param axis2 The second axis.
853
 * @return a new EllipsoidalCS.
854
 */
855
EllipsoidalCSNNPtr
856
EllipsoidalCS::create(const util::PropertyMap &properties,
857
                      const CoordinateSystemAxisNNPtr &axis1,
858
71.4k
                      const CoordinateSystemAxisNNPtr &axis2) {
859
71.4k
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2};
860
71.4k
    auto cs(EllipsoidalCS::nn_make_shared<EllipsoidalCS>(axis));
861
71.4k
    cs->setProperties(properties);
862
71.4k
    return cs;
863
71.4k
}
864
865
// ---------------------------------------------------------------------------
866
867
/** \brief Instantiate a EllipsoidalCS.
868
 *
869
 * @param properties See \ref general_properties.
870
 * @param axis1 The first axis.
871
 * @param axis2 The second axis.
872
 * @param axis3 The third axis.
873
 * @return a new EllipsoidalCS.
874
 */
875
EllipsoidalCSNNPtr
876
EllipsoidalCS::create(const util::PropertyMap &properties,
877
                      const CoordinateSystemAxisNNPtr &axis1,
878
                      const CoordinateSystemAxisNNPtr &axis2,
879
26.3k
                      const CoordinateSystemAxisNNPtr &axis3) {
880
26.3k
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2, axis3};
881
26.3k
    auto cs(EllipsoidalCS::nn_make_shared<EllipsoidalCS>(axis));
882
26.3k
    cs->setProperties(properties);
883
26.3k
    return cs;
884
26.3k
}
885
886
// ---------------------------------------------------------------------------
887
888
//! @cond Doxygen_Suppress
889
CoordinateSystemAxisNNPtr
890
10.9k
CoordinateSystemAxis::createLAT_NORTH(const common::UnitOfMeasure &unit) {
891
10.9k
    return create(
892
10.9k
        util::PropertyMap().set(IdentifiedObject::NAME_KEY, AxisName::Latitude),
893
10.9k
        AxisAbbreviation::lat, AxisDirection::NORTH, unit);
894
10.9k
}
895
896
CoordinateSystemAxisNNPtr
897
10.9k
CoordinateSystemAxis::createLONG_EAST(const common::UnitOfMeasure &unit) {
898
10.9k
    return create(util::PropertyMap().set(IdentifiedObject::NAME_KEY,
899
10.9k
                                          AxisName::Longitude),
900
10.9k
                  AxisAbbreviation::lon, AxisDirection::EAST, unit);
901
10.9k
}
902
//! @endcond
903
904
// ---------------------------------------------------------------------------
905
906
/** \brief Instantiate a EllipsoidalCS with a Latitude (first) and Longitude
907
 * (second) axis.
908
 *
909
 * @param unit Angular unit of the axes.
910
 * @return a new EllipsoidalCS.
911
 */
912
EllipsoidalCSNNPtr
913
576
EllipsoidalCS::createLatitudeLongitude(const common::UnitOfMeasure &unit) {
914
576
    return EllipsoidalCS::create(util::PropertyMap(),
915
576
                                 CoordinateSystemAxis::createLAT_NORTH(unit),
916
576
                                 CoordinateSystemAxis::createLONG_EAST(unit));
917
576
}
918
919
// ---------------------------------------------------------------------------
920
921
/** \brief Instantiate a EllipsoidalCS with a Latitude (first), Longitude
922
 * (second) axis and ellipsoidal height (third) axis.
923
 *
924
 * @param angularUnit Angular unit of the latitude and longitude axes.
925
 * @param linearUnit Linear unit of the ellipsoidal height axis.
926
 * @return a new EllipsoidalCS.
927
 */
928
EllipsoidalCSNNPtr EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
929
    const common::UnitOfMeasure &angularUnit,
930
753
    const common::UnitOfMeasure &linearUnit) {
931
753
    return EllipsoidalCS::create(
932
753
        util::PropertyMap(), CoordinateSystemAxis::createLAT_NORTH(angularUnit),
933
753
        CoordinateSystemAxis::createLONG_EAST(angularUnit),
934
753
        CoordinateSystemAxis::create(
935
753
            util::PropertyMap().set(IdentifiedObject::NAME_KEY,
936
753
                                    AxisName::Ellipsoidal_height),
937
753
            AxisAbbreviation::h, AxisDirection::UP, linearUnit));
938
753
}
939
940
// ---------------------------------------------------------------------------
941
942
/** \brief Instantiate a EllipsoidalCS with a Longitude (first) and Latitude
943
 * (second) axis.
944
 *
945
 * @param unit Angular unit of the axes.
946
 * @return a new EllipsoidalCS.
947
 */
948
EllipsoidalCSNNPtr
949
3.42k
EllipsoidalCS::createLongitudeLatitude(const common::UnitOfMeasure &unit) {
950
3.42k
    return EllipsoidalCS::create(util::PropertyMap(),
951
3.42k
                                 CoordinateSystemAxis::createLONG_EAST(unit),
952
3.42k
                                 CoordinateSystemAxis::createLAT_NORTH(unit));
953
3.42k
}
954
955
// ---------------------------------------------------------------------------
956
957
/** \brief Instantiate a EllipsoidalCS with a Longitude (first), Latitude
958
 * (second) axis and ellipsoidal height (third) axis.
959
 *
960
 * @param angularUnit Angular unit of the latitude and longitude axes.
961
 * @param linearUnit Linear unit of the ellipsoidal height axis.
962
 * @return a new EllipsoidalCS.
963
 * @since 7.0
964
 */
965
EllipsoidalCSNNPtr EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
966
    const common::UnitOfMeasure &angularUnit,
967
6.24k
    const common::UnitOfMeasure &linearUnit) {
968
6.24k
    return EllipsoidalCS::create(
969
6.24k
        util::PropertyMap(), CoordinateSystemAxis::createLONG_EAST(angularUnit),
970
6.24k
        CoordinateSystemAxis::createLAT_NORTH(angularUnit),
971
6.24k
        CoordinateSystemAxis::create(
972
6.24k
            util::PropertyMap().set(IdentifiedObject::NAME_KEY,
973
6.24k
                                    AxisName::Ellipsoidal_height),
974
6.24k
            AxisAbbreviation::h, AxisDirection::UP, linearUnit));
975
6.24k
}
976
977
// ---------------------------------------------------------------------------
978
979
//! @cond Doxygen_Suppress
980
/** \brief Return the axis order in an enumerated way. */
981
72.3k
EllipsoidalCS::AxisOrder EllipsoidalCS::axisOrder() const {
982
72.3k
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
983
72.3k
    const auto &dir0 = l_axisList[0]->direction();
984
72.3k
    const auto &dir1 = l_axisList[1]->direction();
985
72.3k
    if (&dir0 == &AxisDirection::NORTH && &dir1 == &AxisDirection::EAST) {
986
34.1k
        if (l_axisList.size() == 2) {
987
19.4k
            return AxisOrder::LAT_NORTH_LONG_EAST;
988
19.4k
        } else if (&l_axisList[2]->direction() == &AxisDirection::UP) {
989
14.7k
            return AxisOrder::LAT_NORTH_LONG_EAST_HEIGHT_UP;
990
14.7k
        }
991
38.2k
    } else if (&dir0 == &AxisDirection::EAST &&
992
37.9k
               &dir1 == &AxisDirection::NORTH) {
993
37.8k
        if (l_axisList.size() == 2) {
994
20.2k
            return AxisOrder::LONG_EAST_LAT_NORTH;
995
20.2k
        } else if (&l_axisList[2]->direction() == &AxisDirection::UP) {
996
17.6k
            return AxisOrder::LONG_EAST_LAT_NORTH_HEIGHT_UP;
997
17.6k
        }
998
37.8k
    }
999
1000
360
    return AxisOrder::OTHER;
1001
72.3k
}
1002
//! @endcond
1003
1004
// ---------------------------------------------------------------------------
1005
1006
//! @cond Doxygen_Suppress
1007
EllipsoidalCSNNPtr EllipsoidalCS::alterAngularUnit(
1008
0
    const common::UnitOfMeasure &angularUnit) const {
1009
0
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
1010
0
    if (l_axisList.size() == 2) {
1011
0
        return EllipsoidalCS::create(util::PropertyMap(),
1012
0
                                     l_axisList[0]->alterUnit(angularUnit),
1013
0
                                     l_axisList[1]->alterUnit(angularUnit));
1014
0
    } else {
1015
0
        assert(l_axisList.size() == 3);
1016
0
        return EllipsoidalCS::create(
1017
0
            util::PropertyMap(), l_axisList[0]->alterUnit(angularUnit),
1018
0
            l_axisList[1]->alterUnit(angularUnit), l_axisList[2]);
1019
0
    }
1020
0
}
1021
//! @endcond
1022
1023
// ---------------------------------------------------------------------------
1024
1025
//! @cond Doxygen_Suppress
1026
EllipsoidalCSNNPtr
1027
0
EllipsoidalCS::alterLinearUnit(const common::UnitOfMeasure &linearUnit) const {
1028
0
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
1029
0
    if (l_axisList.size() == 2) {
1030
0
        return EllipsoidalCS::create(util::PropertyMap(), l_axisList[0],
1031
0
                                     l_axisList[1]);
1032
0
    } else {
1033
0
        assert(l_axisList.size() == 3);
1034
0
        return EllipsoidalCS::create(util::PropertyMap(), l_axisList[0],
1035
0
                                     l_axisList[1],
1036
0
                                     l_axisList[2]->alterUnit(linearUnit));
1037
0
    }
1038
0
}
1039
//! @endcond
1040
1041
// ---------------------------------------------------------------------------
1042
1043
//! @cond Doxygen_Suppress
1044
VerticalCS::~VerticalCS() = default;
1045
//! @endcond
1046
1047
// ---------------------------------------------------------------------------
1048
1049
//! @cond Doxygen_Suppress
1050
VerticalCS::VerticalCS(const CoordinateSystemAxisNNPtr &axisIn)
1051
16.2k
    : CoordinateSystem(std::vector<CoordinateSystemAxisNNPtr>{axisIn}) {}
1052
//! @endcond
1053
1054
// ---------------------------------------------------------------------------
1055
1056
#ifdef notdef
1057
VerticalCS::VerticalCS(const VerticalCS &) = default;
1058
#endif
1059
1060
// ---------------------------------------------------------------------------
1061
1062
/** \brief Instantiate a VerticalCS.
1063
 *
1064
 * @param properties See \ref general_properties.
1065
 * @param axis The axis.
1066
 * @return a new VerticalCS.
1067
 */
1068
VerticalCSNNPtr VerticalCS::create(const util::PropertyMap &properties,
1069
676
                                   const CoordinateSystemAxisNNPtr &axis) {
1070
676
    auto cs(VerticalCS::nn_make_shared<VerticalCS>(axis));
1071
676
    cs->setProperties(properties);
1072
676
    return cs;
1073
676
}
1074
1075
// ---------------------------------------------------------------------------
1076
1077
/** \brief Instantiate a VerticalCS with a Gravity-related height axis
1078
 *
1079
 * @param unit linear unit.
1080
 * @return a new VerticalCS.
1081
 */
1082
VerticalCSNNPtr
1083
15.6k
VerticalCS::createGravityRelatedHeight(const common::UnitOfMeasure &unit) {
1084
15.6k
    auto cs(VerticalCS::nn_make_shared<VerticalCS>(CoordinateSystemAxis::create(
1085
15.6k
        util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1086
15.6k
                                "Gravity-related height"),
1087
15.6k
        "H", AxisDirection::UP, unit)));
1088
15.6k
    return cs;
1089
15.6k
}
1090
1091
// ---------------------------------------------------------------------------
1092
1093
//! @cond Doxygen_Suppress
1094
0
VerticalCSNNPtr VerticalCS::alterUnit(const common::UnitOfMeasure &unit) const {
1095
0
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
1096
0
    return VerticalCS::nn_make_shared<VerticalCS>(
1097
0
        l_axisList[0]->alterUnit(unit));
1098
0
}
1099
//! @endcond
1100
1101
// ---------------------------------------------------------------------------
1102
1103
//! @cond Doxygen_Suppress
1104
CartesianCS::~CartesianCS() = default;
1105
//! @endcond
1106
1107
// ---------------------------------------------------------------------------
1108
1109
CartesianCS::CartesianCS(const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
1110
17.9k
    : CoordinateSystem(axisIn) {}
1111
1112
// ---------------------------------------------------------------------------
1113
1114
#ifdef notdef
1115
CartesianCS::CartesianCS(const CartesianCS &) = default;
1116
#endif
1117
1118
// ---------------------------------------------------------------------------
1119
1120
/** \brief Instantiate a CartesianCS.
1121
 *
1122
 * @param properties See \ref general_properties.
1123
 * @param axis1 The first axis.
1124
 * @param axis2 The second axis.
1125
 * @param enforceSameUnit Whether to check that all axis have the same unit.
1126
 * (since 9.9)
1127
 * @return a new CartesianCS.
1128
 * @throw InvalidCoordinateSystem in case of error (since 9.9)
1129
 */
1130
CartesianCSNNPtr CartesianCS::create(const util::PropertyMap &properties,
1131
                                     const CoordinateSystemAxisNNPtr &axis1,
1132
                                     const CoordinateSystemAxisNNPtr &axis2,
1133
11.0k
                                     bool enforceSameUnit) {
1134
11.0k
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2};
1135
11.0k
    if (enforceSameUnit && axis1->unit() != axis2->unit()) {
1136
0
        throw InvalidCoordinateSystem(
1137
0
            "All axis of a CartesianCS must have the same unit");
1138
0
    }
1139
11.0k
    auto cs(CartesianCS::nn_make_shared<CartesianCS>(axis));
1140
11.0k
    cs->setProperties(properties);
1141
11.0k
    return cs;
1142
11.0k
}
1143
1144
// ---------------------------------------------------------------------------
1145
1146
/** \brief Instantiate a CartesianCS.
1147
 *
1148
 * @param properties See \ref general_properties.
1149
 * @param axis1 The first axis.
1150
 * @param axis2 The second axis.
1151
 * @param axis3 The third axis.
1152
 * @param enforceSameUnit Whether to check that all axis have the same unit.
1153
 * (since 9.9)
1154
 * @return a new CartesianCS.
1155
 * @throw InvalidCoordinateSystem in case of error (since 9.9)
1156
 */
1157
CartesianCSNNPtr CartesianCS::create(const util::PropertyMap &properties,
1158
                                     const CoordinateSystemAxisNNPtr &axis1,
1159
                                     const CoordinateSystemAxisNNPtr &axis2,
1160
                                     const CoordinateSystemAxisNNPtr &axis3,
1161
6.89k
                                     bool enforceSameUnit) {
1162
6.89k
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2, axis3};
1163
6.89k
    if (enforceSameUnit &&
1164
5.57k
        (axis1->unit() != axis2->unit() || axis1->unit() != axis3->unit())) {
1165
0
        throw InvalidCoordinateSystem(
1166
0
            "All axis of a CartesianCS must have the same unit");
1167
0
    }
1168
6.89k
    auto cs(CartesianCS::nn_make_shared<CartesianCS>(axis));
1169
6.89k
    cs->setProperties(properties);
1170
6.89k
    return cs;
1171
6.89k
}
1172
1173
// ---------------------------------------------------------------------------
1174
1175
/** \brief Instantiate a CartesianCS with a Easting (first) and Northing
1176
 * (second) axis.
1177
 *
1178
 * @param unit Linear unit of the axes.
1179
 * @return a new CartesianCS.
1180
 */
1181
CartesianCSNNPtr
1182
1.23k
CartesianCS::createEastingNorthing(const common::UnitOfMeasure &unit) {
1183
1.23k
    return create(util::PropertyMap(),
1184
1.23k
                  CoordinateSystemAxis::create(
1185
1.23k
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1186
1.23k
                                              AxisName::Easting),
1187
1.23k
                      AxisAbbreviation::E, AxisDirection::EAST, unit),
1188
1.23k
                  CoordinateSystemAxis::create(
1189
1.23k
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1190
1.23k
                                              AxisName::Northing),
1191
1.23k
                      AxisAbbreviation::N, AxisDirection::NORTH, unit));
1192
1.23k
}
1193
1194
// ---------------------------------------------------------------------------
1195
1196
/** \brief Instantiate a CartesianCS with a Northing (first) and Easting
1197
 * (second) axis.
1198
 *
1199
 * @param unit Linear unit of the axes.
1200
 * @return a new CartesianCS.
1201
 */
1202
CartesianCSNNPtr
1203
0
CartesianCS::createNorthingEasting(const common::UnitOfMeasure &unit) {
1204
0
    return create(util::PropertyMap(),
1205
0
                  CoordinateSystemAxis::create(
1206
0
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1207
0
                                              AxisName::Northing),
1208
0
                      AxisAbbreviation::N, AxisDirection::NORTH, unit),
1209
0
                  CoordinateSystemAxis::create(
1210
0
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1211
0
                                              AxisName::Easting),
1212
0
                      AxisAbbreviation::E, AxisDirection::EAST, unit));
1213
0
}
1214
1215
// ---------------------------------------------------------------------------
1216
1217
/** \brief Instantiate a CartesianCS with a Westing (first) and Southing
1218
 * (second) axis.
1219
 *
1220
 * @param unit Linear unit of the axes.
1221
 * @return a new CartesianCS.
1222
 */
1223
CartesianCSNNPtr
1224
0
CartesianCS::createWestingSouthing(const common::UnitOfMeasure &unit) {
1225
0
    return create(util::PropertyMap(),
1226
0
                  CoordinateSystemAxis::create(
1227
0
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1228
0
                                              AxisName::Easting),
1229
0
                      AxisAbbreviation::Y, AxisDirection::WEST, unit),
1230
0
                  CoordinateSystemAxis::create(
1231
0
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1232
0
                                              AxisName::Northing),
1233
0
                      AxisAbbreviation::X, AxisDirection::SOUTH, unit));
1234
0
}
1235
1236
// ---------------------------------------------------------------------------
1237
1238
/** \brief Instantiate a CartesianCS, north-pole centered,
1239
 * with a Easting (first) South-Oriented and
1240
 * Northing (second) South-Oriented axis.
1241
 *
1242
 * @param unit Linear unit of the axes.
1243
 * @return a new CartesianCS.
1244
 */
1245
CartesianCSNNPtr CartesianCS::createNorthPoleEastingSouthNorthingSouth(
1246
13
    const common::UnitOfMeasure &unit) {
1247
13
    return create(util::PropertyMap(),
1248
13
                  CoordinateSystemAxis::create(
1249
13
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1250
13
                                              AxisName::Easting),
1251
13
                      AxisAbbreviation::E, AxisDirection::SOUTH, unit,
1252
13
                      Meridian::create(common::Angle(90))),
1253
13
                  CoordinateSystemAxis::create(
1254
13
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1255
13
                                              AxisName::Northing),
1256
13
                      AxisAbbreviation::N, AxisDirection::SOUTH, unit,
1257
13
                      Meridian::create(common::Angle(180))));
1258
13
}
1259
1260
// ---------------------------------------------------------------------------
1261
1262
/** \brief Instantiate a CartesianCS, south-pole centered,
1263
 * with a Easting (first) North-Oriented and
1264
 * Northing (second) North-Oriented axis.
1265
 *
1266
 * @param unit Linear unit of the axes.
1267
 * @return a new CartesianCS.
1268
 */
1269
CartesianCSNNPtr CartesianCS::createSouthPoleEastingNorthNorthingNorth(
1270
9
    const common::UnitOfMeasure &unit) {
1271
9
    return create(util::PropertyMap(),
1272
9
                  CoordinateSystemAxis::create(
1273
9
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1274
9
                                              AxisName::Easting),
1275
9
                      AxisAbbreviation::E, AxisDirection::NORTH, unit,
1276
9
                      Meridian::create(common::Angle(90))),
1277
9
                  CoordinateSystemAxis::create(
1278
9
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1279
9
                                              AxisName::Northing),
1280
9
                      AxisAbbreviation::N, AxisDirection::NORTH, unit,
1281
9
                      Meridian::create(common::Angle(0))));
1282
9
}
1283
1284
// ---------------------------------------------------------------------------
1285
1286
/** \brief Instantiate a CartesianCS with the three geocentric axes.
1287
 *
1288
 * @param unit Linear unit of the axes.
1289
 * @return a new CartesianCS.
1290
 */
1291
CartesianCSNNPtr
1292
2.70k
CartesianCS::createGeocentric(const common::UnitOfMeasure &unit) {
1293
2.70k
    return create(util::PropertyMap(),
1294
2.70k
                  CoordinateSystemAxis::create(
1295
2.70k
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1296
2.70k
                                              AxisName::Geocentric_X),
1297
2.70k
                      AxisAbbreviation::X, AxisDirection::GEOCENTRIC_X, unit),
1298
2.70k
                  CoordinateSystemAxis::create(
1299
2.70k
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1300
2.70k
                                              AxisName::Geocentric_Y),
1301
2.70k
                      AxisAbbreviation::Y, AxisDirection::GEOCENTRIC_Y, unit),
1302
2.70k
                  CoordinateSystemAxis::create(
1303
2.70k
                      util::PropertyMap().set(IdentifiedObject::NAME_KEY,
1304
2.70k
                                              AxisName::Geocentric_Z),
1305
2.70k
                      AxisAbbreviation::Z, AxisDirection::GEOCENTRIC_Z, unit));
1306
2.70k
}
1307
1308
// ---------------------------------------------------------------------------
1309
1310
//! @cond Doxygen_Suppress
1311
CartesianCSNNPtr
1312
0
CartesianCS::alterUnit(const common::UnitOfMeasure &unit) const {
1313
0
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
1314
0
    if (l_axisList.size() == 2) {
1315
0
        return CartesianCS::create(util::PropertyMap(),
1316
0
                                   l_axisList[0]->alterUnit(unit),
1317
0
                                   l_axisList[1]->alterUnit(unit));
1318
0
    } else {
1319
0
        assert(l_axisList.size() == 3);
1320
0
        return CartesianCS::create(
1321
0
            util::PropertyMap(), l_axisList[0]->alterUnit(unit),
1322
0
            l_axisList[1]->alterUnit(unit), l_axisList[2]->alterUnit(unit));
1323
0
    }
1324
0
}
1325
//! @endcond
1326
1327
// ---------------------------------------------------------------------------
1328
1329
//! @cond Doxygen_Suppress
1330
AffineCS::~AffineCS() = default;
1331
//! @endcond
1332
1333
// ---------------------------------------------------------------------------
1334
1335
AffineCS::AffineCS(const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
1336
0
    : CoordinateSystem(axisIn) {}
1337
1338
// ---------------------------------------------------------------------------
1339
1340
/** \brief Instantiate a AffineCS.
1341
 *
1342
 * @param properties See \ref general_properties.
1343
 * @param axis1 The first axis.
1344
 * @param axis2 The second axis.
1345
 * @return a new AffineCS.
1346
 */
1347
AffineCSNNPtr AffineCS::create(const util::PropertyMap &properties,
1348
                               const CoordinateSystemAxisNNPtr &axis1,
1349
0
                               const CoordinateSystemAxisNNPtr &axis2) {
1350
0
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2};
1351
0
    auto cs(AffineCS::nn_make_shared<AffineCS>(axis));
1352
0
    cs->setProperties(properties);
1353
0
    return cs;
1354
0
}
1355
1356
// ---------------------------------------------------------------------------
1357
1358
/** \brief Instantiate a AffineCS.
1359
 *
1360
 * @param properties See \ref general_properties.
1361
 * @param axis1 The first axis.
1362
 * @param axis2 The second axis.
1363
 * @param axis3 The third axis.
1364
 * @return a new AffineCS.
1365
 */
1366
AffineCSNNPtr AffineCS::create(const util::PropertyMap &properties,
1367
                               const CoordinateSystemAxisNNPtr &axis1,
1368
                               const CoordinateSystemAxisNNPtr &axis2,
1369
0
                               const CoordinateSystemAxisNNPtr &axis3) {
1370
0
    std::vector<CoordinateSystemAxisNNPtr> axis{axis1, axis2, axis3};
1371
0
    auto cs(AffineCS::nn_make_shared<AffineCS>(axis));
1372
0
    cs->setProperties(properties);
1373
0
    return cs;
1374
0
}
1375
1376
// ---------------------------------------------------------------------------
1377
1378
//! @cond Doxygen_Suppress
1379
0
AffineCSNNPtr AffineCS::alterUnit(const common::UnitOfMeasure &unit) const {
1380
0
    const auto &l_axisList = CoordinateSystem::getPrivate()->axisList;
1381
0
    if (l_axisList.size() == 2) {
1382
0
        return AffineCS::create(util::PropertyMap(),
1383
0
                                l_axisList[0]->alterUnit(unit),
1384
0
                                l_axisList[1]->alterUnit(unit));
1385
0
    } else {
1386
0
        assert(l_axisList.size() == 3);
1387
0
        return AffineCS::create(
1388
0
            util::PropertyMap(), l_axisList[0]->alterUnit(unit),
1389
0
            l_axisList[1]->alterUnit(unit), l_axisList[2]->alterUnit(unit));
1390
0
    }
1391
0
}
1392
//! @endcond
1393
1394
// ---------------------------------------------------------------------------
1395
1396
//! @cond Doxygen_Suppress
1397
OrdinalCS::~OrdinalCS() = default;
1398
//! @endcond
1399
1400
// ---------------------------------------------------------------------------
1401
1402
OrdinalCS::OrdinalCS(const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
1403
0
    : CoordinateSystem(axisIn) {}
1404
1405
// ---------------------------------------------------------------------------
1406
1407
#ifdef notdef
1408
OrdinalCS::OrdinalCS(const OrdinalCS &) = default;
1409
#endif
1410
1411
// ---------------------------------------------------------------------------
1412
1413
/** \brief Instantiate a OrdinalCS.
1414
 *
1415
 * @param properties See \ref general_properties.
1416
 * @param axisIn List of axis.
1417
 * @return a new OrdinalCS.
1418
 */
1419
OrdinalCSNNPtr
1420
OrdinalCS::create(const util::PropertyMap &properties,
1421
0
                  const std::vector<CoordinateSystemAxisNNPtr> &axisIn) {
1422
0
    auto cs(OrdinalCS::nn_make_shared<OrdinalCS>(axisIn));
1423
0
    cs->setProperties(properties);
1424
0
    return cs;
1425
0
}
1426
1427
// ---------------------------------------------------------------------------
1428
1429
//! @cond Doxygen_Suppress
1430
ParametricCS::~ParametricCS() = default;
1431
//! @endcond
1432
1433
// ---------------------------------------------------------------------------
1434
1435
ParametricCS::ParametricCS(const std::vector<CoordinateSystemAxisNNPtr> &axisIn)
1436
0
    : CoordinateSystem(axisIn) {}
1437
1438
// ---------------------------------------------------------------------------
1439
1440
#ifdef notdef
1441
ParametricCS::ParametricCS(const ParametricCS &) = default;
1442
#endif
1443
1444
// ---------------------------------------------------------------------------
1445
1446
/** \brief Instantiate a ParametricCS.
1447
 *
1448
 * @param properties See \ref general_properties.
1449
 * @param axisIn Axis.
1450
 * @return a new ParametricCS.
1451
 */
1452
ParametricCSNNPtr
1453
ParametricCS::create(const util::PropertyMap &properties,
1454
0
                     const CoordinateSystemAxisNNPtr &axisIn) {
1455
0
    auto cs(ParametricCS::nn_make_shared<ParametricCS>(
1456
0
        std::vector<CoordinateSystemAxisNNPtr>{axisIn}));
1457
0
    cs->setProperties(properties);
1458
0
    return cs;
1459
0
}
1460
1461
// ---------------------------------------------------------------------------
1462
1463
80
AxisDirection::AxisDirection(const std::string &nameIn) : CodeList(nameIn) {
1464
80
    auto lowerName = tolower(nameIn);
1465
80
    assert(registry.find(lowerName) == registry.end());
1466
80
    registry[lowerName] = this;
1467
80
}
1468
1469
// ---------------------------------------------------------------------------
1470
1471
//! @cond Doxygen_Suppress
1472
const AxisDirection *
1473
40.1k
AxisDirection::valueOf(const std::string &nameIn) noexcept {
1474
40.1k
    auto iter = registry.find(tolower(nameIn));
1475
40.1k
    if (iter == registry.end())
1476
189
        return nullptr;
1477
39.9k
    return iter->second;
1478
40.1k
}
1479
//! @endcond
1480
1481
// ---------------------------------------------------------------------------
1482
1483
4
RangeMeaning::RangeMeaning(const std::string &nameIn) : CodeList(nameIn) {
1484
4
    auto lowerName = tolower(nameIn);
1485
4
    assert(registry.find(lowerName) == registry.end());
1486
4
    registry[lowerName] = this;
1487
4
}
1488
1489
// ---------------------------------------------------------------------------
1490
1491
271k
RangeMeaning::RangeMeaning() : CodeList(std::string()) {}
1492
1493
// ---------------------------------------------------------------------------
1494
1495
//! @cond Doxygen_Suppress
1496
8
const RangeMeaning *RangeMeaning::valueOf(const std::string &nameIn) noexcept {
1497
8
    auto iter = registry.find(tolower(nameIn));
1498
8
    if (iter == registry.end())
1499
8
        return nullptr;
1500
0
    return iter->second;
1501
8
}
1502
//! @endcond
1503
1504
//! @cond Doxygen_Suppress
1505
// ---------------------------------------------------------------------------
1506
1507
AxisDirectionWKT1::AxisDirectionWKT1(const std::string &nameIn)
1508
14
    : CodeList(nameIn) {
1509
14
    auto lowerName = tolower(nameIn);
1510
14
    assert(registry.find(lowerName) == registry.end());
1511
14
    registry[lowerName] = this;
1512
14
}
1513
1514
// ---------------------------------------------------------------------------
1515
1516
0
const AxisDirectionWKT1 *AxisDirectionWKT1::valueOf(const std::string &nameIn) {
1517
0
    auto iter = registry.find(tolower(nameIn));
1518
0
    if (iter == registry.end())
1519
0
        return nullptr;
1520
0
    return iter->second;
1521
0
}
1522
1523
//! @endcond
1524
1525
// ---------------------------------------------------------------------------
1526
1527
//! @cond Doxygen_Suppress
1528
TemporalCS::~TemporalCS() = default;
1529
//! @endcond
1530
1531
// ---------------------------------------------------------------------------
1532
1533
//! @cond Doxygen_Suppress
1534
TemporalCS::TemporalCS(const CoordinateSystemAxisNNPtr &axisIn)
1535
0
    : CoordinateSystem(std::vector<CoordinateSystemAxisNNPtr>{axisIn}) {}
1536
//! @endcond
1537
1538
// ---------------------------------------------------------------------------
1539
1540
//! @cond Doxygen_Suppress
1541
DateTimeTemporalCS::~DateTimeTemporalCS() = default;
1542
//! @endcond
1543
1544
// ---------------------------------------------------------------------------
1545
1546
DateTimeTemporalCS::DateTimeTemporalCS(const CoordinateSystemAxisNNPtr &axisIn)
1547
0
    : TemporalCS(axisIn) {}
1548
1549
// ---------------------------------------------------------------------------
1550
1551
/** \brief Instantiate a DateTimeTemporalCS.
1552
 *
1553
 * @param properties See \ref general_properties.
1554
 * @param axisIn The axis.
1555
 * @return a new DateTimeTemporalCS.
1556
 */
1557
DateTimeTemporalCSNNPtr
1558
DateTimeTemporalCS::create(const util::PropertyMap &properties,
1559
0
                           const CoordinateSystemAxisNNPtr &axisIn) {
1560
0
    auto cs(DateTimeTemporalCS::nn_make_shared<DateTimeTemporalCS>(axisIn));
1561
0
    cs->setProperties(properties);
1562
0
    return cs;
1563
0
}
1564
1565
// ---------------------------------------------------------------------------
1566
1567
0
std::string DateTimeTemporalCS::getWKT2Type(bool use2019Keywords) const {
1568
0
    return use2019Keywords ? WKT2_2019_TYPE : WKT2_2015_TYPE;
1569
0
}
1570
1571
// ---------------------------------------------------------------------------
1572
1573
//! @cond Doxygen_Suppress
1574
TemporalCountCS::~TemporalCountCS() = default;
1575
//! @endcond
1576
1577
// ---------------------------------------------------------------------------
1578
1579
TemporalCountCS::TemporalCountCS(const CoordinateSystemAxisNNPtr &axisIn)
1580
0
    : TemporalCS(axisIn) {}
1581
1582
// ---------------------------------------------------------------------------
1583
1584
/** \brief Instantiate a TemporalCountCS.
1585
 *
1586
 * @param properties See \ref general_properties.
1587
 * @param axisIn The axis.
1588
 * @return a new TemporalCountCS.
1589
 */
1590
TemporalCountCSNNPtr
1591
TemporalCountCS::create(const util::PropertyMap &properties,
1592
0
                        const CoordinateSystemAxisNNPtr &axisIn) {
1593
0
    auto cs(TemporalCountCS::nn_make_shared<TemporalCountCS>(axisIn));
1594
0
    cs->setProperties(properties);
1595
0
    return cs;
1596
0
}
1597
1598
// ---------------------------------------------------------------------------
1599
1600
0
std::string TemporalCountCS::getWKT2Type(bool use2019Keywords) const {
1601
0
    return use2019Keywords ? WKT2_2019_TYPE : WKT2_2015_TYPE;
1602
0
}
1603
1604
// ---------------------------------------------------------------------------
1605
1606
//! @cond Doxygen_Suppress
1607
TemporalMeasureCS::~TemporalMeasureCS() = default;
1608
//! @endcond
1609
1610
// ---------------------------------------------------------------------------
1611
1612
TemporalMeasureCS::TemporalMeasureCS(const CoordinateSystemAxisNNPtr &axisIn)
1613
0
    : TemporalCS(axisIn) {}
1614
1615
// ---------------------------------------------------------------------------
1616
1617
/** \brief Instantiate a TemporalMeasureCS.
1618
 *
1619
 * @param properties See \ref general_properties.
1620
 * @param axisIn The axis.
1621
 * @return a new TemporalMeasureCS.
1622
 */
1623
TemporalMeasureCSNNPtr
1624
TemporalMeasureCS::create(const util::PropertyMap &properties,
1625
0
                          const CoordinateSystemAxisNNPtr &axisIn) {
1626
0
    auto cs(TemporalMeasureCS::nn_make_shared<TemporalMeasureCS>(axisIn));
1627
0
    cs->setProperties(properties);
1628
0
    return cs;
1629
0
}
1630
1631
// ---------------------------------------------------------------------------
1632
1633
0
std::string TemporalMeasureCS::getWKT2Type(bool use2019Keywords) const {
1634
0
    return use2019Keywords ? WKT2_2019_TYPE : WKT2_2015_TYPE;
1635
0
}
1636
1637
} // namespace cs
1638
NS_PROJ_END