Coverage Report

Created: 2026-10-13 07:22

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
3.35M
        : name_(nameIn), toSI_(toSIIn), type_(typeIn), codeSpace_(codeSpaceIn),
83
3.35M
          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
3.35M
    : d(std::make_unique<Private>(nameIn, toSIIn, typeIn, codeSpaceIn,
95
3.35M
                                  codeIn)) {}
96
97
// ---------------------------------------------------------------------------
98
99
UnitOfMeasure::UnitOfMeasure(const UnitOfMeasure &other)
100
12.3M
    : d(std::make_unique<Private>(*(other.d))) {}
101
102
// ---------------------------------------------------------------------------
103
104
//! @cond Doxygen_Suppress
105
15.7M
UnitOfMeasure::~UnitOfMeasure() = default;
106
//! @endcond
107
108
// ---------------------------------------------------------------------------
109
110
//! @cond Doxygen_Suppress
111
340k
UnitOfMeasure &UnitOfMeasure::operator=(const UnitOfMeasure &other) {
112
340k
    if (this != &other) {
113
340k
        *d = *(other.d);
114
340k
    }
115
340k
    return *this;
116
340k
}
117
//! @endcond
118
119
// ---------------------------------------------------------------------------
120
121
//! @cond Doxygen_Suppress
122
3.76k
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
994k
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
32.7M
double UnitOfMeasure::conversionToSI() PROJ_PURE_DEFN { return d->toSI_; }
148
149
// ---------------------------------------------------------------------------
150
151
/** \brief Return the type of the unit of measure.
152
 */
153
1.55M
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
4.03k
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
440k
bool UnitOfMeasure::operator==(const UnitOfMeasure &other) PROJ_PURE_DEFN {
306
440k
    return name() == other.name();
307
440k
}
308
309
// ---------------------------------------------------------------------------
310
311
/** Returns whether two units of measures are different.
312
 *
313
 * The comparison is based on the name.
314
 */
315
54.5k
bool UnitOfMeasure::operator!=(const UnitOfMeasure &other) PROJ_PURE_DEFN {
316
54.5k
    return name() != other.name();
317
54.5k
}
318
319
// ---------------------------------------------------------------------------
320
321
//! @cond Doxygen_Suppress
322
1.50M
std::string UnitOfMeasure::exportToPROJString() const {
323
1.50M
    if (type() == Type::LINEAR) {
324
139k
        auto proj_units = pj_list_linear_units();
325
820k
        for (int i = 0; proj_units[i].id != nullptr; i++) {
326
800k
            if (::fabs(proj_units[i].factor - conversionToSI()) <
327
800k
                1e-10 * conversionToSI()) {
328
120k
                return proj_units[i].id;
329
120k
            }
330
800k
        }
331
1.36M
    } else if (type() == Type::ANGULAR) {
332
1.36M
        auto proj_angular_units = pj_list_angular_units();
333
2.72M
        for (int i = 0; proj_angular_units[i].id != nullptr; i++) {
334
2.72M
            if (::fabs(proj_angular_units[i].factor - conversionToSI()) <
335
2.72M
                1e-10 * conversionToSI()) {
336
1.36M
                return proj_angular_units[i].id;
337
1.36M
            }
338
2.72M
        }
339
1.36M
    }
340
20.4k
    return std::string();
341
1.50M
}
342
//! @endcond
343
344
// ---------------------------------------------------------------------------
345
346
//! @cond Doxygen_Suppress
347
bool UnitOfMeasure::_isEquivalentTo(
348
2.08M
    const UnitOfMeasure &other, util::IComparable::Criterion criterion) const {
349
2.08M
    if (criterion == util::IComparable::Criterion::STRICT) {
350
22
        return operator==(other);
351
22
    }
352
2.08M
    return std::fabs(conversionToSI() - other.conversionToSI()) <=
353
2.08M
           1e-10 * std::fabs(conversionToSI());
354
2.08M
}
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
7.40M
        : 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
7.40M
    : d(std::make_unique<Private>(valueIn, unitIn)) {}
375
376
// ---------------------------------------------------------------------------
377
378
Measure::Measure(const Measure &other)
379
4.61M
    : d(std::make_unique<Private>(*(other.d))) {}
