void FoursquareModel::getAdditionalItems( const GeoDataLatLonAltBox& box, qint32 number ) { if( marbleModel()->planetId() != "earth" ) { return; } QString clientId = "YPRWSYFW1RVL4PJQ2XS5G14RTOGTHOKZVHC1EP5KCCCYQPZF"; QString clientSecret = "5L2JDCAYQCEJWY5FNDU4A1RWATE4E5FIIXXRM41YBTFSERUH"; QString apiUrl( "https://api.foursquare.com/v2/venues/search" ); qreal const distanceLon = marbleModel()->planetRadius() * distanceSphere( box.west(), box.north(), box.east(), box.north() ); qreal const distanceLat = marbleModel()->planetRadius() * distanceSphere( box.west(), box.north(), box.west(), box.south() ); qreal const area = distanceLon * distanceLat; if ( area > 10 * 1000 * KM2METER * KM2METER ) { // Large area (> 10.000 km^2) => too large for bbox queries apiUrl += "?ll=" + QString::number( box.center().latitude(Marble::GeoDataCoordinates::Degree) ); apiUrl += ',' + QString::number( box.center().longitude(Marble::GeoDataCoordinates::Degree) ); apiUrl += "&intent=checkin"; } else { apiUrl += "?ne=" + QString::number( box.north(Marble::GeoDataCoordinates::Degree) ); apiUrl += ',' + QString::number( box.east(Marble::GeoDataCoordinates::Degree) ); apiUrl += "&sw=" + QString::number( box.south(Marble::GeoDataCoordinates::Degree) ); apiUrl += ',' + QString::number( box.west(Marble::GeoDataCoordinates::Degree) ); apiUrl += "&intent=browse"; } apiUrl += "&limit=" + QString::number( number ); apiUrl += "&client_id=" + clientId; apiUrl += "&client_secret=" + clientSecret; apiUrl += "&v=20120601"; downloadDescriptionFile( QUrl( apiUrl ) ); }
int RoutingModel::rightNeighbor( const GeoDataCoordinates &position, RouteRequest const *const route ) const { Q_ASSERT( route && "Must not pass a null route "); // Quick result for trivial cases if ( route->size() < 3 ) { return route->size() - 1; } // Generate an ordered list of all waypoints GeoDataLineString points = d->m_route.path(); QMap<int,int> mapping; // Force first mapping point to match the route start mapping[0] = 0; // Calculate the mapping between waypoints and via points // Need two for loops to avoid getting stuck in local minima for ( int j=1; j<route->size()-1; ++j ) { qreal minDistance = -1.0; for ( int i=mapping[j-1]; i<points.size(); ++i ) { qreal distance = distanceSphere( points[i], route->at(j) ); if (minDistance < 0.0 || distance < minDistance ) { mapping[j] = i; minDistance = distance; } } } // Determine waypoint with minimum distance to the provided position qreal minWaypointDistance = -1.0; int waypoint=0; for ( int i=0; i<points.size(); ++i ) { qreal waypointDistance = distanceSphere( points[i], position ); if ( minWaypointDistance < 0.0 || waypointDistance < minWaypointDistance ) { minWaypointDistance = waypointDistance; waypoint = i; } } // Force last mapping point to match the route destination mapping[route->size()-1] = points.size()-1; // Determine neighbor based on the mapping QMap<int, int>::const_iterator iter = mapping.constBegin(); for ( ; iter != mapping.constEnd(); ++iter ) { if ( iter.value() > waypoint ) { int index = iter.key(); Q_ASSERT( index >= 0 && index <= route->size() ); return index; } } return route->size()-1; }
qreal RoutingPluginPrivate::nextInstructionDistance() const { GeoDataCoordinates position = m_routingModel->route().position(); GeoDataCoordinates interpolated = m_routingModel->route().positionOnRoute(); GeoDataCoordinates onRoute = m_routingModel->route().currentWaypoint(); qreal planetRadius = m_marbleWidget->model()->planet()->radius(); qreal distance = planetRadius * ( distanceSphere( position, interpolated ) + distanceSphere( interpolated, onRoute ) ); const RouteSegment &segment = m_routingModel->route().currentSegment(); for (int i=0; i<segment.path().size(); ++i) { if (segment.path()[i] == onRoute) { return distance + segment.path().length( planetRadius, i ); } } return distance; }
void SearchRunnerManager::Private::addSearchResult( const QVector<GeoDataPlacemark *> &result ) { mDebug() << "Runner reports" << result.size() << " search results"; if( result.isEmpty() ) return; m_modelMutex.lock(); int start = m_placemarkContainer.size(); int count = 0; bool distanceCompare = m_marbleModel->planet() != 0; for( int i=0; i<result.size(); ++i ) { bool same = false; for ( int j=0; j<m_placemarkContainer.size(); ++j ) { if ( distanceCompare && ( distanceSphere( result[i]->coordinate(), m_placemarkContainer[j]->coordinate() ) * m_marbleModel->planet()->radius() < 1 ) ) { same = true; } } if ( !same ) { m_placemarkContainer.append( result[i] ); ++count; } } m_model.addPlacemarks( start, count ); m_modelMutex.unlock(); emit q->searchResultChanged( &m_model ); emit q->searchResultChanged( m_placemarkContainer ); }
void AutoNavigation::Private::adjustZoom( const GeoDataCoordinates ¤tPosition, qreal speed ) { qreal currentX = 0; qreal currentY = 0; if( !m_viewport->screenCoordinates(currentPosition, currentX, currentY ) ) { return; } const GeoDataCoordinates destination = findIntersection( currentX, currentY ); qreal greatCircleDistance = distanceSphere( currentPosition, destination ); qreal radius = m_model->planetRadius(); qreal distance = greatCircleDistance * radius; if( speed != 0 ) { // time (in seconds) remaining to reach the border of the map qreal remainingTime = distance / speed; // tolerance time limits (in seconds) before auto zooming qreal thresholdLow = 15; qreal thresholdHigh = 120; m_selfInteraction = true; if ( remainingTime < thresholdLow ) { emit m_parent->zoomOut( Instant ); } else if ( remainingTime > thresholdHigh ) { emit m_parent->zoomIn( Instant ); } m_selfInteraction = false; } }
void RouteSimulationPositionProviderPlugin::update() { ++m_currentIndex; if ( m_currentIndex >= 0 && m_currentIndex < m_lineString.size() ) { if ( m_status != PositionProviderStatusAvailable ) { m_status = PositionProviderStatusAvailable; emit statusChanged( PositionProviderStatusAvailable ); } GeoDataCoordinates newPosition = m_lineString.at( m_currentIndex ); const QDateTime newDateTime = QDateTime::currentDateTime(); if ( m_currentPosition.isValid() ) { m_speed = distanceSphere( m_currentPosition, newPosition ) * m_marbleModel->planetRadius() / ( m_currentDateTime.msecsTo( newDateTime ) ) * 1000; m_direction = m_currentPosition.bearing( newPosition, GeoDataCoordinates::Degree, GeoDataCoordinates::FinalBearing ); } m_currentPosition = newPosition; m_currentDateTime = newDateTime; emit positionChanged( position(), accuracy() ); } else { // Repeat from start m_currentIndex = -1; if ( m_status != PositionProviderStatusUnavailable ) { m_status = PositionProviderStatusUnavailable; emit statusChanged( PositionProviderStatusUnavailable ); } } QTimer::singleShot( 1000.0 / c_frequency, this, SLOT(update()) ); }
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 int startLevel = levelForResolution( ( lineString.latLonAltBox().width() + lineString.latLonAltBox().height() ) / 2 ); int currentLevel = startLevel; int maxLevel = startLevel; 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); }
QString OsmDatabase::formatDistance( const GeoDataCoordinates &a, const GeoDataCoordinates &b ) { qreal distance = EARTH_RADIUS * distanceSphere( a, b); int precision = 0; QString distanceUnit = QLatin1String( "m" ); if ( MarbleGlobal::getInstance()->locale()->measurementSystem() == MarbleLocale::ImperialSystem ) { precision = 1; distanceUnit = "mi"; distance *= METER2KM; distance *= KM2MI; } else if (MarbleGlobal::getInstance()->locale()->measurementSystem() == MarbleLocale::MetricSystem) { if ( distance >= 1000 ) { distance /= 1000; distanceUnit = "km"; precision = 1; } else if ( distance >= 200 ) { distance = 50 * qRound( distance / 50 ); } else if ( distance >= 100 ) { distance = 25 * qRound( distance / 25 ); } else { distance = 10 * qRound( distance / 10 ); } } else if (MarbleGlobal::getInstance()->locale()->measurementSystem() == MarbleLocale::NauticalSystem) { precision = 2; distanceUnit = "nm"; distance *= METER2KM; distance *= KM2NM; } QString const fuzzyDistance = QString( "%1 %2" ).arg( distance, 0, 'f', precision ).arg( distanceUnit ); int direction = 180 + bearing( a, b ) * RAD2DEG; QString heading = QObject::tr( "north" ); if ( direction > 337 ) { heading = QObject::tr( "north" ); } else if ( direction > 292 ) { heading = QObject::tr( "north-west" ); } else if ( direction > 247 ) { heading = QObject::tr( "west" ); } else if ( direction > 202 ) { heading = QObject::tr( "south-west" ); } else if ( direction > 157 ) { heading = QObject::tr( "south" ); } else if ( direction > 112 ) { heading = QObject::tr( "south-east" ); } else if ( direction > 67 ) { heading = QObject::tr( "east" ); } else if ( direction > 22 ) { heading = QObject::tr( "north-east" ); } return fuzzyDistance + QLatin1Char(' ') + heading; }
void RoutingPluginPrivate::updateDestinationInformation() { if ( m_routingModel->route().currentSegment().isValid() ) { qreal remaining = remainingDistance(); qreal distanceLeft = nextInstructionDistance(); m_audio->update( m_routingModel->route(), distanceLeft, remaining, m_routingModel->deviatedFromRoute() ); m_nearNextInstruction = distanceLeft < thresholdDistance; QString pixmapHtml = "<img src=\":/flag.png\" /><br />"; m_widget.destinationDistanceLabel->setText( pixmapHtml + richText( fuzzyDistance( remaining ) ) ); m_widget.instructionIconLabel->setEnabled( m_nearNextInstruction ); m_widget.progressBar->setMaximum( thresholdDistance ); m_widget.progressBar->setValue( qRound( distanceLeft ) ); updateButtonVisibility(); QString pixmap = MarbleDirs::path( "bitmaps/routing_step.png" ); pixmapHtml = QString( "<img src=\"%1\" />" ).arg( pixmap ); qreal planetRadius = m_marbleWidget->model()->planet()->radius(); GeoDataCoordinates const onRoute = m_routingModel->route().positionOnRoute(); GeoDataCoordinates const ego = m_routingModel->route().position(); qreal const distanceToRoute = planetRadius * distanceSphere( ego, onRoute ); if ( !m_routingModel->route().currentSegment().isValid() ) { m_widget.instructionLabel->setText( richText( QObject::tr( "Calculate a route to get directions." ) ) ); m_widget.instructionIconLabel->setText( pixmapHtml ); } else if ( distanceToRoute > 300.0 ) { m_widget.instructionLabel->setText( richText( QObject::tr( "Route left." ) ) ); m_widget.instructionIconLabel->setText( pixmapHtml ); } else if ( !m_routingModel->route().currentSegment().nextRouteSegment().isValid() ) { m_widget.instructionLabel->setText( richText( QObject::tr( "Destination ahead." ) ) ); m_widget.instructionIconLabel->setText( pixmapHtml ); } else { pixmap = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().directionPixmap(); QString const instructionText = m_routingModel->route().currentSegment().nextRouteSegment().maneuver().instructionText(); m_widget.instructionLabel->setText( richText( "%1" ).arg( instructionText ) ); pixmapHtml = QString( "<p align=\"center\"><img src=\"%1\" /><br />%2</p>" ).arg( pixmap ); m_widget.instructionIconLabel->setText( pixmapHtml.arg( richText( fuzzyDistance( distanceLeft ) ) ) ); if( remaining > 50 ) { m_routeCompleted = false; } else { if ( !m_routeCompleted ) { QString content = QObject::tr( "Arrived at destination. <a href=\"#reverse\">Calculate the way back.</a>" ); m_widget.instructionLabel->setText( richText( "%1" ).arg( content ) ); } m_routeCompleted = true; } } forceRepaint(); } }
PeakAnalyzer::Peaks PeakAnalyzer::peaksNear(const GeoDataPlacemark* placemark, const Peaks &peaks, double maxDistance) { // If this turns out to become a bottleneck due to quadratic runtime, use kd-tree via nanoflann from // https://github.com/jlblancoc/nanoflann to speed it up. Peaks neighbors; for (auto peak: peaks) { if (distanceSphere(peak->coordinate(), placemark->coordinate()) < maxDistance) { neighbors << peak; } } return neighbors; }
void RoutingModelPrivate::updateViaPoints( const GeoDataCoordinates &position ) { // Mark via points visited after approaching them in a range of 500m or less qreal const threshold = 500 / EARTH_RADIUS; for( int i=0; i<m_request->size(); ++i ) { if ( !m_request->visited( i ) ) { if ( distanceSphere( position, m_request->at( i ) ) < threshold ) { m_request->setVisited( i, true ); } } } }
void NavigationPrivate::updateNextInstructionDistance( const Marble::Route &route ) { const Marble::GeoDataCoordinates position = route.position(); const Marble::GeoDataCoordinates interpolated = route.positionOnRoute(); const Marble::GeoDataCoordinates onRoute = route.currentWaypoint(); qreal planetRadius = 0; if (model()){ planetRadius = model()->planet()->radius(); } qreal distance = planetRadius * ( distanceSphere( position, interpolated ) + distanceSphere( interpolated, onRoute ) ); qreal remaining = 0.0; const Marble::RouteSegment &segment = route.currentSegment(); for ( int i=0; i<segment.path().size(); ++i ) { if ( segment.path()[i] == onRoute ) { distance += segment.path().length( planetRadius, i ); break; } } bool upcoming = false; for ( int i=0; i<route.size(); ++i ) { const Marble::RouteSegment &segment = route.at( i ); if ( upcoming ) { remaining += segment.path().length( planetRadius ); } if ( segment == route.currentSegment() ) { upcoming = true; } } m_nextInstructionDistance = distance; m_destinationDistance = distance + remaining; }
qreal GeoDataLineString::length( qreal planetRadius, int offset ) const { if( offset < 0 || offset >= size() ) { return 0; } qreal length = 0.0; 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 ) { length += distanceSphere( vector[i-1], vector[i] ); } return planetRadius * length; }
qreal AlternativeRoutesModelPrivate::distance( const GeoDataCoordinates &satellite, const GeoDataCoordinates &lineA, const GeoDataCoordinates &lineB ) { qreal dist = distanceSphere( lineA, satellite ); qreal bearA = bearing( lineA, satellite ); qreal bearB = bearing( lineA, lineB ); qreal result = asin( sin ( dist ) * sin( bearB - bearA ) ); Q_ASSERT( qMax<qreal>( distanceSphere(satellite, lineA), distanceSphere(satellite, lineB) ) >= qAbs<qreal>(result) ); result = acos( cos( dist ) / cos( result ) ); /** @todo: This is a naive approach. Look into the maths. */ qreal final = qMin<qreal>( distanceSphere( satellite, lineA ), distanceSphere( satellite, lineB ) ); if ( result >= 0 && result <= distanceSphere( lineA, lineB ) ) { GeoDataCoordinates nearest = coordinates( lineA, result, bearB ); return qMin<qreal>( final, distanceSphere( satellite, nearest ) ); } else {
void RoutingModel::updatePosition( GeoDataCoordinates location, qreal /*speed*/ ) { d->m_route.setPosition( location ); d->updateViaPoints( location ); qreal planetRadius = d->m_marbleModel->planet()->radius(); qreal distance = planetRadius * distanceSphere( location, d->m_route.positionOnRoute() ); emit positionChanged(); qreal deviation = 0.0; if ( d->m_positionTracking && d->m_positionTracking->accuracy().vertical > 0.0 ) { deviation = qMax<qreal>( d->m_positionTracking->accuracy().vertical, d->m_positionTracking->accuracy().horizontal ); } qreal const threshold = deviation + 100.0; RoutingModelPrivate::RouteDeviation const deviated = distance < threshold ? RoutingModelPrivate::OnRoute : RoutingModelPrivate::OffRoute; if ( d->m_deviation != deviated ) { d->m_deviation = deviated; emit deviatedFromRoute( deviated == RoutingModelPrivate::OffRoute ); } }
void PeakAnalyzer::dbScan(const Peaks &peaks, double maxDistance, int minPoints) { QSet<GeoDataPlacemark*> visited; QMap<GeoDataPlacemark*, PeakCluster*> associations; Peaks noise; PeakClusters clusters; for(auto peak: peaks) { if (visited.contains(peak)) { continue; } visited << peak; auto neighbors = peaksNear(peak, peaks, maxDistance); if (neighbors.size() < minPoints) { noise << peak; } else { PeakCluster* fit = nullptr; for (auto &cluster: clusters) { for (auto placemark: cluster) { if (distanceSphere(peak->coordinate(), placemark->coordinate()) < maxDistance) { fit = &cluster; } } } if (!fit) { clusters << PeakCluster(); fit = &clusters.last(); } while (!neighbors.isEmpty()) { auto neighbor = neighbors.front(); neighbors.pop_front(); if (!visited.contains(neighbor)) { visited << neighbor; auto const moreNeighbors = peaksNear(neighbor, peaks, maxDistance); if (moreNeighbors.size() >= minPoints) { neighbors += moreNeighbors; } } if (associations[neighbor] == nullptr) { *fit << neighbor; associations[neighbor] = fit; } } } } for (auto &cluster: clusters) { Q_ASSERT(!cluster.isEmpty()); std::sort(cluster.begin(), cluster.end(), [](GeoDataPlacemark* a, GeoDataPlacemark* b) { return a->coordinate().altitude() > b->coordinate().altitude(); }); bool first = true; for (auto peak: cluster) { peak->osmData().addTag(QLatin1String("marbleZoomLevel"), first ? QLatin1String("11") : QLatin1String("13")); first = false; } } for (auto peak: noise) { peak->osmData().addTag(QLatin1String("marbleZoomLevel"), QLatin1String("11")); } }