Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsmapsettings.cpp
3 : : --------------------------------------
4 : : Date : December 2013
5 : : Copyright : (C) 2013 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 "qgsmapsettings.h"
17 : :
18 : : #include "qgsscalecalculator.h"
19 : : #include "qgsmaprendererjob.h"
20 : : #include "qgsmaptopixel.h"
21 : : #include "qgslogger.h"
22 : :
23 : : #include "qgsmessagelog.h"
24 : : #include "qgsmaplayer.h"
25 : : #include "qgsmaplayerlistutils.h"
26 : : #include "qgsproject.h"
27 : : #include "qgsxmlutils.h"
28 : : #include "qgsexception.h"
29 : : #include "qgsgeometry.h"
30 : :
31 : : Q_GUI_EXPORT extern int qt_defaultDpiX();
32 : :
33 : :
34 : 6 : QgsMapSettings::QgsMapSettings()
35 : 6 : : mDpi( qt_defaultDpiX() ) // DPI that will be used by default for QImage instances
36 : 6 : , mSize( QSize( 0, 0 ) )
37 : 6 : , mBackgroundColor( Qt::white )
38 : 6 : , mSelectionColor( Qt::yellow )
39 : 6 : , mFlags( Antialiasing | UseAdvancedEffects | DrawLabeling | DrawSelection )
40 : 6 : , mSegmentationTolerance( M_PI_2 / 90 )
41 : 12 : {
42 : 6 : mScaleCalculator.setMapUnits( QgsUnitTypes::DistanceUnknownUnit );
43 : 6 : mSimplifyMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification );
44 : :
45 : 6 : updateDerived();
46 : 6 : }
47 : :
48 : 0 : void QgsMapSettings::setMagnificationFactor( double factor, const QgsPointXY *center )
49 : : {
50 : 0 : double ratio = mMagnificationFactor / factor;
51 : :
52 : 0 : mMagnificationFactor = factor;
53 : :
54 : 0 : double rot = rotation();
55 : 0 : setRotation( 0.0 );
56 : :
57 : 0 : QgsRectangle ext = visibleExtent();
58 : 0 : ext.scale( ratio, center );
59 : :
60 : 0 : mRotation = rot;
61 : 0 : mExtent = ext;
62 : 0 : mDpi = mDpi / ratio;
63 : :
64 : 0 : QgsDebugMsgLevel( QStringLiteral( "Magnification factor: %1 dpi: %2 ratio: %3" ).arg( factor ).arg( mDpi ).arg( ratio ), 3 );
65 : :
66 : 0 : updateDerived();
67 : 0 : }
68 : :
69 : 0 : double QgsMapSettings::magnificationFactor() const
70 : : {
71 : 0 : return mMagnificationFactor;
72 : : }
73 : :
74 : 0 : QgsRectangle QgsMapSettings::extent() const
75 : : {
76 : 0 : return mExtent;
77 : : }
78 : :
79 : 0 : void QgsMapSettings::setExtent( const QgsRectangle &extent, bool magnified )
80 : : {
81 : 0 : QgsRectangle magnifiedExtent = extent;
82 : :
83 : 0 : if ( !magnified )
84 : 0 : magnifiedExtent.scale( 1 / mMagnificationFactor );
85 : :
86 : 0 : mExtent = magnifiedExtent;
87 : :
88 : 0 : updateDerived();
89 : 0 : }
90 : :
91 : 0 : double QgsMapSettings::extentBuffer() const
92 : : {
93 : 0 : return mExtentBuffer;
94 : : }
95 : :
96 : 0 : void QgsMapSettings::setExtentBuffer( const double buffer )
97 : : {
98 : 0 : mExtentBuffer = buffer;
99 : 0 : }
100 : :
101 : 0 : double QgsMapSettings::rotation() const
102 : : {
103 : 0 : return mRotation;
104 : : }
105 : :
106 : 0 : void QgsMapSettings::setRotation( double degrees )
107 : : {
108 : 0 : if ( qgsDoubleNear( mRotation, degrees ) )
109 : 0 : return;
110 : :
111 : 0 : mRotation = degrees;
112 : :
113 : : // TODO: update extent while keeping scale ?
114 : 0 : updateDerived();
115 : 0 : }
116 : :
117 : :
118 : 6 : void QgsMapSettings::updateDerived()
119 : : {
120 : 6 : QgsRectangle extent = mExtent;
121 : :
122 : 6 : if ( extent.isEmpty() || !extent.isFinite() )
123 : : {
124 : 6 : mValid = false;
125 : 6 : return;
126 : : }
127 : :
128 : : // Don't allow zooms where the current extent is so small that it
129 : : // can't be accurately represented using a double (which is what
130 : : // currentExtent uses). Excluding 0 avoids a divide by zero and an
131 : : // infinite loop when rendering to a new canvas. Excluding extents
132 : : // greater than 1 avoids doing unnecessary calculations.
133 : :
134 : : // The scheme is to compare the width against the mean x coordinate
135 : : // (and height against mean y coordinate) and only allow zooms where
136 : : // the ratio indicates that there is more than about 12 significant
137 : : // figures (there are about 16 significant figures in a double).
138 : :
139 : 0 : if ( extent.width() > 0 &&
140 : 0 : extent.height() > 0 &&
141 : 0 : extent.width() < 1 &&
142 : 0 : extent.height() < 1 )
143 : : {
144 : : // Use abs() on the extent to avoid the case where the extent is
145 : : // symmetrical about 0.
146 : 0 : double xMean = ( std::fabs( extent.xMinimum() ) + std::fabs( extent.xMaximum() ) ) * 0.5;
147 : 0 : double yMean = ( std::fabs( extent.yMinimum() ) + std::fabs( extent.yMaximum() ) ) * 0.5;
148 : :
149 : 0 : double xRange = extent.width() / xMean;
150 : 0 : double yRange = extent.height() / yMean;
151 : :
152 : : static const double MIN_PROPORTION = 1e-12;
153 : 0 : if ( xRange < MIN_PROPORTION || yRange < MIN_PROPORTION )
154 : : {
155 : 0 : mValid = false;
156 : 0 : return;
157 : : }
158 : 0 : }
159 : :
160 : 0 : double myHeight = mSize.height();
161 : 0 : double myWidth = mSize.width();
162 : :
163 : 0 : if ( !myWidth || !myHeight )
164 : : {
165 : 0 : mValid = false;
166 : 0 : return;
167 : : }
168 : :
169 : : // calculate the translation and scaling parameters
170 : 0 : double mapUnitsPerPixelY = mExtent.height() / myHeight;
171 : 0 : double mapUnitsPerPixelX = mExtent.width() / myWidth;
172 : 0 : mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
173 : :
174 : : // calculate the actual extent of the mapCanvas
175 : 0 : double dxmin = mExtent.xMinimum(), dxmax = mExtent.xMaximum(),
176 : 0 : dymin = mExtent.yMinimum(), dymax = mExtent.yMaximum(), whitespace;
177 : :
178 : 0 : if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
179 : : {
180 : 0 : whitespace = ( ( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5;
181 : 0 : dxmin -= whitespace;
182 : 0 : dxmax += whitespace;
183 : 0 : }
184 : : else
185 : : {
186 : 0 : whitespace = ( ( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5;
187 : 0 : dymin -= whitespace;
188 : 0 : dymax += whitespace;
189 : : }
190 : :
191 : 0 : mVisibleExtent.set( dxmin, dymin, dxmax, dymax );
192 : :
193 : : // update the scale
194 : 0 : mScaleCalculator.setDpi( mDpi * mDevicePixelRatio );
195 : 0 : mScale = mScaleCalculator.calculate( mVisibleExtent, mSize.width() );
196 : :
197 : 0 : mMapToPixel.setParameters( mapUnitsPerPixel(),
198 : 0 : visibleExtent().center().x(),
199 : 0 : visibleExtent().center().y(),
200 : 0 : outputSize().width(),
201 : 0 : outputSize().height(),
202 : 0 : mRotation );
203 : :
204 : : #if 1 // set visible extent taking rotation in consideration
205 : 0 : if ( mRotation )
206 : : {
207 : 0 : QgsPointXY p1 = mMapToPixel.toMapCoordinates( QPoint( 0, 0 ) );
208 : 0 : QgsPointXY p2 = mMapToPixel.toMapCoordinates( QPoint( 0, myHeight ) );
209 : 0 : QgsPointXY p3 = mMapToPixel.toMapCoordinates( QPoint( myWidth, 0 ) );
210 : 0 : QgsPointXY p4 = mMapToPixel.toMapCoordinates( QPoint( myWidth, myHeight ) );
211 : 0 : dxmin = std::min( p1.x(), std::min( p2.x(), std::min( p3.x(), p4.x() ) ) );
212 : 0 : dymin = std::min( p1.y(), std::min( p2.y(), std::min( p3.y(), p4.y() ) ) );
213 : 0 : dxmax = std::max( p1.x(), std::max( p2.x(), std::max( p3.x(), p4.x() ) ) );
214 : 0 : dymax = std::max( p1.y(), std::max( p2.y(), std::max( p3.y(), p4.y() ) ) );
215 : 0 : mVisibleExtent.set( dxmin, dymin, dxmax, dymax );
216 : 0 : }
217 : : #endif
218 : :
219 : 0 : QgsDebugMsgLevel( QStringLiteral( "Map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mapUnitsPerPixelX ), qgsDoubleToString( mapUnitsPerPixelY ) ), 5 );
220 : 0 : QgsDebugMsgLevel( QStringLiteral( "Pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mSize.width() ), qgsDoubleToString( mSize.height() ) ), 5 );
221 : 0 : QgsDebugMsgLevel( QStringLiteral( "Extent dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mExtent.width() ), qgsDoubleToString( mExtent.height() ) ), 5 );
222 : 0 : QgsDebugMsgLevel( mExtent.toString(), 5 );
223 : 0 : QgsDebugMsgLevel( QStringLiteral( "Adjusted map units per pixel (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / myWidth ), qgsDoubleToString( mVisibleExtent.height() / myHeight ) ), 5 );
224 : 0 : QgsDebugMsgLevel( QStringLiteral( "Recalced pixmap dimensions (x,y) : %1, %2" ).arg( qgsDoubleToString( mVisibleExtent.width() / mMapUnitsPerPixel ), qgsDoubleToString( mVisibleExtent.height() / mMapUnitsPerPixel ) ), 5 );
225 : 0 : QgsDebugMsgLevel( QStringLiteral( "Scale (assuming meters as map units) = 1:%1" ).arg( qgsDoubleToString( mScale ) ), 5 );
226 : 0 : QgsDebugMsgLevel( QStringLiteral( "Rotation: %1 degrees" ).arg( mRotation ), 5 );
227 : 0 : QgsDebugMsgLevel( QStringLiteral( "Extent: %1" ).arg( mExtent.asWktCoordinates() ), 5 );
228 : 0 : QgsDebugMsgLevel( QStringLiteral( "Visible Extent: %1" ).arg( mVisibleExtent.asWktCoordinates() ), 5 );
229 : 0 : QgsDebugMsgLevel( QStringLiteral( "Magnification factor: %1" ).arg( mMagnificationFactor ), 5 );
230 : :
231 : 0 : mValid = true;
232 : 6 : }
233 : :
234 : :
235 : 0 : QSize QgsMapSettings::outputSize() const
236 : : {
237 : 0 : return mSize;
238 : : }
239 : :
240 : 0 : void QgsMapSettings::setOutputSize( QSize size )
241 : : {
242 : 0 : mSize = size;
243 : :
244 : 0 : updateDerived();
245 : 0 : }
246 : :
247 : 0 : float QgsMapSettings::devicePixelRatio() const
248 : : {
249 : 0 : return mDevicePixelRatio;
250 : : }
251 : :
252 : 0 : void QgsMapSettings::setDevicePixelRatio( float dpr )
253 : : {
254 : 0 : mDevicePixelRatio = dpr;
255 : 0 : updateDerived();
256 : 0 : }
257 : :
258 : 0 : QSize QgsMapSettings::deviceOutputSize() const
259 : : {
260 : 0 : return outputSize() * mDevicePixelRatio;
261 : : }
262 : :
263 : 0 : double QgsMapSettings::outputDpi() const
264 : : {
265 : 0 : return mDpi;
266 : : }
267 : :
268 : 0 : void QgsMapSettings::setOutputDpi( double dpi )
269 : : {
270 : 0 : mDpi = dpi;
271 : :
272 : 0 : updateDerived();
273 : 0 : }
274 : :
275 : :
276 : 0 : QStringList QgsMapSettings::layerIds() const
277 : : {
278 : 0 : return _qgis_listQPointerToIDs( mLayers );
279 : : }
280 : :
281 : :
282 : 0 : QList<QgsMapLayer *> QgsMapSettings::layers() const
283 : : {
284 : 0 : return _qgis_listQPointerToRaw( mLayers );
285 : : }
286 : :
287 : 0 : void QgsMapSettings::setLayers( const QList<QgsMapLayer *> &layers )
288 : : {
289 : : // filter list, removing null layers and non-spatial layers
290 : 0 : auto filteredList = layers;
291 : 0 : filteredList.erase( std::remove_if( filteredList.begin(), filteredList.end(),
292 : 0 : []( QgsMapLayer * layer )
293 : : {
294 : 0 : return !layer || !layer->isSpatial();
295 : 0 : } ), filteredList.end() );
296 : :
297 : 0 : mLayers = _qgis_listRawToQPointer( filteredList );
298 : 0 : }
299 : :
300 : 0 : QMap<QString, QString> QgsMapSettings::layerStyleOverrides() const
301 : : {
302 : 0 : return mLayerStyleOverrides;
303 : : }
304 : :
305 : 0 : void QgsMapSettings::setLayerStyleOverrides( const QMap<QString, QString> &overrides )
306 : : {
307 : 0 : mLayerStyleOverrides = overrides;
308 : 0 : }
309 : :
310 : 0 : void QgsMapSettings::setDestinationCrs( const QgsCoordinateReferenceSystem &crs )
311 : : {
312 : 0 : mDestCRS = crs;
313 : 0 : mScaleCalculator.setMapUnits( crs.mapUnits() );
314 : : // Since the map units have changed, force a recalculation of the scale.
315 : 0 : updateDerived();
316 : 0 : }
317 : :
318 : 0 : QgsCoordinateReferenceSystem QgsMapSettings::destinationCrs() const
319 : : {
320 : 0 : return mDestCRS;
321 : : }
322 : :
323 : 0 : bool QgsMapSettings::setEllipsoid( const QString &ellipsoid )
324 : : {
325 : 0 : QgsEllipsoidUtils::EllipsoidParameters params = QgsEllipsoidUtils::ellipsoidParameters( ellipsoid );
326 : 0 : if ( !params.valid )
327 : : {
328 : 0 : return false;
329 : : }
330 : : else
331 : : {
332 : 0 : mEllipsoid = ellipsoid;
333 : 0 : return true;
334 : : }
335 : 0 : }
336 : :
337 : 0 : void QgsMapSettings::setFlags( QgsMapSettings::Flags flags )
338 : : {
339 : 0 : mFlags = flags;
340 : 0 : }
341 : :
342 : 0 : void QgsMapSettings::setFlag( QgsMapSettings::Flag flag, bool on )
343 : : {
344 : 0 : if ( on )
345 : 0 : mFlags |= flag;
346 : : else
347 : 0 : mFlags &= ~flag;
348 : 0 : }
349 : :
350 : 0 : QgsMapSettings::Flags QgsMapSettings::flags() const
351 : : {
352 : 0 : return mFlags;
353 : : }
354 : :
355 : 0 : bool QgsMapSettings::testFlag( QgsMapSettings::Flag flag ) const
356 : : {
357 : 0 : return mFlags.testFlag( flag );
358 : : }
359 : :
360 : 0 : QgsUnitTypes::DistanceUnit QgsMapSettings::mapUnits() const
361 : : {
362 : 0 : return mScaleCalculator.mapUnits();
363 : : }
364 : :
365 : :
366 : 0 : bool QgsMapSettings::hasValidSettings() const
367 : : {
368 : 0 : return mValid;
369 : : }
370 : :
371 : 0 : QgsRectangle QgsMapSettings::visibleExtent() const
372 : : {
373 : 0 : return mVisibleExtent;
374 : : }
375 : :
376 : 0 : QPolygonF QgsMapSettings::visiblePolygon() const
377 : : {
378 : 0 : QPolygonF poly;
379 : :
380 : 0 : const QSize &sz = outputSize();
381 : 0 : const QgsMapToPixel &m2p = mapToPixel();
382 : :
383 : 0 : poly << m2p.toMapCoordinates( 0.0, 0.0 ).toQPointF();
384 : 0 : poly << m2p.toMapCoordinates( static_cast<double>( sz.width() ), 0.0 ).toQPointF();
385 : 0 : poly << m2p.toMapCoordinates( static_cast<double>( sz.width() ), static_cast<double>( sz.height() ) ).toQPointF();
386 : 0 : poly << m2p.toMapCoordinates( 0.0, static_cast<double>( sz.height() ) ).toQPointF();
387 : :
388 : 0 : return poly;
389 : 0 : }
390 : :
391 : 0 : double QgsMapSettings::mapUnitsPerPixel() const
392 : : {
393 : 0 : return mMapUnitsPerPixel;
394 : : }
395 : :
396 : 0 : double QgsMapSettings::scale() const
397 : : {
398 : 0 : return mScale;
399 : : }
400 : :
401 : 0 : QgsCoordinateTransformContext QgsMapSettings::transformContext() const
402 : : {
403 : : #ifdef QGISDEBUG
404 : : if ( !mHasTransformContext )
405 : : QgsDebugMsgLevel( QStringLiteral( "No QgsCoordinateTransformContext context set for transform" ), 4 );
406 : : #endif
407 : :
408 : 0 : return mTransformContext;
409 : : }
410 : :
411 : 0 : void QgsMapSettings::setTransformContext( const QgsCoordinateTransformContext &context )
412 : : {
413 : 0 : mTransformContext = context;
414 : : #ifdef QGISDEBUG
415 : : mHasTransformContext = true;
416 : : #endif
417 : 0 : }
418 : :
419 : 0 : QgsCoordinateTransform QgsMapSettings::layerTransform( const QgsMapLayer *layer ) const
420 : : {
421 : 0 : if ( !layer )
422 : 0 : return QgsCoordinateTransform();
423 : :
424 : 0 : return QgsCoordinateTransform( layer->crs(), mDestCRS, mTransformContext );
425 : 0 : }
426 : :
427 : 0 : double QgsMapSettings::layerToMapUnits( const QgsMapLayer *layer, const QgsRectangle &referenceExtent ) const
428 : : {
429 : 0 : return layerTransform( layer ).scaleFactor( referenceExtent );
430 : 0 : }
431 : :
432 : :
433 : 0 : QgsRectangle QgsMapSettings::layerExtentToOutputExtent( const QgsMapLayer *layer, QgsRectangle extent ) const
434 : : {
435 : : try
436 : : {
437 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
438 : 0 : if ( ct.isValid() )
439 : : {
440 : 0 : QgsDebugMsgLevel( QStringLiteral( "sourceCrs = %1" ).arg( ct.sourceCrs().authid() ), 3 );
441 : 0 : QgsDebugMsgLevel( QStringLiteral( "destCRS = %1" ).arg( ct.destinationCrs().authid() ), 3 );
442 : 0 : QgsDebugMsgLevel( QStringLiteral( "extent %1" ).arg( extent.toString() ), 3 );
443 : 0 : ct.setBallparkTransformsAreAppropriate( true );
444 : 0 : extent = ct.transformBoundingBox( extent );
445 : 0 : }
446 : 0 : }
447 : : catch ( QgsCsException &cse )
448 : : {
449 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
450 : 0 : }
451 : :
452 : 0 : QgsDebugMsgLevel( QStringLiteral( "proj extent = %1 " ).arg( extent.toString() ), 3 );
453 : :
454 : 0 : return extent;
455 : 0 : }
456 : :
457 : :
458 : 0 : QgsRectangle QgsMapSettings::outputExtentToLayerExtent( const QgsMapLayer *layer, QgsRectangle extent ) const
459 : : {
460 : : try
461 : : {
462 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
463 : 0 : ct.setBallparkTransformsAreAppropriate( true );
464 : 0 : if ( ct.isValid() )
465 : : {
466 : 0 : QgsDebugMsgLevel( QStringLiteral( "sourceCrs = %1" ).arg( ct.sourceCrs().authid() ), 3 );
467 : 0 : QgsDebugMsgLevel( QStringLiteral( "destCRS = %1" ).arg( ct.destinationCrs().authid() ), 3 );
468 : 0 : QgsDebugMsgLevel( QStringLiteral( "extent = %1" ).arg( extent.toString() ), 3 );
469 : 0 : extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
470 : 0 : }
471 : 0 : }
472 : : catch ( QgsCsException &cse )
473 : : {
474 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
475 : 0 : }
476 : :
477 : 0 : QgsDebugMsgLevel( QStringLiteral( "proj extent = %1" ).arg( extent.toString() ), 3 );
478 : :
479 : 0 : return extent;
480 : 0 : }
481 : :
482 : :
483 : 0 : QgsPointXY QgsMapSettings::layerToMapCoordinates( const QgsMapLayer *layer, QgsPointXY point ) const
484 : : {
485 : : try
486 : : {
487 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
488 : 0 : if ( ct.isValid() )
489 : 0 : point = ct.transform( point, QgsCoordinateTransform::ForwardTransform );
490 : 0 : }
491 : : catch ( QgsCsException &cse )
492 : : {
493 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
494 : 0 : }
495 : :
496 : 0 : return point;
497 : 0 : }
498 : :
499 : 0 : QgsPoint QgsMapSettings::layerToMapCoordinates( const QgsMapLayer *layer, const QgsPoint &point ) const
500 : : {
501 : 0 : double x = point.x();
502 : 0 : double y = point.y();
503 : 0 : double z = point.z();
504 : :
505 : : try
506 : : {
507 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
508 : 0 : if ( ct.isValid() )
509 : 0 : ct.transformInPlace( x, y, z, QgsCoordinateTransform::ForwardTransform );
510 : 0 : }
511 : : catch ( QgsCsException &cse )
512 : : {
513 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
514 : 0 : }
515 : :
516 : 0 : return QgsPoint( x, y, z );
517 : 0 : }
518 : :
519 : :
520 : 0 : QgsRectangle QgsMapSettings::layerToMapCoordinates( const QgsMapLayer *layer, QgsRectangle rect ) const
521 : : {
522 : : try
523 : : {
524 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
525 : 0 : if ( ct.isValid() )
526 : 0 : rect = ct.transform( rect, QgsCoordinateTransform::ForwardTransform );
527 : 0 : }
528 : : catch ( QgsCsException &cse )
529 : : {
530 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
531 : 0 : }
532 : :
533 : 0 : return rect;
534 : 0 : }
535 : :
536 : :
537 : 0 : QgsPointXY QgsMapSettings::mapToLayerCoordinates( const QgsMapLayer *layer, QgsPointXY point ) const
538 : : {
539 : : try
540 : : {
541 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
542 : 0 : if ( ct.isValid() )
543 : 0 : point = ct.transform( point, QgsCoordinateTransform::ReverseTransform );
544 : 0 : }
545 : : catch ( QgsCsException &cse )
546 : : {
547 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
548 : 0 : }
549 : :
550 : 0 : return point;
551 : 0 : }
552 : :
553 : 0 : QgsPoint QgsMapSettings::mapToLayerCoordinates( const QgsMapLayer *layer, const QgsPoint &point ) const
554 : : {
555 : 0 : double x = point.x();
556 : 0 : double y = point.y();
557 : 0 : double z = point.z();
558 : :
559 : : try
560 : : {
561 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
562 : 0 : if ( ct.isValid() )
563 : 0 : ct.transformInPlace( x, y, z, QgsCoordinateTransform::ReverseTransform );
564 : 0 : }
565 : : catch ( QgsCsException &cse )
566 : : {
567 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
568 : 0 : }
569 : :
570 : 0 : return QgsPoint( x, y, z );
571 : 0 : }
572 : :
573 : :
574 : 0 : QgsRectangle QgsMapSettings::mapToLayerCoordinates( const QgsMapLayer *layer, QgsRectangle rect ) const
575 : : {
576 : : try
577 : : {
578 : 0 : QgsCoordinateTransform ct = layerTransform( layer );
579 : 0 : if ( ct.isValid() )
580 : 0 : rect = ct.transform( rect, QgsCoordinateTransform::ReverseTransform );
581 : 0 : }
582 : : catch ( QgsCsException &cse )
583 : : {
584 : 0 : QgsMessageLog::logMessage( QObject::tr( "Transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) );
585 : 0 : }
586 : :
587 : 0 : return rect;
588 : 0 : }
589 : :
590 : :
591 : :
592 : 0 : QgsRectangle QgsMapSettings::fullExtent() const
593 : : {
594 : : // reset the map canvas extent since the extent may now be smaller
595 : : // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
596 : 0 : QgsRectangle fullExtent;
597 : 0 : fullExtent.setMinimal();
598 : :
599 : : // iterate through the map layers and test each layers extent
600 : : // against the current min and max values
601 : 0 : QgsDebugMsgLevel( QStringLiteral( "Layer count: %1" ).arg( mLayers.count() ), 5 );
602 : 0 : const auto constMLayers = mLayers;
603 : 0 : for ( const QgsWeakMapLayerPointer &layerPtr : constMLayers )
604 : : {
605 : 0 : if ( QgsMapLayer *lyr = layerPtr.data() )
606 : : {
607 : 0 : QgsDebugMsgLevel( "Updating extent using " + lyr->name(), 5 );
608 : 0 : QgsDebugMsgLevel( "Input extent: " + lyr->extent().toString(), 5 );
609 : :
610 : 0 : if ( lyr->extent().isNull() )
611 : 0 : continue;
612 : :
613 : : // Layer extents are stored in the coordinate system (CS) of the
614 : : // layer. The extent must be projected to the canvas CS
615 : 0 : QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() );
616 : :
617 : 0 : QgsDebugMsgLevel( "Output extent: " + extent.toString(), 5 );
618 : 0 : fullExtent.combineExtentWith( extent );
619 : 0 : }
620 : : }
621 : :
622 : 0 : if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 )
623 : : {
624 : : // If all of the features are at the one point, buffer the
625 : : // rectangle a bit. If they are all at zero, do something a bit
626 : : // more crude.
627 : :
628 : 0 : if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 &&
629 : 0 : fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 )
630 : : {
631 : 0 : fullExtent.set( -1.0, -1.0, 1.0, 1.0 );
632 : 0 : }
633 : : else
634 : : {
635 : 0 : const double padFactor = 1e-8;
636 : 0 : double widthPad = fullExtent.xMinimum() * padFactor;
637 : 0 : double heightPad = fullExtent.yMinimum() * padFactor;
638 : 0 : double xmin = fullExtent.xMinimum() - widthPad;
639 : 0 : double xmax = fullExtent.xMaximum() + widthPad;
640 : 0 : double ymin = fullExtent.yMinimum() - heightPad;
641 : 0 : double ymax = fullExtent.yMaximum() + heightPad;
642 : 0 : fullExtent.set( xmin, ymin, xmax, ymax );
643 : : }
644 : 0 : }
645 : :
646 : 0 : QgsDebugMsgLevel( "Full extent: " + fullExtent.toString(), 5 );
647 : : return fullExtent;
648 : 0 : }
649 : :
650 : :
651 : 0 : void QgsMapSettings::readXml( QDomNode &node )
652 : : {
653 : : // set destination CRS
654 : 0 : QgsCoordinateReferenceSystem srs;
655 : 0 : QDomNode srsNode = node.namedItem( QStringLiteral( "destinationsrs" ) );
656 : 0 : if ( !srsNode.isNull() )
657 : : {
658 : 0 : srs.readXml( srsNode );
659 : 0 : }
660 : 0 : setDestinationCrs( srs );
661 : :
662 : : // set extent
663 : 0 : QDomNode extentNode = node.namedItem( QStringLiteral( "extent" ) );
664 : 0 : QgsRectangle aoi = QgsXmlUtils::readRectangle( extentNode.toElement() );
665 : 0 : setExtent( aoi );
666 : :
667 : : // set rotation
668 : 0 : QDomNode rotationNode = node.namedItem( QStringLiteral( "rotation" ) );
669 : 0 : QString rotationVal = rotationNode.toElement().text();
670 : 0 : if ( ! rotationVal.isEmpty() )
671 : : {
672 : 0 : double rot = rotationVal.toDouble();
673 : 0 : setRotation( rot );
674 : 0 : }
675 : :
676 : : //render map tile
677 : 0 : QDomElement renderMapTileElem = node.firstChildElement( QStringLiteral( "rendermaptile" ) );
678 : 0 : if ( !renderMapTileElem.isNull() )
679 : : {
680 : 0 : setFlag( QgsMapSettings::RenderMapTile, renderMapTileElem.text() == QLatin1String( "1" ) );
681 : 0 : }
682 : 0 : }
683 : :
684 : :
685 : :
686 : 0 : void QgsMapSettings::writeXml( QDomNode &node, QDomDocument &doc )
687 : : {
688 : : // units
689 : 0 : node.appendChild( QgsXmlUtils::writeMapUnits( mapUnits(), doc ) );
690 : :
691 : : // Write current view extents
692 : 0 : node.appendChild( QgsXmlUtils::writeRectangle( extent(), doc ) );
693 : :
694 : : // Write current view rotation
695 : 0 : QDomElement rotNode = doc.createElement( QStringLiteral( "rotation" ) );
696 : 0 : rotNode.appendChild(
697 : 0 : doc.createTextNode( qgsDoubleToString( rotation() ) )
698 : : );
699 : 0 : node.appendChild( rotNode );
700 : :
701 : : // destination CRS
702 : 0 : if ( mDestCRS.isValid() )
703 : : {
704 : 0 : QDomElement srsNode = doc.createElement( QStringLiteral( "destinationsrs" ) );
705 : 0 : node.appendChild( srsNode );
706 : 0 : mDestCRS.writeXml( srsNode, doc );
707 : 0 : }
708 : :
709 : : //render map tile
710 : 0 : QDomElement renderMapTileElem = doc.createElement( QStringLiteral( "rendermaptile" ) );
711 : 0 : QDomText renderMapTileText = doc.createTextNode( testFlag( QgsMapSettings::RenderMapTile ) ? "1" : "0" );
712 : 0 : renderMapTileElem.appendChild( renderMapTileText );
713 : 0 : node.appendChild( renderMapTileElem );
714 : 0 : }
715 : :
716 : 0 : QgsGeometry QgsMapSettings::labelBoundaryGeometry() const
717 : : {
718 : 0 : return mLabelBoundaryGeometry;
719 : : }
720 : :
721 : 0 : void QgsMapSettings::setLabelBoundaryGeometry( const QgsGeometry &boundary )
722 : : {
723 : 0 : mLabelBoundaryGeometry = boundary;
724 : 0 : }
725 : :
726 : 0 : void QgsMapSettings::addClippingRegion( const QgsMapClippingRegion ®ion )
727 : : {
728 : 0 : mClippingRegions.append( region );
729 : 0 : }
730 : :
731 : 0 : void QgsMapSettings::setClippingRegions( const QList<QgsMapClippingRegion> ®ions )
732 : : {
733 : 0 : mClippingRegions = regions;
734 : 0 : }
735 : :
736 : 0 : QList<QgsMapClippingRegion> QgsMapSettings::clippingRegions() const
737 : : {
738 : 0 : return mClippingRegions;
739 : : }
740 : :
741 : 0 : void QgsMapSettings::addRenderedFeatureHandler( QgsRenderedFeatureHandlerInterface *handler )
742 : : {
743 : 0 : mRenderedFeatureHandlers.append( handler );
744 : 0 : }
745 : :
746 : 0 : QList<QgsRenderedFeatureHandlerInterface *> QgsMapSettings::renderedFeatureHandlers() const
747 : : {
748 : 0 : return mRenderedFeatureHandlers;
749 : : }
750 : :
751 : 0 : QgsDoubleRange QgsMapSettings::zRange() const
752 : : {
753 : 0 : return mZRange;
754 : : }
755 : :
756 : 0 : void QgsMapSettings::setZRange( const QgsDoubleRange &zRange )
757 : : {
758 : 0 : mZRange = zRange;
759 : 0 : }
|