Fix saving/loading 3D map view viewpoint

Previously the point was saved in origin-relative coordinates. Now since
we pick the origin automatically, it can be changed by various
initialisation routines, so the coordinates will be interpreted relative
to the wrong origin.

This commit changes the format to use map coordinates instead, and saves
the "original" origin from the XML for interpreting older projects.
This commit is contained in:
David Koňařík 2025-06-26 12:03:52 +02:00 committed by Martin Dobias
parent 429500187c
commit 19ded7be74
6 changed files with 38 additions and 28 deletions

View File

@ -153,7 +153,7 @@ pointing towards north. The angle should range from 0 to 360.
%Docstring
Writes camera configuration to the given DOM element
%End
void readXml( const QDomElement &elem );
void readXml( const QDomElement &elem, QgsVector3D savedOrigin );
%Docstring
Reads camera configuration from the given DOM element
%End

View File

@ -153,7 +153,7 @@ pointing towards north. The angle should range from 0 to 360.
%Docstring
Writes camera configuration to the given DOM element
%End
void readXml( const QDomElement &elem );
void readXml( const QDomElement &elem, QgsVector3D savedOrigin );
%Docstring
Reads camera configuration from the given DOM element
%End

View File

@ -264,38 +264,41 @@ void QgsCameraController::setCameraPose( const QgsCameraPose &camPose, bool forc
QDomElement QgsCameraController::writeXml( QDomDocument &doc ) const
{
QDomElement elemCamera = doc.createElement( QStringLiteral( "camera" ) );
QgsVector3D centerPoint;
switch ( mScene->mapSettings()->sceneMode() )
{
case Qgis::SceneMode::Local:
centerPoint = mCameraPose.centerPoint();
break;
case Qgis::SceneMode::Globe:
// Save center point in map coordinates, since our world origin won't be
// the same on loading
centerPoint = mCameraPose.centerPoint() + mOrigin;
break;
}
elemCamera.setAttribute( QStringLiteral( "x" ), centerPoint.x() );
elemCamera.setAttribute( QStringLiteral( "y" ), centerPoint.z() );
elemCamera.setAttribute( QStringLiteral( "elev" ), centerPoint.y() );
// Save center point in map coordinates, since our world origin won't be
// the same on loading
QgsVector3D centerPoint = mCameraPose.centerPoint() + mOrigin;
elemCamera.setAttribute( QStringLiteral( "xMap" ), centerPoint.x() );
elemCamera.setAttribute( QStringLiteral( "yMap" ), centerPoint.z() );
elemCamera.setAttribute( QStringLiteral( "elevMap" ), centerPoint.y() );
elemCamera.setAttribute( QStringLiteral( "dist" ), mCameraPose.distanceFromCenterPoint() );
elemCamera.setAttribute( QStringLiteral( "pitch" ), mCameraPose.pitchAngle() );
elemCamera.setAttribute( QStringLiteral( "yaw" ), mCameraPose.headingAngle() );
return elemCamera;
}
void QgsCameraController::readXml( const QDomElement &elem )
void QgsCameraController::readXml( const QDomElement &elem, QgsVector3D savedOrigin )
{
const float x = elem.attribute( QStringLiteral( "x" ) ).toFloat();
const float y = elem.attribute( QStringLiteral( "y" ) ).toFloat();
const float elev = elem.attribute( QStringLiteral( "elev" ) ).toFloat();
const float dist = elem.attribute( QStringLiteral( "dist" ) ).toFloat();
const float pitch = elem.attribute( QStringLiteral( "pitch" ) ).toFloat();
const float yaw = elem.attribute( QStringLiteral( "yaw" ) ).toFloat();
QgsVector3D centerPoint( x, elev, y );
if ( mScene->mapSettings()->sceneMode() == Qgis::SceneMode::Globe )
centerPoint = centerPoint - mOrigin;
QgsVector3D centerPoint;
if ( elem.hasAttribute( "xMap" ) )
{
// Prefer newer point saved in map coordinates ...
const float x = elem.attribute( QStringLiteral( "xMap" ) ).toFloat();
const float y = elem.attribute( QStringLiteral( "yMap" ) ).toFloat();
const float elev = elem.attribute( QStringLiteral( "elevMap" ) ).toFloat();
centerPoint = QgsVector3D( x, elev, y ) - mOrigin;
}
else
{
// ... but allow use of older origin-relative coordinates.
const float x = elem.attribute( QStringLiteral( "x" ) ).toFloat();
const float y = elem.attribute( QStringLiteral( "y" ) ).toFloat();
const float elev = elem.attribute( QStringLiteral( "elev" ) ).toFloat();
centerPoint = QgsVector3D( x, elev, y ) - savedOrigin + mOrigin;
}
setLookingAtPoint( centerPoint, dist, pitch, yaw );
}

View File

@ -174,7 +174,7 @@ class _3D_EXPORT QgsCameraController : public QObject
//! Writes camera configuration to the given DOM element
QDomElement writeXml( QDomDocument &doc ) const;
//! Reads camera configuration from the given DOM element
void readXml( const QDomElement &elem );
void readXml( const QDomElement &elem, QgsVector3D savedOrigin );
//! Zoom the map by \a factor
void zoom( float factor );

View File

@ -9145,7 +9145,10 @@ Qgs3DMapCanvasWidget *QgisApp::duplicate3DMapView( const QString &existingViewNa
Qgs3DMapSettings *map = new Qgs3DMapSettings( *widget->mapCanvas3D()->mapSettings() );
newCanvasWidget->setMapSettings( map );
newCanvasWidget->mapCanvas3D()->cameraController()->readXml( widget->mapCanvas3D()->cameraController()->writeXml( doc ) );
newCanvasWidget->mapCanvas3D()->cameraController()->readXml(
widget->mapCanvas3D()->cameraController()->writeXml( doc ),
widget->mapCanvas3D()->mapSettings()->origin()
);
newCanvasWidget->animationWidget()->setAnimation( widget->animationWidget()->animation() );
connect( QgsProject::instance(), &QgsProject::transformContextChanged, map, [map] {
@ -16237,12 +16240,14 @@ void QgisApp::read3DMapViewSettings( Qgs3DMapCanvasWidget *widget, QDomElement &
map->setBackgroundColor( mMapCanvas->canvasColor() );
map->setOutputDpi( QGuiApplication::primaryScreen()->logicalDotsPerInch() );
QgsVector3D savedOrigin = map->origin();
widget->setMapSettings( map );
QDomElement elemCamera = elem3DMap.firstChildElement( QStringLiteral( "camera" ) );
if ( !elemCamera.isNull() )
{
widget->mapCanvas3D()->cameraController()->readXml( elemCamera );
widget->mapCanvas3D()->cameraController()->readXml( elemCamera, savedOrigin );
}
// not nice hack to ensure camera navigation mode is correctly setup to previous mode
widget->mapCanvas3D()->mapSettings()->emit cameraNavigationModeChanged();

View File

@ -139,12 +139,14 @@ void initCanvas3D( Qgs3DMapCanvas *canvas, bool isGlobe, QString viewIdxStr )
map->setMapThemeCollection( QgsProject::instance()->mapThemeCollection() );
map->setOutputDpi( QGuiApplication::primaryScreen()->logicalDotsPerInch() );
QgsVector3D savedOrigin = map->origin();
canvas->setMapSettings( map );
QDomElement elemCamera = viewXml.firstChildElement( QStringLiteral( "camera" ) );
if ( !elemCamera.isNull() )
{
canvas->cameraController()->readXml( elemCamera );
canvas->cameraController()->readXml( elemCamera, savedOrigin );
}
}