QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsprojectviewsettings.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsprojectviewsettings.cpp
3  -----------------------------
4  begin : October 2019
5  copyright : (C) 2019 by Nyall Dawson
6  email : nyall dot dawson 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 "qgsprojectviewsettings.h"
17 #include "qgis.h"
18 #include "qgsproject.h"
19 #include "qgslogger.h"
20 #include <QDomElement>
21 
23  : QObject( project )
24  , mProject( project )
25 {
26 
27 }
28 
30 {
31  mDefaultViewExtent = QgsReferencedRectangle();
32 
33  const bool fullExtentChanged = !mPresetFullExtent.isNull();
34  mPresetFullExtent = QgsReferencedRectangle();
35  if ( fullExtentChanged )
37 
38  if ( mUseProjectScales || !mMapScales.empty() )
39  {
40  mUseProjectScales = false;
41  mMapScales.clear();
42  emit mapScalesChanged();
43  }
44 }
45 
47 {
48  return mDefaultViewExtent;
49 }
50 
52 {
53  mDefaultViewExtent = extent;
54 }
55 
57 {
58  return mPresetFullExtent;
59 }
60 
62 {
63  if ( extent == mPresetFullExtent )
64  return;
65 
66  mPresetFullExtent = extent;
68 }
69 
71 {
72  if ( !mProject )
73  return mPresetFullExtent;
74 
75  if ( !mPresetFullExtent.isNull() )
76  {
77  QgsCoordinateTransform ct( mPresetFullExtent.crs(), mProject->crs(), mProject->transformContext() );
79  return QgsReferencedRectangle( ct.transformBoundingBox( mPresetFullExtent ), mProject->crs() );
80  }
81  else
82  {
83  // reset the map canvas extent since the extent may now be smaller
84  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
87 
88  // iterate through the map layers and test each layers extent
89  // against the current min and max values
90  const QMap<QString, QgsMapLayer *> layers = mProject->mapLayers( true );
91  QgsDebugMsgLevel( QStringLiteral( "Layer count: %1" ).arg( layers.count() ), 5 );
92  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
93  {
94  QgsDebugMsgLevel( "Updating extent using " + it.value()->name(), 5 );
95  QgsDebugMsgLevel( "Input extent: " + it.value()->extent().toString(), 5 );
96 
97  if ( it.value()->extent().isNull() )
98  continue;
99 
100  // Layer extents are stored in the coordinate system (CS) of the
101  // layer. The extent must be projected to the canvas CS
102  QgsCoordinateTransform ct( it.value()->crs(), mProject->crs(), mProject->transformContext() );
104  try
105  {
106  const QgsRectangle extent = ct.transformBoundingBox( it.value()->extent() );
107 
108  QgsDebugMsgLevel( "Output extent: " + extent.toString(), 5 );
109  fullExtent.combineExtentWith( extent );
110  }
111  catch ( QgsCsException & )
112  {
113  QgsDebugMsg( QStringLiteral( "Could not reproject layer extent" ) );
114  }
115  }
116 
117  if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 )
118  {
119  // If all of the features are at the one point, buffer the
120  // rectangle a bit. If they are all at zero, do something a bit
121  // more crude.
122 
123  if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 &&
124  fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 )
125  {
126  fullExtent.set( -1.0, -1.0, 1.0, 1.0 );
127  }
128  else
129  {
130  const double padFactor = 1e-8;
131  double widthPad = fullExtent.xMinimum() * padFactor;
132  double heightPad = fullExtent.yMinimum() * padFactor;
133  double xmin = fullExtent.xMinimum() - widthPad;
134  double xmax = fullExtent.xMaximum() + widthPad;
135  double ymin = fullExtent.yMinimum() - heightPad;
136  double ymax = fullExtent.yMaximum() + heightPad;
137  fullExtent.set( xmin, ymin, xmax, ymax );
138  }
139  }
140 
141  QgsDebugMsgLevel( "Full extent: " + fullExtent.toString(), 5 );
142  return QgsReferencedRectangle( fullExtent, mProject->crs() );
143  }
144 }
145 
146 void QgsProjectViewSettings::setMapScales( const QVector<double> &scales )
147 {
148  // sort scales in descending order
149  QVector< double > sorted = scales;
150  std::sort( sorted.begin(), sorted.end(), std::greater<double>() );
151 
152  if ( sorted == mapScales() )
153  return;
154 
155  mMapScales = sorted;
156 
157  emit mapScalesChanged();
158 }
159 
160 QVector<double> QgsProjectViewSettings::mapScales() const
161 {
162  return mMapScales;
163 }
164 
166 {
167  if ( enabled == useProjectScales() )
168  return;
169 
170  mUseProjectScales = enabled;
171  emit mapScalesChanged();
172 }
173 
175 {
176  return mUseProjectScales;
177 }
178 
179 bool QgsProjectViewSettings::readXml( const QDomElement &element, const QgsReadWriteContext & )
180 {
181  bool useProjectScale = element.attribute( QStringLiteral( "UseProjectScales" ), QStringLiteral( "0" ) ).toInt();
182 
183  QDomNodeList scalesNodes = element.elementsByTagName( QStringLiteral( "Scales" ) );
184  QVector< double > newScales;
185  if ( !scalesNodes.isEmpty() )
186  {
187  QDomElement scalesElement = scalesNodes.at( 0 ).toElement();
188  scalesNodes = scalesElement.elementsByTagName( QStringLiteral( "Scale" ) );
189  for ( int i = 0; i < scalesNodes.count(); i++ )
190  {
191  QDomElement scaleElement = scalesNodes.at( i ).toElement();
192  newScales.append( scaleElement.attribute( QStringLiteral( "Value" ) ).toDouble() );
193  }
194  }
195  if ( useProjectScale != mUseProjectScales || newScales != mMapScales )
196  {
197  mMapScales = newScales;
198  mUseProjectScales = useProjectScale;
199  emit mapScalesChanged();
200  }
201 
202  QDomElement defaultViewElement = element.firstChildElement( QStringLiteral( "DefaultViewExtent" ) );
203  if ( !defaultViewElement.isNull() )
204  {
205  double xMin = defaultViewElement.attribute( QStringLiteral( "xmin" ) ).toDouble();
206  double yMin = defaultViewElement.attribute( QStringLiteral( "ymin" ) ).toDouble();
207  double xMax = defaultViewElement.attribute( QStringLiteral( "xmax" ) ).toDouble();
208  double yMax = defaultViewElement.attribute( QStringLiteral( "ymax" ) ).toDouble();
210  crs.readXml( defaultViewElement );
211  mDefaultViewExtent = QgsReferencedRectangle( QgsRectangle( xMin, yMin, xMax, yMax ), crs );
212  }
213  else
214  {
215  mDefaultViewExtent = QgsReferencedRectangle();
216  }
217 
218  QDomElement presetViewElement = element.firstChildElement( QStringLiteral( "PresetFullExtent" ) );
219  if ( !presetViewElement.isNull() )
220  {
221  double xMin = presetViewElement.attribute( QStringLiteral( "xmin" ) ).toDouble();
222  double yMin = presetViewElement.attribute( QStringLiteral( "ymin" ) ).toDouble();
223  double xMax = presetViewElement.attribute( QStringLiteral( "xmax" ) ).toDouble();
224  double yMax = presetViewElement.attribute( QStringLiteral( "ymax" ) ).toDouble();
226  crs.readXml( presetViewElement );
227  setPresetFullExtent( QgsReferencedRectangle( QgsRectangle( xMin, yMin, xMax, yMax ), crs ) );
228  }
229  else
230  {
232  }
233 
234  return true;
235 }
236 
237 QDomElement QgsProjectViewSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const
238 {
239  QDomElement element = doc.createElement( QStringLiteral( "ProjectViewSettings" ) );
240  element.setAttribute( QStringLiteral( "UseProjectScales" ), mUseProjectScales ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
241 
242  QDomElement scales = doc.createElement( QStringLiteral( "Scales" ) );
243  for ( double scale : mMapScales )
244  {
245  QDomElement scaleElement = doc.createElement( QStringLiteral( "Scale" ) );
246  scaleElement.setAttribute( QStringLiteral( "Value" ), qgsDoubleToString( scale ) );
247  scales.appendChild( scaleElement );
248  }
249  element.appendChild( scales );
250 
251  if ( !mDefaultViewExtent.isNull() )
252  {
253  QDomElement defaultViewElement = doc.createElement( QStringLiteral( "DefaultViewExtent" ) );
254  defaultViewElement.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mDefaultViewExtent.xMinimum() ) );
255  defaultViewElement.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mDefaultViewExtent.yMinimum() ) );
256  defaultViewElement.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mDefaultViewExtent.xMaximum() ) );
257  defaultViewElement.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mDefaultViewExtent.yMaximum() ) );
258  mDefaultViewExtent.crs().writeXml( defaultViewElement, doc );
259  element.appendChild( defaultViewElement );
260  }
261 
262  if ( !mPresetFullExtent.isNull() )
263  {
264  QDomElement presetViewElement = doc.createElement( QStringLiteral( "PresetFullExtent" ) );
265  presetViewElement.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mPresetFullExtent.xMinimum() ) );
266  presetViewElement.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mPresetFullExtent.yMinimum() ) );
267  presetViewElement.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mPresetFullExtent.xMaximum() ) );
268  presetViewElement.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mPresetFullExtent.yMaximum() ) );
269  mPresetFullExtent.crs().writeXml( presetViewElement, doc );
270  element.appendChild( presetViewElement );
271  }
272 
273  return element;
274 }
This class represents a coordinate reference system (CRS).
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
QgsReferencedRectangle defaultViewExtent() const
Returns the default view extent, which should be used as the initial map extent when this project is ...
bool useProjectScales() const
Returns true if project mapScales() are enabled.
QgsReferencedRectangle presetFullExtent() const
Returns the project's preset full extent.
void setPresetFullExtent(const QgsReferencedRectangle &extent)
Sets the project's preset full extent.
void reset()
Resets the settings to a default state.
void presetFullExtentChanged()
Emitted whenever the presetFullExtent() is changed.
void setDefaultViewExtent(const QgsReferencedRectangle &extent)
Sets the default view extent, which should be used as the initial map extent when this project is ope...
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
QVector< double > mapScales() const
Returns the list of custom project map scales.
QgsProjectViewSettings(QgsProject *project=nullptr)
Constructor for QgsProjectViewSettings for the specified project.
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
QgsReferencedRectangle fullExtent() const
Returns the full extent of the project, which represents the maximal limits of the project.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:104
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:447
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
void set(const QgsPointXY &p1, const QgsPointXY &p2)
Sets the rectangle from two QgsPoints.
Definition: qgsrectangle.h:105
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:151
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
Definition: qgsrectangle.h:359
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsRectangle with associated coordinate reference system.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:276
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs