GeoDataLineString.cpp 25.6 KB
Newer Older
Torsten Rahn's avatar
Torsten Rahn committed
1
//
2
// This file is part of the Marble Virtual Globe.
Torsten Rahn's avatar
Torsten Rahn committed
3 4 5 6 7 8
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
// Copyright 2008      Torsten Rahn   <rahn@kde.org>
9
// Copyright 2009 Patrick Spendrin <ps_ml@gmx.de>
Torsten Rahn's avatar
Torsten Rahn committed
10 11 12 13
//


#include "GeoDataLineString.h"
Bruno Bigras's avatar
Bruno Bigras committed
14
#include "GeoDataLineString_p.h"
Torsten Rahn's avatar
Torsten Rahn committed
15

16
#include "GeoDataLinearRing.h"
Torsten Rahn's avatar
Torsten Rahn committed
17
#include "MarbleMath.h"
18
#include "Quaternion.h"
19
#include "MarbleDebug.h"
Torsten Rahn's avatar
Torsten Rahn committed
20

Anke Boersma's avatar
Anke Boersma committed
21 22
#include <QDataStream>

23

24 25
namespace Marble
{
26
GeoDataLineString::GeoDataLineString( TessellationFlags f )
27 28
  : GeoDataGeometry( new GeoDataLineStringPrivate( f ) )
{
Patrick Spendrin's avatar
Patrick Spendrin committed
29
//    mDebug() << "1) GeoDataLineString created:" << p();
30 31
}

32
GeoDataLineString::GeoDataLineString( GeoDataLineStringPrivate* priv )
33 34
  : GeoDataGeometry( priv )
{
Patrick Spendrin's avatar
Patrick Spendrin committed
35
//    mDebug() << "2) GeoDataLineString created:" << p();
36 37
}

38
GeoDataLineString::GeoDataLineString( const GeoDataGeometry & other )
39 40
  : GeoDataGeometry( other )
{
Patrick Spendrin's avatar
Patrick Spendrin committed
41
//    mDebug() << "3) GeoDataLineString created:" << p();
42 43 44 45 46
}

GeoDataLineString::~GeoDataLineString()
{
#ifdef DEBUG_GEODATA
Patrick Spendrin's avatar
Patrick Spendrin committed
47
    mDebug() << "delete Linestring";
48
#endif
49
}
50

51 52 53 54 55 56
GeoDataLineStringPrivate* GeoDataLineString::p()
{
    return static_cast<GeoDataLineStringPrivate*>(d);
}

const GeoDataLineStringPrivate* GeoDataLineString::p() const
Torsten Rahn's avatar
Torsten Rahn committed
57
{
58 59 60
    return static_cast<GeoDataLineStringPrivate*>(d);
}

61 62 63 64
void GeoDataLineStringPrivate::interpolateDateLine( const GeoDataCoordinates & previousCoords,
                                                    const GeoDataCoordinates & currentCoords,
                                                    GeoDataCoordinates & previousAtDateLine,
                                                    GeoDataCoordinates & currentAtDateLine,
65
                                                    TessellationFlags f ) const
66 67 68
{
    GeoDataCoordinates dateLineCoords;

Patrick Spendrin's avatar
Patrick Spendrin committed
69
//    mDebug() << Q_FUNC_INFO;
70 71 72 73 74

    if ( f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude() ) {
        dateLineCoords = currentCoords;
    }
    else {
Dennis Nienhüser's avatar
Dennis Nienhüser committed
75
        int recursionCounter = 0;
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
        dateLineCoords = findDateLine( previousCoords, currentCoords, recursionCounter );
    }

    previousAtDateLine = dateLineCoords;
    currentAtDateLine = dateLineCoords;

    if ( previousCoords.longitude() < 0 ) {
        previousAtDateLine.setLongitude( -M_PI );
        currentAtDateLine.setLongitude( +M_PI );
    }
    else {
        previousAtDateLine.setLongitude( +M_PI );
        currentAtDateLine.setLongitude( -M_PI );
    }
}

GeoDataCoordinates GeoDataLineStringPrivate::findDateLine( const GeoDataCoordinates & previousCoords,
                                             const GeoDataCoordinates & currentCoords,
94
                                             int recursionCounter ) const
95 96 97 98 99 100 101 102
{
    int currentSign = ( currentCoords.longitude() < 0.0 ) ? -1 : +1 ;
    int previousSign = ( previousCoords.longitude() < 0.0 ) ? -1 : +1 ;

    qreal longitudeDiff =   fabs( previousSign * M_PI  - previousCoords.longitude() )
                          + fabs( currentSign * M_PI - currentCoords.longitude() );

    if ( longitudeDiff < 0.001 || recursionCounter == 100 ) {
103
//        mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff;
104 105 106 107 108 109 110 111 112
        return currentCoords;
    }
    ++recursionCounter;

    qreal  lon = 0.0;
    qreal  lat = 0.0;

    qreal altDiff = currentCoords.altitude() - previousCoords.altitude();

113
    const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), 0.5 );
114 115 116 117 118 119 120 121 122
    itpos.getSpherical( lon, lat );

    qreal altitude = previousCoords.altitude() + 0.5 * altDiff;

    GeoDataCoordinates interpolatedCoords( lon, lat, altitude );

    int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ;

/*
Patrick Spendrin's avatar
Patrick Spendrin committed
123 124 125
    mDebug() << "SRC" << previousCoords.toString();
    mDebug() << "TAR" << currentCoords.toString();
    mDebug() << "IPC" << interpolatedCoords.toString();
126 127 128 129 130 131 132 133 134
*/

    if ( interpolatedSign != currentSign ) {
        return findDateLine( interpolatedCoords, currentCoords, recursionCounter );
    }

    return findDateLine( previousCoords, interpolatedCoords, recursionCounter );
}

135
quint8 GeoDataLineStringPrivate::levelForResolution(qreal resolution) const {
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    if (m_previousResolution == resolution) return m_level;

    m_previousResolution = resolution;

    if (resolution < 0.0000005) m_level = 17;
    else if (resolution < 0.0000010) m_level = 16;
    else if (resolution < 0.0000020) m_level = 15;
    else if (resolution < 0.0000040) m_level = 14;
    else if (resolution < 0.0000080) m_level = 13;
    else if (resolution < 0.0000160) m_level = 12;
    else if (resolution < 0.0000320) m_level = 11;
    else if (resolution < 0.0000640) m_level = 10;
    else if (resolution < 0.0001280) m_level = 9;
    else if (resolution < 0.0002560) m_level = 8;
    else if (resolution < 0.0005120) m_level = 7;
    else if (resolution < 0.0010240) m_level = 6;
    else if (resolution < 0.0020480) m_level = 5;
    else if (resolution < 0.0040960) m_level = 4;
    else if (resolution < 0.0081920) m_level = 3;
    else if (resolution < 0.0163840) m_level = 2;
    else m_level =  1;

    return m_level;
}

qreal GeoDataLineStringPrivate::resolutionForLevel(int level) const {
    switch (level) {
        case 0:
            return 0.0655360;
            break;
        case 1:
            return 0.0327680;
            break;
        case 2:
            return 0.0163840;
            break;
        case 3:
            return 0.0081920;
            break;
        case 4:
            return 0.0040960;
            break;
        case 5:
            return 0.0020480;
            break;
        case 6:
            return 0.0010240;
            break;
        case 7:
            return 0.0005120;
            break;
        case 8:
            return 0.0002560;
            break;
        case 9:
            return 0.0001280;
            break;
        case 10:
            return 0.0000640;
            break;
        case 11:
            return 0.0000320;
            break;
        case 12:
            return 0.0000160;
            break;
        case 13:
            return 0.0000080;
            break;
        case 14:
            return 0.0000040;
            break;
        case 15:
            return 0.0000020;
            break;
        case 16:
            return 0.0000010;
            break;
        default:
        case 17:
            return 0.0000005;
            break;
    }
}

void GeoDataLineStringPrivate::optimize (GeoDataLineString& lineString) const
{

    QVector<GeoDataCoordinates>::iterator itCoords = lineString.begin();
    QVector<GeoDataCoordinates>::const_iterator itEnd = lineString.constEnd();

    if (lineString.size() < 2) return;

    // Calculate the least non-zero detail-level by checking the bounding box
230
    quint8 startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 );
231

232 233
    quint8 currentLevel = startLevel;
    quint8 maxLevel = startLevel;
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    GeoDataCoordinates currentCoords;
    lineString.first().setDetail(startLevel);

    // Iterate through the linestring to assign different detail levels to the nodes.
    // In general the first and last node should have the start level assigned as
    // a detail level.
    // Starting from the first node the algorithm picks those nodes which
    // have a distance from each other that is just above the resolution that is
    // associated with the start level (which we use as a "current level").
    // Each of those nodes get the current level assigned as the detail level.
    // After iterating through the linestring we increment the current level value
    // and starting again with the first node we assign detail values in a similar way
    // to the remaining nodes which have no final detail level assigned yet.
    // We do as many iterations through the lineString as needed and bump up the
    // current level until all nodes have a non-zero detail level assigned.

    while ( currentLevel  < 16 && currentLevel <= maxLevel + 1 ) {
        itCoords = lineString.begin();

        currentCoords = *itCoords;
        ++itCoords;

        for( ; itCoords != itEnd; ++itCoords) {
            if (itCoords->detail() != 0 && itCoords->detail() < currentLevel) continue;

            if ( currentLevel == startLevel && (itCoords->longitude() == -M_PI || itCoords->longitude() == M_PI
                || itCoords->latitude() < -89 * DEG2RAD || itCoords->latitude() > 89 * DEG2RAD)) {
                itCoords->setDetail(startLevel);
                currentCoords = *itCoords;
                maxLevel = currentLevel;
                continue;
            }
            if (distanceSphere( currentCoords, *itCoords ) < resolutionForLevel(currentLevel + 1)) {
                itCoords->setDetail(currentLevel + 1);
            }
            else {
                itCoords->setDetail(currentLevel);
                currentCoords = *itCoords;
                maxLevel = currentLevel;
            }
        }
        ++currentLevel;
    }
    lineString.last().setDetail(startLevel);
}

Torsten Rahn's avatar
Torsten Rahn committed
280 281 282 283
bool GeoDataLineString::isEmpty() const
{
    return p()->m_vector.isEmpty();
}
284

285 286 287 288
int GeoDataLineString::size() const
{
    return p()->m_vector.size();
}
289

290 291 292
GeoDataCoordinates& GeoDataLineString::at( int pos )
{
    GeoDataGeometry::detach();
293 294
    p()->m_dirtyRange = true;
    p()->m_dirtyBox = true;
295 296
    return p()->m_vector[ pos ];
}
Torsten Rahn's avatar
Torsten Rahn committed
297

298
const GeoDataCoordinates& GeoDataLineString::at( int pos ) const
299
{
300
    return p()->m_vector.at( pos );
301 302
}

303
GeoDataCoordinates& GeoDataLineString::operator[]( int pos )
304
{
305
    GeoDataGeometry::detach();
306 307
    p()->m_dirtyRange = true;
    p()->m_dirtyBox = true;
308
    return p()->m_vector[ pos ];
309 310
}

311
const GeoDataCoordinates& GeoDataLineString::operator[]( int pos ) const
Torsten Rahn's avatar
Torsten Rahn committed
312
{
313
    return p()->m_vector[ pos ];
Torsten Rahn's avatar
Torsten Rahn committed
314 315
}

316
GeoDataCoordinates& GeoDataLineString::last()
Torsten Rahn's avatar
Torsten Rahn committed
317
{
318
    GeoDataGeometry::detach();
319 320
    p()->m_dirtyRange = true;
    p()->m_dirtyBox = true;
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    return p()->m_vector.last();
}

GeoDataCoordinates& GeoDataLineString::first()
{
    GeoDataGeometry::detach();
    return p()->m_vector.first();
}

const GeoDataCoordinates& GeoDataLineString::last() const
{
    return p()->m_vector.last();
}

