Coverage Report

Created: 2025-07-23 06:58

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