void TestGeoDataTrack::simpleParseTest() { GeoDataDocument* dataDocument = parseKml( simpleExampleContent ); GeoDataFolder *folder = dataDocument->folderList().at( 0 ); QCOMPARE( folder->placemarkList().size(), 1 ); GeoDataPlacemark* placemark = folder->placemarkList().at( 0 ); QCOMPARE( placemark->geometry()->geometryId(), GeoDataTrackId ); GeoDataTrack* track = static_cast<GeoDataTrack*>( placemark->geometry() ); QCOMPARE( track->size(), 7 ); { QDateTime when = track->whenList().at( 0 ); QCOMPARE( when, QDateTime( QDate( 2010, 5, 28 ), QTime( 2, 2, 9 ), Qt::UTC ) ); } { GeoDataCoordinates coord = track->coordinatesList().at( 0 ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), -122.207881 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 37.371915 ); QCOMPARE( coord.altitude(), 156.000000 ); } { GeoDataCoordinates coord = track->coordinatesAt( QDateTime( QDate( 2010, 5, 28 ), QTime( 2, 2, 9 ), Qt::UTC ) ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), -122.207881 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 37.371915 ); QCOMPARE( coord.altitude(), 156.000000 ); } delete dataDocument; }
void CurrentLocationWidgetPrivate::receiveGpsCoordinates( const GeoDataCoordinates &position, qreal speed ) { m_currentPosition = position; QString unitString; QString altitudeUnitString; QString distanceUnitString; qreal unitSpeed = 0.0; qreal altitude = 0.0; qreal length = m_widget->model()->positionTracking()->length( m_widget->model()->planetRadius() ); QString html = "<html><body>"; html += "<table cellspacing=\"2\" cellpadding=\"2\">"; html += "<tr><td>Longitude</td><td><a href=\"http://edu.kde.org/marble\">%1</a></td></tr>"; html += "<tr><td>Latitude</td><td><a href=\"http://edu.kde.org/marble\">%2</a></td></tr>"; html += "<tr><td>Altitude</td><td>%3</td></tr>"; html += "<tr><td>Speed</td><td>%4</td></tr>"; html += "<tr><td>Distance</td><td>%5</td></tr>"; html += "</table>"; html += "</body></html>"; switch ( MarbleGlobal::getInstance()->locale()->measurementSystem() ) { case QLocale::MetricSystem: //kilometers per hour unitString = QObject::tr("km/h"); unitSpeed = speed * HOUR2SEC * METER2KM; altitudeUnitString = QObject::tr("m"); distanceUnitString = QObject::tr("m"); if ( length > 1000.0 ) { length /= 1000.0; distanceUnitString = QObject::tr("km"); } altitude = position.altitude(); break; case QLocale::ImperialSystem: //miles per hour unitString = QObject::tr("m/h"); unitSpeed = speed * HOUR2SEC * METER2KM * KM2MI; altitudeUnitString = QObject::tr("ft"); distanceUnitString = QObject::tr("ft"); altitude = position.altitude() * M2FT; length *= M2FT; break; } // TODO read this value from the incoming signal const QString speedString = QLocale::system().toString( unitSpeed, 'f', 1); const QString altitudeString = QString( "%1 %2" ).arg( altitude, 0, 'f', 1, QChar(' ') ).arg( altitudeUnitString ); const QString distanceString = QString( "%1 %2" ).arg( length, 0, 'f', 1, QChar(' ') ).arg( distanceUnitString ); html = html.arg( position.lonToString() ).arg( position.latToString() ); html = html.arg( altitudeString ).arg( speedString + ' ' + unitString ); html = html.arg( distanceString ); m_currentLocationUi.locationLabel->setText( html ); m_currentLocationUi.showTrackCheckBox->setEnabled( true ); m_currentLocationUi.saveTrackButton->setEnabled( true ); m_currentLocationUi.clearTrackButton->setEnabled( true ); }
void TestGeoDataTrack::withoutTimeTest() { //"Simple Example" from kmlreference; when elements emptied QString content( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<kml xmlns=\"http://www.opengis.net/kml/2.2\"" " xmlns:gx=\"http://www.google.com/kml/ext/2.2\">" "<Folder>" " <Placemark>" " <gx:Track>" " <when></when>" " <when></when>" " <when></when>" " <when></when>" " <when></when>" " <when></when>" " <when></when>" " <gx:coord>-122.207881 37.371915 156.000000</gx:coord>" " <gx:coord>-122.205712 37.373288 152.000000</gx:coord>" " <gx:coord>-122.204678 37.373939 147.000000</gx:coord>" " <gx:coord>-122.203572 37.374630 142.199997</gx:coord>" " <gx:coord>-122.203451 37.374706 141.800003</gx:coord>" " <gx:coord>-122.203329 37.374780 141.199997</gx:coord>" " <gx:coord>-122.203207 37.374857 140.199997</gx:coord>" " </gx:Track>" " </Placemark>" "</Folder>" "</kml>" ); GeoDataDocument* dataDocument = parseKml( content ); GeoDataFolder *folder = dataDocument->folderList().at( 0 ); QCOMPARE( folder->placemarkList().size(), 1 ); GeoDataPlacemark* placemark = folder->placemarkList().at( 0 ); QCOMPARE( placemark->geometry()->geometryId(), GeoDataTrackId ); GeoDataTrack* track = static_cast<GeoDataTrack*>( placemark->geometry() ); QCOMPARE( track->size(), 7 ); { GeoDataCoordinates coord = track->coordinatesList().at( 0 ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), -122.207881 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 37.371915 ); QCOMPARE( coord.altitude(), 156.000000 ); } { const GeoDataLineString *lineString = track->lineString(); QCOMPARE( lineString->size(), 7 ); GeoDataCoordinates coord = lineString->at( 0 ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), -122.207881 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 37.371915 ); QCOMPARE( coord.altitude(), 156.000000 ); } delete dataDocument; }
bool GeoDataLatLonAltBox::contains( const GeoDataCoordinates &point ) const { if ( !GeoDataLatLonBox::contains( point ) ) return false; if ( point.altitude() < d->m_minAltitude || point.altitude() > d->m_maxAltitude ) { return false; } return true; }
bool VerticalPerspectiveProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { Q_D(const VerticalPerspectiveProjection); d->calculateConstants(viewport->radius()); const qreal P = d->m_P; const qreal deltaLambda = coordinates.longitude() - viewport->centerLongitude(); const qreal phi = coordinates.latitude(); const qreal phi1 = viewport->centerLatitude(); qreal cosC = qSin( phi1 ) * qSin( phi ) + qCos( phi1 ) * qCos( phi ) * qCos( deltaLambda ); // Don't display placemarks that are below 10km altitude and // are on the Earth's backside (where cosC < 1/P) if (cosC < 1/P && coordinates.altitude() < 10000) { globeHidesPoint = true; return false; } // Let (x, y) be the position on the screen of the placemark .. // First determine the position in unit coordinates: qreal k = (P - 1) / (P - cosC); // scale factor x = ( qCos( phi ) * qSin( deltaLambda ) ) * k; y = ( qCos( phi1 ) * qSin( phi ) - qSin( phi1 ) * qCos( phi ) * qCos( deltaLambda ) ) * k; // Transform to screen coordinates qreal pixelAltitude = (coordinates.altitude() + EARTH_RADIUS) * d->m_altitudeToPixel; x *= pixelAltitude; y *= pixelAltitude; // Don't display satellites that are on the Earth's backside: if (cosC < 1/P && x*x+y*y < viewport->radius() * viewport->radius()) { globeHidesPoint = true; return false; } // The remaining placemarks are definetely not on the Earth's backside globeHidesPoint = false; x += viewport->width() / 2; y = viewport->height() / 2 - y; // Skip placemarks that are outside the screen area if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { return false; } return true; }
bool SphericalProjection::screenCoordinates( const GeoDataCoordinates &coordinates, const ViewportParams *viewport, qreal &x, qreal &y, bool &globeHidesPoint ) const { qreal absoluteAltitude = coordinates.altitude() + EARTH_RADIUS; Quaternion qpos = coordinates.quaternion(); qpos.rotateAroundAxis( viewport->planetAxisMatrix() ); qreal pixelAltitude = ( ( viewport->radius() ) / EARTH_RADIUS * absoluteAltitude ); if ( coordinates.altitude() < 10000 ) { // Skip placemarks at the other side of the earth. if ( qpos.v[Q_Z] < 0 ) { globeHidesPoint = true; return false; } } else { qreal earthCenteredX = pixelAltitude * qpos.v[Q_X]; qreal earthCenteredY = pixelAltitude * qpos.v[Q_Y]; qreal radius = viewport->radius(); // Don't draw high placemarks (e.g. satellites) that aren't visible. if ( qpos.v[Q_Z] < 0 && ( ( earthCenteredX * earthCenteredX + earthCenteredY * earthCenteredY ) < radius * radius ) ) { globeHidesPoint = true; return false; } } // Let (x, y) be the position on the screen of the placemark.. x = ((qreal)(viewport->width()) / 2 + pixelAltitude * qpos.v[Q_X]); y = ((qreal)(viewport->height()) / 2 - pixelAltitude * qpos.v[Q_Y]); // Skip placemarks that are outside the screen area if ( x < 0 || x >= viewport->width() || y < 0 || y >= viewport->height() ) { globeHidesPoint = false; return false; } globeHidesPoint = false; return true; }
void GpsInfo::updateLocation( GeoDataCoordinates coordinates, qreal) { PositionTracking *tracking = marbleModel()->positionTracking(); qreal speed = tracking->speed(); qreal direction = tracking->direction(); qreal altitude = coordinates.altitude(); qreal precision = tracking->accuracy().horizontal; QString speedString; QString distanceString; switch ( m_locale->measurementSystem() ) { case MarbleLocale::ImperialSystem: //miles per hour speedString = tr("mph"); speed *= HOUR2SEC * METER2KM * KM2MI; distanceString = tr("ft"); altitude *= M2FT; precision *= M2FT; break; case MarbleLocale::MetricSystem: //kilometers per hour speedString = tr("km/h"); speed *= HOUR2SEC * METER2KM; distanceString = tr("m"); break; case MarbleLocale::NauticalSystem: // nm per hour (knots) speedString = tr("kt"); speed *= HOUR2SEC * METER2KM * KM2NM; distanceString = tr("nm"); break; } m_widget.SpeedValue->setText( QString( " %1 %2" ) .arg( QLocale().toString(speed, 'f', 1 ) ) .arg( speedString ) ); m_widget.AltitudeValue->setText( QString( " %1 %2" ) .arg( QLocale().toString(altitude, 'f', 1 ) ) .arg( distanceString ) ); m_widget.DirectionValue->setText( QString( " %1 %2" ) .arg( QLocale().toString(direction, 'f', 1 ) ) .arg( "d" ) ); m_widget.PrecisionValue->setText( QString( " %1 %2" ) .arg( QLocale().toString(precision, 'f', 1 ) ) .arg( distanceString ) ); int const minimumWidth = m_widgetItem->widget()->sizeHint().width(); if ( size().width() < minimumWidth ) { m_widgetItem->setSize( QSizeF( minimumWidth, size().height() ) ); } update(); emit repaintNeeded(); }
GeoDataCoordinates GeoDataLineStringPrivate::findDateLine( const GeoDataCoordinates & previousCoords, const GeoDataCoordinates & currentCoords, int recursionCounter ) { 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 ) { // mDebug() << "stopped at recursion" << recursionCounter << " and longitude difference " << longitudeDiff; return currentCoords; } ++recursionCounter; qreal lon = 0.0; qreal lat = 0.0; qreal altDiff = currentCoords.altitude() - previousCoords.altitude(); const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), 0.5 ); itpos.getSpherical( lon, lat ); qreal altitude = previousCoords.altitude() + 0.5 * altDiff; GeoDataCoordinates interpolatedCoords( lon, lat, altitude ); int interpolatedSign = ( interpolatedCoords.longitude() < 0.0 ) ? -1 : +1 ; /* mDebug() << "SRC" << previousCoords.toString(); mDebug() << "TAR" << currentCoords.toString(); mDebug() << "IPC" << interpolatedCoords.toString(); */ if ( interpolatedSign != currentSign ) { return findDateLine( interpolatedCoords, currentCoords, recursionCounter ); } return findDateLine( previousCoords, interpolatedCoords, recursionCounter ); }
void TourWidgetPrivate::mapCenterOn( const QModelIndex &index ) { QVariant coordinatesVariant = m_widget->model()->treeModel()->data( index, MarblePlacemarkModel::CoordinateRole ); if ( !coordinatesVariant.isNull() ) { GeoDataCoordinates const coordinates = coordinatesVariant.value<GeoDataCoordinates>(); GeoDataLookAt lookat; lookat.setCoordinates( coordinates ); lookat.setRange( coordinates.altitude() ); m_widget->flyTo( lookat, Instant ); } }
GeoDataLatLonAltBox::GeoDataLatLonAltBox( const GeoDataCoordinates & coordinates ) : GeoDataLatLonBox(), d( new GeoDataLatLonAltBoxPrivate ) { setWest( coordinates.longitude() ); setEast( coordinates.longitude() ); setNorth( coordinates.latitude() ); setSouth( coordinates.latitude() ); d->m_minAltitude = coordinates.altitude(); d->m_maxAltitude = coordinates.altitude(); }
bool KmlLineStringTagWriter::write( const GeoNode *node, GeoWriter& writer ) const { const GeoDataLineString *lineString = static_cast<const GeoDataLineString*>( node ); if ( lineString->size() > 1 ) { writer.writeStartElement( kml::kmlTag_LineString ); writer.writeStartElement( "coordinates" ); // Write altitude for *all* elements, if *any* element // has altitude information (!= 0.0) bool hasAltitude = false; for ( int i = 0; i < lineString->size(); ++i ) { if ( lineString->at( i ).altitude() ) { hasAltitude = true; break; } } for ( int i = 0; i < lineString->size(); ++i ) { GeoDataCoordinates coordinates = lineString->at( i ); if ( i > 0 ) { writer.writeCharacters( " " ); } qreal lon = coordinates.longitude( GeoDataCoordinates::Degree ); writer.writeCharacters( QString::number( lon, 'f', 10 ) ); writer.writeCharacters( "," ); qreal lat = coordinates.latitude( GeoDataCoordinates::Degree ); writer.writeCharacters( QString::number( lat, 'f', 10 ) ); if ( hasAltitude ) { qreal alt = coordinates.altitude(); writer.writeCharacters( "," ); writer.writeCharacters( QString::number( alt, 'f', 2 ) ); } } writer.writeEndElement(); writer.writeEndElement(); return true; } return false; }
void TestTrack::simpleParseTest() { //example track recorded using a Garmin Vista HCx and downloaded using gpsbabel QString content( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" "<gpx" " version=\"1.0\"" " creator=\"GPSBabel - http://www.gpsbabel.org\"" " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" " xmlns=\"http://www.topografix.com/GPX/1/0\"" " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">" "<time>2011-07-03T14:19:57Z</time>" "<bounds minlat=\"47.231073193\" minlon=\"12.549449634\" maxlat=\"48.502999926\" maxlon=\"14.302069964\"/>" "<trk>" " <name>test track</name>" "<number>1</number>" "<trkseg>" "<trkpt lat=\"47.231477033\" lon=\"12.560534449\">" " <ele>1130.647705</ele>" " <time>2011-06-24T10:33:40Z</time>" "</trkpt>" "<trkpt lat=\"47.231486840\" lon=\"12.560604354\">" " <ele>1127.763672</ele>" " <time>2011-06-24T10:33:55Z</time>" "</trkpt>" "<trkpt lat=\"47.231497569\" lon=\"12.560612401\">" " <ele>1121.995850</ele>" " <time>2011-06-24T10:34:00Z</time>" "</trkpt>" "</trkseg>" "</trk>" "</gpx>" ); GpxParser parser; QByteArray array( content.toUtf8() ); QBuffer buffer( &array ); buffer.open( QIODevice::ReadOnly ); qDebug() << "Buffer content:" << endl << buffer.buffer(); if ( !parser.read( &buffer ) ) { QFAIL( "Could not parse data!" ); return; } GeoDocument* document = parser.releaseDocument(); QVERIFY( document ); GeoDataDocument *dataDocument = static_cast<GeoDataDocument*>( document ); GeoDataPlacemark* placemark = dataDocument->placemarkList().at( 0 ); QCOMPARE( placemark->geometry()->geometryId(), GeoDataMultiGeometryId ); GeoDataMultiGeometry* multiGeo = static_cast<GeoDataMultiGeometry*>( placemark->geometry() ); GeoDataTrack* track = static_cast<GeoDataTrack*>( &multiGeo->at( 0 ) ); QCOMPARE( track->size(), 3 ); { QDateTime when = track->whenList().at( 0 ); QCOMPARE( when, QDateTime( QDate( 2011, 6, 24 ), QTime( 10, 33, 40 ), Qt::UTC ) ); } { GeoDataCoordinates coord = track->coordinatesAt( 0 ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), 12.560534449 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 47.231477033 ); QCOMPARE( coord.altitude(), 1130.647705 ); } { GeoDataCoordinates coord = track->coordinatesAt( QDateTime( QDate( 2011, 6, 24 ), QTime( 10, 33, 40 ), Qt::UTC ) ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), 12.560534449 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 47.231477033 ); QCOMPARE( coord.altitude(), 1130.647705 ); } { const GeoDataLineString* lineString = track->lineString(); QCOMPARE( lineString->size(), 3 ); GeoDataCoordinates coord = lineString->at( 0 ); QCOMPARE( coord.longitude( GeoDataCoordinates::Degree ), 12.560534449 ); QCOMPARE( coord.latitude( GeoDataCoordinates::Degree ), 47.231477033 ); QCOMPARE( coord.altitude(), 1130.647705 ); } delete document; }
int CylindricalProjectionPrivate::processTessellation( const GeoDataCoordinates &previousCoords, const GeoDataCoordinates ¤tCoords, int tessellatedNodes, QVector<QPolygonF*> &polygons, const ViewportParams *viewport, TessellationFlags f, int mirrorCount, qreal repeatDistance) const { const bool clampToGround = f.testFlag( FollowGround ); const bool followLatitudeCircle = f.testFlag( RespectLatitudeCircle ) && previousCoords.latitude() == currentCoords.latitude(); // Calculate steps for tessellation: lonDiff and altDiff qreal lonDiff = 0.0; if ( followLatitudeCircle ) { const int previousSign = previousCoords.longitude() > 0 ? 1 : -1; const int currentSign = currentCoords.longitude() > 0 ? 1 : -1; lonDiff = currentCoords.longitude() - previousCoords.longitude(); if ( previousSign != currentSign && fabs(previousCoords.longitude()) + fabs(currentCoords.longitude()) > M_PI ) { if ( previousSign > currentSign ) { // going eastwards -> lonDiff += 2 * M_PI ; } else { // going westwards -> lonDiff -= 2 * M_PI; } } if ( fabs( lonDiff ) == 2 * M_PI ) { return mirrorCount; } } const qreal altDiff = currentCoords.altitude() - previousCoords.altitude(); // Create the tessellation nodes. GeoDataCoordinates previousTessellatedCoords = previousCoords; for ( int i = 1; i <= tessellatedNodes; ++i ) { const qreal t = (qreal)(i) / (qreal)( tessellatedNodes + 1 ); // interpolate the altitude, too const qreal altitude = clampToGround ? 0 : altDiff * t + previousCoords.altitude(); qreal lon = 0.0; qreal lat = 0.0; if ( followLatitudeCircle ) { // To tessellate along latitude circles use the // linear interpolation of the longitude. lon = lonDiff * t + previousCoords.longitude(); lat = previousTessellatedCoords.latitude(); } else { // To tessellate along great circles use the // normalized linear interpolation ("NLERP") for latitude and longitude. const Quaternion itpos = Quaternion::nlerp( previousCoords.quaternion(), currentCoords.quaternion(), t ); itpos. getSpherical( lon, lat ); } const GeoDataCoordinates currentTessellatedCoords( lon, lat, altitude ); Q_Q(const CylindricalProjection); qreal bx, by; q->screenCoordinates( currentTessellatedCoords, viewport, bx, by ); mirrorCount = crossDateLine( previousTessellatedCoords, currentTessellatedCoords, bx, by, polygons, mirrorCount, repeatDistance ); previousTessellatedCoords = currentTessellatedCoords; } // For the clampToGround case add the "current" coordinate after adding all other nodes. GeoDataCoordinates currentModifiedCoords( currentCoords ); if ( clampToGround ) { currentModifiedCoords.setAltitude( 0.0 ); } Q_Q(const CylindricalProjection); qreal bx, by; q->screenCoordinates( currentModifiedCoords, viewport, bx, by ); mirrorCount = crossDateLine( previousTessellatedCoords, currentModifiedCoords, bx, by, polygons, mirrorCount, repeatDistance ); return mirrorCount; }
void ElevationProfileFloatItem::paintContent( QPainter *painter ) { // do not try to draw if not initialized if(!isInitialized()) { return; } painter->save(); painter->setRenderHint( QPainter::Antialiasing, true ); painter->setFont( font() ); if ( ! ( m_activeDataSource->isDataAvailable() && m_eleData.size() > 0 ) ) { painter->setPen( QColor( Qt::black ) ); QString text = tr( "Create a route or load a track from file to view its elevation profile." ); painter->drawText( contentRect().toRect(), Qt::TextWordWrap | Qt::AlignCenter, text ); painter->restore(); return; } if ( m_zoomToViewport && ( m_lastVisiblePoint - m_firstVisiblePoint < 5 ) ) { painter->setPen( QColor( Qt::black ) ); QString text = tr( "Not enough points in the current viewport.\nTry to disable 'Zoom to viewport'." ); painter->drawText( contentRect().toRect(), Qt::TextWordWrap | Qt::AlignCenter, text ); painter->restore(); return; } QString intervalStr; int lastStringEnds; // draw viewport bounds if ( ! m_zoomToViewport && ( m_firstVisiblePoint > 0 || m_lastVisiblePoint < m_eleData.size() - 1 ) ) { QColor color( Qt::black ); color.setAlpha( 64 ); QRect rect; rect.setLeft( m_leftGraphMargin + m_eleData.value( m_firstVisiblePoint ).x() * m_eleGraphWidth / m_axisX.range() ); rect.setTop( 0 ); rect.setWidth( ( m_eleData.value( m_lastVisiblePoint ).x() - m_eleData.value( m_firstVisiblePoint ).x() ) * m_eleGraphWidth / m_axisX.range() ); rect.setHeight( m_eleGraphHeight ); painter->fillRect( rect, color ); } // draw X and Y axis painter->setPen( Oxygen::aluminumGray4 ); painter->drawLine( m_leftGraphMargin, m_eleGraphHeight, contentSize().width(), m_eleGraphHeight ); painter->drawLine( m_leftGraphMargin, m_eleGraphHeight, m_leftGraphMargin, 0 ); // draw Y grid and labels painter->setPen( QColor( Qt::black ) ); QPen dashedPen( Qt::DashLine ); dashedPen.setColor( Oxygen::aluminumGray4 ); QRect labelRect( 0, 0, m_leftGraphMargin - 1, m_fontHeight + 2 ); lastStringEnds = m_eleGraphHeight + m_fontHeight; // painter->drawText( m_leftGraphMargin + 1, m_fontHeight, "[" + m_axisY.unit() + "]" ); foreach ( const AxisTick &tick, m_axisY.ticks() ) { const int posY = m_eleGraphHeight - tick.position; painter->setPen( dashedPen ); painter->drawLine( m_leftGraphMargin, posY, contentSize().width(), posY ); labelRect.moveCenter( QPoint( labelRect.center().x(), posY ) ); if ( labelRect.top() < 0 ) { // don't cut off uppermost label labelRect.moveTop( 0 ); } if ( labelRect.bottom() >= lastStringEnds ) { // Don't print overlapping labels continue; } lastStringEnds = labelRect.top(); painter->setPen( QColor( Qt::black ) ); intervalStr.setNum( tick.value * m_axisY.scale() ); painter->drawText( labelRect, Qt::AlignRight, intervalStr ); } // draw X grid and labels painter->setPen( QColor( Qt::black ) ); labelRect.moveTop( m_eleGraphHeight + 1 ); lastStringEnds = 0; foreach ( const AxisTick &tick, m_axisX.ticks() ) { const int posX = m_leftGraphMargin + tick.position; painter->setPen( dashedPen ); painter->drawLine( posX, 0, posX, m_eleGraphHeight ); intervalStr.setNum( tick.value * m_axisX.scale() ); if ( tick.position == m_axisX.ticks().last().position ) { intervalStr += ' ' + m_axisX.unit(); } labelRect.setWidth( QFontMetricsF( font() ).width( intervalStr ) * 1.5 ); labelRect.moveCenter( QPoint( posX, labelRect.center().y() ) ); if ( labelRect.right() > m_leftGraphMargin + m_eleGraphWidth ) { // don't cut off rightmost label labelRect.moveRight( m_leftGraphMargin + m_eleGraphWidth ); } if ( labelRect.left() <= lastStringEnds ) { // Don't print overlapping labels continue; } lastStringEnds = labelRect.right(); painter->setPen( QColor( Qt::black ) ); painter->drawText( labelRect, Qt::AlignCenter, intervalStr ); } // display elevation gain/loss data painter->setPen( QColor( Qt::black ) ); intervalStr = tr( "Difference: %1 %2" ) .arg( QString::number( m_gain - m_loss, 'f', 0 ) ) .arg( m_axisY.unit() ); intervalStr += QString::fromUtf8( " (↗ %1 %3 ↘ %2 %3)" ) .arg( QString::number( m_gain, 'f', 0 ) ) .arg( QString::number( m_loss, 'f', 0 ) ) .arg( m_axisY.unit() ); painter->drawText( contentRect().toRect(), Qt::AlignBottom | Qt::AlignCenter, intervalStr ); // draw elevation profile painter->setPen( QColor( Qt::black ) ); bool const highRes = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::HighResolution; QPen pen = painter->pen(); pen.setWidth( highRes ? 2 : 1 ); painter->setPen( pen ); QLinearGradient fillGradient( 0, 0, 0, m_eleGraphHeight ); QColor startColor = Oxygen::forestGreen4; QColor endColor = Oxygen::hotOrange4; startColor.setAlpha( 200 ); endColor.setAlpha( 32 ); fillGradient.setColorAt( 0.0, startColor ); fillGradient.setColorAt( 1.0, endColor ); QBrush brush = QBrush( fillGradient ); painter->setBrush( brush ); QPoint oldPos; oldPos.setX( m_leftGraphMargin ); oldPos.setY( ( m_axisY.minValue() - m_axisY.minValue() ) * m_eleGraphHeight / ( m_axisY.range() / m_shrinkFactorY ) ); oldPos.setY( m_eleGraphHeight - oldPos.y() ); QPainterPath path; path.moveTo( oldPos.x(), m_eleGraphHeight ); path.lineTo( oldPos.x(), oldPos.y() ); const int start = m_zoomToViewport ? m_firstVisiblePoint : 0; const int end = m_zoomToViewport ? m_lastVisiblePoint : m_eleData.size() - 1; for ( int i = start; i <= end; ++i ) { QPoint newPos; if ( i == start ) { // make sure the plot always starts at the y-axis newPos.setX( 0 ); } else { newPos.setX( ( m_eleData.value(i).x() - m_axisX.minValue() ) * m_eleGraphWidth / m_axisX.range() ); } newPos.rx() += m_leftGraphMargin; if ( newPos.x() != oldPos.x() || newPos.y() != oldPos.y() ) { newPos.setY( ( m_eleData.value(i).y() - m_axisY.minValue() ) * m_eleGraphHeight / ( m_axisY.range() * m_shrinkFactorY ) ); newPos.setY( m_eleGraphHeight - newPos.y() ); path.lineTo( newPos.x(), newPos.y() ); oldPos = newPos; } } path.lineTo( oldPos.x(), m_eleGraphHeight ); // fill painter->setPen( QPen( Qt::NoPen ) ); painter->drawPath( path ); // contour // "remove" the first and last path element first, they are only used to fill down to the bottom painter->setBrush( QBrush( Qt::NoBrush ) ); path.setElementPositionAt( 0, path.elementAt( 1 ).x, path.elementAt( 1 ).y ); path.setElementPositionAt( path.elementCount()-1, path.elementAt( path.elementCount()-2 ).x, path.elementAt( path.elementCount()-2 ).y ); painter->setPen( pen ); painter->drawPath( path ); pen.setWidth( 1 ); painter->setPen( pen ); // draw interactive cursor const GeoDataCoordinates currentPoint = m_markerPlacemark->coordinate(); if ( currentPoint.isValid() ) { painter->setPen( QColor( Qt::white ) ); painter->drawLine( m_leftGraphMargin + m_cursorPositionX, 0, m_leftGraphMargin + m_cursorPositionX, m_eleGraphHeight ); qreal xpos = m_axisX.minValue() + ( m_cursorPositionX / m_eleGraphWidth ) * m_axisX.range(); qreal ypos = m_eleGraphHeight - ( ( currentPoint.altitude() - m_axisY.minValue() ) / ( qMax<qreal>( 1.0, m_axisY.range() ) * m_shrinkFactorY ) ) * m_eleGraphHeight; painter->drawLine( m_leftGraphMargin + m_cursorPositionX - 5, ypos, m_leftGraphMargin + m_cursorPositionX + 5, ypos ); intervalStr.setNum( xpos * m_axisX.scale(), 'f', 2 ); intervalStr += ' ' + m_axisX.unit(); int currentStringBegin = m_leftGraphMargin + m_cursorPositionX - QFontMetricsF( font() ).width( intervalStr ) / 2; painter->drawText( currentStringBegin, contentSize().height() - 1.5 * m_fontHeight, intervalStr ); intervalStr.setNum( currentPoint.altitude(), 'f', 1 ); intervalStr += ' ' + m_axisY.unit(); if ( m_cursorPositionX + QFontMetricsF( font() ).width( intervalStr ) + m_leftGraphMargin < m_eleGraphWidth ) { currentStringBegin = ( m_leftGraphMargin + m_cursorPositionX + 5 + 2 ); } else { currentStringBegin = m_leftGraphMargin + m_cursorPositionX - 5 - QFontMetricsF( font() ).width( intervalStr ) * 1.5; } // Make sure the text still fits into the window while ( ypos < m_fontHeight ) { ypos++; } painter->drawText( currentStringBegin, ypos + m_fontHeight / 2, intervalStr ); } painter->restore(); }