File indexing completed on 2024-05-12 03:50:13

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2008 Torsten Rahn <rahn@kde.org>
0004 // SPDX-FileCopyrightText: 2009 Patrick Spendrin <ps_ml@gmx.de>
0005 //
0006 
0007 
0008 #include "GeoDataLineString.h"
0009 #include "GeoDataLineString_p.h"
0010 
0011 #include "GeoDataLinearRing.h"
0012 #include "GeoDataTypes.h"
0013 #include "Quaternion.h"
0014 #include "MarbleDebug.h"
0015 
0016 #include <QDataStream>
0017 
0018 
0019 namespace Marble
0020 {
0021 GeoDataLineString::GeoDataLineString( TessellationFlags f )
0022   : GeoDataGeometry( new GeoDataLineStringPrivate( f ) )
0023 {
0024 //    mDebug() << "1) GeoDataLineString created:" << p();
0025 }
0026 
0027 GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv )
0028   : GeoDataGeometry( priv )
0029 {
0030 //    mDebug() << "2) GeoDataLineString created:" << p();
0031 }
0032 
0033 GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other )
0034   : GeoDataGeometry( other )
0035 {
0036 //    mDebug() << "3) GeoDataLineString created:" << p();
0037 }
0038 
0039 GeoDataLineString::~GeoDataLineString()
0040 {
0041 #ifdef DEBUG_GEODATA
0042     mDebug() << "delete Linestring";
0043 #endif
0044 }
0045 
0046 const char *GeoDataLineString::nodeType() const
0047 {
0048     return GeoDataTypes::GeoDataLineStringType;
0049 }
0050 
0051 EnumGeometryId GeoDataLineString::geometryId() const
0052 {
0053     return GeoDataLineStringId;
0054 }
0055 
0056 GeoDataGeometry *GeoDataLineString::copy() const
0057 {
0058     return new GeoDataLineString(*this);
0059 }
0060 
0061 void GeoDataLineStringPrivate::interpolateDateLine( const GeoDataCoordinates & previousCoords,
0062                                                     const GeoDataCoordinates & currentCoords,
0063                                                     GeoDataCoordinates & previousAtDateLine,
0064                                                     GeoDataCoordinates & currentAtDateLine,
0065                                                     TessellationFlags f ) const
0066 {
0067     GeoDataCoordinates dateLineCoords;
0068 
0069 //    mDebug();
0070 
0071     if ( f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude() ) {
0072         dateLineCoords = currentCoords;
0073     }
0074     else {
0075         int recursionCounter = 0;
0076         dateLineCoords = findDateLine( previousCoords, currentCoords, recursionCounter );
0077     }
0078 
0079     previousAtDateLine = dateLineCoords;
0080     currentAtDateLine = dateLineCoords;
0081 
0082     if ( previousCoords.longitude() < 0 ) {
0083         previousAtDateLine.setLongitude( -M_PI );
0084         currentAtDateLine.setLongitude( +M_PI );
0085     }
0086     else {
0087         previousAtDateLine.setLongitude( +M_PI );
0088         currentAtDateLine.setLongitude( -M_PI );
0089     }
0090 }
0091 
0092 GeoDataCoordinates GeoDataLineStringPrivate::findDateLine( const GeoDataCoordinates & previousCoords,
0093                                              const GeoDataCoordinates & currentCoords,
0094                                              int recursionCounter ) const
0095 {
0096     int currentSign = ( currentCoords.longitude() < 0.0 ) ? -1 : +1 ;
0097     int previousSign = ( previousCoords.longitude() < 0.0 ) ? -1 : +1 ;
0098 
0099     qreal longitudeDiff =   fabs( previousSign * M_PI  - previousCoords.longitude() )
0100                           + fabs( currentSign * M_PI - currentCoords.longitude() );
0101 
0102     if ( longitudeDiff < 0.001 || recursionCounter == 100 ) {
0103 //        mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff;
0104         return currentCoords;
0105     }
0106     ++recursionCounter;
0107 
0108     const GeoDataCoordinates interpolatedCoords = previousCoords.nlerp(currentCoords, 0.5);
0109 
0110     int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ;
0111 
0112 /*
0113     mDebug() << "SRC" << previousCoords.toString();
0114     mDebug() << "TAR" << currentCoords.toString();
0115     mDebug() << "IPC" << interpolatedCoords.toString();
0116 */
0117 
0118     if ( interpolatedSign != currentSign ) {
0119         return findDateLine( interpolatedCoords, currentCoords, recursionCounter );
0120     }
0121 
0122     return findDateLine( previousCoords, interpolatedCoords, recursionCounter );
0123 }
0124 
0125 quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution) const {
0126     if (m_previousResolution == resolution) return m_level;
0127 
0128     m_previousResolution = resolution;
0129 
0130     if (resolution < 0.0000005) m_level = 17;
0131     else if (resolution < 0.0000010) m_level = 16;
0132     else if (resolution < 0.0000020) m_level = 15;
0133     else if (resolution < 0.0000040) m_level = 14;
0134     else if (resolution < 0.0000080) m_level = 13;
0135     else if (resolution < 0.0000160) m_level = 12;
0136     else if (resolution < 0.0000320) m_level = 11;
0137     else if (resolution < 0.0000640) m_level = 10;
0138     else if (resolution < 0.0001280) m_level = 9;
0139     else if (resolution < 0.0002560) m_level = 8;
0140     else if (resolution < 0.0005120) m_level = 7;
0141     else if (resolution < 0.0010240) m_level = 6;
0142     else if (resolution < 0.0020480) m_level = 5;
0143     else if (resolution < 0.0040960) m_level = 4;
0144     else if (resolution < 0.0081920) m_level = 3;
0145     else if (resolution < 0.0163840) m_level = 2;
0146     else m_level =  1;
0147 
0148     return m_level;
0149 }
0150 
0151 qreal GeoDataLineStringPrivate::resolutionForLevel(int level)
0152 {
0153     switch (level) {
0154         case 0:
0155             return 0.0655360;
0156         case 1:
0157             return 0.0327680;
0158         case 2:
0159             return 0.0163840;
0160         case 3:
0161             return 0.0081920;
0162         case 4:
0163             return 0.0040960;
0164         case 5:
0165             return 0.0020480;
0166         case 6:
0167             return 0.0010240;
0168         case 7:
0169             return 0.0005120;
0170         case 8:
0171             return 0.0002560;
0172         case 9:
0173             return 0.0001280;
0174         case 10:
0175             return 0.0000640;
0176         case 11:
0177             return 0.0000320;
0178         case 12:
0179             return 0.0000160;
0180         case 13:
0181             return 0.0000080;
0182         case 14:
0183             return 0.0000040;
0184         case 15:
0185             return 0.0000020;
0186         case 16:
0187             return 0.0000010;
0188         default:
0189         case 17:
0190             return 0.0000005;
0191     }
0192 }
0193 
0194 void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const
0195 {
0196 
0197     QVector<GeoDataCoordinates>::iterator itCoords = lineString.begin();
0198     QVector<GeoDataCoordinates>::const_iterator itEnd = lineString.constEnd();
0199 
0200     if (lineString.size() < 2) return;
0201 
0202     // Calculate the least non-zero detail-level by checking the bounding box
0203     quint8 startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 );
0204 
0205     quint8 currentLevel = startLevel;
0206     quint8 maxLevel = startLevel;
0207     GeoDataCoordinates currentCoords;
0208     lineString.first().setDetail(startLevel);
0209 
0210     // Iterate through the linestring to assign different detail levels to the nodes.
0211     // In general the first and last node should have the start level assigned as
0212     // a detail level.
0213     // Starting from the first node the algorithm picks those nodes which
0214     // have a distance from each other that is just above the resolution that is
0215     // associated with the start level (which we use as a "current level").
0216     // Each of those nodes get the current level assigned as the detail level.
0217     // After iterating through the linestring we increment the current level value
0218     // and starting again with the first node we assign detail values in a similar way
0219     // to the remaining nodes which have no final detail level assigned yet.
0220     // We do as many iterations through the lineString as needed and bump up the
0221     // current level until all nodes have a non-zero detail level assigned.
0222 
0223     while ( currentLevel  < 16 && currentLevel <= maxLevel + 1 ) {
0224         itCoords = lineString.begin();
0225 
0226         currentCoords = *itCoords;
0227         ++itCoords;
0228 
0229         for( ; itCoords != itEnd; ++itCoords) {
0230             if (itCoords->detail() != 0 && itCoords->detail() < currentLevel) continue;
0231 
0232             if ( currentLevel == startLevel && (itCoords->longitude() == -M_PI || itCoords->longitude() == M_PI
0233                 || itCoords->latitude() < -89 * DEG2RAD || itCoords->latitude() > 89 * DEG2RAD)) {
0234                 itCoords->setDetail(startLevel);
0235                 currentCoords = *itCoords;
0236                 maxLevel = currentLevel;
0237                 continue;
0238             }
0239             if (currentCoords.sphericalDistanceTo(*itCoords) < resolutionForLevel(currentLevel + 1)) {
0240                 itCoords->setDetail(currentLevel + 1);
0241             }
0242             else {
0243                 itCoords->setDetail(currentLevel);
0244                 currentCoords = *itCoords;
0245                 maxLevel = currentLevel;
0246             }
0247         }
0248         ++currentLevel;
0249     }
0250     lineString.last().setDetail(startLevel);
0251 }
0252 
0253 bool GeoDataLineString::isEmpty() const
0254 {
0255     Q_D(const GeoDataLineString);
0256     return d->m_vector.isEmpty();
0257 }
0258 
0259 int GeoDataLineString::size() const
0260 {
0261     Q_D(const GeoDataLineString);
0262     return d->m_vector.size();
0263 }
0264 
0265 GeoDataCoordinates& GeoDataLineString::at( int pos )
0266 {
0267     detach();
0268 
0269     Q_D(GeoDataLineString);
0270     d->m_dirtyRange = true;
0271     d->m_dirtyBox = true;
0272     return d->m_vector[pos];
0273 }
0274 
0275 const GeoDataCoordinates& GeoDataLineString::at( int pos ) const
0276 {
0277     Q_D(const GeoDataLineString);
0278     return d->m_vector.at(pos);
0279 }
0280 
0281 GeoDataCoordinates& GeoDataLineString::operator[]( int pos )
0282 {
0283     detach();
0284 
0285     Q_D(GeoDataLineString);
0286     d->m_dirtyRange = true;
0287     d->m_dirtyBox = true;
0288     return d->m_vector[pos];
0289 }
0290 
0291 GeoDataLineString GeoDataLineString::mid(int pos, int length) const
0292 {
0293     GeoDataLineString substring;
0294     auto d = substring.d_func();
0295     d->m_vector = d_func()->m_vector.mid(pos, length);
0296     d->m_dirtyBox = true;
0297     d->m_dirtyRange = true;
0298     d->m_tessellationFlags = d_func()->m_tessellationFlags;
0299     d->m_extrude = d_func()->m_extrude;
0300     return substring;
0301 }
0302 
0303 const GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) const
0304 {
0305     Q_D(const GeoDataLineString);
0306     return d->m_vector[pos];
0307 }
0308 
0309 GeoDataCoordinates& GeoDataLineString::last()
0310 {
0311     detach();
0312 
0313     Q_D(GeoDataLineString);
0314     d->m_dirtyRange = true;
0315     d->m_dirtyBox = true;
0316     return d->m_vector.last();
0317 }
0318 
0319 GeoDataCoordinates& GeoDataLineString::first()
0320 {
0321     detach();
0322 
0323     Q_D(GeoDataLineString);
0324     return d->m_vector.first();
0325 }
0326 
0327 const GeoDataCoordinates& GeoDataLineString::last() const
0328 {
0329     Q_D(const GeoDataLineString);
0330     return d->m_vector.last();
0331 }
0332 
0333 const GeoDataCoordinates& GeoDataLineString::first() const
0334 {
0335     Q_D(const GeoDataLineString);
0336     return d->m_vector.first();
0337 }
0338 
0339 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::begin()
0340 {
0341     detach();
0342 
0343     Q_D(GeoDataLineString);
0344     return d->m_vector.begin();
0345 }
0346 
0347 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::begin() const
0348 {
0349     Q_D(const GeoDataLineString);
0350     return d->m_vector.constBegin();
0351 }
0352 
0353 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::end()
0354 {
0355     detach();
0356 
0357     Q_D(GeoDataLineString);
0358     return d->m_vector.end();
0359 }
0360 
0361 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::end() const
0362 {
0363     Q_D(const GeoDataLineString);
0364     return d->m_vector.constEnd();
0365 }
0366 
0367 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constBegin() const
0368 {
0369     Q_D(const GeoDataLineString);
0370     return d->m_vector.constBegin();
0371 }
0372 
0373 QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constEnd() const
0374 {
0375     Q_D(const GeoDataLineString);
0376     return d->m_vector.constEnd();
0377 }
0378 
0379 void GeoDataLineString::insert( int index, const GeoDataCoordinates& value )
0380 {
0381     detach();
0382 
0383     Q_D(GeoDataLineString);
0384     delete d->m_rangeCorrected;
0385     d->m_rangeCorrected = nullptr;
0386     d->m_dirtyRange = true;
0387     d->m_dirtyBox = true;
0388     d->m_vector.insert( index, value );
0389 }
0390 
0391 void GeoDataLineString::append ( const GeoDataCoordinates& value )
0392 {
0393     detach();
0394 
0395     Q_D(GeoDataLineString);
0396     delete d->m_rangeCorrected;
0397     d->m_rangeCorrected = nullptr;
0398     d->m_dirtyRange = true;
0399     d->m_dirtyBox = true;
0400     d->m_vector.append( value );
0401 }
0402 
0403 void GeoDataLineString::reserve(int size)
0404 {
0405     Q_D(GeoDataLineString);
0406     d->m_vector.reserve(size);
0407 }
0408 
0409 void GeoDataLineString::append(const QVector<GeoDataCoordinates>& values)
0410 {
0411     detach();
0412 
0413     Q_D(GeoDataLineString);
0414     delete d->m_rangeCorrected;
0415     d->m_rangeCorrected = nullptr;
0416     d->m_dirtyRange = true;
0417     d->m_dirtyBox = true;
0418 
0419     d->m_vector.append(values);
0420 }
0421 
0422 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataCoordinates& value )
0423 {
0424     detach();
0425 
0426     Q_D(GeoDataLineString);
0427     delete d->m_rangeCorrected;
0428     d->m_rangeCorrected = nullptr;
0429     d->m_dirtyRange = true;
0430     d->m_dirtyBox = true;
0431     d->m_vector.append( value );
0432     return *this;
0433 }
0434 
0435 GeoDataLineString& GeoDataLineString::operator << ( const GeoDataLineString& value )
0436 {
0437     detach();
0438 
0439     Q_D(GeoDataLineString);
0440     delete d->m_rangeCorrected;
0441     d->m_rangeCorrected = nullptr;
0442     d->m_dirtyRange = true;
0443     d->m_dirtyBox = true;
0444 
0445     QVector<GeoDataCoordinates>::const_iterator itCoords = value.constBegin();
0446     QVector<GeoDataCoordinates>::const_iterator itEnd = value.constEnd();
0447 
0448     d->m_vector.reserve(d->m_vector.size() + value.size());
0449     for( ; itCoords != itEnd; ++itCoords ) {
0450         d->m_vector.append( *itCoords );
0451     }
0452 
0453     return *this;
0454 }
0455 
0456 bool GeoDataLineString::operator==( const GeoDataLineString &other ) const
0457 {
0458     if ( !GeoDataGeometry::equals(other) ||
0459           size() != other.size() ||
0460           tessellate() != other.tessellate() ) {
0461         return false;
0462     }
0463 
0464     Q_D(const GeoDataLineString);
0465     const GeoDataLineStringPrivate* other_d = other.d_func();
0466 
0467     QVector<GeoDataCoordinates>::const_iterator itCoords = d->m_vector.constBegin();
0468     QVector<GeoDataCoordinates>::const_iterator otherItCoords = other_d->m_vector.constBegin();
0469     QVector<GeoDataCoordinates>::const_iterator itEnd = d->m_vector.constEnd();
0470     QVector<GeoDataCoordinates>::const_iterator otherItEnd = other_d->m_vector.constEnd();
0471 
0472     for ( ; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords ) {
0473         if ( *itCoords != *otherItCoords ) {
0474             return false;
0475         }
0476     }
0477 
0478     Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd );
0479     return true;
0480 }
0481 
0482 bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const
0483 {
0484     return !this->operator==(other);
0485 }
0486 
0487 void GeoDataLineString::clear()
0488 {
0489     detach();
0490 
0491     Q_D(GeoDataLineString);
0492     delete d->m_rangeCorrected;
0493     d->m_rangeCorrected = nullptr;
0494     d->m_dirtyRange = true;
0495     d->m_dirtyBox = true;
0496 
0497     d->m_vector.clear();
0498 }
0499 
0500 bool GeoDataLineString::isClosed() const
0501 {
0502     return false;
0503 }
0504 
0505 bool GeoDataLineString::tessellate() const
0506 {
0507     Q_D(const GeoDataLineString);
0508     return d->m_tessellationFlags.testFlag(Tessellate);
0509 }
0510 
0511 void GeoDataLineString::setTessellate( bool tessellate )
0512 {
0513     detach();
0514 
0515     Q_D(GeoDataLineString);
0516     // According to the KML reference the tesselation of line strings in Google Earth
0517     // is generally done along great circles. However for subsequent points that share
0518     // the same latitude the latitude circles are followed. Our Tesselate and RespectLatitude
0519     // Flags provide this behaviour. For true polygons the latitude circles don't get considered.
0520 
0521     if (tessellate) {
0522         d->m_tessellationFlags |= (Tessellate | RespectLatitudeCircle);
0523     } else {
0524         d->m_tessellationFlags &= ~(Tessellate | RespectLatitudeCircle);
0525     }
0526 }
0527 
0528 TessellationFlags GeoDataLineString::tessellationFlags() const
0529 {
0530     Q_D(const GeoDataLineString);
0531     return d->m_tessellationFlags;
0532 }
0533 
0534 void GeoDataLineString::setTessellationFlags( TessellationFlags f )
0535 {
0536     detach();
0537 
0538     Q_D(GeoDataLineString);
0539     d->m_tessellationFlags = f;
0540 }
0541 
0542 void GeoDataLineString::reverse()
0543 {
0544     detach();
0545 
0546     Q_D(GeoDataLineString);
0547     delete d->m_rangeCorrected;
0548     d->m_rangeCorrected = nullptr;
0549     d->m_dirtyRange = true;
0550     d->m_dirtyBox = true;
0551     std::reverse(begin(), end());
0552 }
0553 
0554 GeoDataLineString GeoDataLineString::toNormalized() const
0555 {
0556     Q_D(const GeoDataLineString);
0557 
0558     GeoDataLineString normalizedLineString;
0559 
0560     normalizedLineString.setTessellationFlags( tessellationFlags() );
0561 
0562     qreal lon;
0563     qreal lat;
0564 
0565     // FIXME: Think about how we can avoid unnecessary copies
0566     //        if the linestring stays the same.
0567     QVector<GeoDataCoordinates>::const_iterator end = d->m_vector.constEnd();
0568     for( QVector<GeoDataCoordinates>::const_iterator itCoords
0569           = d->m_vector.constBegin();
0570          itCoords != end;
0571          ++itCoords ) {
0572 
0573         itCoords->geoCoordinates( lon, lat );
0574         qreal alt = itCoords->altitude();
0575         GeoDataCoordinates::normalizeLonLat( lon, lat );
0576 
0577         GeoDataCoordinates normalizedCoords( *itCoords );
0578         normalizedCoords.set( lon, lat, alt );
0579         normalizedLineString << normalizedCoords;
0580     }
0581 
0582     return normalizedLineString;
0583 }
0584 
0585 GeoDataLineString GeoDataLineString::toRangeCorrected() const
0586 {
0587     Q_D(const GeoDataLineString);
0588 
0589     if (d->m_dirtyRange) {
0590 
0591         delete d->m_rangeCorrected;
0592 
0593         if( isClosed() ) {
0594             d->m_rangeCorrected = new GeoDataLinearRing(toPoleCorrected());
0595         } else {
0596             d->m_rangeCorrected = new GeoDataLineString(toPoleCorrected());
0597         }
0598         d->m_dirtyRange = false;
0599     }
0600 
0601     return *d->m_rangeCorrected;
0602 }
0603 
0604 QVector<GeoDataLineString*> GeoDataLineString::toDateLineCorrected() const
0605 {
0606     Q_D(const GeoDataLineString);
0607 
0608     QVector<GeoDataLineString*> lineStrings;
0609 
0610     d->toDateLineCorrected(*this, lineStrings);
0611 
0612     return lineStrings;
0613 }
0614 
0615 GeoDataLineString GeoDataLineString::toPoleCorrected() const
0616 {
0617     Q_D(const GeoDataLineString);
0618 
0619     if( isClosed() ) {
0620         GeoDataLinearRing poleCorrected;
0621         d->toPoleCorrected(*this, poleCorrected);
0622         return poleCorrected;
0623     } else {
0624         GeoDataLineString poleCorrected;
0625         d->toPoleCorrected(*this, poleCorrected);
0626         return poleCorrected;
0627     }
0628 }
0629 
0630 void GeoDataLineStringPrivate::toPoleCorrected( const GeoDataLineString& q, GeoDataLineString& poleCorrected ) const
0631 {
0632     poleCorrected.setTessellationFlags( q.tessellationFlags() );
0633 
0634     GeoDataCoordinates previousCoords;
0635     GeoDataCoordinates currentCoords;
0636 
0637     if ( q.isClosed() ) {
0638         if ( !( m_vector.first().isPole() ) &&
0639               ( m_vector.last().isPole() ) ) {
0640                 qreal firstLongitude = ( m_vector.first() ).longitude();
0641                 GeoDataCoordinates modifiedCoords( m_vector.last() );
0642                 modifiedCoords.setLongitude( firstLongitude );
0643                 poleCorrected << modifiedCoords;
0644         }
0645     }
0646 
0647     QVector<GeoDataCoordinates>::const_iterator itCoords = m_vector.constBegin();
0648     QVector<GeoDataCoordinates>::const_iterator itEnd = m_vector.constEnd();
0649 
0650     for( ; itCoords != itEnd; ++itCoords ) {
0651 
0652         currentCoords  = *itCoords;
0653 
0654         if ( itCoords == m_vector.constBegin() ) {
0655             previousCoords = currentCoords;
0656         }
0657 
0658         if ( currentCoords.isPole() ) {
0659             if ( previousCoords.isPole() ) {
0660                 continue;
0661             }
0662             else {
0663                 qreal previousLongitude = previousCoords.longitude();
0664                 GeoDataCoordinates currentModifiedCoords( currentCoords );
0665                 currentModifiedCoords.setLongitude( previousLongitude );
0666                 poleCorrected << currentModifiedCoords;
0667             }
0668         }
0669         else {
0670             if ( previousCoords.isPole() ) {
0671                 qreal currentLongitude = currentCoords.longitude();
0672                 GeoDataCoordinates previousModifiedCoords( previousCoords );
0673                 previousModifiedCoords.setLongitude( currentLongitude );
0674                 poleCorrected << previousModifiedCoords;
0675                 poleCorrected << currentCoords;
0676             }
0677             else {
0678                 // No poles at all. Nothing special to handle
0679                 poleCorrected << currentCoords;
0680             }
0681         }
0682         previousCoords = currentCoords;
0683     }
0684 
0685     if ( q.isClosed() ) {
0686         if (  ( m_vector.first().isPole() ) &&
0687              !( m_vector.last().isPole() ) ) {
0688                 qreal lastLongitude = ( m_vector.last() ).longitude();
0689                 GeoDataCoordinates modifiedCoords( m_vector.first() );
0690                 modifiedCoords.setLongitude( lastLongitude );
0691                 poleCorrected << modifiedCoords;
0692         }
0693     }
0694 }
0695 
0696 void GeoDataLineStringPrivate::toDateLineCorrected(
0697                            const GeoDataLineString & q,
0698                            QVector<GeoDataLineString*> & lineStrings
0699                            ) const
0700 {
0701     const bool isClosed = q.isClosed();
0702 
0703     const QVector<GeoDataCoordinates>::const_iterator itStartPoint = q.constBegin();
0704     const QVector<GeoDataCoordinates>::const_iterator itEndPoint = q.constEnd();
0705     QVector<GeoDataCoordinates>::const_iterator itPoint = itStartPoint;
0706     QVector<GeoDataCoordinates>::const_iterator itPreviousPoint = itPoint;
0707 
0708     TessellationFlags f = q.tessellationFlags();
0709 
0710     GeoDataLineString * unfinishedLineString = nullptr;
0711 
0712     GeoDataLineString * dateLineCorrected = isClosed ? new GeoDataLinearRing( f )
0713                                                      : new GeoDataLineString( f );
0714 
0715     qreal previousLon = 0.0;
0716     int previousSign = 1;
0717 
0718     bool unfinished = false;
0719 
0720     for (; itPoint != itEndPoint; ++itPoint ) {
0721         const qreal currentLon = itPoint->longitude();
0722 
0723         int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ;
0724 
0725         if( itPoint == q.constBegin() ) {
0726             previousSign = currentSign;
0727             previousLon  = currentLon;
0728         }
0729 
0730         // If we are crossing the date line ...
0731         if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) {
0732 
0733             unfinished = !unfinished;
0734 
0735             GeoDataCoordinates previousTemp;
0736             GeoDataCoordinates currentTemp;
0737 
0738             interpolateDateLine( *itPreviousPoint, *itPoint,
0739                                  previousTemp, currentTemp, q.tessellationFlags() );
0740 
0741             *dateLineCorrected << previousTemp;
0742 
0743             if ( isClosed && unfinished ) {
0744                 // If it's a linear ring and if it crossed the IDL only once then
0745                 // store the current string inside the unfinishedLineString for later use ...
0746                 unfinishedLineString = dateLineCorrected;
0747                 // ... and start a new linear ring for now.
0748                 dateLineCorrected = new GeoDataLinearRing( f );
0749             }
0750             else {
0751                 // Now it can only be a (finished) line string or a finished linear ring.
0752                 // Store it in the vector  if the size is not zero.
0753                 if ( dateLineCorrected->size() > 0 ) {
0754                     lineStrings << dateLineCorrected;
0755                 }
0756                 else {
0757                     // Or delete it.
0758                     delete dateLineCorrected;
0759                 }
0760 
0761                 // If it's a finished linear ring restore the "remembered" unfinished String
0762                 if ( isClosed && !unfinished && unfinishedLineString ) {
0763                     dateLineCorrected = unfinishedLineString;
0764                 }
0765                 else {
0766                     // if it's a line string just create a new line string.
0767                     dateLineCorrected = new GeoDataLineString( f );
0768                 }
0769             }
0770 
0771             *dateLineCorrected << currentTemp;
0772             *dateLineCorrected << *itPoint;
0773 
0774         }
0775         else {
0776             *dateLineCorrected << *itPoint;
0777         }
0778 
0779         previousSign = currentSign;
0780         previousLon  = currentLon;
0781         itPreviousPoint = itPoint;
0782     }
0783 
0784     // If the line string doesn't cross the dateline an even number of times
0785     // then need to take care of the data stored in the unfinishedLineString
0786     if ( unfinished && unfinishedLineString && !unfinishedLineString->isEmpty() ) {
0787         *dateLineCorrected << *unfinishedLineString;
0788         delete unfinishedLineString;
0789     }
0790 
0791     lineStrings << dateLineCorrected;
0792 }
0793 
0794 const GeoDataLatLonAltBox& GeoDataLineString::latLonAltBox() const
0795 {
0796     Q_D(const GeoDataLineString);
0797 
0798     // GeoDataLatLonAltBox::fromLineString is very expensive
0799     // that's why we recreate it only if the m_dirtyBox
0800     // is TRUE.
0801     // DO NOT REMOVE THIS CONSTRUCT OR MARBLE WILL BE SLOW.
0802     if (d->m_dirtyBox) {
0803         d->m_latLonAltBox = GeoDataLatLonAltBox::fromLineString(*this);
0804         d->m_dirtyBox = false;
0805     }
0806 
0807     return d->m_latLonAltBox;
0808 }
0809 
0810 qreal GeoDataLineString::length( qreal planetRadius, int offset ) const
0811 {
0812     if( offset < 0 || offset >= size() ) {
0813         return 0;
0814     }
0815 
0816     Q_D(const GeoDataLineString);
0817     qreal length = 0.0;
0818     QVector<GeoDataCoordinates> const & vector = d->m_vector;
0819     int const start = qMax(offset+1, 1);
0820     int const end = d->m_vector.size();
0821     for( int i=start; i<end; ++i )
0822     {
0823         length += vector[i-1].sphericalDistanceTo(vector[i]);
0824     }
0825 
0826     return planetRadius * length;
0827 }
0828 
0829 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& pos )
0830 {
0831     detach();
0832 
0833     Q_D(GeoDataLineString);
0834     delete d->m_rangeCorrected;
0835     d->m_rangeCorrected = nullptr;
0836     d->m_dirtyRange = true;
0837     d->m_dirtyBox = true;
0838     return d->m_vector.erase( pos );
0839 }
0840 
0841 QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( const QVector<GeoDataCoordinates>::Iterator& begin,
0842                                                                  const QVector<GeoDataCoordinates>::Iterator& end )
0843 {
0844     detach();
0845 
0846     Q_D(GeoDataLineString);
0847     delete d->m_rangeCorrected;
0848     d->m_rangeCorrected = nullptr;
0849     d->m_dirtyRange = true;
0850     d->m_dirtyBox = true;
0851     return d->m_vector.erase( begin, end );
0852 }
0853 
0854 void GeoDataLineString::remove ( int i )
0855 {
0856     detach();
0857 
0858     Q_D(GeoDataLineString);
0859     d->m_dirtyRange = true;
0860     d->m_dirtyBox = true;
0861     d->m_vector.remove( i );
0862 }
0863 
0864 GeoDataLineString GeoDataLineString::optimized () const
0865 {
0866     Q_D(const GeoDataLineString);
0867 
0868     if( isClosed() ) {
0869         GeoDataLinearRing linearRing(*this);
0870         d->optimize(linearRing);
0871         return linearRing;
0872     } else {
0873         GeoDataLineString lineString(*this);
0874         d->optimize(lineString);
0875         return lineString;
0876     }
0877 }
0878 
0879 QVariantList GeoDataLineString::toVariantList() const
0880 {
0881     Q_D(const GeoDataLineString);
0882 
0883     QVariantList variantList;
0884     for( const GeoDataCoordinates & itCoords : qAsConst(d->m_vector) ) {
0885         QVariantMap map;
0886         map.insert("lon", itCoords.longitude(GeoDataCoordinates::Degree));
0887         map.insert("lat", itCoords.latitude(GeoDataCoordinates::Degree));
0888         map.insert("alt", itCoords.altitude());
0889         variantList << map;
0890     }
0891 
0892     if (isClosed()) {
0893         QVariantMap map;
0894         map.insert("lon", d->m_vector.first().longitude(GeoDataCoordinates::Degree));
0895         map.insert("lat", d->m_vector.first().latitude(GeoDataCoordinates::Degree));
0896         map.insert("alt", d->m_vector.first().altitude());
0897         variantList << map;
0898     }
0899 
0900     return variantList;
0901 }
0902 
0903 void GeoDataLineString::pack( QDataStream& stream ) const
0904 {
0905     Q_D(const GeoDataLineString);
0906 
0907     GeoDataGeometry::pack( stream );
0908 
0909     stream << size();
0910     stream << (qint32)(d->m_tessellationFlags);
0911 
0912     for( QVector<GeoDataCoordinates>::const_iterator iterator
0913           = d->m_vector.constBegin();
0914          iterator != d->m_vector.constEnd();
0915          ++iterator ) {
0916         mDebug() << "innerRing: size" << d->m_vector.size();
0917         GeoDataCoordinates coord = ( *iterator );
0918         coord.pack( stream );
0919     }
0920 
0921 }
0922 
0923 void GeoDataLineString::unpack( QDataStream& stream )
0924 {
0925     detach();
0926 
0927     Q_D(GeoDataLineString);
0928 
0929     GeoDataGeometry::unpack( stream );
0930     qint32 size;
0931     qint32 tessellationFlags;
0932 
0933     stream >> size;
0934     stream >> tessellationFlags;
0935 
0936     d->m_tessellationFlags = (TessellationFlags)(tessellationFlags);
0937 
0938     d->m_vector.reserve(d->m_vector.size() + size);
0939 
0940     for(qint32 i = 0; i < size; i++ ) {
0941         GeoDataCoordinates coord;
0942         coord.unpack( stream );
0943         d->m_vector.append( coord );
0944     }
0945 }
0946 
0947 }