380
381
// ---------------------------------------------------------------------------
382
383
//! @cond Doxygen_Suppress
384
12.0M
Measure::~Measure() = default;
385
//! @endcond
386
387
// ---------------------------------------------------------------------------
388
389
/** \brief Return the unit of the Measure.
390
 */
391
3.60M
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
24.2M
double Measure::getSIValue() PROJ_PURE_DEFN {
400
24.2M
    return d->value_ * d->unit_.conversionToSI();
401
24.2M
}
402
403
// ---------------------------------------------------------------------------
404
405
/** \brief Return the value of the measure, expressed in the unit()
406
 */
407
3.59M
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
664k
double Measure::convertToUnit(const UnitOfMeasure &otherUnit) PROJ_PURE_DEFN {
414
664k
    return getSIValue() / otherUnit.conversionToSI();
415
664k
}
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
457k
bool Measure::operator==(const Measure &other) PROJ_PURE_DEFN {
424
457k
    return d->value_ == other.d->value_ && d->unit_ == other.d->unit_;
425
457k
}
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
6.23M
                              double maxRelativeError) const {
438
6.23M
    if (criterion == util::IComparable::Criterion::STRICT) {
439
141k
        return operator==(other);
440
141k
    }
441
6.09M
    const double SIValue = getSIValue();
442
6.09M
    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
6.09M
    if (std::isinf(SIValue) && std::isinf(otherSIValue))
446
0
        return SIValue * otherSIValue > 0;
447
6.09M
    return std::fabs(SIValue - otherSIValue) <=
448
6.09M
           maxRelativeError * std::fabs(SIValue);
449
6.09M
}
450
451
// ---------------------------------------------------------------------------
452
453
/** \brief Instantiate a Scale.
454
 *
455
 * @param valueIn value
456
 */
457
23.5k
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
7.90k
    : Measure(valueIn, unitIn) {}
468
469
// ---------------------------------------------------------------------------
470
471
//! @cond Doxygen_Suppress
472
16.8k
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
391k
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
143k
    : Measure(valueIn, unitIn) {}
498
499
// ---------------------------------------------------------------------------
500
501
//! @cond Doxygen_Suppress
502
29.5k
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
354k
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
3.42M
    : Measure(valueIn, unitIn) {}
528
529
// ---------------------------------------------------------------------------
530
531
//! @cond Doxygen_Suppress
532
856k
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
640k
    explicit Private(const std::string &str) : str_(str) {}
548
};
549
//! @endcond
550
551
// ---------------------------------------------------------------------------
552
553
591k
DateTime::DateTime() : d(std::make_unique<Private>(std::string())) {}
554
555
// ---------------------------------------------------------------------------
556
557
DateTime::DateTime(const std::string &str)
558
48.8k
    : d(std::make_unique<Private>(str)) {}
559
560
// ---------------------------------------------------------------------------
561
562
//! @cond Doxygen_Suppress
563
DateTime::DateTime(const DateTime &other)
564
17
    : d(std::make_unique<Private>(*(other.d))) {}
565
//! @endcond
566
567
// ---------------------------------------------------------------------------
568
569
//! @cond Doxygen_Suppress
570
48.8k
DateTime &DateTime::operator=(const DateTime &other) {
571
48.8k
    d->str_ = other.d->str_;
572
48.8k
    return *this;
573
48.8k
}
574
//! @endcond
575
576
// ---------------------------------------------------------------------------
577
578
//! @cond Doxygen_Suppress
579
640k
DateTime::~DateTime() = default;
580
//! @endcond
581
582
// ---------------------------------------------------------------------------
583
584
/** \brief Instantiate a DateTime. */
585
48.8k
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
14.9k
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
4.40M
IdentifiedObject::IdentifiedObject() : d(std::make_unique<Private>()) {}
625
626
// ---------------------------------------------------------------------------
627
628
IdentifiedObject::IdentifiedObject(const IdentifiedObject &other)
629
271k
    : d(std::make_unique<Private>(*(other.d))) {}