const GeoDataCoordinates& GeoDataLineString::first() const
{
    return p()->m_vector.first();
}

QVector<GeoDataCoordinates>::Iterator GeoDataLineString::begin()
{
    GeoDataGeometry::detach();
    return p()->m_vector.begin();
}

346 347 348 349 350
QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::begin() const
{
    return p()->m_vector.constBegin();
}

351 352 353 354 355 356
QVector<GeoDataCoordinates>::Iterator GeoDataLineString::end()
{
    GeoDataGeometry::detach();
    return p()->m_vector.end();
}

357 358 359 360 361
QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::end() const
{
    return p()->m_vector.constEnd();
}

362 363 364
QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constBegin() const
{
    return p()->m_vector.constBegin();
365 366
}

367
QVector<GeoDataCoordinates>::ConstIterator GeoDataLineString::constEnd() const
368
{
369 370
    return p()->m_vector.constEnd();
}
371

372 373 374 375 376 377 378 379 380 381 382
void GeoDataLineString::insert( int index, const GeoDataCoordinates& value )
{
    GeoDataGeometry::detach();
    GeoDataLineStringPrivate* d = p();
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    d->m_vector.insert( index, value );
}

383 384 385
void GeoDataLineString::append ( const GeoDataCoordinates& value )
{
    GeoDataGeometry::detach();
386
    GeoDataLineStringPrivate* d = p();
387 388
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
389 390 391
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    d->m_vector.append( value );
392 393 394 395 396
}

GeoDataLineString& GeoDataLineString::operator << ( const GeoDataCoordinates& value )
{
    GeoDataGeometry::detach();
397
    GeoDataLineStringPrivate* d = p();
398 399
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
400 401 402
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    d->m_vector.append( value );
403 404
    return *this;
}
405

406 407 408
GeoDataLineString& GeoDataLineString::operator << ( const GeoDataLineString& value )
{
    GeoDataGeometry::detach();
409
    GeoDataLineStringPrivate* d = p();
410 411
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
412 413
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
414 415 416 417 418

    QVector<GeoDataCoordinates>::const_iterator itCoords = value.constBegin();
    QVector<GeoDataCoordinates>::const_iterator itEnd = value.constEnd();

    for( ; itCoords != itEnd; ++itCoords ) {
419
        d->m_vector.append( *itCoords );
420 421 422 423 424
    }

    return *this;
}

425 426 427 428
bool GeoDataLineString::operator==( const GeoDataLineString &other ) const
{
    if ( !GeoDataGeometry::equals(other) ||
          size() != other.size() ||
429 430 431
          tessellate() != other.tessellate() ) {
        return false;
    }
432

433 434
    const GeoDataLineStringPrivate* d = p();
    const GeoDataLineStringPrivate* other_d = other.p();
435 436 437 438 439 440 441

    QVector<GeoDataCoordinates>::const_iterator itCoords = d->m_vector.constBegin();
    QVector<GeoDataCoordinates>::const_iterator otherItCoords = other_d->m_vector.constBegin();
    QVector<GeoDataCoordinates>::const_iterator itEnd = d->m_vector.constEnd();
    QVector<GeoDataCoordinates>::const_iterator otherItEnd = other_d->m_vector.constEnd();

    for ( ; itCoords != itEnd && otherItCoords != otherItEnd; ++itCoords, ++otherItCoords ) {
442
        if ( *itCoords != *otherItCoords ) {
443
            return false;
444
        }
445 446
    }

447
    Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd );
448 449 450 451 452 453 454 455
    return true;
}

bool GeoDataLineString::operator!=( const GeoDataLineString &other ) const
{
    return !this->operator==(other);
}

456 457 458
void GeoDataLineString::clear()
{
    GeoDataGeometry::detach();
459
    GeoDataLineStringPrivate* d = p();
460 461
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
462 463
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
464

465
    d->m_vector.clear();
Torsten Rahn's avatar
Torsten Rahn committed
466 467
}

468 469
bool GeoDataLineString::isClosed() const
{
470
    return false;
471 472
}

