/src/PROJ/src/iso19111/metadata.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/metadata.hpp" |
34 | | #include "proj/common.hpp" |
35 | | #include "proj/io.hpp" |
36 | | #include "proj/util.hpp" |
37 | | |
38 | | #include "proj/internal/internal.hpp" |
39 | | #include "proj/internal/io_internal.hpp" |
40 | | |
41 | | #include "proj_json_streaming_writer.hpp" |
42 | | |
43 | | #include <algorithm> |
44 | | #include <cmath> |
45 | | #include <limits> |
46 | | #include <memory> |
47 | | #include <string> |
48 | | #include <vector> |
49 | | |
50 | | using namespace NS_PROJ::internal; |
51 | | using namespace NS_PROJ::io; |
52 | | using namespace NS_PROJ::util; |
53 | | |
54 | | #if 0 |
55 | | namespace dropbox{ namespace oxygen { |
56 | | template<> nn<std::shared_ptr<NS_PROJ::metadata::Citation>>::~nn() = default; |
57 | | template<> nn<NS_PROJ::metadata::ExtentPtr>::~nn() = default; |
58 | | template<> nn<NS_PROJ::metadata::GeographicBoundingBoxPtr>::~nn() = default; |
59 | | template<> nn<NS_PROJ::metadata::GeographicExtentPtr>::~nn() = default; |
60 | | template<> nn<NS_PROJ::metadata::VerticalExtentPtr>::~nn() = default; |
61 | | template<> nn<NS_PROJ::metadata::TemporalExtentPtr>::~nn() = default; |
62 | | template<> nn<NS_PROJ::metadata::IdentifierPtr>::~nn() = default; |
63 | | template<> nn<NS_PROJ::metadata::PositionalAccuracyPtr>::~nn() = default; |
64 | | }} |
65 | | #endif |
66 | | |
67 | | NS_PROJ_START |
68 | | namespace metadata { |
69 | | |
70 | | // --------------------------------------------------------------------------- |
71 | | |
72 | | //! @cond Doxygen_Suppress |
73 | | struct Citation::Private { |
74 | | optional<std::string> title{}; |
75 | | }; |
76 | | //! @endcond |
77 | | |
78 | | // --------------------------------------------------------------------------- |
79 | | |
80 | | //! @cond Doxygen_Suppress |
81 | 2.22M | Citation::Citation() : d(internal::make_unique<Private>()) {} |
82 | | //! @endcond |
83 | | |
84 | | // --------------------------------------------------------------------------- |
85 | | |
86 | | /** \brief Constructs a citation by its title. */ |
87 | | Citation::Citation(const std::string &titleIn) |
88 | 6.79k | : d(internal::make_unique<Private>()) { |
89 | 6.79k | d->title = titleIn; |
90 | 6.79k | } |
91 | | |
92 | | // --------------------------------------------------------------------------- |
93 | | |
94 | | //! @cond Doxygen_Suppress |
95 | | Citation::Citation(const Citation &other) |
96 | 0 | : d(internal::make_unique<Private>(*(other.d))) {} |
97 | | |
98 | | // --------------------------------------------------------------------------- |
99 | | |
100 | 2.22M | Citation::~Citation() = default; |
101 | | |
102 | | // --------------------------------------------------------------------------- |
103 | | |
104 | 6.79k | Citation &Citation::operator=(const Citation &other) { |
105 | 6.79k | if (this != &other) { |
106 | 6.79k | *d = *other.d; |
107 | 6.79k | } |
108 | 6.79k | return *this; |
109 | 6.79k | } |
110 | | //! @endcond |
111 | | |
112 | | // --------------------------------------------------------------------------- |
113 | | |
114 | | /** \brief Returns the name by which the cited resource is known. */ |
115 | 0 | const optional<std::string> &Citation::title() PROJ_PURE_DEFN { |
116 | 0 | return d->title; |
117 | 0 | } |
118 | | |
119 | | // --------------------------------------------------------------------------- |
120 | | |
121 | | //! @cond Doxygen_Suppress |
122 | | struct GeographicExtent::Private {}; |
123 | | //! @endcond |
124 | | |
125 | | // --------------------------------------------------------------------------- |
126 | | |
127 | 1.45k | GeographicExtent::GeographicExtent() : d(internal::make_unique<Private>()) {} |
128 | | |
129 | | // --------------------------------------------------------------------------- |
130 | | |
131 | | //! @cond Doxygen_Suppress |
132 | 1.45k | GeographicExtent::~GeographicExtent() = default; |
133 | | //! @endcond |
134 | | |
135 | | // --------------------------------------------------------------------------- |
136 | | |
137 | | //! @cond Doxygen_Suppress |
138 | | struct GeographicBoundingBox::Private { |
139 | | double west_{}; |
140 | | double south_{}; |
141 | | double east_{}; |
142 | | double north_{}; |
143 | | |
144 | | Private(double west, double south, double east, double north) |
145 | 1.89k | : west_(west), south_(south), east_(east), north_(north) {} |
146 | | |
147 | | bool intersects(const Private &other) const; |
148 | | |
149 | | std::unique_ptr<Private> intersection(const Private &other) const; |
150 | | }; |
151 | | //! @endcond |
152 | | |
153 | | // --------------------------------------------------------------------------- |
154 | | |
155 | | GeographicBoundingBox::GeographicBoundingBox(double west, double south, |
156 | | double east, double north) |
157 | | : GeographicExtent(), |
158 | 1.45k | d(internal::make_unique<Private>(west, south, east, north)) {} |
159 | | |
160 | | // --------------------------------------------------------------------------- |
161 | | |
162 | | //! @cond Doxygen_Suppress |
163 | 1.45k | GeographicBoundingBox::~GeographicBoundingBox() = default; |
164 | | //! @endcond |
165 | | |
166 | | // --------------------------------------------------------------------------- |
167 | | |
168 | | /** \brief Returns the western-most coordinate of the limit of the dataset |
169 | | * extent. |
170 | | * |
171 | | * The unit is degrees. |
172 | | * |
173 | | * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses |
174 | | * the anti-meridian. |
175 | | */ |
176 | 7.02k | double GeographicBoundingBox::westBoundLongitude() PROJ_PURE_DEFN { |
177 | 7.02k | return d->west_; |
178 | 7.02k | } |
179 | | |
180 | | // --------------------------------------------------------------------------- |
181 | | |
182 | | /** \brief Returns the southern-most coordinate of the limit of the dataset |
183 | | * extent. |
184 | | * |
185 | | * The unit is degrees. |
186 | | */ |
187 | 7.02k | double GeographicBoundingBox::southBoundLatitude() PROJ_PURE_DEFN { |
188 | 7.02k | return d->south_; |
189 | 7.02k | } |
190 | | |
191 | | // --------------------------------------------------------------------------- |
192 | | |
193 | | /** \brief Returns the eastern-most coordinate of the limit of the dataset |
194 | | * extent. |
195 | | * |
196 | | * The unit is degrees. |
197 | | * |
198 | | * If eastBoundLongitude < westBoundLongitude(), then the bounding box crosses |
199 | | * the anti-meridian. |
200 | | */ |
201 | 7.02k | double GeographicBoundingBox::eastBoundLongitude() PROJ_PURE_DEFN { |
202 | 7.02k | return d->east_; |
203 | 7.02k | } |
204 | | |
205 | | // --------------------------------------------------------------------------- |
206 | | |
207 | | /** \brief Returns the northern-most coordinate of the limit of the dataset |
208 | | * extent. |
209 | | * |
210 | | * The unit is degrees. |
211 | | */ |
212 | 7.02k | double GeographicBoundingBox::northBoundLatitude() PROJ_PURE_DEFN { |
213 | 7.02k | return d->north_; |
214 | 7.02k | } |
215 | | |
216 | | // --------------------------------------------------------------------------- |
217 | | |
218 | | /** \brief Instantiate a GeographicBoundingBox. |
219 | | * |
220 | | * If east < west, then the bounding box crosses the anti-meridian. |
221 | | * |
222 | | * @param west Western-most coordinate of the limit of the dataset extent (in |
223 | | * degrees). |
224 | | * @param south Southern-most coordinate of the limit of the dataset extent (in |
225 | | * degrees). |
226 | | * @param east Eastern-most coordinate of the limit of the dataset extent (in |
227 | | * degrees). |
228 | | * @param north Northern-most coordinate of the limit of the dataset extent (in |
229 | | * degrees). |
230 | | * @return a new GeographicBoundingBox. |
231 | | */ |
232 | | GeographicBoundingBoxNNPtr GeographicBoundingBox::create(double west, |
233 | | double south, |
234 | | double east, |
235 | 1.45k | double north) { |
236 | 1.45k | if (std::isnan(west) || std::isnan(south) || std::isnan(east) || |
237 | 1.45k | std::isnan(north)) { |
238 | 0 | throw InvalidValueTypeException( |
239 | 0 | "GeographicBoundingBox::create() does not accept NaN values"); |
240 | 0 | } |
241 | 1.45k | return GeographicBoundingBox::nn_make_shared<GeographicBoundingBox>( |
242 | 1.45k | west, south, east, north); |
243 | 1.45k | } |
244 | | |
245 | | // --------------------------------------------------------------------------- |
246 | | |
247 | | //! @cond Doxygen_Suppress |
248 | | bool GeographicBoundingBox::_isEquivalentTo( |
249 | | const util::IComparable *other, util::IComparable::Criterion, |
250 | 587 | const io::DatabaseContextPtr &) const { |
251 | 587 | auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other); |
252 | 587 | if (!otherExtent) |
253 | 0 | return false; |
254 | 587 | return d->west_ == otherExtent->d->west_ && |
255 | 587 | d->south_ == otherExtent->d->south_ && |
256 | 587 | d->east_ == otherExtent->d->east_ && |
257 | 587 | d->north_ == otherExtent->d->north_; |
258 | 587 | } |
259 | | //! @endcond |
260 | | |
261 | | // --------------------------------------------------------------------------- |
262 | | |
263 | 9.98k | bool GeographicBoundingBox::contains(const GeographicExtentNNPtr &other) const { |
264 | 9.98k | auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get()); |
265 | 9.98k | if (!otherExtent) { |
266 | 0 | return false; |
267 | 0 | } |
268 | 9.98k | const double W = d->west_; |
269 | 9.98k | const double E = d->east_; |
270 | 9.98k | const double N = d->north_; |
271 | 9.98k | const double S = d->south_; |
272 | 9.98k | const double oW = otherExtent->d->west_; |
273 | 9.98k | const double oE = otherExtent->d->east_; |
274 | 9.98k | const double oN = otherExtent->d->north_; |
275 | 9.98k | const double oS = otherExtent->d->south_; |
276 | | |
277 | 9.98k | if (!(S <= oS && N >= oN)) { |
278 | 651 | return false; |
279 | 651 | } |
280 | | |
281 | 9.33k | if (W == -180.0 && E == 180.0) { |
282 | 8.44k | return oW != oE; |
283 | 8.44k | } |
284 | | |
285 | 886 | if (oW == -180.0 && oE == 180.0) { |
286 | 2 | return false; |
287 | 2 | } |
288 | | |
289 | | // Normal bounding box ? |
290 | 884 | if (W < E) { |
291 | 223 | if (oW < oE) { |
292 | 60 | return W <= oW && E >= oE; |
293 | 163 | } else { |
294 | 163 | return false; |
295 | 163 | } |
296 | | // No: crossing antimerian |
297 | 661 | } else { |
298 | 661 | if (oW < oE) { |
299 | 146 | if (oW >= W) { |
300 | 36 | return true; |
301 | 110 | } else if (oE <= E) { |
302 | 70 | return true; |
303 | 70 | } else { |
304 | 40 | return false; |
305 | 40 | } |
306 | 515 | } else { |
307 | 515 | return W <= oW && E >= oE; |
308 | 515 | } |
309 | 661 | } |
310 | 884 | } |
311 | | |
312 | | // --------------------------------------------------------------------------- |
313 | | |
314 | | //! @cond Doxygen_Suppress |
315 | 353 | bool GeographicBoundingBox::Private::intersects(const Private &other) const { |
316 | 353 | const double W = west_; |
317 | 353 | const double E = east_; |
318 | 353 | const double N = north_; |
319 | 353 | const double S = south_; |
320 | 353 | const double oW = other.west_; |
321 | 353 | const double oE = other.east_; |
322 | 353 | const double oN = other.north_; |
323 | 353 | const double oS = other.south_; |
324 | | |
325 | 353 | if (N < oS || S > oN) { |
326 | 18 | return false; |
327 | 18 | } |
328 | | |
329 | 335 | if (W == -180.0 && E == 180.0 && oW > oE) { |
330 | 176 | return true; |
331 | 176 | } |
332 | | |
333 | 159 | if (oW == -180.0 && oE == 180.0 && W > E) { |
334 | 0 | return true; |
335 | 0 | } |
336 | | |
337 | | // Normal bounding box ? |
338 | 159 | if (W <= E) { |
339 | 48 | if (oW <= oE) { |
340 | 40 | if (std::max(W, oW) < std::min(E, oE)) { |
341 | 34 | return true; |
342 | 34 | } |
343 | 6 | return false; |
344 | 40 | } |
345 | | |
346 | | // Bail out on longitudes not in [-180,180]. We could probably make |
347 | | // some sense of them, but this check at least avoid potential infinite |
348 | | // recursion. |
349 | 8 | if (oW > 180 || oE < -180) { |
350 | 2 | return false; |
351 | 2 | } |
352 | | |
353 | 6 | return intersects(Private(oW, oS, 180.0, oN)) || |
354 | 6 | intersects(Private(-180.0, oS, oE, oN)); |
355 | | |
356 | | // No: crossing antimerian |
357 | 111 | } else { |
358 | 111 | if (oW <= oE) { |
359 | 7 | return other.intersects(*this); |
360 | 7 | } |
361 | | |
362 | 104 | return true; |
363 | 111 | } |
364 | 159 | } |
365 | | //! @endcond |
366 | | |
367 | | bool GeographicBoundingBox::intersects( |
368 | 337 | const GeographicExtentNNPtr &other) const { |
369 | 337 | auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get()); |
370 | 337 | if (!otherExtent) { |
371 | 0 | return false; |
372 | 0 | } |
373 | 337 | return d->intersects(*(otherExtent->d)); |
374 | 337 | } |
375 | | |
376 | | // --------------------------------------------------------------------------- |
377 | | |
378 | | GeographicExtentPtr |
379 | 296 | GeographicBoundingBox::intersection(const GeographicExtentNNPtr &other) const { |
380 | 296 | auto otherExtent = dynamic_cast<const GeographicBoundingBox *>(other.get()); |
381 | 296 | if (!otherExtent) { |
382 | 0 | return nullptr; |
383 | 0 | } |
384 | 296 | auto ret = d->intersection(*(otherExtent->d)); |
385 | 296 | if (ret) { |
386 | 216 | auto bbox = GeographicBoundingBox::create(ret->west_, ret->south_, |
387 | 216 | ret->east_, ret->north_); |
388 | 216 | return bbox.as_nullable(); |
389 | 216 | } |
390 | 80 | return nullptr; |
391 | 296 | } |
392 | | |
393 | | //! @cond Doxygen_Suppress |
394 | | std::unique_ptr<GeographicBoundingBox::Private> |
395 | 528 | GeographicBoundingBox::Private::intersection(const Private &otherExtent) const { |
396 | 528 | const double W = west_; |
397 | 528 | const double E = east_; |
398 | 528 | const double N = north_; |
399 | 528 | const double S = south_; |
400 | 528 | const double oW = otherExtent.west_; |
401 | 528 | const double oE = otherExtent.east_; |
402 | 528 | const double oN = otherExtent.north_; |
403 | 528 | const double oS = otherExtent.south_; |
404 | | |
405 | 528 | if (N < oS || S > oN) { |
406 | 32 | return nullptr; |
407 | 32 | } |
408 | | |
409 | 496 | if (W == -180.0 && E == 180.0 && oW > oE) { |
410 | 6 | return internal::make_unique<Private>(oW, std::max(S, oS), oE, |
411 | 6 | std::min(N, oN)); |
412 | 6 | } |
413 | | |
414 | 490 | if (oW == -180.0 && oE == 180.0 && W > E) { |
415 | 21 | return internal::make_unique<Private>(W, std::max(S, oS), E, |
416 | 21 | std::min(N, oN)); |
417 | 21 | } |
418 | | |
419 | | // Normal bounding box ? |
420 | 469 | if (W <= E) { |
421 | 354 | if (oW <= oE) { |
422 | 231 | const double resW = std::max(W, oW); |
423 | 231 | const double resE = std::min(E, oE); |
424 | 231 | if (resW < resE) { |
425 | 142 | return internal::make_unique<Private>(resW, std::max(S, oS), |
426 | 142 | resE, std::min(N, oN)); |
427 | 142 | } |
428 | 89 | return nullptr; |
429 | 231 | } |
430 | | |
431 | | // Bail out on longitudes not in [-180,180]. We could probably make |
432 | | // some sense of them, but this check at least avoid potential infinite |
433 | | // recursion. |
434 | 123 | if (oW > 180 || oE < -180) { |
435 | 28 | return nullptr; |
436 | 28 | } |
437 | | |
438 | | // Return larger of two parts of the multipolygon |
439 | 95 | auto inter1 = intersection(Private(oW, oS, 180.0, oN)); |
440 | 95 | auto inter2 = intersection(Private(-180.0, oS, oE, oN)); |
441 | 95 | if (!inter1) { |
442 | 28 | return inter2; |
443 | 28 | } |
444 | 67 | if (!inter2) { |
445 | 41 | return inter1; |
446 | 41 | } |
447 | 26 | if (inter1->east_ - inter1->west_ > inter2->east_ - inter2->west_) { |
448 | 2 | return inter1; |
449 | 2 | } |
450 | 24 | return inter2; |
451 | | // No: crossing antimerian |
452 | 115 | } else { |
453 | 115 | if (oW <= oE) { |
454 | 42 | return otherExtent.intersection(*this); |
455 | 42 | } |
456 | | |
457 | 73 | return internal::make_unique<Private>(std::max(W, oW), std::max(S, oS), |
458 | 73 | std::min(E, oE), std::min(N, oN)); |
459 | 115 | } |
460 | 469 | } |
461 | | //! @endcond |
462 | | |
463 | | // --------------------------------------------------------------------------- |
464 | | |
465 | | //! @cond Doxygen_Suppress |
466 | | struct VerticalExtent::Private { |
467 | | double minimum_{}; |
468 | | double maximum_{}; |
469 | | common::UnitOfMeasureNNPtr unit_; |
470 | | |
471 | | Private(double minimum, double maximum, |
472 | | const common::UnitOfMeasureNNPtr &unit) |
473 | 91 | : minimum_(minimum), maximum_(maximum), unit_(unit) {} |
474 | | }; |
475 | | //! @endcond |
476 | | |
477 | | // --------------------------------------------------------------------------- |
478 | | |
479 | | VerticalExtent::VerticalExtent(double minimumIn, double maximumIn, |
480 | | const common::UnitOfMeasureNNPtr &unitIn) |
481 | 91 | : d(internal::make_unique<Private>(minimumIn, maximumIn, unitIn)) {} |
482 | | |
483 | | // --------------------------------------------------------------------------- |
484 | | |
485 | | //! @cond Doxygen_Suppress |
486 | 91 | VerticalExtent::~VerticalExtent() = default; |
487 | | //! @endcond |
488 | | |
489 | | // --------------------------------------------------------------------------- |
490 | | |
491 | | /** \brief Returns the minimum of the vertical extent. |
492 | | */ |
493 | 0 | double VerticalExtent::minimumValue() PROJ_PURE_DEFN { return d->minimum_; } |
494 | | |
495 | | // --------------------------------------------------------------------------- |
496 | | |
497 | | /** \brief Returns the maximum of the vertical extent. |
498 | | */ |
499 | 0 | double VerticalExtent::maximumValue() PROJ_PURE_DEFN { return d->maximum_; } |
500 | | |
501 | | // --------------------------------------------------------------------------- |
502 | | |
503 | | /** \brief Returns the unit of the vertical extent. |
504 | | */ |
505 | 0 | common::UnitOfMeasureNNPtr &VerticalExtent::unit() PROJ_PURE_DEFN { |
506 | 0 | return d->unit_; |
507 | 0 | } |
508 | | |
509 | | // --------------------------------------------------------------------------- |
510 | | |
511 | | /** \brief Instantiate a VerticalExtent. |
512 | | * |
513 | | * @param minimumIn minimum. |
514 | | * @param maximumIn maximum. |
515 | | * @param unitIn unit. |
516 | | * @return a new VerticalExtent. |
517 | | */ |
518 | | VerticalExtentNNPtr |
519 | | VerticalExtent::create(double minimumIn, double maximumIn, |
520 | 91 | const common::UnitOfMeasureNNPtr &unitIn) { |
521 | 91 | return VerticalExtent::nn_make_shared<VerticalExtent>(minimumIn, maximumIn, |
522 | 91 | unitIn); |
523 | 91 | } |
524 | | |
525 | | // --------------------------------------------------------------------------- |
526 | | |
527 | | //! @cond Doxygen_Suppress |
528 | | bool VerticalExtent::_isEquivalentTo(const util::IComparable *other, |
529 | | util::IComparable::Criterion, |
530 | 94 | const io::DatabaseContextPtr &) const { |
531 | 94 | auto otherExtent = dynamic_cast<const VerticalExtent *>(other); |
532 | 94 | if (!otherExtent) |
533 | 0 | return false; |
534 | 94 | return d->minimum_ == otherExtent->d->minimum_ && |
535 | 94 | d->maximum_ == otherExtent->d->maximum_ && |
536 | 94 | d->unit_ == otherExtent->d->unit_; |
537 | 94 | } |
538 | | //! @endcond |
539 | | |
540 | | // --------------------------------------------------------------------------- |
541 | | |
542 | | /** \brief Returns whether this extent contains the other one. |
543 | | */ |
544 | 71 | bool VerticalExtent::contains(const VerticalExtentNNPtr &other) const { |
545 | 71 | const double thisUnitToSI = d->unit_->conversionToSI(); |
546 | 71 | const double otherUnitToSI = other->d->unit_->conversionToSI(); |
547 | 71 | return d->minimum_ * thisUnitToSI <= other->d->minimum_ * otherUnitToSI && |
548 | 71 | d->maximum_ * thisUnitToSI >= other->d->maximum_ * otherUnitToSI; |
549 | 71 | } |
550 | | |
551 | | // --------------------------------------------------------------------------- |
552 | | |
553 | | /** \brief Returns whether this extent intersects the other one. |
554 | | */ |
555 | 16 | bool VerticalExtent::intersects(const VerticalExtentNNPtr &other) const { |
556 | 16 | const double thisUnitToSI = d->unit_->conversionToSI(); |
557 | 16 | const double otherUnitToSI = other->d->unit_->conversionToSI(); |
558 | 16 | return d->minimum_ * thisUnitToSI <= other->d->maximum_ * otherUnitToSI && |
559 | 16 | d->maximum_ * thisUnitToSI >= other->d->minimum_ * otherUnitToSI; |
560 | 16 | } |
561 | | |
562 | | // --------------------------------------------------------------------------- |
563 | | |
564 | | //! @cond Doxygen_Suppress |
565 | | struct TemporalExtent::Private { |
566 | | std::string start_{}; |
567 | | std::string stop_{}; |
568 | | |
569 | | Private(const std::string &start, const std::string &stop) |
570 | 426 | : start_(start), stop_(stop) {} |
571 | | }; |
572 | | //! @endcond |
573 | | |
574 | | // --------------------------------------------------------------------------- |
575 | | |
576 | | TemporalExtent::TemporalExtent(const std::string &startIn, |
577 | | const std::string &stopIn) |
578 | 426 | : d(internal::make_unique<Private>(startIn, stopIn)) {} |
579 | | |
580 | | // --------------------------------------------------------------------------- |
581 | | |
582 | | //! @cond Doxygen_Suppress |
583 | 426 | TemporalExtent::~TemporalExtent() = default; |
584 | | //! @endcond |
585 | | |
586 | | // --------------------------------------------------------------------------- |
587 | | |
588 | | /** \brief Returns the start of the temporal extent. |
589 | | */ |
590 | 937 | const std::string &TemporalExtent::start() PROJ_PURE_DEFN { return d->start_; } |
591 | | |
592 | | // --------------------------------------------------------------------------- |
593 | | |
594 | | /** \brief Returns the end of the temporal extent. |
595 | | */ |
596 | 827 | const std::string &TemporalExtent::stop() PROJ_PURE_DEFN { return d->stop_; } |
597 | | |
598 | | // --------------------------------------------------------------------------- |
599 | | |
600 | | /** \brief Instantiate a TemporalExtent. |
601 | | * |
602 | | * @param start start. |
603 | | * @param stop stop. |
604 | | * @return a new TemporalExtent. |
605 | | */ |
606 | | TemporalExtentNNPtr TemporalExtent::create(const std::string &start, |
607 | 426 | const std::string &stop) { |
608 | 426 | return TemporalExtent::nn_make_shared<TemporalExtent>(start, stop); |
609 | 426 | } |
610 | | |
611 | | // --------------------------------------------------------------------------- |
612 | | |
613 | | //! @cond Doxygen_Suppress |
614 | | bool TemporalExtent::_isEquivalentTo(const util::IComparable *other, |
615 | | util::IComparable::Criterion, |
616 | 248 | const io::DatabaseContextPtr &) const { |
617 | 248 | auto otherExtent = dynamic_cast<const TemporalExtent *>(other); |
618 | 248 | if (!otherExtent) |
619 | 0 | return false; |
620 | 248 | return start() == otherExtent->start() && stop() == otherExtent->stop(); |
621 | 248 | } |
622 | | //! @endcond |
623 | | |
624 | | // --------------------------------------------------------------------------- |
625 | | |
626 | | /** \brief Returns whether this extent contains the other one. |
627 | | */ |
628 | 182 | bool TemporalExtent::contains(const TemporalExtentNNPtr &other) const { |
629 | 182 | return start() <= other->start() && stop() >= other->stop(); |
630 | 182 | } |
631 | | |
632 | | // --------------------------------------------------------------------------- |
633 | | |
634 | | /** \brief Returns whether this extent intersects the other one. |
635 | | */ |
636 | 51 | bool TemporalExtent::intersects(const TemporalExtentNNPtr &other) const { |
637 | 51 | return start() <= other->stop() && stop() >= other->start(); |
638 | 51 | } |
639 | | |
640 | | // --------------------------------------------------------------------------- |
641 | | |
642 | | //! @cond Doxygen_Suppress |
643 | | struct Extent::Private { |
644 | | optional<std::string> description_{}; |
645 | | std::vector<GeographicExtentNNPtr> geographicElements_{}; |
646 | | std::vector<VerticalExtentNNPtr> verticalElements_{}; |
647 | | std::vector<TemporalExtentNNPtr> temporalElements_{}; |
648 | | }; |
649 | | //! @endcond |
650 | | |
651 | | // --------------------------------------------------------------------------- |
652 | | |
653 | | //! @cond Doxygen_Suppress |
654 | 1.93k | Extent::Extent() : d(internal::make_unique<Private>()) {} |
655 | | |
656 | | // --------------------------------------------------------------------------- |
657 | | |
658 | | Extent::Extent(const Extent &other) |
659 | 0 | : d(internal::make_unique<Private>(*other.d)) {} |
660 | | |
661 | | // --------------------------------------------------------------------------- |
662 | | |
663 | 1.93k | Extent::~Extent() = default; |
664 | | //! @endcond |
665 | | |
666 | | // --------------------------------------------------------------------------- |
667 | | |
668 | | /** Return a textual description of the extent. |
669 | | * |
670 | | * @return the description, or empty. |
671 | | */ |
672 | 2.97k | const optional<std::string> &Extent::description() PROJ_PURE_DEFN { |
673 | 2.97k | return d->description_; |
674 | 2.97k | } |
675 | | |
676 | | // --------------------------------------------------------------------------- |
677 | | |
678 | | /** Return the geographic element(s) of the extent |
679 | | * |
680 | | * @return the geographic element(s), or empty. |
681 | | */ |
682 | | const std::vector<GeographicExtentNNPtr> & |
683 | 7.05k | Extent::geographicElements() PROJ_PURE_DEFN { |
684 | 7.05k | return d->geographicElements_; |
685 | 7.05k | } |
686 | | |
687 | | // --------------------------------------------------------------------------- |
688 | | |
689 | | /** Return the vertical element(s) of the extent |
690 | | * |
691 | | * @return the vertical element(s), or empty. |
692 | | */ |
693 | | const std::vector<VerticalExtentNNPtr> & |
694 | 0 | Extent::verticalElements() PROJ_PURE_DEFN { |
695 | 0 | return d->verticalElements_; |
696 | 0 | } |
697 | | |
698 | | // --------------------------------------------------------------------------- |
699 | | |
700 | | /** Return the temporal element(s) of the extent |
701 | | * |
702 | | * @return the temporal element(s), or empty. |
703 | | */ |
704 | | const std::vector<TemporalExtentNNPtr> & |
705 | 0 | Extent::temporalElements() PROJ_PURE_DEFN { |
706 | 0 | return d->temporalElements_; |
707 | 0 | } |
708 | | |
709 | | // --------------------------------------------------------------------------- |
710 | | |
711 | | /** \brief Instantiate a Extent. |
712 | | * |
713 | | * @param descriptionIn Textual description, or empty. |
714 | | * @param geographicElementsIn Geographic element(s), or empty. |
715 | | * @param verticalElementsIn Vertical element(s), or empty. |
716 | | * @param temporalElementsIn Temporal element(s), or empty. |
717 | | * @return a new Extent. |
718 | | */ |
719 | | ExtentNNPtr |
720 | | Extent::create(const optional<std::string> &descriptionIn, |
721 | | const std::vector<GeographicExtentNNPtr> &geographicElementsIn, |
722 | | const std::vector<VerticalExtentNNPtr> &verticalElementsIn, |
723 | 1.93k | const std::vector<TemporalExtentNNPtr> &temporalElementsIn) { |
724 | 1.93k | auto extent = Extent::nn_make_shared<Extent>(); |
725 | 1.93k | extent->assignSelf(extent); |
726 | 1.93k | extent->d->description_ = descriptionIn; |
727 | 1.93k | extent->d->geographicElements_ = geographicElementsIn; |
728 | 1.93k | extent->d->verticalElements_ = verticalElementsIn; |
729 | 1.93k | extent->d->temporalElements_ = temporalElementsIn; |
730 | 1.93k | return extent; |
731 | 1.93k | } |
732 | | |
733 | | // --------------------------------------------------------------------------- |
734 | | |
735 | | /** \brief Instantiate a Extent from a bounding box |
736 | | * |
737 | | * @param west Western-most coordinate of the limit of the dataset extent (in |
738 | | * degrees). |
739 | | * @param south Southern-most coordinate of the limit of the dataset extent (in |
740 | | * degrees). |
741 | | * @param east Eastern-most coordinate of the limit of the dataset extent (in |
742 | | * degrees). |
743 | | * @param north Northern-most coordinate of the limit of the dataset extent (in |
744 | | * degrees). |
745 | | * @param descriptionIn Textual description, or empty. |
746 | | * @return a new Extent. |
747 | | */ |
748 | | ExtentNNPtr |
749 | | Extent::createFromBBOX(double west, double south, double east, double north, |
750 | 2 | const util::optional<std::string> &descriptionIn) { |
751 | 2 | return create( |
752 | 2 | descriptionIn, |
753 | 2 | std::vector<GeographicExtentNNPtr>{ |
754 | 2 | nn_static_pointer_cast<GeographicExtent>( |
755 | 2 | GeographicBoundingBox::create(west, south, east, north))}, |
756 | 2 | std::vector<VerticalExtentNNPtr>(), std::vector<TemporalExtentNNPtr>()); |
757 | 2 | } |
758 | | |
759 | | // --------------------------------------------------------------------------- |
760 | | |
761 | | //! @cond Doxygen_Suppress |
762 | | bool Extent::_isEquivalentTo(const util::IComparable *other, |
763 | | util::IComparable::Criterion criterion, |
764 | 756 | const io::DatabaseContextPtr &dbContext) const { |
765 | 756 | auto otherExtent = dynamic_cast<const Extent *>(other); |
766 | 756 | bool ret = |
767 | 756 | (otherExtent && |
768 | 756 | description().has_value() == otherExtent->description().has_value() && |
769 | 756 | *description() == *otherExtent->description() && |
770 | 756 | d->geographicElements_.size() == |
771 | 694 | otherExtent->d->geographicElements_.size() && |
772 | 756 | d->verticalElements_.size() == |
773 | 691 | otherExtent->d->verticalElements_.size() && |
774 | 756 | d->temporalElements_.size() == |
775 | 669 | otherExtent->d->temporalElements_.size()); |
776 | 756 | if (ret) { |
777 | 1.22k | for (size_t i = 0; ret && i < d->geographicElements_.size(); ++i) { |
778 | 587 | ret = d->geographicElements_[i]->_isEquivalentTo( |
779 | 587 | otherExtent->d->geographicElements_[i].get(), criterion, |
780 | 587 | dbContext); |
781 | 587 | } |
782 | 735 | for (size_t i = 0; ret && i < d->verticalElements_.size(); ++i) { |
783 | 94 | ret = d->verticalElements_[i]->_isEquivalentTo( |
784 | 94 | otherExtent->d->verticalElements_[i].get(), criterion, |
785 | 94 | dbContext); |
786 | 94 | } |
787 | 889 | for (size_t i = 0; ret && i < d->temporalElements_.size(); ++i) { |
788 | 248 | ret = d->temporalElements_[i]->_isEquivalentTo( |
789 | 248 | otherExtent->d->temporalElements_[i].get(), criterion, |
790 | 248 | dbContext); |
791 | 248 | } |
792 | 641 | } |
793 | 756 | return ret; |
794 | 756 | } |
795 | | //! @endcond |
796 | | |
797 | | // --------------------------------------------------------------------------- |
798 | | |
799 | | /** \brief Returns whether this extent contains the other one. |
800 | | * |
801 | | * Behavior only well specified if each sub-extent category as at most |
802 | | * one element. |
803 | | */ |
804 | 10.0k | bool Extent::contains(const ExtentNNPtr &other) const { |
805 | 10.0k | bool res = true; |
806 | 10.0k | if (d->geographicElements_.size() == 1 && |
807 | 10.0k | other->d->geographicElements_.size() == 1) { |
808 | 9.98k | res = d->geographicElements_[0]->contains( |
809 | 9.98k | other->d->geographicElements_[0]); |
810 | 9.98k | } |
811 | 10.0k | if (res && d->verticalElements_.size() == 1 && |
812 | 10.0k | other->d->verticalElements_.size() == 1) { |
813 | 71 | res = d->verticalElements_[0]->contains(other->d->verticalElements_[0]); |
814 | 71 | } |
815 | 10.0k | if (res && d->temporalElements_.size() == 1 && |
816 | 10.0k | other->d->temporalElements_.size() == 1) { |
817 | 182 | res = d->temporalElements_[0]->contains(other->d->temporalElements_[0]); |
818 | 182 | } |
819 | 10.0k | return res; |
820 | 10.0k | } |
821 | | |
822 | | // --------------------------------------------------------------------------- |
823 | | |
824 | | /** \brief Returns whether this extent intersects the other one. |
825 | | * |
826 | | * Behavior only well specified if each sub-extent category as at most |
827 | | * one element. |
828 | | */ |
829 | 358 | bool Extent::intersects(const ExtentNNPtr &other) const { |
830 | 358 | bool res = true; |
831 | 358 | if (d->geographicElements_.size() == 1 && |
832 | 358 | other->d->geographicElements_.size() == 1) { |
833 | 337 | res = d->geographicElements_[0]->intersects( |
834 | 337 | other->d->geographicElements_[0]); |
835 | 337 | } |
836 | 358 | if (res && d->verticalElements_.size() == 1 && |
837 | 358 | other->d->verticalElements_.size() == 1) { |
838 | 16 | res = |
839 | 16 | d->verticalElements_[0]->intersects(other->d->verticalElements_[0]); |
840 | 16 | } |
841 | 358 | if (res && d->temporalElements_.size() == 1 && |
842 | 358 | other->d->temporalElements_.size() == 1) { |
843 | 51 | res = |
844 | 51 | d->temporalElements_[0]->intersects(other->d->temporalElements_[0]); |
845 | 51 | } |
846 | 358 | return res; |
847 | 358 | } |
848 | | |
849 | | // --------------------------------------------------------------------------- |
850 | | |
851 | | /** \brief Returns the intersection of this extent with another one. |
852 | | * |
853 | | * Behavior only well specified if there is one single GeographicExtent |
854 | | * in each object. |
855 | | * Returns nullptr otherwise. |
856 | | */ |
857 | 8.96k | ExtentPtr Extent::intersection(const ExtentNNPtr &other) const { |
858 | 8.96k | if (d->geographicElements_.size() == 1 && |
859 | 8.96k | other->d->geographicElements_.size() == 1) { |
860 | 8.91k | if (contains(other)) { |
861 | 8.18k | return other.as_nullable(); |
862 | 8.18k | } |
863 | 730 | auto self = util::nn_static_pointer_cast<Extent>(shared_from_this()); |
864 | 730 | if (other->contains(self)) { |
865 | 434 | return self.as_nullable(); |
866 | 434 | } |
867 | 296 | auto geogIntersection = d->geographicElements_[0]->intersection( |
868 | 296 | other->d->geographicElements_[0]); |
869 | 296 | if (geogIntersection) { |
870 | 216 | return create(util::optional<std::string>(), |
871 | 216 | std::vector<GeographicExtentNNPtr>{ |
872 | 216 | NN_NO_CHECK(geogIntersection)}, |
873 | 216 | std::vector<VerticalExtentNNPtr>{}, |
874 | 216 | std::vector<TemporalExtentNNPtr>{}); |
875 | 216 | } |
876 | 296 | } |
877 | 125 | return nullptr; |
878 | 8.96k | } |
879 | | |
880 | | // --------------------------------------------------------------------------- |
881 | | |
882 | | //! @cond Doxygen_Suppress |
883 | | struct Identifier::Private { |
884 | | optional<Citation> authority_{}; |
885 | | std::string code_{}; |
886 | | optional<std::string> codeSpace_{}; |
887 | | optional<std::string> version_{}; |
888 | | optional<std::string> description_{}; |
889 | | optional<std::string> uri_{}; |
890 | | |
891 | 909k | Private() = default; |
892 | | |
893 | | Private(const std::string &codeIn, const PropertyMap &properties) |
894 | 1.19M | : code_(codeIn) { |
895 | 1.19M | setProperties(properties); |
896 | 1.19M | } |
897 | | |
898 | | private: |
899 | | // cppcheck-suppress functionStatic |
900 | | void setProperties(const PropertyMap &properties); |
901 | | }; |
902 | | |
903 | | // --------------------------------------------------------------------------- |
904 | | |
905 | | void Identifier::Private::setProperties( |
906 | | const PropertyMap &properties) // throw(InvalidValueTypeException) |
907 | 1.19M | { |
908 | 1.19M | { |
909 | 1.19M | const auto pVal = properties.get(AUTHORITY_KEY); |
910 | 1.19M | if (pVal) { |
911 | 6.79k | if (auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) { |
912 | 6.79k | if (genVal->type() == BoxedValue::Type::STRING) { |
913 | 6.79k | authority_ = Citation(genVal->stringValue()); |
914 | 6.79k | } else { |
915 | 0 | throw InvalidValueTypeException("Invalid value type for " + |
916 | 0 | AUTHORITY_KEY); |
917 | 0 | } |
918 | 6.79k | } else { |
919 | 0 | if (auto citation = |
920 | 0 | dynamic_cast<const Citation *>(pVal->get())) { |
921 | 0 | authority_ = *citation; |
922 | 0 | } else { |
923 | 0 | throw InvalidValueTypeException("Invalid value type for " + |
924 | 0 | AUTHORITY_KEY); |
925 | 0 | } |
926 | 0 | } |
927 | 6.79k | } |
928 | 1.19M | } |
929 | | |
930 | 1.19M | { |
931 | 1.19M | const auto pVal = properties.get(CODE_KEY); |
932 | 1.19M | if (pVal) { |
933 | 144k | if (auto genVal = dynamic_cast<const BoxedValue *>(pVal->get())) { |
934 | 144k | if (genVal->type() == BoxedValue::Type::INTEGER) { |
935 | 144k | code_ = toString(genVal->integerValue()); |
936 | 144k | } else if (genVal->type() == BoxedValue::Type::STRING) { |
937 | 5 | code_ = genVal->stringValue(); |
938 | 5 | } else { |
939 | 0 | throw InvalidValueTypeException("Invalid value type for " + |
940 | 0 | CODE_KEY); |
941 | 0 | } |
942 | 144k | } else { |
943 | 0 | throw InvalidValueTypeException("Invalid value type for " + |
944 | 0 | CODE_KEY); |
945 | 0 | } |
946 | 144k | } |
947 | 1.19M | } |
948 | | |
949 | 1.19M | properties.getStringValue(CODESPACE_KEY, codeSpace_); |
950 | 1.19M | properties.getStringValue(VERSION_KEY, version_); |
951 | 1.19M | properties.getStringValue(DESCRIPTION_KEY, description_); |
952 | 1.19M | properties.getStringValue(URI_KEY, uri_); |
953 | 1.19M | } |
954 | | |
955 | | //! @endcond |
956 | | |
957 | | // --------------------------------------------------------------------------- |
958 | | |
959 | | Identifier::Identifier(const std::string &codeIn, |
960 | | const util::PropertyMap &properties) |
961 | 1.19M | : d(internal::make_unique<Private>(codeIn, properties)) {} |
962 | | |
963 | | // --------------------------------------------------------------------------- |
964 | | |
965 | | //! @cond Doxygen_Suppress |
966 | | |
967 | | // --------------------------------------------------------------------------- |
968 | | |
969 | 909k | Identifier::Identifier() : d(internal::make_unique<Private>()) {} |
970 | | |
971 | | // --------------------------------------------------------------------------- |
972 | | |
973 | | Identifier::Identifier(const Identifier &other) |
974 | 0 | : d(internal::make_unique<Private>(*(other.d))) {} |
975 | | |
976 | | // --------------------------------------------------------------------------- |
977 | | |
978 | 2.10M | Identifier::~Identifier() = default; |
979 | | //! @endcond |
980 | | |
981 | | // --------------------------------------------------------------------------- |
982 | | |
983 | | /** \brief Instantiate a Identifier. |
984 | | * |
985 | | * @param codeIn Alphanumeric value identifying an instance in the codespace |
986 | | * @param properties See \ref general_properties. |
987 | | * Generally, the Identifier::CODESPACE_KEY should be set. |
988 | | * @return a new Identifier. |
989 | | */ |
990 | | IdentifierNNPtr Identifier::create(const std::string &codeIn, |
991 | 1.19M | const PropertyMap &properties) { |
992 | 1.19M | return Identifier::nn_make_shared<Identifier>(codeIn, properties); |
993 | 1.19M | } |
994 | | |
995 | | // --------------------------------------------------------------------------- |
996 | | |
997 | | //! @cond Doxygen_Suppress |
998 | | IdentifierNNPtr |
999 | 909k | Identifier::createFromDescription(const std::string &descriptionIn) { |
1000 | 909k | auto id = Identifier::nn_make_shared<Identifier>(); |
1001 | 909k | id->d->description_ = descriptionIn; |
1002 | 909k | return id; |
1003 | 909k | } |
1004 | | //! @endcond |
1005 | | |
1006 | | // --------------------------------------------------------------------------- |
1007 | | |
1008 | | /** \brief Return a citation for the organization responsible for definition and |
1009 | | * maintenance of the code. |
1010 | | * |
1011 | | * @return the citation for the authority, or empty. |
1012 | | */ |
1013 | 0 | const optional<Citation> &Identifier::authority() PROJ_PURE_DEFN { |
1014 | 0 | return d->authority_; |
1015 | 0 | } |
1016 | | |
1017 | | // --------------------------------------------------------------------------- |
1018 | | |
1019 | | /** \brief Return the alphanumeric value identifying an instance in the |
1020 | | * codespace. |
1021 | | * |
1022 | | * e.g. "4326" (for EPSG:4326 WGS 84 GeographicCRS) |
1023 | | * |
1024 | | * @return the code. |
1025 | | */ |
1026 | 460k | const std::string &Identifier::code() PROJ_PURE_DEFN { return d->code_; } |
1027 | | |
1028 | | // --------------------------------------------------------------------------- |
1029 | | |
1030 | | /** \brief Return the organization responsible for definition and maintenance of |
1031 | | * the code. |
1032 | | * |
1033 | | * e.g "EPSG" |
1034 | | * |
1035 | | * @return the authority codespace, or empty. |
1036 | | */ |
1037 | 461k | const optional<std::string> &Identifier::codeSpace() PROJ_PURE_DEFN { |
1038 | 461k | return d->codeSpace_; |
1039 | 461k | } |
1040 | | |
1041 | | // --------------------------------------------------------------------------- |
1042 | | |
1043 | | /** \brief Return the version identifier for the namespace. |
1044 | | * |
1045 | | * When appropriate, the edition is identified by the effective date, coded |
1046 | | * using ISO 8601 date format. |
1047 | | * |
1048 | | * @return the version or empty. |
1049 | | */ |
1050 | 0 | const optional<std::string> &Identifier::version() PROJ_PURE_DEFN { |
1051 | 0 | return d->version_; |
1052 | 0 | } |
1053 | | |
1054 | | // --------------------------------------------------------------------------- |
1055 | | |
1056 | | /** \brief Return the natural language description of the meaning of the code |
1057 | | * value. |
1058 | | * |
1059 | | * @return the description or empty. |
1060 | | */ |
1061 | 2.65M | const optional<std::string> &Identifier::description() PROJ_PURE_DEFN { |
1062 | 2.65M | return d->description_; |
1063 | 2.65M | } |
1064 | | |
1065 | | // --------------------------------------------------------------------------- |
1066 | | |
1067 | | /** \brief Return the URI of the identifier. |
1068 | | * |
1069 | | * @return the URI or empty. |
1070 | | */ |
1071 | 0 | const optional<std::string> &Identifier::uri() PROJ_PURE_DEFN { |
1072 | 0 | return d->uri_; |
1073 | 0 | } |
1074 | | |
1075 | | // --------------------------------------------------------------------------- |
1076 | | |
1077 | | //! @cond Doxygen_Suppress |
1078 | 0 | void Identifier::_exportToWKT(WKTFormatter *formatter) const { |
1079 | 0 | const bool isWKT2 = formatter->version() == WKTFormatter::Version::WKT2; |
1080 | 0 | const std::string &l_code = code(); |
1081 | 0 | std::string l_codeSpace = *codeSpace(); |
1082 | 0 | std::string l_version = *version(); |
1083 | 0 | const auto &dbContext = formatter->databaseContext(); |
1084 | 0 | if (dbContext) { |
1085 | 0 | dbContext->getAuthorityAndVersion(*codeSpace(), l_codeSpace, l_version); |
1086 | 0 | } |
1087 | 0 | if (!l_codeSpace.empty() && !l_code.empty()) { |
1088 | 0 | if (isWKT2) { |
1089 | 0 | formatter->startNode(WKTConstants::ID, false); |
1090 | 0 | formatter->addQuotedString(l_codeSpace); |
1091 | 0 | try { |
1092 | 0 | (void)std::stoi(l_code); |
1093 | 0 | formatter->add(l_code); |
1094 | 0 | } catch (const std::exception &) { |
1095 | 0 | formatter->addQuotedString(l_code); |
1096 | 0 | } |
1097 | 0 | if (!l_version.empty()) { |
1098 | 0 | bool isDouble = false; |
1099 | 0 | (void)c_locale_stod(l_version, isDouble); |
1100 | 0 | if (isDouble) { |
1101 | 0 | formatter->add(l_version); |
1102 | 0 | } else { |
1103 | 0 | formatter->addQuotedString(l_version); |
1104 | 0 | } |
1105 | 0 | } |
1106 | 0 | if (authority().has_value() && |
1107 | 0 | *(authority()->title()) != *codeSpace()) { |
1108 | 0 | formatter->startNode(WKTConstants::CITATION, false); |
1109 | 0 | formatter->addQuotedString(*(authority()->title())); |
1110 | 0 | formatter->endNode(); |
1111 | 0 | } |
1112 | 0 | if (uri().has_value()) { |
1113 | 0 | formatter->startNode(WKTConstants::URI, false); |
1114 | 0 | formatter->addQuotedString(*(uri())); |
1115 | 0 | formatter->endNode(); |
1116 | 0 | } |
1117 | 0 | formatter->endNode(); |
1118 | 0 | } else { |
1119 | 0 | formatter->startNode(WKTConstants::AUTHORITY, false); |
1120 | 0 | formatter->addQuotedString(l_codeSpace); |
1121 | 0 | formatter->addQuotedString(l_code); |
1122 | 0 | formatter->endNode(); |
1123 | 0 | } |
1124 | 0 | } |
1125 | 0 | } |
1126 | | |
1127 | | // --------------------------------------------------------------------------- |
1128 | | |
1129 | 0 | void Identifier::_exportToJSON(JSONFormatter *formatter) const { |
1130 | 0 | const std::string &l_code = code(); |
1131 | 0 | std::string l_codeSpace = *codeSpace(); |
1132 | 0 | std::string l_version = *version(); |
1133 | 0 | const auto &dbContext = formatter->databaseContext(); |
1134 | 0 | if (dbContext) { |
1135 | 0 | dbContext->getAuthorityAndVersion(*codeSpace(), l_codeSpace, l_version); |
1136 | 0 | } |
1137 | 0 | if (!l_codeSpace.empty() && !l_code.empty()) { |
1138 | 0 | auto writer = formatter->writer(); |
1139 | 0 | auto objContext(formatter->MakeObjectContext(nullptr, false)); |
1140 | 0 | writer->AddObjKey("authority"); |
1141 | 0 | writer->Add(l_codeSpace); |
1142 | 0 | writer->AddObjKey("code"); |
1143 | 0 | try { |
1144 | 0 | writer->Add(std::stoi(l_code)); |
1145 | 0 | } catch (const std::exception &) { |
1146 | 0 | writer->Add(l_code); |
1147 | 0 | } |
1148 | |
|
1149 | 0 | if (!l_version.empty()) { |
1150 | 0 | writer->AddObjKey("version"); |
1151 | 0 | bool isDouble = false; |
1152 | 0 | (void)c_locale_stod(l_version, isDouble); |
1153 | 0 | if (isDouble) { |
1154 | 0 | writer->AddUnquoted(l_version.c_str()); |
1155 | 0 | } else { |
1156 | 0 | writer->Add(l_version); |
1157 | 0 | } |
1158 | 0 | } |
1159 | 0 | if (authority().has_value() && |
1160 | 0 | *(authority()->title()) != *codeSpace()) { |
1161 | 0 | writer->AddObjKey("authority_citation"); |
1162 | 0 | writer->Add(*(authority()->title())); |
1163 | 0 | } |
1164 | 0 | if (uri().has_value()) { |
1165 | 0 | writer->AddObjKey("uri"); |
1166 | 0 | writer->Add(*(uri())); |
1167 | 0 | } |
1168 | 0 | } |
1169 | 0 | } |
1170 | | |
1171 | | //! @endcond |
1172 | | |
1173 | | // --------------------------------------------------------------------------- |
1174 | | |
1175 | | //! @cond Doxygen_Suppress |
1176 | 62.5M | static bool isIgnoredChar(char ch) { |
1177 | 62.5M | return ch == ' ' || ch == '_' || ch == '-' || ch == '/' || ch == '(' || |
1178 | 62.5M | ch == ')' || ch == '.' || ch == '&' || ch == ','; |
1179 | 62.5M | } |
1180 | | //! @endcond |
1181 | | |
1182 | | // --------------------------------------------------------------------------- |
1183 | | |
1184 | | //! @cond Doxygen_Suppress |
1185 | | static const struct utf8_to_lower { |
1186 | | const char *utf8; |
1187 | | char ascii; |
1188 | | } map_utf8_to_lower[] = { |
1189 | | {"\xc3\xa1", 'a'}, // a acute |
1190 | | {"\xc3\xa4", 'a'}, // a tremma |
1191 | | |
1192 | | {"\xc4\x9b", 'e'}, // e reverse circumflex |
1193 | | {"\xc3\xa8", 'e'}, // e grave |
1194 | | {"\xc3\xa9", 'e'}, // e acute |
1195 | | {"\xc3\xab", 'e'}, // e tremma |
1196 | | |
1197 | | {"\xc3\xad", 'i'}, // i grave |
1198 | | |
1199 | | {"\xc3\xb4", 'o'}, // o circumflex |
1200 | | {"\xc3\xb6", 'o'}, // o tremma |
1201 | | |
1202 | | {"\xc3\xa7", 'c'}, // c cedilla |
1203 | | }; |
1204 | | |
1205 | 977k | static const struct utf8_to_lower *get_ascii_replacement(const char *c_str) { |
1206 | 9.73M | for (const auto &pair : map_utf8_to_lower) { |
1207 | 9.73M | if (*c_str == pair.utf8[0] && |
1208 | 9.73M | strncmp(c_str, pair.utf8, strlen(pair.utf8)) == 0) { |
1209 | 8.91k | return &pair; |
1210 | 8.91k | } |
1211 | 9.73M | } |
1212 | 968k | return nullptr; |
1213 | 977k | } |
1214 | | //! @endcond |
1215 | | |
1216 | | // --------------------------------------------------------------------------- |
1217 | | |
1218 | | //! @cond Doxygen_Suppress |
1219 | 23.5k | std::string Identifier::canonicalizeName(const std::string &str) { |
1220 | 23.5k | std::string res; |
1221 | 23.5k | const char *c_str = str.c_str(); |
1222 | 417k | for (size_t i = 0; c_str[i] != 0; ++i) { |
1223 | 394k | const auto ch = c_str[i]; |
1224 | 394k | if (ch == ' ' && c_str[i + 1] == '+' && c_str[i + 2] == ' ') { |
1225 | 507 | i += 2; |
1226 | 507 | continue; |
1227 | 507 | } |
1228 | 393k | if (ch == '1' && !res.empty() && |
1229 | 393k | !(res.back() >= '0' && res.back() <= '9') && c_str[i + 1] == '9' && |
1230 | 393k | c_str[i + 2] >= '0' && c_str[i + 2] <= '9') { |
1231 | 678 | ++i; |
1232 | 678 | continue; |
1233 | 678 | } |
1234 | 393k | if (static_cast<unsigned char>(ch) > 127) { |
1235 | 36.4k | const auto *replacement = get_ascii_replacement(c_str + i); |
1236 | 36.4k | if (replacement) { |
1237 | 134 | res.push_back(replacement->ascii); |
1238 | 134 | i += strlen(replacement->utf8) - 1; |
1239 | 134 | continue; |
1240 | 134 | } |
1241 | 36.4k | } |
1242 | 392k | if (!isIgnoredChar(ch)) { |
1243 | 353k | res.push_back(static_cast<char>(::tolower(ch))); |
1244 | 353k | } |
1245 | 392k | } |
1246 | 23.5k | return res; |
1247 | 23.5k | } |
1248 | | //! @endcond |
1249 | | |
1250 | | // --------------------------------------------------------------------------- |
1251 | | |
1252 | | /** \brief Returns whether two names are considered equivalent. |
1253 | | * |
1254 | | * Two names are equivalent by removing any space, underscore, dash, slash, |
1255 | | * { or } character from them, and comparing in a case insensitive way. |
1256 | | */ |
1257 | 18.4M | bool Identifier::isEquivalentName(const char *a, const char *b) noexcept { |
1258 | 18.4M | size_t i = 0; |
1259 | 18.4M | size_t j = 0; |
1260 | 18.4M | char lastValidA = 0; |
1261 | 18.4M | char lastValidB = 0; |
1262 | 32.2M | while (a[i] != 0 || b[j] != 0) { |
1263 | 31.7M | char aCh = a[i]; |
1264 | 31.7M | char bCh = b[j]; |
1265 | 31.7M | if (aCh == ' ' && a[i + 1] == '+' && a[i + 2] == ' ' && a[i + 3] != 0) { |
1266 | 2.02k | i += 3; |
1267 | 2.02k | continue; |
1268 | 2.02k | } |
1269 | 31.7M | if (bCh == ' ' && b[j + 1] == '+' && b[j + 2] == ' ' && b[j + 3] != 0) { |
1270 | 490 | j += 3; |
1271 | 490 | continue; |
1272 | 490 | } |
1273 | 31.7M | if (isIgnoredChar(aCh)) { |
1274 | 1.35M | ++i; |
1275 | 1.35M | continue; |
1276 | 1.35M | } |
1277 | 30.3M | if (isIgnoredChar(bCh)) { |
1278 | 1.11M | ++j; |
1279 | 1.11M | continue; |
1280 | 1.11M | } |
1281 | 29.2M | if (aCh == '1' && !(lastValidA >= '0' && lastValidA <= '9') && |
1282 | 29.2M | a[i + 1] == '9' && a[i + 2] >= '0' && a[i + 2] <= '9') { |
1283 | 117k | i += 2; |
1284 | 117k | lastValidA = '9'; |
1285 | 117k | continue; |
1286 | 117k | } |
1287 | 29.1M | if (bCh == '1' && !(lastValidB >= '0' && lastValidB <= '9') && |
1288 | 29.1M | b[j + 1] == '9' && b[j + 2] >= '0' && b[j + 2] <= '9') { |
1289 | 116k | j += 2; |
1290 | 116k | lastValidB = '9'; |
1291 | 116k | continue; |
1292 | 116k | } |
1293 | 29.0M | if (static_cast<unsigned char>(aCh) > 127) { |
1294 | 524k | const auto *replacement = get_ascii_replacement(a + i); |
1295 | 524k | if (replacement) { |
1296 | 7.65k | aCh = replacement->ascii; |
1297 | 7.65k | i += strlen(replacement->utf8) - 1; |
1298 | 7.65k | } |
1299 | 524k | } |
1300 | 29.0M | if (static_cast<unsigned char>(bCh) > 127) { |
1301 | 416k | const auto *replacement = get_ascii_replacement(b + j); |
1302 | 416k | if (replacement) { |
1303 | 1.12k | bCh = replacement->ascii; |
1304 | 1.12k | j += strlen(replacement->utf8) - 1; |
1305 | 1.12k | } |
1306 | 416k | } |
1307 | 29.0M | if ((aCh == 0 && bCh != 0) || (aCh != 0 && bCh == 0) || |
1308 | 29.0M | ::tolower(aCh) != ::tolower(bCh)) { |
1309 | 17.9M | return false; |
1310 | 17.9M | } |
1311 | 11.0M | lastValidA = aCh; |
1312 | 11.0M | lastValidB = bCh; |
1313 | 11.0M | if (aCh != 0) |
1314 | 11.0M | ++i; |
1315 | 11.0M | if (bCh != 0) |
1316 | 11.0M | ++j; |
1317 | 11.0M | } |
1318 | 475k | return true; |
1319 | 18.4M | } |
1320 | | |
1321 | | // --------------------------------------------------------------------------- |
1322 | | |
1323 | | //! @cond Doxygen_Suppress |
1324 | | struct PositionalAccuracy::Private { |
1325 | | std::string value_{}; |
1326 | | }; |
1327 | | //! @endcond |
1328 | | |
1329 | | // --------------------------------------------------------------------------- |
1330 | | |
1331 | | PositionalAccuracy::PositionalAccuracy(const std::string &valueIn) |
1332 | 9.40k | : d(internal::make_unique<Private>()) { |
1333 | 9.40k | d->value_ = valueIn; |
1334 | 9.40k | } |
1335 | | |
1336 | | // --------------------------------------------------------------------------- |
1337 | | |
1338 | | //! @cond Doxygen_Suppress |
1339 | 9.40k | PositionalAccuracy::~PositionalAccuracy() = default; |
1340 | | //! @endcond |
1341 | | |
1342 | | // --------------------------------------------------------------------------- |
1343 | | |
1344 | | /** \brief Return the value of the positional accuracy. |
1345 | | */ |
1346 | 2.71k | const std::string &PositionalAccuracy::value() PROJ_PURE_DEFN { |
1347 | 2.71k | return d->value_; |
1348 | 2.71k | } |
1349 | | |
1350 | | // --------------------------------------------------------------------------- |
1351 | | |
1352 | | /** \brief Instantiate a PositionalAccuracy. |
1353 | | * |
1354 | | * @param valueIn positional accuracy value. |
1355 | | * @return a new PositionalAccuracy. |
1356 | | */ |
1357 | 9.40k | PositionalAccuracyNNPtr PositionalAccuracy::create(const std::string &valueIn) { |
1358 | 9.40k | return PositionalAccuracy::nn_make_shared<PositionalAccuracy>(valueIn); |
1359 | 9.40k | } |
1360 | | |
1361 | | } // namespace metadata |
1362 | | NS_PROJ_END |