630
631
// ---------------------------------------------------------------------------
632
633
//! @cond Doxygen_Suppress
634
4.67M
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
21.3k
const IdentifierNNPtr &IdentifiedObject::name() PROJ_PURE_DEFN {
645
21.3k
    return d->name;
646
21.3k
}
647
648
// ---------------------------------------------------------------------------
649
650
/** \brief Return the name of the object.
651
 *
652
 * Return *(name()->description())
653
 */
654
27.6M
const std::string &IdentifiedObject::nameStr() PROJ_PURE_DEFN {
655
27.6M
    return *(d->name->description());
656
27.6M
}
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
17.4M
IdentifiedObject::identifiers() PROJ_PURE_DEFN {
667
17.4M
    return d->identifiers;
668
17.4M
}
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
10.3M
int IdentifiedObject::getEPSGCode() PROJ_PURE_DEFN {
697
10.3M
    for (const auto &id : identifiers()) {
698
8.17M
        if (ci_equal(*(id->codeSpace()), metadata::Identifier::EPSG)) {
699
8.12M
            return ::atoi(id->code().c_str());
700
8.12M
        }
701
8.17M
    }
702
2.24M
    return 0;
703
10.3M
}
704
705
// ---------------------------------------------------------------------------
706
707
/** \brief Return the remarks.
708
 */
709
607k
const std::string &IdentifiedObject::remarks() PROJ_PURE_DEFN {
710
607k
    return d->remarks;
711
607k
}
712
713
// ---------------------------------------------------------------------------
714
715
/** \brief Return whether the object is deprecated.
716
 *
717
 * \remark Extension of \ref ISO_19111_2019
718
 */
719
509k
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
4.40M
{
727
4.40M
    const auto pVal = properties.get(NAME_KEY);
728
4.40M
    if (!pVal) {
729
184k
        return;
730
184k
    }
731
4.22M
    if (const auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) {
732
4.22M
        if (genVal->type() == BoxedValue::Type::STRING) {
733
4.22M
            name = Identifier::createFromDescription(genVal->stringValue());
734
4.22M
        } else {
735
0
            throw InvalidValueTypeException("Invalid value type for " +
736
0
                                            NAME_KEY);
737
0
        }
738
4.22M
    } 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
4.22M
}
748
749
// ---------------------------------------------------------------------------
750
751
void IdentifiedObject::Private::setIdentifiers(
752
    const PropertyMap &properties) // throw(InvalidValueTypeException)
753
4.40M
{
754
4.40M
    auto pVal = properties.get(IDENTIFIERS_KEY);
755
4.40M
    if (!pVal) {
756
757
4.11M
        pVal = properties.get(Identifier::CODE_KEY);
758
4.11M
        if (pVal) {
759
2.46M
            identifiers.clear();
760
2.46M
            identifiers.push_back(
761
2.46M
                Identifier::create(std::string(), properties));
762
2.46M
        }
763
4.11M
        return;
764
4.11M
    }
765
296k
    if (auto identifier = util::nn_dynamic_pointer_cast<Identifier>(*pVal)) {
766
0
        identifiers.clear();
767
0
        identifiers.push_back(NN_NO_CHECK(identifier));
768
296k
    } else {
769
296k
        if (auto array = dynamic_cast<const ArrayOfBaseObject *>(pVal->get())) {
770
296k
            identifiers.clear();
771
297k
            for (const auto &val : *array) {
772
297k
                identifier = util::nn_dynamic_pointer_cast<Identifier>(val);
773
297k
                if (identifier) {
774
297k
                    identifiers.push_back(NN_NO_CHECK(identifier));
775
297k
                } else {
776
0
                    throw InvalidValueTypeException("Invalid value type for " +
777
0
                                                    IDENTIFIERS_KEY);
778
0
                }
779
297k
            }
780
296k
        } else {
781
0
            throw InvalidValueTypeException("Invalid value type for " +
782
0
                                            IDENTIFIERS_KEY);
783
0
        }
784
296k
    }
785
296k
}
786
787
// ---------------------------------------------------------------------------
788
789
void IdentifiedObject::Private::setAliases(
790
    const PropertyMap &properties) // throw(InvalidValueTypeException)
