1 /***************************************************************************
2   qgslayout3dmapwidget.cpp
3   --------------------------------------
4   Date                 : August 2018
5   Copyright            : (C) 2018 by Martin Dobias
6   Email                : wonder dot sk at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "qgslayout3dmapwidget.h"
17 
18 #include "qgisapp.h"
19 #include "qgs3dmapcanvas.h"
20 #include "qgs3dmapcanvasdockwidget.h"
21 #include "qgs3dmapsettings.h"
22 #include "qgscameracontroller.h"
23 #include <QMenu>
24 
25 
_normalizedAngle(float x)26 float _normalizedAngle( float x )
27 {
28   x = std::fmod( x, 360 );
29   if ( x < 0 ) x += 360;
30   return x;
31 }
32 
33 template<typename Func1>
_prepare3DViewsMenu(QMenu * menu,QgsLayout3DMapWidget * w,Func1 slot)34 void _prepare3DViewsMenu( QMenu *menu, QgsLayout3DMapWidget *w, Func1 slot )
35 {
36   QObject::connect( menu, &QMenu::aboutToShow, w, [menu, w, slot]
37   {
38     const QList<Qgs3DMapCanvasDockWidget *> lst = QgisApp::instance()->findChildren<Qgs3DMapCanvasDockWidget *>();
39     menu->clear();
40     for ( auto dock : lst )
41     {
42       QAction *a = menu->addAction( dock->mapCanvas3D()->objectName(), w, slot );
43       // need to use a custom property for identification because Qt likes to add "&" to the action text
44       a->setProperty( "name", dock->mapCanvas3D()->objectName() );
45     }
46     if ( lst.isEmpty() )
47     {
48       menu->addAction( QObject::tr( "No 3D maps defined" ) )->setEnabled( false );
49     }
50   } );
51 }
52 
_dock3DViewFromSender(QObject * sender)53 Qgs3DMapCanvasDockWidget *_dock3DViewFromSender( QObject *sender )
54 {
55   QAction *action = qobject_cast<QAction *>( sender );
56   if ( !action )
57     return nullptr;
58 
59   QString actionText = action->property( "name" ).toString();
60   const QList<Qgs3DMapCanvasDockWidget *> lst = QgisApp::instance()->findChildren<Qgs3DMapCanvasDockWidget *>();
61   for ( auto dock : lst )
62   {
63     QString objName = dock->mapCanvas3D()->objectName();
64     if ( objName == actionText )
65     {
66       return dock;
67     }
68   }
69   return nullptr;
70 }
71 
72 
QgsLayout3DMapWidget(QgsLayoutItem3DMap * map3D)73 QgsLayout3DMapWidget::QgsLayout3DMapWidget( QgsLayoutItem3DMap *map3D )
74   : QgsLayoutItemBaseWidget( nullptr, map3D )
75   , mMap3D( map3D )
76 {
77   setupUi( this );
78 
79   mCenterXSpinBox->setClearValue( 0 );
80   mCenterYSpinBox->setClearValue( 0 );
81   mCenterZSpinBox->setClearValue( 0 );
82   mDistanceToCenterSpinBox->setClearValue( 1000 );
83 
84   //add widget for general composer item properties
85   mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, map3D );
86   mainLayout->addWidget( mItemPropertiesWidget );
87 
88   mMenu3DCanvases = new QMenu( this );
89   mCopySettingsButton->setMenu( mMenu3DCanvases );
90   _prepare3DViewsMenu( mMenu3DCanvases, this, &QgsLayout3DMapWidget::copy3DMapSettings );
91 
92   mMenu3DCanvasesPose = new QMenu( this );
93   mPoseFromViewButton->setMenu( mMenu3DCanvasesPose );
94   _prepare3DViewsMenu( mMenu3DCanvasesPose, this, &QgsLayout3DMapWidget::copeCameraPose );
95 
96   QList<QgsDoubleSpinBox *> lst;
97   lst << mCenterXSpinBox << mCenterYSpinBox << mCenterZSpinBox << mDistanceToCenterSpinBox << mPitchAngleSpinBox << mHeadingAngleSpinBox;
98   for ( QgsDoubleSpinBox *spinBox : qgis::as_const( lst ) )
99     connect( spinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayout3DMapWidget::updateCameraPose );
100 
101   updateCameraPoseWidgetsFromItem();
102 }
103 
setMasterLayout(QgsMasterLayoutInterface * masterLayout)104 void QgsLayout3DMapWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayout )
105 {
106   if ( mItemPropertiesWidget )
107     mItemPropertiesWidget->setMasterLayout( masterLayout );
108 }
109 
updateCameraPoseWidgetsFromItem()110 void QgsLayout3DMapWidget::updateCameraPoseWidgetsFromItem()
111 {
112   QgsCameraPose pose = mMap3D->cameraPose();
113   whileBlocking( mCenterXSpinBox )->setValue( pose.centerPoint().x() );
114   whileBlocking( mCenterYSpinBox )->setValue( pose.centerPoint().y() );
115   whileBlocking( mCenterZSpinBox )->setValue( pose.centerPoint().z() );
116   whileBlocking( mDistanceToCenterSpinBox )->setValue( pose.distanceFromCenterPoint() );
117   whileBlocking( mPitchAngleSpinBox )->setValue( _normalizedAngle( pose.pitchAngle() ) );
118   whileBlocking( mHeadingAngleSpinBox )->setValue( _normalizedAngle( pose.headingAngle() ) );
119 }
120 
copy3DMapSettings()121 void QgsLayout3DMapWidget::copy3DMapSettings()
122 {
123   Qgs3DMapCanvasDockWidget *dock = _dock3DViewFromSender( sender() );
124   if ( !dock )
125     return;
126 
127   Qgs3DMapSettings *settings = new Qgs3DMapSettings( *dock->mapCanvas3D()->map() );
128 
129   // first setting passed on
130   if ( !mMap3D->mapSettings() )
131   {
132     // copy background color
133     mMap3D->setBackgroundColor( settings->backgroundColor() );
134 
135     // copy camera position details
136     mMap3D->setCameraPose( dock->mapCanvas3D()->cameraController()->cameraPose() );
137     updateCameraPoseWidgetsFromItem();
138   }
139 
140   mMap3D->setMapSettings( settings );
141 }
142 
copeCameraPose()143 void QgsLayout3DMapWidget::copeCameraPose()
144 {
145   Qgs3DMapCanvasDockWidget *dock = _dock3DViewFromSender( sender() );
146   if ( dock )
147   {
148     mMap3D->setCameraPose( dock->mapCanvas3D()->cameraController()->cameraPose() );
149     updateCameraPoseWidgetsFromItem();
150   }
151 }
152 
updateCameraPose()153 void QgsLayout3DMapWidget::updateCameraPose()
154 {
155   QgsCameraPose pose;
156   pose.setCenterPoint( QgsVector3D( mCenterXSpinBox->value(), mCenterYSpinBox->value(), mCenterZSpinBox->value() ) );
157   pose.setDistanceFromCenterPoint( mDistanceToCenterSpinBox->value() );
158   pose.setPitchAngle( mPitchAngleSpinBox->value() );
159   pose.setHeadingAngle( mHeadingAngleSpinBox->value() );
160   mMap3D->setCameraPose( pose );
161 }
162 
setNewItem(QgsLayoutItem * item)163 bool QgsLayout3DMapWidget::setNewItem( QgsLayoutItem *item )
164 {
165   QgsLayoutItem3DMap *newItem = qobject_cast< QgsLayoutItem3DMap * >( item );
166   if ( !newItem )
167     return false;
168 
169   mMap3D = newItem;
170   mItemPropertiesWidget->setItem( newItem );
171 
172   updateCameraPoseWidgetsFromItem();
173 
174   return true;
175 }
176