Exemple #1
0
 qreal totalDistance() const
 {
     GeoDataLineString measure;
     GeoDataCoordinates sourcePosition(m_source.longitude(), m_source.latitude());
     GeoDataCoordinates targetPosition(m_target.longitude(), m_target.latitude());
     measure << sourcePosition << targetPosition;
     return measure.length(m_planetRadius);
 }
Exemple #2
0
qreal YoursRunner::distance( const GeoDataDocument* document ) const
{
    QVector<GeoDataFolder*> folders = document->folderList();
    foreach( const GeoDataFolder *folder, folders ) {
        foreach( const GeoDataPlacemark *placemark, folder->placemarkList() ) {
            GeoDataGeometry* geometry = placemark->geometry();
            if ( geometry->geometryId() == GeoDataLineStringId ) {
                GeoDataLineString* lineString = dynamic_cast<GeoDataLineString*>( geometry );
                Q_ASSERT( lineString && "Internal error: geometry ID does not match class type" );
                return lineString->length( EARTH_RADIUS );
            }
        }
    }
Exemple #3
0
GeoDataDocument* MapQuestRunner::parse( const QByteArray &content ) const
{
    QDomDocument xml;
    if ( !xml.setContent( content ) ) {
        mDebug() << "Cannot parse xml file with routing instructions.";
        return 0;
    }

    // mDebug() << xml.toString(2);
    QDomElement root = xml.documentElement();

    GeoDataDocument* result = new GeoDataDocument();
    result->setName( "MapQuest" );
    GeoDataPlacemark* routePlacemark = new GeoDataPlacemark;
    routePlacemark->setName( "Route" );

    GeoDataLineString* routeWaypoints = new GeoDataLineString;
    QDomNodeList shapePoints = root.elementsByTagName( "shapePoints" );
    if ( shapePoints.size() == 1 ) {
        QDomNodeList geometry = shapePoints.at( 0 ).toElement().elementsByTagName( "latLng" );
        for ( int i=0; i<geometry.size(); ++i ) {
            double const lat = geometry.item( i ).namedItem( "lat" ).toElement().text().toDouble();
            double const lon = geometry.item( i ).namedItem( "lng" ).toElement().text().toDouble();
            GeoDataCoordinates const position( lon, lat, 0.0, GeoDataCoordinates::Degree );
            routeWaypoints->append( position );
        }
    }
    routePlacemark->setGeometry( routeWaypoints );

    QTime time;
    time = time.addSecs( root.elementsByTagName( "time" ).at( 0 ).toElement().text().toInt() );
    qreal length = routeWaypoints->length( EARTH_RADIUS );
    const QString name = nameString( "MQ", length, time );
    const GeoDataExtendedData data = routeData( length, time );
    routePlacemark->setExtendedData( data );
    result->setName( name );
    result->append( routePlacemark );

    QMap<int,int> mapping;
    QDomNodeList maneuvers = root.elementsByTagName( "maneuverIndexes" );
    if ( maneuvers.size() == 1 ) {
        maneuvers = maneuvers.at( 0 ).childNodes();
        for ( int i=0; i<maneuvers.size(); ++i ) {
            mapping[i] = maneuvers.at( i ).toElement().text().toInt();
            if ( mapping[i] == routeWaypoints->size() ) {
                --mapping[i];
            }
        }
    }

    QDomNodeList instructions = root.elementsByTagName( "maneuver" );
    unsigned int const lastInstruction = qMax<int>( 0, instructions.length()-1 ); // ignore the last 'Welcome to xy' instruction
    for ( unsigned int i = 0; i < lastInstruction; ++i ) {
        QDomElement node = instructions.item( i ).toElement();

        QDomNodeList maneuver = node.elementsByTagName( "turnType" );
        QDomNodeList textNodes = node.elementsByTagName( "narrative" );
        QDomNodeList points = node.elementsByTagName( "startPoint" );
        QDomNodeList streets = node.elementsByTagName( "streets" );

        Q_ASSERT( mapping.contains( i ) );
        if ( textNodes.size() == 1 && maneuver.size() == 1 && points.size() == 1 && mapping.contains( i ) ) {
            GeoDataPlacemark* instruction = new GeoDataPlacemark;
            instruction->setName( textNodes.at( 0 ).toElement().text() );

            GeoDataExtendedData extendedData;
            GeoDataData turnType;
            turnType.setName( "turnType" );
            turnType.setValue( maneuverType( maneuver.at( 0 ).toElement().text().toInt() ) );
            extendedData.addValue( turnType );
            if ( streets.size() == 1 ) {
                GeoDataData roadName;
                roadName.setName( "roadName" );
                roadName.setValue( streets.at( 0 ).toElement().text() );
                extendedData.addValue( roadName );
            }
            instruction->setExtendedData( extendedData );

            int const start = mapping[i];
            int const end = mapping.contains(i+1) ? mapping[i+1] : routeWaypoints->size()-1;
            if ( start >= 0 && start < routeWaypoints->size() && end < routeWaypoints->size() ) {
                instruction->setName( textNodes.item( 0 ).toElement().text() );
                GeoDataLineString *lineString = new GeoDataLineString;
                for ( int j=start; j<=end; ++j ) {
                    *lineString << GeoDataCoordinates( routeWaypoints->at( j ).longitude(), routeWaypoints->at( j ).latitude() );
                }

                if ( !lineString->isEmpty() ) {
                    instruction->setGeometry( lineString );
                    result->append( instruction );
                }
            }
        }
    }

    if ( routeWaypoints->size() < 1 ) {
        delete result;
        result = 0;
    }

    return result;
}
Exemple #4
0
GeoDataDocument* OpenRouteServiceRunner::parse( const QByteArray &content ) const
{
    QDomDocument xml;
    if ( !xml.setContent( content ) ) {
        mDebug() << "Cannot parse xml file with routing instructions.";
        return nullptr;
    }

    QDomElement root = xml.documentElement();

    GeoDataDocument* result = new GeoDataDocument();
    result->setName(QStringLiteral("OpenRouteService"));

    QDomNodeList errors = root.elementsByTagName(QStringLiteral("xls:Error"));
    if ( errors.size() > 0 ) {
        return nullptr;
        // Returning early because fallback routing providers are used now
        // The code below can be used to parse OpenGis errors reported by ORS
        // and may be useful in the future

        for (int i=0 ; i < errors.length(); ++i ) {
            QDomNode node = errors.item( i );
            QString errorMessage = node.attributes().namedItem(QStringLiteral("message")).nodeValue();
            QRegExp regexp = QRegExp( "^(.*) Please Check your Position: (-?[0-9]+.[0-9]+) (-?[0-9]+.[0-9]+) !" );
            if ( regexp.indexIn( errorMessage ) == 0 ) {
                if ( regexp.capturedTexts().size() == 4 ) {
                    GeoDataPlacemark* placemark = new GeoDataPlacemark;
                    placemark->setName( regexp.capturedTexts().at( 1 ) );
                    GeoDataCoordinates position;
                    position.setLongitude( regexp.capturedTexts().at( 2 ).toDouble(), GeoDataCoordinates::Degree );
                    position.setLatitude( regexp.capturedTexts().at( 3 ).toDouble(), GeoDataCoordinates::Degree );
                    placemark->setCoordinate( position );
                    result->append( placemark );
                }
            } else {
                mDebug() << "Error message " << errorMessage << " not parsable.";
                /** @todo: How to handle this now with plugins? */
//                QString message = tr( "Sorry, a problem occurred when calculating the route. Try adjusting start and destination points." );
//                QPointer<QMessageBox> messageBox = new QMessageBox( QMessageBox::Warning, "Route Error", message );
//                messageBox->setDetailedText( errorMessage );
//                messageBox->exec();
//                delete messageBox;
            }
        }
    }

    GeoDataPlacemark* routePlacemark = new GeoDataPlacemark;
    routePlacemark->setName(QStringLiteral("Route"));
    QTime time;
    QDomNodeList summary = root.elementsByTagName(QStringLiteral("xls:RouteSummary"));
    if ( summary.size() > 0 ) {
        QDomNodeList timeNodeList = summary.item(0).toElement().elementsByTagName(QStringLiteral("xls:TotalTime"));
        if ( timeNodeList.size() == 1 ) {
            QRegExp regexp = QRegExp( "^P(?:(\\d+)D)?T(?:(\\d+)H)?(?:(\\d+)M)?(\\d+)S" );
            if ( regexp.indexIn( timeNodeList.item( 0 ).toElement().text() ) == 0 ) {
                QStringList matches = regexp.capturedTexts();
                unsigned int hours( 0 ), minutes( 0 ), seconds( 0 );
                switch ( matches.size() ) {
                case 5:
                    // days    = regexp.cap( matches.size() - 4 ).toInt();
                    // Intentionally no break
                case 4:
                    hours   = regexp.cap( matches.size() - 3 ).toInt();
                    // Intentionally no break
                case 3:
                    minutes = regexp.cap( matches.size() - 2 ).toInt();
                    // Intentionally no break
                case 2:
                    seconds = regexp.cap( matches.size() - 1 ).toInt();
                    break;
                default:
                    mDebug() << "Unable to parse time string " << timeNodeList.item( 0 ).toElement().text();
                }

                time = QTime( hours, minutes, seconds, 0 );
            }
        }
    }

    GeoDataLineString* routeWaypoints = new GeoDataLineString;
    QDomNodeList geometry = root.elementsByTagName(QStringLiteral("xls:RouteGeometry"));
    if ( geometry.size() > 0 ) {
        QDomNodeList waypoints = geometry.item( 0 ).toElement().elementsByTagName( "gml:pos" );
        for (int i=0 ; i < waypoints.length(); ++i ) {
            QDomNode node = waypoints.item( i );
            const QStringList content = node.toElement().text().split(QLatin1Char(' '));
            if ( content.length() == 2 ) {
                GeoDataCoordinates position;
                position.setLongitude( content.at( 0 ).toDouble(), GeoDataCoordinates::Degree );
                position.setLatitude( content.at( 1 ).toDouble(), GeoDataCoordinates::Degree );
                routeWaypoints->append( position );
            }
        }
    }
    routePlacemark->setGeometry( routeWaypoints );

    qreal length = routeWaypoints->length( EARTH_RADIUS );
    const QString name = nameString( "ORS", length, time );
    const GeoDataExtendedData data = routeData( length, time );
    routePlacemark->setExtendedData( data );
    result->setName( name );

    result->append( routePlacemark );

    QDomNodeList instructionList = root.elementsByTagName(QStringLiteral("xls:RouteInstructionsList"));
    if ( instructionList.size() > 0 ) {
        QDomNodeList instructions = instructionList.item(0).toElement().elementsByTagName(QStringLiteral("xls:RouteInstruction"));
        for (int i=0 ; i < instructions.length(); ++i ) {
            QDomElement node = instructions.item( i ).toElement();

            QDomNodeList textNodes = node.elementsByTagName(QStringLiteral("xls:Instruction"));
            QDomNodeList positions = node.elementsByTagName(QStringLiteral("gml:pos"));

            if ( textNodes.size() > 0 && positions.size() > 0 ) {
                const QStringList content = positions.at(0).toElement().text().split(QLatin1Char(' '));
                if ( content.length() == 2 ) {
                    GeoDataLineString *lineString = new GeoDataLineString;

                    for( int i = 0; i < positions.count(); ++i ) {
                         const QStringList pointList = positions.at(i).toElement().text().split(QLatin1Char(' '));
                         GeoDataCoordinates position;
                         position.setLongitude( pointList.at( 0 ).toDouble(), GeoDataCoordinates::Degree );
                         position.setLatitude( pointList.at( 1 ).toDouble(), GeoDataCoordinates::Degree );
                         lineString->append( position );
                    }

                    GeoDataPlacemark* instruction = new GeoDataPlacemark;

                    QString const text = textNodes.item( 0 ).toElement().text().remove(QRegExp("<[^>]*>"));
                    GeoDataExtendedData extendedData;
                    GeoDataData turnTypeData;
                    turnTypeData.setName(QStringLiteral("turnType"));
                    QString road;
                    RoutingInstruction::TurnType turnType = parseTurnType( text, &road );
                    turnTypeData.setValue( turnType );
                    extendedData.addValue( turnTypeData );
                    if ( !road.isEmpty() ) {
                        GeoDataData roadName;
                        roadName.setName(QStringLiteral("roadName"));
                        roadName.setValue( road );
                        extendedData.addValue( roadName );
                    }

                    QString const instructionText = turnType == RoutingInstruction::Unknown ? text : RoutingInstruction::generateRoadInstruction( turnType, road );
                    instruction->setName( instructionText );
                    instruction->setExtendedData( extendedData );
                    instruction->setGeometry( lineString );
                    result->append( instruction );
                }
            }
        }
    }

    return result;
}
Exemple #5
0
void EclipsesItem::calculate()
{
    int np, kp, j;
    double lat1, lng1, lat2, lng2, lat3, lng3, lat4, lng4;
    double ltf[60], lnf[60];

    m_ecl->putEclSelect( m_index );

    // FIXME: set observer location
    m_ecl->getMaxPos( lat1, lng1 );
    m_ecl->setLocalPos( lat1, lng1, 0 );

    // eclipse's maximum location
    m_maxLocation = GeoDataCoordinates( lng1, lat1, 0., GeoDataCoordinates::Degree );

    // calculate central line
    np = m_ecl->eclPltCentral( true, lat1, lng1 );
    kp = np;
    m_centralLine.clear();
    m_centralLine << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                         GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                         0., GeoDataCoordinates::Degree );

    if( np > 3 ) { // central eclipse
        while( np > 3 ) {
            np = m_ecl->eclPltCentral( false, lat1, lng1 );
            if( np > 3 ) {
                m_centralLine << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                                     GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                                     0., GeoDataCoordinates::Degree );
            }
        }
    }

    // calculate umbra
    np = kp;
    m_umbra.clear();
    if( np > 3 ) { // total or annual eclipse
        // northern /southern boundaries of umbra
        np = m_ecl->centralBound( true, lat1, lng1, lat2, lng2 );

        GeoDataLinearRing lowerUmbra( Tessellate ), upperUmbra( Tessellate );
        lowerUmbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                          GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                          0., GeoDataCoordinates::Degree );
        upperUmbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                          GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                          0., GeoDataCoordinates::Degree );

        while( np > 0 ) {
            np = m_ecl->centralBound( false, lat1, lng1, lat2, lng2 );
            if( lat1 <= 90. ) {
                lowerUmbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                                  GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                                  0., GeoDataCoordinates::Degree );
            }
            if( lat1 <= 90. ) {
                upperUmbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng2, GeoDataCoordinates::Degree),
                                                  GeoDataCoordinates::normalizeLat(lat2, GeoDataCoordinates::Degree),
                                                  0., GeoDataCoordinates::Degree );
            }
        }

        GeoDataLinearRing invertedUpperUmbra( Tessellate );
        QVector<GeoDataCoordinates>::const_iterator iter = upperUmbra.constEnd() - 1;
        for( ; iter != upperUmbra.constBegin(); --iter ) {
            invertedUpperUmbra << *iter;
        }
        invertedUpperUmbra << upperUmbra.first();
        upperUmbra = invertedUpperUmbra;

        m_umbra << lowerUmbra << upperUmbra;
    }

    // shadow cones
    m_shadowConeUmbra.clear();
    m_shadowConePenumbra.clear();
    m_shadowCone60MagPenumbra.clear();

    m_ecl->getLocalMax( lat2, lat3, lat4 );

    m_ecl->getShadowCone( lat2, true, 40, ltf, lnf );
    for( j = 0; j < 40; ++j ) {
        if( ltf[j] < 100. ) {
            m_shadowConeUmbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lnf[j], GeoDataCoordinates::Degree),
                                                     GeoDataCoordinates::normalizeLat(ltf[j], GeoDataCoordinates::Degree),
                                                     0., GeoDataCoordinates::Degree );
        }
    }

    m_ecl->setPenumbraAngle( 1., 0 );
    m_ecl->getShadowCone( lat2, false, 60, ltf, lnf );
    for( j = 0; j < 60; ++j ) {
        if( ltf[j] < 100. ) {
            m_shadowConePenumbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lnf[j], GeoDataCoordinates::Degree),
                                                        GeoDataCoordinates::normalizeLat(ltf[j], GeoDataCoordinates::Degree),
                                                        0., GeoDataCoordinates::Degree );
        }
    }

    m_ecl->setPenumbraAngle( 0.6, 1 );
    m_ecl->getShadowCone( lat2, false, 60, ltf, lnf );
    for( j = 0; j < 60; ++j ) {
        if( ltf[j] < 100. ) {
            m_shadowCone60MagPenumbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lnf[j], GeoDataCoordinates::Degree),
                                                             GeoDataCoordinates::normalizeLat(ltf[j], GeoDataCoordinates::Degree),
                                                             0., GeoDataCoordinates::Degree );
        }
    }

    m_ecl->setPenumbraAngle( 1., 0 );

    // eclipse boundaries
    m_southernPenumbra.clear();
    m_northernPenumbra.clear();

    np = m_ecl->GNSBound( true, true, lat1, lng2 );
    while( np > 0 ) {
        np = m_ecl->GNSBound( false, true, lat1, lng1 );
        if( ( np > 0 ) && ( lat1 <= 90. ) ) {
            m_southernPenumbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                                      GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                                      0., GeoDataCoordinates::Degree );
        }
    }

    np = m_ecl->GNSBound( true, false, lat1, lng1 );
    while( np > 0 ) {
        np = m_ecl->GNSBound( false, false, lat1, lng1 );
        if( ( np > 0 ) && ( lat1 <= 90. ) ) {
            m_northernPenumbra << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                                      GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                                      0., GeoDataCoordinates::Degree );
        }
    }

    // sunrise / sunset boundaries

    QList<GeoDataLinearRing*> sunBoundaries;
    np = m_ecl->GRSBound( true, lat1, lng1, lat3, lng3 );

    GeoDataLinearRing *lowerBoundary = new GeoDataLinearRing( Tessellate );
    *lowerBoundary << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng1, GeoDataCoordinates::Degree),
                                          GeoDataCoordinates::normalizeLat(lat1, GeoDataCoordinates::Degree),
                                          0., GeoDataCoordinates::Degree );

    GeoDataLinearRing *upperBoundary = new GeoDataLinearRing( Tessellate );
    *upperBoundary << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng3, GeoDataCoordinates::Degree),
                                          GeoDataCoordinates::normalizeLat(lat3, GeoDataCoordinates::Degree),
                                          0., GeoDataCoordinates::Degree );

    m_sunBoundaries.clear();

    while ( np > 0 ) {
        np = m_ecl->GRSBound( false, lat2, lng2, lat4, lng4 );
        bool pline = fabs( lng1 - lng2 ) < 10.; // during partial eclipses, the Rise/Set
                                                // lines switch at one stage.
                                                // This will prevent an ugly line between
                                                // the switch points. If there is a
                                                // longitude jump then add the current
                                                // section to our sun boundaries collection
                                                // and start a new section
        if ( !pline && !lowerBoundary->isEmpty() ) {
            sunBoundaries.prepend( lowerBoundary );
            lowerBoundary = new GeoDataLinearRing( Tessellate );
        }
        if ( ( np > 0 ) && ( lat2 <= 90. ) && ( lat1 <= 90. ) ) {
            *lowerBoundary << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng2, GeoDataCoordinates::Degree),
                                                  GeoDataCoordinates::normalizeLat(lat2, GeoDataCoordinates::Degree),
                                                  0., GeoDataCoordinates::Degree );
        }
        pline = fabs( lng3 - lng4 ) < 10.; // during partial eclipses, the Rise/Set lines
                                           // switch at one stage.
                                           // This will prevent an ugly line between the
                                           // switch points. If there is a longitude jump
                                           // then add the current section to our sun
                                           // boundaries collection and start a new section
        if ( !pline && !upperBoundary->isEmpty() ) {
            sunBoundaries.prepend( upperBoundary );
            upperBoundary = new GeoDataLinearRing( Tessellate );
        }
        if ( pline && ( np > 0 ) && ( lat4 <= 90. ) && ( lat3 <= 90. ) ) {
            *upperBoundary << GeoDataCoordinates( GeoDataCoordinates::normalizeLon(lng4, GeoDataCoordinates::Degree),
                                                  GeoDataCoordinates::normalizeLat(lat4, GeoDataCoordinates::Degree),
                                                  0., GeoDataCoordinates::Degree );
        }

        lng1 = lng2;
        lat1 = lat2;
        lng3 = lng4;
        lat3 = lat4;
    }

    if ( !lowerBoundary->isEmpty() ) {
        sunBoundaries.prepend(lowerBoundary);
    } else {
        delete lowerBoundary;
    }
    if ( !upperBoundary->isEmpty() ) {
        sunBoundaries.prepend(upperBoundary);
    } else {
        delete upperBoundary;
    }

    for ( int result = 0; result < 2; ++result ) {
        GeoDataLinearRing sunBoundary( Tessellate );

        sunBoundary = *sunBoundaries.last();
        sunBoundaries.pop_back();

        while ( sunBoundaries.size() > 0) {
            int closestSection = -1;

            // TODO: Now that MableMath is not public anymore we need a
            // GeoDataCoordinates::distance() method in Marble.
            GeoDataLineString ruler;
            ruler << sunBoundary.last() << sunBoundary.first();
            qreal closestDistance = ruler.length( 1 );
            int closestEnd = 0;  // 0 = start of section, 1 = end of section

            // Look for a section that is closest to our sunBoundary section.
            for ( int it = 0; it < sunBoundaries.size(); ++it ) {
                GeoDataLineString distanceStartSection;
                distanceStartSection << sunBoundary.last() << sunBoundaries.at( it )->first();

                GeoDataLineString distanceEndSection;
                distanceEndSection << sunBoundary.last() << sunBoundaries.at( it )->last();
                if ( distanceStartSection.length( 1 ) < closestDistance ) {
                    closestDistance = distanceStartSection.length( 1 );
                    closestSection = it;
                    closestEnd = 0;
                }
                if ( distanceEndSection.length(1) < closestDistance ) {
                    closestDistance = distanceEndSection.length( 1 );
                    closestSection = it;
                    closestEnd = 1;
                }
            }

            if ( closestSection == -1 ) {
                // There is no other section that is closer to the end of
                // our sunBoundary section than the startpoint of our
                // sunBoundary itself
                break;
            }
            else {
                // We now concatenate the closest section to the sunBoundary.
                // First we might have to invert it so that we concatenate
                // the right end
                if ( closestEnd == 1 ) {
                    // TODO: replace this with a GeoDataLinearRing::invert()
                    // method that needs to be added to Marble ...
                    GeoDataLinearRing * invertedBoundary = new GeoDataLinearRing( Tessellate );
                    QVector<GeoDataCoordinates>::const_iterator iter = sunBoundaries.at( closestSection )->constEnd();
                    --iter;
                    for( ; iter != sunBoundaries.at( closestSection )->constBegin(); --iter ) {
                        *invertedBoundary << *iter;
                    }
                    *invertedBoundary << sunBoundaries.at( closestSection )->first();
                    delete sunBoundaries[closestSection];
                    sunBoundaries[closestSection] = invertedBoundary;
                }
                sunBoundary << *sunBoundaries[closestSection];

                // Now remove the section that we've just added from the list
                delete sunBoundaries[closestSection];
                sunBoundaries.removeAt( closestSection );
            }
        }

        m_sunBoundaries << sunBoundary;

        if ( sunBoundaries.size() == 0 ) break;
    }

    m_calculationsNeedUpdate = false;
}
GeoDataDocument *CycleStreetsRunner::parse( const QByteArray &content ) const
{
    QDomDocument xml;
    if ( !xml.setContent( content ) ) {
        mDebug() << "Cannot parse xml file with routing instructions.";
        return 0;
    }
    GeoDataDocument *result = new GeoDataDocument();
    result->setName( "CycleStreets" );
    GeoDataPlacemark *routePlacemark = new GeoDataPlacemark;
    routePlacemark->setName( "Route" );

    GeoDataLineString *routeWaypoints = new GeoDataLineString;
    QDomNodeList features = xml.elementsByTagName( "gml:featureMember" );

    if ( features.isEmpty() ) {
        return 0;
    }
    QDomElement route = features.at( 0 ).toElement().firstChild().toElement();
    QDomElement lineString = route.elementsByTagName( "gml:LineString" ).at( 0 ).toElement();
    QDomElement coordinates = lineString.toElement().elementsByTagName( "gml:coordinates" ).at( 0 ).toElement();
    QStringList coordinatesList = coordinates.text().split( ' ' );

    QStringList::iterator iter = coordinatesList.begin();
    QStringList::iterator end = coordinatesList.end();

    for( ; iter != end; ++iter) {
        QStringList coordinate =  iter->split(',');
        if ( coordinate.size() == 2 ) {
            double const lon = coordinate.at( 0 ).toDouble();
            double const lat = coordinate.at( 1 ).toDouble();
            GeoDataCoordinates const position( lon, lat, 0.0, GeoDataCoordinates::Degree );
            routeWaypoints->append( position );
        }
    }
    routePlacemark->setGeometry( routeWaypoints );

    QDomElement durationElement = route.elementsByTagName( "cs:time" ).at(0).toElement();
    QTime duration;
    duration = duration.addSecs( durationElement.text().toInt() );
    qreal length = routeWaypoints->length( EARTH_RADIUS );

    const QString name = nameString( "CS", length, duration );
    const GeoDataExtendedData data = routeData( length, duration );
    routePlacemark->setExtendedData( data );
    result->setName( name );
    result->append( routePlacemark );

    int i;
    for ( i = 1; i < features.count() && features.at( i ).firstChildElement().tagName() != "cs:segment"; ++i );
    for ( ; i < features.count(); ++i) {
        QDomElement segment = features.at( i ).toElement();

        QString name = segment.elementsByTagName( "cs:name" ).at( 0 ).toElement().text();
        QString maneuver = segment.elementsByTagName( "cs:turn" ).at( 0 ).toElement().text();
        QStringList points = segment.elementsByTagName( "cs:points" ).at( 0 ).toElement().text().split( ' ' );
        QStringList const elevation = segment.elementsByTagName( "cs:elevations" ).at( 0 ).toElement().text().split( ',' );

        GeoDataPlacemark *instructions = new GeoDataPlacemark;
        QString instructionName;
        if ( !maneuver.isEmpty() ) {
            instructionName = maneuver.left( 1 ).toUpper() + maneuver.mid( 1 );
        } else {
            instructionName = "Straight";
        }
        if ( name != "Short un-named link" && name != "Un-named link" ){
            instructionName.append( " into " + name );
        }
        instructions->setName( instructionName );

        GeoDataExtendedData extendedData;
        GeoDataData turnType;
        turnType.setName( "turnType" );
        turnType.setValue( maneuverType( maneuver ) );
        extendedData.addValue( turnType );

        instructions->setExtendedData( extendedData );
        GeoDataLineString *lineString = new GeoDataLineString;
        QStringList::iterator iter = points.begin();
        QStringList::iterator end = points.end();
        for  ( int j=0; iter != end; ++iter, ++j ) {
            QStringList coordinate = iter->split( ',' );
            if ( coordinate.size() == 2 ) {
                double const lon = coordinate.at( 0 ).toDouble();
                double const lat = coordinate.at( 1 ).toDouble();
                double const alt = j < elevation.size() ? elevation[j].toDouble() : 0.0;
                lineString->append( GeoDataCoordinates( lon, lat, alt, GeoDataCoordinates::Degree ) );
            }
        }
        instructions->setGeometry( lineString );
        result->append( instructions );
    }
    return result;
}