791
4.40M
{
792
4.40M
    const auto pVal = properties.get(ALIAS_KEY);
793
4.40M
    if (!pVal) {
794
4.40M
        return;
795
4.40M
    }
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
4.40M
{
842
4.40M
    d->setName(properties);
843
4.40M
    d->setIdentifiers(properties);
844
4.40M
    d->setAliases(properties);
845
846
4.40M
    properties.getStringValue(REMARKS_KEY, d->remarks);
847
848
4.40M
    {
849
4.40M
        const auto pVal = properties.get(DEPRECATED_KEY);
850
4.40M
        if (pVal) {
851
354
            if (const auto genVal =
852
354
                    dynamic_cast<const BoxedValue *>(pVal->get())) {
853
354
                if (genVal->type() == BoxedValue::Type::BOOLEAN) {
854
354
                    d->isDeprecated = genVal->booleanValue();
855
354
                } else {
856
0
                    throw InvalidValueTypeException("Invalid value type for " +
857
0
                                                    DEPRECATED_KEY);
858
0
                }
859
354
            } else {
860
0
                throw InvalidValueTypeException("Invalid value type for " +
861
0
                                                DEPRECATED_KEY);
862
0
            }
863
354
        }
864
4.40M
    }
865
4.40M
}
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
4.51M
    const io::DatabaseContextPtr &dbContext) const {
922
4.51M
    auto otherIdObj = dynamic_cast<const IdentifiedObject *>(other);
923
4.51M
    if (!otherIdObj)
924
0
        return false;
925
4.51M
    return _isEquivalentTo(otherIdObj, criterion, dbContext);
926
4.51M
}
927
928
// ---------------------------------------------------------------------------
929
930
bool IdentifiedObject::_isEquivalentTo(
931
    const IdentifiedObject *otherIdObj, util::IComparable::Criterion criterion,
932
4.51M
    const io::DatabaseContextPtr &dbContext) PROJ_PURE_DEFN {
933
4.51M
    if (criterion == util::IComparable::Criterion::STRICT) {
934
147k
        if (!ci_equal(nameStr(), otherIdObj->nameStr())) {
935
30.4k
            return false;
936
30.4k
        }
937
        // TODO test id etc
938
4.36M
    } else {
939
4.36M
        const auto &l_name = nameStr();
940
4.36M
        const auto &otherName = otherIdObj->nameStr();
941
4.36M
        if (!metadata::Identifier::isEquivalentName(l_name.c_str(),
942
4.36M
                                                    otherName.c_str()) &&
943
383k
            !hasEquivalentNameToUsingAlias(otherIdObj, dbContext)) {
944
378k
            return (l_name == "unknown" && otherName == "unnamed") ||
945
378k
                   (l_name == "unnamed" && otherName == "unknown");
946
378k
        }
947
4.36M
    }
948
4.10M
    return true;
949
4.51M
}
950
951
// ---------------------------------------------------------------------------
952
953
bool IdentifiedObject::hasEquivalentNameToUsingAlias(
954
199k
    const IdentifiedObject *, const io::DatabaseContextPtr &) const {
955
199k
    return false;
956
199k
}
957
958
//! @endcond
959
960
// ---------------------------------------------------------------------------
961
962
//! @cond Doxygen_Suppress
963
struct ObjectDomain::Private {
964
    optional<std::string> scope_{};
965
    ExtentPtr domainOfValidity_{};
966
967
    Private(const optional<std::string> &scopeIn, const ExtentPtr &extent)
968
536k
        : scope_(scopeIn), domainOfValidity_(extent) {}
969
};
970
//! @endcond
971
972
// ---------------------------------------------------------------------------
973
974
//! @cond Doxygen_Suppress
975
ObjectDomain::ObjectDomain(const optional<std::string> &scopeIn,
976
                           const ExtentPtr &extent)
977
536k
    : d(std::make_unique<Private>(scopeIn, extent)) {}
978
//! @endcond
979
980
// ---------------------------------------------------------------------------
981
982
ObjectDomain::ObjectDomain(const ObjectDomain &other)
983
0
    : d(std::make_unique<Private>(*(other.d))) {}
