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 ); }
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; }
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; }