473 474
bool GeoDataLineString::tessellate() const
{
475
    return p()->m_tessellationFlags.testFlag(Tessellate);
476 477 478 479
}

void GeoDataLineString::setTessellate( bool tessellate )
{
480
    GeoDataGeometry::detach();
481 482
    // According to the KML reference the tesselation of line strings in Google Earth
    // is generally done along great circles. However for subsequent points that share
483
    // the same latitude the latitude circles are followed. Our Tesselate and RespectLatitude
484
    // Flags provide this behaviour. For true polygons the latitude circles don't get considered.
485

486
    if ( tessellate ) {
487 488
        p()->m_tessellationFlags |= Tessellate;
        p()->m_tessellationFlags |= RespectLatitudeCircle;
489
    } else {
490 491
        p()->m_tessellationFlags ^= Tessellate;
        p()->m_tessellationFlags ^= RespectLatitudeCircle;
492 493 494 495 496
    }
}

TessellationFlags GeoDataLineString::tessellationFlags() const
{
497
    return p()->m_tessellationFlags;
498 499 500 501
}

void GeoDataLineString::setTessellationFlags( TessellationFlags f )
{
502
    p()->m_tessellationFlags = f;
503 504
}

505
GeoDataLineString GeoDataLineString::toNormalized() const
Torsten Rahn's avatar
 
Torsten Rahn committed
506
{
507 508 509 510 511 512 513
    GeoDataLineString normalizedLineString;

    normalizedLineString.setTessellationFlags( tessellationFlags() );

    qreal lon;
    qreal lat;

514 515
    // FIXME: Think about how we can avoid unnecessary copies
    //        if the linestring stays the same.
516
    QVector<GeoDataCoordinates>::const_iterator end = p()->m_vector.constEnd();
517 518
    for( QVector<GeoDataCoordinates>::const_iterator itCoords
          = p()->m_vector.constBegin();
519
         itCoords != end;
520 521 522
         ++itCoords ) {

        itCoords->geoCoordinates( lon, lat );
Torsten Rahn's avatar
Torsten Rahn committed
523
        qreal alt = itCoords->altitude();
524 525 526
        GeoDataCoordinates::normalizeLonLat( lon, lat );

        GeoDataCoordinates normalizedCoords( *itCoords );
Torsten Rahn's avatar
Torsten Rahn committed
527
        normalizedCoords.set( lon, lat, alt );
528
        normalizedLineString << normalizedCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
529
    }
530 531 532 533

    return normalizedLineString;
}

534
GeoDataLineString GeoDataLineString::toRangeCorrected() const
535 536 537
{
    if ( p()->m_dirtyRange ) {

538
        delete p()->m_rangeCorrected;
539

540 541 542 543 544
        if( isClosed() ) {
            p()->m_rangeCorrected = new GeoDataLinearRing( toPoleCorrected() );
        } else {
            p()->m_rangeCorrected = new GeoDataLineString( toPoleCorrected() );
        }
545
        p()->m_dirtyRange = false;
546
    }
547

548
    return *p()->m_rangeCorrected;
549 550 551 552 553 554 555 556 557 558 559
}

QVector<GeoDataLineString*> GeoDataLineString::toDateLineCorrected() const
{
    QVector<GeoDataLineString*> lineStrings;

    p()->toDateLineCorrected( *this, lineStrings );

    return lineStrings;
}

560 561
GeoDataLineString GeoDataLineString::toPoleCorrected() const
{
562 563 564 565 566 567 568 569 570
    if( isClosed() ) {
        GeoDataLinearRing poleCorrected;
        p()->toPoleCorrected( *this, poleCorrected );
        return poleCorrected;
    } else {
        GeoDataLineString poleCorrected;
        p()->toPoleCorrected( *this, poleCorrected );
        return poleCorrected;
    }
571
}
Torsten Rahn's avatar
 
Torsten Rahn committed
572

