Coverage Report

Created: 2026-07-14 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/iso19111/common.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/common.hpp"
34
#include "proj/io.hpp"
35
#include "proj/metadata.hpp"
36
#include "proj/util.hpp"
37
38
#include "proj/internal/internal.hpp"
39
#include "proj/internal/io_internal.hpp"
40
41
#include "proj.h"
42
#include "proj_internal.h"
43
44
#include "proj_json_streaming_writer.hpp"
45
46
#include <cmath> // M_PI
47
#include <cstdlib>
48
#include <memory>
49
#include <string>
50
#include <vector>
51
52
using namespace NS_PROJ::internal;
53
using namespace NS_PROJ::io;
54
using namespace NS_PROJ::metadata;
55
using namespace NS_PROJ::util;
56
57
#if 0
58
namespace dropbox{ namespace oxygen {
59
template<> nn<NS_PROJ::common::IdentifiedObjectPtr>::~nn() = default;
60
template<> nn<NS_PROJ::common::ObjectDomainPtr>::~nn() = default;
61
template<> nn<NS_PROJ::common::ObjectUsagePtr>::~nn() = default;
62
template<> nn<NS_PROJ::common::UnitOfMeasurePtr>::~nn() = default;
63
}}
64
#endif
65
66
NS_PROJ_START
67
namespace common {
68
69
// ---------------------------------------------------------------------------
70
71
//! @cond Doxygen_Suppress
72
struct UnitOfMeasure::Private {
73
    std::string name_{};
74
    double toSI_ = 1.0;
75
    UnitOfMeasure::Type type_{UnitOfMeasure::Type::UNKNOWN};
76
    std::string codeSpace_{};
77
    std::string code_{};
78
79
    Private(const std::string &nameIn, double toSIIn,
80
            UnitOfMeasure::Type typeIn, const std::string &codeSpaceIn,
81
            const std::string &codeIn)
82
185
        : name_(nameIn), toSI_(toSIIn), type_(typeIn), codeSpace_(codeSpaceIn),
83
185
          code_(codeIn) {}
84
};
85
//! @endcond
86
87
// ---------------------------------------------------------------------------
88
89
/** \brief Creates a UnitOfMeasure. */
90
UnitOfMeasure::UnitOfMeasure(const std::string &nameIn, double toSIIn,
91
                             UnitOfMeasure::Type typeIn,
92
                             const std::string &codeSpaceIn,
93
                             const std::string &codeIn)
94
185
    : d(std::make_unique<Private>(nameIn, toSIIn, typeIn, codeSpaceIn,
95
185
                                  codeIn)) {}
96
97
// ---------------------------------------------------------------------------
98
99
UnitOfMeasure::UnitOfMeasure(const UnitOfMeasure &other)
100
175
    : d(std::make_unique<Private>(*(other.d))) {}
101
102
// ---------------------------------------------------------------------------
103
104
//! @cond Doxygen_Suppress
105
80
UnitOfMeasure::~UnitOfMeasure() = default;
106
//! @endcond
107
108
// ---------------------------------------------------------------------------
109
110
//! @cond Doxygen_Suppress
111
80
UnitOfMeasure &UnitOfMeasure::operator=(const UnitOfMeasure &other) {
112
80
    if (this != &other) {
113
80
        *d = *(other.d);
114
80
    }
115
80
    return *this;
116
80
}
117
//! @endcond
118
119
// ---------------------------------------------------------------------------
120
121
//! @cond Doxygen_Suppress
122
0
UnitOfMeasure &UnitOfMeasure::operator=(UnitOfMeasure &&) = default;
123
//! @endcond
124
125
// ---------------------------------------------------------------------------
126
127
//! @cond Doxygen_Suppress
128
0
UnitOfMeasureNNPtr UnitOfMeasure::create(const UnitOfMeasure &other) {
129
0
    return util::nn_make_shared<UnitOfMeasure>(other);
130
0
}
131
//! @endcond
132
133
// ---------------------------------------------------------------------------
134
135
/** \brief Return the name of the unit of measure. */
136
20
const std::string &UnitOfMeasure::name() PROJ_PURE_DEFN { return d->name_; }
137
138
// ---------------------------------------------------------------------------
139
140
/** \brief Return the conversion factor to the unit of the
141
 * International System of Units of the same Type.
142
 *
143
 * For example, for foot, this would be 0.3048 (metre)
144
 *
145
 * @return the conversion factor, or 0 if no conversion exists.
146
 */
147
0
double UnitOfMeasure::conversionToSI() PROJ_PURE_DEFN { return d->toSI_; }
148
149
// ---------------------------------------------------------------------------
150
151
/** \brief Return the type of the unit of measure.
152
 */
153
0
UnitOfMeasure::Type UnitOfMeasure::type() PROJ_PURE_DEFN { return d->type_; }
154
155
// ---------------------------------------------------------------------------
156
157
/** \brief Return the code space of the unit of measure.
158
 *
159
 * For example "EPSG"
160
 *
161
 * @return the code space, or empty string.
162
 */
163
0
const std::string &UnitOfMeasure::codeSpace() PROJ_PURE_DEFN {
164
0
    return d->codeSpace_;
165
0
}
166
167
// ---------------------------------------------------------------------------
168
169
/** \brief Return the code of the unit of measure.
170
 *
171
 * @return the code, or empty string.
172
 */
173
0
const std::string &UnitOfMeasure::code() PROJ_PURE_DEFN { return d->code_; }
174
175
// ---------------------------------------------------------------------------
176
177
//! @cond Doxygen_Suppress
178
void UnitOfMeasure::_exportToWKT(
179
    WKTFormatter *formatter,
180
    const std::string &unitType) const // throw(FormattingException)
181
0
{
182
0
    const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
183
184
0
    const auto l_type = type();
185
0
    if (!unitType.empty()) {
186
0
        formatter->startNode(unitType, !codeSpace().empty());
187
0
    } else if (formatter->forceUNITKeyword() && l_type != Type::PARAMETRIC) {
188
0
        formatter->startNode(WKTConstants::UNIT, !codeSpace().empty());
189
0
    } else {
190
0
        if (isWKT2 && l_type == Type::LINEAR) {
191
0
            formatter->startNode(WKTConstants::LENGTHUNIT,
192
0
                                 !codeSpace().empty());
193
0
        } else if (isWKT2 && l_type == Type::ANGULAR) {
194
0
            formatter->startNode(WKTConstants::ANGLEUNIT, !codeSpace().empty());
195
0
        } else if (isWKT2 && l_type == Type::SCALE) {
196
0
            formatter->startNode(WKTConstants::SCALEUNIT, !codeSpace().empty());
197
0
        } else if (isWKT2 && l_type == Type::TIME) {
198
0
            formatter->startNode(WKTConstants::TIMEUNIT, !codeSpace().empty());
199
0
        } else if (isWKT2 && l_type == Type::PARAMETRIC) {
200
0
            formatter->startNode(WKTConstants::PARAMETRICUNIT,
201
0
                                 !codeSpace().empty());
202
0
        } else {
203
0
            formatter->startNode(WKTConstants::UNIT, !codeSpace().empty());
204
0
        }
205
0
    }
206
207
0
    {
208
0
        const auto &l_name = name();
209
0
        const bool esri = formatter->useESRIDialect();
210
0
        if (esri) {
211
0
            if (ci_equal(l_name, "degree")) {
212
0
                formatter->addQuotedString("Degree");
213
0
            } else if (ci_equal(l_name, "grad")) {
214
0
                formatter->addQuotedString("Grad");
215
0
            } else if (ci_equal(l_name, "metre")) {
216
0
                formatter->addQuotedString("Meter");
217
0
            } else {
218
0
                formatter->addQuotedString(l_name);
219
0
            }
220
0
        } else {
221
0
            formatter->addQuotedString(l_name);
222
0
        }
223
0
        const auto &factor = conversionToSI();
224
0
        if (!isWKT2 || l_type != Type::TIME || factor != 0.0) {
225
            // Some TIMEUNIT do not have a conversion factor
226
0
            formatter->add(factor);
227
0
        }
228
0
        if (!codeSpace().empty() && formatter->outputId()) {
229
0
            formatter->startNode(
230
0
                isWKT2 ? WKTConstants::ID : WKTConstants::AUTHORITY, false);
231
0
            formatter->addQuotedString(codeSpace());
232
0
            const auto &l_code = code();
233
0
            if (isWKT2) {
234
0
                try {
235
0
                    (void)std::stoi(l_code);
236
0
                    formatter->add(l_code);
237
0
                } catch (const std::exception &) {
238
0
                    formatter->addQuotedString(l_code);
239
0
                }
240
0
            } else {
241
0
                formatter->addQuotedString(l_code);
242
0
            }
243
0
            formatter->endNode();
244
0
        }
245
0
    }
246
0
    formatter->endNode();
247
0
}
248
249
// ---------------------------------------------------------------------------
250
251
void UnitOfMeasure::_exportToJSON(
252
    JSONFormatter *formatter) const // throw(FormattingException)
253
0
{
254
0
    auto writer = formatter->writer();
255
0
    const auto &l_codeSpace = codeSpace();
256
0
    auto objContext(
257
0
        formatter->MakeObjectContext(nullptr, !l_codeSpace.empty()));
258
0
    writer->AddObjKey("type");
259
0
    const auto l_type = type();
260
0
    if (l_type == Type::LINEAR) {
261
0
        writer->Add("LinearUnit");
262
0
    } else if (l_type == Type::ANGULAR) {
263
0
        writer->Add("AngularUnit");
264
0
    } else if (l_type == Type::SCALE) {
265
0
        writer->Add("ScaleUnit");
266
0
    } else if (l_type == Type::TIME) {
267
0
        writer->Add("TimeUnit");
268
0
    } else if (l_type == Type::PARAMETRIC) {
269
0
        writer->Add("ParametricUnit");
270
0
    } else {
271
0
        writer->Add("Unit");
272
0
    }
273
274
0
    writer->AddObjKey("name");
275
0
    const auto &l_name = name();
276
0
    writer->Add(l_name);
277
278
0
    const auto &factor = conversionToSI();
279
0
    writer->AddObjKey("conversion_factor");
280
0
    writer->Add(factor, 15);
281
282
0
    if (!l_codeSpace.empty() && formatter->outputId()) {
283
0
        writer->AddObjKey("id");
284
0
        auto idContext(formatter->MakeObjectContext(nullptr, false));
285
0
        writer->AddObjKey("authority");
286
0
        writer->Add(l_codeSpace);
287
0
        writer->AddObjKey("code");
288
0
        const auto &l_code = code();
289
0
        try {
290
0
            writer->Add(std::stoi(l_code));
291
0
        } catch (const std::exception &) {
292
0
            writer->Add(l_code);
293
0
        }
294
0
    }
295
0
}
296
297
//! @endcond
298
299
// ---------------------------------------------------------------------------
300
301
/** Returns whether two units of measures are equal.
302
 *
303
 * The comparison is based on the name.
304
 */
305
0
bool UnitOfMeasure::operator==(const UnitOfMeasure &other) PROJ_PURE_DEFN {
306
0
    return name() == other.name();
307
0
}
308
309
// ---------------------------------------------------------------------------
310
311
/** Returns whether two units of measures are different.
312
 *
313
 * The comparison is based on the name.
314
 */
315
10
bool UnitOfMeasure::operator!=(const UnitOfMeasure &other) PROJ_PURE_DEFN {
316
10
    return name() != other.name();
317
10
}
318
319
// ---------------------------------------------------------------------------
320
321
//! @cond Doxygen_Suppress
322
0
std::string UnitOfMeasure::exportToPROJString() const {
323
0
    if (type() == Type::LINEAR) {
324
0
        auto proj_units = pj_list_linear_units();
325
0
        for (int i = 0; proj_units[i].id != nullptr; i++) {
326
0
            if (::fabs(proj_units[i].factor - conversionToSI()) <
327
0
                1e-10 * conversionToSI()) {
328
0
                return proj_units[i].id;
329
0
            }
330
0
        }
331
0
    } else if (type() == Type::ANGULAR) {
332
0
        auto proj_angular_units = pj_list_angular_units();
333
0
        for (int i = 0; proj_angular_units[i].id != nullptr; i++) {
334
0
            if (::fabs(proj_angular_units[i].factor - conversionToSI()) <
335
0
                1e-10 * conversionToSI()) {
336
0
                return proj_angular_units[i].id;
337
0
            }
338
0
        }
339
0
    }
340
0
    return std::string();
341
0
}
342
//! @endcond
343
344
// ---------------------------------------------------------------------------
345
346
//! @cond Doxygen_Suppress
347
bool UnitOfMeasure::_isEquivalentTo(
348
0
    const UnitOfMeasure &other, util::IComparable::Criterion criterion) const {
349
0
    if (criterion == util::IComparable::Criterion::STRICT) {
350
0
        return operator==(other);
351
0
    }
352
0
    return std::fabs(conversionToSI() - other.conversionToSI()) <=
353
0
           1e-10 * std::fabs(conversionToSI());
354
0
}
355
356
//! @endcond
357
// ---------------------------------------------------------------------------
358
359
//! @cond Doxygen_Suppress
360
struct Measure::Private {
361
    double value_ = 0.0;
362
    UnitOfMeasure unit_{};
363
364
    Private(double valueIn, const UnitOfMeasure &unitIn)
365
120
        : value_(valueIn), unit_(unitIn) {}
366
};
367
//! @endcond
368
369
// ---------------------------------------------------------------------------
370
371
/** \brief Instantiate a Measure.
372
 */
373
Measure::Measure(double valueIn, const UnitOfMeasure &unitIn)
374
120
    : d(std::make_unique<Private>(valueIn, unitIn)) {}
375
376
// ---------------------------------------------------------------------------
377
378
Measure::Measure(const Measure &other)
379
55
    : d(std::make_unique<Private>(*(other.d))) {}
380
381
// ---------------------------------------------------------------------------
382
383
//! @cond Doxygen_Suppress
384
55
Measure::~Measure() = default;
385
//! @endcond
386
387
// ---------------------------------------------------------------------------
388
389
/** \brief Return the unit of the Measure.
390
 */
391
0
const UnitOfMeasure &Measure::unit() PROJ_PURE_DEFN { return d->unit_; }
392
393
// ---------------------------------------------------------------------------
394
395
/** \brief Return the value of the Measure, after conversion to the
396
 * corresponding
397
 * unit of the International System.
398
 */
399
0
double Measure::getSIValue() PROJ_PURE_DEFN {
400
0
    return d->value_ * d->unit_.conversionToSI();
401
0
}
402
403
// ---------------------------------------------------------------------------
404
405
/** \brief Return the value of the measure, expressed in the unit()
406
 */
407
15
double Measure::value() PROJ_PURE_DEFN { return d->value_; }
408
409
// ---------------------------------------------------------------------------
410
411
/** \brief Return the value of this measure expressed into the provided unit.
412
 */
413
0
double Measure::convertToUnit(const UnitOfMeasure &otherUnit) PROJ_PURE_DEFN {
414
0
    return getSIValue() / otherUnit.conversionToSI();
415
0
}
416
417
// ---------------------------------------------------------------------------
418
419
/** \brief Return whether two measures are equal.
420
 *
421
 * The comparison is done both on the value and the unit.
422
 */
423
0
bool Measure::operator==(const Measure &other) PROJ_PURE_DEFN {
424
0
    return d->value_ == other.d->value_ && d->unit_ == other.d->unit_;
425
0
}
426
427
// ---------------------------------------------------------------------------
428
429
/** \brief Returns whether an object is equivalent to another one.
430
 * @param other other object to compare to
431
 * @param criterion comparison criterion.
432
 * @param maxRelativeError Maximum relative error allowed.
433
 * @return true if objects are equivalent.
434
 */
435
bool Measure::_isEquivalentTo(const Measure &other,
436
                              util::IComparable::Criterion criterion,
437
0
                              double maxRelativeError) const {
438
0
    if (criterion == util::IComparable::Criterion::STRICT) {
439
0
        return operator==(other);
440
0
    }
441
0
    const double SIValue = getSIValue();
442
0
    const double otherSIValue = other.getSIValue();
443
    // It is arguable that we have to deal with infinite values, but this
444
    // helps robustify some situations.
445
0
    if (std::isinf(SIValue) && std::isinf(otherSIValue))
446
0
        return SIValue * otherSIValue > 0;
447
0
    return std::fabs(SIValue - otherSIValue) <=
448
0
           maxRelativeError * std::fabs(SIValue);
449
0
}
450
451
// ---------------------------------------------------------------------------
452
453
/** \brief Instantiate a Scale.
454
 *
455
 * @param valueIn value
456
 */
457
20
Scale::Scale(double valueIn) : Measure(valueIn, UnitOfMeasure::SCALE_UNITY) {}
458
459
// ---------------------------------------------------------------------------
460
461
/** \brief Instantiate a Scale.
462
 *
463
 * @param valueIn value
464
 * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::SCALE
465
 */
466
Scale::Scale(double valueIn, const UnitOfMeasure &unitIn)
467
0
    : Measure(valueIn, unitIn) {}
468
469
// ---------------------------------------------------------------------------
470
471
//! @cond Doxygen_Suppress
472
15
Scale::Scale(const Scale &) = default;
473
//! @endcond
474
475
// ---------------------------------------------------------------------------
476
477
//! @cond Doxygen_Suppress
478
Scale::~Scale() = default;
479
//! @endcond
480
481
// ---------------------------------------------------------------------------
482
483
/** \brief Instantiate a Angle.
484
 *
485
 * @param valueIn value
486
 */
487
10
Angle::Angle(double valueIn) : Measure(valueIn, UnitOfMeasure::DEGREE) {}
488
489
// ---------------------------------------------------------------------------
490
491
/** \brief Instantiate a Angle.
492
 *
493
 * @param valueIn value
494
 * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::ANGULAR
495
 */
496
Angle::Angle(double valueIn, const UnitOfMeasure &unitIn)
497
5
    : Measure(valueIn, unitIn) {}
498
499
// ---------------------------------------------------------------------------
500
501
//! @cond Doxygen_Suppress
502
15
Angle::Angle(const Angle &) = default;
503
//! @endcond
504
505
// ---------------------------------------------------------------------------
506
507
//! @cond Doxygen_Suppress
508
Angle::~Angle() = default;
509
//! @endcond
510
511
// ---------------------------------------------------------------------------
512
513
/** \brief Instantiate a Length.
514
 *
515
 * @param valueIn value
516
 */
517
60
Length::Length(double valueIn) : Measure(valueIn, UnitOfMeasure::METRE) {}
518
519
// ---------------------------------------------------------------------------
520
521
/** \brief Instantiate a Length.
522
 *
523
 * @param valueIn value
524
 * @param unitIn unit. Constraint: unit.type() == UnitOfMeasure::Type::LINEAR
525
 */
526
Length::Length(double valueIn, const UnitOfMeasure &unitIn)
527
0
    : Measure(valueIn, unitIn) {}
528
529
// ---------------------------------------------------------------------------
530
531
//! @cond Doxygen_Suppress
532
25
Length::Length(const Length &) = default;
533
//! @endcond
534
535
// ---------------------------------------------------------------------------
536
537
//! @cond Doxygen_Suppress
538
Length::~Length() = default;
539
//! @endcond
540
541
// ---------------------------------------------------------------------------
542
543
//! @cond Doxygen_Suppress
544
struct DateTime::Private {
545
    std::string str_{};
546
547
20
    explicit Private(const std::string &str) : str_(str) {}
548
};
549
//! @endcond
550
551
// ---------------------------------------------------------------------------
552
553
20
DateTime::DateTime() : d(std::make_unique<Private>(std::string())) {}
554
555
// ---------------------------------------------------------------------------
556
557
DateTime::DateTime(const std::string &str)
558
0
    : d(std::make_unique<Private>(str)) {}
559
560
// ---------------------------------------------------------------------------
561
562
//! @cond Doxygen_Suppress
563
DateTime::DateTime(const DateTime &other)
564
0
    : d(std::make_unique<Private>(*(other.d))) {}
565
//! @endcond
566
567
// ---------------------------------------------------------------------------
568
569
//! @cond Doxygen_Suppress
570
0
DateTime &DateTime::operator=(const DateTime &other) {
571
0
    d->str_ = other.d->str_;
572
0
    return *this;
573
0
}
574
//! @endcond
575
576
// ---------------------------------------------------------------------------
577
578
//! @cond Doxygen_Suppress
579
0
DateTime::~DateTime() = default;
580
//! @endcond
581
582
// ---------------------------------------------------------------------------
583
584
/** \brief Instantiate a DateTime. */
585
0
DateTime DateTime::create(const std::string &str) { return DateTime(str); }
586
587
// ---------------------------------------------------------------------------
588
589
/** \brief Return whether the DateTime is ISO:8601 compliant.
590
 *
591
 * \remark The current implementation is really simplistic, and aimed at
592
 * detecting date-times that are not ISO:8601 compliant.
593
 */
594
0
bool DateTime::isISO_8601() const {
595
0
    return !d->str_.empty() && d->str_[0] >= '0' && d->str_[0] <= '9' &&
596
0
           d->str_.find(' ') == std::string::npos;
597
0
}
598
599
// ---------------------------------------------------------------------------
600
601
/** \brief Return the DateTime as a string.
602
 */
603
0
std::string DateTime::toString() const { return d->str_; }
604
605
// ---------------------------------------------------------------------------
606
607
//! @cond Doxygen_Suppress
608
// cppcheck-suppress copyCtorAndEqOperator
609
struct IdentifiedObject::Private {
610
    IdentifierNNPtr name{Identifier::create()};
611
    std::vector<IdentifierNNPtr> identifiers{};
612
    std::vector<GenericNameNNPtr> aliases{};
613
    std::string remarks{};
614
    bool isDeprecated{};
615
616
    void setIdentifiers(const PropertyMap &properties);
617
    void setName(const PropertyMap &properties);
618
    void setAliases(const PropertyMap &properties);
619
};
620
//! @endcond
621
622
// ---------------------------------------------------------------------------
623
624
205
IdentifiedObject::IdentifiedObject() : d(std::make_unique<Private>()) {}
625
626
// ---------------------------------------------------------------------------
627
628
IdentifiedObject::IdentifiedObject(const IdentifiedObject &other)
629
0
    : d(std::make_unique<Private>(*(other.d))) {}
630
631
// ---------------------------------------------------------------------------
632
633
//! @cond Doxygen_Suppress
634
0
IdentifiedObject::~IdentifiedObject() = default;
635
//! @endcond
636
637
// ---------------------------------------------------------------------------
638
639
/** \brief Return the name of the object.
640
 *
641
 * Generally, the only interesting field of the name will be
642
 * name()->description().
643
 */
644
0
const IdentifierNNPtr &IdentifiedObject::name() PROJ_PURE_DEFN {
645
0
    return d->name;
646
0
}
647
648
// ---------------------------------------------------------------------------
649
650
/** \brief Return the name of the object.
651
 *
652
 * Return *(name()->description())
653
 */
654
0
const std::string &IdentifiedObject::nameStr() PROJ_PURE_DEFN {
655
0
    return *(d->name->description());
656
0
}
657
658
// ---------------------------------------------------------------------------
659
660
/** \brief Return the identifier(s) of the object
661
 *
662
 * Generally, those will have Identifier::code() and Identifier::codeSpace()
663
 * filled.
664
 */
665
const std::vector<IdentifierNNPtr> &
666
0
IdentifiedObject::identifiers() PROJ_PURE_DEFN {
667
0
    return d->identifiers;
668
0
}
669
670
// ---------------------------------------------------------------------------
671
672
/** \brief Return the alias(es) of the object.
673
 */
674
const std::vector<GenericNameNNPtr> &
675
0
IdentifiedObject::aliases() PROJ_PURE_DEFN {
676
0
    return d->aliases;
677
0
}
678
679
// ---------------------------------------------------------------------------
680
681
/** \brief Return the (first) alias of the object as a string.
682
 *
683
 * Shortcut for aliases()[0]->toFullyQualifiedName()->toString()
684
 */
685
0
std::string IdentifiedObject::alias() PROJ_PURE_DEFN {
686
0
    if (d->aliases.empty())
687
0
        return std::string();
688
0
    return d->aliases[0]->toFullyQualifiedName()->toString();
689
0
}
690
691
// ---------------------------------------------------------------------------
692
693
/** \brief Return the EPSG code.
694
 * @return code, or 0 if not found
695
 */
696
0
int IdentifiedObject::getEPSGCode() PROJ_PURE_DEFN {
697
0
    for (const auto &id : identifiers()) {
698
0
        if (ci_equal(*(id->codeSpace()), metadata::Identifier::EPSG)) {
699
0
            return ::atoi(id->code().c_str());
700
0
        }
701
0
    }
702
0
    return 0;
703
0
}
704
705
// ---------------------------------------------------------------------------
706
707
/** \brief Return the remarks.
708
 */
709
0
const std::string &IdentifiedObject::remarks() PROJ_PURE_DEFN {
710
0
    return d->remarks;
711
0
}
712
713
// ---------------------------------------------------------------------------
714
715
/** \brief Return whether the object is deprecated.
716
 *
717
 * \remark Extension of \ref ISO_19111_2019
718
 */
719
0
bool IdentifiedObject::isDeprecated() PROJ_PURE_DEFN { return d->isDeprecated; }
720
721
// ---------------------------------------------------------------------------
722
//! @cond Doxygen_Suppress
723
724
void IdentifiedObject::Private::setName(
725
    const PropertyMap &properties) // throw(InvalidValueTypeException)
726
205
{
727
205
    const auto pVal = properties.get(NAME_KEY);
728
205
    if (!pVal) {
729
35
        return;
730
35
    }
731
170
    if (const auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) {
732
170
        if (genVal->type() == BoxedValue::Type::STRING) {
733
170
            name = Identifier::createFromDescription(genVal->stringValue());
734
170
        } else {
735
0
            throw InvalidValueTypeException("Invalid value type for " +
736
0
                                            NAME_KEY);
737
0
        }
738
170
    } else {
739
0
        if (auto identifier =
740
0
                util::nn_dynamic_pointer_cast<Identifier>(*pVal)) {
741
0
            name = NN_NO_CHECK(identifier);
742
0
        } else {
743
0
            throw InvalidValueTypeException("Invalid value type for " +
744
0
                                            NAME_KEY);
745
0
        }
746
0
    }
747
170
}
748
749
// ---------------------------------------------------------------------------
750
751
void IdentifiedObject::Private::setIdentifiers(
752
    const PropertyMap &properties) // throw(InvalidValueTypeException)
753
205
{
754
205
    auto pVal = properties.get(IDENTIFIERS_KEY);
755
205
    if (!pVal) {
756
757
205
        pVal = properties.get(Identifier::CODE_KEY);
758
205
        if (pVal) {
759
85
            identifiers.clear();
760
85
            identifiers.push_back(
761
85
                Identifier::create(std::string(), properties));
762
85
        }
763
205
        return;
764
205
    }
765
0
    if (auto identifier = util::nn_dynamic_pointer_cast<Identifier>(*pVal)) {
766
0
        identifiers.clear();
767
0
        identifiers.push_back(NN_NO_CHECK(identifier));
768
0
    } else {
769
0
        if (auto array = dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) {
770
0
            identifiers.clear();
771
0
            for (const auto &val : *array) {
772
0
                identifier = util::nn_dynamic_pointer_cast<Identifier>(val);
773
0
                if (identifier) {
774
0
                    identifiers.push_back(NN_NO_CHECK(identifier));
775
0
                } else {
776
0
                    throw InvalidValueTypeException("Invalid value type for " +
777
0
                                                    IDENTIFIERS_KEY);
778
0
                }
779
0
            }
780
0
        } else {
781
0
            throw InvalidValueTypeException("Invalid value type for " +
782
0
                                            IDENTIFIERS_KEY);
783
0
        }
784
0
    }
785
0
}
786
787
// ---------------------------------------------------------------------------
788
789
void IdentifiedObject::Private::setAliases(
790
    const PropertyMap &properties) // throw(InvalidValueTypeException)
791
205
{
792
205
    const auto pVal = properties.get(ALIAS_KEY);
793
205
    if (!pVal) {
794
205
        return;
795
205
    }
796
0
    if (auto l_name = util::nn_dynamic_pointer_cast<GenericName>(*pVal)) {
797
0
        aliases.clear();
798
0
        aliases.push_back(NN_NO_CHECK(l_name));
799
0
    } else {
800
0
        if (const auto array =
801
0
                dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) {
802
0
            aliases.clear();
803
0
            for (const auto &val : *array) {
804
0
                l_name = util::nn_dynamic_pointer_cast<GenericName>(val);
805
0
                if (l_name) {
806
0
                    aliases.push_back(NN_NO_CHECK(l_name));
807
0
                } else {
808
0
                    if (auto genVal =
809
0
                            dynamic_cast<const BoxedValue *>(val.get())) {
810
0
                        if (genVal->type() == BoxedValue::Type::STRING) {
811
0
                            aliases.push_back(NameFactory::createLocalName(
812
0
                                nullptr, genVal->stringValue()));
813
0
                        } else {
814
0
                            throw InvalidValueTypeException(
815
0
                                "Invalid value type for " + ALIAS_KEY);
816
0
                        }
817
0
                    } else {
818
0
                        throw InvalidValueTypeException(
819
0
                            "Invalid value type for " + ALIAS_KEY);
820
0
                    }
821
0
                }
822
0
            }
823
0
        } else {
824
0
            std::string temp;
825
0
            if (properties.getStringValue(ALIAS_KEY, temp)) {
826
0
                aliases.clear();
827
0
                aliases.push_back(NameFactory::createLocalName(nullptr, temp));
828
0
            } else {
829
0
                throw InvalidValueTypeException("Invalid value type for " +
830
0
                                                ALIAS_KEY);
831
0
            }
832
0
        }
833
0
    }
834
0
}
835
//! @endcond
836
837
// ---------------------------------------------------------------------------
838
839
void IdentifiedObject::setProperties(
840
    const PropertyMap &properties) // throw(InvalidValueTypeException)
841
205
{
842
205
    d->setName(properties);
843
205
    d->setIdentifiers(properties);
844
205
    d->setAliases(properties);
845
846
205
    properties.getStringValue(REMARKS_KEY, d->remarks);
847
848
205
    {
849
205
        const auto pVal = properties.get(DEPRECATED_KEY);
850
205
        if (pVal) {
851
0
            if (const auto genVal =
852
0
                    dynamic_cast<const BoxedValue *>(pVal->get())) {
853
0
                if (genVal->type() == BoxedValue::Type::BOOLEAN) {
854
0
                    d->isDeprecated = genVal->booleanValue();
855
0
                } else {
856
0
                    throw InvalidValueTypeException("Invalid value type for " +
857
0
                                                    DEPRECATED_KEY);
858
0
                }
859
0
            } else {
860
0
                throw InvalidValueTypeException("Invalid value type for " +
861
0
                                                DEPRECATED_KEY);
862
0
            }
863
0
        }
864
205
    }
865
205
}
866
867
// ---------------------------------------------------------------------------
868
869
//! @cond Doxygen_Suppress
870
0
void IdentifiedObject::formatID(WKTFormatter *formatter) const {
871
0
    const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
872
0
    for (const auto &id : identifiers()) {
873
0
        id->_exportToWKT(formatter);
874
0
        if (!isWKT2) {
875
0
            break;
876
0
        }
877
0
    }
878
0
}
879
880
// ---------------------------------------------------------------------------
881
882
0
void IdentifiedObject::formatRemarks(WKTFormatter *formatter) const {
883
0
    if (!remarks().empty()) {
884
0
        formatter->startNode(WKTConstants::REMARK, false);
885
0
        formatter->addQuotedString(remarks());
886
0
        formatter->endNode();
887
0
    }
888
0
}
889
890
// ---------------------------------------------------------------------------
891
892
0
void IdentifiedObject::formatID(JSONFormatter *formatter) const {
893
0
    const auto &ids(identifiers());
894
0
    auto writer = formatter->writer();
895
0
    if (ids.size() == 1) {
896
0
        writer->AddObjKey("id");
897
0
        ids.front()->_exportToJSON(formatter);
898
0
    } else if (!ids.empty()) {
899
0
        writer->AddObjKey("ids");
900
0
        auto arrayContext(writer->MakeArrayContext());
901
0
        for (const auto &id : ids) {
902
0
            id->_exportToJSON(formatter);
903
0
        }
904
0
    }
905
0
}
906
907
// ---------------------------------------------------------------------------
908
909
0
void IdentifiedObject::formatRemarks(JSONFormatter *formatter) const {
910
0
    if (!remarks().empty()) {
911
0
        auto writer = formatter->writer();
912
0
        writer->AddObjKey("remarks");
913
0
        writer->Add(remarks());
914
0
    }
915
0
}
916
917
// ---------------------------------------------------------------------------
918
919
bool IdentifiedObject::_isEquivalentTo(
920
    const util::IComparable *other, util::IComparable::Criterion criterion,
921
0
    const io::DatabaseContextPtr &dbContext) const {
922
0
    auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other);
923
0
    if (!otherIdObj)
924
0
        return false;
925
0
    return _isEquivalentTo(otherIdObj, criterion, dbContext);
926
0
}
927
928
// ---------------------------------------------------------------------------
929
930
bool IdentifiedObject::_isEquivalentTo(
931
    const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion,
932
0
    const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN {
933
0
    if (criterion == util::IComparable::Criterion::STRICT) {
934
0
        if (!ci_equal(nameStr(), otherIdObj->nameStr())) {
935
0
            return false;
936
0
        }
937
        // TODO test id etc
938
0
    } else {
939
0
        if (!metadata::Identifier::isEquivalentName(
940
0
                nameStr().c_str(), otherIdObj->nameStr().c_str())) {
941
0
            return hasEquivalentNameToUsingAlias(otherIdObj, dbContext);
942
0
        }
943
0
    }
944
0
    return true;
945
0
}
946
947
// ---------------------------------------------------------------------------
948
949
bool IdentifiedObject::hasEquivalentNameToUsingAlias(
950
0
    const IdentifiedObject *, const io::DatabaseContextPtr &) const {
951
0
    return false;
952
0
}
953
954
//! @endcond
955
956
// ---------------------------------------------------------------------------
957
958
//! @cond Doxygen_Suppress
959
struct ObjectDomain::Private {
960
    optional<std::string> scope_{};
961
    ExtentPtr domainOfValidity_{};
962
963
    Private(const optional<std::string> &scopeIn, const ExtentPtr &extent)
964
0
        : scope_(scopeIn), domainOfValidity_(extent) {}
965
};
966
//! @endcond
967
968
// ---------------------------------------------------------------------------
969
970
//! @cond Doxygen_Suppress
971
ObjectDomain::ObjectDomain(const optional<std::string> &scopeIn,
972
                           const ExtentPtr &extent)
973
0
    : d(std::make_unique<Private>(scopeIn, extent)) {}
974
//! @endcond
975
976
// ---------------------------------------------------------------------------
977
978
ObjectDomain::ObjectDomain(const ObjectDomain &other)
979
0
    : d(std::make_unique<Private>(*(other.d))) {}
980
981
// ---------------------------------------------------------------------------
982
983
//! @cond Doxygen_Suppress
984
0
ObjectDomain::~ObjectDomain() = default;
985
//! @endcond
986
987
// ---------------------------------------------------------------------------
988
989
/** \brief Return the scope.
990
 *
991
 * @return the scope, or empty.
992
 */
993
0
const optional<std::string> &ObjectDomain::scope() PROJ_PURE_DEFN {
994
0
    return d->scope_;
995
0
}
996
997
// ---------------------------------------------------------------------------
998
999
/** \brief Return the domain of validity.
1000
 *
1001
 * @return the domain of validity, or nullptr.
1002
 */
1003
0
const ExtentPtr &ObjectDomain::domainOfValidity() PROJ_PURE_DEFN {
1004
0
    return d->domainOfValidity_;
1005
0
}
1006
1007
// ---------------------------------------------------------------------------
1008
1009
/** \brief Instantiate a ObjectDomain.
1010
 */
1011
ObjectDomainNNPtr ObjectDomain::create(const optional<std::string> &scopeIn,
1012
0
                                       const ExtentPtr &extent) {
1013
0
    return ObjectDomain::nn_make_shared<ObjectDomain>(scopeIn, extent);
1014
0
}
1015
1016
// ---------------------------------------------------------------------------
1017
1018
//! @cond Doxygen_Suppress
1019
0
void ObjectDomain::_exportToWKT(WKTFormatter *formatter) const {
1020
0
    if (d->scope_.has_value()) {
1021
0
        formatter->startNode(WKTConstants::SCOPE, false);
1022
0
        formatter->addQuotedString(*(d->scope_));
1023
0
        formatter->endNode();
1024
0
    } else if (formatter->use2019Keywords()) {
1025
0
        formatter->startNode(WKTConstants::SCOPE, false);
1026
0
        formatter->addQuotedString("unknown");
1027
0
        formatter->endNode();
1028
0
    }
1029
0
    if (d->domainOfValidity_) {
1030
0
        if (d->domainOfValidity_->description().has_value()) {
1031
0
            formatter->startNode(WKTConstants::AREA, false);
1032
0
            formatter->addQuotedString(*(d->domainOfValidity_->description()));
1033
0
            formatter->endNode();
1034
0
        }
1035
0
        if (d->domainOfValidity_->geographicElements().size() == 1) {
1036
0
            const auto bbox = dynamic_cast<const GeographicBoundingBox *>(
1037
0
                d->domainOfValidity_->geographicElements()[0].get());
1038
0
            if (bbox) {
1039
0
                formatter->startNode(WKTConstants::BBOX, false);
1040
0
                formatter->add(bbox->southBoundLatitude());
1041
0
                formatter->add(bbox->westBoundLongitude());
1042
0
                formatter->add(bbox->northBoundLatitude());
1043
0
                formatter->add(bbox->eastBoundLongitude());
1044
0
                formatter->endNode();
1045
0
            }
1046
0
        }
1047
0
        if (d->domainOfValidity_->verticalElements().size() == 1) {
1048
0
            auto extent = d->domainOfValidity_->verticalElements()[0];
1049
0
            formatter->startNode(WKTConstants::VERTICALEXTENT, false);
1050
0
            formatter->add(extent->minimumValue());
1051
0
            formatter->add(extent->maximumValue());
1052
0
            extent->unit()->_exportToWKT(formatter);
1053
0
            formatter->endNode();
1054
0
        }
1055
0
        if (d->domainOfValidity_->temporalElements().size() == 1) {
1056
0
            auto extent = d->domainOfValidity_->temporalElements()[0];
1057
0
            formatter->startNode(WKTConstants::TIMEEXTENT, false);
1058
0
            if (DateTime::create(extent->start()).isISO_8601()) {
1059
0
                formatter->add(extent->start());
1060
0
            } else {
1061
0
                formatter->addQuotedString(extent->start());
1062
0
            }
1063
0
            if (DateTime::create(extent->stop()).isISO_8601()) {
1064
0
                formatter->add(extent->stop());
1065
0
            } else {
1066
0
                formatter->addQuotedString(extent->stop());
1067
0
            }
1068
0
            formatter->endNode();
1069
0
        }
1070
0
    }
1071
0
}
1072
//! @endcond
1073
1074
// ---------------------------------------------------------------------------
1075
1076
//! @cond Doxygen_Suppress
1077
0
void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const {
1078
0
    auto writer = formatter->writer();
1079
0
    if (d->scope_.has_value()) {
1080
0
        writer->AddObjKey("scope");
1081
0
        writer->Add(*(d->scope_));
1082
0
    }
1083
0
    if (d->domainOfValidity_) {
1084
0
        if (d->domainOfValidity_->description().has_value()) {
1085
0
            writer->AddObjKey("area");
1086
0
            writer->Add(*(d->domainOfValidity_->description()));
1087
0
        }
1088
0
        if (d->domainOfValidity_->geographicElements().size() == 1) {
1089
0
            const auto bbox = dynamic_cast<const GeographicBoundingBox *>(
1090
0
                d->domainOfValidity_->geographicElements()[0].get());
1091
0
            if (bbox) {
1092
0
                writer->AddObjKey("bbox");
1093
0
                auto bboxContext(writer->MakeObjectContext());
1094
0
                writer->AddObjKey("south_latitude");
1095
0
                writer->Add(bbox->southBoundLatitude(), 15);
1096
0
                writer->AddObjKey("west_longitude");
1097
0
                writer->Add(bbox->westBoundLongitude(), 15);
1098
0
                writer->AddObjKey("north_latitude");
1099
0
                writer->Add(bbox->northBoundLatitude(), 15);
1100
0
                writer->AddObjKey("east_longitude");
1101
0
                writer->Add(bbox->eastBoundLongitude(), 15);
1102
0
            }
1103
0
        }
1104
0
        if (d->domainOfValidity_->verticalElements().size() == 1) {
1105
0
            const auto &verticalExtent =
1106
0
                d->domainOfValidity_->verticalElements().front();
1107
0
            writer->AddObjKey("vertical_extent");
1108
0
            auto bboxContext(writer->MakeObjectContext());
1109
0
            writer->AddObjKey("minimum");
1110
0
            writer->Add(verticalExtent->minimumValue(), 15);
1111
0
            writer->AddObjKey("maximum");
1112
0
            writer->Add(verticalExtent->maximumValue(), 15);
1113
0
            const auto &unit = verticalExtent->unit();
1114
0
            if (*unit != common::UnitOfMeasure::METRE) {
1115
0
                writer->AddObjKey("unit");
1116
0
                unit->_exportToJSON(formatter);
1117
0
            }
1118
0
        }
1119
0
        if (d->domainOfValidity_->temporalElements().size() == 1) {
1120
0
            const auto &temporalExtent =
1121
0
                d->domainOfValidity_->temporalElements().front();
1122
0
            writer->AddObjKey("temporal_extent");
1123
0
            auto bboxContext(writer->MakeObjectContext());
1124
0
            writer->AddObjKey("start");
1125
0
            writer->Add(temporalExtent->start());
1126
0
            writer->AddObjKey("end");
1127
0
            writer->Add(temporalExtent->stop());
1128
0
        }
1129
0
    }
1130
0
}
1131
//! @endcond
1132
1133
// ---------------------------------------------------------------------------
1134
1135
//! @cond Doxygen_Suppress
1136
bool ObjectDomain::_isEquivalentTo(
1137
    const util::IComparable *other, util::IComparable::Criterion criterion,
1138
0
    const io::DatabaseContextPtr &dbContext) const {
1139
0
    auto otherDomain = dynamic_cast<const ObjectDomain *>(other);
1140
0
    if (!otherDomain)
1141
0
        return false;
1142
0
    if (scope().has_value() != otherDomain->scope().has_value())
1143
0
        return false;
1144
0
    if (*scope() != *otherDomain->scope())
1145
0
        return false;
1146
0
    if ((domainOfValidity().get() != nullptr) ^
1147
0
        (otherDomain->domainOfValidity().get() != nullptr))
1148
0
        return false;
1149
0
    return domainOfValidity().get() == nullptr ||
1150
0
           domainOfValidity()->_isEquivalentTo(
1151
0
               otherDomain->domainOfValidity().get(), criterion, dbContext);
1152
0
}
1153
//! @endcond
1154
1155
// ---------------------------------------------------------------------------
1156
1157
//! @cond Doxygen_Suppress
1158
struct ObjectUsage::Private {
1159
    std::vector<ObjectDomainNNPtr> domains_{};
1160
};
1161
//! @endcond
1162
1163
// ---------------------------------------------------------------------------
1164
1165
55
ObjectUsage::ObjectUsage() : d(std::make_unique<Private>()) {}
1166
1167
// ---------------------------------------------------------------------------
1168
1169
ObjectUsage::ObjectUsage(const ObjectUsage &other)
1170
0
    : IdentifiedObject(other), d(std::make_unique<Private>(*(other.d))) {}
1171
1172
// ---------------------------------------------------------------------------
1173
1174
//! @cond Doxygen_Suppress
1175
0
ObjectUsage::~ObjectUsage() = default;
1176
//! @endcond
1177
1178
// ---------------------------------------------------------------------------
1179
1180
/** \brief Return the domains of the object.
1181
 */
1182
0
const std::vector<ObjectDomainNNPtr> &ObjectUsage::domains() PROJ_PURE_DEFN {
1183
0
    return d->domains_;
1184
0
}
1185
1186
// ---------------------------------------------------------------------------
1187
1188
void ObjectUsage::setProperties(
1189
    const PropertyMap &properties) // throw(InvalidValueTypeException)
1190
55
{
1191
55
    IdentifiedObject::setProperties(properties);
1192
1193
55
    optional<std::string> scope;
1194
55
    properties.getStringValue(SCOPE_KEY, scope);
1195
1196
55
    ExtentPtr domainOfValidity;
1197
55
    {
1198
55
        const auto pVal = properties.get(DOMAIN_OF_VALIDITY_KEY);
1199
55
        if (pVal) {
1200
0
            domainOfValidity = util::nn_dynamic_pointer_cast<Extent>(*pVal);
1201
0
            if (!domainOfValidity) {
1202
0
                throw InvalidValueTypeException("Invalid value type for " +
1203
0
                                                DOMAIN_OF_VALIDITY_KEY);
1204
0
            }
1205
0
        }
1206
55
    }
1207
1208
55
    if (scope.has_value() || domainOfValidity) {
1209
0
        d->domains_.emplace_back(ObjectDomain::create(scope, domainOfValidity));
1210
0
    }
1211
1212
55
    {
1213
55
        const auto pVal = properties.get(OBJECT_DOMAIN_KEY);
1214
55
        if (pVal) {
1215
0
            if (auto objectDomain =
1216
0
                    util::nn_dynamic_pointer_cast<ObjectDomain>(*pVal)) {
1217
0
                d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
1218
0
            } else if (const auto array =
1219
0
                           dynamic_cast<const ArrayOfBaseObject *>(
1220
0
                               pVal->get())) {
1221
0
                for (const auto &val : *array) {
1222
0
                    objectDomain =
1223
0
                        util::nn_dynamic_pointer_cast<ObjectDomain>(val);
1224
0
                    if (objectDomain) {
1225
0
                        d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
1226
0
                    } else {
1227
0
                        throw InvalidValueTypeException(
1228
0
                            "Invalid value type for " + OBJECT_DOMAIN_KEY);
1229
0
                    }
1230
0
                }
1231
0
            } else {
1232
0
                throw InvalidValueTypeException("Invalid value type for " +
1233
0
                                                OBJECT_DOMAIN_KEY);
1234
0
            }
1235
0
        }
1236
55
    }
1237
55
}
1238
1239
// ---------------------------------------------------------------------------
1240
1241
0
void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const {
1242
0
    const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
1243
0
    if (isWKT2 && formatter->outputUsage()) {
1244
0
        auto l_domains = domains();
1245
0
        if (!l_domains.empty()) {
1246
0
            if (formatter->use2019Keywords()) {
1247
0
                for (const auto &domain : l_domains) {
1248
0
                    formatter->startNode(WKTConstants::USAGE, false);
1249
0
                    domain->_exportToWKT(formatter);
1250
0
                    formatter->endNode();
1251
0
                }
1252
0
            } else {
1253
0
                l_domains[0]->_exportToWKT(formatter);
1254
0
            }
1255
0
        }
1256
0
    }
1257
0
    if (formatter->outputId()) {
1258
0
        formatID(formatter);
1259
0
    }
1260
0
    if (isWKT2 && formatter->outputUsage()) {
1261
0
        formatRemarks(formatter);
1262
0
    }
1263
0
}
1264
1265
// ---------------------------------------------------------------------------
1266
1267
0
void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const {
1268
1269
0
    auto writer = formatter->writer();
1270
0
    if (formatter->outputUsage()) {
1271
0
        const auto &l_domains = domains();
1272
0
        if (l_domains.size() == 1) {
1273
0
            l_domains[0]->_exportToJSON(formatter);
1274
0
        } else if (!l_domains.empty()) {
1275
0
            writer->AddObjKey("usages");
1276
0
            auto arrayContext(writer->MakeArrayContext(false));
1277
0
            for (const auto &domain : l_domains) {
1278
0
                auto objContext(writer->MakeObjectContext());
1279
0
                domain->_exportToJSON(formatter);
1280
0
            }
1281
0
        }
1282
0
    }
1283
1284
0
    if (formatter->outputId()) {
1285
0
        formatID(formatter);
1286
0
    }
1287
0
    formatRemarks(formatter);
1288
0
}
1289
1290
// ---------------------------------------------------------------------------
1291
1292
//! @cond Doxygen_Suppress
1293
bool ObjectUsage::_isEquivalentTo(
1294
    const util::IComparable *other, util::IComparable::Criterion criterion,
1295
0
    const io::DatabaseContextPtr &dbContext) const {
1296
0
    auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other);
1297
0
    if (!otherObjUsage)
1298
0
        return false;
1299
1300
    // TODO: incomplete
1301
0
    return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
1302
0
}
1303
//! @endcond
1304
1305
// ---------------------------------------------------------------------------
1306
1307
//! @cond Doxygen_Suppress
1308
struct DataEpoch::Private {
1309
    Measure coordinateEpoch_{};
1310
1311
    explicit Private(const Measure &coordinateEpochIn)
1312
0
        : coordinateEpoch_(coordinateEpochIn) {}
1313
};
1314
//! @endcond
1315
1316
// ---------------------------------------------------------------------------
1317
1318
0
DataEpoch::DataEpoch() : d(std::make_unique<Private>(Measure())) {}
1319
1320
// ---------------------------------------------------------------------------
1321
1322
DataEpoch::DataEpoch(const Measure &coordinateEpochIn)
1323
0
    : d(std::make_unique<Private>(coordinateEpochIn)) {}
1324
1325
// ---------------------------------------------------------------------------
1326
1327
DataEpoch::DataEpoch(const DataEpoch &other)
1328
0
    : d(std::make_unique<Private>(*(other.d))) {}
1329
1330
// ---------------------------------------------------------------------------
1331
1332
//! @cond Doxygen_Suppress
1333
0
DataEpoch::~DataEpoch() = default;
1334
//! @endcond
1335
1336
// ---------------------------------------------------------------------------
1337
1338
/** \brief Return the coordinate epoch, as a measure in decimal year.
1339
 */
1340
0
const Measure &DataEpoch::coordinateEpoch() const {
1341
0
    return d->coordinateEpoch_;
1342
0
}
1343
1344
} // namespace common
1345
NS_PROJ_END