Marble

GeoDataLineString.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
4// SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de>
5//
6
7#include "GeoDataLineString.h"
8#include "GeoDataLineString_p.h"
9
10#include "GeoDataLinearRing.h"
11#include "GeoDataTypes.h"
12#include "MarbleDebug.h"
13#include "Quaternion.h"
14
15#include <QDataStream>
16#include <QVariant>
17
18namespace Marble
19{
21 : GeoDataGeometry(new GeoDataLineStringPrivate(f))
22{
23 // mDebug() << "1) GeoDataLineString created:" << p();
24}
25
26GeoDataLineString::GeoDataLineString(GeoDataLineStringPrivate *priv)
27 : GeoDataGeometry(priv)
28{
29 // mDebug() << "2) GeoDataLineString created:" << p();
30}
31
33 : GeoDataGeometry(other)
34{
35 // mDebug() << "3) GeoDataLineString created:" << p();
36}
37
39{
40#ifdef DEBUG_GEODATA
41 mDebug() << "delete Linestring";
42#endif
43}
44
45const char *GeoDataLineString::nodeType() const
46{
47 return GeoDataTypes::GeoDataLineStringType;
48}
49
50EnumGeometryId GeoDataLineString::geometryId() const
51{
52 return GeoDataLineStringId;
53}
54
55GeoDataGeometry *GeoDataLineString::copy() const
56{
57 return new GeoDataLineString(*this);
58}
59
60void GeoDataLineStringPrivate::interpolateDateLine(const GeoDataCoordinates &previousCoords,
61 const GeoDataCoordinates &currentCoords,
62 GeoDataCoordinates &previousAtDateLine,
63 GeoDataCoordinates &currentAtDateLine,
64 TessellationFlags f) const
65{
66 GeoDataCoordinates dateLineCoords;
67
68 // mDebug();
69
70 if (f.testFlag(RespectLatitudeCircle) && previousCoords.latitude() == currentCoords.latitude()) {
71 dateLineCoords = currentCoords;
72 } else {
73 int recursionCounter = 0;
74 dateLineCoords = findDateLine(previousCoords, currentCoords, recursionCounter);
75 }
76
77 previousAtDateLine = dateLineCoords;
78 currentAtDateLine = dateLineCoords;
79
80 if (previousCoords.longitude() < 0) {
81 previousAtDateLine.setLongitude(-M_PI);
82 currentAtDateLine.setLongitude(+M_PI);
83 } else {
84 previousAtDateLine.setLongitude(+M_PI);
85 currentAtDateLine.setLongitude(-M_PI);
86 }
87}
88
89GeoDataCoordinates
90GeoDataLineStringPrivate::findDateLine(const GeoDataCoordinates &previousCoords, const GeoDataCoordinates &currentCoords, int recursionCounter) const
91{
92 int currentSign = (currentCoords.longitude() < 0.0) ? -1 : +1;
93 int previousSign = (previousCoords.longitude() < 0.0) ? -1 : +1;
94
95 qreal longitudeDiff = fabs(previousSign * M_PI - previousCoords.longitude()) + fabs(currentSign * M_PI - currentCoords.longitude());
96
97 if (longitudeDiff < 0.001 || recursionCounter == 100) {
98 // mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff;
99 return currentCoords;
100 }
101 ++recursionCounter;
102
103 const GeoDataCoordinates interpolatedCoords = previousCoords.nlerp(currentCoords, 0.5);
104
105 int interpolatedSign = (interpolatedCoords.longitude() < 0.0) ? -1 : +1;
106
107 /*
108 mDebug() << "SRC" << previousCoords.toString();
109 mDebug() << "TAR" << currentCoords.toString();
110 mDebug() << "IPC" << interpolatedCoords.toString();
111 */
112
113 if (interpolatedSign != currentSign) {
114 return findDateLine(interpolatedCoords, currentCoords, recursionCounter);
115 }
116
117 return findDateLine(previousCoords, interpolatedCoords, recursionCounter);
118}
119
120quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution) const
121{
122 if (m_previousResolution == resolution)
123 return m_level;
124
125 m_previousResolution = resolution;
126
127 if (resolution < 0.0000005)
128 m_level = 17;
129 else if (resolution < 0.0000010)
130 m_level = 16;
131 else if (resolution < 0.0000020)
132 m_level = 15;
133 else if (resolution < 0.0000040)
134 m_level = 14;
135 else if (resolution < 0.0000080)
136 m_level = 13;
137 else if (resolution < 0.0000160)
138 m_level = 12;
139 else if (resolution < 0.0000320)
140 m_level = 11;
141 else if (resolution < 0.0000640)
142 m_level = 10;
143 else if (resolution < 0.0001280)
144 m_level = 9;
145 else if (resolution < 0.0002560)
146 m_level = 8;
147 else if (resolution < 0.0005120)
148 m_level = 7;
149 else if (resolution < 0.0010240)
150 m_level = 6;
151 else if (resolution < 0.0020480)
152 m_level = 5;
153 else if (resolution < 0.0040960)
154 m_level = 4;
155 else if (resolution < 0.0081920)
156 m_level = 3;
157 else if (resolution < 0.0163840)
158 m_level = 2;
159 else
160 m_level = 1;
161
162 return m_level;
163}
164
165qreal GeoDataLineStringPrivate::resolutionForLevel(int level)
166{
167 switch (level) {
168 case 0:
169 return 0.0655360;
170 case 1:
171 return 0.0327680;
172 case 2:
173 return 0.0163840;
174 case 3:
175 return 0.0081920;
176 case 4:
177 return 0.0040960;
178 case 5:
179 return 0.0020480;
180 case 6:
181 return 0.0010240;
182 case 7:
183 return 0.0005120;
184 case 8:
185 return 0.0002560;
186 case 9:
187 return 0.0001280;
188 case 10:
189 return 0.0000640;
190 case 11:
191 return 0.0000320;
192 case 12:
193 return 0.0000160;
194 case 13:
195 return 0.0000080;
196 case 14:
197 return 0.0000040;
198 case 15:
199 return 0.0000020;
200 case 16:
201 return 0.0000010;
202 default:
203 case 17:
204 return 0.0000005;
205 }
206}
207
208void GeoDataLineStringPrivate::optimize(GeoDataLineString &lineString) const
209{
210 QList<GeoDataCoordinates>::iterator itCoords = lineString.begin();
211 QList<GeoDataCoordinates>::const_iterator itEnd = lineString.constEnd();
212
213 if (lineString.size() < 2)
214 return;
215
216 // Calculate the least non-zero detail-level by checking the bounding box
217 quint8 startLevel = levelForResolution((lineString.latLonAltBox().width() + lineString.latLonAltBox().height()) / 2);
218
219 quint8 currentLevel = startLevel;
220 quint8 maxLevel = startLevel;
221 GeoDataCoordinates currentCoords;
222 lineString.first().setDetail(startLevel);
223
224 // Iterate through the linestring to assign different detail levels to the nodes.
225 // In general the first and last node should have the start level assigned as
226 // a detail level.
227 // Starting from the first node the algorithm picks those nodes which
228 // have a distance from each other that is just above the resolution that is
229 // associated with the start level (which we use as a "current level").
230 // Each of those nodes get the current level assigned as the detail level.
231 // After iterating through the linestring we increment the current level value
232 // and starting again with the first node we assign detail values in a similar way
233 // to the remaining nodes which have no final detail level assigned yet.
234 // We do as many iterations through the lineString as needed and bump up the
235 // current level until all nodes have a non-zero detail level assigned.
236
237 while (currentLevel < 16 && currentLevel <= maxLevel + 1) {
238 itCoords = lineString.begin();
239
240 currentCoords = *itCoords;
241 ++itCoords;
242
243 for (; itCoords != itEnd; ++itCoords) {
244 if (itCoords->detail() != 0 && itCoords->detail() < currentLevel)
245 continue;
246
247 if (currentLevel == startLevel
248 && (itCoords->longitude() == -M_PI || itCoords->longitude() == M_PI || itCoords->latitude() < -89 * DEG2RAD
249 || itCoords->latitude() > 89 * DEG2RAD)) {
250 itCoords->setDetail(startLevel);
251 currentCoords = *itCoords;
252 maxLevel = currentLevel;
253 continue;
254 }
255 if (currentCoords.sphericalDistanceTo(*itCoords) < resolutionForLevel(currentLevel + 1)) {
256 itCoords->setDetail(currentLevel + 1);
257 } else {
258 itCoords->setDetail(currentLevel);
259 currentCoords = *itCoords;
260 maxLevel = currentLevel;
261 }
262 }
263 ++currentLevel;
264 }
265 lineString.last().setDetail(startLevel);
266}
267
269{
270 Q_D(const GeoDataLineString);
271 return d->m_vector.isEmpty();
272}
273
275{
276 Q_D(const GeoDataLineString);
277 return d->m_vector.size();
278}
279
281{
282 detach();
283
285 d->m_dirtyRange = true;
286 d->m_dirtyBox = true;
287 return d->m_vector[pos];
288}
289
291{
292 Q_D(const GeoDataLineString);
293 return d->m_vector.at(pos);
294}
295
297{
298 detach();
299
301 d->m_dirtyRange = true;
302 d->m_dirtyBox = true;
303 return d->m_vector[pos];
304}
305
307{
308 GeoDataLineString substring;
309 auto d = substring.d_func();
310 d->m_vector = d_func()->m_vector.mid(pos, length);
311 d->m_dirtyBox = true;
312 d->m_dirtyRange = true;
313 d->m_tessellationFlags = d_func()->m_tessellationFlags;
314 d->m_extrude = d_func()->m_extrude;
315 return substring;
316}
317
319{
320 Q_D(const GeoDataLineString);
321 return d->m_vector[pos];
322}
323
325{
326 detach();
327
329 d->m_dirtyRange = true;
330 d->m_dirtyBox = true;
331 return d->m_vector.last();
332}
333
335{
336 detach();
337
339 return d->m_vector.first();
340}
341
343{
344 Q_D(const GeoDataLineString);
345 return d->m_vector.last();
346}
347
349{
350 Q_D(const GeoDataLineString);
351 return d->m_vector.first();
352}
353
355{
356 detach();
357
359 return d->m_vector.begin();
360}
361
363{
364 Q_D(const GeoDataLineString);
365 return d->m_vector.constBegin();
366}
367
369{
370 detach();
371
373 return d->m_vector.end();
374}
375
377{
378 Q_D(const GeoDataLineString);
379 return d->m_vector.constEnd();
380}
381
383{
384 Q_D(const GeoDataLineString);
385 return d->m_vector.constBegin();
386}
387
389{
390 Q_D(const GeoDataLineString);
391 return d->m_vector.constEnd();
392}
393
395{
396 detach();
397
399 delete d->m_rangeCorrected;
400 d->m_rangeCorrected = nullptr;
401 d->m_dirtyRange = true;
402 d->m_dirtyBox = true;
403 d->m_vector.insert(index, value);
404}
405
407{
408 detach();
409
411 delete d->m_rangeCorrected;
412 d->m_rangeCorrected = nullptr;
413 d->m_dirtyRange = true;
414 d->m_dirtyBox = true;
415 d->m_vector.append(value);
416}
417
419{
421 d->m_vector.reserve(size);
422}
423
425{
426 detach();
427
429 delete d->m_rangeCorrected;
430 d->m_rangeCorrected = nullptr;
431 d->m_dirtyRange = true;
432 d->m_dirtyBox = true;
433
434 d->m_vector.append(values);
435}
436
438{
439 detach();
440
442 delete d->m_rangeCorrected;
443 d->m_rangeCorrected = nullptr;
444 d->m_dirtyRange = true;
445 d->m_dirtyBox = true;
446 d->m_vector.append(value);
447 return *this;
448}
449
451{
452 detach();
453
455 delete d->m_rangeCorrected;
456 d->m_rangeCorrected = nullptr;
457 d->m_dirtyRange = true;
458 d->m_dirtyBox = true;
459
462
463 d->m_vector.reserve(d->m_vector.size() + value.size());
464 for (; itCoords != itEnd; ++itCoords) {
465 d->m_vector.append(*itCoords);
466 }
467
468 return *this;
469}
470
472{
473 if (!GeoDataGeometry::equals(other) || size() != other.size() || tessellate() != other.tessellate()) {
474 return false;
475 }
476
477 Q_D(const GeoDataLineString);
478 const GeoDataLineStringPrivate *other_d = other.d_func();
479
480 QList<GeoDataCoordinates>::const_iterator itCoords = d->m_vector.constBegin();
481 QList<GeoDataCoordinates>::const_iterator otherItCoords = other_d->m_vector.constBegin();
482 QList<GeoDataCoordinates>::const_iterator itEnd = d->m_vector.constEnd();
483 QList<GeoDataCoordinates>::const_iterator otherItEnd = other_d->m_vector.constEnd();
484
485 for (; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords) {
486 if (*itCoords != *otherItCoords) {
487 return false;
488 }
489 }
490
491 Q_ASSERT(itCoords == itEnd && otherItCoords == otherItEnd);
492 return true;
493}
494
495bool GeoDataLineString::operator!=(const GeoDataLineString &other) const
496{
497 return !this->operator==(other);
498}
499
501{
502 detach();
503
505 delete d->m_rangeCorrected;
506 d->m_rangeCorrected = nullptr;
507 d->m_dirtyRange = true;
508 d->m_dirtyBox = true;
509
510 d->m_vector.clear();
511}
512
514{
515 return false;
516}
517
519{
520 Q_D(const GeoDataLineString);
521 return d->m_tessellationFlags.testFlag(Tessellate);
522}
523
525{
526 detach();
527
529 // According to the KML reference the tesselation of line strings in Google Earth
530 // is generally done along great circles. However for subsequent points that share
531 // the same latitude the latitude circles are followed. Our Tesselate and RespectLatitude
532 // Flags provide this behaviour. For true polygons the latitude circles don't get considered.
533
534 if (tessellate) {
535 d->m_tessellationFlags |= (Tessellate | RespectLatitudeCircle);
536 } else {
537 d->m_tessellationFlags &= ~(Tessellate | RespectLatitudeCircle);
538 }
539}
540
542{
543 Q_D(const GeoDataLineString);
544 return d->m_tessellationFlags;
545}
546
548{
549 detach();
550
552 d->m_tessellationFlags = f;
553}
554
556{
557 detach();
558
560 delete d->m_rangeCorrected;
561 d->m_rangeCorrected = nullptr;
562 d->m_dirtyRange = true;
563 d->m_dirtyBox = true;
564 std::reverse(begin(), end());
565}
566
568{
569 Q_D(const GeoDataLineString);
570
571 GeoDataLineString normalizedLineString;
572
573 normalizedLineString.setTessellationFlags(tessellationFlags());
574
575 qreal lon;
576 qreal lat;
577
578 // FIXME: Think about how we can avoid unnecessary copies
579 // if the linestring stays the same.
580 QList<GeoDataCoordinates>::const_iterator end = d->m_vector.constEnd();
581 for (QList<GeoDataCoordinates>::const_iterator itCoords = d->m_vector.constBegin(); itCoords != end; ++itCoords) {
582 itCoords->geoCoordinates(lon, lat);
583 qreal alt = itCoords->altitude();
585
586 GeoDataCoordinates normalizedCoords(*itCoords);
587 normalizedCoords.set(lon, lat, alt);
588 normalizedLineString << normalizedCoords;
589 }
590
591 return normalizedLineString;
592}
593
595{
596 Q_D(const GeoDataLineString);
597
598 if (d->m_dirtyRange) {
599 delete d->m_rangeCorrected;
600
601 if (isClosed()) {
602 d->m_rangeCorrected = new GeoDataLinearRing(toPoleCorrected());
603 } else {
604 d->m_rangeCorrected = new GeoDataLineString(toPoleCorrected());
605 }
606 d->m_dirtyRange = false;
607 }
608
609 return *d->m_rangeCorrected;
610}
611
613{
614 Q_D(const GeoDataLineString);
615
616 QList<GeoDataLineString *> lineStrings;
617
618 d->toDateLineCorrected(*this, lineStrings);
619
620 return lineStrings;
621}
622
624{
625 Q_D(const GeoDataLineString);
626
627 if (isClosed()) {
628 GeoDataLinearRing poleCorrected;
629 d->toPoleCorrected(*this, poleCorrected);
630 return poleCorrected;
631 } else {
632 GeoDataLineString poleCorrected;
633 d->toPoleCorrected(*this, poleCorrected);
634 return poleCorrected;
635 }
636}
637
638void GeoDataLineStringPrivate::toPoleCorrected(const GeoDataLineString &q, GeoDataLineString &poleCorrected) const
639{
640 poleCorrected.setTessellationFlags(q.tessellationFlags());
641
642 GeoDataCoordinates previousCoords;
643 GeoDataCoordinates currentCoords;
644
645 if (q.isClosed()) {
646 if (!(m_vector.first().isPole()) && (m_vector.last().isPole())) {
647 qreal firstLongitude = (m_vector.first()).longitude();
648 GeoDataCoordinates modifiedCoords(m_vector.last());
649 modifiedCoords.setLongitude(firstLongitude);
650 poleCorrected << modifiedCoords;
651 }
652 }
653
654 QList<GeoDataCoordinates>::const_iterator itCoords = m_vector.constBegin();
655 QList<GeoDataCoordinates>::const_iterator itEnd = m_vector.constEnd();
656
657 for (; itCoords != itEnd; ++itCoords) {
658 currentCoords = *itCoords;
659
660 if (itCoords == m_vector.constBegin()) {
661 previousCoords = currentCoords;
662 }
663
664 if (currentCoords.isPole()) {
665 if (previousCoords.isPole()) {
666 continue;
667 } else {
668 qreal previousLongitude = previousCoords.longitude();
669 GeoDataCoordinates currentModifiedCoords(currentCoords);
670 currentModifiedCoords.setLongitude(previousLongitude);
671 poleCorrected << currentModifiedCoords;
672 }
673 } else {
674 if (previousCoords.isPole()) {
675 qreal currentLongitude = currentCoords.longitude();
676 GeoDataCoordinates previousModifiedCoords(previousCoords);
677 previousModifiedCoords.setLongitude(currentLongitude);
678 poleCorrected << previousModifiedCoords;
679 poleCorrected << currentCoords;
680 } else {
681 // No poles at all. Nothing special to handle
682 poleCorrected << currentCoords;
683 }
684 }
685 previousCoords = currentCoords;
686 }
687
688 if (q.isClosed()) {
689 if ((m_vector.first().isPole()) && !(m_vector.last().isPole())) {
690 qreal lastLongitude = (m_vector.last()).longitude();
691 GeoDataCoordinates modifiedCoords(m_vector.first());
692 modifiedCoords.setLongitude(lastLongitude);
693 poleCorrected << modifiedCoords;
694 }
695 }
696}
697
698void GeoDataLineStringPrivate::toDateLineCorrected(const GeoDataLineString &q, QList<GeoDataLineString *> &lineStrings) const
699{
700 const bool isClosed = q.isClosed();
701
702 const QList<GeoDataCoordinates>::const_iterator itStartPoint = q.constBegin();
703 const QList<GeoDataCoordinates>::const_iterator itEndPoint = q.constEnd();
704 QList<GeoDataCoordinates>::const_iterator itPoint = itStartPoint;
705 QList<GeoDataCoordinates>::const_iterator itPreviousPoint = itPoint;
706
707 TessellationFlags f = q.tessellationFlags();
708
709 GeoDataLineString *unfinishedLineString = nullptr;
710
711 GeoDataLineString *dateLineCorrected = isClosed ? new GeoDataLinearRing(f) : new GeoDataLineString(f);
712
713 qreal previousLon = 0.0;
714 int previousSign = 1;
715
716 bool unfinished = false;
717
718 for (; itPoint != itEndPoint; ++itPoint) {
719 const qreal currentLon = itPoint->longitude();
720
721 int currentSign = (currentLon < 0.0) ? -1 : +1;
722
723 if (itPoint == q.constBegin()) {
724 previousSign = currentSign;
725 previousLon = currentLon;
726 }
727
728 // If we are crossing the date line ...
729 if (previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI) {
730 unfinished = !unfinished;
731
732 GeoDataCoordinates previousTemp;
733 GeoDataCoordinates currentTemp;
734
735 interpolateDateLine(*itPreviousPoint, *itPoint, previousTemp, currentTemp, q.tessellationFlags());
736
737 *dateLineCorrected << previousTemp;
738
739 if (isClosed && unfinished) {
740 // If it's a linear ring and if it crossed the IDL only once then
741 // store the current string inside the unfinishedLineString for later use ...
742 unfinishedLineString = dateLineCorrected;
743 // ... and start a new linear ring for now.
744 dateLineCorrected = new GeoDataLinearRing(f);
745 } else {
746 // Now it can only be a (finished) line string or a finished linear ring.
747 // Store it in the vector if the size is not zero.
748 if (dateLineCorrected->size() > 0) {
749 lineStrings << dateLineCorrected;
750 } else {
751 // Or delete it.
752 delete dateLineCorrected;
753 }
754
755 // If it's a finished linear ring restore the "remembered" unfinished String
756 if (isClosed && !unfinished && unfinishedLineString) {
757 dateLineCorrected = unfinishedLineString;
758 } else {
759 // if it's a line string just create a new line string.
760 dateLineCorrected = new GeoDataLineString(f);
761 }
762 }
763
764 *dateLineCorrected << currentTemp;
765 *dateLineCorrected << *itPoint;
766
767 } else {
768 *dateLineCorrected << *itPoint;
769 }
770
771 previousSign = currentSign;
772 previousLon = currentLon;
773 itPreviousPoint = itPoint;
774 }
775
776 // If the line string doesn't cross the dateline an even number of times
777 // then need to take care of the data stored in the unfinishedLineString
778 if (unfinished && unfinishedLineString && !unfinishedLineString->isEmpty()) {
779 *dateLineCorrected << *unfinishedLineString;
780 delete unfinishedLineString;
781 }
782
783 lineStrings << dateLineCorrected;
784}
785
787{
788 Q_D(const GeoDataLineString);
789
790 // GeoDataLatLonAltBox::fromLineString is very expensive
791 // that's why we recreate it only if the m_dirtyBox
792 // is TRUE.
793 // DO NOT REMOVE THIS CONSTRUCT OR MARBLE WILL BE SLOW.
794 if (d->m_dirtyBox) {
795 d->m_latLonAltBox = GeoDataLatLonAltBox::fromLineString(*this);
796 d->m_dirtyBox = false;
797 }
798
799 return d->m_latLonAltBox;
800}
801
802qreal GeoDataLineString::length(qreal planetRadius, int offset) const
803{
804 if (offset < 0 || offset >= size()) {
805 return 0;
806 }
807
808 Q_D(const GeoDataLineString);
809 qreal length = 0.0;
810 QList<GeoDataCoordinates> const &vector = d->m_vector;
811 int const start = qMax(offset + 1, 1);
812 int const end = d->m_vector.size();
813 for (int i = start; i < end; ++i) {
814 length += vector[i - 1].sphericalDistanceTo(vector[i]);
815 }
816
817 return planetRadius * length;
818}
819
821{
822 detach();
823
825 delete d->m_rangeCorrected;
826 d->m_rangeCorrected = nullptr;
827 d->m_dirtyRange = true;
828 d->m_dirtyBox = true;
829 return d->m_vector.erase(pos);
830}
831
833{
834 detach();
835
837 delete d->m_rangeCorrected;
838 d->m_rangeCorrected = nullptr;
839 d->m_dirtyRange = true;
840 d->m_dirtyBox = true;
841 return d->m_vector.erase(begin, end);
842}
843
845{
846 detach();
847
849 d->m_dirtyRange = true;
850 d->m_dirtyBox = true;
851 d->m_vector.remove(i);
852}
853
855{
856 Q_D(const GeoDataLineString);
857
858 if (isClosed()) {
859 GeoDataLinearRing linearRing(*this);
860 d->optimize(linearRing);
861 return linearRing;
862 } else {
863 GeoDataLineString lineString(*this);
864 d->optimize(lineString);
865 return lineString;
866 }
867}
868
870{
871 Q_D(const GeoDataLineString);
872
873 QVariantList variantList;
874 for (const GeoDataCoordinates &itCoords : std::as_const(d->m_vector)) {
875 QVariantMap map;
876 map.insert(QLatin1StringView("lon"), itCoords.longitude(GeoDataCoordinates::Degree));
877 map.insert(QLatin1StringView("lat"), itCoords.latitude(GeoDataCoordinates::Degree));
878 map.insert(QLatin1StringView("alt"), itCoords.altitude());
879 variantList << map;
880 }
881
882 if (isClosed()) {
883 QVariantMap map;
884 map.insert(QLatin1StringView("lon"), d->m_vector.first().longitude(GeoDataCoordinates::Degree));
885 map.insert(QLatin1StringView("lat"), d->m_vector.first().latitude(GeoDataCoordinates::Degree));
886 map.insert(QLatin1StringView("alt"), d->m_vector.first().altitude());
887 variantList << map;
888 }
889
890 return variantList;
891}
892
894{
895 Q_D(const GeoDataLineString);
896
897 GeoDataGeometry::pack(stream);
898
899 stream << size();
900 stream << (qint32)(d->m_tessellationFlags);
901
902 for (QList<GeoDataCoordinates>::const_iterator iterator = d->m_vector.constBegin(); iterator != d->m_vector.constEnd(); ++iterator) {
903 mDebug() << "innerRing: size" << d->m_vector.size();
904 GeoDataCoordinates coord = (*iterator);
905 coord.pack(stream);
906 }
907}
908
910{
911 detach();
912
914
916 qint32 size;
917 qint32 tessellationFlags;
918
919 stream >> size;
920 stream >> tessellationFlags;
921
922 d->m_tessellationFlags = (TessellationFlags)(tessellationFlags);
923
924 d->m_vector.reserve(d->m_vector.size() + size);
925
926 for (qint32 i = 0; i < size; i++) {
927 GeoDataCoordinates coord;
928 coord.unpack(stream);
929 d->m_vector.append(coord);
930 }
931}
932
933}
A 3d point representation.
bool isPole(Pole=AnyPole) const
return whether our coordinates represent a pole This method can be used to check whether the coordina...
void unpack(QDataStream &stream)
Unserialize the contents of the feature from stream.
void set(qreal lon, qreal lat, qreal alt=0, GeoDataCoordinates::Unit unit=GeoDataCoordinates::Radian)
(re)set the coordinates in a GeoDataCoordinates object
static void normalizeLonLat(qreal &lon, qreal &lat, GeoDataCoordinates::Unit=GeoDataCoordinates::Radian)
normalize both longitude and latitude at the same time This method normalizes both latitude and longi...
void pack(QDataStream &stream) const
Serialize the contents of the feature to stream.
A base class for all geodata features.
void pack(QDataStream &stream) const override
Serialize the contents of the feature to stream.
void unpack(QDataStream &stream) override
Unserialize the contents of the feature from stream.
A class that defines a 3D bounding box for geographic data.
static GeoDataLatLonAltBox fromLineString(const GeoDataLineString &lineString)
Create the smallest bounding box from a line string.
A LineString that allows to store a contiguous set of line segments.
virtual bool isClosed() const
Returns whether a LineString is a closed polygon.
bool isEmpty() const
Returns whether the LineString has no nodes at all.
GeoDataLineString(TessellationFlags f=NoTessellation)
Creates a new LineString.
void insert(int index, const GeoDataCoordinates &value)
Inserts a new node at the given index.
void reverse()
Reverses the LineString.
void remove(int i)
Removes the node at the given position and destroys it.
void unpack(QDataStream &stream) override
Unserialize the LineString from a stream.
void setTessellationFlags(TessellationFlags f)
Sets the given tessellation flags for a LineString.
TessellationFlags tessellationFlags() const
Returns the tessellation flags for a LineString.
GeoDataCoordinates & first()
Returns a reference to the first node in the LineString. This method detaches the returned coordinate...
GeoDataCoordinates & at(int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
int size() const
Returns the number of nodes in a LineString.
QVariantList toVariantList() const
Returns a javascript-style list (that can be used e.g. with the QML GeoPolyline element).
GeoDataLineString optimized() const
Returns a linestring with detail values assigned to each node.
GeoDataLineString mid(int pos, int length=-1) const
Returns a sub-string which contains elements from this vector, starting at position pos.
QList< GeoDataCoordinates >::ConstIterator constBegin() const
Returns a const iterator that points to the begin of the LineString.
void clear()
Destroys all nodes in a LineString.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
virtual GeoDataLineString toRangeCorrected() const
Provides a more generic representation of the LineString.
virtual QList< GeoDataLineString * > toDateLineCorrected() const
The line string corrected for date line crossing.
~GeoDataLineString() override
Destroys a LineString.
QList< GeoDataCoordinates >::ConstIterator constEnd() const
Returns a const iterator that points to the end of the LineString.
GeoDataLineString & operator<<(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
const char * nodeType() const override
Provides type information for downcasting a GeoNode.
bool operator==(const GeoDataLineString &other) const
Returns true/false depending on whether this and other are/are not equal.
QList< GeoDataCoordinates >::Iterator begin()
Returns an iterator that points to the begin of the LineString.
virtual GeoDataLineString toNormalized() const
The line string with nodes that have proper longitude/latitude ranges.
QList< GeoDataCoordinates >::Iterator end()
Returns an iterator that points to the end of the LineString.
void pack(QDataStream &stream) const override
Serialize the LineString to a stream.
GeoDataCoordinates & operator[](int pos)
Returns a reference to the coordinates of a node at a given position. This method detaches the return...
void setTessellate(bool tessellate)
Sets the tessellation property for the LineString.
void append(const GeoDataCoordinates &value)
Appends a given geodesic position as a new node to the LineString.
virtual GeoDataLineString toPoleCorrected() const
The line string with more generic pole values.
virtual qreal length(qreal planetRadius, int offset=0) const
Returns the length of LineString across a sphere starting from a coordinate in LineString This method...
GeoDataCoordinates & last()
Returns a reference to the last node in the LineString. This method detaches the returned coordinate ...
void reserve(int size)
Attempts to allocate memory for at least size coordinates.
QList< GeoDataCoordinates >::Iterator erase(const QList< GeoDataCoordinates >::Iterator &position)
Removes the node at the given position and returns it.
bool tessellate() const
Returns whether the LineString follows the earth's surface.
A LinearRing that allows to store a closed, contiguous set of line segments.
Q_SCRIPTABLE Q_NOREPLY void start()
Binds a QML item to a specific geodetic location in screen coordinates.
QLatin1Char first() const const
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 29 2024 11:55:53 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.