573
void GeoDataLineStringPrivate::toPoleCorrected( const GeoDataLineString& q, GeoDataLineString& poleCorrected ) const
574 575
{
    poleCorrected.setTessellationFlags( q.tessellationFlags() );
576

Torsten Rahn's avatar
 
Torsten Rahn committed
577 578 579
    GeoDataCoordinates previousCoords;
    GeoDataCoordinates currentCoords;

580 581 582 583 584
    if ( q.isClosed() ) {
        if ( !( m_vector.first().isPole() ) &&
              ( m_vector.last().isPole() ) ) {
                qreal firstLongitude = ( m_vector.first() ).longitude();
                GeoDataCoordinates modifiedCoords( m_vector.last() );
Torsten Rahn's avatar
 
Torsten Rahn committed
585
                modifiedCoords.setLongitude( firstLongitude );
586
                poleCorrected << modifiedCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
587 588 589
        }
    }

590 591 592 593
    QVector<GeoDataCoordinates>::const_iterator itCoords = m_vector.constBegin();
    QVector<GeoDataCoordinates>::const_iterator itEnd = m_vector.constEnd();

    for( ; itCoords != itEnd; ++itCoords ) {
Torsten Rahn's avatar
 
Torsten Rahn committed
594 595 596

        currentCoords  = *itCoords;

597
        if ( itCoords == m_vector.constBegin() ) {
Torsten Rahn's avatar
 
Torsten Rahn committed
598 599 600 601 602 603 604 605 606 607 608
            previousCoords = currentCoords;
        }

        if ( currentCoords.isPole() ) {
            if ( previousCoords.isPole() ) {
                continue;
            }
            else {
                qreal previousLongitude = previousCoords.longitude();
                GeoDataCoordinates currentModifiedCoords( currentCoords );
                currentModifiedCoords.setLongitude( previousLongitude );
609
                poleCorrected << currentModifiedCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
610 611 612 613 614 615 616
            }
        }
        else {
            if ( previousCoords.isPole() ) {
                qreal currentLongitude = currentCoords.longitude();
                GeoDataCoordinates previousModifiedCoords( previousCoords );
                previousModifiedCoords.setLongitude( currentLongitude );
617 618
                poleCorrected << previousModifiedCoords;
                poleCorrected << currentCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
619 620
            }
            else {
621 622
                // No poles at all. Nothing special to handle
                poleCorrected << currentCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
623 624 625 626 627
            }
        }
        previousCoords = currentCoords;
    }

628 629 630 631 632
    if ( q.isClosed() ) {
        if (  ( m_vector.first().isPole() ) &&
             !( m_vector.last().isPole() ) ) {
                qreal lastLongitude = ( m_vector.last() ).longitude();
                GeoDataCoordinates modifiedCoords( m_vector.first() );
Torsten Rahn's avatar
 
Torsten Rahn committed
633
                modifiedCoords.setLongitude( lastLongitude );
634
                poleCorrected << modifiedCoords;
Torsten Rahn's avatar
 
Torsten Rahn committed
635 636
        }
    }
637 638
}

639 640 641
void GeoDataLineStringPrivate::toDateLineCorrected(
                           const GeoDataLineString & q,
                           QVector<GeoDataLineString*> & lineStrings
642
                           ) const