984
985
// ---------------------------------------------------------------------------
986
987
//! @cond Doxygen_Suppress
988
536k
ObjectDomain::~ObjectDomain() = default;
989
//! @endcond
990
991
// ---------------------------------------------------------------------------
992
993
/** \brief Return the scope.
994
 *
995
 * @return the scope, or empty.
996
 */
997
0
const optional<std::string> &ObjectDomain::scope() PROJ_PURE_DEFN {
998
0
    return d->scope_;
999
0
}
1000
1001
// ---------------------------------------------------------------------------
1002
1003
/** \brief Return the domain of validity.
1004
 *
1005
 * @return the domain of validity, or nullptr.
1006
 */
1007
490k
const ExtentPtr &ObjectDomain::domainOfValidity() PROJ_PURE_DEFN {
1008
490k
    return d->domainOfValidity_;
1009
490k
}
1010
1011
// ---------------------------------------------------------------------------
1012
1013
/** \brief Instantiate a ObjectDomain.
1014
 */
1015
ObjectDomainNNPtr ObjectDomain::create(const optional<std::string> &scopeIn,
1016
536k
                                       const ExtentPtr &extent) {
1017
536k
    return ObjectDomain::nn_make_shared<ObjectDomain>(scopeIn, extent);
1018
536k
}
1019
1020
// ---------------------------------------------------------------------------
1021
1022
//! @cond Doxygen_Suppress
1023
0
void ObjectDomain::_exportToWKT(WKTFormatter *formatter) const {
1024
0
    if (d->scope_.has_value()) {
1025
0
        formatter->startNode(WKTConstants::SCOPE, false);
1026
0
        formatter->addQuotedString(*(d->scope_));
1027
0
        formatter->endNode();
1028
0
    } else if (formatter->use2019Keywords()) {
1029
0
        formatter->startNode(WKTConstants::SCOPE, false);
1030
0
        formatter->addQuotedString("unknown");
1031
0
        formatter->endNode();
1032
0
    }
1033
0
    if (d->domainOfValidity_) {
1034
0
        if (d->domainOfValidity_->description().has_value()) {
1035
0
            formatter->startNode(WKTConstants::AREA, false);
1036
0
            formatter->addQuotedString(*(d->domainOfValidity_->description()));
1037
0
            formatter->endNode();
1038
0
        }
1039
0
        if (d->domainOfValidity_->geographicElements().size() == 1) {
1040
0
            const auto bbox = dynamic_cast<const GeographicBoundingBox *>(
1041
0
                d->domainOfValidity_->geographicElements()[0].get());
1042
0
            if (bbox) {
1043
0
                formatter->startNode(WKTConstants::BBOX, false);
1044
0
                formatter->add(bbox->southBoundLatitude());
1045
0
                formatter->add(bbox->westBoundLongitude());
1046
0
                formatter->add(bbox->northBoundLatitude());
1047
0
                formatter->add(bbox->eastBoundLongitude());
1048
0
                formatter->endNode();
1049
0
            }
1050
0
        }
1051
0
        if (d->domainOfValidity_->verticalElements().size() == 1) {
1052
0
            auto extent = d->domainOfValidity_->verticalElements()[0];
1053
0
            formatter->startNode(WKTConstants::VERTICALEXTENT, false);
1054
0
            formatter->add(extent->minimumValue());
1055
0
            formatter->add(extent->maximumValue());
1056
0
            extent->unit()->_exportToWKT(formatter);
1057
0
            formatter->endNode();
1058
0
        }
1059
0
        if (d->domainOfValidity_->temporalElements().size() == 1) {
1060
0
            auto extent = d->domainOfValidity_->temporalElements()[0];
1061
0
            formatter->startNode(WKTConstants::TIMEEXTENT, false);
1062
0
            if (DateTime::create(extent->start()).isISO_8601()) {
1063
0
                formatter->add(extent->start());
1064
0
            } else {
1065
0
                formatter->addQuotedString(extent->start());
1066
0
            }
1067
0
            if (DateTime::create(extent->stop()).isISO_8601()) {
1068
0
                formatter->add(extent->stop());
1069
0
            } else {
1070
0
                formatter->addQuotedString(extent->stop());
1071
0
            }
1072
0
            formatter->endNode();
1073
0
        }
1074
0
    }
1075
0
}
1076
//! @endcond
1077
1078
// ---------------------------------------------------------------------------
1079
1080
//! @cond Doxygen_Suppress
1081
0
void ObjectDomain::_exportToJSON(JSONFormatter *formatter) const {
1082
0
    auto writer = formatter->writer();
1083
0
    if (d->scope_.has_value()) {
1084
0
        writer->AddObjKey("scope");
1085
0
        writer->Add(*(d->scope_));
1086
0
    }
1087
0
    if (d->domainOfValidity_) {
1088
0
        if (d->domainOfValidity_->description().has_value()) {
1089
0
            writer->AddObjKey("area");
1090
0
            writer->Add(*(d->domainOfValidity_->description()));
1091
0
        }
1092
0
        if (d->domainOfValidity_->geographicElements().size() == 1) {
1093
0
            const auto bbox = dynamic_cast<const GeographicBoundingBox *>(
1094
0
                d->domainOfValidity_->geographicElements()[0].get());
1095
0
            if (bbox) {
1096
0
                writer->AddObjKey("bbox");
1097
0
                auto bboxContext(writer->MakeObjectContext());
1098
0
                writer->AddObjKey("south_latitude");
1099
0
                writer->Add(bbox->southBoundLatitude(), 15);
1100
0
                writer->AddObjKey("west_longitude");
1101
0
                writer->Add(bbox->westBoundLongitude(), 15);
1102
0
                writer->AddObjKey("north_latitude");
1103
0
                writer->Add(bbox->northBoundLatitude(), 15);
1104
0
                writer->AddObjKey("east_longitude");
1105
0
                writer->Add(bbox->eastBoundLongitude(), 15);
1106
0
            }
1107
0
        }
1108
0
        if (d->domainOfValidity_->verticalElements().size() == 1) {
1109
0
            const auto &verticalExtent =
1110
0
                d->domainOfValidity_->verticalElements().front();
1111
0
            writer->AddObjKey("vertical_extent");
1112
0
            auto bboxContext(writer->MakeObjectContext());
1113
0
            writer->AddObjKey("minimum");
1114
0
            writer->Add(verticalExtent->minimumValue(), 15);
1115
0
            writer->AddObjKey("maximum");
1116
0
            writer->Add(verticalExtent->maximumValue(), 15);
1117
0
            const auto &unit = verticalExtent->unit();
1118
0
            if (*unit != common::UnitOfMeasure::METRE) {
1119
0
                writer->AddObjKey("unit");
1120
0
                unit->_exportToJSON(formatter);
1121
0
            }
1122
0
        }
1123
0
        if (d->domainOfValidity_->temporalElements().size() == 1) {
1124
0
            const auto &temporalExtent =
1125
0
                d->domainOfValidity_->temporalElements().front();
1126
0
            writer->AddObjKey("temporal_extent");
1127
0
            auto bboxContext(writer->MakeObjectContext());
1128
0
            writer->AddObjKey("start");
1129
0
            writer->Add(temporalExtent->start());
1130
0
            writer->AddObjKey("end");
1131
0
            writer->Add(temporalExtent->stop());
1132
0
        }
1133
0
    }
1134
0
}
1135
//! @endcond
1136
1137
// ---------------------------------------------------------------------------
1138
1139
//! @cond Doxygen_Suppress
1140
bool ObjectDomain::_isEquivalentTo(
1141
    const util::IComparable *other, util::IComparable::Criterion criterion,
1142
0
    const io::DatabaseContextPtr &dbContext) const {
1143
0
    auto otherDomain = dynamic_cast<const ObjectDomain *>(other);
1144
0
    if (!otherDomain)
1145
0
        return false;
1146
0
    if (scope().has_value() != otherDomain->scope().has_value())
1147
0
        return false;
1148
0
    if (*scope() != *otherDomain->scope())
1149
0
        return false;
1150
0
    if ((domainOfValidity().get() != nullptr) ^
1151
0
        (otherDomain->domainOfValidity().get() != nullptr))
1152
0
        return false;
1153
0
    return domainOfValidity().get() == nullptr ||
1154
0
           domainOfValidity()->_isEquivalentTo(
1155
0
               otherDomain->domainOfValidity().get(), criterion, dbContext);
1156
0
}
1157
//! @endcond
1158
1159
// ---------------------------------------------------------------------------
1160
1161
//! @cond Doxygen_Suppress
1162
struct ObjectUsage::Private {
1163
    std::vector<ObjectDomainNNPtr> domains_{};
1164
};
1165
//! @endcond
1166
1167
// ---------------------------------------------------------------------------
1168
1169
1.95M
ObjectUsage::ObjectUsage() : d(std::make_unique<Private>()) {}
1170
1171
// ---------------------------------------------------------------------------
1172
1173
ObjectUsage::ObjectUsage(const ObjectUsage &other)
1174
270k
    : IdentifiedObject(other), d(std::make_unique<Private>(*(other.d))) {}
