QgsFeatureIds QgsMapToolSelectUtils::getMatchingFeatures( QgsMapCanvas *canvas, const QgsGeometry &selectGeometry, bool doContains, bool singleSelect ) { QgsFeatureIds newSelectedFeatures; if ( selectGeometry.type() != QgsWkbTypes::PolygonGeometry ) return newSelectedFeatures; QgsVectorLayer *vlayer = QgsMapToolSelectUtils::getCurrentVectorLayer( canvas ); if ( !vlayer ) return newSelectedFeatures; // toLayerCoordinates will throw an exception for any 'invalid' points in // the rubber band. // For example, if you project a world map onto a globe using EPSG 2163 // and then click somewhere off the globe, an exception will be thrown. QgsGeometry selectGeomTrans = selectGeometry; try { QgsCoordinateTransform ct( canvas->mapSettings().destinationCrs(), vlayer->crs(), QgsProject::instance() ); if ( !ct.isShortCircuited() && selectGeomTrans.type() == QgsWkbTypes::PolygonGeometry ) { // convert add more points to the edges of the rectangle // improve transformation result QgsPolygonXY poly( selectGeomTrans.asPolygon() ); if ( poly.size() == 1 && poly.at( 0 ).size() == 5 ) { const QgsPolylineXY &ringIn = poly.at( 0 ); QgsPolygonXY newpoly( 1 ); newpoly[0].resize( 41 ); QgsPolylineXY &ringOut = newpoly[0]; ringOut[ 0 ] = ringIn.at( 0 ); int i = 1; for ( int j = 1; j < 5; j++ ) { QgsVector v( ( ringIn.at( j ) - ringIn.at( j - 1 ) ) / 10.0 ); for ( int k = 0; k < 9; k++ ) { ringOut[ i ] = ringOut[ i - 1 ] + v; i++; } ringOut[ i++ ] = ringIn.at( j ); } selectGeomTrans = QgsGeometry::fromPolygonXY( newpoly ); } } selectGeomTrans.transform( ct ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and leave existing selection unchanged QgsDebugMsg( QStringLiteral( "Caught CRS exception " ) ); QgisApp::instance()->messageBar()->pushMessage( QObject::tr( "CRS Exception" ), QObject::tr( "Selection extends beyond layer's coordinate system" ), Qgis::Warning, QgisApp::instance()->messageTimeout() ); return newSelectedFeatures; } QgsDebugMsgLevel( "Selection layer: " + vlayer->name(), 3 ); QgsDebugMsgLevel( "Selection polygon: " + selectGeomTrans.asWkt(), 3 ); QgsDebugMsgLevel( "doContains: " + QString( doContains ? "T" : "F" ), 3 ); QgsRenderContext context = QgsRenderContext::fromMapSettings( canvas->mapSettings() ); context.expressionContext() << QgsExpressionContextUtils::layerScope( vlayer ); std::unique_ptr< QgsFeatureRenderer > r; if ( vlayer->renderer() ) { r.reset( vlayer->renderer()->clone() ); r->startRender( context, vlayer->fields() ); } QgsFeatureRequest request; request.setFilterRect( selectGeomTrans.boundingBox() ); request.setFlags( QgsFeatureRequest::ExactIntersect ); if ( r ) request.setSubsetOfAttributes( r->usedAttributes( context ), vlayer->fields() ); else request.setNoAttributes(); QgsFeatureIterator fit = vlayer->getFeatures( request ); QgsFeature f; QgsFeatureId closestFeatureId = 0; bool foundSingleFeature = false; double closestFeatureDist = std::numeric_limits<double>::max(); while ( fit.nextFeature( f ) ) { context.expressionContext().setFeature( f ); // make sure to only use features that are visible if ( r && !r->willRenderFeature( f, context ) ) continue; QgsGeometry g = f.geometry(); if ( doContains ) { if ( !selectGeomTrans.contains( g ) ) continue; } else { if ( !selectGeomTrans.intersects( g ) ) continue; } if ( singleSelect ) { foundSingleFeature = true; double distance = g.distance( selectGeomTrans ); if ( distance <= closestFeatureDist ) { closestFeatureDist = distance; closestFeatureId = f.id(); } } else { newSelectedFeatures.insert( f.id() ); } } if ( singleSelect && foundSingleFeature ) { newSelectedFeatures.insert( closestFeatureId ); } if ( r ) r->stopRender( context ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) ); return newSelectedFeatures; }
QDomElement QgsXmlUtils::writeVariant( const QVariant &value, QDomDocument &doc ) { QDomElement element = doc.createElement( QStringLiteral( "Option" ) ); switch ( value.type() ) { case QVariant::Invalid: { element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "invalid" ) ); break; } case QVariant::Map: { QVariantMap map = value.toMap(); for ( auto option = map.constBegin(); option != map.constEnd(); ++option ) { QDomElement optionElement = writeVariant( option.value(), doc ); optionElement.setAttribute( QStringLiteral( "name" ), option.key() ); element.appendChild( optionElement ); element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "Map" ) ); } break; } case QVariant::List: { QVariantList list = value.toList(); const auto constList = list; for ( const QVariant &value : constList ) { QDomElement valueElement = writeVariant( value, doc ); element.appendChild( valueElement ); element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "List" ) ); } break; } case QVariant::StringList: { QStringList list = value.toStringList(); const auto constList = list; for ( const QString &value : constList ) { QDomElement valueElement = writeVariant( value, doc ); element.appendChild( valueElement ); element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "StringList" ) ); } break; } case QVariant::Int: case QVariant::UInt: case QVariant::Bool: case QVariant::Double: case QVariant::LongLong: case QVariant::ULongLong: case QVariant::String: element.setAttribute( QStringLiteral( "type" ), QVariant::typeToName( value.type() ) ); element.setAttribute( QStringLiteral( "value" ), value.toString() ); break; case QVariant::UserType: { if ( value.canConvert< QgsProperty >() ) { element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QgsProperty" ) ); const QDomElement propertyElem = QgsXmlUtils::writeVariant( value.value< QgsProperty >().toVariant(), doc ); element.appendChild( propertyElem ); break; } else if ( value.canConvert< QgsCoordinateReferenceSystem >() ) { element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QgsCoordinateReferenceSystem" ) ); const QgsCoordinateReferenceSystem crs = value.value< QgsCoordinateReferenceSystem >(); crs.writeXml( element, doc ); break; } else if ( value.canConvert< QgsGeometry >() ) { element.setAttribute( QStringLiteral( "type" ), QStringLiteral( "QgsGeometry" ) ); const QgsGeometry geom = value.value< QgsGeometry >(); element.setAttribute( QStringLiteral( "value" ), geom.asWkt() ); break; } FALLTHROUGH } default: Q_ASSERT_X( false, "QgsXmlUtils::writeVariant", QStringLiteral( "unsupported variant type %1" ).arg( QVariant::typeToName( value.type() ) ).toLocal8Bit() ); break; } return element; }