643
{
644
    const bool isClosed = q.isClosed();
645

646 647
    const QVector<GeoDataCoordinates>::const_iterator itStartPoint = q.constBegin();
    const QVector<GeoDataCoordinates>::const_iterator itEndPoint = q.constEnd();
648 649 650
    QVector<GeoDataCoordinates>::const_iterator itPoint = itStartPoint;
    QVector<GeoDataCoordinates>::const_iterator itPreviousPoint = itPoint;

651
    TessellationFlags f = q.tessellationFlags();
652 653 654

    GeoDataLineString * unfinishedLineString = 0;

Torsten Rahn's avatar
Torsten Rahn committed
655 656
    GeoDataLineString * dateLineCorrected = isClosed ? new GeoDataLinearRing( f )
                                                     : new GeoDataLineString( f );
657 658 659 660 661

    qreal currentLon = 0.0;
    qreal previousLon = 0.0;
    int previousSign = 1;

662 663
    bool unfinished = false;

664 665 666
    for (; itPoint != itEndPoint; ++itPoint ) {
        currentLon = itPoint->longitude();

667
        int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ;
668

669
        if( itPoint == q.constBegin() ) {
670 671 672 673
            previousSign = currentSign;
            previousLon  = currentLon;
        }

Torsten Rahn's avatar
Torsten Rahn committed
674
        // If we are crossing the date line ...
675
        if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) {
676 677 678 679 680 681

            unfinished = !unfinished;

            GeoDataCoordinates previousTemp;
            GeoDataCoordinates currentTemp;

682 683
            interpolateDateLine( *itPreviousPoint, *itPoint,
                                 previousTemp, currentTemp, q.tessellationFlags() );
684

685
            *dateLineCorrected << previousTemp;
686 687

            if ( isClosed && unfinished ) {
Torsten Rahn's avatar
Torsten Rahn committed
688 689
                // If it's a linear ring and if it crossed the IDL only once then
                // store the current string inside the unfinishedLineString for later use ...
690
                unfinishedLineString = dateLineCorrected;
Torsten Rahn's avatar
Torsten Rahn committed
691 692
                // ... and start a new linear ring for now.
                dateLineCorrected = new GeoDataLinearRing( f );
693 694
            }
            else {
Torsten Rahn's avatar
Torsten Rahn committed
695 696 697 698 699 700 701 702 703
                // Now it can only be a (finished) line string or a finished linear ring.
                // Store it in the vector  if the size is not zero.
                if ( dateLineCorrected->size() > 0 ) {
                    lineStrings << dateLineCorrected;
                }
                else {
                    // Or delete it.
                    delete dateLineCorrected;
                }
704

705
                // If it's a finished linear ring restore the "remembered" unfinished String
Torsten Rahn's avatar
Torsten Rahn committed
706
                if ( isClosed && !unfinished && unfinishedLineString ) {
707 708 709
                    dateLineCorrected = unfinishedLineString;
                }
                else {
Torsten Rahn's avatar
Torsten Rahn committed
710
                    // if it's a line string just create a new line string.
711 712
                    dateLineCorrected = new GeoDataLineString( f );
                }
713 714
            }

715 716
            *dateLineCorrected << currentTemp;
            *dateLineCorrected << *itPoint;
717

718 719
        }
        else {
720
            *dateLineCorrected << *itPoint;
721 722 723 724 725 726 727
        }

        previousSign = currentSign;
        previousLon  = currentLon;
        itPreviousPoint = itPoint;
    }

728 729 730 731 732 733 734
    // If the line string doesn't cross the dateline an even number of times
    // then need to take care of the data stored in the unfinishedLineString
    if ( unfinished && unfinishedLineString && !unfinishedLineString->isEmpty() ) {
        *dateLineCorrected << *unfinishedLineString;
        delete unfinishedLineString;
    }

735
    lineStrings << dateLineCorrected;
Torsten Rahn's avatar
 
Torsten Rahn committed
736 737
}

738
const GeoDataLatLonAltBox& GeoDataLineString::latLonAltBox() const
Torsten Rahn's avatar
Torsten Rahn committed
739
{
Torsten Rahn's avatar
 
Torsten Rahn committed
740 741 742 743 744 745 746 747 748 749
    // GeoDataLatLonAltBox::fromLineString is very expensive
    // that's why we recreate it only if the m_dirtyBox
    // is TRUE.
    // DO NOT REMOVE THIS CONSTRUCT OR MARBLE WILL BE SLOW.
    if ( p()->m_dirtyBox ) {
        p()->m_latLonAltBox = GeoDataLatLonAltBox::fromLineString( *this );
    }
    p()->m_dirtyBox = false;

    return p()->m_latLonAltBox;
Torsten Rahn's avatar
Torsten Rahn committed
750 751
}

