// 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 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 ) ); } } 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 srand( time( NULL ) ); ///////////////////////////////////////////////////////////////// // 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 myRestorePlugins = true; bool myCustomization = true; // 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 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 ) { 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::convertSeparators( 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::convertSeparators( 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::convertSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == "--configpath" || arg == "-c" ) ) { configpath = QDir::convertSeparators( QDir( args[++i] ).absolutePath() ); } else if ( i + 1 < argc && ( arg == "--code" || arg == "-f" ) ) { pythonfile = QDir::convertSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else if ( i + 1 < argc && ( arg == "--customizationfile" || arg == "-z" ) ) { customizationfile = QDir::convertSeparators( QFileInfo( args[++i] ).absoluteFilePath() ); } else { myFileList.append( QDir::convertSeparators( 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::convertSeparators( 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 ///////////////////////////////////////////////////////////////////// #ifdef Q_WS_X11 bool myUseGuiFlag = getenv( "DISPLAY" ) != 0; #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 ); // (if Windows/Mac, use icon from resource) #if !defined(Q_WS_WIN) && !defined(Q_WS_MAC) myApp.setWindowIcon( QIcon( QgsApplication::iconsPath() + "qgis-icon-60x60.png" ) ); #endif // // Set up the QSettings environment must be done after qapp is created QCoreApplication::setOrganizationName( "QGIS" ); QCoreApplication::setOrganizationDomain( "qgis.org" ); QCoreApplication::setApplicationName( "QGIS2" ); 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 ); } #endif QSettings mySettings; // update any saved setting for older themes to new default 'gis' theme (2013-04-15) if ( mySettings.contains( "/Themes" ) ) { QString theme = mySettings.value( "/Themes", "default" ).toString(); if ( theme == QString( "gis" ) || theme == QString( "classic" ) || theme == QString( "nkids" ) ) { mySettings.setValue( "/Themes", QString( "default" ) ); } } // custom environment variables QMap<QString, QString> systemEnvVars = QgsApplication::systemEnvVars(); bool useCustomVars = mySettings.value( "qgis/customEnvVarsUse", QVariant( false ) ).toBool(); if ( useCustomVars ) { QStringList customVarsList = mySettings.value( "qgis/customEnvVars", "" ).toStringList(); if ( !customVarsList.isEmpty() ) { foreach ( const QString &varStr, customVarsList ) { int pos = varStr.indexOf( QLatin1Char( '|' ) ); if ( pos == -1 ) continue; QString envVarApply = varStr.left( pos ); QString varStrNameValue = varStr.mid( pos + 1 ); pos = varStrNameValue.indexOf( QLatin1Char( '=' ) ); if ( pos == -1 ) continue; QString envVarName = varStrNameValue.left( pos ); QString envVarValue = varStrNameValue.mid( pos + 1 ); if ( systemEnvVars.contains( envVarName ) ) { if ( envVarApply == "prepend" ) { envVarValue += systemEnvVars.value( envVarName ); } else if ( envVarApply == "append" ) { envVarValue = systemEnvVars.value( envVarName ) + envVarValue; } } if ( systemEnvVars.contains( envVarName ) && envVarApply == "unset" ) { #ifdef Q_WS_WIN putenv( envVarName.toUtf8().constData() ); #else unsetenv( envVarName.toUtf8().constData() ); #endif } else { #ifdef Q_WS_WIN if ( envVarApply != "undefined" || !getenv( envVarName.toUtf8().constData() ) ) putenv( QString( "%1=%2" ).arg( envVarName ).arg( envVarValue ).toUtf8().constData() ); #else setenv( envVarName.toUtf8().constData(), envVarValue.toUtf8().constData(), envVarApply == "undefined" ? 0 : 1 ); #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( 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; } } }