QgsRectangle QgsRectangle::intersect( const QgsRectangle * rect ) const { QgsRectangle intersection = QgsRectangle(); //If they don't actually intersect an empty QgsRectangle should be returned if ( !rect || !intersects( *rect ) ) { return intersection; } intersection.setXMinimum( xmin > rect->xMinimum() ? xmin : rect->xMinimum() ); intersection.setXMaximum( xmax < rect->xMaximum() ? xmax : rect->xMaximum() ); intersection.setYMinimum( ymin > rect->yMinimum() ? ymin : rect->yMinimum() ); intersection.setYMaximum( ymax < rect->yMaximum() ? ymax : rect->yMaximum() ); return intersection; }
void QgsComposerMapWidget::on_mSetToMapCanvasExtentButton_clicked() { if ( mComposerMap ) { const QgsMapRenderer* renderer = mComposerMap->mapRenderer(); if ( renderer ) { QgsRectangle newExtent = renderer->extent(); //Make sure the width/height ratio is the same as in current composer map extent. //This is to keep the map item frame and the page layout fixed QgsRectangle currentMapExtent = *( mComposerMap->currentMapExtent() ); double currentWidthHeightRatio = currentMapExtent.width() / currentMapExtent.height(); double newWidthHeightRatio = newExtent.width() / newExtent.height(); if ( currentWidthHeightRatio < newWidthHeightRatio ) { //enlarge height of new extent, ensuring the map center stays the same double newHeight = newExtent.width() / currentWidthHeightRatio; double deltaHeight = newHeight - newExtent.height(); newExtent.setYMinimum( newExtent.yMinimum() - deltaHeight / 2 ); newExtent.setYMaximum( newExtent.yMaximum() + deltaHeight / 2 ); } else { //enlarge width of new extent, ensuring the map center stays the same double newWidth = currentWidthHeightRatio * newExtent.height(); double deltaWidth = newWidth - newExtent.width(); newExtent.setXMinimum( newExtent.xMinimum() - deltaWidth / 2 ); newExtent.setXMaximum( newExtent.xMaximum() + deltaWidth / 2 ); } //fill text into line edits mXMinLineEdit->setText( QString::number( newExtent.xMinimum() ) ); mXMaxLineEdit->setText( QString::number( newExtent.xMaximum() ) ); mYMinLineEdit->setText( QString::number( newExtent.yMinimum() ) ); mYMaxLineEdit->setText( QString::number( newExtent.yMaximum() ) ); mComposerMap->beginCommand( tr( "Map extent changed" ) ); mComposerMap->setNewExtent( newExtent ); mComposerMap->endCommand(); } } }
void TestQgsCoordinateTransform::transformBoundingBox() { //test transforming a bounding box which crosses the 180 degree longitude line QgsCoordinateReferenceSystem sourceSrs; sourceSrs.createFromSrid( 3994 ); QgsCoordinateReferenceSystem destSrs; destSrs.createFromSrid( 4326 ); QgsCoordinateTransform tr( sourceSrs, destSrs ); QgsRectangle crossingRect( 6374985, -3626584, 7021195, -3272435 ); QgsRectangle resultRect = tr.transformBoundingBox( crossingRect, QgsCoordinateTransform::ForwardTransform, true ); QgsRectangle expectedRect; expectedRect.setXMinimum( 175.771 ); expectedRect.setYMinimum( -39.7222 ); expectedRect.setXMaximum( -176.549 ); expectedRect.setYMaximum( -36.3951 ); QVERIFY( qgsDoubleNear( resultRect.xMinimum(), expectedRect.xMinimum(), 0.001 ) ); QVERIFY( qgsDoubleNear( resultRect.yMinimum(), expectedRect.yMinimum(), 0.001 ) ); QVERIFY( qgsDoubleNear( resultRect.xMaximum(), expectedRect.xMaximum(), 0.001 ) ); QVERIFY( qgsDoubleNear( resultRect.yMaximum(), expectedRect.yMaximum(), 0.001 ) ); }
QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPoint &mapPosition, QgsMapCanvas *mapCanvas ) { QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ); if ( !vlayer ) return QString(); double searchRadius = QgsMapTool::searchRadiusMU( mapCanvas ); QgsRectangle r; r.setXMinimum( mapPosition.x() - searchRadius ); r.setYMinimum( mapPosition.y() - searchRadius ); r.setXMaximum( mapPosition.x() + searchRadius ); r.setYMaximum( mapPosition.y() + searchRadius ); r = mapCanvas->mapSettings().mapToLayerCoordinates( layer, r ); QgsFeature feature; if ( !vlayer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ).nextFeature( feature ) ) return QString(); QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vlayer ) ); if ( mapCanvas ) context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) ); context.setFeature( feature ); QString mapTip = vlayer->mapTipTemplate(); if ( !mapTip.isEmpty() ) { return QgsExpression::replaceExpressionText( mapTip, &context ); } else { QgsExpression exp( vlayer->displayExpression() ); return exp.evaluate( &context ).toString(); } }
/** * Selection routine called by the mouse release event * @param thePoint = QgsPoint representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( QgsPoint thePoint ) { if ( 0 == mCanvas ) return; QgsVectorLayer* myLayer = ( QgsVectorLayer* )mCanvas->currentLayer( ); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = mCanvas->extent( ).width( ) * (( double )QGis::DEFAULT_IDENTIFY_RADIUS / 100.0 ); QgsRectangle myRectangle; myRectangle.setXMinimum( thePoint.x( ) - searchWidth ); myRectangle.setXMaximum( thePoint.x( ) + searchWidth ); myRectangle.setYMinimum( thePoint.y( ) - searchWidth ); myRectangle.setYMaximum( thePoint.y( ) + searchWidth ); //Transform rectange to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //Rather than add to the current selection, clear all selected features myLayer->removeSelection( false ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->setSelectedFeatures( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, NULL ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
int QgsVectorLayerEditUtils::addRing( const QList<QgsPoint>& ring ) { if ( !L->hasGeometryType() ) return 5; int addRingReturnCode = 5; //default: return code for 'ring not inserted' double xMin, yMin, xMax, yMax; QgsRectangle bBox; if ( boundingBoxFromPointList( ring, xMin, yMin, xMax, yMax ) == 0 ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return 3; //ring not valid } QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) { addRingReturnCode = f.geometry()->addRing( ring ); if ( addRingReturnCode == 0 ) { L->editBuffer()->changeGeometry( f.id(), f.geometry() ); //setModified( true, true ); break; } } return addRingReturnCode; }
void QgsHeatmapRenderer::modifyRequestExtent( QgsRectangle &extent, QgsRenderContext& context ) { //we need to expand out the request extent so that it includes points which are up to the heatmap radius outside of the //actual visible extent double extension = 0.0; if ( mRadiusUnit == QgsSymbolV2::Pixel ) { extension = mRadius / QgsSymbolLayerV2Utils::pixelSizeScaleFactor( context, QgsSymbolV2::MapUnit, QgsMapUnitScale() ); } else if ( mRadiusUnit == QgsSymbolV2::MM ) { double pixelSize = mRadius * QgsSymbolLayerV2Utils::pixelSizeScaleFactor( context, QgsSymbolV2::MM, QgsMapUnitScale() ); extension = pixelSize / QgsSymbolLayerV2Utils::pixelSizeScaleFactor( context, QgsSymbolV2::MapUnit, QgsMapUnitScale() ); } else { extension = mRadius; } extent.setXMinimum( extent.xMinimum() - extension ); extent.setXMaximum( extent.xMaximum() + extension ); extent.setYMinimum( extent.yMinimum() - extension ); extent.setYMaximum( extent.yMaximum() + extension ); }
void QgsHighlight::updateRect() { if ( mGeometry ) { QgsRectangle r = mGeometry->boundingBox(); if ( r.isEmpty() ) { double d = mMapCanvas->extent().width() * 0.005; r.setXMinimum( r.xMinimum() - d ); r.setYMinimum( r.yMinimum() - d ); r.setXMaximum( r.xMaximum() + d ); r.setYMaximum( r.yMaximum() + d ); } setRect( r ); setVisible( mGeometry ); } else { setRect( QgsRectangle() ); } }
/** * Selection routine called by the mouse release event * @param point = QgsPointXY representing the x, y coordinates of the mouse release event */ void eVisEventIdTool::select( const QgsPointXY &point ) { if ( !mCanvas ) return; QgsVectorLayer *myLayer = ( QgsVectorLayer * )mCanvas->currentLayer(); // create the search rectangle. this was modeled after the QgsMapIdentifyTool in core QGIS application double searchWidth = QgsMapTool::searchRadiusMU( mCanvas ); QgsRectangle myRectangle; myRectangle.setXMinimum( point.x() - searchWidth ); myRectangle.setXMaximum( point.x() + searchWidth ); myRectangle.setYMinimum( point.y() - searchWidth ); myRectangle.setYMaximum( point.y() + searchWidth ); //Transform rectangle to map coordinates myRectangle = toLayerCoordinates( myLayer, myRectangle ); //select features QgsFeatureIterator fit = myLayer->getFeatures( QgsFeatureRequest().setFilterRect( myRectangle ).setFlags( QgsFeatureRequest::ExactIntersect ).setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; QgsFeatureIds newSelectedFeatures; while ( fit.nextFeature( f ) ) { newSelectedFeatures.insert( f.id() ); } myLayer->selectByIds( newSelectedFeatures ); //Launch a new event browser to view selected features mBrowser = new eVisGenericEventBrowserGui( mCanvas, mCanvas, nullptr ); mBrowser->setAttribute( Qt::WA_DeleteOnClose ); }
bool QgsConfigParser::exGeographicBoundingBox( const QDomElement& layerElement, QgsRectangle& rect ) const { if ( layerElement.isNull() ) { return false; } QDomElement exGeogElem = layerElement.firstChildElement( "EX_GeographicBoundingBox" ); if ( exGeogElem.isNull() ) { return false; } bool ok = true; //minx QDomElement westBoundElem = exGeogElem.firstChildElement( "westBoundLongitude" ); if ( westBoundElem.isNull() ) { return false; } double minx = westBoundElem.text().toDouble( &ok ); if ( !ok ) { return false; } //maxx QDomElement eastBoundElem = exGeogElem.firstChildElement( "eastBoundLongitude" ); if ( eastBoundElem.isNull() ) { return false; } double maxx = eastBoundElem.text().toDouble( &ok ); if ( !ok ) { return false; } //miny QDomElement southBoundElem = exGeogElem.firstChildElement( "southBoundLatitude" ); if ( southBoundElem.isNull() ) { return false; } double miny = southBoundElem.text().toDouble( &ok ); if ( !ok ) { return false; } //maxy QDomElement northBoundElem = exGeogElem.firstChildElement( "northBoundLatitude" ); if ( northBoundElem.isNull() ) { return false; } double maxy = northBoundElem.text().toDouble( &ok ); if ( !ok ) { return false; } rect.setXMinimum( minx ); rect.setXMaximum( maxx ); rect.setYMinimum( miny ); rect.setYMaximum( maxy ); return true; }
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing ) { if ( !L->hasGeometryType() ) return 4; QgsFeatureList newFeatures; //store all the newly created features double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line int returnCode = 0; int splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplittedFeatures = 0; QgsFeatureIterator features; const QgsFeatureIds selectedIds = L->selectedFeaturesIds(); if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection { features = L->selectedFeaturesIterator(); } else //else consider all the feature that intersect the bounding box of the split line { if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return 1; } if ( bBox.isEmpty() ) { //if the bbox is a line, try to make a square out of it if ( bBox.width() == 0.0 && bBox.height() > 0 ) { bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); } else if ( bBox.height() == 0.0 && bBox.width() > 0 ) { bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); } else { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; if ( L->crs().geographicFlag() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); bBox.setYMinimum( bBox.yMinimum() - bufferDistance ); bBox.setYMaximum( bBox.yMaximum() + bufferDistance ); } } features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } QgsFeature feat; while ( features.nextFeature( feat ) ) { if ( !feat.constGeometry() ) { continue; } QList<QgsGeometry*> newGeometries; QList<QgsPoint> topologyTestPoints; QgsGeometry* newGeometry = 0; splitFunctionReturn = feat.geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); if ( splitFunctionReturn == 0 ) { //change this geometry L->editBuffer()->changeGeometry( feat.id(), feat.geometry() ); //insert new features for ( int i = 0; i < newGeometries.size(); ++i ) { newGeometry = newGeometries.at( i ); QgsFeature newFeature; newFeature.setGeometry( newGeometry ); //use default value where possible for primary key (e.g. autoincrement), //and use the value from the original (split) feature if not primary key QgsAttributes newAttributes = feat.attributes(); Q_FOREACH ( int pkIdx, L->dataProvider()->pkAttributeIndexes() ) { const QVariant defaultValue = L->dataProvider()->defaultValue( pkIdx ); if ( !defaultValue.isNull() ) { newAttributes[ pkIdx ] = defaultValue; } else //try with NULL { newAttributes[ pkIdx ] = QVariant(); } } newFeature.setAttributes( newAttributes ); newFeatures.append( newFeature ); } if ( topologicalEditing ) { QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin(); for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) { addTopologicalPoints( *topol_it ); } } ++numberOfSplittedFeatures; } else if ( splitFunctionReturn > 1 ) //1 means no split but also no error
int QgsVectorLayerEditUtils::splitParts( const QList<QgsPoint>& splitLine, bool topologicalEditing ) { if ( !L->hasGeometryType() ) return 4; double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line int returnCode = 0; int splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplittedParts = 0; QgsFeatureIterator fit; if ( L->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection { fit = L->selectedFeaturesIterator(); } else //else consider all the feature that intersect the bounding box of the split line { if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return 1; } if ( bBox.isEmpty() ) { //if the bbox is a line, try to make a square out of it if ( bBox.width() == 0.0 && bBox.height() > 0 ) { bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); } else if ( bBox.height() == 0.0 && bBox.width() > 0 ) { bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); } else { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; if ( L->crs().isGeographic() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); bBox.setYMinimum( bBox.yMinimum() - bufferDistance ); bBox.setYMaximum( bBox.yMaximum() + bufferDistance ); } } fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } int addPartRet = 0; QgsFeature feat; while ( fit.nextFeature( feat ) ) { QList<QgsGeometry> newGeometries; QList<QgsPoint> topologyTestPoints; QgsGeometry featureGeom = feat.geometry(); splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); if ( splitFunctionReturn == 0 ) { //add new parts if ( !newGeometries.isEmpty() ) featureGeom.convertToMultiType(); for ( int i = 0; i < newGeometries.size(); ++i ) { addPartRet = featureGeom.addPart( newGeometries.at( i ) ); if ( addPartRet ) break; } // For test only: Exception already thrown here... // feat.geometry()->asWkb(); if ( !addPartRet ) { L->editBuffer()->changeGeometry( feat.id(), featureGeom ); } else { // Test addPartRet switch ( addPartRet ) { case 1: QgsDebugMsg( "Not a multipolygon" ); break; case 2: QgsDebugMsg( "Not a valid geometry" ); break; case 3: QgsDebugMsg( "New polygon ring" ); break; } } L->editBuffer()->changeGeometry( feat.id(), featureGeom ); if ( topologicalEditing ) { QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin(); for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) { addTopologicalPoints( *topol_it ); } } ++numberOfSplittedParts; } else if ( splitFunctionReturn > 1 ) //1 means no split but also no error { returnCode = splitFunctionReturn; } } if ( numberOfSplittedParts == 0 && L->selectedFeatureCount() > 0 && returnCode == 0 ) { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split returnCode = 4; } return returnCode; }
// On Android, there there is a libqgis.so instead of a qgis executable. // The main method symbol of this library needs to be exported so it can be called by java // On Windows this main is included in qgis_app and called from mainwin.cpp APP_EXPORT #endif int main( int argc, char *argv[] ) { #ifdef Q_OS_MACX // Increase file resource limits (i.e., number of allowed open files) // (from code provided by Larry Biehl, Purdue University, USA, from 'MultiSpec' project) // This is generally 256 for the soft limit on Mac // NOTE: setrlimit() must come *before* initialization of stdio strings, // e.g. before any debug messages, or setrlimit() gets ignored // see: http://stackoverflow.com/a/17726104/2865523 struct rlimit rescLimit; if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 ) { rlim_t oldSoft( rescLimit.rlim_cur ); rlim_t oldHard( rescLimit.rlim_max ); #ifdef OPEN_MAX rlim_t newSoft( OPEN_MAX ); rlim_t newHard( std::min( oldHard, newSoft ) ); #else rlim_t newSoft( 4096 ); rlim_t newHard( std::min( ( rlim_t )8192, oldHard ) ); #endif if ( rescLimit.rlim_cur < newSoft ) { rescLimit.rlim_cur = newSoft; rescLimit.rlim_max = newHard; if ( setrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 ) { QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard NEW: %1 / %2" ) .arg( rescLimit.rlim_cur ).arg( rescLimit.rlim_max ) ); } } Q_UNUSED( oldSoft ); //avoid warnings QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard ORIG: %1 / %2" ) .arg( oldSoft ).arg( oldHard ) ); } #endif QgsDebugMsg( QString( "Starting qgis main" ) ); #ifdef WIN32 // Windows #ifdef _MSC_VER _set_fmode( _O_BINARY ); #else //MinGW _fmode = _O_BINARY; #endif // _MSC_VER #endif // WIN32 // Set up the custom qWarning/qDebug custom handler #ifndef ANDROID qInstallMsgHandler( myMessageOutput ); #endif #if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__) signal( SIGQUIT, qgisCrash ); signal( SIGILL, qgisCrash ); signal( SIGFPE, qgisCrash ); signal( SIGSEGV, qgisCrash ); signal( SIGBUS, qgisCrash ); signal( SIGSYS, qgisCrash ); signal( SIGTRAP, qgisCrash ); signal( SIGXCPU, qgisCrash ); signal( SIGXFSZ, qgisCrash ); #endif #ifdef Q_OS_WIN SetUnhandledExceptionFilter( QgsCrashHandler::handle ); #endif // initialize random number seed qsrand( time( nullptr ) ); ///////////////////////////////////////////////////////////////// // Command line options 'behavior' flag setup //////////////////////////////////////////////////////////////// // // Parse the command line arguments, looking to see if the user has asked for any // special behaviors. Any remaining non command arguments will be kept aside to // be passed as a list of layers and / or a project that should be loaded. // // This behavior is used to load the app, snapshot the map, // save the image to disk and then exit QString mySnapshotFileName; QString configLocalStorageLocation; QString profileName; int mySnapshotWidth = 800; int mySnapshotHeight = 600; bool myHideSplash = false; bool mySettingsMigrationForce = false; bool mySkipVersionCheck = false; #if defined(ANDROID) QgsDebugMsg( QString( "Android: Splash hidden" ) ); myHideSplash = true; #endif bool myRestoreDefaultWindowState = false; bool myRestorePlugins = true; bool myCustomization = true; QString dxfOutputFile; QgsDxfExport::SymbologyExport dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology; double dxfScale = 50000.0; QString dxfEncoding = QStringLiteral( "CP1252" ); QString dxfPreset; QgsRectangle dxfExtent; // This behavior will set initial extent of map canvas, but only if // there are no command line arguments. This gives a usable map // extent when qgis starts with no layers loaded. When layers are // loaded, we let the layers define the initial extent. QString myInitialExtent; if ( argc == 1 ) myInitialExtent = QStringLiteral( "-1,-1,1,1" ); // This behavior will allow you to force the use of a translation file // which is useful for testing QString myTranslationCode; // The user can specify a path which will override the default path of custom // user settings (~/.qgis) and it will be used for QgsSettings INI file QString configpath; QString authdbdirectory; QString pythonfile; QString customizationfile; QString globalsettingsfile; // TODO Fix android #if defined(ANDROID) QgsDebugMsg( QString( "Android: All params stripped" ) );// Param %1" ).arg( argv[0] ) ); //put all QGIS settings in the same place configpath = QgsApplication::qgisSettingsDirPath(); QgsDebugMsg( QString( "Android: configpath set to %1" ).arg( configpath ) ); #endif QStringList args; if ( !bundleclicked( argc, argv ) ) { // Build a local QCoreApplication from arguments. This way, arguments are correctly parsed from their native locale // It will use QString::fromLocal8Bit( argv ) under Unix and GetCommandLine() under Windows. QCoreApplication coreApp( argc, argv ); args = QCoreApplication::arguments(); for ( int i = 1; i < args.size(); ++i ) { const QString &arg = args[i]; if ( arg == QLatin1String( "--help" ) || arg == QLatin1String( "-?" ) ) { usage( args[0] ); return 2; } else if ( arg == QLatin1String( "--nologo" ) || arg == QLatin1String( "-n" ) ) { myHideSplash = true; } else if ( arg == QLatin1String( "--version-migration" ) ) { mySettingsMigrationForce = true; } else if ( arg == QLatin1String( "--noversioncheck" ) || arg == QLatin1String( "-V" ) ) { mySkipVersionCheck = true; } else if ( arg == QLatin1String( "--noplugins" ) || arg == QLatin1String( "-P" ) ) { myRestorePlugins = false; } else if ( arg == QLatin1String( "--nocustomization" ) || arg == QLatin1String( "-C" ) ) { myCustomization = false; } else if ( i + 1 < argc && ( arg == QLatin1String( "--profile" ) ) ) { profileName = args[++i]; } else if ( i + 1 < argc && ( arg == QLatin1String( "--profiles-path" ) || arg == QLatin1String( "-s" ) ) ) { configLocalStorageLocation = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--snapshot" ) || arg == QLatin1String( "-s" ) ) ) { mySnapshotFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--width" ) || arg == QLatin1String( "-w" ) ) ) { mySnapshotWidth = QString( args[++i] ).toInt(); } else if ( i + 1 < argc && ( arg == QLatin1String( "--height" ) || arg == QLatin1String( "-h" ) ) ) { mySnapshotHeight = QString( args[++i] ).toInt(); } else if ( i + 1 < argc && ( arg == QLatin1String( "--lang" ) || arg == QLatin1String( "-l" ) ) ) { myTranslationCode = args[++i]; } else if ( i + 1 < argc && ( arg == QLatin1String( "--project" ) || arg == QLatin1String( "-p" ) ) ) { sProjectFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--extent" ) || arg == QLatin1String( "-e" ) ) ) { myInitialExtent = args[++i]; } else if ( i + 1 < argc && ( arg == QLatin1String( "--authdbdirectory" ) || arg == QLatin1String( "-a" ) ) ) { authdbdirectory = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--code" ) || arg == QLatin1String( "-f" ) ) ) { pythonfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--customizationfile" ) || arg == QLatin1String( "-z" ) ) ) { customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == QLatin1String( "--globalsettingsfile" ) || arg == QLatin1String( "-g" ) ) ) { globalsettingsfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( arg == QLatin1String( "--defaultui" ) || arg == QLatin1String( "-d" ) ) { myRestoreDefaultWindowState = true; } else if ( arg == QLatin1String( "--dxf-export" ) ) { dxfOutputFile = args[++i]; } else if ( arg == QLatin1String( "--dxf-extent" ) ) { QgsLocaleNumC l; QString ext( args[++i] ); QStringList coords( ext.split( ',' ) ); if ( coords.size() != 4 ) { std::cerr << "invalid dxf extent " << ext.toStdString() << std::endl; return 2; } for ( int i = 0; i < 4; i++ ) { bool ok; double d; d = coords[i].toDouble( &ok ); if ( !ok ) { std::cerr << "invalid dxf coordinate " << coords[i].toStdString() << " in extent " << ext.toStdString() << std::endl; return 2; } switch ( i ) { case 0: dxfExtent.setXMinimum( d ); break; case 1: dxfExtent.setYMinimum( d ); break; case 2: dxfExtent.setXMaximum( d ); break; case 3: dxfExtent.setYMaximum( d ); break; } } } else if ( arg == QLatin1String( "--dxf-symbology-mode" ) ) { QString mode( args[++i] ); if ( mode == QLatin1String( "none" ) ) { dxfSymbologyMode = QgsDxfExport::NoSymbology; } else if ( mode == QLatin1String( "symbollayer" ) ) { dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology; } else if ( mode == QLatin1String( "feature" ) ) { dxfSymbologyMode = QgsDxfExport::FeatureSymbology; } else { std::cerr << "invalid dxf symbology mode " << mode.toStdString() << std::endl; return 2; } } else if ( arg == QLatin1String( "--dxf-scale-denom" ) ) { bool ok; QString scale( args[++i] ); dxfScale = scale.toDouble( &ok ); if ( !ok ) { std::cerr << "invalid dxf scale " << scale.toStdString() << std::endl; return 2; } } else if ( arg == QLatin1String( "--dxf-encoding" ) ) { dxfEncoding = args[++i]; } else if ( arg == QLatin1String( "--dxf-preset" ) ) { dxfPreset = args[++i]; } else if ( arg == QLatin1String( "--" ) ) { for ( i++; i < args.size(); ++i ) sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) ); } else { sFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) ); } } } ///////////////////////////////////////////////////////////////////// // If no --project was specified, parse the args to look for a // // .qgs file and set myProjectFileName to it. This allows loading // // of a project file by clicking on it in various desktop managers // // where an appropriate mime-type has been set up. // ///////////////////////////////////////////////////////////////////// if ( sProjectFileName.isEmpty() ) { // check for a .qgs for ( int i = 0; i < args.size(); i++ ) { QString arg = QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ); if ( arg.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) ) { sProjectFileName = arg; break; } } } ///////////////////////////////////////////////////////////////////// // Now we have the handlers for the different behaviors... //////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // Initialize the application and the translation stuff ///////////////////////////////////////////////////////////////////// #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(ANDROID) bool myUseGuiFlag = nullptr != getenv( "DISPLAY" ); #else bool myUseGuiFlag = true; #endif if ( !myUseGuiFlag ) { std::cerr << QObject::tr( "QGIS starting in non-interactive mode not supported.\n" "You are seeing this message most likely because you " "have no DISPLAY environment variable set.\n" ).toUtf8().constData(); exit( 1 ); //exit for now until a version of qgis is capabable of running non interactive } // GUI customization is enabled according to settings (loaded when instance is created) // we force disabled here if --nocustomization argument is used if ( !myCustomization ) { QgsCustomization::instance()->setEnabled( false ); } QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME ); QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN ); QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME ); QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false ); QgsSettings settings; if ( configLocalStorageLocation.isEmpty() ) { if ( getenv( "QGIS_CUSTOM_CONFIG_PATH" ) ) { configLocalStorageLocation = getenv( "QGIS_CUSTOM_CONFIG_PATH" ); } else if ( settings.contains( QStringLiteral( "profilesPath" ), QgsSettings::Core ) ) { configLocalStorageLocation = settings.value( QStringLiteral( "profilesPath" ), "", QgsSettings::Core ).toString(); QgsDebugMsg( QString( "Loading profiles path from global config at %1" ).arg( configLocalStorageLocation ) ); } // If it is still empty at this point we get it from the standard location. if ( configLocalStorageLocation.isEmpty() ) { configLocalStorageLocation = QStandardPaths::standardLocations( QStandardPaths::AppDataLocation ).value( 0 ); } } QString rootProfileFolder = QgsUserProfileManager::resolveProfilesFolder( configLocalStorageLocation ); QgsUserProfileManager manager( rootProfileFolder ); QgsUserProfile *profile = manager.getProfile( profileName, true ); QString profileFolder = profile->folder(); profileName = profile->name(); delete profile; QgsDebugMsg( "User profile details:" ); QgsDebugMsg( QString( "\t - %1" ).arg( profileName ) ); QgsDebugMsg( QString( "\t - %1" ).arg( profileFolder ) ); QgsDebugMsg( QString( "\t - %1" ).arg( rootProfileFolder ) ); QgsApplication myApp( argc, argv, myUseGuiFlag, profileFolder ); // SetUp the QgsSettings Global Settings: // - use the path specified with --globalsettingsfile path, // - use the environment if not found // - use a default location as a fallback if ( globalsettingsfile.isEmpty() ) { globalsettingsfile = getenv( "QGIS_GLOBAL_SETTINGS_FILE" ); } if ( globalsettingsfile.isEmpty() ) { QString default_globalsettingsfile = QgsApplication::pkgDataPath() + "/qgis_global_settings.ini"; if ( QFile::exists( default_globalsettingsfile ) ) { globalsettingsfile = default_globalsettingsfile; } } if ( !globalsettingsfile.isEmpty() ) { if ( ! QgsSettings::setGlobalSettingsPath( globalsettingsfile ) ) { QgsMessageLog::logMessage( QStringLiteral( "Invalid globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) ); } else { QgsMessageLog::logMessage( QStringLiteral( "Successfully loaded globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) ); } } // Settings migration is only supported on the default profile for now. if ( profileName == "default" ) { std::unique_ptr< QgsVersionMigration > migration( QgsVersionMigration::canMigrate( 20000, Qgis::QGIS_VERSION_INT ) ); if ( migration && ( mySettingsMigrationForce || migration->requiresMigration() ) ) { QgsDebugMsg( "RUNNING MIGRATION" ); migration->runMigration(); } } // Redefine QgsApplication::libraryPaths as necessary. // IMPORTANT: Do *after* QgsApplication myApp(...), but *before* Qt uses any plugins, // e.g. loading splash screen, setting window icon, etc. // Always honor QT_PLUGIN_PATH env var or qt.conf, which will // be part of libraryPaths just after QgsApplication creation. #ifdef Q_OS_WIN // For non static builds on win (static builds are not supported) // we need to be sure we can find the qt image plugins. QCoreApplication::addLibraryPath( QApplication::applicationDirPath() + QDir::separator() + "qtplugins" ); #endif #ifdef Q_OS_MAC // Resulting libraryPaths has critical QGIS plugin paths first, then any Qt plugin paths, then // any dev-defined paths (in app's qt.conf) and/or user-defined paths (QT_PLUGIN_PATH env var). // // NOTE: Minimizes, though does not fully protect against, crashes due to dev/user-defined libs // built against a different Qt/QGIS, while still allowing custom C++ plugins to load. QStringList libPaths( QCoreApplication::libraryPaths() ); QgsDebugMsgLevel( QStringLiteral( "Initial macOS QCoreApplication::libraryPaths: %1" ) .arg( libPaths.join( " " ) ), 4 ); // Strip all critical paths that should always be prepended if ( libPaths.removeAll( QDir::cleanPath( QgsApplication::pluginPath() ) ) ) { QgsDebugMsgLevel( QStringLiteral( "QgsApplication::pluginPath removed from initial libraryPaths" ), 4 ); } if ( libPaths.removeAll( QCoreApplication::applicationDirPath() ) ) { QgsDebugMsgLevel( QStringLiteral( "QCoreApplication::applicationDirPath removed from initial libraryPaths" ), 4 ); } // Prepend path, so a standard Qt bundle directory is parsed QgsDebugMsgLevel( QStringLiteral( "Prepending QCoreApplication::applicationDirPath to libraryPaths" ), 4 ); libPaths.prepend( QCoreApplication::applicationDirPath() ); // Check if we are running in a 'release' app bundle, i.e. contains copied-in // standard Qt-specific plugin subdirectories (ones never created by QGIS, e.g. 'sqldrivers' is). // Note: bundleclicked(...) is inadequate to determine which *type* of bundle was opened, e.g. release or build dir. // An app bundled with QGIS_MACAPP_BUNDLE > 0 is considered a release bundle. QString relLibPath( QDir::cleanPath( QCoreApplication::applicationDirPath().append( "/../PlugIns" ) ) ); // Note: relLibPath becomes the defacto QT_PLUGINS_DIR of a release app bundle if ( QFile::exists( relLibPath + QStringLiteral( "/imageformats" ) ) && QFile::exists( relLibPath + QStringLiteral( "/codecs" ) ) ) { // We are in a release app bundle. // Strip QT_PLUGINS_DIR because it will crash a launched release app bundle, since // the appropriate Qt frameworks and plugins have been copied into the bundle. if ( libPaths.removeAll( QT_PLUGINS_DIR ) ) { QgsDebugMsgLevel( QStringLiteral( "QT_PLUGINS_DIR removed from initial libraryPaths" ), 4 ); } // Prepend the Plugins path, so copied-in Qt plugin bundle directories are parsed. QgsDebugMsgLevel( QStringLiteral( "Prepending <bundle>/Plugins to libraryPaths" ), 4 ); libPaths.prepend( relLibPath ); // TODO: see if this or another method can be used to avoid QCA's install prefix plugins // from being parsed and loaded (causes multi-Qt-loaded errors when bundled Qt should // be the only one loaded). QCA core (> v2.1.3) needs an update first. //setenv( "QCA_PLUGIN_PATH", relLibPath.toUtf8().constData(), 1 ); } else { // We are either running from build dir bundle, or launching Mach-O binary directly. // Add system Qt plugins, since they are not bundled, and not always referenced by default. // An app bundled with QGIS_MACAPP_BUNDLE = 0 will still have Plugins/qgis in it. // Note: Don't always prepend. // User may have already defined it in QT_PLUGIN_PATH in a specific order. if ( !libPaths.contains( QT_PLUGINS_DIR ) ) { QgsDebugMsgLevel( QStringLiteral( "Prepending QT_PLUGINS_DIR to libraryPaths" ), 4 ); libPaths.prepend( QT_PLUGINS_DIR ); } } QgsDebugMsgLevel( QStringLiteral( "Prepending QgsApplication::pluginPath to libraryPaths" ), 4 ); libPaths.prepend( QDir::cleanPath( QgsApplication::pluginPath() ) ); // Redefine library search paths. QCoreApplication::setLibraryPaths( libPaths ); QgsDebugMsgLevel( QStringLiteral( "Rewritten macOS QCoreApplication::libraryPaths: %1" ) .arg( QCoreApplication::libraryPaths().join( " " ) ), 4 ); #endif #ifdef Q_OS_MAC // Set hidpi icons; use SVG icons, as PNGs will be relatively too small QCoreApplication::setAttribute( Qt::AA_UseHighDpiPixmaps ); // Set 1024x1024 icon for dock, app switcher, etc., rendering myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + QStringLiteral( "qgis-icon-macos.png" ) ) ); #else myApp.setWindowIcon( QIcon( QgsApplication::appIconPath() ) ); #endif // TODO: use QgsSettings QSettings *customizationsettings = nullptr; // Using the customizationfile option always overrides the option and config path options. if ( !customizationfile.isEmpty() ) { customizationsettings = new QSettings( customizationfile, QSettings::IniFormat ); QgsCustomization::instance()->setEnabled( true ); } else { customizationsettings = new QSettings( QStringLiteral( "QGIS" ), QStringLiteral( "QGISCUSTOMIZATION2" ) ); } // Load and set possible default customization, must be done after QgsApplication init and QgsSettings ( QCoreApplication ) init QgsCustomization::instance()->setSettings( customizationsettings ); QgsCustomization::instance()->loadDefault(); #ifdef Q_OS_MACX // If the GDAL plugins are bundled with the application and GDAL_DRIVER_PATH // is not already defined, use the GDAL plugins in the application bundle. QString gdalPlugins( QCoreApplication::applicationDirPath().append( "/lib/gdalplugins" ) ); if ( QFile::exists( gdalPlugins ) && !getenv( "GDAL_DRIVER_PATH" ) ) { setenv( "GDAL_DRIVER_PATH", gdalPlugins.toUtf8(), 1 ); } // Point GDAL_DATA at any GDAL share directory embedded in the app bundle if ( !getenv( "GDAL_DATA" ) ) { QStringList gdalShares; QString appResources( QDir::cleanPath( QgsApplication::pkgDataPath() ) ); gdalShares << QCoreApplication::applicationDirPath().append( "/share/gdal" ) << appResources.append( "/share/gdal" ) << appResources.append( "/gdal" ); Q_FOREACH ( const QString &gdalShare, gdalShares ) { if ( QFile::exists( gdalShare ) ) { setenv( "GDAL_DATA", gdalShare.toUtf8().constData(), 1 ); break; } } }
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing ) { if ( !L->hasGeometryType() ) return 4; QgsFeatureList newFeatures; //store all the newly created features double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line int returnCode = 0; int splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplittedFeatures = 0; QgsFeatureList featureList; const QgsFeatureIds selectedIds = L->selectedFeaturesIds(); if ( selectedIds.size() > 0 ) //consider only the selected features if there is a selection { featureList = L->selectedFeatures(); } else //else consider all the feature that intersect the bounding box of the split line { if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return 1; } if ( bBox.isEmpty() ) { //if the bbox is a line, try to make a square out of it if ( bBox.width() == 0.0 && bBox.height() > 0 ) { bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); } else if ( bBox.height() == 0.0 && bBox.width() > 0 ) { bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); } else { return 2; } } QgsFeatureIterator fit = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featureList << QgsFeature( f ); } QgsFeatureList::iterator select_it = featureList.begin(); for ( ; select_it != featureList.end(); ++select_it ) { if ( !select_it->geometry() ) { continue; } QList<QgsGeometry*> newGeometries; QList<QgsPoint> topologyTestPoints; QgsGeometry* newGeometry = 0; splitFunctionReturn = select_it->geometry()->splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); if ( splitFunctionReturn == 0 ) { //change this geometry L->editBuffer()->changeGeometry( select_it->id(), select_it->geometry() ); //insert new features for ( int i = 0; i < newGeometries.size(); ++i ) { newGeometry = newGeometries.at( i ); QgsFeature newFeature; newFeature.setGeometry( newGeometry ); //use default value where possible (primary key issue), otherwise the value from the original (split) feature QgsAttributes newAttributes = select_it->attributes(); QVariant defaultValue; for ( int j = 0; j < newAttributes.count(); ++j ) { defaultValue = L->dataProvider()->defaultValue( j ); if ( !defaultValue.isNull() ) { newAttributes[ j ] = defaultValue; } } newFeature.setAttributes( newAttributes ); newFeatures.append( newFeature ); } if ( topologicalEditing ) { QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin(); for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) { addTopologicalPoints( *topol_it ); } } ++numberOfSplittedFeatures; } else if ( splitFunctionReturn > 1 ) //1 means no split but also no error { returnCode = splitFunctionReturn; } } if ( numberOfSplittedFeatures == 0 && selectedIds.size() > 0 ) { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split returnCode = 4; } //now add the new features to this vectorlayer L->editBuffer()->addFeatures( newFeatures ); return returnCode; }
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QgsPointXY point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); QgsRectangle r; // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { r = toLayerCoordinates( layer, r ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } QgsAction defaultAction = layer->actions()->defaultAction( QStringLiteral( "Canvas" ) ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature feat; while ( fit.nextFeature( feat ) ) { if ( defaultAction.isValid() ) { // define custom substitutions: layer id and clicked coords QgsExpressionContext context; context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope( QgsProject::instance() ) << QgsExpressionContextUtils::mapSettingsScope( mCanvas->mapSettings() ); QgsExpressionContextScope *actionScope = new QgsExpressionContextScope(); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_x" ), point.x(), true ) ); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "click_y" ), point.y(), true ) ); actionScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "action_scope" ), QStringLiteral( "Canvas" ), true ) ); context << actionScope; defaultAction.run( layer, feat, context ); } else { QgsMapLayerAction *mapLayerAction = QgsGui::mapLayerActionRegistry()->defaultActionForLayer( layer ); if ( mapLayerAction ) { mapLayerAction->triggerForFeature( layer, &feat ); } } } return true; }
void QgsMapToolFillRing::canvasReleaseEvent( QMouseEvent * e ) { //check if we operate on a vector layer QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mCanvas->currentLayer() ); if ( !vlayer ) { notifyNotVectorLayer(); return; } if ( !vlayer->isEditable() ) { notifyNotEditableLayer(); return; } //add point to list and to rubber band if ( e->button() == Qt::LeftButton ) { int error = addVertex( e->pos() ); if ( error == 1 ) { //current layer is not a vector layer return; } else if ( error == 2 ) { //problem with coordinate transformation QMessageBox::information( 0, tr( "Coordinate transform error" ), tr( "Cannot transform the point to the layers coordinate system" ) ); return; } startCapturing(); } else if ( e->button() == Qt::RightButton ) { deleteTempRubberBand(); closePolygon(); vlayer->beginEditCommand( tr( "Ring added and filled" ) ); int addRingReturnCode = vlayer->addRing( points() ); if ( addRingReturnCode != 0 ) { QString errorMessage; //todo: open message box to communicate errors if ( addRingReturnCode == 1 ) { errorMessage = tr( "A problem with geometry type occured" ); } else if ( addRingReturnCode == 2 ) { errorMessage = tr( "The inserted Ring is not closed" ); } else if ( addRingReturnCode == 3 ) { errorMessage = tr( "The inserted Ring is not a valid geometry" ); } else if ( addRingReturnCode == 4 ) { errorMessage = tr( "The inserted Ring crosses existing rings" ); } else if ( addRingReturnCode == 5 ) { errorMessage = tr( "The inserted Ring is not contained in a feature" ); } else { errorMessage = tr( "An unknown error occured" ); } QMessageBox::critical( 0, tr( "Error, could not add ring" ), errorMessage ); vlayer->destroyEditCommand(); } else { // find parent feature and get it attributes double xMin, xMax, yMin, yMax; QgsRectangle bBox; xMin = std::numeric_limits<double>::max(); xMax = -std::numeric_limits<double>::max(); yMin = std::numeric_limits<double>::max(); yMax = -std::numeric_limits<double>::max(); for ( QList<QgsPoint>::const_iterator it = points().constBegin(); it != points().constEnd(); ++it ) { if ( it->x() < xMin ) { xMin = it->x(); } if ( it->x() > xMax ) { xMax = it->x(); } if ( it->y() < yMin ) { yMin = it->y(); } if ( it->y() > yMax ) { yMax = it->y(); } } bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; bool res = false; while ( fit.nextFeature( f ) ) { //create QgsFeature with wkb representation QgsFeature* ft = new QgsFeature( vlayer->pendingFields(), 0 ); QgsGeometry *g; g = QgsGeometry::fromPolygon( QgsPolygon() << points().toVector() ); ft->setGeometry( g ); ft->setAttributes( f.attributes() ); if ( QgsApplication::keyboardModifiers() == Qt::ControlModifier ) { res = vlayer->addFeature( *ft ); } else { QgsAttributeDialog *dialog = new QgsAttributeDialog( vlayer, ft, false, NULL, true ); dialog->setIsAddDialog( true ); res = dialog->exec(); // will also add the feature } if ( res ) { vlayer->endEditCommand(); } else { delete ft; vlayer->destroyEditCommand(); } res = false; } } stopCapturing(); } }
bool QgsMapToolIdentify::identifyVectorLayer( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); // load identify radius from settings QSettings settings; double identifyValue = settings.value( "/Map/identifyRadius", QGis::DEFAULT_IDENTIFY_RADIUS ).toDouble(); QString ellipsoid = settings.value( "/qgis/measure/ellipsoid", "WGS84" ).toString(); if ( identifyValue <= 0.0 ) identifyValue = QGis::DEFAULT_IDENTIFY_RADIUS; int featureCount = 0; QgsFeatureList featureList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = mCanvas->extent().width() * ( identifyValue / 100.0 ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); layer->select( layer->pendingAllAttributesList(), r, true, true ); QgsFeature f; while ( layer->nextFeature( f ) ) featureList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } // init distance/area calculator QgsDistanceArea calc; if ( !featureList.count() == 0 ) { calc.setProjectionsEnabled( mCanvas->hasCrsTransformEnabled() ); // project? calc.setEllipsoid( ellipsoid ); calc.setSourceCrs( layer->crs().srsid() ); } QgsFeatureList::iterator f_it = featureList.begin(); for ( ; f_it != featureList.end(); ++f_it ) { featureCount++; QgsFeatureId fid = f_it->id(); QMap<QString, QString> derivedAttributes; // Calculate derived attributes and insert: // measure distance or area depending on geometry type if ( layer->geometryType() == QGis::Line ) { double dist = calc.measure( f_it->geometry() ); QGis::UnitType myDisplayUnits; convertMeasurement( calc, dist, myDisplayUnits, false ); QString str = calc.textUnit( dist, 3, myDisplayUnits, false ); // dist and myDisplayUnits are out params derivedAttributes.insert( tr( "Length" ), str ); if ( f_it->geometry()->wkbType() == QGis::WKBLineString || f_it->geometry()->wkbType() == QGis::WKBLineString25D ) { // Add the start and end points in as derived attributes str = QLocale::system().toString( f_it->geometry()->asPolyline().first().x(), 'g', 10 ); derivedAttributes.insert( tr( "firstX", "attributes get sorted; translation for lastX should be lexically larger than this one" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().first().y(), 'g', 10 ); derivedAttributes.insert( tr( "firstY" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().last().x(), 'g', 10 ); derivedAttributes.insert( tr( "lastX", "attributes get sorted; translation for firstX should be lexically smaller than this one" ), str ); str = QLocale::system().toString( f_it->geometry()->asPolyline().last().y(), 'g', 10 ); derivedAttributes.insert( tr( "lastY" ), str ); } } else if ( layer->geometryType() == QGis::Polygon ) { double area = calc.measure( f_it->geometry() ); QGis::UnitType myDisplayUnits; convertMeasurement( calc, area, myDisplayUnits, true ); // area and myDisplayUnits are out params QString str = calc.textUnit( area, 3, myDisplayUnits, true ); derivedAttributes.insert( tr( "Area" ), str ); } else if ( layer->geometryType() == QGis::Point && ( f_it->geometry()->wkbType() == QGis::WKBPoint || f_it->geometry()->wkbType() == QGis::WKBPoint25D ) ) { // Include the x and y coordinates of the point as a derived attribute QString str; str = QLocale::system().toString( f_it->geometry()->asPoint().x(), 'g', 10 ); derivedAttributes.insert( "X", str ); str = QLocale::system().toString( f_it->geometry()->asPoint().y(), 'g', 10 ); derivedAttributes.insert( "Y", str ); } derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) ); results()->addFeature( layer, *f_it, derivedAttributes ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); return featureCount > 0; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPointXY point, const QgsRectangle &viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPointXY pointInCanvasCrs = point; try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( QStringLiteral( "identify/format" ) ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to align raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // Width and height are calculated from not projected extent and we hope that // are similar to source width and height used to reproject layer for drawing. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = std::round( viewExtent.width() / mapUnitsPerPixel ); int height = std::round( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate X)" ), formatXCoordinate( pointInCanvasCrs ) ); derivedAttributes.insert( tr( "(clicked coordinate Y)" ), formatYCoordinate( pointInCanvasCrs ) ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QString valueString; if ( it.value().isNull() ) { valueString = tr( "no data" ); } else { QVariant value( it.value() ); // The cast is legit. Quoting QT doc : // "Although this function is declared as returning QVariant::Type, // the return value should be interpreted as QMetaType::Type" if ( static_cast<QMetaType::Type>( value.type() ) == QMetaType::Float ) { valueString = QgsRasterBlock::printValue( value.toFloat() ); } else { valueString = QgsRasterBlock::printValue( value.toDouble() ); } } attributes.insert( dprovider->generateBandName( it.key() ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } else if ( format == QgsRaster::IdentifyFormatFeature ) { for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QVariant value = it.value(); if ( value.type() == QVariant::Bool && !value.toBool() ) { // sublayer not visible or not queryable continue; } if ( value.type() == QVariant::String ) { // error // TODO: better error reporting QString label = layer->subLayers().value( it.key() ); attributes.clear(); attributes.insert( tr( "Error" ), value.toString() ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); continue; } // list of feature stores for a single sublayer const QgsFeatureStoreList featureStoreList = it.value().value<QgsFeatureStoreList>(); for ( const QgsFeatureStore &featureStore : featureStoreList ) { const QgsFeatureList storeFeatures = featureStore.features(); for ( QgsFeature feature : storeFeatures ) { attributes.clear(); // WMS sublayer and feature type, a sublayer may contain multiple feature types. // Sublayer name may be the same as layer name and feature type name // may be the same as sublayer. We try to avoid duplicities in label. QString sublayer = featureStore.params().value( QStringLiteral( "sublayer" ) ).toString(); QString featureType = featureStore.params().value( QStringLiteral( "featureType" ) ).toString(); // Strip UMN MapServer '_feature' featureType.remove( QStringLiteral( "_feature" ) ); QStringList labels; if ( sublayer.compare( layer->name(), Qt::CaseInsensitive ) != 0 ) { labels << sublayer; } if ( featureType.compare( sublayer, Qt::CaseInsensitive ) != 0 || labels.isEmpty() ) { labels << featureType; } QMap< QString, QString > derAttributes = derivedAttributes; derAttributes.unite( featureDerivedAttributes( &feature, layer ) ); IdentifyResult identifyResult( qobject_cast<QgsMapLayer *>( layer ), labels.join( QStringLiteral( " / " ) ), featureStore.fields(), feature, derAttributes ); identifyResult.mParams.insert( QStringLiteral( "getFeatureInfoUrl" ), featureStore.params().value( QStringLiteral( "getFeatureInfoUrl" ) ) ); results->append( identifyResult ); } } } } else // text or html { QgsDebugMsg( QString( "%1 HTML or text values" ).arg( values.size() ) ); for ( auto it = values.constBegin(); it != values.constEnd(); ++it ) { QString value = it.value().toString(); attributes.clear(); attributes.insert( QLatin1String( "" ), value ); QString label = layer->subLayers().value( it.key() ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } } } else { attributes.clear(); QString value = identifyResult.error().message( QgsErrorMessage::Text ); attributes.insert( tr( "Error" ), value ); QString label = tr( "Identify error" ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); } return true; }
void QgsMapToolZoom::canvasReleaseEvent( QMouseEvent * e ) { if ( e->button() != Qt::LeftButton ) return; // We are not really dragging in this case. This is sometimes caused by // a pen based computer reporting a press, move, and release, all the // one point. if ( mDragging && ( mZoomRect.topLeft() == mZoomRect.bottomRight() ) ) { mDragging = false; delete mRubberBand; mRubberBand = 0; } if ( mDragging ) { mDragging = false; delete mRubberBand; mRubberBand = 0; // store the rectangle mZoomRect.setRight( e->pos().x() ); mZoomRect.setBottom( e->pos().y() ); const QgsMapToPixel* coordinateTransform = mCanvas->getCoordinateTransform(); // set the extent to the zoomBox QgsPoint ll = coordinateTransform->toMapCoordinates( mZoomRect.left(), mZoomRect.bottom() ); QgsPoint ur = coordinateTransform->toMapCoordinates( mZoomRect.right(), mZoomRect.top() ); QgsRectangle r; r.setXMinimum( ll.x() ); r.setYMinimum( ll.y() ); r.setXMaximum( ur.x() ); r.setYMaximum( ur.y() ); r.normalize(); // prevent zooming to an empty extent if ( r.width() == 0 || r.height() == 0 ) { return; } if ( mZoomOut ) { QgsPoint cer = r.center(); QgsRectangle extent = mCanvas->extent(); double sf; if ( mZoomRect.width() > mZoomRect.height() ) { sf = extent.width() / r.width(); } else { sf = extent.height() / r.height(); } r.expand( sf ); QgsDebugMsg( QString( "Extent scaled by %1 to %2" ).arg( sf ).arg( r.toString().toLocal8Bit().constData() ) ); QgsDebugMsg( QString( "Center of currentExtent after scaling is %1" ).arg( r.center().toString().toLocal8Bit().constData() ) ); } mCanvas->setExtent( r ); mCanvas->refresh(); } else // not dragging { // change to zoom in/out by the default multiple mCanvas->zoomWithCenter( e->x(), e->y(), !mZoomOut ); } }
int main( int argc, char *argv[] ) { #ifdef Q_OS_MACX // Increase file resource limits (i.e., number of allowed open files) // (from code provided by Larry Biehl, Purdue University, USA, from 'MultiSpec' project) // This is generally 256 for the soft limit on Mac // NOTE: setrlimit() must come *before* initialization of stdio strings, // e.g. before any debug messages, or setrlimit() gets ignored // see: http://stackoverflow.com/a/17726104/2865523 struct rlimit rescLimit; if ( getrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 ) { rlim_t oldSoft( rescLimit.rlim_cur ); rlim_t oldHard( rescLimit.rlim_max ); #ifdef OPEN_MAX rlim_t newSoft( OPEN_MAX ); rlim_t newHard( std::min( oldHard, newSoft ) ); #else rlim_t newSoft( 4096 ); rlim_t newHard( std::min(( rlim_t )8192, oldHard ) ); #endif if ( rescLimit.rlim_cur < newSoft ) { rescLimit.rlim_cur = newSoft; rescLimit.rlim_max = newHard; if ( setrlimit( RLIMIT_NOFILE, &rescLimit ) == 0 ) { QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard NEW: %1 / %2" ) .arg( rescLimit.rlim_cur ).arg( rescLimit.rlim_max ) ); } } Q_UNUSED( oldSoft ); //avoid warnings QgsDebugMsg( QString( "Mac RLIMIT_NOFILE Soft/Hard ORIG: %1 / %2" ) .arg( oldSoft ).arg( oldHard ) ); } #endif QgsDebugMsg( QString( "Starting qgis main" ) ); #ifdef WIN32 // Windows #ifdef _MSC_VER _set_fmode( _O_BINARY ); #else //MinGW _fmode = _O_BINARY; #endif // _MSC_VER #endif // WIN32 // Set up the custom qWarning/qDebug custom handler #ifndef ANDROID qInstallMsgHandler( myMessageOutput ); #endif #if (defined(linux) && !defined(ANDROID)) || defined(__FreeBSD__) signal( SIGQUIT, qgisCrash ); signal( SIGILL, qgisCrash ); signal( SIGFPE, qgisCrash ); signal( SIGSEGV, qgisCrash ); signal( SIGBUS, qgisCrash ); signal( SIGSYS, qgisCrash ); signal( SIGTRAP, qgisCrash ); signal( SIGXCPU, qgisCrash ); signal( SIGXFSZ, qgisCrash ); #endif #ifdef Q_OS_WIN SetUnhandledExceptionFilter( QgisApp::qgisCrashDump ); #endif // initialize random number seed qsrand( time( nullptr ) ); ///////////////////////////////////////////////////////////////// // Command line options 'behaviour' flag setup //////////////////////////////////////////////////////////////// // // Parse the command line arguments, looking to see if the user has asked for any // special behaviours. Any remaining non command arguments will be kept aside to // be passed as a list of layers and / or a project that should be loaded. // // This behaviour is used to load the app, snapshot the map, // save the image to disk and then exit QString mySnapshotFileName = ""; int mySnapshotWidth = 800; int mySnapshotHeight = 600; bool myHideSplash = false; #if defined(ANDROID) QgsDebugMsg( QString( "Android: Splash hidden" ) ); myHideSplash = true; #endif bool myRestoreDefaultWindowState = false; bool myRestorePlugins = true; bool myCustomization = true; QString dxfOutputFile; QgsDxfExport::SymbologyExport dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology; double dxfScaleDenom = 50000.0; QString dxfEncoding = "CP1252"; QString dxfPreset; QgsRectangle dxfExtent; // This behaviour will set initial extent of map canvas, but only if // there are no command line arguments. This gives a usable map // extent when qgis starts with no layers loaded. When layers are // loaded, we let the layers define the initial extent. QString myInitialExtent = ""; if ( argc == 1 ) myInitialExtent = "-1,-1,1,1"; // This behaviour will allow you to force the use of a translation file // which is useful for testing QString myTranslationCode; // The user can specify a path which will override the default path of custom // user settings (~/.qgis) and it will be used for QSettings INI file QString configpath; QString optionpath; QString authdbdirectory; QString pythonfile; QString customizationfile; #if defined(ANDROID) QgsDebugMsg( QString( "Android: All params stripped" ) );// Param %1" ).arg( argv[0] ) ); //put all QGIS settings in the same place configpath = QgsApplication::qgisSettingsDirPath(); QgsDebugMsg( QString( "Android: configpath set to %1" ).arg( configpath ) ); #endif QStringList args; if ( !bundleclicked( argc, argv ) ) { // Build a local QCoreApplication from arguments. This way, arguments are correctly parsed from their native locale // It will use QString::fromLocal8Bit( argv ) under Unix and GetCommandLine() under Windows. QCoreApplication coreApp( argc, argv ); args = QCoreApplication::arguments(); for ( int i = 1; i < args.size(); ++i ) { const QString &arg = args[i]; if ( arg == "--help" || arg == "-?" ) { usage( args[0].toStdString() ); return 2; } else if ( arg == "--nologo" || arg == "-n" ) { myHideSplash = true; } else if ( arg == "--noplugins" || arg == "-P" ) { myRestorePlugins = false; } else if ( arg == "--nocustomization" || arg == "-C" ) { myCustomization = false; } else if ( i + 1 < argc && ( arg == "--snapshot" || arg == "-s" ) ) { mySnapshotFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == "--width" || arg == "-w" ) ) { mySnapshotWidth = QString( args[++i] ).toInt(); } else if ( i + 1 < argc && ( arg == "--height" || arg == "-h" ) ) { mySnapshotHeight = QString( args[++i] ).toInt(); } else if ( i + 1 < argc && ( arg == "--lang" || arg == "-l" ) ) { myTranslationCode = args[++i]; } else if ( i + 1 < argc && ( arg == "--project" || arg == "-p" ) ) { myProjectFileName = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == "--extent" || arg == "-e" ) ) { myInitialExtent = args[++i]; } else if ( i + 1 < argc && ( arg == "--optionspath" || arg == "-o" ) ) { optionpath = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == "--configpath" || arg == "-c" ) ) { configpath = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == "--authdbdirectory" || arg == "-a" ) ) { authdbdirectory = QDir::toNativeSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == "--code" || arg == "-f" ) ) { pythonfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == "--customizationfile" || arg == "-z" ) ) { customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( arg == "--defaultui" || arg == "-d" ) { myRestoreDefaultWindowState = true; } else if ( arg == "--dxf-export" ) { dxfOutputFile = args[++i]; } else if ( arg == "--dxf-extent" ) { QgsLocaleNumC l; QString ext( args[++i] ); QStringList coords( ext.split( ',' ) ); if ( coords.size() != 4 ) { std::cerr << "invalid dxf extent " << ext.toStdString() << std::endl; return 2; } for ( int i = 0; i < 4; i++ ) { bool ok; double d; d = coords[i].toDouble( &ok ); if ( !ok ) { std::cerr << "invalid dxf coordinate " << coords[i].toStdString() << " in extent " << ext.toStdString() << std::endl; return 2; } switch ( i ) { case 0: dxfExtent.setXMinimum( d ); break; case 1: dxfExtent.setYMinimum( d ); break; case 2: dxfExtent.setXMaximum( d ); break; case 3: dxfExtent.setYMaximum( d ); break; } } } else if ( arg == "--dxf-symbology-mode" ) { QString mode( args[++i] ); if ( mode == "none" ) { dxfSymbologyMode = QgsDxfExport::NoSymbology; } else if ( mode == "symbollayer" ) { dxfSymbologyMode = QgsDxfExport::SymbolLayerSymbology; } else if ( mode == "feature" ) { dxfSymbologyMode = QgsDxfExport::FeatureSymbology; } else { std::cerr << "invalid dxf symbology mode " << mode.toStdString() << std::endl; return 2; } } else if ( arg == "--dxf-scale-denom" ) { bool ok; QString scale( args[++i] ); dxfScaleDenom = scale.toDouble( &ok ); if ( !ok ) { std::cerr << "invalid dxf scale " << scale.toStdString() << std::endl; return 2; } } else if ( arg == "--dxf-encoding" ) { dxfEncoding = args[++i]; } else if ( arg == "--dxf-preset" ) { dxfPreset = args[++i]; } else if ( arg == "--" ) { for ( i++; i < args.size(); ++i ) myFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) ); } else { myFileList.append( QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ) ); } } } ///////////////////////////////////////////////////////////////////// // If no --project was specified, parse the args to look for a // // .qgs file and set myProjectFileName to it. This allows loading // // of a project file by clicking on it in various desktop managers // // where an appropriate mime-type has been set up. // ///////////////////////////////////////////////////////////////////// if ( myProjectFileName.isEmpty() ) { // check for a .qgs for ( int i = 0; i < args.size(); i++ ) { QString arg = QDir::toNativeSeparators( QFileInfo( args[i] ).absoluteFilePath() ); if ( arg.contains( ".qgs" ) ) { myProjectFileName = arg; break; } } } ///////////////////////////////////////////////////////////////////// // Now we have the handlers for the different behaviours... //////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// // Initialise the application and the translation stuff ///////////////////////////////////////////////////////////////////// #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(ANDROID) bool myUseGuiFlag = nullptr != getenv( "DISPLAY" ); #else bool myUseGuiFlag = true; #endif if ( !myUseGuiFlag ) { std::cerr << QObject::tr( "QGIS starting in non-interactive mode not supported.\n" "You are seeing this message most likely because you " "have no DISPLAY environment variable set.\n" ).toUtf8().constData(); exit( 1 ); //exit for now until a version of qgis is capabable of running non interactive } if ( !optionpath.isEmpty() || !configpath.isEmpty() ) { // tell QSettings to use INI format and save the file in custom config path QSettings::setDefaultFormat( QSettings::IniFormat ); QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, optionpath.isEmpty() ? configpath : optionpath ); } // GUI customization is enabled according to settings (loaded when instance is created) // we force disabled here if --nocustomization argument is used if ( !myCustomization ) { QgsCustomization::instance()->setEnabled( false ); } QgsApplication myApp( argc, argv, myUseGuiFlag, configpath ); myApp.setWindowIcon( QIcon( QgsApplication::appIconPath() ) ); // // Set up the QSettings environment must be done after qapp is created QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME ); QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN ); QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME ); QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false ); QSettings* customizationsettings; if ( !optionpath.isEmpty() || !configpath.isEmpty() ) { // tell QSettings to use INI format and save the file in custom config path QSettings::setDefaultFormat( QSettings::IniFormat ); QString path = optionpath.isEmpty() ? configpath : optionpath; QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, path ); customizationsettings = new QSettings( QSettings::IniFormat, QSettings::UserScope, "QGIS", "QGISCUSTOMIZATION2" ); } else { customizationsettings = new QSettings( "QGIS", "QGISCUSTOMIZATION2" ); } // Using the customizationfile option always overrides the option and config path options. if ( !customizationfile.isEmpty() ) { customizationsettings = new QSettings( customizationfile, QSettings::IniFormat ); QgsCustomization::instance()->setEnabled( true ); } // Load and set possible default customization, must be done afterQgsApplication init and QSettings ( QCoreApplication ) init QgsCustomization::instance()->setSettings( customizationsettings ); QgsCustomization::instance()->loadDefault(); #ifdef Q_OS_MACX // If the GDAL plugins are bundled with the application and GDAL_DRIVER_PATH // is not already defined, use the GDAL plugins in the application bundle. QString gdalPlugins( QCoreApplication::applicationDirPath().append( "/lib/gdalplugins" ) ); if ( QFile::exists( gdalPlugins ) && !getenv( "GDAL_DRIVER_PATH" ) ) { setenv( "GDAL_DRIVER_PATH", gdalPlugins.toUtf8(), 1 ); } // Point GDAL_DATA at any GDAL share directory embedded in the app bundle if ( !getenv( "GDAL_DATA" ) ) { QStringList gdalShares; QString appResources( QDir::cleanPath( QgsApplication::pkgDataPath() ) ); gdalShares << QCoreApplication::applicationDirPath().append( "/share/gdal" ) << appResources.append( "/share/gdal" ) << appResources.append( "/gdal" ); Q_FOREACH ( const QString& gdalShare, gdalShares ) { if ( QFile::exists( gdalShare ) ) { setenv( "GDAL_DATA", gdalShare.toUtf8().constData(), 1 ); break; } } }
int QgsVectorLayerEditUtils::splitFeatures( const QList<QgsPoint>& splitLine, bool topologicalEditing ) { if ( !L->hasGeometryType() ) return 4; QgsFeatureList newFeatures; //store all the newly created features double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line int returnCode = 0; int splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplittedFeatures = 0; QgsFeatureIterator features; const QgsFeatureIds selectedIds = L->selectedFeatureIds(); if ( !selectedIds.isEmpty() ) //consider only the selected features if there is a selection { features = L->selectedFeaturesIterator(); } else //else consider all the feature that intersect the bounding box of the split line { if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) == 0 ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return 1; } if ( bBox.isEmpty() ) { //if the bbox is a line, try to make a square out of it if ( bBox.width() == 0.0 && bBox.height() > 0 ) { bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); } else if ( bBox.height() == 0.0 && bBox.width() > 0 ) { bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); } else { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; if ( L->crs().isGeographic() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); bBox.setYMinimum( bBox.yMinimum() - bufferDistance ); bBox.setYMaximum( bBox.yMaximum() + bufferDistance ); } } features = L->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } QgsFeature feat; while ( features.nextFeature( feat ) ) { if ( !feat.hasGeometry() ) { continue; } QList<QgsGeometry> newGeometries; QList<QgsPoint> topologyTestPoints; QgsGeometry featureGeom = feat.geometry(); splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); if ( splitFunctionReturn == 0 ) { //change this geometry L->editBuffer()->changeGeometry( feat.id(), featureGeom ); //insert new features for ( int i = 0; i < newGeometries.size(); ++i ) { QgsFeature f = QgsVectorLayerUtils::createFeature( L, newGeometries.at( i ), feat.attributes().toMap() ); L->editBuffer()->addFeature( f ); } if ( topologicalEditing ) { QList<QgsPoint>::const_iterator topol_it = topologyTestPoints.constBegin(); for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) { addTopologicalPoints( *topol_it ); } } ++numberOfSplittedFeatures; } else if ( splitFunctionReturn > 1 ) //1 means no split but also no error { returnCode = splitFunctionReturn; } } if ( numberOfSplittedFeatures == 0 && !selectedIds.isEmpty() ) { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split returnCode = 4; } return returnCode; }
bool QgsMapToolFeatureAction::doAction( QgsVectorLayer *layer, int x, int y ) { if ( !layer ) return false; QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( x, y ); QgsFeatureList featList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } if ( featList.size() == 0 ) return false; Q_FOREACH ( const QgsFeature& feat, featList ) { if ( layer->actions()->defaultAction() >= 0 ) { // define custom substitutions: layer id and clicked coords QMap<QString, QVariant> substitutionMap; substitutionMap.insert( "$layerid", layer->id() ); point = toLayerCoordinates( layer, point ); substitutionMap.insert( "$clickx", point.x() ); substitutionMap.insert( "$clicky", point.y() ); int actionIdx = layer->actions()->defaultAction(); layer->actions()->doAction( actionIdx, feat, &substitutionMap ); } else { QgsMapLayerAction* mapLayerAction = QgsMapLayerActionRegistry::instance()->defaultActionForLayer( layer ); if ( mapLayerAction ) { mapLayerAction->triggerForFeature( layer, &feat ); } } } return true; }
bool QgsMapRendererJob::reprojectToLayerExtent( const QgsCoordinateTransform* ct, bool layerCrsGeographic, QgsRectangle& extent, QgsRectangle& r2 ) { bool split = false; try { #ifdef QGISDEBUG // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__); #endif // Split the extent into two if the source CRS is // geographic and the extent crosses the split in // geographic coordinates (usually +/- 180 degrees, // and is assumed to be so here), and draw each // extent separately. static const double splitCoord = 180.0; if ( layerCrsGeographic ) { // Note: ll = lower left point // and ur = upper right point QgsPoint ll = ct->transform( extent.xMinimum(), extent.yMinimum(), QgsCoordinateTransform::ReverseTransform ); QgsPoint ur = ct->transform( extent.xMaximum(), extent.yMaximum(), QgsCoordinateTransform::ReverseTransform ); extent = ct->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); if ( ll.x() > ur.x() ) { // the coordinates projected in reverse order than what one would expect. // we are probably looking at an area that includes longitude of 180 degrees. // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180) // so let's use (-180,180). This hopefully does not add too much overhead. It is // more straightforward than rendering with two separate extents and more consistent // for rendering, labeling and caching as everything is rendered just in one go extent.setXMinimum( -splitCoord ); extent.setXMaximum( splitCoord ); } // TODO: the above rule still does not help if using a projection that covers the whole // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx. // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90, // but in fact the extent should cover the whole world. } else // can't cross 180 { if ( ct->destCRS().geographicFlag() && ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 || extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) ) // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates. // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity. // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat, // but this seems like a safer choice. extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); else extent = ct->transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "Transform error caught" ); extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); r2 = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); } return split; }
bool QgsMapToolIdentify::identifyRasterLayer( QList<IdentifyResult> *results, QgsRasterLayer *layer, QgsPoint point, const QgsRectangle& viewExtent, double mapUnitsPerPixel ) { QgsDebugMsg( "point = " + point.toString() ); if ( !layer ) return false; QgsRasterDataProvider *dprovider = layer->dataProvider(); if ( !dprovider ) return false; int capabilities = dprovider->capabilities(); if ( !( capabilities & QgsRasterDataProvider::Identify ) ) return false; QgsPoint pointInCanvasCrs = point; try { point = toLayerCoordinates( layer, point ); } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( QString( "coordinate not reprojectable: %1" ).arg( cse.what() ) ); return false; } QgsDebugMsg( QString( "point = %1 %2" ).arg( point.x() ).arg( point.y() ) ); if ( !layer->extent().contains( point ) ) return false; QMap< QString, QString > attributes, derivedAttributes; QgsRaster::IdentifyFormat format = QgsRasterDataProvider::identifyFormatFromName( layer->customProperty( "identify/format" ).toString() ); // check if the format is really supported otherwise use first supported format if ( !( QgsRasterDataProvider::identifyFormatToCapability( format ) & capabilities ) ) { if ( capabilities & QgsRasterInterface::IdentifyFeature ) format = QgsRaster::IdentifyFormatFeature; else if ( capabilities & QgsRasterInterface::IdentifyValue ) format = QgsRaster::IdentifyFormatValue; else if ( capabilities & QgsRasterInterface::IdentifyHtml ) format = QgsRaster::IdentifyFormatHtml; else if ( capabilities & QgsRasterInterface::IdentifyText ) format = QgsRaster::IdentifyFormatText; else return false; } QgsRasterIdentifyResult identifyResult; // We can only use current map canvas context (extent, width, height) if layer is not reprojected, if ( mCanvas->hasCrsTransformEnabled() && dprovider->crs() != mCanvas->mapSettings().destinationCrs() ) { // To get some reasonable response for point/line WMS vector layers we must // use a context with approximately a resolution in layer CRS units // corresponding to current map canvas resolution (for examplei UMN Mapserver // in msWMSFeatureInfo() -> msQueryByRect() is using requested pixel // + TOLERANCE (layer param) for feature selection) // QgsRectangle r; r.setXMinimum( pointInCanvasCrs.x() - mapUnitsPerPixel / 2. ); r.setXMaximum( pointInCanvasCrs.x() + mapUnitsPerPixel / 2. ); r.setYMinimum( pointInCanvasCrs.y() - mapUnitsPerPixel / 2. ); r.setYMaximum( pointInCanvasCrs.y() + mapUnitsPerPixel / 2. ); r = toLayerCoordinates( layer, r ); // will be a bit larger // Mapserver (6.0.3, for example) does not work with 1x1 pixel box // but that is fixed (the rect is enlarged) in the WMS provider identifyResult = dprovider->identify( point, format, r, 1, 1 ); } else { // It would be nice to use the same extent and size which was used for drawing, // so that WCS can use cache from last draw, unfortunately QgsRasterLayer::draw() // is doing some tricks with extent and size to allign raster to output which // would be difficult to replicate here. // Note: cutting the extent may result in slightly different x and y resolutions // and thus shifted point calculated back in QGIS WMS (using average resolution) //viewExtent = dprovider->extent().intersect( &viewExtent ); // Width and height are calculated from not projected extent and we hope that // are similar to source width and height used to reproject layer for drawing. // TODO: may be very dangerous, because it may result in different resolutions // in source CRS, and WMS server (QGIS server) calcs wrong coor using average resolution. int width = qRound( viewExtent.width() / mapUnitsPerPixel ); int height = qRound( viewExtent.height() / mapUnitsPerPixel ); QgsDebugMsg( QString( "viewExtent.width = %1 viewExtent.height = %2" ).arg( viewExtent.width() ).arg( viewExtent.height() ) ); QgsDebugMsg( QString( "width = %1 height = %2" ).arg( width ).arg( height ) ); QgsDebugMsg( QString( "xRes = %1 yRes = %2 mapUnitsPerPixel = %3" ).arg( viewExtent.width() / width ).arg( viewExtent.height() / height ).arg( mapUnitsPerPixel ) ); identifyResult = dprovider->identify( point, format, viewExtent, width, height ); } derivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); if ( identifyResult.isValid() ) { QMap<int, QVariant> values = identifyResult.results(); QgsGeometry geometry; if ( format == QgsRaster::IdentifyFormatValue ) { Q_FOREACH ( int bandNo, values.keys() ) { QString valueString; if ( values.value( bandNo ).isNull() ) { valueString = tr( "no data" ); } else { double value = values.value( bandNo ).toDouble(); valueString = QgsRasterBlock::printValue( value ); } attributes.insert( dprovider->generateBandName( bandNo ), valueString ); } QString label = layer->name(); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), label, attributes, derivedAttributes ) ); }
QgsRectangle QgsServerProjectParser::layerBoundingBoxInProjectCrs( const QDomElement& layerElem, const QDomDocument &doc ) const { QgsRectangle BBox; if ( layerElem.isNull() ) { return BBox; } //read box coordinates and layer auth. id QDomElement boundingBoxElem = layerElem.firstChildElement( "BoundingBox" ); if ( boundingBoxElem.isNull() ) { return BBox; } double minx, miny, maxx, maxy; bool conversionOk; minx = boundingBoxElem.attribute( "minx" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } miny = boundingBoxElem.attribute( "miny" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } maxx = boundingBoxElem.attribute( "maxx" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } maxy = boundingBoxElem.attribute( "maxy" ).toDouble( &conversionOk ); if ( !conversionOk ) { return BBox; } QString version = doc.documentElement().attribute( "version" ); //create layer crs QgsCoordinateReferenceSystem layerCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( boundingBoxElem.attribute( version == "1.1.1" ? "SRS" : "CRS" ) ); if ( !layerCrs.isValid() ) { return BBox; } BBox.setXMinimum( minx ); BBox.setXMaximum( maxx ); BBox.setYMinimum( miny ); BBox.setYMaximum( maxy ); if ( version != "1.1.1" && layerCrs.hasAxisInverted() ) { BBox.invert(); } //get project crs QgsCoordinateTransform t( layerCrs, projectCrs() ); //transform BBox = t.transformBoundingBox( BBox ); return BBox; }
QgsGeometry::OperationResult QgsVectorLayerEditUtils::splitParts( const QVector<QgsPointXY> &splitLine, bool topologicalEditing ) { if ( !mLayer->isSpatial() ) return QgsGeometry::InvalidBaseGeometry; double xMin, yMin, xMax, yMax; QgsRectangle bBox; //bounding box of the split line QgsGeometry::OperationResult returnCode = QgsGeometry::OperationResult::Success; QgsGeometry::OperationResult splitFunctionReturn; //return code of QgsGeometry::splitGeometry int numberOfSplitParts = 0; QgsFeatureIterator fit; if ( mLayer->selectedFeatureCount() > 0 ) //consider only the selected features if there is a selection { fit = mLayer->getSelectedFeatures(); } else //else consider all the feature that intersect the bounding box of the split line { if ( boundingBoxFromPointList( splitLine, xMin, yMin, xMax, yMax ) ) { bBox.setXMinimum( xMin ); bBox.setYMinimum( yMin ); bBox.setXMaximum( xMax ); bBox.setYMaximum( yMax ); } else { return QgsGeometry::OperationResult::InvalidInputGeometryType; } if ( bBox.isEmpty() ) { //if the bbox is a line, try to make a square out of it if ( bBox.width() == 0.0 && bBox.height() > 0 ) { bBox.setXMinimum( bBox.xMinimum() - bBox.height() / 2 ); bBox.setXMaximum( bBox.xMaximum() + bBox.height() / 2 ); } else if ( bBox.height() == 0.0 && bBox.width() > 0 ) { bBox.setYMinimum( bBox.yMinimum() - bBox.width() / 2 ); bBox.setYMaximum( bBox.yMaximum() + bBox.width() / 2 ); } else { //If we have a single point, we still create a non-null box double bufferDistance = 0.000001; if ( mLayer->crs().isGeographic() ) bufferDistance = 0.00000001; bBox.setXMinimum( bBox.xMinimum() - bufferDistance ); bBox.setXMaximum( bBox.xMaximum() + bufferDistance ); bBox.setYMinimum( bBox.yMinimum() - bufferDistance ); bBox.setYMaximum( bBox.yMaximum() + bufferDistance ); } } fit = mLayer->getFeatures( QgsFeatureRequest().setFilterRect( bBox ).setFlags( QgsFeatureRequest::ExactIntersect ) ); } QgsGeometry::OperationResult addPartRet = QgsGeometry::OperationResult::Success; QgsFeature feat; while ( fit.nextFeature( feat ) ) { QVector<QgsGeometry> newGeometries; QVector<QgsPointXY> topologyTestPoints; QgsGeometry featureGeom = feat.geometry(); splitFunctionReturn = featureGeom.splitGeometry( splitLine, newGeometries, topologicalEditing, topologyTestPoints ); if ( splitFunctionReturn == 0 ) { //add new parts if ( !newGeometries.isEmpty() ) featureGeom.convertToMultiType(); for ( int i = 0; i < newGeometries.size(); ++i ) { addPartRet = featureGeom.addPart( newGeometries.at( i ) ); if ( addPartRet ) break; } // For test only: Exception already thrown here... // feat.geometry()->asWkb(); if ( !addPartRet ) { mLayer->editBuffer()->changeGeometry( feat.id(), featureGeom ); } if ( topologicalEditing ) { QVector<QgsPointXY>::const_iterator topol_it = topologyTestPoints.constBegin(); for ( ; topol_it != topologyTestPoints.constEnd(); ++topol_it ) { addTopologicalPoints( *topol_it ); } } ++numberOfSplitParts; } else if ( splitFunctionReturn != QgsGeometry::OperationResult::Success && splitFunctionReturn != QgsGeometry::OperationResult::NothingHappened ) { returnCode = splitFunctionReturn; } } if ( numberOfSplitParts == 0 && mLayer->selectedFeatureCount() > 0 && returnCode == QgsGeometry::Success ) { //There is a selection but no feature has been split. //Maybe user forgot that only the selected features are split returnCode = QgsGeometry::OperationResult::NothingHappened; } return returnCode; }
bool QgsMapToolIdentify::identifyVectorLayer( QList<IdentifyResult> *results, QgsVectorLayer *layer, const QgsPoint& point ) { if ( !layer || !layer->hasGeometryType() ) return false; if ( layer->hasScaleBasedVisibility() && ( layer->minimumScale() > mCanvas->mapSettings().scale() || layer->maximumScale() <= mCanvas->mapSettings().scale() ) ) { QgsDebugMsg( "Out of scale limits" ); return false; } QApplication::setOverrideCursor( Qt::WaitCursor ); QMap< QString, QString > commonDerivedAttributes; commonDerivedAttributes.insert( tr( "(clicked coordinate)" ), point.toString() ); int featureCount = 0; QgsFeatureList featureList; // toLayerCoordinates will throw an exception for an 'invalid' point. // 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. try { // create the search rectangle double searchRadius = searchRadiusMU( mCanvas ); QgsRectangle r; r.setXMinimum( point.x() - searchRadius ); r.setXMaximum( point.x() + searchRadius ); r.setYMinimum( point.y() - searchRadius ); r.setYMaximum( point.y() + searchRadius ); r = toLayerCoordinates( layer, r ); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterRect( r ).setFlags( QgsFeatureRequest::ExactIntersect ) ); QgsFeature f; while ( fit.nextFeature( f ) ) featureList << QgsFeature( f ); } catch ( QgsCsException & cse ) { Q_UNUSED( cse ); // catch exception for 'invalid' point and proceed with no features found QgsDebugMsg( QString( "Caught CRS exception %1" ).arg( cse.what() ) ); } QgsFeatureList::iterator f_it = featureList.begin(); bool filter = false; QgsRenderContext context( QgsRenderContext::fromMapSettings( mCanvas->mapSettings() ) ); context.expressionContext() << QgsExpressionContextUtils::layerScope( layer ); QgsFeatureRendererV2* renderer = layer->rendererV2(); if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { // setup scale for scale dependent visibility (rule based) renderer->startRender( context, layer->fields() ); filter = renderer->capabilities() & QgsFeatureRendererV2::Filter; } for ( ; f_it != featureList.end(); ++f_it ) { QMap< QString, QString > derivedAttributes = commonDerivedAttributes; QgsFeatureId fid = f_it->id(); context.expressionContext().setFeature( *f_it ); if ( filter && !renderer->willRenderFeature( *f_it, context ) ) continue; featureCount++; derivedAttributes.unite( featureDerivedAttributes( &( *f_it ), layer, toLayerCoordinates( layer, point ) ) ); derivedAttributes.insert( tr( "feature id" ), fid < 0 ? tr( "new feature" ) : FID_TO_STRING( fid ) ); results->append( IdentifyResult( qobject_cast<QgsMapLayer *>( layer ), *f_it, derivedAttributes ) ); } if ( renderer && renderer->capabilities() & QgsFeatureRendererV2::ScaleDependent ) { renderer->stopRender( context ); } QgsDebugMsg( "Feature count on identify: " + QString::number( featureCount ) ); QApplication::restoreOverrideCursor(); return featureCount > 0; }
bool QgsMapRendererJob::reprojectToLayerExtent( const QgsMapLayer *ml, const QgsCoordinateTransform &ct, QgsRectangle &extent, QgsRectangle &r2 ) { bool split = false; try { #ifdef QGISDEBUG // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__); #endif // Split the extent into two if the source CRS is // geographic and the extent crosses the split in // geographic coordinates (usually +/- 180 degrees, // and is assumed to be so here), and draw each // extent separately. static const double SPLIT_COORD = 180.0; if ( ml->crs().isGeographic() ) { if ( ml->type() == QgsMapLayer::VectorLayer && !ct.destinationCrs().isGeographic() ) { // if we transform from a projected coordinate system check // check if transforming back roughly returns the input // extend - otherwise render the world. QgsRectangle extent1 = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsRectangle extent2 = ct.transformBoundingBox( extent1, QgsCoordinateTransform::ForwardTransform ); QgsDebugMsgLevel( QString( "\n0:%1 %2x%3\n1:%4\n2:%5 %6x%7 (w:%8 h:%9)" ) .arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ) .arg( extent1.toString(), extent2.toString() ).arg( extent2.width() ).arg( extent2.height() ) .arg( std::fabs( 1.0 - extent2.width() / extent.width() ) ) .arg( std::fabs( 1.0 - extent2.height() / extent.height() ) ) , 3 ); if ( std::fabs( 1.0 - extent2.width() / extent.width() ) < 0.5 && std::fabs( 1.0 - extent2.height() / extent.height() ) < 0.5 ) { extent = extent1; } else { extent = QgsRectangle( -180.0, -90.0, 180.0, 90.0 ); } } else { // Note: ll = lower left point QgsPointXY ll = ct.transform( extent.xMinimum(), extent.yMinimum(), QgsCoordinateTransform::ReverseTransform ); // and ur = upper right point QgsPointXY ur = ct.transform( extent.xMaximum(), extent.yMaximum(), QgsCoordinateTransform::ReverseTransform ); QgsDebugMsgLevel( QString( "in:%1 (ll:%2 ur:%3)" ).arg( extent.toString(), ll.toString(), ur.toString() ), 4 ); extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); QgsDebugMsgLevel( QString( "out:%1 (w:%2 h:%3)" ).arg( extent.toString() ).arg( extent.width() ).arg( extent.height() ), 4 ); if ( ll.x() > ur.x() ) { // the coordinates projected in reverse order than what one would expect. // we are probably looking at an area that includes longitude of 180 degrees. // we need to take into account coordinates from two intervals: (-180,x1) and (x2,180) // so let's use (-180,180). This hopefully does not add too much overhead. It is // more straightforward than rendering with two separate extents and more consistent // for rendering, labeling and caching as everything is rendered just in one go extent.setXMinimum( -SPLIT_COORD ); extent.setXMaximum( SPLIT_COORD ); } } // TODO: the above rule still does not help if using a projection that covers the whole // world. E.g. with EPSG:3857 the longitude spectrum -180 to +180 is mapped to approx. // -2e7 to +2e7. Converting extent from -5e7 to +5e7 is transformed as -90 to +90, // but in fact the extent should cover the whole world. } else // can't cross 180 { if ( ct.destinationCrs().isGeographic() && ( extent.xMinimum() <= -180 || extent.xMaximum() >= 180 || extent.yMinimum() <= -90 || extent.yMaximum() >= 90 ) ) // Use unlimited rectangle because otherwise we may end up transforming wrong coordinates. // E.g. longitude -200 to +160 would be understood as +40 to +160 due to periodicity. // We could try to clamp coords to (-180,180) for lon resp. (-90,90) for lat, // but this seems like a safer choice. extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); else extent = ct.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform ); } } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( "Transform error caught" ); extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); r2 = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX ); } return split; }
QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback ) { QgsDebugMsgLevel( QString( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 ); QgsDebugMsgLevel( QString( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 ); QgsRasterBlock *block = new QgsRasterBlock( dataType( bandNo ), width, height ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { block->setNoDataValue( sourceNoDataValue( bandNo ) ); } if ( block->isEmpty() ) { QgsDebugMsg( "Couldn't create raster block" ); return block; } // Read necessary extent only QgsRectangle tmpExtent = extent().intersect( &boundingBox ); if ( tmpExtent.isEmpty() ) { QgsDebugMsg( "Extent outside provider extent" ); block->setIsNoData(); return block; } double xRes = boundingBox.width() / width; double yRes = boundingBox.height() / height; double tmpXRes, tmpYRes; double providerXRes = 0; double providerYRes = 0; if ( capabilities() & Size ) { providerXRes = extent().width() / xSize(); providerYRes = extent().height() / ySize(); tmpXRes = std::max( providerXRes, xRes ); tmpYRes = std::max( providerYRes, yRes ); if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes; if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes; } else { tmpXRes = xRes; tmpYRes = yRes; } if ( tmpExtent != boundingBox || tmpXRes > xRes || tmpYRes > yRes ) { // Read smaller extent or lower resolution if ( !extent().contains( boundingBox ) ) { QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() ); block->setIsNoDataExcept( subRect ); } // Calculate row/col limits (before tmpExtent is aligned) int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes ); int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1; int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes ); int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1; QgsDebugMsgLevel( QString( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 ); if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height || fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width ) { // Should not happen QgsDebugMsg( "Row or column limits out of range" ); return block; } // If lower source resolution is used, the extent must beS aligned to original // resolution to avoid possible shift due to resampling if ( tmpXRes > xRes ) { int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes ); col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes ); tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes ); } if ( tmpYRes > yRes ) { int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes ); tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes ); row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes ); tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes ); } int tmpWidth = std::round( tmpExtent.width() / tmpXRes ); int tmpHeight = std::round( tmpExtent.height() / tmpYRes ); tmpXRes = tmpExtent.width() / tmpWidth; tmpYRes = tmpExtent.height() / tmpHeight; QgsDebugMsgLevel( QString( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 ); QgsDebugMsgLevel( QString( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 ); QgsRasterBlock *tmpBlock = new QgsRasterBlock( dataType( bandNo ), tmpWidth, tmpHeight ); if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) ) { tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) ); } readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ); int pixelSize = dataTypeSize( bandNo ); double xMin = boundingBox.xMinimum(); double yMax = boundingBox.yMaximum(); double tmpXMin = tmpExtent.xMinimum(); double tmpYMax = tmpExtent.yMaximum(); for ( int row = fromRow; row <= toRow; row++ ) { double y = yMax - ( row + 0.5 ) * yRes; int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes ); for ( int col = fromCol; col <= toCol; col++ ) { double x = xMin + ( col + 0.5 ) * xRes; int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes ); if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth ) { QgsDebugMsg( "Source row or column limits out of range" ); block->setIsNoData(); // so that the problem becomes obvious and fixed delete tmpBlock; return block; } qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol; qgssize index = row * static_cast< qgssize >( width ) + col; char *tmpBits = tmpBlock->bits( tmpIndex ); char *bits = block->bits( index ); if ( !tmpBits ) { QgsDebugMsg( QString( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) ); continue; } if ( !bits ) { QgsDebugMsg( "Cannot set output block data." ); continue; } memcpy( bits, tmpBits, pixelSize ); } } delete tmpBlock; } else { readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ); } // apply scale and offset block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) ); // apply user no data values block->applyNoDataValues( userNoDataValues( bandNo ) ); return block; }
QgsAfsProvider::QgsAfsProvider( const QString &uri ) : QgsVectorDataProvider( uri ) , mValid( false ) , mObjectIdFieldIdx( -1 ) { mSharedData.reset( new QgsAfsSharedData() ); mSharedData->mGeometryType = QgsWkbTypes::Unknown; mSharedData->mDataSource = QgsDataSourceUri( uri ); // Set CRS mSharedData->mSourceCRS = QgsCoordinateReferenceSystem::fromOgcWmsCrs( mSharedData->mDataSource.param( QStringLiteral( "crs" ) ) ); // Get layer info QString errorTitle, errorMessage; const QVariantMap layerData = QgsArcGisRestUtils::getLayerInfo( mSharedData->mDataSource.param( QStringLiteral( "url" ) ), errorTitle, errorMessage ); if ( layerData.isEmpty() ) { pushError( errorTitle + ": " + errorMessage ); appendError( QgsErrorMessage( tr( "getLayerInfo failed" ), QStringLiteral( "AFSProvider" ) ) ); return; } mLayerName = layerData[QStringLiteral( "name" )].toString(); mLayerDescription = layerData[QStringLiteral( "description" )].toString(); // Set extent QStringList coords = mSharedData->mDataSource.param( QStringLiteral( "bbox" ) ).split( ',' ); if ( coords.size() == 4 ) { bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; mSharedData->mExtent.setXMinimum( coords[0].toDouble( &xminOk ) ); mSharedData->mExtent.setYMinimum( coords[1].toDouble( &yminOk ) ); mSharedData->mExtent.setXMaximum( coords[2].toDouble( &xmaxOk ) ); mSharedData->mExtent.setYMaximum( coords[3].toDouble( &ymaxOk ) ); if ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) mSharedData->mExtent = QgsRectangle(); } const QVariantMap layerExtentMap = layerData[QStringLiteral( "extent" )].toMap(); bool xminOk = false, yminOk = false, xmaxOk = false, ymaxOk = false; QgsRectangle originalExtent; originalExtent.setXMinimum( layerExtentMap[QStringLiteral( "xmin" )].toDouble( &xminOk ) ); originalExtent.setYMinimum( layerExtentMap[QStringLiteral( "ymin" )].toDouble( &yminOk ) ); originalExtent.setXMaximum( layerExtentMap[QStringLiteral( "xmax" )].toDouble( &xmaxOk ) ); originalExtent.setYMaximum( layerExtentMap[QStringLiteral( "ymax" )].toDouble( &ymaxOk ) ); if ( mSharedData->mExtent.isEmpty() && ( !xminOk || !yminOk || !xmaxOk || !ymaxOk ) ) { appendError( QgsErrorMessage( tr( "Could not retrieve layer extent" ), QStringLiteral( "AFSProvider" ) ) ); return; } QgsCoordinateReferenceSystem extentCrs = QgsArcGisRestUtils::parseSpatialReference( layerExtentMap[QStringLiteral( "spatialReference" )].toMap() ); if ( mSharedData->mExtent.isEmpty() && !extentCrs.isValid() ) { appendError( QgsErrorMessage( tr( "Could not parse spatial reference" ), QStringLiteral( "AFSProvider" ) ) ); return; } if ( xminOk && yminOk && xmaxOk && ymaxOk ) { QgsLayerMetadata::SpatialExtent spatialExtent; spatialExtent.bounds = QgsBox3d( originalExtent ); spatialExtent.extentCrs = extentCrs; QgsLayerMetadata::Extent metadataExtent; metadataExtent.setSpatialExtents( QList< QgsLayerMetadata::SpatialExtent >() << spatialExtent ); mLayerMetadata.setExtent( metadataExtent ); } if ( extentCrs.isValid() ) { mLayerMetadata.setCrs( extentCrs ); } if ( mSharedData->mExtent.isEmpty() ) { mSharedData->mExtent = originalExtent; Q_NOWARN_DEPRECATED_PUSH mSharedData->mExtent = QgsCoordinateTransform( extentCrs, mSharedData->mSourceCRS ).transformBoundingBox( mSharedData->mExtent ); Q_NOWARN_DEPRECATED_POP }