752
qreal GeoDataLineString::length( qreal planetRadius, int offset ) const
Torsten Rahn's avatar
Torsten Rahn committed
753
{
754 755 756 757 758
    if( offset < 0 || offset >= size() ) {
        return 0;
    }

    qreal length = 0.0;
759 760 761 762
    QVector<GeoDataCoordinates> const & vector = p()->m_vector;
    int const start = qMax(offset+1, 1);
    int const end = p()->m_vector.size();
    for( int i=start; i<end; ++i )
Torsten Rahn's avatar
Torsten Rahn committed
763
    {
764
        length += distanceSphere( vector[i-1], vector[i] );
Torsten Rahn's avatar
Torsten Rahn committed
765 766 767
    }

    return planetRadius * length;
768
}
769

770
QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( QVector<GeoDataCoordinates>::Iterator pos )
Torsten Rahn's avatar
Torsten Rahn committed
771
{
772
    GeoDataGeometry::detach();
773
    GeoDataLineStringPrivate* d = p();
774 775
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
776 777 778
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    return d->m_vector.erase( pos );
Torsten Rahn's avatar
Torsten Rahn committed
779 780
}

781
QVector<GeoDataCoordinates>::Iterator GeoDataLineString::erase ( QVector<GeoDataCoordinates>::Iterator begin,
782
                                                                 QVector<GeoDataCoordinates>::Iterator end )
Torsten Rahn's avatar
Torsten Rahn committed
783
{
784
    GeoDataGeometry::detach();
785
    GeoDataLineStringPrivate* d = p();
786 787
    delete d->m_rangeCorrected;
    d->m_rangeCorrected = 0;
788 789 790
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    return d->m_vector.erase( begin, end );
Torsten Rahn's avatar
Torsten Rahn committed
791 792
}

Torsten Rahn's avatar
Torsten Rahn committed
793 794 795
void GeoDataLineString::remove ( int i )
{
    GeoDataGeometry::detach();
796 797 798 799
    GeoDataLineStringPrivate* d = p();
    d->m_dirtyRange = true;
    d->m_dirtyBox = true;
    d->m_vector.remove( i );
Torsten Rahn's avatar
Torsten Rahn committed
800 801
}

802 803 804 805 806 807 808 809 810 811 812 813 814
GeoDataLineString GeoDataLineString::optimized () const
{
    if( isClosed() ) {
        GeoDataLinearRing linearRing(*this);
        p()->optimize(linearRing);
        return linearRing;
    } else {
        GeoDataLineString lineString(*this);
        p()->optimize(lineString);
        return lineString;
    }
}

Patrick Spendrin's avatar
Patrick Spendrin committed
815 816 817 818 819
void GeoDataLineString::pack( QDataStream& stream ) const
{
    GeoDataGeometry::pack( stream );

    stream << size();
820
    stream << (qint32)(p()->m_tessellationFlags);
821 822 823

    for( QVector<GeoDataCoordinates>::const_iterator iterator
          = p()->m_vector.constBegin();
824
         iterator != p()->m_vector.constEnd();
Patrick Spendrin's avatar
Patrick Spendrin committed
825
         ++iterator ) {
Patrick Spendrin's avatar
Patrick Spendrin committed
826
        mDebug() << "innerRing: size" << p()->m_vector.size();
827 828
        GeoDataCoordinates coord = ( *iterator );
        coord.pack( stream );
Patrick Spendrin's avatar
Patrick Spendrin committed
829
    }
830

Patrick Spendrin's avatar
Patrick Spendrin committed
831 832 833 834
}

void GeoDataLineString::unpack( QDataStream& stream )
{
835
    GeoDataGeometry::detach();
Patrick Spendrin's avatar
Patrick Spendrin committed
836
    GeoDataGeometry::unpack( stream );
837 838 839
    qint32 size;
    qint32 tessellationFlags;

Patrick Spendrin's avatar
Patrick Spendrin committed
840
    stream >> size;
841 842
    stream >> tessellationFlags;

843
    p()->m_tessellationFlags = (TessellationFlags)(tessellationFlags);
844 845

    for(qint32 i = 0; i < size; i++ ) {
846 847
        GeoDataCoordinates coord;
        coord.unpack( stream );
848
        p()->m_vector.append( coord );
Patrick Spendrin's avatar
Patrick Spendrin committed
849 850
    }
}
851 852

}