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

KDE's Doxygen guidelines are available online.