File indexing completed on 2025-01-05 03:58:57
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 #include "GeoDataLineString.h" 0008 #include "GeoDataLineString_p.h" 0009 0010 #include <QDataStream> 0011 #include <QVariant> 0012 0013 #include "GeoDataLinearRing.h" 0014 #include "GeoDataTypes.h" 0015 #include "Quaternion.h" 0016 0017 #include "digikam_debug.h" 0018 0019 namespace Marble 0020 { 0021 GeoDataLineString::GeoDataLineString( TessellationFlags f ) 0022 : GeoDataGeometry( new GeoDataLineStringPrivate( f ) ) 0023 { 0024 // qCDebug(DIGIKAM_MARBLE_LOG) << "1) GeoDataLineString created:" << p(); 0025 } 0026 0027 GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv ) 0028 : GeoDataGeometry( priv ) 0029 { 0030 // qCDebug(DIGIKAM_MARBLE_LOG) << "2) GeoDataLineString created:" << p(); 0031 } 0032 0033 GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other ) 0034 : GeoDataGeometry( other ) 0035 { 0036 // qCDebug(DIGIKAM_MARBLE_LOG) << "3) GeoDataLineString created:" << p(); 0037 } 0038 0039 GeoDataLineString::~GeoDataLineString() 0040 { 0041 #ifdef DEBUG_GEODATA 0042 qCDebug(DIGIKAM_MARBLE_LOG) << "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 // qCDebug(DIGIKAM_MARBLE_LOG) << Q_FUNC_INFO; 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 // qCDebug(DIGIKAM_MARBLE_LOG) << "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 qCDebug(DIGIKAM_MARBLE_LOG) << "SRC" << previousCoords.toString(); 0114 qCDebug(DIGIKAM_MARBLE_LOG) << "TAR" << currentCoords.toString(); 0115 qCDebug(DIGIKAM_MARBLE_LOG) << "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(QLatin1String("lon"), itCoords.longitude(GeoDataCoordinates::Degree)); 0887 map.insert(QLatin1String("lat"), itCoords.latitude(GeoDataCoordinates::Degree)); 0888 map.insert(QLatin1String("alt"), itCoords.altitude()); 0889 variantList << map; 0890 } 0891 0892 if (isClosed()) { 0893 QVariantMap map; 0894 map.insert(QLatin1String("lon"), d->m_vector.first().longitude(GeoDataCoordinates::Degree)); 0895 map.insert(QLatin1String("lat"), d->m_vector.first().latitude(GeoDataCoordinates::Degree)); 0896 map.insert(QLatin1String("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 qCDebug(DIGIKAM_MARBLE_LOG) << "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 }