1175
1176
// ---------------------------------------------------------------------------
1177
1178
//! @cond Doxygen_Suppress
1179
2.22M
ObjectUsage::~ObjectUsage() = default;
1180
//! @endcond
1181
1182
// ---------------------------------------------------------------------------
1183
1184
/** \brief Return the domains of the object.
1185
 */
1186
1.72M
const std::vector<ObjectDomainNNPtr> &ObjectUsage::domains() PROJ_PURE_DEFN {
1187
1.72M
    return d->domains_;
1188
1.72M
}
1189
1190
// ---------------------------------------------------------------------------
1191
1192
void ObjectUsage::setProperties(
1193
    const PropertyMap &properties) // throw(InvalidValueTypeException)
1194
1.97M
{
1195
1.97M
    IdentifiedObject::setProperties(properties);
1196
1197
1.97M
    optional<std::string> scope;
1198
1.97M
    properties.getStringValue(SCOPE_KEY, scope);
1199
1200
1.97M
    ExtentPtr domainOfValidity;
1201
1.97M
    {
1202
1.97M
        const auto pVal = properties.get(DOMAIN_OF_VALIDITY_KEY);
1203
1.97M
        if (pVal) {
1204
225k
            domainOfValidity = util::nn_dynamic_pointer_cast<Extent>(*pVal);
1205
225k
            if (!domainOfValidity) {
1206
0
                throw InvalidValueTypeException("Invalid value type for " +
1207
0
                                                DOMAIN_OF_VALIDITY_KEY);
1208
0
            }
1209
225k
        }
1210
1.97M
    }
1211
1212
1.97M
    if (scope.has_value() || domainOfValidity) {
1213
225k
        d->domains_.emplace_back(ObjectDomain::create(scope, domainOfValidity));
1214
225k
    }
1215
1216
1.97M
    {
1217
1.97M
        const auto pVal = properties.get(OBJECT_DOMAIN_KEY);
1218
1.97M
        if (pVal) {
1219
1.18M
            if (auto objectDomain =
1220
1.18M
                    util::nn_dynamic_pointer_cast<ObjectDomain>(*pVal)) {
1221
344
                d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
1222
1.18M
            } else if (const auto array =
1223
1.18M
                           dynamic_cast<const ArrayOfBaseObject *>(
1224
1.18M
                               pVal->get())) {
1225
1.18M
                for (const auto &val : *array) {
1226
1.18M
                    objectDomain =
1227
1.18M
                        util::nn_dynamic_pointer_cast<ObjectDomain>(val);
1228
1.18M
                    if (objectDomain) {
1229
1.18M
                        d->domains_.emplace_back(NN_NO_CHECK(objectDomain));
1230
1.18M
                    } else {
1231
0
                        throw InvalidValueTypeException(
1232
0
                            "Invalid value type for " + OBJECT_DOMAIN_KEY);
1233
0
                    }
1234
1.18M
                }
1235
1.18M
            } else {
1236
0
                throw InvalidValueTypeException("Invalid value type for " +
1237
0
                                                OBJECT_DOMAIN_KEY);
1238
0
            }
1239
1.18M
        }
1240
1.97M
    }
1241
1.97M
}
1242
1243
// ---------------------------------------------------------------------------
1244
1245
0
void ObjectUsage::baseExportToWKT(WKTFormatter *formatter) const {
1246
0
    const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2;
1247
0
    if (isWKT2 && formatter->outputUsage()) {
1248
0
        auto l_domains = domains();
1249
0
        if (!l_domains.empty()) {
1250
0
            if (formatter->use2019Keywords()) {
1251
0
                for (const auto &domain : l_domains) {
1252
0
                    formatter->startNode(WKTConstants::USAGE, false);
1253
0
                    domain->_exportToWKT(formatter);
1254
0
                    formatter->endNode();
1255
0
                }
1256
0
            } else {
1257
0
                l_domains[0]->_exportToWKT(formatter);
1258
0
            }
1259
0
        }
1260
0
    }
1261
0
    if (formatter->outputId()) {
1262
0
        formatID(formatter);
1263
0
    }
1264
0
    if (isWKT2 && formatter->outputUsage()) {
1265
0
        formatRemarks(formatter);
1266
0
    }
1267
0
}
1268
1269
// ---------------------------------------------------------------------------
1270
1271
0
void ObjectUsage::baseExportToJSON(JSONFormatter *formatter) const {
1272
1273
0
    auto writer = formatter->writer();
1274
0
    if (formatter->outputUsage()) {
1275
0
        const auto &l_domains = domains();
1276
0
        if (l_domains.size() == 1) {
1277
0
            l_domains[0]->_exportToJSON(formatter);
1278
0
        } else if (!l_domains.empty()) {
1279
0
            writer->AddObjKey("usages");
1280
0
            auto arrayContext(writer->MakeArrayContext(false));
1281
0
            for (const auto &domain : l_domains) {
1282
0
                auto objContext(writer->MakeObjectContext());
1283
0
                domain->_exportToJSON(formatter);
1284
0
            }
1285
0
        }
1286
0
    }
1287
1288
0
    if (formatter->outputId()) {
1289
0
        formatID(formatter);
1290
0
    }
1291
0
    formatRemarks(formatter);
1292
0
}
1293
1294
// ---------------------------------------------------------------------------
1295
1296
//! @cond Doxygen_Suppress
1297
bool ObjectUsage::_isEquivalentTo(
1298
    const util::IComparable *other, util::IComparable::Criterion criterion,
1299
1.86M
    const io::DatabaseContextPtr &dbContext) const {
1300
1.86M
    auto otherObjUsage = dynamic_cast<const ObjectUsage *>(other);
1301
1.86M
    if (!otherObjUsage)
1302
0
        return false;
1303
1304
    // TODO: incomplete
1305
1.86M
    return IdentifiedObject::_isEquivalentTo(other, criterion, dbContext);
1306
1.86M
}
1307
//! @endcond
1308
1309
// ---------------------------------------------------------------------------
1310
1311
//! @cond Doxygen_Suppress
1312
struct DataEpoch::Private {
1313
    Measure coordinateEpoch_{};
1314
1315
    explicit Private(const Measure &coordinateEpochIn)
1316
2.35M
        : coordinateEpoch_(coordinateEpochIn) {}
1317
};
1318
//! @endcond
1319
1320
// ---------------------------------------------------------------------------
1321
1322
2.35M
DataEpoch::DataEpoch() : d(std::make_unique<Private>(Measure())) {}
1323
1324
// ---------------------------------------------------------------------------
1325
1326
DataEpoch::DataEpoch(const Measure &coordinateEpochIn)
1327
92
    : d(std::make_unique<Private>(coordinateEpochIn)) {}
1328
1329
// ---------------------------------------------------------------------------
1330
1331
DataEpoch::DataEpoch(const DataEpoch &other)
1332
393k
    : d(std::make_unique<Private>(*(other.d))) {}
1333
1334
// ---------------------------------------------------------------------------
1335
1336
//! @cond Doxygen_Suppress
1337
2.75M
DataEpoch::~DataEpoch() = default;
1338
//! @endcond
1339
1340
// ---------------------------------------------------------------------------
1341
1342
/** \brief Return the coordinate epoch, as a measure in decimal year.
1343
 */
1344
516
const Measure &DataEpoch::coordinateEpoch() const {
1345
516
    return d->coordinateEpoch_;
1346
516
}
1347
1348
} // namespace common
1349
NS_PROJ_END