Branch data Line data Source code
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 : : 22 : 5 : QgsProjectViewSettings::QgsProjectViewSettings( QgsProject *project ) 23 : 5 : : QObject( project ) 24 : 5 : , mProject( project ) 25 : 10 : { 26 : : 27 : 5 : } 28 : : 29 : 8 : void QgsProjectViewSettings::reset() 30 : : { 31 : 8 : mDefaultViewExtent = QgsReferencedRectangle(); 32 : : 33 : 8 : const bool fullExtentChanged = !mPresetFullExtent.isNull(); 34 : 8 : mPresetFullExtent = QgsReferencedRectangle(); 35 : 8 : if ( fullExtentChanged ) 36 : 0 : emit presetFullExtentChanged(); 37 : : 38 : 8 : if ( mUseProjectScales || !mMapScales.empty() ) 39 : : { 40 : 0 : mUseProjectScales = false; 41 : 0 : mMapScales.clear(); 42 : 0 : emit mapScalesChanged(); 43 : 0 : } 44 : 8 : } 45 : : 46 : 0 : QgsReferencedRectangle QgsProjectViewSettings::defaultViewExtent() const 47 : : { 48 : 0 : return mDefaultViewExtent; 49 : : } 50 : : 51 : 0 : void QgsProjectViewSettings::setDefaultViewExtent( const QgsReferencedRectangle &extent ) 52 : : { 53 : 0 : mDefaultViewExtent = extent; 54 : 0 : } 55 : : 56 : 0 : QgsReferencedRectangle QgsProjectViewSettings::presetFullExtent() const 57 : : { 58 : 0 : return mPresetFullExtent; 59 : : } 60 : : 61 : 0 : void QgsProjectViewSettings::setPresetFullExtent( const QgsReferencedRectangle &extent ) 62 : : { 63 : 0 : if ( extent == mPresetFullExtent ) 64 : 0 : return; 65 : : 66 : 0 : mPresetFullExtent = extent; 67 : 0 : emit presetFullExtentChanged(); 68 : 0 : } 69 : : 70 : 0 : QgsReferencedRectangle QgsProjectViewSettings::fullExtent() const 71 : : { 72 : 0 : if ( !mProject ) 73 : 0 : return mPresetFullExtent; 74 : : 75 : 0 : if ( !mPresetFullExtent.isNull() ) 76 : : { 77 : 0 : QgsCoordinateTransform ct( mPresetFullExtent.crs(), mProject->crs(), mProject->transformContext() ); 78 : 0 : ct.setBallparkTransformsAreAppropriate( true ); 79 : 0 : return QgsReferencedRectangle( ct.transformBoundingBox( mPresetFullExtent ), mProject->crs() ); 80 : 0 : } 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 85 : 0 : QgsRectangle fullExtent; 86 : 0 : fullExtent.setMinimal(); 87 : : 88 : : // iterate through the map layers and test each layers extent 89 : : // against the current min and max values 90 : 0 : const QMap<QString, QgsMapLayer *> layers = mProject->mapLayers( true ); 91 : 0 : QgsDebugMsgLevel( QStringLiteral( "Layer count: %1" ).arg( layers.count() ), 5 ); 92 : 0 : for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it ) 93 : : { 94 : 0 : QgsDebugMsgLevel( "Updating extent using " + it.value()->name(), 5 ); 95 : 0 : QgsDebugMsgLevel( "Input extent: " + it.value()->extent().toString(), 5 ); 96 : : 97 : 0 : if ( it.value()->extent().isNull() ) 98 : 0 : 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 : 0 : QgsCoordinateTransform ct( it.value()->crs(), mProject->crs(), mProject->transformContext() ); 103 : 0 : ct.setBallparkTransformsAreAppropriate( true ); 104 : : try 105 : : { 106 : 0 : const QgsRectangle extent = ct.transformBoundingBox( it.value()->extent() ); 107 : : 108 : 0 : QgsDebugMsgLevel( "Output extent: " + extent.toString(), 5 ); 109 : 0 : fullExtent.combineExtentWith( extent ); 110 : 0 : } 111 : : catch ( QgsCsException & ) 112 : : { 113 : 0 : QgsDebugMsg( QStringLiteral( "Could not reproject layer extent" ) ); 114 : 0 : } 115 : 0 : } 116 : : 117 : 0 : 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 : 0 : if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 && 124 : 0 : fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 ) 125 : : { 126 : 0 : fullExtent.set( -1.0, -1.0, 1.0, 1.0 ); 127 : 0 : } 128 : : else 129 : : { 130 : 0 : const double padFactor = 1e-8; 131 : 0 : double widthPad = fullExtent.xMinimum() * padFactor; 132 : 0 : double heightPad = fullExtent.yMinimum() * padFactor; 133 : 0 : double xmin = fullExtent.xMinimum() - widthPad; 134 : 0 : double xmax = fullExtent.xMaximum() + widthPad; 135 : 0 : double ymin = fullExtent.yMinimum() - heightPad; 136 : 0 : double ymax = fullExtent.yMaximum() + heightPad; 137 : 0 : fullExtent.set( xmin, ymin, xmax, ymax ); 138 : : } 139 : 0 : } 140 : : 141 : 0 : QgsDebugMsgLevel( "Full extent: " + fullExtent.toString(), 5 ); 142 : 0 : return QgsReferencedRectangle( fullExtent, mProject->crs() ); 143 : 0 : } 144 : 0 : } 145 : : 146 : 0 : void QgsProjectViewSettings::setMapScales( const QVector<double> &scales ) 147 : : { 148 : : // sort scales in descending order 149 : 0 : QVector< double > sorted = scales; 150 : 0 : std::sort( sorted.begin(), sorted.end(), std::greater<double>() ); 151 : : 152 : 0 : if ( sorted == mapScales() ) 153 : 0 : return; 154 : : 155 : 0 : mMapScales = sorted; 156 : : 157 : 0 : emit mapScalesChanged(); 158 : 0 : } 159 : : 160 : 0 : QVector<double> QgsProjectViewSettings::mapScales() const 161 : : { 162 : 0 : return mMapScales; 163 : : } 164 : : 165 : 0 : void QgsProjectViewSettings::setUseProjectScales( bool enabled ) 166 : : { 167 : 0 : if ( enabled == useProjectScales() ) 168 : 0 : return; 169 : : 170 : 0 : mUseProjectScales = enabled; 171 : 0 : emit mapScalesChanged(); 172 : 0 : } 173 : : 174 : 0 : bool QgsProjectViewSettings::useProjectScales() const 175 : : { 176 : 0 : return mUseProjectScales; 177 : : } 178 : : 179 : 0 : bool QgsProjectViewSettings::readXml( const QDomElement &element, const QgsReadWriteContext & ) 180 : : { 181 : 0 : bool useProjectScale = element.attribute( QStringLiteral( "UseProjectScales" ), QStringLiteral( "0" ) ).toInt(); 182 : : 183 : 0 : QDomNodeList scalesNodes = element.elementsByTagName( QStringLiteral( "Scales" ) ); 184 : 0 : QVector< double > newScales; 185 : 0 : if ( !scalesNodes.isEmpty() ) 186 : : { 187 : 0 : QDomElement scalesElement = scalesNodes.at( 0 ).toElement(); 188 : 0 : scalesNodes = scalesElement.elementsByTagName( QStringLiteral( "Scale" ) ); 189 : 0 : for ( int i = 0; i < scalesNodes.count(); i++ ) 190 : : { 191 : 0 : QDomElement scaleElement = scalesNodes.at( i ).toElement(); 192 : 0 : newScales.append( scaleElement.attribute( QStringLiteral( "Value" ) ).toDouble() ); 193 : 0 : } 194 : 0 : } 195 : 0 : if ( useProjectScale != mUseProjectScales || newScales != mMapScales ) 196 : : { 197 : 0 : mMapScales = newScales; 198 : 5 : mUseProjectScales = useProjectScale; 199 : 0 : emit mapScalesChanged(); 200 : 0 : } 201 : : 202 : 0 : QDomElement defaultViewElement = element.firstChildElement( QStringLiteral( "DefaultViewExtent" ) ); 203 : 0 : if ( !defaultViewElement.isNull() ) 204 : : { 205 : 0 : double xMin = defaultViewElement.attribute( QStringLiteral( "xmin" ) ).toDouble(); 206 : 0 : double yMin = defaultViewElement.attribute( QStringLiteral( "ymin" ) ).toDouble(); 207 : 0 : double xMax = defaultViewElement.attribute( QStringLiteral( "xmax" ) ).toDouble(); 208 : 0 : double yMax = defaultViewElement.attribute( QStringLiteral( "ymax" ) ).toDouble(); 209 : 0 : QgsCoordinateReferenceSystem crs; 210 : 0 : crs.readXml( defaultViewElement ); 211 : 0 : mDefaultViewExtent = QgsReferencedRectangle( QgsRectangle( xMin, yMin, xMax, yMax ), crs ); 212 : 0 : } 213 : : else 214 : : { 215 : 0 : mDefaultViewExtent = QgsReferencedRectangle(); 216 : : } 217 : : 218 : 0 : QDomElement presetViewElement = element.firstChildElement( QStringLiteral( "PresetFullExtent" ) ); 219 : 0 : if ( !presetViewElement.isNull() ) 220 : : { 221 : 0 : double xMin = presetViewElement.attribute( QStringLiteral( "xmin" ) ).toDouble(); 222 : 0 : double yMin = presetViewElement.attribute( QStringLiteral( "ymin" ) ).toDouble(); 223 : 0 : double xMax = presetViewElement.attribute( QStringLiteral( "xmax" ) ).toDouble(); 224 : 0 : double yMax = presetViewElement.attribute( QStringLiteral( "ymax" ) ).toDouble(); 225 : 0 : QgsCoordinateReferenceSystem crs; 226 : 0 : crs.readXml( presetViewElement ); 227 : 0 : setPresetFullExtent( QgsReferencedRectangle( QgsRectangle( xMin, yMin, xMax, yMax ), crs ) ); 228 : 0 : } 229 : : else 230 : : { 231 : 0 : setPresetFullExtent( QgsReferencedRectangle() ); 232 : : } 233 : : 234 : : return true; 235 : 0 : } 236 : : 237 : 0 : QDomElement QgsProjectViewSettings::writeXml( QDomDocument &doc, const QgsReadWriteContext & ) const 238 : : { 239 : 0 : QDomElement element = doc.createElement( QStringLiteral( "ProjectViewSettings" ) ); 240 : 0 : element.setAttribute( QStringLiteral( "UseProjectScales" ), mUseProjectScales ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); 241 : : 242 : 0 : QDomElement scales = doc.createElement( QStringLiteral( "Scales" ) ); 243 : 0 : for ( double scale : mMapScales ) 244 : : { 245 : 0 : QDomElement scaleElement = doc.createElement( QStringLiteral( "Scale" ) ); 246 : 0 : scaleElement.setAttribute( QStringLiteral( "Value" ), qgsDoubleToString( scale ) ); 247 : 0 : scales.appendChild( scaleElement ); 248 : 0 : } 249 : 0 : element.appendChild( scales ); 250 : : 251 : 0 : if ( !mDefaultViewExtent.isNull() ) 252 : : { 253 : 0 : QDomElement defaultViewElement = doc.createElement( QStringLiteral( "DefaultViewExtent" ) ); 254 : 0 : defaultViewElement.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mDefaultViewExtent.xMinimum() ) ); 255 : 0 : defaultViewElement.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mDefaultViewExtent.yMinimum() ) ); 256 : 0 : defaultViewElement.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mDefaultViewExtent.xMaximum() ) ); 257 : 0 : defaultViewElement.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mDefaultViewExtent.yMaximum() ) ); 258 : 0 : mDefaultViewExtent.crs().writeXml( defaultViewElement, doc ); 259 : 0 : element.appendChild( defaultViewElement ); 260 : 0 : } 261 : : 262 : 0 : if ( !mPresetFullExtent.isNull() ) 263 : : { 264 : 0 : QDomElement presetViewElement = doc.createElement( QStringLiteral( "PresetFullExtent" ) ); 265 : 0 : presetViewElement.setAttribute( QStringLiteral( "xmin" ), qgsDoubleToString( mPresetFullExtent.xMinimum() ) ); 266 : 0 : presetViewElement.setAttribute( QStringLiteral( "ymin" ), qgsDoubleToString( mPresetFullExtent.yMinimum() ) ); 267 : 0 : presetViewElement.setAttribute( QStringLiteral( "xmax" ), qgsDoubleToString( mPresetFullExtent.xMaximum() ) ); 268 : 0 : presetViewElement.setAttribute( QStringLiteral( "ymax" ), qgsDoubleToString( mPresetFullExtent.yMaximum() ) ); 269 : 0 : mPresetFullExtent.crs().writeXml( presetViewElement, doc ); 270 : 0 : element.appendChild( presetViewElement ); 271 : 0 : } 272 : : 273 : 0 : return element; 274 : 0 : }