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 
19 namespace Marble
20 {
GeoDataLineString(TessellationFlags f)21 GeoDataLineString::GeoDataLineString( TessellationFlags f )
22   : GeoDataGeometry( new GeoDataLineStringPrivate( f ) )
23 {
24 //    mDebug() << "1) GeoDataLineString created:" << p();
25 }
26 
GeoDataLineString(GeoDataLineStringPrivate * priv)27 GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv )
28   : GeoDataGeometry( priv )
29 {
30 //    mDebug() << "2) GeoDataLineString created:" << p();
31 }
32 
GeoDataLineString(const GeoDataGeometry & other)33 GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other )
34   : GeoDataGeometry( other )
35 {
36 //    mDebug() << "3) GeoDataLineString created:" << p();
37 }
38 
~GeoDataLineString()39 GeoDataLineString::~GeoDataLineString()
40 {
41 #ifdef DEBUG_GEODATA
42     mDebug() << "delete Linestring";
43 #endif
44 }
45 
nodeType() const46 const char *GeoDataLineString::nodeType() const
47 {
48     return GeoDataTypes::GeoDataLineStringType;
49 }
50 
geometryId() const51 EnumGeometryId GeoDataLineString::geometryId() const
52 {
53     return GeoDataLineStringId;
54 }
55 
copy() const56 GeoDataGeometry *GeoDataLineString::copy() const
57 {
58     return new GeoDataLineString(*this);
59 }
60 
interpolateDateLine(const GeoDataCoordinates & previousCoords,const GeoDataCoordinates & currentCoords,GeoDataCoordinates & previousAtDateLine,GeoDataCoordinates & currentAtDateLine,TessellationFlags f) const61 void 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() << Q_FUNC_INFO;
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 
findDateLine(const GeoDataCoordinates & previousCoords,const GeoDataCoordinates & currentCoords,int recursionCounter) const92 GeoDataCoordinates 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 
levelForResolution(qreal resolution) const125 quint8 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 
resolutionForLevel(int level)151 qreal 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 
optimize(GeoDataLineString & lineString) const194 void 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 
isEmpty() const253 bool GeoDataLineString::isEmpty() const
254 {
255     Q_D(const GeoDataLineString);
256     return d->m_vector.isEmpty();
257 }
258 
size() const259 int GeoDataLineString::size() const
260 {
261     Q_D(const GeoDataLineString);
262     return d->m_vector.size();
263 }
264 
at(int pos)265 GeoDataCoordinates& GeoDataLineString::at( int pos )
266 {
267     detach();
268 
269     Q_D(GeoDataLineString);
270     d->m_dirtyRange = true;
271     d->m_dirtyBox = true;
272     return d->m_vector[pos];
273 }
274 
at(int pos) const275 const GeoDataCoordinates& GeoDataLineString::at( int pos ) const
276 {
277     Q_D(const GeoDataLineString);
278     return d->m_vector.at(pos);
279 }
280 
operator [](int pos)281 GeoDataCoordinates& GeoDataLineString::operator[]( int pos )
282 {
283     detach();
284 
285     Q_D(GeoDataLineString);
286     d->m_dirtyRange = true;
287     d->m_dirtyBox = true;
288     return d->m_vector[pos];
289 }
290 
mid(int pos,int length) const291 GeoDataLineString GeoDataLineString::mid(int pos, int length) const
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 
operator [](int pos) const303 const GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) const
304 {
305     Q_D(const GeoDataLineString);
306     return d->m_vector[pos];
307 }
308 
last()309 GeoDataCoordinates& GeoDataLineString::last()
310 {
311     detach();
312 
313     Q_D(GeoDataLineString);
314     d->m_dirtyRange = true;
315     d->m_dirtyBox = true;
316     return d->m_vector.last();
317 }
318 
first()319 GeoDataCoordinates& GeoDataLineString::first()
320 {
321     detach();
322 
323     Q_D(GeoDataLineString);
324     return d->m_vector.first();
325 }
326 
last() const327 const GeoDataCoordinates& GeoDataLineString::last() const
328 {
329     Q_D(const GeoDataLineString);
330     return d->m_vector.last();
331 }
332 
first() const333 const GeoDataCoordinates& GeoDataLineString::first() const
334 {
335     Q_D(const GeoDataLineString);
336     return d->m_vector.first();
337 }
338 
begin()339 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::begin()
340 {
341     detach();
342 
343     Q_D(GeoDataLineString);
344     return d->m_vector.begin();
345 }
346 
begin() const347 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::begin() const
348 {
349     Q_D(const GeoDataLineString);
350     return d->m_vector.constBegin();
351 }
352 
end()353 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::end()
354 {
355     detach();
356 
357     Q_D(GeoDataLineString);
358     return d->m_vector.end();
359 }
360 
end() const361 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::end() const
362 {
363     Q_D(const GeoDataLineString);
364     return d->m_vector.constEnd();
365 }
366 
constBegin() const367 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constBegin() const
368 {
369     Q_D(const GeoDataLineString);
370     return d->m_vector.constBegin();
371 }
372 
constEnd() const373 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constEnd() const
374 {
375     Q_D(const GeoDataLineString);
376     return d->m_vector.constEnd();
377 }
378 
insert(int index,const GeoDataCoordinates & value)379 void GeoDataLineString::insert( int index, const GeoDataCoordinates& value )
380 {
381     detach();
382 
383     Q_D(GeoDataLineString);
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 
append(const GeoDataCoordinates & value)391 void GeoDataLineString::append ( const GeoDataCoordinates& value )
392 {
393     detach();
394 
395     Q_D(GeoDataLineString);
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 
reserve(int size)403 void GeoDataLineString::reserve(int size)
404 {
405     Q_D(GeoDataLineString);
406     d->m_vector.reserve(size);
407 }
408 
append(const QVector<GeoDataCoordinates> & values)409 void GeoDataLineString::append(const QVector<GeoDataCoordinates>& values)
410 {
411     detach();
412 
413     Q_D(GeoDataLineString);
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 
operator <<(const GeoDataCoordinates & value)422 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataCoordinates& value )
423 {
424     detach();
425 
426     Q_D(GeoDataLineString);
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 
operator <<(const GeoDataLineString & value)435 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataLineString& value )
436 {
437     detach();
438 
439     Q_D(GeoDataLineString);
440     delete d->m_rangeCorrected;
441     d->m_rangeCorrected = nullptr;
442     d->m_dirtyRange = true;
443     d->m_dirtyBox = true;
444 
445     QVector<GeoDataCoordinates>::const_iterator itCoords = value.constBegin();
446     QVector<GeoDataCoordinates>::const_iterator itEnd = value.constEnd();
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 
operator ==(const GeoDataLineString & other) const456 bool GeoDataLineString::operator==( const GeoDataLineString &other ) const
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 
operator !=(const GeoDataLineString & other) const482 bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const
483 {
484     return !this->operator==(other);
485 }
486 
clear()487 void GeoDataLineString::clear()
488 {
489     detach();
490 
491     Q_D(GeoDataLineString);
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 
isClosed() const500 bool GeoDataLineString::isClosed() const
501 {
502     return false;
503 }
504 
tessellate() const505 bool GeoDataLineString::tessellate() const
506 {
507     Q_D(const GeoDataLineString);
508     return d->m_tessellationFlags.testFlag(Tessellate);
509 }
510 
setTessellate(bool tessellate)511 void GeoDataLineString::setTessellate( bool tessellate )
512 {
513     detach();
514 
515     Q_D(GeoDataLineString);
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 
tessellationFlags() const528 TessellationFlags GeoDataLineString::tessellationFlags() const
529 {
530     Q_D(const GeoDataLineString);
531     return d->m_tessellationFlags;
532 }
533 
setTessellationFlags(TessellationFlags f)534 void GeoDataLineString::setTessellationFlags( TessellationFlags f )
535 {
536     detach();
537 
538     Q_D(GeoDataLineString);
539     d->m_tessellationFlags = f;
540 }
541 
reverse()542 void GeoDataLineString::reverse()
543 {
544     detach();
545 
546     Q_D(GeoDataLineString);
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 
toNormalized() const554 GeoDataLineString GeoDataLineString::toNormalized() const
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();
568     for( QVector<GeoDataCoordinates>::const_iterator itCoords
569           = d->m_vector.constBegin();
570          itCoords != end;
571          ++itCoords ) {
572 
573         itCoords->geoCoordinates( lon, lat );
574         qreal alt = itCoords->altitude();
575         GeoDataCoordinates::normalizeLonLat( lon, lat );
576 
577         GeoDataCoordinates normalizedCoords( *itCoords );
578         normalizedCoords.set( lon, lat, alt );
579         normalizedLineString << normalizedCoords;
580     }
581 
582     return normalizedLineString;
583 }
584 
toRangeCorrected() const585 GeoDataLineString GeoDataLineString::toRangeCorrected() const
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 
toDateLineCorrected() const604 QVector<GeoDataLineString*> GeoDataLineString::toDateLineCorrected() const
605 {
606     Q_D(const GeoDataLineString);
607 
608     QVector<GeoDataLineString*> lineStrings;
609 
610     d->toDateLineCorrected(*this, lineStrings);
611 
612     return lineStrings;
613 }
614 
toPoleCorrected() const615 GeoDataLineString GeoDataLineString::toPoleCorrected() const
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 
toPoleCorrected(const GeoDataLineString & q,GeoDataLineString & poleCorrected) const630 void 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 
toDateLineCorrected(const GeoDataLineString & q,QVector<GeoDataLineString * > & lineStrings) const696 void 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 
latLonAltBox() const794 const GeoDataLatLonAltBox& GeoDataLineString::latLonAltBox() const
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 
length(qreal planetRadius,int offset) const810 qreal 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 
erase(const QVector<GeoDataCoordinates>::Iterator & pos)829 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& pos )
830 {
831     detach();
832 
833     Q_D(GeoDataLineString);
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 
erase(const QVector<GeoDataCoordinates>::Iterator & begin,const QVector<GeoDataCoordinates>::Iterator & end)841 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& begin,
842                                                                  const QVector<GeoDataCoordinates>::Iterator& end )
843 {
844     detach();
845 
846     Q_D(GeoDataLineString);
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 
remove(int i)854 void GeoDataLineString::remove ( int i )
855 {
856     detach();
857 
858     Q_D(GeoDataLineString);
859     d->m_dirtyRange = true;
860     d->m_dirtyBox = true;
861     d->m_vector.remove( i );
862 }
863 
optimized() const864 GeoDataLineString GeoDataLineString::optimized () const
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 
toVariantList() const879 QVariantList GeoDataLineString::toVariantList() const
880 {
881     Q_D(const GeoDataLineString);
882 
883     QVariantList variantList;
884     for( const GeoDataCoordinates & itCoords : 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 
pack(QDataStream & stream) const903 void GeoDataLineString::pack( QDataStream& stream ) const
904 {
905     Q_D(const GeoDataLineString);
906 
907     GeoDataGeometry::pack( stream );
908 
909     stream << size();
910     stream << (qint32)(d->m_tessellationFlags);
911 
912     for( QVector<GeoDataCoordinates>::const_iterator iterator
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 
unpack(QDataStream & stream)923 void GeoDataLineString::unpack( QDataStream& stream )
924 {
925     detach();
926 
927     Q_D(GeoDataLineString);
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 }
948