void TestQgsCoordinateTransform::isShortCircuited() { QgsCoordinateTransform tr; //invalid transform shortcircuits QVERIFY( tr.isShortCircuited() ); QgsCoordinateReferenceSystem srs1; srs1.createFromSrid( 3994 ); QgsCoordinateReferenceSystem srs2; srs2.createFromSrid( 4326 ); // valid source, invalid destination QgsCoordinateTransform tr2( srs1, QgsCoordinateReferenceSystem() ); QVERIFY( tr2.isShortCircuited() ); // invalid source, valid destination QgsCoordinateTransform tr3( QgsCoordinateReferenceSystem(), srs2 ); QVERIFY( tr3.isShortCircuited() ); // equal, valid source and destination QgsCoordinateTransform tr4( srs1, srs1 ); QVERIFY( tr4.isShortCircuited() ); // valid but different source and destination QgsCoordinateTransform tr5( srs1, srs2 ); QVERIFY( !tr5.isShortCircuited() ); // try to short circuit by changing dest tr5.setDestinationCrs( srs1 ); QVERIFY( tr5.isShortCircuited() ); }
bool QgsVectorLayerRenderer::render() { if ( mGeometryType == QgsWkbTypes::NullGeometry || mGeometryType == QgsWkbTypes::UnknownGeometry ) return true; if ( !mRenderer ) { mErrors.append( QObject::tr( "No renderer for drawing." ) ); return false; } bool usingEffect = false; if ( mRenderer->paintEffect() && mRenderer->paintEffect()->enabled() ) { usingEffect = true; mRenderer->paintEffect()->begin( mContext ); } // Per feature blending mode if ( mContext.useAdvancedEffects() && mFeatureBlendMode != QPainter::CompositionMode_SourceOver ) { // set the painter to the feature blend mode, so that features drawn // on this layer will interact and blend with each other mContext.painter()->setCompositionMode( mFeatureBlendMode ); } mRenderer->startRender( mContext, mFields ); QString rendererFilter = mRenderer->filter( mFields ); QgsRectangle requestExtent = mContext.extent(); mRenderer->modifyRequestExtent( requestExtent, mContext ); QgsFeatureRequest featureRequest = QgsFeatureRequest() .setFilterRect( requestExtent ) .setSubsetOfAttributes( mAttrNames, mFields ) .setExpressionContext( mContext.expressionContext() ); if ( mRenderer->orderByEnabled() ) { featureRequest.setOrderBy( mRenderer->orderBy() ); } const QgsFeatureFilterProvider *featureFilterProvider = mContext.featureFilterProvider(); if ( featureFilterProvider ) { featureFilterProvider->filterFeatures( mLayer, featureRequest ); } if ( !rendererFilter.isEmpty() && rendererFilter != QLatin1String( "TRUE" ) ) { featureRequest.combineFilterExpression( rendererFilter ); } // enable the simplification of the geometries (Using the current map2pixel context) before send it to renderer engine. if ( mSimplifyGeometry ) { double map2pixelTol = mSimplifyMethod.threshold(); bool validTransform = true; const QgsMapToPixel &mtp = mContext.mapToPixel(); map2pixelTol *= mtp.mapUnitsPerPixel(); QgsCoordinateTransform ct = mContext.coordinateTransform(); // resize the tolerance using the change of size of an 1-BBOX from the source CoordinateSystem to the target CoordinateSystem if ( ct.isValid() && !ct.isShortCircuited() ) { try { QgsPointXY center = mContext.extent().center(); double rectSize = ct.sourceCrs().isGeographic() ? 0.0008983 /* ~100/(40075014/360=111319.4833) */ : 100; QgsRectangle sourceRect = QgsRectangle( center.x(), center.y(), center.x() + rectSize, center.y() + rectSize ); QgsRectangle targetRect = ct.transform( sourceRect ); QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceTransformRect=%1" ).arg( sourceRect.toString( 16 ) ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetTransformRect=%1" ).arg( targetRect.toString( 16 ) ), 4 ); if ( !sourceRect.isEmpty() && sourceRect.isFinite() && !targetRect.isEmpty() && targetRect.isFinite() ) { QgsPointXY minimumSrcPoint( sourceRect.xMinimum(), sourceRect.yMinimum() ); QgsPointXY maximumSrcPoint( sourceRect.xMaximum(), sourceRect.yMaximum() ); QgsPointXY minimumDstPoint( targetRect.xMinimum(), targetRect.yMinimum() ); QgsPointXY maximumDstPoint( targetRect.xMaximum(), targetRect.yMaximum() ); double sourceHypothenuse = std::sqrt( minimumSrcPoint.sqrDist( maximumSrcPoint ) ); double targetHypothenuse = std::sqrt( minimumDstPoint.sqrDist( maximumDstPoint ) ); QgsDebugMsgLevel( QStringLiteral( "Simplify - SourceHypothenuse=%1" ).arg( sourceHypothenuse ), 4 ); QgsDebugMsgLevel( QStringLiteral( "Simplify - TargetHypothenuse=%1" ).arg( targetHypothenuse ), 4 ); if ( !qgsDoubleNear( targetHypothenuse, 0.0 ) ) map2pixelTol *= ( sourceHypothenuse / targetHypothenuse ); } } catch ( QgsCsException &cse ) { QgsMessageLog::logMessage( QObject::tr( "Simplify transform error caught: %1" ).arg( cse.what() ), QObject::tr( "CRS" ) ); validTransform = false; } } if ( validTransform ) { QgsSimplifyMethod simplifyMethod; simplifyMethod.setMethodType( QgsSimplifyMethod::OptimizeForRendering ); simplifyMethod.setTolerance( map2pixelTol ); simplifyMethod.setThreshold( mSimplifyMethod.threshold() ); simplifyMethod.setForceLocalOptimization( mSimplifyMethod.forceLocalOptimization() ); featureRequest.setSimplifyMethod( simplifyMethod ); QgsVectorSimplifyMethod vectorMethod = mSimplifyMethod; vectorMethod.setTolerance( map2pixelTol ); mContext.setVectorSimplifyMethod( vectorMethod ); } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } } else { QgsVectorSimplifyMethod vectorMethod; vectorMethod.setSimplifyHints( QgsVectorSimplifyMethod::NoSimplification ); mContext.setVectorSimplifyMethod( vectorMethod ); } QgsFeatureIterator fit = mSource->getFeatures( featureRequest ); // Attach an interruption checker so that iterators that have potentially // slow fetchFeature() implementations, such as in the WFS provider, can // check it, instead of relying on just the mContext.renderingStopped() check // in drawRenderer() fit.setInterruptionChecker( mInterruptionChecker.get() ); if ( ( mRenderer->capabilities() & QgsFeatureRenderer::SymbolLevels ) && mRenderer->usingSymbolLevels() ) drawRendererLevels( fit ); else drawRenderer( fit ); if ( !fit.isValid() ) { mErrors.append( QStringLiteral( "Data source invalid" ) ); } if ( usingEffect ) { mRenderer->paintEffect()->end( mContext ); } return true; }
QgsLabelFeature* QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature& feat, QgsRenderContext &context, QgsGeometry* obstacleGeometry ) { const QgsMapSettings& mapSettings = mEngine->mapSettings(); const QgsDiagramRenderer* dr = mSettings.getRenderer(); if ( dr ) { QList<QgsDiagramSettings> settingList = dr->diagramSettings(); if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility ) { double minScale = settingList.at( 0 ).minScaleDenominator; if ( minScale > 0 && context.rendererScale() < minScale ) { return nullptr; } double maxScale = settingList.at( 0 ).maxScaleDenominator; if ( maxScale > 0 && context.rendererScale() > maxScale ) { return nullptr; } } } //convert geom to geos QgsGeometry geom = feat.geometry(); QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() ); if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) ) { //PAL features are prerotated, so extent also needs to be unrotated extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() ); } const GEOSGeometry* geos_geom = nullptr; QScopedPointer<QgsGeometry> scopedPreparedGeom; if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) { scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), &extentGeom ) ) ); QgsGeometry* preparedGeom = scopedPreparedGeom.data(); if ( preparedGeom->isEmpty() ) return nullptr; geos_geom = preparedGeom->asGeos(); } else { geos_geom = geom.asGeos(); } if ( !geos_geom ) return nullptr; // invalid geometry GEOSGeometry* geomCopy = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom ); const GEOSGeometry* geosObstacleGeom = nullptr; QScopedPointer<QgsGeometry> scopedObstacleGeom; if ( mSettings.isObstacle() && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ) ) { QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( *obstacleGeometry, context, mSettings.coordinateTransform(), &extentGeom ); geosObstacleGeom = preparedObstacleGeom.asGeos(); } else if ( mSettings.isObstacle() && obstacleGeometry ) { geosObstacleGeom = obstacleGeometry->asGeos(); } GEOSGeometry* geosObstacleGeomClone = nullptr; if ( geosObstacleGeom ) { geosObstacleGeomClone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geosObstacleGeom ); } double diagramWidth = 0; double diagramHeight = 0; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat, context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } } // feature to the layer bool alwaysShow = mSettings.showAllDiagrams(); int ddColX = mSettings.xPosColumn; int ddColY = mSettings.yPosColumn; double ddPosX = 0.0; double ddPosY = 0.0; bool ddPos = ( ddColX >= 0 && ddColY >= 0 ); if ( ddPos && ! feat.attribute( ddColX ).isNull() && ! feat.attribute( ddColY ).isNull() ) { bool posXOk, posYOk; ddPosX = feat.attribute( ddColX ).toDouble( &posXOk ); ddPosY = feat.attribute( ddColY ).toDouble( &posYOk ); if ( !posXOk || !posYOk ) { ddPos = false; } else { QgsCoordinateTransform ct = mSettings.coordinateTransform(); if ( ct.isValid() && !ct.isShortCircuited() ) { double z = 0; ct.transformInPlace( ddPosX, ddPosY, z ); } //data defined diagram position is always centered ddPosX -= diagramWidth / 2.0; ddPosY -= diagramHeight / 2.0; } } else ddPos = false; int ddColShow = mSettings.showColumn; if ( ddColShow >= 0 && ! feat.attribute( ddColShow ).isNull() ) { bool showOk; bool ddShow = feat.attribute( ddColShow ).toDouble( &showOk ); if ( showOk && ! ddShow ) return nullptr; } QgsDiagramLabelFeature* lf = new QgsDiagramLabelFeature( feat.id(), geomCopy, QSizeF( diagramWidth, diagramHeight ) ); lf->setHasFixedPosition( ddPos ); lf->setFixedPosition( QgsPoint( ddPosX, ddPosY ) ); lf->setHasFixedAngle( true ); lf->setFixedAngle( 0 ); lf->setAlwaysShow( alwaysShow ); lf->setIsObstacle( mSettings.isObstacle() ); lf->setZIndex( mSettings.getZIndex() ); if ( geosObstacleGeomClone ) { lf->setObstacleGeometry( geosObstacleGeomClone ); } if ( dr ) { //append the diagram attributes to lbl lf->setAttributes( feat.attributes() ); } QgsPoint ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 ); QgsPoint ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 ); lf->setDistLabel( ptOne.distance( ptZero ) * mSettings.distance() ); return lf; }
QgsLabelFeature *QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature &feat, QgsRenderContext &context, const QgsGeometry &obstacleGeometry ) { const QgsMapSettings &mapSettings = mEngine->mapSettings(); const QgsDiagramRenderer *dr = mSettings.renderer(); if ( dr ) { QList<QgsDiagramSettings> settingList = dr->diagramSettings(); if ( !settingList.isEmpty() && settingList.at( 0 ).scaleBasedVisibility ) { double maxScale = settingList.at( 0 ).maximumScale; if ( maxScale > 0 && context.rendererScale() < maxScale ) { return nullptr; } double minScale = settingList.at( 0 ).minimumScale; if ( minScale > 0 && context.rendererScale() > minScale ) { return nullptr; } } } // data defined show diagram? check this before doing any other processing if ( !mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::Show, context.expressionContext(), true ) ) return nullptr; // data defined obstacle? bool isObstacle = mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::IsObstacle, context.expressionContext(), mSettings.isObstacle() ); //convert geom to geos QgsGeometry geom = feat.geometry(); QgsGeometry extentGeom = QgsGeometry::fromRect( mapSettings.visibleExtent() ); if ( !qgsDoubleNear( mapSettings.rotation(), 0.0 ) ) { //PAL features are prerotated, so extent also needs to be unrotated extentGeom.rotate( -mapSettings.rotation(), mapSettings.visibleExtent().center() ); } geos::unique_ptr geomCopy; std::unique_ptr<QgsGeometry> scopedPreparedGeom; if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, mSettings.coordinateTransform(), extentGeom ) ) { scopedPreparedGeom.reset( new QgsGeometry( QgsPalLabeling::prepareGeometry( geom, context, mSettings.coordinateTransform(), extentGeom ) ) ); QgsGeometry *preparedGeom = scopedPreparedGeom.get(); if ( preparedGeom->isNull() ) return nullptr; geomCopy = QgsGeos::asGeos( *preparedGeom ); } else { geomCopy = QgsGeos::asGeos( geom ); } if ( !geomCopy ) return nullptr; // invalid geometry geos::unique_ptr geosObstacleGeomClone; std::unique_ptr<QgsGeometry> scopedObstacleGeom; if ( isObstacle && obstacleGeometry && QgsPalLabeling::geometryRequiresPreparation( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom ) ) { QgsGeometry preparedObstacleGeom = QgsPalLabeling::prepareGeometry( obstacleGeometry, context, mSettings.coordinateTransform(), extentGeom ); geosObstacleGeomClone = QgsGeos::asGeos( preparedObstacleGeom ); } else if ( mSettings.isObstacle() && !obstacleGeometry.isNull() ) { geosObstacleGeomClone = QgsGeos::asGeos( obstacleGeometry ); } double diagramWidth = 0; double diagramHeight = 0; if ( dr ) { QSizeF diagSize = dr->sizeMapUnits( feat, context ); if ( diagSize.isValid() ) { diagramWidth = diagSize.width(); diagramHeight = diagSize.height(); } } // feature to the layer bool alwaysShow = mSettings.showAllDiagrams(); context.expressionContext().setOriginalValueVariable( alwaysShow ); alwaysShow = mSettings.dataDefinedProperties().valueAsBool( QgsDiagramLayerSettings::AlwaysShow, context.expressionContext(), alwaysShow ); // new style data defined position bool ddPos = false; double ddPosX = 0.0; double ddPosY = 0.0; if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::PositionX ) && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::PositionX ).isActive() && mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::PositionY ) && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::PositionY ).isActive() ) { ddPosX = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionX, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() ); ddPosY = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::PositionY, context.expressionContext(), std::numeric_limits<double>::quiet_NaN() ); ddPos = !std::isnan( ddPosX ) && !std::isnan( ddPosY ); if ( ddPos ) { QgsCoordinateTransform ct = mSettings.coordinateTransform(); if ( ct.isValid() && !ct.isShortCircuited() ) { double z = 0; ct.transformInPlace( ddPosX, ddPosY, z ); } //data defined diagram position is always centered ddPosX -= diagramWidth / 2.0; ddPosY -= diagramHeight / 2.0; } } QgsDiagramLabelFeature *lf = new QgsDiagramLabelFeature( feat.id(), std::move( geomCopy ), QSizeF( diagramWidth, diagramHeight ) ); lf->setHasFixedPosition( ddPos ); lf->setFixedPosition( QgsPointXY( ddPosX, ddPosY ) ); lf->setHasFixedAngle( true ); lf->setFixedAngle( 0 ); lf->setAlwaysShow( alwaysShow ); lf->setIsObstacle( isObstacle ); if ( geosObstacleGeomClone ) { lf->setObstacleGeometry( std::move( geosObstacleGeomClone ) ); } if ( dr ) { //append the diagram attributes to lbl lf->setAttributes( feat.attributes() ); } // data defined priority? if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::Priority ) && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::Priority ).isActive() ) { context.expressionContext().setOriginalValueVariable( mSettings.priority() ); double priorityD = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::Priority, context.expressionContext(), mSettings.priority() ); priorityD = qBound( 0.0, priorityD, 10.0 ); priorityD = 1 - priorityD / 10.0; // convert 0..10 --> 1..0 lf->setPriority( priorityD ); } // z-Index double zIndex = mSettings.zIndex(); if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::ZIndex ) && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::ZIndex ).isActive() ) { context.expressionContext().setOriginalValueVariable( zIndex ); zIndex = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::ZIndex, context.expressionContext(), zIndex ); } lf->setZIndex( zIndex ); // label distance QgsPointXY ptZero = mapSettings.mapToPixel().toMapCoordinates( 0, 0 ); QgsPointXY ptOne = mapSettings.mapToPixel().toMapCoordinates( 1, 0 ); double dist = mSettings.distance(); if ( mSettings.dataDefinedProperties().hasProperty( QgsDiagramLayerSettings::Distance ) && mSettings.dataDefinedProperties().property( QgsDiagramLayerSettings::Distance ).isActive() ) { context.expressionContext().setOriginalValueVariable( dist ); dist = mSettings.dataDefinedProperties().valueAsDouble( QgsDiagramLayerSettings::Distance, context.expressionContext(), dist ); } dist *= ptOne.distance( ptZero ); lf->setDistLabel( dist ); return lf; }