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; }
void RoutingModel::exportGpx( QIODevice *device ) const { QString content( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" ); content += "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" creator=\"Marble\" version=\"1.1\" "; content += "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "; content += "xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 "; content += "http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"; content += "<metadata>\n <link href=\"http://edu.kde.org/marble\">\n "; content += "<text>Marble Virtual Globe</text>\n </link>\n</metadata>\n"; content += " <rte>\n <name>Route</name>\n"; bool hasAltitude = false; for ( int i=0; !hasAltitude && i<d->m_route.size(); ++i ) { hasAltitude = d->m_route.at( i ).maneuver().position().altitude() != 0.0; } for ( int i=0; i<d->m_route.size(); ++i ) { const Maneuver &maneuver = d->m_route.at( i ).maneuver(); qreal lon = maneuver.position().longitude( GeoDataCoordinates::Degree ); qreal lat = maneuver.position().latitude( GeoDataCoordinates::Degree ); QString const text = maneuver.instructionText(); content += QString( " <rtept lat=\"%1\" lon=\"%2\">\n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 ); content += QString( " <name>%1</name>\n").arg( text ); if ( hasAltitude ) { content += QString( " <ele>%1</ele>\n" ).arg( maneuver.position().altitude(), 0, 'f', 2 ); } content += QString( " </rtept>\n" ); } content += " </rte>\n"; content += "<trk>\n <name>Route</name>\n <trkseg>\n"; GeoDataLineString points = d->m_route.path(); hasAltitude = false; for ( int i=0; !hasAltitude && i<points.size(); ++i ) { hasAltitude = points[i].altitude() != 0.0; } for ( int i=0; i<points.size(); ++i ) { GeoDataCoordinates const &point = points[i]; qreal lon = point.longitude( GeoDataCoordinates::Degree ); qreal lat = point.latitude( GeoDataCoordinates::Degree ); content += QString( " <trkpt lat=\"%1\" lon=\"%2\">\n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 ); if ( hasAltitude ) { content += QString( " <ele>%1</ele>\n" ).arg( point.altitude(), 0, 'f', 2 ); } content += QString( " </trkpt>\n" ); } content += " </trkseg>\n </trk>\n"; content += "</gpx>\n"; device->write( content.toUtf8() ); }
void RoutingWidget::handleSearchResult( RoutingInputWidget *widget ) { d->setActiveInput( widget ); MarblePlacemarkModel *model = widget->searchResultModel(); if ( model->rowCount() ) { QString const results = tr( "%n placemarks found", "", model->rowCount() ); d->m_ui.resultLabel->setText( results ); d->m_ui.resultLabel->setVisible( true ); // Make sure we have a selection activatePlacemark( model->index( 0, 0 ) ); } else { QString const results = tr( "No placemark found" ); d->m_ui.resultLabel->setText( "<font color=\"red\">" + results + "</font>" ); d->m_ui.resultLabel->setVisible( true ); } GeoDataLineString placemarks; for ( int i = 0; i < model->rowCount(); ++i ) { QVariant data = model->index( i, 0 ).data( MarblePlacemarkModel::CoordinateRole ); if ( !data.isNull() ) { placemarks << qVariantValue<GeoDataCoordinates>( data ); } } if ( placemarks.size() > 1 ) { d->m_widget->centerOn( GeoDataLatLonBox::fromLineString( placemarks ) ); //d->m_ui.descriptionLabel->setVisible( false ); if ( MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen ) { d->m_ui.directionsListView->setVisible( true ); } } }
void PolylineAnnotation::updateRegions( GeoPainter *painter ) { if ( m_busy ) { return; } const GeoDataLineString line = static_cast<const GeoDataLineString>( *placemark()->geometry() ); if ( state() == SceneGraphicsItem::AddingNodes ) { // Create and update virtual nodes lists when being in the AddingPolgonNodes state, to // avoid overhead in other states. m_virtualNodesList.clear(); for ( int i = 0; i < line.size() - 1; ++i ) { const QRegion newRegion( painter->regionFromEllipse( line.at(i).interpolate( line.at(i+1), 0.5 ), hoveredDim, hoveredDim ) ); m_virtualNodesList.append( PolylineNode( newRegion ) ); } } // Update the polyline region; m_polylineRegion = painter->regionFromPolyline( line, 15 ); // Update the node lists. for ( int i = 0; i < m_nodesList.size(); ++i ) { const QRegion newRegion = m_nodesList.at(i).isSelected() ? painter->regionFromEllipse( line.at(i), selectedDim, selectedDim ) : painter->regionFromEllipse( line.at(i), regularDim, regularDim ); m_nodesList[i].setRegion( newRegion ); } }
bool GeoDataLineString::operator==( const GeoDataLineString &other ) const { if ( !GeoDataGeometry::equals(other) || size() != other.size() || tessellate() != other.tessellate() ) { return false; } const GeoDataLineStringPrivate* d = p(); const GeoDataLineStringPrivate* other_d = other.p(); 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 ) { if ( *itCoords != *otherItCoords ) { return false; } } Q_ASSERT ( itCoords == itEnd && otherItCoords == otherItEnd ); return true; }
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); }
void EditPolylineDialog::handleItemMoving( GeoDataPlacemark *item ) { if( item == d->m_placemark ) { d->m_nodeModel->clear(); if( d->m_placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType ) { GeoDataLineString *lineString = static_cast<GeoDataLineString*>( d->m_placemark->geometry() ); for( int i = 0; i < lineString->size(); ++i ) { d->m_nodeModel->addNode( lineString->at( i ) ); } } } }
GeoDataLatLonAltBox GeoDataLatLonAltBox::fromLineString( const GeoDataLineString& lineString ) { // If the line string is empty return a boundingbox that contains everything if ( lineString.size() == 0 ) { return GeoDataLatLonAltBox(); } const qreal altitude = lineString.first().altitude(); GeoDataLatLonAltBox temp ( GeoDataLatLonBox::fromLineString( lineString ), altitude, altitude ); qreal maxAltitude = altitude; qreal minAltitude = altitude; // If there's only a single node stored then the boundingbox only contains that point if ( lineString.size() == 1 ) { temp.setMinAltitude( minAltitude ); temp.setMaxAltitude( maxAltitude ); return temp; } QVector<GeoDataCoordinates>::ConstIterator it( lineString.constBegin() ); QVector<GeoDataCoordinates>::ConstIterator itEnd( lineString.constEnd() ); for ( ; it != itEnd; ++it ) { // Get coordinates and normalize them to the desired range. const qreal altitude = (it)->altitude(); // Determining the maximum and minimum latitude if ( altitude > maxAltitude ) maxAltitude = altitude; if ( altitude < minAltitude ) minAltitude = altitude; } temp.setMinAltitude( minAltitude ); temp.setMaxAltitude( maxAltitude ); return temp; }
bool PolylineAnnotation::processEditingOnMove( QMouseEvent *mouseEvent ) { if ( !m_viewport ) { return false; } qreal lon, lat; m_viewport->geoCoordinates( mouseEvent->pos().x(), mouseEvent->pos().y(), lon, lat, GeoDataCoordinates::Radian ); const GeoDataCoordinates newCoords( lon, lat ); if ( m_interactingObj == InteractingNode ) { GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); line->at(m_clickedNodeIndex) = newCoords; return true; } else if ( m_interactingObj == InteractingPolyline ) { GeoDataLineString *lineString = static_cast<GeoDataLineString*>( placemark()->geometry() ); const GeoDataLineString oldLineString = *lineString; lineString->clear(); const qreal deltaLat = lat - m_movedPointCoords.latitude(); const qreal deltaLon = lon - m_movedPointCoords.longitude(); Quaternion latRectAxis = Quaternion::fromEuler( 0, lon, 0); Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0); Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0); Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis; qreal lonRotated, latRotated; for ( int i = 0; i < oldLineString.size(); ++i ) { Quaternion qpos = oldLineString.at(i).quaternion(); qpos.rotateAroundAxis(rotAxis); qpos.getSpherical( lonRotated, latRotated ); GeoDataCoordinates movedPoint( lonRotated, latRotated, 0 ); lineString->append( movedPoint ); } m_movedPointCoords = newCoords; return true; } return dealWithHovering( mouseEvent ); }
bool PolylineAnnotation::processMergingOnPress( QMouseEvent *mouseEvent ) { if ( mouseEvent->button() != Qt::LeftButton ) { return false; } GeoDataLineString line = static_cast<GeoDataLineString>( *placemark()->geometry() ); const int index = nodeContains( mouseEvent->pos() ); if ( index == -1 ) { return false; } // If this is the first node selected to be merged. if ( m_firstMergedNode == -1 ) { m_firstMergedNode = index; m_nodesList[index].setFlag( PolylineNode::NodeIsMerged ); } else { Q_ASSERT( m_firstMergedNode != -1 ); // Clicking two times the same node results in unmarking it for merging. if ( m_firstMergedNode == index ) { m_nodesList[index].setFlag( PolylineNode::NodeIsMerged, false ); m_firstMergedNode = -1; return true; } // If these two nodes are the last ones remained as part of the polyline, remove // the whole polyline. if ( line.size() <= 2 ) { setRequest( SceneGraphicsItem::RemovePolylineRequest ); return true; } m_nodesList[index].setFlag( PolylineNode::NodeIsMerged ); m_secondMergedNode = index; delete m_animation; m_animation = new MergingPolylineNodesAnimation( this ); setRequest( SceneGraphicsItem::StartPolylineAnimation ); } return true; }
void PolylineAnnotation::deleteAllSelectedNodes() { if ( state() != SceneGraphicsItem::Editing ) { return; } GeoDataLineString *line = static_cast<GeoDataLineString*>( placemark()->geometry() ); for ( int i = 0; i < line->size(); ++i ) { if ( m_nodesList.at(i).isSelected() ) { if ( m_nodesList.size() <= 2 ) { setRequest( SceneGraphicsItem::RemovePolylineRequest ); return; } m_nodesList.removeAt( i ); line->remove( i ); --i; } } }
void EditPolylineDialog::checkFields() { bool ok = true; if ( d->m_name->text().isEmpty() ) { QMessageBox::warning( this, tr( "No name specified" ), tr( "Please specify a name for this polyline." ) ); ok = false; } else { if ( d->m_placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType ) { GeoDataLineString *lineString = static_cast<GeoDataLineString*>( d->m_placemark->geometry() ); if( lineString->size() < 2 ) { QMessageBox::warning( this, tr( "Not enough nodes specified." ), tr( "Please specify at least 2 nodes for the path by clicking on the map." ) ); ok = false; } } } if( ok ) { accept(); } }
void PolylineAnnotation::move( const GeoDataCoordinates &source, const GeoDataCoordinates &destination ) { GeoDataLineString *lineString = static_cast<GeoDataLineString*>( placemark()->geometry() ); GeoDataLineString oldLineString = *lineString; lineString->clear(); const qreal deltaLat = destination.latitude() - source.latitude(); const qreal deltaLon = destination.longitude() - source.longitude(); Quaternion latRectAxis = Quaternion::fromEuler( 0, destination.longitude(), 0); Quaternion latAxis = Quaternion::fromEuler( -deltaLat, 0, 0); Quaternion lonAxis = Quaternion::fromEuler(0, deltaLon, 0); Quaternion rotAxis = latRectAxis * latAxis * latRectAxis.inverse() * lonAxis; qreal lonRotated, latRotated; for ( int i = 0; i < oldLineString.size(); ++i ) { Quaternion qpos = oldLineString.at(i).quaternion(); qpos.rotateAroundAxis(rotAxis); qpos.getSpherical( lonRotated, latRotated ); GeoDataCoordinates movedPoint( lonRotated, latRotated, 0 ); lineString->append( movedPoint ); } }
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; }
void GeoDataLineStringPrivate::toDateLineCorrected( const GeoDataLineString & q, QVector<GeoDataLineString*> & lineStrings ) { const bool isClosed = q.isClosed(); const QVector<GeoDataCoordinates>::const_iterator itStartPoint = q.constBegin(); const QVector<GeoDataCoordinates>::const_iterator itEndPoint = q.constEnd(); QVector<GeoDataCoordinates>::const_iterator itPoint = itStartPoint; QVector<GeoDataCoordinates>::const_iterator itPreviousPoint = itPoint; TessellationFlags f = q.tessellationFlags(); GeoDataLineString * unfinishedLineString = 0; GeoDataLineString * dateLineCorrected = isClosed ? new GeoDataLinearRing( f ) : new GeoDataLineString( f ); qreal currentLon = 0.0; qreal previousLon = 0.0; int previousSign = 1; bool unfinished = false; for (; itPoint != itEndPoint; ++itPoint ) { currentLon = itPoint->longitude(); int currentSign = ( currentLon < 0.0 ) ? -1 : +1 ; if( itPoint == q.constBegin() ) { previousSign = currentSign; previousLon = currentLon; } // If we are crossing the date line ... if ( previousSign != currentSign && fabs(previousLon) + fabs(currentLon) > M_PI ) { unfinished = !unfinished; GeoDataCoordinates previousTemp; GeoDataCoordinates currentTemp; interpolateDateLine( *itPreviousPoint, *itPoint, previousTemp, currentTemp, q.tessellationFlags() ); *dateLineCorrected << previousTemp; if ( isClosed && unfinished ) { // 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 ... unfinishedLineString = dateLineCorrected; // ... and start a new linear ring for now. dateLineCorrected = new GeoDataLinearRing( f ); } else { // 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; } // If it's a finished linear ring restore the "remembered" unfinished String if ( isClosed && !unfinished && unfinishedLineString ) { dateLineCorrected = unfinishedLineString; } else { // if it's a line string just create a new line string. dateLineCorrected = new GeoDataLineString( f ); } } *dateLineCorrected << currentTemp; *dateLineCorrected << *itPoint; } else { *dateLineCorrected << *itPoint; } previousSign = currentSign; previousLon = currentLon; itPreviousPoint = itPoint; } // 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; } lineStrings << dateLineCorrected; }
bool CylindricalProjectionPrivate::lineStringToPolygon( const GeoDataLineString &lineString, const ViewportParams *viewport, QVector<QPolygonF *> &polygons ) const { const TessellationFlags f = lineString.tessellationFlags(); qreal x = 0; qreal y = 0; qreal previousX = -1.0; qreal previousY = -1.0; int mirrorCount = 0; qreal distance = repeatDistance( viewport ); polygons.append( new QPolygonF ); GeoDataLineString::ConstIterator itCoords = lineString.constBegin(); GeoDataLineString::ConstIterator itPreviousCoords = lineString.constBegin(); GeoDataLineString::ConstIterator itBegin = lineString.constBegin(); GeoDataLineString::ConstIterator itEnd = lineString.constEnd(); bool processingLastNode = false; // We use a while loop to be able to cover linestrings as well as linear rings: // Linear rings require to tessellate the path from the last node to the first node // which isn't really convenient to achieve with a for loop ... const bool isLong = lineString.size() > 10; const int maximumDetail = levelForResolution(viewport->angularResolution()); // The first node of optimized linestrings has a non-zero detail value. const bool hasDetail = itBegin->detail() != 0; while ( itCoords != itEnd ) { // Optimization for line strings with a big amount of nodes bool skipNode = (hasDetail ? itCoords->detail() > maximumDetail : itCoords != itBegin && isLong && !processingLastNode && !viewport->resolves( *itPreviousCoords, *itCoords ) ); if ( !skipNode ) { Q_Q( const CylindricalProjection ); q->screenCoordinates( *itCoords, viewport, x, y ); // Initializing variables that store the values of the previous iteration if ( !processingLastNode && itCoords == itBegin ) { itPreviousCoords = itCoords; previousX = x; previousY = y; } // This if-clause contains the section that tessellates the line // segments of a linestring. If you are about to learn how the code of // this class works you can safely ignore this section for a start. if ( lineString.tessellate() ) { mirrorCount = tessellateLineSegment( *itPreviousCoords, previousX, previousY, *itCoords, x, y, polygons, viewport, f, mirrorCount, distance ); } else { // special case for polys which cross dateline but have no Tesselation Flag // the expected rendering is a screen coordinates straight line between // points, but in projections with repeatX things are not smooth mirrorCount = crossDateLine( *itPreviousCoords, *itCoords, x, y, polygons, mirrorCount, distance ); } itPreviousCoords = itCoords; previousX = x; previousY = y; } // Here we modify the condition to be able to process the // first node after the last node in a LinearRing. if ( processingLastNode ) { break; } ++itCoords; if ( itCoords == itEnd && lineString.isClosed() ) { itCoords = itBegin; processingLastNode = true; } } GeoDataLatLonAltBox box = lineString.latLonAltBox(); // Closing e.g. in the Antarctica case. // This code makes the assumption that // - the first node is located at 180 E // - and the last node is located at 180 W // TODO: add a similar pattern in the crossDateLine() code. /* if( lineString.isClosed() && box.width() == 2*M_PI ) { QPolygonF *poly = polygons.last(); if( box.containsPole( NorthPole ) ) { qreal topMargin = 0.0; qreal dummy = 0.0; q_ptr->screenCoordinates(0.0, q_ptr->maxLat(), viewport, topMargin, dummy ); poly->push_back( QPointF( poly->last().x(), topMargin ) ); poly->push_back( QPointF( poly->first().x(), topMargin ) ); } else { qreal bottomMargin = 0.0; qreal dummy = 0.0; q_ptr->screenCoordinates(0.0, q_ptr->minLat(), viewport, bottomMargin, dummy ); poly->push_back( QPointF( poly->last().x(), bottomMargin ) ); poly->push_back( QPointF( poly->first().x(), bottomMargin ) ); } } */ repeatPolygons( viewport, polygons ); return polygons.isEmpty(); }
void PolylineAnnotation::drawNodes( GeoPainter *painter ) { // These are the 'real' dimensions of the drawn nodes. The ones which have class scope are used // to generate the regions and they are a little bit larger, because, for example, it would be // a little bit too hard to select nodes. static const int d_regularDim = 10; static const int d_selectedDim = 10; static const int d_mergedDim = 20; static const int d_hoveredDim = 20; const GeoDataLineString line = static_cast<const GeoDataLineString>( *placemark()->geometry() ); QColor glowColor = QApplication::palette().highlightedText().color(); glowColor.setAlpha(120); for ( int i = 0; i < line.size(); ++i ) { // The order here is important, because a merged node can be at the same time selected. if ( m_nodesList.at(i).isBeingMerged() ) { painter->setBrush( mergedColor ); painter->drawEllipse( line.at(i), d_mergedDim, d_mergedDim ); } else if ( m_nodesList.at(i).isSelected() ) { painter->setBrush( selectedColor ); painter->drawEllipse( line.at(i), d_selectedDim, d_selectedDim ); if ( m_nodesList.at(i).isEditingHighlighted() || m_nodesList.at(i).isMergingHighlighted() ) { QPen defaultPen = painter->pen(); QPen newPen; newPen.setWidth( defaultPen.width() + 3 ); newPen.setColor( glowColor ); painter->setBrush( Qt::NoBrush ); painter->setPen( newPen ); painter->drawEllipse( line.at(i), d_selectedDim + 2, d_selectedDim + 2 ); painter->setPen( defaultPen ); } } else { painter->setBrush( regularColor ); painter->drawEllipse( line.at(i), d_regularDim, d_regularDim ); if ( m_nodesList.at(i).isEditingHighlighted() || m_nodesList.at(i).isMergingHighlighted() ) { QPen defaultPen = painter->pen(); QPen newPen; newPen.setWidth( defaultPen.width() + 3 ); newPen.setColor( glowColor ); painter->setPen( newPen ); painter->setBrush( Qt::NoBrush ); painter->drawEllipse( line.at(i), d_regularDim + 2, d_regularDim + 2 ); painter->setPen( defaultPen ); } } } if ( m_virtualHoveredNode != -1 ) { painter->setBrush( hoveredColor ); GeoDataCoordinates newCoords; if ( m_virtualHoveredNode + 1 ) { newCoords = line.at( m_virtualHoveredNode + 1 ).interpolate( line.at( m_virtualHoveredNode ), 0.5 ); } else { newCoords = line.first().interpolate( line.last(), 0.5 ); } painter->drawEllipse( newCoords, d_hoveredDim, d_hoveredDim ); } }